diff --git a/.eslintignore b/.eslintignore index a1a7449696c6..0b7533e1b70a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,3 +3,8 @@ js/viz/docs/* node_modules/* testing/helpers/sinon/* /themebuilder/data/metadata/* +*.p.js +*.j.js +*.p.d.ts +*.j.d.ts +playground/react/* diff --git a/.eslintrc b/.eslintrc index 684a2f7d484b..0065c834fd58 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,8 +2,10 @@ "env": { "es6": true }, - "parser": "babel-eslint", + "parser": "@typescript-eslint/parser", "parserOptions": { + "createDefaultProgram": true, + "project": "./tsconfig.json", "ecmaVersion": 6, "sourceType": "module", "ecmaFeatures": { @@ -11,9 +13,6 @@ "jsx": true } }, - "plugins": [ - "spellcheck" - ], "globals": { "setInterval": true, "setTimeout": true, @@ -23,7 +22,7 @@ "module": true, "exports": true }, - "extends": "eslint:recommended", + "extends": ["eslint:recommended", "devextreme/spell-check"], "rules": { "block-spacing": "error", "comma-spacing": "error", @@ -52,7 +51,7 @@ "no-new-func": "error", "no-eval": "error", "no-undef-init": "error", - "no-unused-vars": ["error", { "args": "none" }], + "no-unused-vars": ["error", { "args": "none", "ignoreRestSiblings": true }], "no-extend-native": "error", "no-alert": "error", "no-console": "error", @@ -90,437 +89,6 @@ } } ], - "quotes": ["error", "single"], - "spellcheck/spell-checker": [ - "error", - { - "lang": "en_US", - "comments": false, - "strings": false, - "identifiers": true, - "templates": false, - "skipIfMatch": [ - "^\\$?..$" - ], - "skipWords": [ - "dx", // DevExpress - "el", // Element - "fn", // Function - "fx", // Effects - "jq", // jQuery - "js", // JavaScript - "ko", // Knockout - "ln", // Math - "na", // Special case for NaN - "ng", // Angular - "ok", // OK - "px", // Pixel - "tz", // Timezone - "ua", // User-agent - "ui", // User Interface - "un", // "Un-Escape" - "xs", // extra small - "xy", // XY-diagram - "vm", // view-model - - "amd", // AMD modules - "bing", - "browserslist", // auto-prefixer browsers list - "cldr", // Unicode CLDR Project - "cssom", // cssom parser - "cwd", // current working directory - "edm", // Entity Data Model - "eol", // end of line - "etag", // HTTP header - "eula", // EULA - "eslintrc", - "jsrender", // JsRender template engine - "hsl", // HSL color - "hsv", // HSV color - "iana", // IANA (time-zone database) - "ie", - "ie11", - "ios", - "ipad", - "iphone", - "linejoin", // SVG "stroke-linejoin" - "linux", - "ltr", // Left-to-Right - "mdx", // OLAP Multi-dimensional expressions - "mercator", // Map term - "microsoft", - "moz", // Vendor prefix - "mozilla", - "mvc", - "firefox", - "fmt", - "msie", - "odata", // OData - "readonly", - "rebase", // clean-css option - "rtl", // Right-to-Left - "scss", - "semver", // The semantic versioner for npm - "sinon", // JS library - "tspan", // SVG element - "tspans", - "uglify", // UglifyJS - "untils", // Time-zone term - "viapoint", // Geo term - "webkit", - "webpack", - "xmla", // XML for Analysis - "ldml", // LOCALE DATA MARKUP LANGUAGE - - "png", - "jpg", - "svg", - - "API", - "accessor", - "accessors", - "acos", - "activedescendant", - "adaptivity", - "addons", - "affine", - "aggregator", - "aggregators", - "ajax", - "ampm", - "anim", - "appt", - "appts", - "arabic", - "arg", - "argc", - "args", - "argv", - "asc", - "ascii", - "asin", - "aspnet", - "async", - "atan", - "attr", - "attributor", - "attrs", - "autocomplete", - "autocompletion", - "backend", - "backends", - "basename", - "bezier", - "bindable", - "bool", - "buf", - "calc", - "camelize", - "cancelable", - "captionize", - "ceil", - "centroid", - "checkbox", - "checkboxes", - "codomain", - "coef", - "coefs", - "coeff", - "coeffs", - "colgroup", - "colgroups", - "colorizer", - "colorizers", - "colspan", - "colspans", - "concat", - "cond", - "configs", - "configurator", - "configurators", - "const", - "consts", - "conv", - "coord", - "coords", - "cordova", - "cpus", - "crit", - "crosshair", - "ctor", - "ctors", - "ctrl", - "ctx", - "dasherize", - "dataset", - "datetime", - "dblclick", - "deactivator", - "dec", - "decrement", - "deferreds", - "defs", - "del", - "dels", - "denormalize", - "deps", - "desc", - "deserialization", - "deserialize", - "dest", - "dev", - "devtool", - "dir", - "dirname", - "dom", - "donut", - "downloader", - "draggable", - "draggables", - "drilldown", - "droppable", - "durations", - "eigen", - "elems", - "enctype", - "enqueue", - "enum", - "esc", - "etalon", - "exceedings", - "exchanger", - "expander", - "expando", - "expr", - "exprs", - "extname", - "extremum", - "fieldset", - "fieldsets", - "filename", - "focusable", - "focusin", - "focusout", - "foreach", - "formatter", - "formatters", - "fullscreen", - "func", - "funcs", - "gantt", - "gaussian", - "geo", - "geocode", - "geocoded", - "geocoder", - "getter", - "getters", - "gregorian", - "guid", - "gte", - "haspopup", - "hideable", - "historyless", - "hor", - "horz", - "hostname", - "hoverable", - "href", - "html", - "http", - "idx", - "img", - "impl", - "inflector", - "infobox", - "infos", - "init", - "inited", - "intervalize", - "invertible", - "invoker", - "iri", - "iso", - "iter", - "jsonp", - "keydown", - "len", - "lng", - "localizable", - "lookups", - "marginate", - "matcher", - "matchers", - "metadata", - "minify", - "mixin", - "mixins", - "multiline", - "multipane", - "multitouch", - "namespace", - "namespaced", - "namespaces", - "nav", - "navbar", - "noop", - "normalizer", - "num", - "observables", - "overline", - "paddings", - "param", - "params", - "parsers", - "patcher", - "pathname", - "pdf", - "penult", - "polyfill", - "polyline", - "polymorph", - "polynom", - "popout", - "popup", - "pos", - "postfix", - "postfixes", - "postprocess", - "pre", - "preact", - "preload", - "prepend", - "prerender", - "prev", - "proj", - "proto", - "proxied", - "queryable", - "radian", - "radians", - "radiuses", - "readdir", - "rect", - "rects", - "registrator", - "reinit", - "rels", - "renderer", - "renderers", - "reposition", - "resample", - "resampled", - "resizable", - "resizables", - "resize", - "resized", - "resizer", - "resizing", - "resolvers", - "rgb", - "rgba", - "roadmap", - "rowspan", - "rowspans", - "sankey", - "scalebar", - "scrollable", - "scrollbar", - "scroller", - "scrollers", - "seg", - "selectable", - "semidiscrete", - "serializers", - "shader", - "sortable", - "sparkline", - "sparklines", - "splitter", - "sqrt", - "squarified", - "squarify", - "src", - "str", - "strikethrough", - "stringify", - "struct", - "stylesheets", - "sublevel", - "submenu", - "submenus", - "substr", - "substring", - "substrings", - "subtags", - "subvalue", - "subvalues", - "sugiyama", - "svg", - "swipeable", - "synchronizable", - "synchronizer", - "tabbable", - "tabindex", - "tbody", - "templated", - "thead", - "timeline", - "timestamp", - "timezones", - "titleize", - "tfoot", - "tmp", - "tmpl", - "toolbars", - "tooltip", - "tooltips", - "transclude", - "transcluded", - "treeview", - "turndown", - "uid", - "uint", - "unary", - "undelete", - "ungroup", - "ungrouping", - "unicode", - "unlink", - "unmap", - "unmerge", - "unmerged", - "unmocked", - "unproject", - "unregister", - "unselect", - "unselected", - "unshift", - "untranslate", - "updatable", - "uploader", - "uri", - "utc", - "utils", - "validator", - "validators", - "vals", - "ver", - "vert", - "viewport", - "vml", - "waypoint", - "waypoints", - "whitelist", - "winloss", - "workspace", - "writeable", - "xhr", - "xlsx", - "xml", - "xmlns" - ] - } - ] + "quotes": ["error", "single"] } } diff --git a/.gitignore b/.gitignore index 9daeaae71b3f..4ebc2489bfe8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ /js/localization/cldr-data /js/renovation/**/*.p.d.ts /js/renovation/**/*.p.js +/js/renovation/**/*.j.d.ts +/js/renovation/**/*.j.js /themebuilder/data/less /themebuilder/data/metadata /scss diff --git a/build/gulp/generator/gulpfile.js b/build/gulp/generator/gulpfile.js index ff523bd2d88b..4ca4ee7899a6 100644 --- a/build/gulp/generator/gulpfile.js +++ b/build/gulp/generator/gulpfile.js @@ -5,23 +5,26 @@ const ts = require('gulp-typescript'); const lint = require('gulp-eslint'); const plumber = require('gulp-plumber'); const gulpIf = require('gulp-if'); -const merge = require('merge-stream'); const babel = require('gulp-babel'); +const notify = require('gulp-notify'); +const watch = require('gulp-watch'); -const SRC = 'js/renovation/**/*.tsx'; +const SRC = ['js/renovation/**/*.tsx']; const DEST = 'js/renovation/'; -const GLOB_TS = require('../ts').GLOB_TS; -const COMMON_SRC = ['js/**/*.*', '!' + GLOB_TS]; +const COMMON_SRC = ['js/**/*.*', `!${SRC}`]; const knownErrors = [ 'Cannot find module \'preact\'.', - 'Cannot find module \'preact/hooks\'.' + 'Cannot find module \'preact/hooks\'.', + 'Cannot find module \'preact/compat\'.' ]; gulp.task('generate-components', function() { const tsProject = ts.createProject('build/gulp/generator/ts-configs/preact.tsconfig.json'); generator.defaultOptionsModule = 'js/core/options/utils'; + generator.jqueryComponentRegistratorModule = 'js/core/component_registrator'; + generator.jqueryBaseComponentModule = 'js/renovation/preact-wrapper/component'; return gulp.src(SRC) .pipe(generateComponents(generator)) @@ -45,36 +48,104 @@ gulp.task('generate-components', function() { .pipe(gulp.dest(DEST)); }); -function addGenerationTask(frameworkName, knownErrors = []) { +function addGenerationTask( + frameworkName, + knownErrors = [], + compileTs = true, + copyArtifacts = false, + babelGeneratedFiles = true +) { + const frameworkDest = `artifacts/${frameworkName}`; const generator = require(`devextreme-generator/${frameworkName}-generator`).default; - gulp.task(`generate-${frameworkName}`, function() { - const tsProject = ts.createProject(`build/gulp/generator/ts-configs/${frameworkName}.tsconfig.json`); - generator.defaultOptionsModule = 'js/core/options/utils'; - - return merge( - gulp.src(COMMON_SRC), - gulp.src('js/**/*.tsx') - .pipe(generateComponents(generator)) - .pipe(plumber(() => null)) - .pipe(tsProject({ - error(e) { - if(!knownErrors.some(i => e.message.endsWith(i))) { - console.log(e.message); - } - }, - finish() { } - })) - ).pipe(babel()) - .pipe(gulp.dest(`artifacts/${frameworkName}`)); + let tsProject = () => () => { }; + if(compileTs) { + tsProject = ts.createProject(`build/gulp/generator/ts-configs/${frameworkName}.tsconfig.json`); + } + + generator.defaultOptionsModule = 'js/core/options/utils'; + + gulp.task(`generate-${frameworkName}-declaration-only`, function() { + return gulp.src('js/**/*.tsx') + .pipe(generateComponents(generator)) + .pipe(plumber(() => null)) + .pipe(gulpIf(compileTs, tsProject({ + error(e) { + if(!knownErrors.some(i => e.message.endsWith(i))) { + console.log(e.message); + } + }, + finish() { } + }))) + .pipe(gulpIf(babelGeneratedFiles, babel())) + .pipe(gulp.dest(frameworkDest)); }); + + const artifactsSrc = ['./artifacts/css/**/*', `./artifacts/${frameworkName}/**/*`]; + + const generateSeries = [ + `generate-${frameworkName}-declaration-only`, + function() { + return gulp.src(COMMON_SRC) + .pipe( + gulpIf( + file => file.extname === '.js', + babel() + ) + ) + .pipe(gulp.dest(frameworkDest)); + }]; + + if(copyArtifacts) { + generateSeries.push(function copyArtifacts() { + return gulp.src(artifactsSrc, { base: './artifacts/' }) + .pipe(gulp.dest(`./playground/${frameworkName}/src/artifacts`)); + }); + } + + gulp.task(`generate-${frameworkName}`, gulp.series(...generateSeries)); + + const watchTasks = [ + function() { + watch(COMMON_SRC) + .pipe(plumber({ + errorHandler: notify.onError('Error: <%= error.message %>') + .bind() // bind call is necessary to prevent firing 'end' event in notify.onError implementation + })) + .pipe( + gulpIf( + file => file.extname === '.js', + babel() + ) + ) + .pipe(gulp.dest(frameworkDest)); + }, + function declarationBuild() { + gulp.watch(SRC, gulp.series(`generate-${frameworkName}-declaration-only`)); + } + ]; + + if(copyArtifacts) { + watchTasks.push(function copyArtifacts() { + return gulp.src(artifactsSrc, { base: './artifacts/' }) + .pipe(watch(artifactsSrc, { base: './artifacts/', readDelay: 1000 })) + .pipe(gulp.dest(`./playground/${frameworkName}/src/artifacts`)); + }); + } + + gulp.task(`generate-${frameworkName}-watch`, gulp.series( + `generate-${frameworkName}`, + gulp.parallel(...watchTasks) + )); } -addGenerationTask('react', ['Cannot find module \'csstype\'.']); +addGenerationTask('react', ['Cannot find module \'csstype\'.'], false, true, false); addGenerationTask('angular', [ 'Cannot find module \'@angular/core\'.', 'Cannot find module \'@angular/common\'.' ]); +addGenerationTask('vue', [], false, true, false); + gulp.task('generate-components-watch', gulp.series('generate-components', function() { - gulp.watch([SRC], gulp.series('generate-components')); + gulp.watch(SRC, gulp.series('generate-components')); })); diff --git a/build/gulp/generator/ts-configs/tsconfig.json b/build/gulp/generator/ts-configs/tsconfig.json index 3756fc0f3e89..f7e064c71382 100644 --- a/build/gulp/generator/ts-configs/tsconfig.json +++ b/build/gulp/generator/ts-configs/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2016", + "target": "ES2020", "module": "ESNext", "strict": false, "esModuleInterop": false, diff --git a/build/gulp/scss/compiler.js b/build/gulp/scss/compiler.js index f1284b8ab9c2..94f648592a18 100644 --- a/build/gulp/scss/compiler.js +++ b/build/gulp/scss/compiler.js @@ -30,7 +30,7 @@ function compile() { ]); } -gulp.task('compile', gulp.series( +gulp.task('compile-scss', gulp.series( processDataUri, compile )); diff --git a/build/gulp/scss/generator.js b/build/gulp/scss/generator.js index 8226d550333d..23f4974b4420 100644 --- a/build/gulp/scss/generator.js +++ b/build/gulp/scss/generator.js @@ -387,7 +387,7 @@ const fillWidgetColors = (theme) => { }); }; -const makeVariableDefinitionDefault = (content) => content.replace(/(\$.*?)( !default)*;$/gm, '$1 !default;'); +const makeVariableDefinitionDefault = (content) => content.replace(/(\$.*?)( !default)*;(\s)/gm, '$1 !default;$3'); const collectWidgetColorVariables = (content, schemeName) => { const widgetContentRegex = /\/\/\s?(?!TODO)(dx)?(\w.*)([\w\W]*?)(\n\/\/|$)/g; diff --git a/build/gulp/scss/tasks.js b/build/gulp/scss/tasks.js index a122785244a4..14eb4fc0f938 100644 --- a/build/gulp/scss/tasks.js +++ b/build/gulp/scss/tasks.js @@ -13,6 +13,6 @@ gulp.task('generate-scss', gulp.series( 'fix-mixins', 'create-base-widget', 'create-theme-index', - 'compile', + 'compile-scss', 'scss-raw-scss-clean' )); diff --git a/build/gulp/scss/theme-replacements.js b/build/gulp/scss/theme-replacements.js index 2d76fa08683c..d60a17f5f25b 100644 --- a/build/gulp/scss/theme-replacements.js +++ b/build/gulp/scss/theme-replacements.js @@ -181,6 +181,8 @@ module.exports = { { import: '../../base/icons', type: 'index' }, { import: '../list/sizes', type: 'index' }, { import: '../button', type: 'index' }, + { import: '../button/colors', type: 'index' }, + { import: '../menuBase', type: 'index' }, { regex: /@mixin dx-toolbar-item-padding\(\$MATERIAL_TOOLBAR_ITEM_SPACING\),/, replacement: '@include dx-toolbar-item-padding($MATERIAL_TOOLBAR_ITEM_SPACING);' }, { regex: /.dx-toolbar-item-padding\(\$MATERIAL_MOBILE_TOOLBAR_ITEM_SPACING\),/, replacement: '@include dx-toolbar-item-padding($MATERIAL_MOBILE_TOOLBAR_ITEM_SPACING);' }, { regex: /(-bg|-color|: 0|MATERIAL_LIST_ITEM_HEIGHT|MATERIAL_LIST_ITEM_HORIZONTAL_PADDING|4px|2 0|50%),/g, replacement: '$1;' }, diff --git a/build/gulp/vendor.js b/build/gulp/vendor.js index a1421d56c641..c1e4ea168b87 100644 --- a/build/gulp/vendor.js +++ b/build/gulp/vendor.js @@ -18,6 +18,10 @@ const JS_VENDORS = [ path: '/preact/hooks/dist/hooks.js', noUglyFile: true }, + { + path: '/preact/compat/dist/compat.js', + noUglyFile: true + }, { path: '/jquery/dist/jquery.js' }, diff --git a/images/widgets/common/diagram/connector-begin-arrow.svg b/images/widgets/common/diagram/connector-begin-arrow.svg index 1a169940623c..c62bd296da8b 100644 --- a/images/widgets/common/diagram/connector-begin-arrow.svg +++ b/images/widgets/common/diagram/connector-begin-arrow.svg @@ -1,8 +1,8 @@ - + - + diff --git a/images/widgets/common/diagram/connector-begin-filled-triangle.svg b/images/widgets/common/diagram/connector-begin-filled-triangle.svg new file mode 100644 index 000000000000..d2e929734c9f --- /dev/null +++ b/images/widgets/common/diagram/connector-begin-filled-triangle.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/images/widgets/common/diagram/connector-begin-outlined-triangle.svg b/images/widgets/common/diagram/connector-begin-outlined-triangle.svg new file mode 100644 index 000000000000..446aa38da730 --- /dev/null +++ b/images/widgets/common/diagram/connector-begin-outlined-triangle.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/images/widgets/common/diagram/connector-end-arrow.svg b/images/widgets/common/diagram/connector-end-arrow.svg index 198c719750f6..8639c7d27276 100644 --- a/images/widgets/common/diagram/connector-end-arrow.svg +++ b/images/widgets/common/diagram/connector-end-arrow.svg @@ -1,8 +1,8 @@ - - + diff --git a/images/widgets/common/diagram/connector-end-filled-triangle.svg b/images/widgets/common/diagram/connector-end-filled-triangle.svg new file mode 100644 index 000000000000..c85bbf133621 --- /dev/null +++ b/images/widgets/common/diagram/connector-end-filled-triangle.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/images/widgets/common/diagram/connector-end-outlined-triangle.svg b/images/widgets/common/diagram/connector-end-outlined-triangle.svg new file mode 100644 index 000000000000..58f87d2e14b7 --- /dev/null +++ b/images/widgets/common/diagram/connector-end-outlined-triangle.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-horizontal-arrow-end.png b/images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-horizontal-arrow-end.png deleted file mode 100644 index 51074f9623a0..000000000000 Binary files a/images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-horizontal-arrow-end.png and /dev/null differ diff --git a/images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-horizontal-arrow-start.png b/images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-horizontal-arrow-start.png deleted file mode 100644 index 8264ba561b9f..000000000000 Binary files a/images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-horizontal-arrow-start.png and /dev/null differ diff --git a/images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-vertical-arrow-down.png b/images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-vertical-arrow-down.png deleted file mode 100644 index fb815a069fdc..000000000000 Binary files a/images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-vertical-arrow-down.png and /dev/null differ diff --git a/images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-vertical-arrow-up.png b/images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-vertical-arrow-up.png deleted file mode 100644 index 0312f76b4f29..000000000000 Binary files a/images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-vertical-arrow-up.png and /dev/null differ diff --git a/images/widgets/generic/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-end.png b/images/widgets/generic/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-end.png deleted file mode 100644 index fa76da6dfe13..000000000000 Binary files a/images/widgets/generic/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-end.png and /dev/null differ diff --git a/images/widgets/generic/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-start.png b/images/widgets/generic/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-start.png deleted file mode 100644 index 13ac0ea6da19..000000000000 Binary files a/images/widgets/generic/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-start.png and /dev/null differ diff --git a/images/widgets/generic/color-schemes/dark/scrollable/scrollbar-vertical-arrow-down.png b/images/widgets/generic/color-schemes/dark/scrollable/scrollbar-vertical-arrow-down.png deleted file mode 100644 index 801c20e5392f..000000000000 Binary files a/images/widgets/generic/color-schemes/dark/scrollable/scrollbar-vertical-arrow-down.png and /dev/null differ diff --git a/images/widgets/generic/color-schemes/dark/scrollable/scrollbar-vertical-arrow-up.png b/images/widgets/generic/color-schemes/dark/scrollable/scrollbar-vertical-arrow-up.png deleted file mode 100644 index fc764f2ff1d8..000000000000 Binary files a/images/widgets/generic/color-schemes/dark/scrollable/scrollbar-vertical-arrow-up.png and /dev/null differ diff --git a/images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-end.png b/images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-end.png deleted file mode 100644 index 7bac3c610b45..000000000000 Binary files a/images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-end.png and /dev/null differ diff --git a/images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-start.png b/images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-start.png deleted file mode 100644 index 26cbd82845fb..000000000000 Binary files a/images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-start.png and /dev/null differ diff --git a/images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-down.png b/images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-down.png deleted file mode 100644 index e548ba3afe52..000000000000 Binary files a/images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-down.png and /dev/null differ diff --git a/images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-up.png b/images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-up.png deleted file mode 100644 index 8658dcd86d6b..000000000000 Binary files a/images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-up.png and /dev/null differ diff --git a/images/widgets/material/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-end.png b/images/widgets/material/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-end.png deleted file mode 100644 index 7bac3c610b45..000000000000 Binary files a/images/widgets/material/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-end.png and /dev/null differ diff --git a/images/widgets/material/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-start.png b/images/widgets/material/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-start.png deleted file mode 100644 index 26cbd82845fb..000000000000 Binary files a/images/widgets/material/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-start.png and /dev/null differ diff --git a/images/widgets/material/color-schemes/dark/scrollable/scrollbar-vertical-arrow-down.png b/images/widgets/material/color-schemes/dark/scrollable/scrollbar-vertical-arrow-down.png deleted file mode 100644 index e548ba3afe52..000000000000 Binary files a/images/widgets/material/color-schemes/dark/scrollable/scrollbar-vertical-arrow-down.png and /dev/null differ diff --git a/images/widgets/material/color-schemes/dark/scrollable/scrollbar-vertical-arrow-up.png b/images/widgets/material/color-schemes/dark/scrollable/scrollbar-vertical-arrow-up.png deleted file mode 100644 index 8658dcd86d6b..000000000000 Binary files a/images/widgets/material/color-schemes/dark/scrollable/scrollbar-vertical-arrow-up.png and /dev/null differ diff --git a/images/widgets/material/color-schemes/light/scrollable/scrollbar-horizontal-arrow-end.png b/images/widgets/material/color-schemes/light/scrollable/scrollbar-horizontal-arrow-end.png deleted file mode 100644 index 7bac3c610b45..000000000000 Binary files a/images/widgets/material/color-schemes/light/scrollable/scrollbar-horizontal-arrow-end.png and /dev/null differ diff --git a/images/widgets/material/color-schemes/light/scrollable/scrollbar-horizontal-arrow-start.png b/images/widgets/material/color-schemes/light/scrollable/scrollbar-horizontal-arrow-start.png deleted file mode 100644 index 26cbd82845fb..000000000000 Binary files a/images/widgets/material/color-schemes/light/scrollable/scrollbar-horizontal-arrow-start.png and /dev/null differ diff --git a/images/widgets/material/color-schemes/light/scrollable/scrollbar-vertical-arrow-down.png b/images/widgets/material/color-schemes/light/scrollable/scrollbar-vertical-arrow-down.png deleted file mode 100644 index e548ba3afe52..000000000000 Binary files a/images/widgets/material/color-schemes/light/scrollable/scrollbar-vertical-arrow-down.png and /dev/null differ diff --git a/images/widgets/material/color-schemes/light/scrollable/scrollbar-vertical-arrow-up.png b/images/widgets/material/color-schemes/light/scrollable/scrollbar-vertical-arrow-up.png deleted file mode 100644 index 8658dcd86d6b..000000000000 Binary files a/images/widgets/material/color-schemes/light/scrollable/scrollbar-vertical-arrow-up.png and /dev/null differ diff --git a/jest.config.js b/jest.config.js index 569a5d38dcb1..6fecc04644c9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -11,7 +11,11 @@ module.exports = { } }, collectCoverage: true, - collectCoverageFrom: ['./js/renovation/**/*.p.js'], + collectCoverageFrom: [ + './js/renovation/**/*.p.js', + '!./js/renovation/number-box.p.js', + '!./js/renovation/select-box.p.js', + ], coverageDirectory: './testing/jest/code_coverage', coverageThreshold: { './js/renovation/**/*.p.js': { @@ -24,7 +28,7 @@ module.exports = { moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], preset: 'ts-jest', setupFiles: [ - path.join(path.resolve('.'), './testing/jest/setup-enzyme.js'), + path.join(path.resolve('.'), './testing/jest/setup-enzyme.ts'), ], testMatch: [ path.join(path.resolve('.'), './testing/jest/**/*.tests.[jt]s?(x)') diff --git a/js/bundles/modules/parts/widgets-base.js b/js/bundles/modules/parts/widgets-base.js index c63dc08d5019..daddceea96ce 100644 --- a/js/bundles/modules/parts/widgets-base.js +++ b/js/bundles/modules/parts/widgets-base.js @@ -90,8 +90,8 @@ ui.dxDropDownEditor = require('../../../ui/drop_down_editor/ui.drop_down_editor' // Reports // Renovation -ui.Button = require('../../../renovation/dist/button.j'); -ui.Widget = require('../../../renovation/dist/widget.j'); +ui.Button = require('../../../renovation/button.j').default; +ui.Widget = require('../../../renovation/widget.j').default; // Renovation module.exports = ui; diff --git a/js/core/dom_component.js b/js/core/dom_component.js index d5dc75041cfa..75c88fa503c4 100644 --- a/js/core/dom_component.js +++ b/js/core/dom_component.js @@ -437,6 +437,7 @@ const DOMComponent = Component.inherit({ if(anonymousTemplateMeta.name && !anonymousTemplate) { this._options.silent(`integrationOptions.templates.${anonymousTemplateMeta.name}`, anonymousTemplateMeta.template); + this._options.silent('_hasAnonymousTemplateContent', true); } }, diff --git a/js/core/options/utils.js b/js/core/options/utils.js index 84df374783ca..12c0cb8af957 100644 --- a/js/core/options/utils.js +++ b/js/core/options/utils.js @@ -35,3 +35,7 @@ export const getNestedOptionValue = function(optionsObject, name) { cachedGetters[name] = cachedGetters[name] || compileGetter(name); return cachedGetters[name](optionsObject, { functionsAsIs: true }); }; + +export default function createDefaultOptionRules(options = []) { + return options; +} diff --git a/js/core/utils/icon.js b/js/core/utils/icon.js index ef0175f29986..20c39f4767cd 100644 --- a/js/core/utils/icon.js +++ b/js/core/utils/icon.js @@ -3,7 +3,7 @@ import $ from '../../core/renderer'; const ICON_CLASS = 'dx-icon'; const SVG_ICON_CLASS = 'dx-svg-icon'; -const getImageSourceType = (source) => { +export const getImageSourceType = (source) => { if(!source || typeof source !== 'string') { return false; } @@ -27,7 +27,7 @@ const getImageSourceType = (source) => { return false; }; -const getImageContainer = (source) => { +export const getImageContainer = (source) => { switch(getImageSourceType(source)) { case 'image': return $('').attr('src', source).addClass(ICON_CLASS); @@ -41,6 +41,3 @@ const getImageContainer = (source) => { return null; } }; - -exports.getImageSourceType = getImageSourceType; -exports.getImageContainer = getImageContainer; diff --git a/js/docEnums.js b/js/docEnums.js index afb8d913a155..0d7893409fcb 100644 --- a/js/docEnums.js +++ b/js/docEnums.js @@ -1003,7 +1003,7 @@ /** * @typedef {string} Enums.HtmlEditorToolbarItem - * @enum {'background'|'bold'|'color'|'italic'|'link'|'image'|'strike'|'subscript'|'superscript'|'underline'|'blockquote'|'header'|'increaseIndent'|'decreaseIndent'|'orderedList'|'bulletList'|'alignLeft'|'alignCenter'|'alignRight'|'alignJustify'|'codeBlock'|'variable'|'separator'|'undo'|'redo'|'clear'} + * @enum {'background'|'bold'|'color'|'font'|'italic'|'link'|'image'|'size'|'strike'|'subscript'|'superscript'|'underline'|'blockquote'|'header'|'increaseIndent'|'decreaseIndent'|'orderedList'|'bulletList'|'alignLeft'|'alignCenter'|'alignRight'|'alignJustify'|'codeBlock'|'variable'|'separator'|'undo'|'redo'|'clear'} */ /** @@ -1102,7 +1102,7 @@ /** * @typedef {string} Enums.DiagramConnectorLineEnd - * @enum {'none'|'arrow'} + * @enum {'none'|'arrow'|'outlinedTriangle'|'filledTriangle'} */ /** @@ -1121,7 +1121,7 @@ */ /** - * @typedef {string} Enums.DiagramAutoZoom + * @typedef {string} Enums.DiagramAutoZoomMode * @enum {'fitContent'|'fitWidth'|'disabled'} */ diff --git a/js/exporter/exceljs/export_data_grid.js b/js/exporter/exceljs/export_data_grid.js index b7582ce6ec3b..e97298f0ad0c 100644 --- a/js/exporter/exceljs/export_data_grid.js +++ b/js/exporter/exceljs/export_data_grid.js @@ -1,4 +1,4 @@ -import { isDefined, isString, isObject } from '../../core/utils/type'; +import { isDefined, isString, isObject, isDate } from '../../core/utils/type'; import excelFormatConverter from '../excel_format_converter'; import messageLocalization from '../../localization/message'; import { extend } from '../../core/utils/extend'; @@ -84,7 +84,7 @@ function exportDataGrid(options) { if(Object.keys(worksheetViewSettings).indexOf('state') === -1) { extend(worksheetViewSettings, { state: 'frozen', ySplit: cellRange.from.row + dataProvider.getFrozenArea().y - 1 }); } - _setAutoFilter(dataProvider, worksheet, component, cellRange, autoFilterEnabled); + _setAutoFilter(dataProvider, worksheet, cellRange, autoFilterEnabled); } if(Object.keys(worksheetViewSettings).length > 0) { @@ -136,7 +136,12 @@ function _exportRow(rowIndex, cellCount, row, startColumnIndex, dataProvider, cu const gridCell = cellData.cellSourceData; const excelCell = row.getCell(startColumnIndex + cellIndex); - excelCell.value = cellData.value; + + if(isDate(cellData.value)) { + excelCell.value = _convertDateForExcelJS(cellData.value); + } else { + excelCell.value = cellData.value; + } if(isDefined(excelCell.value)) { const { bold, alignment: horizontalAlignment, format, dataType } = styles[dataProvider.getStyleId(rowIndex, cellIndex)]; @@ -170,7 +175,11 @@ function _exportRow(rowIndex, cellCount, row, startColumnIndex, dataProvider, cu } } -function _setAutoFilter(dataProvider, worksheet, component, cellRange, autoFilterEnabled) { +function _convertDateForExcelJS(date) { + return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds())); +} + +function _setAutoFilter(dataProvider, worksheet, cellRange, autoFilterEnabled) { if(autoFilterEnabled) { if(!isDefined(worksheet.autoFilter) && dataProvider.getRowsCount() > 0) { worksheet.autoFilter = cellRange; diff --git a/js/file_management/file_system_item.d.ts b/js/file_management/file_system_item.d.ts index 8fb35a42099f..20215d51a1bf 100644 --- a/js/file_management/file_system_item.d.ts +++ b/js/file_management/file_system_item.d.ts @@ -66,6 +66,14 @@ export default class FileSystemItem { */ isDirectory: boolean; + /** + * @docid FileSystemItemFields.hasSubDirectories + * @type boolean + * @prevFileNamespace DevExpress.fileManagement + * @public + */ + hasSubDirectories: boolean; + /** * @docid FileSystemItemFields.thumbnail * @type string diff --git a/js/file_management/file_system_item.js b/js/file_management/file_system_item.js index 0a2b8aac3469..0065d26eb625 100644 --- a/js/file_management/file_system_item.js +++ b/js/file_management/file_system_item.js @@ -93,7 +93,7 @@ class FileSystemItem { result.dateModified = this.dateModified; result.thumbnail = this.thumbnail; result.tooltipText = this.tooltipText; - result.hasSubDirs = this.hasSubDirs; + result.hasSubDirectories = this.hasSubDirectories; result.dataItem = this.dataItem; return result; } diff --git a/js/file_management/object_provider.js b/js/file_management/object_provider.js index fcaaecee52d9..86b262cdc56f 100644 --- a/js/file_management/object_provider.js +++ b/js/file_management/object_provider.js @@ -66,9 +66,10 @@ class ObjectFileSystemProvider extends FileSystemProviderBase { if(!item) { return; } - this._nameSetter(item.dataItem, name); + const dataItem = this._findDataObject(item); + this._nameSetter(dataItem, name); item.name = name; - item.key = this._ensureDataObjectKey(item.dataItem); + item.key = this._ensureDataObjectKey(dataItem); } createDirectory(parentDir, name) { @@ -83,12 +84,15 @@ class ObjectFileSystemProvider extends FileSystemProviderBase { } moveItems(items, destinationDir) { - const array = this._getDirectoryDataItems(destinationDir.dataItem); + const destinationDataItem = this._findDataObject(destinationDir); + const array = this._getDirectoryDataItems(destinationDataItem); const deferreds = items.map(item => this._executeActionAsDeferred(() => { this._checkAbilityToMoveOrCopyItem(item, destinationDir); + + const dataItem = this._findDataObject(item); this._deleteItem(item); - array.push(item.dataItem); + array.push(dataItem); })); this._updateHasSubDirs(destinationDir); @@ -97,12 +101,14 @@ class ObjectFileSystemProvider extends FileSystemProviderBase { } copyItems(items, destinationDir) { - const array = this._getDirectoryDataItems(destinationDir.dataItem); + const destinationDataItem = this._findDataObject(destinationDir); + const array = this._getDirectoryDataItems(destinationDataItem); const deferreds = items.map(item => this._executeActionAsDeferred(() => { this._checkAbilityToMoveOrCopyItem(item, destinationDir); - const copiedItem = this._createCopy(item.dataItem); + const dataItem = this._findDataObject(item); + const copiedItem = this._createCopy(dataItem); array.push(copiedItem); })); @@ -184,7 +190,8 @@ class ObjectFileSystemProvider extends FileSystemProviderBase { } _getFileContent(file) { - return this._contentGetter(file.dataItem) || ''; + const dataItem = this._findDataObject(file); + return this._contentGetter(dataItem) || ''; } _validateDirectoryExists(directoryInfo) { @@ -197,7 +204,8 @@ class ObjectFileSystemProvider extends FileSystemProviderBase { } _checkAbilityToMoveOrCopyItem(item, destinationDir) { - const itemKey = this._getKeyFromDataObject(item.dataItem, item.parentPath); + const dataItem = this._findDataObject(item); + const itemKey = this._getKeyFromDataObject(dataItem, item.parentPath); const pathInfo = destinationDir.getFullPathInfo(); let currentPath = ''; @@ -220,7 +228,8 @@ class ObjectFileSystemProvider extends FileSystemProviderBase { this._getIsDirSetter(dataObj, isDirectory); this._keySetter(dataObj, String(new Guid())); - const array = this._getDirectoryDataItems(parentDir.dataItem); + const parentDataItem = this._findDataObject(parentDir); + const array = this._getDirectoryDataItems(parentDataItem); array.push(dataObj); if(isDirectory) { @@ -248,17 +257,10 @@ class ObjectFileSystemProvider extends FileSystemProviderBase { } _deleteItem(fileItem) { - const fileItemObj = this._findFileItemObj(fileItem.getFullPathInfo()); - if(!fileItemObj) { - throw { - errorId: fileItem.isDirectory ? ErrorCode.DirectoryNotFound : ErrorCode.FileNotFound, - fileItem: fileItem - }; - } - + const dataItem = this._findDataObject(fileItem); const parentDirDataObj = this._findFileItemObj(fileItem.pathInfo); const array = this._getDirectoryDataItems(parentDirDataObj); - const index = array.indexOf(fileItemObj); + const index = array.indexOf(dataItem); array.splice(index, 1); } @@ -302,6 +304,21 @@ class ObjectFileSystemProvider extends FileSystemProviderBase { }); } + _findDataObject(item) { + if(item.isRoot()) { + return null; + } + + const result = this._findFileItemObj(item.getFullPathInfo()); + if(!result) { + throw { + errorId: item.isDirectory ? ErrorCode.DirectoryNotFound : ErrorCode.FileNotFound, + fileItem: item + }; + } + return result; + } + _findFileItemObj(pathInfo) { if(!Array.isArray(pathInfo)) { pathInfo = [ ]; @@ -345,7 +362,8 @@ class ObjectFileSystemProvider extends FileSystemProviderBase { _updateHasSubDirs(dir) { if(dir && !dir.isRoot()) { - dir.hasSubDirs = this._hasSubDirs(dir.dataItem); + const dataItem = this._findDataObject(dir); + dir.hasSubDirectories = this._hasSubDirs(dataItem); } } diff --git a/js/file_management/provider_base.js b/js/file_management/provider_base.js index 914d9a31bd28..98b172dd55fb 100644 --- a/js/file_management/provider_base.js +++ b/js/file_management/provider_base.js @@ -3,7 +3,7 @@ import { ensureDefined } from '../core/utils/common'; import { deserializeDate } from '../core/utils/date_serialization'; import { each } from '../core/utils/iterator'; import { isPromise } from '../core/utils/type'; -import { Deferred } from '../core/utils/deferred'; +import { Deferred, fromPromise } from '../core/utils/deferred'; import FileSystemItem from './file_system_item'; const DEFAULT_FILE_UPLOAD_CHUNK_SIZE = 200000; @@ -82,7 +82,7 @@ class FileSystemProviderBase { } if(fileItem.isDirectory) { - fileItem.hasSubDirs = this._hasSubDirs(dataObj); + fileItem.hasSubDirectories = this._hasSubDirs(dataObj); } fileItem.key = this._keyGetter(dataObj); @@ -134,7 +134,7 @@ class FileSystemProviderBase { const result = action(); if(isPromise(result)) { - result + fromPromise(result) .done(userResult => deferred.resolve(keepResult && userResult || undefined)) .fail(error => deferred.reject(error)); } else { diff --git a/js/file_management/remote_provider.js b/js/file_management/remote_provider.js index 66626f7c6cae..fb5106737209 100644 --- a/js/file_management/remote_provider.js +++ b/js/file_management/remote_provider.js @@ -41,7 +41,7 @@ class RemoteFileSystemProvider extends FileSystemProviderBase { name }).done(() => { if(parentDir && !parentDir.isRoot()) { - parentDir.hasSubDirs = true; + parentDir.hasSubDirectories = true; } }); } diff --git a/js/localization/intl/number.js b/js/localization/intl/number.js index ae76b5767283..d384a9cd0699 100644 --- a/js/localization/intl/number.js +++ b/js/localization/intl/number.js @@ -1,8 +1,6 @@ /* globals Intl */ import dxConfig from '../../core/config'; import { locale, getValueByClosestLocale } from '../core'; -import dxVersion from '../../core/version'; -import { compare as compareVersions } from '../../core/utils/version'; import openXmlCurrencyFormat from '../open_xml_currency_format'; import accountingFormats from '../cldr-data/accounting_formats'; @@ -88,46 +86,6 @@ module.exports = { return this.callBase.apply(this, arguments); }, - parse: function(text, format) { - if(compareVersions(dxVersion, '17.2.8') >= 0) { - return this.callBase.apply(this, arguments); - } - if(!text) { - return; - } - - if(format && format.parser) { - return format.parser(text); - } - - text = this._normalizeNumber(text, format); - - if(text.length > 15) { - return NaN; - } - - return parseFloat(text); - }, - _normalizeNumber: function(text, format) { - const isExponentialRegexp = /^[-+]?[0-9]*.?[0-9]+([eE][-+]?[0-9]+)+$/; - const legitDecimalSeparator = '.'; - - if(this.convertDigits) { - text = this.convertDigits(text, true); - } - - if(isExponentialRegexp.test(text)) { - return text; - } - - const decimalSeparator = this._getDecimalSeparator(format); - const cleanUpRegexp = new RegExp('[^0-9-\\' + decimalSeparator + ']', 'g'); - - return text.replace(cleanUpRegexp, '').replace(decimalSeparator, legitDecimalSeparator); - }, - _getDecimalSeparator: function(format) { - return getFormatter(format)(0.1)[1]; - }, _getCurrencySymbolInfo: function(currency) { const formatter = getCurrencyFormatter(currency); return this._extractCurrencySymbolInfo(formatter.format(0)); diff --git a/js/localization/messages/de.json b/js/localization/messages/de.json index 5e2cd317f84f..f03b664dd2c8 100644 --- a/js/localization/messages/de.json +++ b/js/localization/messages/de.json @@ -295,6 +295,10 @@ "dxFileManager-dialogRenameItemButtonText": "Speichern", "dxFileManager-dialogCreateDirectoryTitle": "Neues Verzeichnis", "dxFileManager-dialogCreateDirectoryButtonText": "Erstellen", + "dxFileManager-dialogDeleteItemTitle": "TODO", + "dxFileManager-dialogDeleteItemButtonText": "TODO", + "dxFileManager-dialogDeleteItemSingleItemConfirmation": "TODO", + "dxFileManager-dialogDeleteItemMultipleItemsConfirmation": "TODO", "dxFileManager-dialogButtonCancel": "Abbrechen", "dxFileManager-editingCreateSingleItemProcessingMessage": "Ein Verzeichnis wird in {0} erstellt", @@ -490,27 +494,27 @@ "dxDiagram-shapeCardWithImageOnTop": "Karte mit Bild oben", "dxDiagram-shapeCardWithImageOnRight": "Karte mit Bild rechts", - "dxGantt-dialogTitle": "TODO", - "dxGantt-dialogStartTitle": "TODO", - "dxGantt-dialogEndTitle": "TODO", - "dxGantt-dialogProgressTitle": "TODO", - "dxGantt-dialogResourcesTitle": "TODO", - "dxGantt-dialogResourceManagerTitle": "TODO", - "dxGantt-dialogTaskDetailsTitle": "TODO", - "dxGantt-dialogEditResourceListHint": "TODO", - "dxGantt-dialogEditNoResources": "TODO", - "dxGantt-dialogButtonAdd": "TODO", - "dxGantt-contextMenuNewTask": "TODO", - "dxGantt-contextMenuNewSubtask": "TODO", - "dxGantt-contextMenuDeleteTask": "TODO", - "dxGantt-contextMenuDeleteDependency": "TODO", - "dxGantt-dialogTaskDeleteConfirmation": "TODO", - "dxGantt-dialogDependencyDeleteConfirmation": "TODO", - "dxGantt-dialogResourcesDeleteConfirmation": "TODO", - "dxGantt-dialogConstraintCriticalViolationMessage": "TODO", - "dxGantt-dialogConstraintViolationMessage": "TODO", - "dxGantt-dialogCancelOperationMessage": "TODO", - "dxGantt-dialogDeleteDependencyMessage": "TODO", - "dxGantt-dialogMoveTaskAndKeepDependencyMessage": "TODO" + "dxGantt-dialogTitle": "Titel", + "dxGantt-dialogStartTitle": "Beginn", + "dxGantt-dialogEndTitle": "Ende", + "dxGantt-dialogProgressTitle": "Fortschritt", + "dxGantt-dialogResourcesTitle": "Ressourcen", + "dxGantt-dialogResourceManagerTitle": "Ressourcen-Manager", + "dxGantt-dialogTaskDetailsTitle": "Aufgabendetails", + "dxGantt-dialogEditResourceListHint": "Ressourcenliste bearbeiten", + "dxGantt-dialogEditNoResources": "Keine Ressourcen", + "dxGantt-dialogButtonAdd": "Hinzufügen", + "dxGantt-contextMenuNewTask": "Neue Aufgabe", + "dxGantt-contextMenuNewSubtask": "Neue Teilaufgabe", + "dxGantt-contextMenuDeleteTask": "Aufgabe löschen", + "dxGantt-contextMenuDeleteDependency": "Abhängigkeit entfernen", + "dxGantt-dialogTaskDeleteConfirmation": "Abhängigkeiten und Teilaufgaben werden zusammen mit dieser Aufgabe gelöscht. Möchten Sie diese Aufgabe löschen?", + "dxGantt-dialogDependencyDeleteConfirmation": "Möchten Sie die Abhängigkeit von der Aufgabe entfernen?", + "dxGantt-dialogResourcesDeleteConfirmation": "Wenn Sie diese Ressource löschen, wird sie von allen Aufgaben entfernt. Möchten Sie die Ressource löschen? Ressource: {0}", + "dxGantt-dialogConstraintCriticalViolationMessage": "Die Aufgabe, die Sie verschieben möchten, ist mit einer zweiten Aufgabe durch eine Abhängigkeit verbunden. Die Änderung würde gegen Abhängigkeitsregeln verstossen. Wie möchten Sie fortfahren?", + "dxGantt-dialogConstraintViolationMessage": "Die Aufgabe, die Sie verschieben möchten, ist mit einer zweiten Aufgabe durch eine Abhängigkeit verbunden. Wie möchten Sie fortfahren?", + "dxGantt-dialogCancelOperationMessage": "Vorgang abbrechen", + "dxGantt-dialogDeleteDependencyMessage": "Abhängigkeit löschen", + "dxGantt-dialogMoveTaskAndKeepDependencyMessage": "Aufgabe verschieben und Abhängigkeit beibehalten" } } diff --git a/js/localization/messages/en.json b/js/localization/messages/en.json index 504816c3e7be..c450ea1659ef 100644 --- a/js/localization/messages/en.json +++ b/js/localization/messages/en.json @@ -327,6 +327,10 @@ "dxFileManager-dialogRenameItemButtonText": "Save", "dxFileManager-dialogCreateDirectoryTitle": "New directory", "dxFileManager-dialogCreateDirectoryButtonText": "Create", + "dxFileManager-dialogDeleteItemTitle": "Delete", + "dxFileManager-dialogDeleteItemButtonText": "Delete", + "dxFileManager-dialogDeleteItemSingleItemConfirmation": "Are you sure you want to delete {0}?", + "dxFileManager-dialogDeleteItemMultipleItemsConfirmation": "Are you sure you want to delete {0} items?", "dxFileManager-dialogButtonCancel": "Cancel", "dxFileManager-editingCreateSingleItemProcessingMessage": "Creating a directory inside {0}", @@ -384,7 +388,7 @@ "dxDiagram-categoryGeneral": "General", "dxDiagram-categoryFlowchart": "Flowchart", - "dxDiagram-categoryOrgChart": "Organizational Chart", + "dxDiagram-categoryOrgChart": "Org Chart", "dxDiagram-categoryContainers": "Containers", "dxDiagram-categoryCustom": "Custom", diff --git a/js/localization/messages/es.json b/js/localization/messages/es.json index b1e89a5cdb6b..30264725e8f0 100644 --- a/js/localization/messages/es.json +++ b/js/localization/messages/es.json @@ -292,6 +292,10 @@ "dxFileManager-dialogRenameItemButtonText": "TODO", "dxFileManager-dialogCreateDirectoryTitle": "TODO", "dxFileManager-dialogCreateDirectoryButtonText": "TODO", + "dxFileManager-dialogDeleteItemTitle": "TODO", + "dxFileManager-dialogDeleteItemButtonText": "TODO", + "dxFileManager-dialogDeleteItemSingleItemConfirmation": "TODO", + "dxFileManager-dialogDeleteItemMultipleItemsConfirmation": "TODO", "dxFileManager-editingCreateSingleItemProcessingMessage": "TODO", "dxFileManager-editingCreateSingleItemSuccessMessage": "TODO", diff --git a/js/localization/messages/fr.json b/js/localization/messages/fr.json index 4e92d361ca09..1174f7cec278 100644 --- a/js/localization/messages/fr.json +++ b/js/localization/messages/fr.json @@ -325,6 +325,10 @@ "dxFileManager-dialogRenameItemButtonText": "Sauvegarder", "dxFileManager-dialogCreateDirectoryTitle": "Nouveau répertoire", "dxFileManager-dialogCreateDirectoryButtonText": "Créer", + "dxFileManager-dialogDeleteItemTitle": "TODO", + "dxFileManager-dialogDeleteItemButtonText": "TODO", + "dxFileManager-dialogDeleteItemSingleItemConfirmation": "TODO", + "dxFileManager-dialogDeleteItemMultipleItemsConfirmation": "TODO", "dxFileManager-editingCreateSingleItemProcessingMessage": "Créer un répertoire dans {0}", "dxFileManager-editingCreateSingleItemSuccessMessage": "Répertoire créé dans {0}", diff --git a/js/localization/messages/ja.json b/js/localization/messages/ja.json index 199e49d3e185..5e98b33b91d5 100644 --- a/js/localization/messages/ja.json +++ b/js/localization/messages/ja.json @@ -295,6 +295,10 @@ "dxFileManager-dialogRenameItemButtonText": "保存", "dxFileManager-dialogCreateDirectoryTitle": "新しいディレクトリ", "dxFileManager-dialogCreateDirectoryButtonText": "作成", + "dxFileManager-dialogDeleteItemTitle": "TODO", + "dxFileManager-dialogDeleteItemButtonText": "TODO", + "dxFileManager-dialogDeleteItemSingleItemConfirmation": "TODO", + "dxFileManager-dialogDeleteItemMultipleItemsConfirmation": "TODO", "dxFileManager-dialogButtonCancel": "キャンセル", "dxFileManager-editingCreateSingleItemProcessingMessage": "{0} 内にディレクトリを作成中", @@ -490,27 +494,27 @@ "dxDiagram-shapeCardWithImageOnTop": "上部に画像を表示するカード", "dxDiagram-shapeCardWithImageOnRight": "右側に画像を表示するカード", - "dxGantt-dialogTitle": "TODO", - "dxGantt-dialogStartTitle": "TODO", - "dxGantt-dialogEndTitle": "TODO", - "dxGantt-dialogProgressTitle": "TODO", - "dxGantt-dialogResourcesTitle": "TODO", - "dxGantt-dialogResourceManagerTitle": "TODO", - "dxGantt-dialogTaskDetailsTitle": "TODO", - "dxGantt-dialogEditResourceListHint": "TODO", - "dxGantt-dialogEditNoResources": "TODO", - "dxGantt-dialogButtonAdd": "TODO", - "dxGantt-contextMenuNewTask": "TODO", - "dxGantt-contextMenuNewSubtask": "TODO", - "dxGantt-contextMenuDeleteTask": "TODO", - "dxGantt-contextMenuDeleteDependency": "TODO", - "dxGantt-dialogTaskDeleteConfirmation": "TODO", - "dxGantt-dialogDependencyDeleteConfirmation": "TODO", - "dxGantt-dialogResourcesDeleteConfirmation": "TODO", - "dxGantt-dialogConstraintCriticalViolationMessage": "TODO", - "dxGantt-dialogConstraintViolationMessage": "TODO", - "dxGantt-dialogCancelOperationMessage": "TODO", - "dxGantt-dialogDeleteDependencyMessage": "TODO", - "dxGantt-dialogMoveTaskAndKeepDependencyMessage": "TODO" + "dxGantt-dialogTitle": "タイトル", + "dxGantt-dialogStartTitle": "開始日時", + "dxGantt-dialogEndTitle": "終了日時", + "dxGantt-dialogProgressTitle": "進行状況", + "dxGantt-dialogResourcesTitle": "リソース", + "dxGantt-dialogResourceManagerTitle": "リソース マネージャー", + "dxGantt-dialogTaskDetailsTitle": "タスクの詳細", + "dxGantt-dialogEditResourceListHint": "リソース リストの編集", + "dxGantt-dialogEditNoResources": "リソースがありません", + "dxGantt-dialogButtonAdd": "追加", + "dxGantt-contextMenuNewTask": "新しいタスク", + "dxGantt-contextMenuNewSubtask": "新しいサブタスク", + "dxGantt-contextMenuDeleteTask": "タスクを削除", + "dxGantt-contextMenuDeleteDependency": "依存関係を削除", + "dxGantt-dialogTaskDeleteConfirmation": "タスクを削除すると、すべての依存関係とサブタスクが削除されます。このタスクを削除してもよろしいですか?", + "dxGantt-dialogDependencyDeleteConfirmation": "タスクの依存関係を削除してもよろしいですか?", + "dxGantt-dialogResourcesDeleteConfirmation": "リソースを削除すると、リソースが割り当てられているタスクからも削除されます。このリソースを削除してもよろしいですか?リソース: {0}", + "dxGantt-dialogConstraintCriticalViolationMessage": "移動しようとしているタスクは、依存関係により2つ目のタスクとリンクされています。この変更は依存関係ルールと競合するため、どのように続行しますか?", + "dxGantt-dialogConstraintViolationMessage": "移動しようとしているタスクは、依存関係により2つ目のタスクとリンクされています。どのように続行しますか?", + "dxGantt-dialogCancelOperationMessage": "操作をキャンセルする", + "dxGantt-dialogDeleteDependencyMessage": "依存関係を削除する", + "dxGantt-dialogMoveTaskAndKeepDependencyMessage": "タスクを移動して、依存関係を保持する" } } diff --git a/js/localization/messages/ru.json b/js/localization/messages/ru.json index 65e056b07765..8a3361f7c7f4 100644 --- a/js/localization/messages/ru.json +++ b/js/localization/messages/ru.json @@ -295,6 +295,10 @@ "dxFileManager-dialogRenameItemButtonText": "Сохранить", "dxFileManager-dialogCreateDirectoryTitle": "Новая папка", "dxFileManager-dialogCreateDirectoryButtonText": "Создать", + "dxFileManager-dialogDeleteItemTitle": "TODO", + "dxFileManager-dialogDeleteItemButtonText": "TODO", + "dxFileManager-dialogDeleteItemSingleItemConfirmation": "TODO", + "dxFileManager-dialogDeleteItemMultipleItemsConfirmation": "TODO", "dxFileManager-editingCreateSingleItemProcessingMessage": "Создаётся папка в {0}", "dxFileManager-editingCreateSingleItemSuccessMessage": "Создана папка в {0}", @@ -435,8 +439,8 @@ "dxDiagram-uiSearch": "Поиск", "dxDiagram-uiStyle": "Стиль", "dxDiagram-uiLayout": "Компоновка", - "dxDiagram-uiLayoutTree": "Древовидное", - "dxDiagram-uiLayoutLayered": "Многоуровневое", + "dxDiagram-uiLayoutTree": "Древовидная", + "dxDiagram-uiLayoutLayered": "Многоуровневая", "dxDiagram-uiDiagram": "Диаграмма", "dxDiagram-uiText": "Текст", "dxDiagram-uiObject": "Объект", @@ -489,27 +493,27 @@ "dxDiagram-shapeCardWithImageOnTop": "Карточка с изображением сверху", "dxDiagram-shapeCardWithImageOnRight": "Карточка с изображением справа", - "dxGantt-dialogTitle": "TODO", - "dxGantt-dialogStartTitle": "TODO", - "dxGantt-dialogEndTitle": "TODO", - "dxGantt-dialogProgressTitle": "TODO", - "dxGantt-dialogResourcesTitle": "TODO", - "dxGantt-dialogResourceManagerTitle": "TODO", - "dxGantt-dialogTaskDetailsTitle": "TODO", - "dxGantt-dialogEditResourceListHint": "TODO", - "dxGantt-dialogEditNoResources": "TODO", - "dxGantt-dialogButtonAdd": "TODO", - "dxGantt-contextMenuNewTask": "TODO", - "dxGantt-contextMenuNewSubtask": "TODO", - "dxGantt-contextMenuDeleteTask": "TODO", - "dxGantt-contextMenuDeleteDependency": "TODO", - "dxGantt-dialogTaskDeleteConfirmation": "TODO", - "dxGantt-dialogDependencyDeleteConfirmation": "TODO", - "dxGantt-dialogResourcesDeleteConfirmation": "TODO", - "dxGantt-dialogConstraintCriticalViolationMessage": "TODO", - "dxGantt-dialogConstraintViolationMessage": "TODO", - "dxGantt-dialogCancelOperationMessage": "TODO", - "dxGantt-dialogDeleteDependencyMessage": "TODO", - "dxGantt-dialogMoveTaskAndKeepDependencyMessage": "TODO" + "dxGantt-dialogTitle": "Название", + "dxGantt-dialogStartTitle": "Начало", + "dxGantt-dialogEndTitle": "Окончание", + "dxGantt-dialogProgressTitle": "Прогресс", + "dxGantt-dialogResourcesTitle": "Ресурсы", + "dxGantt-dialogResourceManagerTitle": "Управление ресурсами", + "dxGantt-dialogTaskDetailsTitle": "Детали задачи", + "dxGantt-dialogEditResourceListHint": "Редактировать список ресурсов", + "dxGantt-dialogEditNoResources": "Список ресурсов пуст", + "dxGantt-dialogButtonAdd": "Добавить", + "dxGantt-contextMenuNewTask": "Новая задача", + "dxGantt-contextMenuNewSubtask": "Новая подзадача", + "dxGantt-contextMenuDeleteTask": "Удалить задачу", + "dxGantt-contextMenuDeleteDependency": "Удалить зависимость", + "dxGantt-dialogTaskDeleteConfirmation": "Удаление задачи приведет к удалению всех её зависимостей и подзадач. Вы уверены, что вы хотите удалить эту задачу?", + "dxGantt-dialogDependencyDeleteConfirmation": "Вы уверены, что хотите удалить эту зависимость из задачи?", + "dxGantt-dialogResourcesDeleteConfirmation": "Удаление ресурса также удалит его из всех задач, в которых он используется. Вы уверены, что хотите удалить эти ресурсы? Ресурсы: {0}", + "dxGantt-dialogConstraintCriticalViolationMessage": "Задача, которую вы передвигаете, имеет зависимость от другой задачи. Это изменение противоречит правилам валидации. Как вы хотите поступить?", + "dxGantt-dialogConstraintViolationMessage": "Задача, которую вы передвигаете, имеет зависимость от другой задачи. Как вы хотите поступить?", + "dxGantt-dialogCancelOperationMessage": "Отменить операцию", + "dxGantt-dialogDeleteDependencyMessage": "Удалить задачу", + "dxGantt-dialogMoveTaskAndKeepDependencyMessage": "Сохранить зависимость и передвинуть задачу" } } diff --git a/js/renovation/.eslintrc b/js/renovation/.eslintrc new file mode 100644 index 000000000000..7024e2a49fb5 --- /dev/null +++ b/js/renovation/.eslintrc @@ -0,0 +1,8 @@ +{ + "extends": [ + "devextreme/renovation-declarations" + ], + "rules": { + "@typescript-eslint/no-unused-expressions": ["error", { "allowShortCircuit": true, "allowTernary": true }] + } +} diff --git a/js/renovation/button.tsx b/js/renovation/button.tsx index 456050c15d7d..6f7ddd5903b5 100644 --- a/js/renovation/button.tsx +++ b/js/renovation/button.tsx @@ -1,209 +1,266 @@ +import { + Component, + ComponentBindings, + Effect, + Event, + JSXComponent, + Method, + OneWay, + Ref, + Template, +} from 'devextreme-generator/component_declaration/common'; import createDefaultOptionRules from '../core/options/utils'; import devices from '../core/devices'; import noop from './utils/noop'; import themes from '../ui/themes'; import { click } from '../events/short'; import { getImageSourceType } from '../core/utils/icon'; -import { initConfig, showWave, hideWave } from '../ui/widget/utils.ink_ripple'; -import { Component, ComponentBindings, Effect, JSXComponent, OneWay, Ref, Template } from 'devextreme-generator/component_declaration/common'; import Icon from './icon'; -import Widget, { WidgetInput } from './widget'; +import InkRipple from './ink-ripple'; +import Widget from './widget'; +import { BaseWidgetProps } from './utils/base-props'; +import BaseComponent from './preact-wrapper/button'; -const defaultClassNames = ['dx-button']; const stylingModes = ['outlined', 'text', 'contained']; -const getInkRippleConfig = ({ text, icon, type }: ButtonInput) => { - const isOnlyIconButton = !text && icon || type === 'back'; - const config: any = isOnlyIconButton ? { - isCentered: true, - useHoldAnimation: false, - waveSizeCoefficient: 1, - } : {}; +const getInkRippleConfig = ({ text, icon, type }: ButtonProps) => { + const isOnlyIconButton = (!text && icon) || (type === 'back'); + const config: any = isOnlyIconButton ? { + isCentered: true, + useHoldAnimation: false, + waveSizeCoefficient: 1, + } : {}; - return initConfig(config); + return config; }; -const getCssClasses = (model: ButtonInput) => { - const { text, icon, stylingMode, type, iconPosition } = model; - const classNames = defaultClassNames.concat(model.classNames || []); - const isValidStylingMode = stylingMode && stylingModes.indexOf(stylingMode) !== -1; +const getCssClasses = (model: ButtonProps) => { + const { + text, icon, stylingMode, type, iconPosition, + } = model; + const classNames = ['dx-button']; + const isValidStylingMode = stylingMode && stylingModes.indexOf(stylingMode) !== -1; - classNames.push(`dx-button-mode-${isValidStylingMode ? stylingMode : 'contained'}`); - classNames.push(`dx-button-${type || 'normal'}`); + classNames.push(`dx-button-mode-${isValidStylingMode ? stylingMode : 'contained'}`); + classNames.push(`dx-button-${type || 'normal'}`); - text && classNames.push('dx-button-has-text'); - icon && classNames.push('dx-button-has-icon'); - iconPosition !== 'left' && classNames.push('dx-button-icon-right'); + text && classNames.push('dx-button-has-text'); + icon && classNames.push('dx-button-has-icon'); + iconPosition !== 'left' && classNames.push('dx-button-icon-right'); - return classNames.join(' '); + return classNames.join(' '); }; const getAriaLabel = (text, icon) => { - let label = text && text.trim() || icon; + let label = (text && text.trim()) || icon; - if (!text && getImageSourceType(icon) === 'image') { - label = icon.indexOf('base64') === -1 ? icon.replace(/.+\/([^.]+)\..+$/, '$1') : 'Base64'; - } + if (!text && getImageSourceType(icon) === 'image') { + label = icon.indexOf('base64') === -1 ? icon.replace(/.+\/([^.]+)\..+$/, '$1') : 'Base64'; + } - return label ? { label } : {}; + return label ? { label } : {}; }; export const viewFunction = (viewModel: Button) => { - const { icon, iconPosition, template, text } = viewModel.props; - const renderText = !template && text; - const isIconLeft = iconPosition === 'left'; - const leftIcon = !template && isIconLeft; - const rightIcon = !template && !isIconLeft; - const iconComponent = !template && viewModel.iconSource - && ; - - return ; + + return ( + -
- {template && +
+ {template + && ( - } - {leftIcon && iconComponent} - {renderText && - {text} - } - {rightIcon && iconComponent} - {viewModel.props.useSubmitBehavior && - - } -
- ; + )} + {isIconLeft && iconComponent} + {renderText + && {text}} + {!isIconLeft && iconComponent} + {viewModel.props.useSubmitBehavior + && } + {viewModel.props.useInkRipple + && } +
+
+ ); }; @ComponentBindings() -export class ButtonInput extends WidgetInput { - @OneWay() activeStateEnabled?: boolean = true; - @OneWay() classNames?: string[]; - @OneWay() hoverStateEnabled?: boolean = true; - @OneWay() icon?: string = ''; - @OneWay() iconPosition?: string = 'left'; - @OneWay() onClick?: (e: any) => any = noop; - @OneWay() onSubmit?: (e: any) => any = noop; - @OneWay() pressed?: boolean; - @OneWay() stylingMode?: 'outlined' | 'text' | 'contained'; - @Template() template?: any = ''; - @OneWay() text?: string = ''; - @OneWay() type?: string; - @OneWay() useInkRipple?: boolean = false; - @OneWay() useSubmitBehavior?: boolean = false; - @OneWay() validationGroup?: string = undefined; +export class ButtonProps extends BaseWidgetProps { + @OneWay() activeStateEnabled?: boolean = true; + + @OneWay() hoverStateEnabled?: boolean = true; + + @OneWay() icon?: string = ''; + + @OneWay() iconPosition?: string = 'left'; + + @Event({ + actionConfig: { excludeValidators: ['readOnly'] }, + }) + onClick?: (e: any) => any = noop; + + @Event() onSubmit?: (e: any) => any = noop; + + @OneWay() pressed?: boolean; + + @OneWay() stylingMode?: 'outlined' | 'text' | 'contained'; + + @Template({ canBeAnonymous: true }) template?: any = ''; + + @OneWay() text?: string = ''; + + @OneWay() type?: string; + + @OneWay() useInkRipple?: boolean = false; + + @OneWay() useSubmitBehavior?: boolean = false; + + @OneWay() validationGroup?: string = undefined; } -const defaultOptionRules = createDefaultOptionRules([{ - device: () => devices.real().deviceType === 'desktop' && !(devices as any).isSimulator(), - options: { focusStateEnabled: true }, +const defaultOptionRules = createDefaultOptionRules([{ + device: () => devices.real().deviceType === 'desktop' && !(devices as any).isSimulator(), + options: { focusStateEnabled: true }, }, { - device: () => (themes as any).isMaterial(themes.current()), - options: { useInkRipple: true }, + device: () => (themes as any).isMaterial(themes.current()), + options: { useInkRipple: true }, }]); -// tslint:disable-next-line: max-classes-per-file @Component({ - defaultOptionRules, - view: viewFunction, + defaultOptionRules, + jQuery: { + component: BaseComponent, + register: true, + }, + view: viewFunction, }) -export default class Button extends JSXComponent { - @Ref() contentRef!: HTMLDivElement; - @Ref() submitInputRef!: HTMLInputElement; +export default class Button extends JSXComponent { + @Ref() contentRef!: HTMLDivElement; - @Effect() - contentReadyEffect() { - // NOTE: we should trigger this effect on change each - // property upon which onContentReady depends - // (for example, text, icon, etc) - const { onContentReady } = this.props; + @Ref() inkRippleRef!: InkRipple; - onContentReady!({ element: this.contentRef.parentNode }); - } + @Ref() submitInputRef!: HTMLInputElement; - onActive(event: Event) { - const { useInkRipple } = this.props; - const config = getInkRippleConfig(this.props); + @Ref() widgetRef!: Widget; - useInkRipple && showWave(config, { element: this.contentRef, event }); - } + @Effect() + contentReadyEffect() { + // NOTE: we should trigger this effect on change each + // property upon which onContentReady depends + // (for example, text, icon, etc) + const { onContentReady } = this.props; - onInactive(event: Event) { - const { useInkRipple } = this.props; - const config = getInkRippleConfig(this.props); + onContentReady!({ element: this.contentRef.parentNode }); + } - useInkRipple && hideWave(config, { element: this.contentRef, event }); - } + @Method() + focus() { + this.widgetRef.focus(); + } - onWidgetClick(event: Event) { - const { onClick, useSubmitBehavior, validationGroup } = this.props; + onActive(event: Event) { + const { useInkRipple } = this.props; - onClick!({ event, validationGroup }); - useSubmitBehavior && this.submitInputRef.click(); - } + useInkRipple && this.inkRippleRef.showWave({ element: this.contentRef, event }); + } - onWidgetKeyPress(event: Event, { keyName, which }) { - if (keyName === 'space' || which === 'space' || keyName === 'enter' || which === 'enter') { - event.preventDefault(); - this.onWidgetClick(event); - } - } + onInactive(event: Event) { + const { useInkRipple } = this.props; - @Effect() - submitEffect() { - const namespace = 'UIFeedback'; - const { useSubmitBehavior, onSubmit } = this.props; + useInkRipple && this.inkRippleRef.hideWave({ element: this.contentRef, event }); + } - if (useSubmitBehavior) { - click.on(this.submitInputRef, - event => onSubmit!({ event, submitInput: this.submitInputRef }), - { namespace }, - ); + onWidgetClick(event: Event) { + const { onClick, useSubmitBehavior, validationGroup } = this.props; - return () => click.off(this.submitInputRef, { namespace }); - } + onClick!({ event, validationGroup }); + useSubmitBehavior && this.submitInputRef.click(); + } - return null; - } + onWidgetKeyDown(event: Event, options) { + const { onKeyDown } = this.props; + const { keyName, which } = options; - get aria() { - return getAriaLabel(this.props.text, this.props.icon); + const result = onKeyDown?.(event, options); + if (result?.cancel) { + return result; } - get cssClasses():string { - return getCssClasses(this.props); + if (keyName === 'space' || which === 'space' || keyName === 'enter' || which === 'enter') { + event.preventDefault(); + this.onWidgetClick(event); } - get elementAttr() { - return { ...this.props.elementAttr, role: 'button' }; - } + return undefined; + } + + @Effect() + submitEffect() { + const namespace = 'UIFeedback'; + const { useSubmitBehavior, onSubmit } = this.props; - get iconSource(): string { - const { icon, type } = this.props; + if (useSubmitBehavior) { + click.on(this.submitInputRef, + (event) => onSubmit!({ event, submitInput: this.submitInputRef }), + { namespace }); - return (icon || type === 'back') ? (icon || 'back') : ''; + return () => click.off(this.submitInputRef, { namespace }); } + + return undefined; + } + + get aria() { + return getAriaLabel(this.props.text, this.props.icon); + } + + get cssClasses(): string { + return getCssClasses(this.props); + } + + get elementAttr() { + return { ...this.props.elementAttr, role: 'button' }; + } + + get iconSource(): string { + const { icon, type } = this.props; + + return (icon || type === 'back') ? (icon || 'back') : ''; + } + + get inkRippleConfig() { + return getInkRippleConfig(this.props); + } } diff --git a/js/renovation/dist/button.j.jsx b/js/renovation/dist/button.j.jsx deleted file mode 100644 index 16233bc0a315..000000000000 --- a/js/renovation/dist/button.j.jsx +++ /dev/null @@ -1,157 +0,0 @@ -import $ from '../../core/renderer'; -import * as Preact from 'preact'; -import registerComponent from '../../core/component_registrator'; -import ValidationEngine from '../../ui/validation_engine'; -import Widget from '../preact-wrapper/component'; -import { extend } from '../../core/utils/extend'; -import ButtonView from '../button.p'; -import { wrapElement, getInnerActionName, removeDifferentElements } from '../preact-wrapper/utils'; -import { useLayoutEffect } from 'preact/hooks'; -import { getPublicElement } from '../../core/utils/dom'; - -const TEMPLATE_WRAPPER_CLASS = 'dx-template-wrapper'; - -const actions = { - onClick: { excludeValidators: ['readOnly'] }, - onContentReady: { excludeValidators: ['disabled', 'readOnly'] }, -}; - -class Button extends Widget { - getView() { - return ButtonView; - } - - getProps(isFirstRender) { - const props = super.getProps(isFirstRender); - - if(props.template) { - const template = this._getTemplate(props.template); - - props.render = ({ parentRef, ...restProps }) => { - useLayoutEffect(() => { - const $parent = $(parentRef.current); - const $children = $parent.contents(); - - let $template = $(template.render({ - container: getPublicElement($parent), - model: restProps, - })); - - if($template.hasClass(TEMPLATE_WRAPPER_CLASS)) { - $template = wrapElement($parent, $template); - } - const $newChildren = $parent.contents(); - - return () => { - // NOTE: order is important - removeDifferentElements($children, $newChildren); - }; - }, Object.keys(props).map(key => props[key])); - - return (); - }; - } - - Object.keys(actions).forEach((name) => { - props[name] = this.option(getInnerActionName(name)); - }); - - props.validationGroup = ValidationEngine.getGroupConfig(this._findGroup()); - - return props; - } - - _findGroup() { - const $element = this.$element(); - const model = this._modelByElement($element); - const { validationGroup } = this.option(); - - return validationGroup || ValidationEngine.findGroup($element, model); - } - - _getDefaultOptions() { - return extend(super._getDefaultOptions(), { - activeStateEnabled: true, - focusStateEnabled: true, - hoverStateEnabled: true, - icon: '', - iconPosition: 'left', - template: '', - text: '', - useInkRipple: false, - useSubmitBehavior: false, - validationGroup: undefined, - }); - } - - _init() { - super._init(); - - if(this.option('useSubmitBehavior')) { - this.option('onSubmit', this._getSubmitAction()); - } - - Object.keys(actions).forEach((name) => { - this._addAction(name, actions[name]); - }); - } - - _optionChanged(option) { - const { name, value } = option; - if(actions[name]) { - this._addAction(name, actions[name]); - } - - switch(name) { - case 'useSubmitBehavior': - value === true && this.option('onSubmit', this._getSubmitAction()); - break; - case 'onOptionChanged': - super._optionChanged(option); - break; - } - - super._optionChanged(); - } - - _getSubmitAction() { - let needValidate = true; - let validationStatus = 'valid'; - - return this._createAction(({ event, submitInput }) => { - if(needValidate) { - const validationGroup = this._validationGroupConfig; - - if(validationGroup) { - const { status, complete } = validationGroup.validate(); - - validationStatus = status; - - if(status === 'pending') { - needValidate = false; - this.option('disabled', true); - - complete.then(({ status }) => { - needValidate = true; - this.option('disabled', false); - - validationStatus = status; - validationStatus === 'valid' && submitInput.click(); - }); - } - } - } - - validationStatus !== 'valid' && event.preventDefault(); - event.stopPropagation(); - }); - } - - get _validationGroupConfig() { - return ValidationEngine.getGroupConfig(this._findGroup()); - } -} - -registerComponent('Button', Button); - -module.exports = Button; diff --git a/js/renovation/dist/widget.j.jsx b/js/renovation/dist/widget.j.jsx deleted file mode 100644 index f05d6d32eec4..000000000000 --- a/js/renovation/dist/widget.j.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import registerComponent from '../../core/component_registrator'; -import WidgetBase from '../preact-wrapper/component'; -import { extend } from '../../core/utils/extend'; -import WidgetView from '../widget.p'; - -class Widget extends WidgetBase { - getView() { - return WidgetView; - } - - getProps(isFirstRender) { - const props = super.getProps(isFirstRender); - - return props; - } - - _getDefaultOptions() { - return extend(super._getDefaultOptions(), { - focusStateEnabled: true - }); - } -} - -registerComponent('Widget', Widget); - -module.exports = Widget; diff --git a/js/renovation/error-message.tsx b/js/renovation/error-message.tsx index b731cc1876a6..2cdab02380ad 100644 --- a/js/renovation/error-message.tsx +++ b/js/renovation/error-message.tsx @@ -1,20 +1,25 @@ -import { Component, ComponentBindings, JSXComponent, OneWay } from 'devextreme-generator/component_declaration/common'; +import { + Component, ComponentBindings, JSXComponent, OneWay, +} from 'devextreme-generator/component_declaration/common'; -export const viewFunction = ({ props: { message, className } }: ErrorMessage) => ( -
- {message} -
+export const viewFunction = ({ props: { message, className }, restAttributes }: ErrorMessage) => ( +
+ {message} +
); @ComponentBindings() -export class ErrorMessageInput { - @OneWay() className?: string = ''; - @OneWay() message?: string = ''; +export class ErrorMessageProps { + @OneWay() className?: string = ''; + + @OneWay() message?: string = ''; } -// tslint:disable-next-line: max-classes-per-file @Component({ - defaultOptionRules: null, - view: viewFunction, + defaultOptionRules: null, + view: viewFunction, }) -export default class ErrorMessage extends JSXComponent {} +export default class ErrorMessage extends JSXComponent {} diff --git a/js/renovation/icon.tsx b/js/renovation/icon.tsx index 7e7d943663b6..454d8fdeef93 100644 --- a/js/renovation/icon.tsx +++ b/js/renovation/icon.tsx @@ -1,32 +1,34 @@ +import { + Component, ComponentBindings, JSXComponent, OneWay, Fragment, +} from 'devextreme-generator/component_declaration/common'; import { getImageSourceType } from '../core/utils/icon'; -import { Component, ComponentBindings, JSXComponent, OneWay, Fragment } from 'devextreme-generator/component_declaration/common'; -export const viewFunction = ({ sourceType, cssClass, props: { source } }: Icon) => { - return ( - {sourceType === 'dxIcon' && } - {sourceType === 'fontIcon' && } - {sourceType === 'image' && } - {sourceType === 'svg' && {source}} - ); -}; +export const viewFunction = ({ sourceType, cssClass, props: { source } }: Icon) => ( + + {sourceType === 'dxIcon' && } + {sourceType === 'fontIcon' && } + {sourceType === 'image' && } + {sourceType === 'svg' && {source}} + +); @ComponentBindings() -export class IconInput { - @OneWay() position?: string = 'left'; - @OneWay() source?: string = ''; +export class IconProps { + @OneWay() position?: string = 'left'; + + @OneWay() source?: string = ''; } -// tslint:disable-next-line: max-classes-per-file @Component({ - defaultOptionRules: null, - view: viewFunction, + defaultOptionRules: null, + view: viewFunction, }) -export default class Icon extends JSXComponent { - get sourceType() { - return getImageSourceType(this.props.source); - } +export default class Icon extends JSXComponent { + get sourceType() { + return getImageSourceType(this.props.source); + } - get cssClass() { - return this.props.position !== 'left' ? 'dx-icon-right' : ''; - } + get cssClass() { + return this.props.position !== 'left' ? 'dx-icon-right' : ''; + } } diff --git a/js/renovation/ink-ripple.tsx b/js/renovation/ink-ripple.tsx new file mode 100644 index 000000000000..1d2e980f5365 --- /dev/null +++ b/js/renovation/ink-ripple.tsx @@ -0,0 +1,38 @@ +import { + Component, ComponentBindings, JSXComponent, OneWay, Method, +} from 'devextreme-generator/component_declaration/common'; +import { initConfig, showWave, hideWave } from '../ui/widget/utils.ink_ripple'; + +// TODO: remake old ink ripple in new JSX component +export const viewFunction = (model: InkRipple) => ( +
+); + +@ComponentBindings() +export class InkRippleProps { + @OneWay() config?: any = {}; +} + +@Component({ + defaultOptionRules: null, + view: viewFunction, +}) +export default class InkRipple extends JSXComponent { + @Method() + hideWave(event) { + hideWave(this.getConfig, event); + } + + @Method() + showWave(event) { + showWave(this.getConfig, event); + } + + get getConfig() { + const { config } = this.props; + return initConfig(config); + } +} diff --git a/js/renovation/number-box.tsx b/js/renovation/number-box.tsx new file mode 100644 index 000000000000..c6fcb2129ee0 --- /dev/null +++ b/js/renovation/number-box.tsx @@ -0,0 +1,56 @@ +import { + Ref, Effect, Component, ComponentBindings, JSXComponent, OneWay, Event, TwoWay, +} from 'devextreme-generator/component_declaration/common'; +import DxNumberBox from '../ui/number_box'; +import { WidgetProps } from './widget'; + +export const viewFunction = ({ widgetRef }: NumberBox) => (
); + +@ComponentBindings() +export class NumberBoxProps extends WidgetProps { + // props was copied from js\ui\number_box.d.ts + + // buttons?: Array<'clear' | 'spins' | dxTextEditorButton>; + // format?: format; + @OneWay() invalidValueMessage?: string; + + @OneWay() max?: number; + + @OneWay() min?: number; + + @OneWay() mode?: 'number' | 'text' | 'tel'; + + // Needed only for jQuery. Should be auto-generated + // onValueChanged?: ((e: { component?: T, element?: dxElement, model?: any, + // value?: any, previousValue?: any, event?: event }) => any); + @OneWay() showSpinButtons?: boolean; + + @OneWay() step?: number; + + @OneWay() useLargeSpinButtons?: boolean; + + @TwoWay() value?: number; + + @Event() valueChange?: ((value: number) => void) = () => {}; +} + +@Component({ + defaultOptionRules: null, + view: viewFunction, +}) +export default class NumberBox extends JSXComponent { + @Ref() + widgetRef!: HTMLDivElement; + + @Effect() + setupWidget() { + const { valueChange } = this.props; + + new DxNumberBox(this.widgetRef, { // eslint-disable-line no-new + ...this.props as any, + onValueChanged: (e) => { + valueChange!(e.value); + }, + }); + } +} diff --git a/js/renovation/preact-wrapper/button.d.ts b/js/renovation/preact-wrapper/button.d.ts new file mode 100644 index 000000000000..7b0968779962 --- /dev/null +++ b/js/renovation/preact-wrapper/button.d.ts @@ -0,0 +1,13 @@ +/* eslint-disable no-underscore-dangle */ +import Component from './component'; + +export default class Button extends Component { + getAllProps(isFirstRender: boolean): any; + + _init(): any; + + _getSubmitAction(): any; + + _findGroup(): any; +} +/* eslint-enable no-underscore-dangle */ diff --git a/js/renovation/preact-wrapper/button.js b/js/renovation/preact-wrapper/button.js new file mode 100644 index 000000000000..fab660e39eb0 --- /dev/null +++ b/js/renovation/preact-wrapper/button.js @@ -0,0 +1,59 @@ +/* eslint-disable */ +import ValidationEngine from '../../ui/validation_engine'; +import Component from './component'; + +export default class Button extends Component { + _init() { + super._init(); + this._addAction('onSubmit', this._getSubmitAction()); + } + + getAllProps(isFirstRender) { + const props = super.getAllProps(isFirstRender); + props.validationGroup = this._validationGroupConfig; + return props; + } + + _getSubmitAction() { + let needValidate = true; + let validationStatus = 'valid'; + + return this._createAction(({ event, submitInput }) => { + if(needValidate) { + const validationGroup = this._validationGroupConfig; + + if(validationGroup) { + const { status, complete } = validationGroup.validate(); + + validationStatus = status; + + if(status === 'pending') { + needValidate = false; + this.option('disabled', true); + + complete.then(({ status }) => { + needValidate = true; + this.option('disabled', false); + + validationStatus = status; + validationStatus === 'valid' && submitInput.click(); + }); + } + } + } + + validationStatus !== 'valid' && event.preventDefault(); + event.stopPropagation(); + }); + } + + get _validationGroupConfig() { + return ValidationEngine.getGroupConfig(this._findGroup()); + } + + _findGroup() { + const $element = this.$element(); + return this.option('validationGroup') || ValidationEngine.findGroup($element, this._modelByElement($element)); + } +} +/* eslint-enable */ diff --git a/js/renovation/preact-wrapper/component.d.ts b/js/renovation/preact-wrapper/component.d.ts new file mode 100644 index 000000000000..e09972a4876a --- /dev/null +++ b/js/renovation/preact-wrapper/component.d.ts @@ -0,0 +1,38 @@ +/* eslint-disable no-underscore-dangle */ +import DOMComponent from '../../core/dom_component'; + +export default class PreactWrapper extends DOMComponent { + viewRef: any; + + getInstance(): this; + + _initMarkup(): void; + + _render(): void; + + getAllProps(isFirstRender: boolean): any; + + _getActionsMap(): any; + + _init(): any; + + _createViewRef(): any; + + _optionChanged(option: any): any; + + _addAction(name: string, config: any): any; + + _stateChange(name: string): (value: any) => void; + + _createTemplateComponent(props: any, templateOption: any, canBeAnonymous: boolean): any; + + _wrapKeyDownHandler(handler: (event: any, options: any) => any): any; + + // Public API + repaint(): any; + + registerKeyHandler(key: string, handler: (e: any) => any): void; + + setAria(): any; +} +/* eslint-enable no-underscore-dangle */ diff --git a/js/renovation/preact-wrapper/component.js b/js/renovation/preact-wrapper/component.js index dbb447c9eec8..fe2c5a049fb8 100644 --- a/js/renovation/preact-wrapper/component.js +++ b/js/renovation/preact-wrapper/component.js @@ -1,65 +1,190 @@ -import Widget from '../../ui/widget/ui.widget'; +/* eslint-disable */ +import $ from '../../core/renderer'; +import DOMComponent from '../../core/dom_component'; import * as Preact from 'preact'; -import { extend } from '../../core/utils/extend'; -import { getInnerActionName } from './utils'; +import { isEmpty } from '../../core/utils/string'; +import { wrapElement, removeDifferentElements } from '../preact-wrapper/utils'; +import { useLayoutEffect } from 'preact/hooks'; +import { getPublicElement } from '../../core/utils/dom'; -export default class PreactWrapper extends Widget { +const TEMPLATE_WRAPPER_CLASS = 'dx-template-wrapper'; + +export default class PreactWrapper extends DOMComponent { getInstance() { return this; } - getView() { - return null; - } + _initMarkup() { + const isFirstRender = this.$element().children().length === 0; + const hasParent = this.$element().parent().length > 0; + const container = isFirstRender && hasParent ? this.$element().get(0) : undefined; - renderView(options) { - return Preact.h(this.getView(), options); + Preact.render(Preact.h(this._viewComponent, this.getAllProps(isFirstRender)), this.$element().get(0), container); } - _initMarkup() { } - _render() { - this._renderContent(); + // NOTE: see ui.widget + // this._renderContent(); } + // _renderContent() { } - getProps(isFirstRender) { - const options = extend({}, this.option()); + getAllProps(isFirstRender) { + const options = { ...this.option() }; const attributes = this.$element()[0].attributes; + const { width, height } = this.$element()[0].style; if(isFirstRender) { - options.elementAttr = extend(Object.keys(attributes).reduce((a, key) => { - if(attributes[key].specified) { - a[attributes[key].name] = attributes[key].value; - } - return a; - }, {}), options.elementAttr); - } else if(attributes.id) { - // NOTE: workaround to save container id - options.elementAttr = extend({ [attributes.id.name]: attributes.id.value }, options.elementAttr); + options.elementAttr = { + ...Object.keys(attributes).reduce((a, key) => { + if(attributes[key].specified) { + a[attributes[key].name] = attributes[key].value; + } + return a; + }, {}), + ...options.elementAttr + }; + } else { + if(attributes.id) { + // NOTE: workaround to save container id + options.elementAttr = { + [attributes.id.name]: attributes.id.value, + ...options.elementAttr + }; + } + if(attributes.class) { + // NOTE: workaround to save custom classes on type changes + const customClass = attributes.class.value + .split(' ') + .filter(name => name.indexOf('dx-') < 0) + .join(' '); + const classes = options.elementAttr.class ? options.elementAttr.class.concat(customClass) : customClass; + options.elementAttr = { class: classes, ...options.elementAttr }; + } } + if(!isEmpty(width)) { + options.width = width; + } + if(!isEmpty(height)) { + options.height = height; + } + + if(this.viewRef) { + options.ref = this.viewRef; + } + + Object.keys(this._actionsMap).forEach(name => { + options[name] = this._actionsMap[name]; + }); - return options; + return this.getProps && this.getProps(options) || options; } - _renderContent() { - const isFirstRender = this.$element().children().length === 0; - const container = isFirstRender ? this.$element().get(0) : undefined; + _getActionConfigs() { return {}; } + + _init() { + super._init(); + this._actionsMap = {}; + + Object.keys(this._getActionConfigs()).forEach(name => this._addAction(name)); + + this._initWidget && this._initWidget(); + this._supportedKeys = () => ({}); + } - Preact.render(this.renderView(this.getProps(isFirstRender)), this.$element().get(0), container); + _addAction(event, action) { + this._actionsMap[event] = action || this._createActionByOption(event, this._getActionConfigs()[event]); + } + + _createViewRef() { + this.viewRef = Preact.createRef(); } _optionChanged(option) { - if(option) { - super._optionChanged(option); + const { name } = option || {}; + if(name && this._getActionConfigs()[name]) { + this._addAction(name); } + + super._optionChanged(option); this._invalidate(); } - _addAction(name, config) { - this.option(getInnerActionName(name), this._createActionByOption(name, config)); + _stateChange(name) { + return (value) => this.option(name, value); + } + + _createTemplateComponent(props, templateOption, canBeAnonymous) { + if(!templateOption && this.option('_hasAnonymousTemplateContent') && canBeAnonymous) { + templateOption = this._templateManager.anonymousTemplateName; + } + if(!templateOption) { + return; + } + + const template = this._getTemplate(templateOption); + return ({ parentRef, ...restProps }) => { + useLayoutEffect(() => { + const $parent = $(parentRef.current); + const $children = $parent.contents(); + + let $template = $(template.render({ + container: getPublicElement($parent), + model: restProps, + transclude: canBeAnonymous && templateOption === this._templateManager.anonymousTemplateName, + // TODO index + })); + + if($template.hasClass(TEMPLATE_WRAPPER_CLASS)) { + $template = wrapElement($parent, $template); + } + const $newChildren = $parent.contents(); + + return () => { + // NOTE: order is important + removeDifferentElements($children, $newChildren); + }; + }, Object.keys(props).map(key => props[key])); + + return (); + }; + } + + _wrapKeyDownHandler(handler) { + return (event, options) => { + const { originalEvent, keyName, which } = options; + const keys = this._supportedKeys(); + const func = keys[keyName] || keys[which]; + + // NOTE: registered handler has more priority + if(func !== undefined) { + const handler = func.bind(this); + const result = handler(originalEvent, options); + + if(!result) { + event.cancel = true; + return event; + } + } + + // NOTE: make it possible to pass onKeyDown property + return handler?.(event, options); + }; + } + + // Public API + repaint() { + this._refresh(); + } + + registerKeyHandler(key, handler) { + const currentKeys = this._supportedKeys(); + this._supportedKeys = () => ({ ...currentKeys, [key]: handler }); } - _refresh() { - this._renderComponent(); + // NOTE: this method will be deprecated + // aria changes should be defined in declaration or passed through property + setAria() { + throw new Error('"setAria" method is deprecated, use "aria" property instead'); } } +/* eslint-enable */ diff --git a/js/renovation/preact-wrapper/utils.js b/js/renovation/preact-wrapper/utils.js index 1ad434e0eab3..3ff12ea8c6ee 100644 --- a/js/renovation/preact-wrapper/utils.js +++ b/js/renovation/preact-wrapper/utils.js @@ -1,40 +1,40 @@ import { each } from '../../core/utils/iterator'; +const addAttributes = ($element, attributes) => { + each(attributes, (_, { name, value }) => { + if (name === 'class') { + $element.addClass(value); + } else { + $element.attr(name, value); + } + }); +}; + // NOTE: function for jQuery templates export const wrapElement = ($element, $wrapper) => { - const attributes = $wrapper.get(0).attributes; - const children = $wrapper.contents(); + const { attributes } = $wrapper.get(0); + const children = $wrapper.contents(); - each(attributes, (_, { name, value }) => { - if(name === 'class') { - $element.addClass(value); - } else { - $element.attr(name, value); - } - }); + addAttributes($element, attributes); - $wrapper.remove(); - each(children, (_, child) => { - $element.append(child); - }); + $wrapper.remove(); + each(children, (_, child) => { + $element.append(child); + }); - return children; + return children; }; export const removeDifferentElements = ($children, $newChildren) => { - each($newChildren, (_, element) => { - let hasComponent = false; - each($children, (_, oldElement) => { - if(element === oldElement) { - hasComponent = true; - } - }); - if(!hasComponent) { - element.remove(); - } + each($newChildren, (__, element) => { + let hasComponent = false; + each($children, (_, oldElement) => { + if (element === oldElement) { + hasComponent = true; + } }); -}; - -export const getInnerActionName = (actionName) => { - return actionName.charAt(2).toLowerCase() + actionName.substr(3) + 'Action'; + if (!hasComponent) { + element.parentNode.removeChild(element); + } + }); }; diff --git a/js/renovation/select-box.tsx b/js/renovation/select-box.tsx new file mode 100644 index 000000000000..78d3cb751016 --- /dev/null +++ b/js/renovation/select-box.tsx @@ -0,0 +1,41 @@ +import { + Ref, Effect, Component, ComponentBindings, JSXComponent, Event, OneWay, TwoWay, +} from 'devextreme-generator/component_declaration/common'; +import { WidgetProps } from './widget'; +import DataSource, { DataSourceOptions } from '../data/data_source'; +import DxSelectBox from '../ui/select_box'; + +export const viewFunction = ({ widgetRef }: SelectBox) => (
); + +@ComponentBindings() +export class SelectBoxProps extends WidgetProps { + @OneWay() dataSource?: string | Array | DataSource | DataSourceOptions; + + @OneWay() displayExpr?: string; + + @TwoWay() value?: number; + + @OneWay() valueExpr?: string; + + @Event() valueChange?: ((value: number) => void) = () => {}; +} +@Component({ + defaultOptionRules: null, + view: viewFunction, +}) +export default class SelectBox extends JSXComponent { + @Ref() + widgetRef!: HTMLDivElement; + + @Effect() + setupWidget() { + const { valueChange } = this.props; + + new DxSelectBox(this.widgetRef, { // eslint-disable-line no-new + ...this.props as any, + onValueChanged: (e) => { + valueChange!(e.value); + }, + }); + } +} diff --git a/js/renovation/utils/base-props.tsx b/js/renovation/utils/base-props.tsx new file mode 100644 index 000000000000..07c768943c95 --- /dev/null +++ b/js/renovation/utils/base-props.tsx @@ -0,0 +1,39 @@ +import { + Event, OneWay, ComponentBindings, +} from 'devextreme-generator/component_declaration/common'; +import config from '../../core/config'; + +@ComponentBindings() +export class BaseWidgetProps { // eslint-disable-line import/prefer-default-export + @OneWay() accessKey?: string | null = null; + + @OneWay() activeStateEnabled?: boolean = false; + + @OneWay() disabled?: boolean = false; + + @OneWay() elementAttr?: { [name: string]: any }; + + @OneWay() focusStateEnabled?: boolean = false; + + @OneWay() height?: string | number | null = null; + + @OneWay() hint?: string; + + @OneWay() hoverStateEnabled?: boolean = false; + + @Event() onClick?: (e: any) => void; + + @Event({ + actionConfig: { excludeValidators: ['disabled', 'readOnly'] }, + }) onContentReady?: (e: any) => any = (() => {}); + + @Event() onKeyDown?: (e: any, options: any) => any; + + @OneWay() rtlEnabled?: boolean = config().rtlEnabled; + + @OneWay() tabIndex?: number = 0; + + @OneWay() visible?: boolean = true; + + @OneWay() width?: string | number | null = null; +} diff --git a/js/renovation/utils/noop.js b/js/renovation/utils/noop.js index c80045f4c880..5ff8d71cb4e4 100644 --- a/js/renovation/utils/noop.js +++ b/js/renovation/utils/noop.js @@ -1 +1 @@ -export default () => void 0; +export default () => undefined; diff --git a/js/renovation/widget.tsx b/js/renovation/widget.tsx index d7169bdaef8e..b7e4e321f696 100644 --- a/js/renovation/widget.tsx +++ b/js/renovation/widget.tsx @@ -1,349 +1,366 @@ -import config from '../core/config'; import { - Component, - ComponentBindings, - Effect, - Event, - InternalState, - JSXComponent, - OneWay, - React, - Ref, - Slot, + Component, + ComponentBindings, + Effect, + Event, + InternalState, + JSXComponent, + Method, + OneWay, + Ref, + Slot, } from 'devextreme-generator/component_declaration/common'; - import '../events/click'; import '../events/hover'; -import { active, dxClick, focus, hover, keyboard, resize, visibility } from '../events/short'; +import { + active, dxClick, focus, hover, keyboard, resize, visibility, +} from '../events/short'; import { each } from '../core/utils/iterator'; import { extend } from '../core/utils/extend'; import { focusable } from '../ui/widget/selectors'; import { isFakeClickEvent } from '../events/utils'; +import { BaseWidgetProps } from './utils/base-props'; -const getStyles = ({ width, height }) => { - const computedWidth = typeof width === 'function' ? width() : width; - const computedHeight = typeof height === 'function' ? height() : height; +const getStyles = ({ width, height, style }) => { + const computedWidth = typeof width === 'function' ? width() : width; + const computedHeight = typeof height === 'function' ? height() : height; - return { - height: computedHeight ?? void 0, - width: computedWidth ?? void 0, - }; + return { + height: computedHeight ?? undefined, + width: computedWidth ?? undefined, + ...style, + }; }; const setAttribute = (name, value) => { - const result = {}; + const result = {}; - if (value) { - const attrName = (name === 'role' || name === 'id') ? name : `aria-${name}`; + if (value) { + const attrName = (name === 'role' || name === 'id') ? name : `aria-${name}`; - result[attrName] = String(value); - } + result[attrName] = String(value); + } - return result; + return result; }; const getAria = (args) => { - let attrs = {}; + let attrs = {}; - each(args, (name, value) => attrs = { ...attrs, ...setAttribute(name, value) }); + each(args, (name, value) => { + attrs = { ...attrs, ...setAttribute(name, value) }; + }); - return attrs; + return attrs; }; const getAttributes = ({ elementAttr, accessKey }) => { - const attrs = extend({}, elementAttr, accessKey && { accessKey }); + const attrs = extend({}, elementAttr, accessKey && { accessKey }); - delete attrs.class; + delete attrs.class; - return attrs; + return attrs; }; -const getCssClasses = (model: Partial & Partial) => { - const className = ['dx-widget']; - const isFocusable = model.focusStateEnabled && !model.disabled; - const isHoverable = model.hoverStateEnabled && !model.disabled; - - model.className && className.push(model.className); - model.disabled && className.push('dx-state-disabled'); - !model.visible && className.push('dx-state-invisible'); - model._focused && isFocusable && className.push('dx-state-focused'); - model._active && className.push('dx-state-active'); - model._hovered && isHoverable && !model._active && className.push('dx-state-hover'); - model.rtlEnabled && className.push('dx-rtl'); - model.onVisibilityChange && className.push('dx-visibility-change-handler'); - model.elementAttr?.class && className.push(model.elementAttr.class); - - return className.join(' '); +const getCssClasses = (model: Partial & Partial) => { + const className = ['dx-widget']; + const isFocusable = model.focusStateEnabled && !model.disabled; + const isHoverable = model.hoverStateEnabled && !model.disabled; + + model.classes && className.push(model.classes); + model.className && className.push(model.className); + model.disabled && className.push('dx-state-disabled'); + !model.visible && className.push('dx-state-invisible'); + model.focused && isFocusable && className.push('dx-state-focused'); + model.active && className.push('dx-state-active'); + model.hovered && isHoverable && !model.active && className.push('dx-state-hover'); + model.rtlEnabled && className.push('dx-rtl'); + model.onVisibilityChange && className.push('dx-visibility-change-handler'); + model.elementAttr?.class && className.push(model.elementAttr.class); + + return className.join(' '); }; -export const viewFunction = (viewModel: Widget) => { - return ( - - ); -}; +export const viewFunction = (viewModel: Widget) => ( + +); @ComponentBindings() -export class WidgetInput { - @OneWay() _feedbackHideTimeout?: number = 400; - @OneWay() _feedbackShowTimeout?: number = 30; - @OneWay() accessKey?: string | null = null; - @OneWay() activeStateEnabled?: boolean = false; - @OneWay() activeStateUnit?: string; - @OneWay() aria?: any = {}; - @Slot() children?: any; - @OneWay() className?: string | undefined = ''; - @OneWay() clickArgs?: any = {}; - @OneWay() disabled?: boolean = false; - @OneWay() elementAttr?: { [name: string]: any }; - @OneWay() focusStateEnabled?: boolean = false; - @OneWay() height?: string | number | null = null; - @OneWay() hint?: string; - @OneWay() hoverStateEnabled?: boolean = false; - @OneWay() name?: string = ''; - @OneWay() onActive?: (e: any) => any; - @Event() onClick?: (e: any) => void; - @Event() onContentReady?: (e: any) => any = (() => {}); - @OneWay() onDimensionChanged?: () => any; - @OneWay() onInactive?: (e: any) => any; - @OneWay() onKeyboardHandled?: (args: any) => any | undefined; - @OneWay() onKeyPress?: (e: any, options: any) => any; - @OneWay() onVisibilityChange?: (args: boolean) => undefined; - @OneWay() rtlEnabled?: boolean = config().rtlEnabled; - @OneWay() tabIndex?: number = 0; - @OneWay() visible?: boolean = true; - @OneWay() width?: string | number | null = null; +export class WidgetProps extends BaseWidgetProps { + @OneWay() _feedbackHideTimeout?: number = 400; + + @OneWay() _feedbackShowTimeout?: number = 30; + + @OneWay() activeStateUnit?: string; + + @OneWay() aria?: any = {}; + + @Slot() children?: any; + + @OneWay() classes?: string | undefined = ''; + + @OneWay() className?: string = ''; + + @OneWay() name?: string = ''; + + @Event() onActive?: (e: any) => any; + + @Event() onDimensionChanged?: () => any; + + @Event() onInactive?: (e: any) => any; + + @Event() onKeyboardHandled?: (args: any) => any | undefined; + + @Event() onVisibilityChange?: (args: boolean) => undefined; + + @OneWay() style?: { [name: string]: any }; } -// tslint:disable-next-line: max-classes-per-file @Component({ - defaultOptionRules: null, - view: viewFunction, + defaultOptionRules: null, + jQuery: { + register: true, + }, + view: viewFunction, }) -export default class Widget extends JSXComponent { - @InternalState() _active: boolean = false; - @InternalState() _focused: boolean = false; - @InternalState() _hovered: boolean = false; - - @Ref() - widgetRef!: HTMLDivElement; - - @Effect() - accessKeyEffect() { - const namespace = 'UIFeedback'; - const { accessKey, focusStateEnabled, disabled } = this.props; - const isFocusable = focusStateEnabled && !disabled; - const canBeFocusedByKey = isFocusable && accessKey; - - if (canBeFocusedByKey) { - dxClick.on(this.widgetRef, (e) => { - if (isFakeClickEvent(e)) { - e.stopImmediatePropagation(); - this._focused = true; - } - }, { namespace }); - - return () => dxClick.off(this.widgetRef, { namespace }); - } +export default class Widget extends JSXComponent { + @InternalState() active = false; - return null; - } + @InternalState() focused = false; - @Effect() - activeEffect() { - const { - activeStateEnabled, activeStateUnit, disabled, onInactive, - _feedbackShowTimeout, _feedbackHideTimeout, onActive, - } = this.props; - const selector = activeStateUnit; - const namespace = 'UIFeedback'; - - if (activeStateEnabled && !disabled) { - active.on(this.widgetRef, - ({ event }) => { - this._active = true; - onActive?.(event); - }, - ({ event }) => { - this._active = false; - onInactive?.(event); - }, { - hideTimeout: _feedbackHideTimeout, - namespace, - selector, - showTimeout: _feedbackShowTimeout, - }, - ); - - return () => active.off(this.widgetRef, { selector, namespace }); - } - - return null; - } + @InternalState() hovered = false; - @Effect() - clickEffect() { - const { name, clickArgs, onClick } = this.props; - const namespace = name; + @Ref() + widgetRef!: HTMLDivElement; - if (onClick) { - dxClick.on(this.widgetRef, - e => onClick({ ...clickArgs, ...e }), - { namespace }, - ); + @Effect() + accessKeyEffect() { + const namespace = 'UIFeedback'; + const { accessKey, focusStateEnabled, disabled } = this.props; + const isFocusable = focusStateEnabled && !disabled; + const canBeFocusedByKey = isFocusable && accessKey; - return () => dxClick.off(this.widgetRef, { namespace }); + if (canBeFocusedByKey) { + dxClick.on(this.widgetRef, (e) => { + if (isFakeClickEvent(e)) { + e.stopImmediatePropagation(); + this.focused = true; } + }, { namespace }); - return null; + return () => dxClick.off(this.widgetRef, { namespace }); } - @Effect() - focusEffect() { - const { disabled, focusStateEnabled, name } = this.props; - const namespace = `${name}Focus`; - const isFocusable = focusStateEnabled && !disabled; - - if (isFocusable) { - focus.on(this.widgetRef, - e => !e.isDefaultPrevented() && (this._focused = true), - e => !e.isDefaultPrevented() && (this._focused = false), - { - isFocusable: focusable, - namespace, - }, - ); - - return () => focus.off(this.widgetRef, { namespace }); - } + return undefined; + } + + @Effect() + activeEffect() { + const { + activeStateEnabled, activeStateUnit, disabled, onInactive, + _feedbackShowTimeout, _feedbackHideTimeout, onActive, + } = this.props; + const selector = activeStateUnit; + const namespace = 'UIFeedback'; + + if (activeStateEnabled && !disabled) { + active.on(this.widgetRef, + ({ event }) => { + this.active = true; + onActive?.(event); + }, + ({ event }) => { + this.active = false; + onInactive?.(event); + }, { + hideTimeout: _feedbackHideTimeout, + namespace, + selector, + showTimeout: _feedbackShowTimeout, + }); - return null; + return () => active.off(this.widgetRef, { selector, namespace }); } - @Effect() - hoverEffect() { - const namespace = 'UIFeedback'; - const { activeStateUnit, hoverStateEnabled, disabled } = this.props; - const selector = activeStateUnit; - const isHoverable = hoverStateEnabled && !disabled; - - if (isHoverable) { - hover.on(this.widgetRef, - () => !this._active && (this._hovered = true), - () => this._hovered = false, - { selector, namespace }, - ); - - return () => hover.off(this.widgetRef, { selector, namespace }); - } + return undefined; + } - return null; - } + @Effect() + clickEffect() { + const { name, onClick } = this.props; + const namespace = name; - @Effect() - keyboardEffect() { - const { focusStateEnabled, onKeyPress } = this.props; + if (onClick) { + dxClick.on(this.widgetRef, + (e) => onClick(e), + { namespace }); - if (focusStateEnabled || onKeyPress) { - const id = keyboard.on(this.widgetRef, this.widgetRef, - options => onKeyPress!(options.originalEvent, options)); + return () => dxClick.off(this.widgetRef, { namespace }); + } - return () => keyboard.off(id); - } + return undefined; + } + + @Method() + focus() { + focus.trigger(this.widgetRef); + } + + @Effect() + focusEffect() { + const { disabled, focusStateEnabled, name } = this.props; + const namespace = `${name}Focus`; + const isFocusable = focusStateEnabled && !disabled; + + if (isFocusable) { + focus.on(this.widgetRef, + (e) => { !e.isDefaultPrevented() && (this.focused = true); }, + (e) => { !e.isDefaultPrevented() && (this.focused = false); }, + { + isFocusable: focusable, + namespace, + }); - return null; + return () => focus.off(this.widgetRef, { namespace }); } - @Effect() - resizeEffect() { - const namespace = `${this.props.name}VisibilityChange`; - const { onDimensionChanged } = this.props; + return undefined; + } - if (onDimensionChanged) { - resize.on(this.widgetRef, onDimensionChanged, { namespace }); + @Effect() + hoverEffect() { + const namespace = 'UIFeedback'; + const { activeStateUnit, hoverStateEnabled, disabled } = this.props; + const selector = activeStateUnit; + const isHoverable = hoverStateEnabled && !disabled; - return () => resize.off(this.widgetRef, { namespace }); - } + if (isHoverable) { + hover.on(this.widgetRef, + () => { !this.active && (this.hovered = true); }, + () => { this.hovered = false; }, + { selector, namespace }); - return null; + return () => hover.off(this.widgetRef, { selector, namespace }); } - @Effect() - visibilityEffect() { - const { name, onVisibilityChange } = this.props; - const namespace = `${name}VisibilityChange`; + return undefined; + } - if (onVisibilityChange) { - visibility.on(this.widgetRef, - () => onVisibilityChange!(true), - () => onVisibilityChange!(false), - { namespace }, - ); + @Effect() + keyboardEffect() { + const { focusStateEnabled, onKeyDown } = this.props; - return () => visibility.off(this.widgetRef, { namespace }); - } + if (focusStateEnabled || onKeyDown) { + const id = keyboard.on(this.widgetRef, this.widgetRef, + (options) => onKeyDown!(options.originalEvent, options)); - return null; + return () => keyboard.off(id); } - get attributes() { - const { - accessKey, - aria, - disabled, - elementAttr, - focusStateEnabled, - visible, - } = this.props; - - const arias = getAria({ ...aria, disabled, hidden: !visible }); - const attrsWithoutClass = getAttributes({ - accessKey: focusStateEnabled && !disabled && accessKey, - elementAttr, - }); + return undefined; + } - return { ...attrsWithoutClass, ...arias }; - } + @Effect() + resizeEffect() { + const namespace = `${this.props.name}VisibilityChange`; + const { onDimensionChanged } = this.props; - get styles() { - const { width, height } = this.props; + if (onDimensionChanged) { + resize.on(this.widgetRef, onDimensionChanged, { namespace }); - return getStyles({ width, height }); + return () => resize.off(this.widgetRef, { namespace }); } - get cssClasses() { - const { - className, - disabled, - elementAttr, - focusStateEnabled, - hoverStateEnabled, - onVisibilityChange, - rtlEnabled, - visible, - } = this.props; - - return getCssClasses({ - _active: this._active, _focused: this._focused, _hovered: this._hovered, className, - disabled, elementAttr, focusStateEnabled, hoverStateEnabled, - onVisibilityChange, rtlEnabled, visible, - }); - } + return undefined; + } - get tabIndex() { - const { focusStateEnabled, disabled } = this.props; + @Effect() + visibilityEffect() { + const { name, onVisibilityChange } = this.props; + const namespace = `${name}VisibilityChange`; - return focusStateEnabled && !disabled && this.props.tabIndex; + if (onVisibilityChange) { + visibility.on(this.widgetRef, + () => onVisibilityChange!(true), + () => onVisibilityChange!(false), + { namespace }); + + return () => visibility.off(this.widgetRef, { namespace }); } + + return undefined; + } + + get attributes() { + const { + accessKey, + aria, + disabled, + elementAttr, + focusStateEnabled, + visible, + } = this.props; + + const arias = getAria({ ...aria, disabled, hidden: !visible }); + const attrsWithoutClass = getAttributes({ + accessKey: focusStateEnabled && !disabled && accessKey, + elementAttr, + }); + + return { ...attrsWithoutClass, ...arias }; + } + + get styles() { + const { width, height, style } = this.props; + + return getStyles({ width, height, style }); + } + + get cssClasses() { + const { + classes, + className, + disabled, + elementAttr, + focusStateEnabled, + hoverStateEnabled, + onVisibilityChange, + rtlEnabled, + visible, + } = this.props; + + return getCssClasses({ + active: this.active, + focused: this.focused, + hovered: this.hovered, + className, + classes, + disabled, + elementAttr, + focusStateEnabled, + hoverStateEnabled, + onVisibilityChange, + rtlEnabled, + visible, + }); + } + + get tabIndex() { + const { focusStateEnabled, disabled } = this.props; + + return focusStateEnabled && !disabled && this.props.tabIndex; + } } diff --git a/js/ui/autocomplete.js b/js/ui/autocomplete.js index b9771dc372b4..861d0dfc92e1 100644 --- a/js/ui/autocomplete.js +++ b/js/ui/autocomplete.js @@ -3,7 +3,6 @@ const noop = require('../core/utils/common').noop; const registerComponent = require('../core/component_registrator'); const extend = require('../core/utils/extend').extend; const DropDownList = require('./drop_down_editor/ui.drop_down_list'); -const Deferred = require('../core/utils/deferred').Deferred; const AUTOCOMPLETE_CLASS = 'dx-autocomplete'; const AUTOCOMPLETE_POPUP_WRAPPER_CLASS = 'dx-autocomplete-popup-wrapper'; @@ -103,19 +102,10 @@ const Autocomplete = DropDownList.inherit({ this.setAria('autocomplete', 'inline'); }, - _loadValue: function() { - return new Deferred().resolve(this.option('value')); - }, - _displayGetterExpr: function() { return this.option('valueExpr'); }, - _setSelectedItem: function(item) { - this.callBase(item); - this.option('displayValue', this.option('value')); - }, - _popupConfig: function() { return extend(this.callBase(), { closeOnOutsideClick: (function(e) { diff --git a/js/ui/color_box/color_box.js b/js/ui/color_box/color_box.js index f2901913acb0..a5985167c6e8 100644 --- a/js/ui/color_box/color_box.js +++ b/js/ui/color_box/color_box.js @@ -3,7 +3,6 @@ const eventsEngine = require('../../events/core/events_engine'); const Color = require('../../color'); const ColorView = require('./color_view'); const extend = require('../../core/utils/extend').extend; -const isFunction = require('../../core/utils/type').isFunction; const registerComponent = require('../../core/component_registrator'); const DropDownEditor = require('../drop_down_editor/ui.drop_down_editor'); @@ -93,10 +92,6 @@ const ColorBox = DropDownEditor.inherit({ fieldTemplate: null, - // TODO: convert these options to actions and publish them - onApplyButtonClick: null, - onCancelButtonClick: null, - buttonsLocation: 'bottom after' @@ -253,20 +248,12 @@ const ColorBox = DropDownEditor.inherit({ _applyButtonHandler: function() { this._applyNewColor(this._colorView.option('value')); - if(isFunction(this.option('onApplyButtonClick'))) { - this.option('onApplyButtonClick')(); - } - this.callBase(); }, _cancelButtonHandler: function() { this._resetInputValue(); - if(isFunction(this.option('onCancelButtonClick'))) { - this.option('onCancelButtonClick')(); - } - this.callBase(); }, @@ -373,22 +360,11 @@ const ColorBox = DropDownEditor.inherit({ this._popup && this._addPopupBottomClasses(); break; case 'editAlphaChannel': - case 'onCancelButtonClick': - case 'onApplyButtonClick': case 'keyStep': if(this._colorView) { this._colorView.option(name, value); } break; - case 'applyValueMode': - this.callBase(args); - break; - case 'rtlEnabled': - if(this._colorView) { - this._colorView.option(name, value); - } - this.callBase(args); - break; default: this.callBase(args); } diff --git a/js/ui/date_box/ui.date_box.base.js b/js/ui/date_box/ui.date_box.base.js index b440be7c09fb..2ad3032e5e10 100644 --- a/js/ui/date_box/ui.date_box.base.js +++ b/js/ui/date_box/ui.date_box.base.js @@ -544,7 +544,7 @@ const DateBox = DropDownEditor.inherit({ _applyCustomValidation: function(value) { this.validationRequest.fire({ editor: this, - value + value: this._serializeDate(value) }); return this.option('isValid'); @@ -744,8 +744,12 @@ const DateBox = DropDownEditor.inherit({ return dateSerialization.deserializeDate(this.option(optionName)); } + this.option(optionName, this._serializeDate(value)); + }, + + _serializeDate: function(date) { const serializationFormat = this._getSerializationFormat(); - this.option(optionName, dateSerialization.serializeDate(value, serializationFormat)); + return dateSerialization.serializeDate(date, serializationFormat); }, reset: function() { diff --git a/js/ui/diagram.d.ts b/js/ui/diagram.d.ts index e5235fe2e553..803e3e4755ad 100644 --- a/js/ui/diagram.d.ts +++ b/js/ui/diagram.d.ts @@ -17,7 +17,7 @@ import Widget, { export interface dxDiagramOptions extends WidgetOptions { /** * @docid dxDiagramOptions.autoZoomMode - * @type Enums.DiagramAutoZoom + * @type Enums.DiagramAutoZoomMode * @default "disabled" * @prevFileNamespace DevExpress.ui * @public @@ -106,7 +106,7 @@ export interface dxDiagramOptions extends WidgetOptions { * @prevFileNamespace DevExpress.ui * @public */ - nodes?: { autoLayout?: 'off' | 'tree' | 'layered' | { orientation?: 'auto' | 'vertical' | 'horizontal', type?: 'off' | 'tree' | 'layered' }, childrenExpr?: string | ((data: any) => any), containerKeyExpr?: string | ((data: any) => any), dataSource?: Array | DataSource | DataSourceOptions, heightExpr?: string | ((data: any) => any), imageUrlExpr?: string | ((data: any) => any), itemsExpr?: string | ((data: any) => any), keyExpr?: string | ((data: any) => any), leftExpr?: string | ((data: any) => any), lockedExpr?: string | ((data: any) => any), parentKeyExpr?: string | ((data: any) => any), styleExpr?: string | ((data: any) => any), textExpr?: string | ((data: any) => any), textStyleExpr?: string | ((data: any) => any), topExpr?: string | ((data: any) => any), typeExpr?: string | ((data: any) => any), widthExpr?: string | ((data: any) => any), zIndexExpr?: string | ((data: any) => any) }; + nodes?: { autoLayout?: 'off' | 'tree' | 'layered' | { orientation?: 'auto' | 'vertical' | 'horizontal', type?: 'off' | 'tree' | 'layered' }, containerChildrenExpr?: string | ((data: any) => any), containerKeyExpr?: string | ((data: any) => any), dataSource?: Array | DataSource | DataSourceOptions, heightExpr?: string | ((data: any) => any), imageUrlExpr?: string | ((data: any) => any), itemsExpr?: string | ((data: any) => any), keyExpr?: string | ((data: any) => any), leftExpr?: string | ((data: any) => any), lockedExpr?: string | ((data: any) => any), parentKeyExpr?: string | ((data: any) => any), styleExpr?: string | ((data: any) => any), textExpr?: string | ((data: any) => any), textStyleExpr?: string | ((data: any) => any), topExpr?: string | ((data: any) => any), typeExpr?: string | ((data: any) => any), widthExpr?: string | ((data: any) => any), zIndexExpr?: string | ((data: any) => any) }; /** * @docid dxDiagramOptions.hasChanges * @type Boolean diff --git a/js/ui/diagram/diagram.commands_manager.js b/js/ui/diagram/diagram.commands_manager.js index 6db203ac5b66..d5f45f86d07c 100644 --- a/js/ui/diagram/diagram.commands_manager.js +++ b/js/ui/diagram/diagram.commands_manager.js @@ -1,23 +1,30 @@ import { getDiagram } from './diagram.importer'; -import { extend } from '../../core/utils/extend'; import { fileSaver } from '../../exporter/file_saver'; import { isFunction } from '../../core/utils/type'; import { getWindow } from '../../core/utils/window'; +import { extend } from '../../core/utils/extend'; import messageLocalization from '../../localization/message'; -const SEPARATOR = { widget: 'separator' }; +const SEPARATOR = 'separator'; +const SEPARATOR_COMMAND = { widget: SEPARATOR }; const CSS_CLASSES = { - SMALL_SELECT: 'dx-diagram-select-sm', - BUTTON_SELECT: 'dx-diagram-select-b', - BUTTON_COLOR: 'dx-diagram-color-b', + SMALL_EDITOR_ITEM: 'dx-diagram-sm-edit-item', + MEDIUM_EDITOR_ITEM: 'dx-diagram-md-edit-item', + LARGE_EDITOR_ITEM: 'dx-diagram-lg-edit-item', + IMAGE_DROPDOWN_ITEM: 'dx-diagram-image-dropdown-item', + COLOR_EDITOR_ITEM: 'dx-diagram-color-edit-item', + LARGE_ICON_ITEM: 'dx-diagram-lg-icon-item' }; const DiagramCommandsManager = { + SHOW_TOOLBOX_COMMAND_NAME: 'toolbox', + SHOW_PROPERTIES_PANEL_COMMAND_NAME: 'propertiesPanel', + getAllCommands: function() { const { DiagramCommand } = getDiagram(); - return this.allCommands || - (this.allCommands = { - separator: SEPARATOR, + return this._allCommands || + (this._allCommands = { + separator: SEPARATOR_COMMAND, exportSvg: { command: DiagramCommand.ExportSvg, // eslint-disable-line spellcheck/spell-checker @@ -45,33 +52,35 @@ const DiagramCommandsManager = { hint: messageLocalization.format('dxDiagram-commandUndo'), text: messageLocalization.format('dxDiagram-commandUndo'), icon: 'undo', + menuIcon: 'undo' }, redo: { command: DiagramCommand.Redo, hint: messageLocalization.format('dxDiagram-commandRedo'), text: messageLocalization.format('dxDiagram-commandRedo'), icon: 'redo', + menuIcon: 'redo' }, cut: { command: DiagramCommand.Cut, hint: messageLocalization.format('dxDiagram-commandCut'), text: messageLocalization.format('dxDiagram-commandCut'), icon: 'cut', + menuIcon: 'cut' }, copy: { command: DiagramCommand.Copy, hint: messageLocalization.format('dxDiagram-commandCopy'), text: messageLocalization.format('dxDiagram-commandCopy'), - icon: 'copy' + icon: 'copy', + menuIcon: 'copy' }, paste: { command: DiagramCommand.PasteInPosition, hint: messageLocalization.format('dxDiagram-commandPaste'), text: messageLocalization.format('dxDiagram-commandPaste'), icon: 'paste', - getParameter: (diagramContextMenu) => { - return diagramContextMenu.clickPosition; - } + menuIcon: 'paste' }, selectAll: { command: DiagramCommand.SelectAll, @@ -84,38 +93,62 @@ const DiagramCommandsManager = { command: DiagramCommand.Delete, hint: messageLocalization.format('dxDiagram-commandDelete'), text: messageLocalization.format('dxDiagram-commandDelete'), - icon: 'remove' + icon: 'remove', + menuIcon: 'remove' }, fontName: { command: DiagramCommand.FontName, hint: messageLocalization.format('dxDiagram-commandFontName'), + text: messageLocalization.format('dxDiagram-commandFontName'), widget: 'dxSelectBox', - items: ['Arial', 'Arial Black', 'Helvetica', 'Times New Roman', 'Courier New', 'Courier', 'Verdana', 'Georgia', 'Comic Sans MS', 'Trebuchet MS'] + items: [ + 'Arial', + 'Arial Black', + 'Helvetica', + 'Times New Roman', + 'Courier New', + 'Courier', + 'Verdana', + 'Georgia', + 'Comic Sans MS', + 'Trebuchet MS' + ].map(item => { + return { text: item, value: item }; + }), + cssClass: CSS_CLASSES.MEDIUM_EDITOR_ITEM }, fontSize: { command: DiagramCommand.FontSize, hint: messageLocalization.format('dxDiagram-commandFontSize'), + text: messageLocalization.format('dxDiagram-commandFontSize'), widget: 'dxSelectBox', - items: ['8pt', '9pt', '10pt', '11pt', '12pt', '14pt', '16pt', '18pt', '20pt', '22pt', '24pt', '26pt', '28pt', '36pt', '48pt', '72pt'], - cssClass: CSS_CLASSES.SMALL_SELECT + items: [ + 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72 + ].map(item => { + return { text: item + 'pt', value: item + 'pt' }; + }), + cssClass: CSS_CLASSES.SMALL_EDITOR_ITEM }, bold: { command: DiagramCommand.Bold, hint: messageLocalization.format('dxDiagram-commandBold'), text: messageLocalization.format('dxDiagram-commandBold'), - icon: 'bold' + icon: 'bold', + menuIcon: 'bold', }, italic: { command: DiagramCommand.Italic, hint: messageLocalization.format('dxDiagram-commandItalic'), text: messageLocalization.format('dxDiagram-commandItalic'), - icon: 'italic' + icon: 'italic', + menuIcon: 'italic' }, underline: { command: DiagramCommand.Underline, hint: messageLocalization.format('dxDiagram-commandUnderline'), text: messageLocalization.format('dxDiagram-commandUnderline'), - icon: 'underline' + icon: 'underline', + menuIcon: 'underline' }, fontColor: { command: DiagramCommand.FontColor, @@ -123,7 +156,8 @@ const DiagramCommandsManager = { hint: messageLocalization.format('dxDiagram-commandTextColor'), widget: 'dxColorBox', icon: 'dx-icon dx-icon-color', - cssClass: CSS_CLASSES.BUTTON_COLOR + menuIcon: 'dx-icon dx-icon-color', + cssClass: CSS_CLASSES.COLOR_EDITOR_ITEM }, lineColor: { command: DiagramCommand.StrokeColor, @@ -131,7 +165,44 @@ const DiagramCommandsManager = { hint: messageLocalization.format('dxDiagram-commandLineColor'), widget: 'dxColorBox', icon: 'dx-icon dx-icon-background', - cssClass: CSS_CLASSES.BUTTON_COLOR + menuIcon: 'dx-icon dx-icon-background', + cssClass: CSS_CLASSES.COLOR_EDITOR_ITEM + }, + lineWidth: { + command: DiagramCommand.StrokeWidth, + text: messageLocalization.format('dxDiagram-commandLineWidth'), + hint: messageLocalization.format('dxDiagram-commandLineWidth'), + widget: 'dxSelectBox', + items: [ + 1, 2, 3, 4, 5, 6, 7, 8 + ].map(item => { + return { text: item + 'px', value: item.toString() }; + }), + cssClass: CSS_CLASSES.SMALL_EDITOR_ITEM + }, + lineStyle: { + command: DiagramCommand.StrokeStyle, + text: messageLocalization.format('dxDiagram-commandLineStyle'), + hint: messageLocalization.format('dxDiagram-commandLineStyle'), + widget: 'dxSelectBox', + items: [ + { + value: '', + menuIcon: 'dx-diagram-i-line-solid dx-diagram-i', + hint: messageLocalization.format('dxDiagram-commandLineStyleSolid') + }, + { + value: '2,2', + menuIcon: 'dx-diagram-i-line-dotted dx-diagram-i', + hint: messageLocalization.format('dxDiagram-commandLineStyleDotted') + }, + { + value: '6,2', + menuIcon: 'dx-diagram-i-line-dashed dx-diagram-i', + hint: messageLocalization.format('dxDiagram-commandLineStyleDashed') + } + ], + cssClass: CSS_CLASSES.IMAGE_DROPDOWN_ITEM }, fillColor: { command: DiagramCommand.FillColor, @@ -139,25 +210,29 @@ const DiagramCommandsManager = { hint: messageLocalization.format('dxDiagram-commandFillColor'), widget: 'dxColorBox', icon: 'dx-diagram-i dx-diagram-i-button-fill', - cssClass: CSS_CLASSES.BUTTON_COLOR + menuIcon: 'dx-diagram-i dx-diagram-i-menu-fill', + cssClass: CSS_CLASSES.COLOR_EDITOR_ITEM }, textAlignLeft: { command: DiagramCommand.TextLeftAlign, hint: messageLocalization.format('dxDiagram-commandAlignLeft'), text: messageLocalization.format('dxDiagram-commandAlignLeft'), icon: 'alignleft', + menuIcon: 'alignleft' }, textAlignCenter: { command: DiagramCommand.TextCenterAlign, hint: messageLocalization.format('dxDiagram-commandAlignCenter'), text: messageLocalization.format('dxDiagram-commandAlignCenter'), - icon: 'aligncenter' + icon: 'aligncenter', + menuIcon: 'aligncenter' }, textAlignRight: { command: DiagramCommand.TextRightAlign, hint: messageLocalization.format('dxDiagram-commandAlignRight'), text: messageLocalization.format('dxDiagram-commandAlignRight'), - icon: 'alignright' + icon: 'alignright', + menu: 'alignright' }, lock: { command: DiagramCommand.Lock, @@ -212,18 +287,16 @@ const DiagramCommandsManager = { items: [ { value: 0, - icon: 'dx-diagram-i-connector-straight dx-diagram-i', + menuIcon: 'dx-diagram-i-connector-straight dx-diagram-i', hint: messageLocalization.format('dxDiagram-commandConnectorLineStraight') }, { value: 1, - icon: 'dx-diagram-i-connector-orthogonal dx-diagram-i', + menuIcon: 'dx-diagram-i-connector-orthogonal dx-diagram-i', hint: messageLocalization.format('dxDiagram-commandConnectorLineOrthogonal') } ], - displayExpr: 'name', - valueExpr: 'value', - cssClass: CSS_CLASSES.BUTTON_SELECT + cssClass: CSS_CLASSES.IMAGE_DROPDOWN_ITEM }, connectorLineStart: { command: DiagramCommand.ConnectorStartLineEnding, @@ -231,19 +304,27 @@ const DiagramCommandsManager = { items: [ { value: 0, - icon: 'dx-diagram-i-connector-begin-none dx-diagram-i', + menuIcon: 'dx-diagram-i-connector-begin-none dx-diagram-i', hint: messageLocalization.format('dxDiagram-commandConnectorLineNone') }, { value: 1, - icon: 'dx-diagram-i-connector-begin-arrow dx-diagram-i', + menuIcon: 'dx-diagram-i-connector-begin-arrow dx-diagram-i', + hint: messageLocalization.format('dxDiagram-commandConnectorLineArrow') + }, + { + value: 2, + menuIcon: 'dx-diagram-i-connector-begin-outlined-triangle dx-diagram-i', + hint: messageLocalization.format('dxDiagram-commandConnectorLineArrow') + }, + { + value: 3, + menuIcon: 'dx-diagram-i-connector-begin-filled-triangle dx-diagram-i', hint: messageLocalization.format('dxDiagram-commandConnectorLineArrow') } ], - displayExpr: 'name', - valueExpr: 'value', hint: messageLocalization.format('dxDiagram-commandConnectorLineStart'), - cssClass: CSS_CLASSES.BUTTON_SELECT + cssClass: CSS_CLASSES.IMAGE_DROPDOWN_ITEM }, connectorLineEnd: { command: DiagramCommand.ConnectorEndLineEnding, @@ -251,59 +332,91 @@ const DiagramCommandsManager = { items: [ { value: 0, - icon: 'dx-diagram-i-connector-begin-none dx-diagram-i', + menuIcon: 'dx-diagram-i-connector-end-none dx-diagram-i', hint: messageLocalization.format('dxDiagram-commandConnectorLineNone') }, { value: 1, - icon: 'dx-diagram-i-connector-begin-arrow dx-diagram-i', + menuIcon: 'dx-diagram-i-connector-end-arrow dx-diagram-i', hint: messageLocalization.format('dxDiagram-commandConnectorLineArrow') - } - ], - displayExpr: 'name', - valueExpr: 'value', - hint: messageLocalization.format('dxDiagram-commandConnectorLineEnd'), - cssClass: CSS_CLASSES.BUTTON_SELECT - }, - autoLayout: { - widget: 'dxButton', - text: messageLocalization.format('dxDiagram-commandAutoLayout'), - showText: 'always', - items: [ + }, { - text: messageLocalization.format('dxDiagram-commandAutoLayoutTree'), - items: [ - { - command: DiagramCommand.AutoLayoutTreeVertical, - text: messageLocalization.format('dxDiagram-commandAutoLayoutVertical') - }, - { - command: DiagramCommand.AutoLayoutTreeHorizontal, - text: messageLocalization.format('dxDiagram-commandAutoLayoutHorizontal') - } - ] + value: 2, + menuIcon: 'dx-diagram-i-connector-end-outlined-triangle dx-diagram-i', + hint: messageLocalization.format('dxDiagram-commandConnectorLineArrow') }, { - text: messageLocalization.format('dxDiagram-commandAutoLayoutLayered'), - items: [ - { - command: DiagramCommand.AutoLayoutLayeredVertical, - text: messageLocalization.format('dxDiagram-commandAutoLayoutVertical') - }, - { - command: DiagramCommand.AutoLayoutLayeredHorizontal, - text: messageLocalization.format('dxDiagram-commandAutoLayoutHorizontal') - } - ] + value: 3, + menuIcon: 'dx-diagram-i-connector-end-filled-triangle dx-diagram-i', + hint: messageLocalization.format('dxDiagram-commandConnectorLineArrow') } - ] + ], + hint: messageLocalization.format('dxDiagram-commandConnectorLineEnd'), + cssClass: CSS_CLASSES.IMAGE_DROPDOWN_ITEM + }, + layoutTreeTopToBottom: { + command: DiagramCommand.AutoLayoutTreeVertical, + text: messageLocalization.format('dxDiagram-commandLayoutTopToBottom'), + hint: messageLocalization.format('dxDiagram-commandLayoutTopToBottom'), + icon: 'dx-diagram-i-button-layout-tree-tb dx-diagram-i', + cssClass: CSS_CLASSES.LARGE_ICON_ITEM + }, + layoutTreeBottomToTop: { + command: DiagramCommand.AutoLayoutTreeVerticalBottomToTop, + text: messageLocalization.format('dxDiagram-commandLayoutBottomToTop'), + hint: messageLocalization.format('dxDiagram-commandLayoutBottomToTop'), + icon: 'dx-diagram-i-button-layout-tree-bt dx-diagram-i', + cssClass: CSS_CLASSES.LARGE_ICON_ITEM + }, + layoutTreeLeftToRight: { + command: DiagramCommand.AutoLayoutTreeHorizontal, + text: messageLocalization.format('dxDiagram-commandLayoutLeftToRight'), + hint: messageLocalization.format('dxDiagram-commandLayoutLeftToRight'), + icon: 'dx-diagram-i-button-layout-tree-lr dx-diagram-i', + cssClass: CSS_CLASSES.LARGE_ICON_ITEM + }, + layoutTreeRightToLeft: { + command: DiagramCommand.AutoLayoutTreeHorizontalRightToLeft, + text: messageLocalization.format('dxDiagram-commandLayoutRightToLeft'), + hint: messageLocalization.format('dxDiagram-commandLayoutRightToLeft'), + icon: 'dx-diagram-i-button-layout-tree-rl dx-diagram-i', + cssClass: CSS_CLASSES.LARGE_ICON_ITEM + }, + layoutLayeredTopToBottom: { + command: DiagramCommand.AutoLayoutLayeredVertical, + text: messageLocalization.format('dxDiagram-commandLayoutTopToBottom'), + hint: messageLocalization.format('dxDiagram-commandLayoutTopToBottom'), + icon: 'dx-diagram-i-button-layout-layered-tb dx-diagram-i', + cssClass: CSS_CLASSES.LARGE_ICON_ITEM + }, + layoutLayeredBottomToTop: { + command: DiagramCommand.AutoLayoutLayeredVerticalBottomToTop, + text: messageLocalization.format('dxDiagram-commandLayoutBottomToTop'), + hint: messageLocalization.format('dxDiagram-commandLayoutBottomToTop'), + icon: 'dx-diagram-i-button-layout-layered-bt dx-diagram-i', + cssClass: CSS_CLASSES.LARGE_ICON_ITEM + }, + layoutLayeredLeftToRight: { + command: DiagramCommand.AutoLayoutLayeredHorizontal, + text: messageLocalization.format('dxDiagram-commandLayoutLeftToRight'), + hint: messageLocalization.format('dxDiagram-commandLayoutLeftToRight'), + icon: 'dx-diagram-i-button-layout-layered-lr dx-diagram-i', + cssClass: CSS_CLASSES.LARGE_ICON_ITEM + }, + layoutLayeredRightToLeft: { + command: DiagramCommand.AutoLayoutLayeredHorizontalRightToLeft, + text: messageLocalization.format('dxDiagram-commandLayoutRightToLeft'), + hint: messageLocalization.format('dxDiagram-commandLayoutRightToLeft'), + icon: 'dx-diagram-i-button-layout-layered-rl dx-diagram-i', + cssClass: CSS_CLASSES.LARGE_ICON_ITEM }, fullScreen: { command: DiagramCommand.Fullscreen, hint: messageLocalization.format('dxDiagram-commandFullscreen'), text: messageLocalization.format('dxDiagram-commandFullscreen'), icon: 'dx-diagram-i dx-diagram-i-button-fullscreen', - cssClass: CSS_CLASSES.BUTTON_COLOR + menuIcon: 'dx-diagram-i dx-diagram-i-menu-fullscreen', + cssClass: CSS_CLASSES.COLOR_EDITOR_ITEM }, units: { @@ -342,8 +455,9 @@ const DiagramCommandsManager = { hint: messageLocalization.format('dxDiagram-commandPageSize'), text: messageLocalization.format('dxDiagram-commandPageSize'), widget: 'dxSelectBox', - getValue: (v) => JSON.parse(v), - setValue: (v) => JSON.stringify(v) + cssClass: CSS_CLASSES.LARGE_EDITOR_ITEM, + getCommandValue: (v) => JSON.parse(v), + getEditorValue: (v) => JSON.stringify(v) }, pageOrientation: { command: DiagramCommand.PageLandscape, @@ -351,214 +465,370 @@ const DiagramCommandsManager = { text: messageLocalization.format('dxDiagram-commandPageOrientation'), widget: 'dxSelectBox', items: [ - { value: true, title: messageLocalization.format('dxDiagram-commandPageOrientationLandscape') }, - { value: false, title: messageLocalization.format('dxDiagram-commandPageOrientationPortrait') } - ] + { value: true, text: messageLocalization.format('dxDiagram-commandPageOrientationLandscape') }, + { value: false, text: messageLocalization.format('dxDiagram-commandPageOrientationPortrait') } + ], + cssClass: CSS_CLASSES.MEDIUM_EDITOR_ITEM }, pageColor: { command: DiagramCommand.PageColor, hint: messageLocalization.format('dxDiagram-commandPageColor'), text: messageLocalization.format('dxDiagram-commandPageColor'), widget: 'dxColorBox', + icon: 'dx-diagram-i dx-diagram-i-button-fill', + menuIcon: 'dx-diagram-i dx-diagram-i-menu-fill', + cssClass: CSS_CLASSES.COLOR_EDITOR_ITEM }, zoomLevel: { command: DiagramCommand.ZoomLevel, hint: messageLocalization.format('dxDiagram-commandZoomLevel'), text: messageLocalization.format('dxDiagram-commandZoomLevel'), - widget: 'dxSelectBox' - }, - autoZoom: { - command: DiagramCommand.ToggleAutoZoom, - hint: messageLocalization.format('dxDiagram-commandAutoZoom'), - text: messageLocalization.format('dxDiagram-commandAutoZoom'), - widget: 'dxCheckBox' + widget: 'dxTextBox', + items: [ + SEPARATOR_COMMAND, + { + command: DiagramCommand.FitToScreen, + hint: messageLocalization.format('dxDiagram-commandFitToContent'), + text: messageLocalization.format('dxDiagram-commandFitToContent'), + }, + { + command: DiagramCommand.FitToWidth, + hint: messageLocalization.format('dxDiagram-commandFitToWidth'), + text: messageLocalization.format('dxDiagram-commandFitToWidth'), + }, + SEPARATOR_COMMAND, + { + command: DiagramCommand.AutoZoomToContent, + hint: messageLocalization.format('dxDiagram-commandAutoZoomByContent'), + text: messageLocalization.format('dxDiagram-commandAutoZoomByContent'), + }, + { + command: DiagramCommand.AutoZoomToWidth, + hint: messageLocalization.format('dxDiagram-commandAutoZoomByWidth'), + text: messageLocalization.format('dxDiagram-commandAutoZoomByWidth'), + } + ], + getEditorDisplayValue: (v) => { + return Math.round(v * 100) + '%'; + }, + cssClass: CSS_CLASSES.SMALL_EDITOR_ITEM + }, + // Custom commands + showToolbox: { + command: this.SHOW_TOOLBOX_COMMAND_NAME, + iconChecked: 'dx-diagram-i dx-diagram-i-button-toolbox-close', + iconUnchecked: 'dx-diagram-i dx-diagram-i-button-toolbox-open', + hint: messageLocalization.format('dxDiagram-uiShowToolbox'), + text: messageLocalization.format('dxDiagram-uiShowToolbox') + }, + showPropertiesPanel: { + command: this.SHOW_PROPERTIES_PANEL_COMMAND_NAME, + iconChecked: 'close', + iconUnchecked: 'dx-diagram-i dx-diagram-i-button-properties-panel-open', + hint: messageLocalization.format('dxDiagram-uiProperties'), + text: messageLocalization.format('dxDiagram-uiProperties') } }); }, - getMainToolbarCommands: function(commands) { + getMainToolbarCommands: function(commands, excludeCommands) { const allCommands = this.getAllCommands(); - const mainToolbarCommands = commands ? this._getCustomCommands(allCommands, commands) : + const mainToolbarCommands = commands ? this._getPreparedCommands(allCommands, commands) : this._getDefaultMainToolbarCommands(allCommands); - return this._prepareToolbarCommands(mainToolbarCommands); + return this._prepareToolbarCommands(mainToolbarCommands, excludeCommands); }, _getDefaultMainToolbarCommands: function(allCommands) { - return [ - allCommands['undo'], - allCommands['redo'], - allCommands['separator'], - allCommands['fontName'], - allCommands['fontSize'], - allCommands['separator'], - allCommands['bold'], - allCommands['italic'], - allCommands['underline'], - allCommands['separator'], - allCommands['fontColor'], - allCommands['lineColor'], - allCommands['fillColor'], - allCommands['separator'], - allCommands['textAlignLeft'], - allCommands['textAlignCenter'], - allCommands['textAlignRight'], - allCommands['separator'], - allCommands['connectorLineType'], - allCommands['connectorLineStart'], - allCommands['connectorLineEnd'], - allCommands['separator'], - allCommands['autoLayout'], - ]; + return this._defaultMainToolbarCommands || + (this._defaultMainToolbarCommands = [ + allCommands['undo'], + allCommands['redo'], + allCommands['separator'], + allCommands['fontName'], + allCommands['fontSize'], + allCommands['bold'], + allCommands['italic'], + allCommands['underline'], + allCommands['separator'], + allCommands['lineWidth'], + allCommands['lineStyle'], + allCommands['separator'], + allCommands['fontColor'], + allCommands['lineColor'], + allCommands['fillColor'], + allCommands['separator'], + allCommands['textAlignLeft'], + allCommands['textAlignCenter'], + allCommands['textAlignRight'], + allCommands['separator'], + allCommands['connectorLineType'], + allCommands['connectorLineStart'], + allCommands['connectorLineEnd'], + allCommands['separator'], + { + text: messageLocalization.format('dxDiagram-uiLayout'), + showText: 'always', + items: [ + { + text: messageLocalization.format('dxDiagram-uiLayoutTree'), + items: [ + allCommands['layoutTreeTopToBottom'], + allCommands['layoutTreeBottomToTop'], + allCommands['layoutTreeLeftToRight'], + allCommands['layoutTreeRightToLeft'] + ] + }, + { + text: messageLocalization.format('dxDiagram-uiLayoutLayered'), + items: [ + allCommands['layoutLayeredTopToBottom'], + allCommands['layoutLayeredBottomToTop'], + allCommands['layoutLayeredLeftToRight'], + allCommands['layoutLayeredRightToLeft'] + ] + } + ] + } + ]); }, - getHistoryToolbarCommands: function(commands) { + getHistoryToolbarCommands: function(commands, excludeCommands) { const allCommands = this.getAllCommands(); - const historyToolbarCommands = commands ? this._getCustomCommands(allCommands, commands) : + const historyToolbarCommands = commands ? this._getPreparedCommands(allCommands, commands) : this._getDefaultHistoryToolbarCommands(allCommands); - return this._prepareToolbarCommands(historyToolbarCommands); + return this._prepareToolbarCommands(historyToolbarCommands, excludeCommands); }, _getDefaultHistoryToolbarCommands: function(allCommands) { - return [ - allCommands['undo'], - allCommands['separator'], - allCommands['redo'] - ]; + return this._defaultHistoryToolbarCommands || + (this._defaultHistoryToolbarCommands = [ + allCommands['undo'], + allCommands['redo'], + allCommands['separator'], + allCommands['showToolbox'] + ]); }, - getViewToolbarCommands: function(commands) { + getViewToolbarCommands: function(commands, excludeCommands) { const allCommands = this.getAllCommands(); - const viewToolbarCommands = commands ? this._getCustomCommands(allCommands, commands) : + const viewToolbarCommands = commands ? this._getPreparedCommands(allCommands, commands) : this._getDefaultViewToolbarCommands(allCommands); - return this._prepareToolbarCommands(viewToolbarCommands); + return this._prepareToolbarCommands(viewToolbarCommands, excludeCommands); }, _getDefaultViewToolbarCommands: function(allCommands) { - return [ - allCommands['zoomLevel'], - allCommands['separator'], - allCommands['fullScreen'], - allCommands['separator'], - { - widget: 'dxButton', - icon: 'export', - text: messageLocalization.format('dxDiagram-commandExport'), - hint: messageLocalization.format('dxDiagram-commandExport'), - items: [ - allCommands['exportSvg'], - allCommands['exportPng'], - allCommands['exportJpg'] - ] - }, - { - icon: 'preferences', - hint: messageLocalization.format('dxDiagram-commandProperties'), - text: messageLocalization.format('dxDiagram-commandProperties'), - items: [ - allCommands['units'], - allCommands['separator'], - allCommands['showGrid'], - allCommands['snapToGrid'], - allCommands['gridSize'], - allCommands['separator'], - allCommands['simpleView'] - ] - } - ]; + return this._defaultViewToolbarCommands || + (this._defaultViewToolbarCommands = [ + allCommands['zoomLevel'], + allCommands['separator'], + allCommands['fullScreen'], + allCommands['separator'], + { + widget: 'dxButton', + icon: 'export', + text: messageLocalization.format('dxDiagram-uiExport'), + hint: messageLocalization.format('dxDiagram-uiExport'), + items: [ + allCommands['exportSvg'], + allCommands['exportPng'], + allCommands['exportJpg'] + ] + }, + { + icon: 'preferences', + hint: messageLocalization.format('dxDiagram-uiSettings'), + text: messageLocalization.format('dxDiagram-uiSettings'), + items: [ + allCommands['units'], + allCommands['separator'], + allCommands['showGrid'], + allCommands['snapToGrid'], + allCommands['gridSize'], + allCommands['separator'], + allCommands['simpleView'], + allCommands['showToolbox'] + ] + } + ]); + }, + getPropertiesToolbarCommands: function(commands, excludeCommands) { + const allCommands = this.getAllCommands(); + const propertiesCommands = commands ? this._getPreparedCommands(allCommands, commands) : + this._getDefaultPropertiesToolbarCommands(allCommands); + return this._prepareToolbarCommands(propertiesCommands, excludeCommands); + }, + _getDefaultPropertiesToolbarCommands: function(allCommands) { + return this._defaultPropertiesToolbarCommands || + (this._defaultPropertiesToolbarCommands = [ + allCommands['showPropertiesPanel'] + ]); }, - getDefaultPropertyPanelCommandGroups: function() { - return [ - { commands: ['pageSize', 'pageOrientation', 'pageColor'] } - ]; + _getDefaultPropertyPanelCommandGroups: function() { + return this._defaultPropertyPanelCommandGroups || + (this._defaultPropertyPanelCommandGroups = [ + { + title: messageLocalization.format('dxDiagram-uiStyle'), + groups: [ + { + title: messageLocalization.format('dxDiagram-uiText'), + commands: ['fontName', 'fontSize', 'bold', 'italic', 'underline', 'textAlignLeft', 'textAlignCenter', 'textAlignRight', 'fontColor'] + }, + { + title: messageLocalization.format('dxDiagram-uiObject'), + commands: ['lineStyle', 'lineWidth', 'lineColor', 'fillColor'] + }, + { + title: messageLocalization.format('dxDiagram-uiConnector'), + commands: ['connectorLineType', 'connectorLineStart', 'connectorLineEnd'] + } + ] + }, + { + title: messageLocalization.format('dxDiagram-uiLayout'), + groups: [ + { + title: messageLocalization.format('dxDiagram-uiLayoutLayered'), + commands: [ 'layoutLayeredTopToBottom', 'layoutLayeredBottomToTop', 'layoutLayeredLeftToRight', 'layoutLayeredRightToLeft'] + }, + { + title: messageLocalization.format('dxDiagram-uiLayoutTree'), + commands: [ 'layoutTreeTopToBottom', 'layoutTreeBottomToTop', 'layoutTreeLeftToRight', 'layoutTreeRightToLeft'] + } + ] + }, + { + title: messageLocalization.format('dxDiagram-uiDiagram'), + groups: [ + { + title: messageLocalization.format('dxDiagram-uiPage'), + commands: ['pageSize', 'pageOrientation', 'pageColor'] + } + ] + } + ]); }, - _getPropertyPanelCommandsByGroups: function(groups) { + _preparePropertyPanelGroups: function(groups) { const allCommands = this.getAllCommands(); const result = []; - groups.forEach(function(g, gi) { - g.commands.forEach(function(cn, ci) { - result.push(extend({ - beginGroup: gi > 0 && ci === 0 - }, allCommands[cn])); + groups.forEach(g => { + let commands = g.commands; + if(commands) { + commands = this._getPreparedCommands(allCommands, commands); + commands = this._prepareToolbarCommands(commands); + } + + let subGroups; + if(g.groups) { + subGroups = []; + g.groups.forEach(sg => { + let subCommands = sg.commands; + if(subCommands) { + subCommands = this._getPreparedCommands(allCommands, subCommands); + subCommands = this._prepareToolbarCommands(subCommands); + } + subGroups.push({ + title: sg.title, + commands: subCommands + }); + }); + } + result.push({ + title: g.title, + commands: commands, + groups: subGroups }); }); return result; }, - getPropertyPanelCommands: function(commandGroups) { - commandGroups = commandGroups || this.getDefaultPropertyPanelCommandGroups(); - return this._getPropertyPanelCommandsByGroups(commandGroups); + getPropertyPanelCommandTabs: function(commandGroups) { + commandGroups = commandGroups || this._getDefaultPropertyPanelCommandGroups(); + return this._preparePropertyPanelGroups(commandGroups); }, getContextMenuCommands: function(commands) { const allCommands = this.getAllCommands(); - const contextMenuCommands = commands ? this._getCustomCommands(allCommands, commands) : + const contextMenuCommands = commands ? this._getPreparedCommands(allCommands, commands) : this._getDefaultContextMenuCommands(allCommands); return this._prepareContextMenuCommands(contextMenuCommands); }, _getDefaultContextMenuCommands: function(allCommands) { - return [ - allCommands['cut'], - allCommands['copy'], - allCommands['paste'], - allCommands['delete'], - allCommands['separator'], - allCommands['selectAll'], - allCommands['separator'], - allCommands['bringToFront'], - allCommands['sendToBack'], - allCommands['separator'], - allCommands['lock'], - allCommands['unlock'], - allCommands['separator'], - allCommands['insertShapeImage'], - allCommands['editShapeImage'], - allCommands['deleteShapeImage'] - ]; + return this._defaultContextMenuCommands || + (this._defaultContextMenuCommands = [ + allCommands['cut'], + allCommands['copy'], + allCommands['paste'], + allCommands['delete'], + allCommands['separator'], + allCommands['selectAll'], + allCommands['separator'], + allCommands['bringToFront'], + allCommands['sendToBack'], + allCommands['separator'], + allCommands['lock'], + allCommands['unlock'], + allCommands['separator'], + allCommands['insertShapeImage'], + allCommands['editShapeImage'], + allCommands['deleteShapeImage'] + ]); }, - _getCustomCommands(allCommands, customCommands) { - return customCommands.map(c => { + _getPreparedCommands(allCommands, commands) { + return commands.map(c => { if(allCommands[c]) { return allCommands[c]; } else if(c.text || c.icon) { const command = { + command: c.name, text: c.text, + hint: c.text, icon: c.icon, - onExecuted: c.onClick + menuIcon: c.icon }; if(Array.isArray(c.items)) { - command.items = this._getCustomCommands(allCommands, c.items); + command.items = this._getPreparedCommands(allCommands, c.items); } return command; } }).filter(c => c); }, - - _prepareContextMenuCommands(commands) { - const result = []; + _prepareContextMenuCommands(commands, excludeCommands, rootCommand) { let beginGroup = false; - commands.forEach(command => { - if(command === SEPARATOR) { + return commands.map(c => { + if(!this._isValidCommand(c, excludeCommands)) return; + + if(c === SEPARATOR_COMMAND) { beginGroup = true; } else { - if(typeof command === 'object') { - if(Array.isArray(command.items)) { - command.items = this._prepareContextMenuCommands(command.items); - } - result.push(extend(command, { - beginGroup: beginGroup - })); - } else { - result.push(command); - } + const command = this._cloneCommand(c, excludeCommands); + command.icon = command.menuIcon; + command.beginGroup = beginGroup; + command.rootCommand = !command.command ? rootCommand && rootCommand.command : undefined; beginGroup = false; + return command; } - }); - return result; + }).filter(c => c); }, - _prepareToolbarCommands(commands) { - const result = []; - commands.forEach(command => { - if(Array.isArray(command.items)) { - command.items = this._prepareContextMenuCommands(command.items); - } - result.push(command); - }); - return result; + _prepareToolbarCommands(commands, excludeCommands) { + return commands + .map(c => { + if(this._isValidCommand(c, excludeCommands)) { + return this._cloneCommand(c, excludeCommands); + } + }) + .filter(c => c) + .filter((c, index, arr) => { + if(c.widget === SEPARATOR && index === arr.length - 1) { + return false; + } + return c; + }); + }, + _cloneCommand(c, excludeCommands) { + const command = extend({}, c); + if(Array.isArray(c.items)) { + command.items = this._prepareContextMenuCommands(c.items, excludeCommands, command); + } + return command; + }, + _isValidCommand(c, excludeCommands) { + excludeCommands = excludeCommands || []; + return excludeCommands.indexOf(c.command) === -1; }, _exportTo(widget, dataURI, format, mimeString) { diff --git a/js/ui/diagram/ui.diagram.context_menu.js b/js/ui/diagram/ui.diagram.context_menu.js index f7365e0ccb64..c495c5416618 100644 --- a/js/ui/diagram/ui.diagram.context_menu.js +++ b/js/ui/diagram/ui.diagram.context_menu.js @@ -67,7 +67,6 @@ class DiagramContextMenu extends Widget { }); } _show(x, y, selection) { - this.clickPosition = { x, y }; const { Browser } = getDiagram(); this._contextMenuInstance.hide(); diff --git a/js/ui/diagram/ui.diagram.js b/js/ui/diagram/ui.diagram.js index 9ce1dbba978a..dda24385548a 100644 --- a/js/ui/diagram/ui.diagram.js +++ b/js/ui/diagram/ui.diagram.js @@ -767,6 +767,9 @@ class Diagram extends Widget { let startLineEndingSetter; let endLineEndingGetter; let endLineEndingSetter; + let containerKeyGetter; + let containerKeySetter; + const data = { nodeDataSource: this._nodesOption && this._nodesOption.getItems(), edgeDataSource: this._edgesOption && this._edgesOption.getItems(), @@ -805,10 +808,10 @@ class Diagram extends Widget { getItems: this._createOptionGetter('nodes.itemsExpr'), setItems: this._createOptionSetter('nodes.itemsExpr'), - getContainerKey: this._createOptionGetter('nodes.containerKeyExpr'), - setContainerKey: this._createOptionSetter('nodes.containerKeyExpr'), - getChildren: this._createOptionGetter('nodes.childrenExpr'), - setChildren: this._createOptionSetter('nodes.childrenExpr') + getContainerKey: (containerKeyGetter = this._createOptionGetter('nodes.containerKeyExpr')), + setContainerKey: (containerKeySetter = this._createOptionSetter('nodes.containerKeyExpr')), + getChildren: !containerKeyGetter && !containerKeySetter && this._createOptionGetter('nodes.containerChildrenExpr'), + setChildren: !containerKeyGetter && !containerKeySetter && this._createOptionSetter('nodes.containerChildrenExpr') }, edgeDataImporter: { getKey: this._createOptionGetter('edges.keyExpr'), @@ -862,6 +865,10 @@ class Diagram extends Widget { switch(lineType) { case 'arrow': return ConnectorLineEnding.Arrow; + case 'outlinedTriangle': + return ConnectorLineEnding.OutlinedTriangle; + case 'filledTriangle': + return ConnectorLineEnding.FilledTriangle; default: return ConnectorLineEnding.None; } @@ -871,6 +878,12 @@ class Diagram extends Widget { case ConnectorLineEnding.Arrow: value = 'arrow'; break; + case ConnectorLineEnding.OutlinedTriangle: + value = 'outlinedTriangle'; + break; + case ConnectorLineEnding.FilledTriangle: + value = 'filledTriangle'; + break; case ConnectorLineEnding.None: value = 'none'; break; @@ -882,6 +895,10 @@ class Diagram extends Widget { switch(lineType) { case 'none': return ConnectorLineEnding.None; + case 'outlinedTriangle': + return ConnectorLineEnding.OutlinedTriangle; + case 'filledTriangle': + return ConnectorLineEnding.FilledTriangle; default: return ConnectorLineEnding.Arrow; } @@ -891,6 +908,12 @@ class Diagram extends Widget { case ConnectorLineEnding.Arrow: value = 'arrow'; break; + case ConnectorLineEnding.OutlinedTriangle: + value = 'outlinedTriangle'; + break; + case ConnectorLineEnding.FilledTriangle: + value = 'filledTriangle'; + break; case ConnectorLineEnding.None: value = 'none'; break; @@ -968,11 +991,7 @@ class Diagram extends Widget { } _updateCustomShapes(customShapes, prevCustomShapes) { if(Array.isArray(prevCustomShapes)) { - this._diagramInstance.removeCustomShapes(prevCustomShapes.map( - function(s) { - return s.type; - } - )); + this._diagramInstance.removeCustomShapes(prevCustomShapes.map(s => s.type)); } if(Array.isArray(customShapes)) { @@ -1520,12 +1539,12 @@ class Diagram extends Widget { */ containerKeyExpr: undefined, /** - * @name dxDiagramOptions.nodes.childrenExpr + * @name dxDiagramOptions.nodes.containerChildrenExpr * @type string|function(data) * @type_function_param1 data:object * @default "children" */ - childrenExpr: 'children', + containerChildrenExpr: 'children', /** * @name dxDiagramOptions.nodes.autoLayout * @type Enums.DiagramDataLayoutType|Object diff --git a/js/ui/diagram/ui.diagram.toolbox.js b/js/ui/diagram/ui.diagram.toolbox.js index 17b7dec0a899..16af0fc14084 100644 --- a/js/ui/diagram/ui.diagram.toolbox.js +++ b/js/ui/diagram/ui.diagram.toolbox.js @@ -136,7 +136,7 @@ class DiagramToolbox extends DiagramFloatingPanel { updateMaxHeight() { if(this.isMobileView()) return; - let maxHeight = 4; + let maxHeight = 6; if(this._popup) { const $title = this._getPopupTitle(); maxHeight += $title.outerHeight(); diff --git a/js/ui/drawer/ui.drawer.js b/js/ui/drawer/ui.drawer.js index 3e9eafcb82b5..986d61cda4a9 100644 --- a/js/ui/drawer/ui.drawer.js +++ b/js/ui/drawer/ui.drawer.js @@ -413,12 +413,12 @@ const Drawer = Widget.inherit({ if(this.option('shading')) { this._$shader.toggleClass(INVISIBLE_STATE_CLASS, !visible); this._$shader.css('visibility', visible ? 'visible' : 'hidden'); - - this.updateZIndex(visible); } else { this._$shader.toggleClass(INVISIBLE_STATE_CLASS, true); this._$shader.css('visibility', 'hidden'); } + + this.updateZIndex(visible); }, updateZIndex(visible) { diff --git a/js/ui/drawer/ui.drawer.rendering.strategy.js b/js/ui/drawer/ui.drawer.rendering.strategy.js index d30ad2bc4c7b..a40245d904e5 100644 --- a/js/ui/drawer/ui.drawer.rendering.strategy.js +++ b/js/ui/drawer/ui.drawer.rendering.strategy.js @@ -208,9 +208,11 @@ class DrawerStrategy { } updateZIndex() { - if(!isDefined(this._shaderZIndex)) { - this._shaderZIndex = zIndexPool.base() + 500; - this._drawer._$shader.css('zIndex', this._shaderZIndex); + if(this._drawer.option('shading')) { + if(!isDefined(this._shaderZIndex)) { + this._shaderZIndex = zIndexPool.base() + 500; + this._drawer._$shader.css('zIndex', this._shaderZIndex); + } } } diff --git a/js/ui/drawer/ui.drawer.rendering.strategy.overlap.js b/js/ui/drawer/ui.drawer.rendering.strategy.overlap.js index 4726ebeba56d..917bbfebf804 100644 --- a/js/ui/drawer/ui.drawer.rendering.strategy.overlap.js +++ b/js/ui/drawer/ui.drawer.rendering.strategy.overlap.js @@ -35,9 +35,7 @@ class OverlapStrategy extends DrawerStrategy { contentTemplate: drawer.option('template'), onContentReady: () => { whenPanelContentRendered.resolve(); - if(drawer.option('shading')) { - drawer.updateZIndex(opened); - } + drawer.updateZIndex(opened); }, visible: true, propagateOutsideClick: true diff --git a/js/ui/drawer/ui.drawer.rendering.strategy.shrink.js b/js/ui/drawer/ui.drawer.rendering.strategy.shrink.js index 14a21469be86..b57505dbdbc9 100644 --- a/js/ui/drawer/ui.drawer.rendering.strategy.shrink.js +++ b/js/ui/drawer/ui.drawer.rendering.strategy.shrink.js @@ -2,9 +2,7 @@ import { animation } from './ui.drawer.rendering.strategy'; import DrawerStrategy from './ui.drawer.rendering.strategy'; import $ from '../../core/renderer'; import { extend } from '../../core/utils/extend'; -import { isDefined } from '../../core/utils/type'; import { camelize } from '../../core/utils/inflector'; -import * as zIndexPool from '../overlay/z_index'; class ShrinkStrategy extends DrawerStrategy { _slidePositionRendering(config, _, animate) { @@ -51,24 +49,6 @@ class ShrinkStrategy extends DrawerStrategy { isViewContentFirst(position, isRtl) { return (isRtl ? position === 'left' : position === 'right') || position === 'bottom'; } - - updateZIndex() { - super.updateZIndex(); - if(!isDefined(this._panelZIndex)) { - this._panelZIndex = zIndexPool.base() + 501; - this._drawer._$panelContentWrapper.css('zIndex', this._panelZIndex); - } - - } - - clearZIndex() { - if(isDefined(this._panelZIndex)) { - this._drawer._$panelContentWrapper.css('zIndex', ''); - delete this._panelZIndex; - } - - super.clearZIndex(); - } } module.exports = ShrinkStrategy; diff --git a/js/ui/drop_down_box.js b/js/ui/drop_down_box.js index b6c2b597c492..3b50be37ad3f 100644 --- a/js/ui/drop_down_box.js +++ b/js/ui/drop_down_box.js @@ -248,6 +248,16 @@ const DropDownBox = DropDownEditor.inherit({ return realDevice.deviceType === 'desktop' && this._canShowVirtualKeyboard() && this._isNestedElementActive(); }, + _popupHiddenHandler: function() { + this.callBase(); + this._popupPosition = undefined; + }, + + _popupPositionedHandler: function(e) { + this.callBase(e); + this._popupPosition = e.position; + }, + _popupConfig: function() { const { focusStateEnabled } = this.option(); const horizontalAlignment = this.option('rtlEnabled') ? 'right' : 'left'; @@ -273,7 +283,9 @@ const DropDownBox = DropDownEditor.inherit({ }, onKeyboardHandled: opts => this.option('focusStateEnabled') && this._popupElementTabHandler(opts), maxHeight: function() { - return getElementMaxHeightByWindow(this.$element()); + const popupLocation = this._popupPosition?.v.location; + + return getElementMaxHeightByWindow(this.$element(), popupLocation); }.bind(this) }); }, diff --git a/js/ui/drop_down_editor/ui.drop_down_list.js b/js/ui/drop_down_editor/ui.drop_down_list.js index da7b5e20518b..3d7f16683a88 100644 --- a/js/ui/drop_down_editor/ui.drop_down_list.js +++ b/js/ui/drop_down_editor/ui.drop_down_list.js @@ -601,6 +601,19 @@ const DropDownList = DropDownEditor.inherit({ return this._searchValue().toString().length >= this.option('minSearchLength'); }, + _needClearFilter: function() { + return this._canKeepDataSource() ? false : this._needPassDataSourceToList(); + }, + + _canKeepDataSource: function() { + const isMinSearchLengthExceeded = this._isMinSearchLengthExceeded(); + return this._dataSource?.isLoaded() && + this.option('showDataBeforeSearch') && + this.option('minSearchLength') && + !isMinSearchLengthExceeded && + !this._isLastMinSearchLengthExceeded; + }, + _searchValue: function() { return this._input().val() || ''; }, @@ -651,7 +664,7 @@ const DropDownList = DropDownEditor.inherit({ _searchCanceled: function() { this._clearSearchTimer(); - if(this._needPassDataSourceToList()) { + if(this._needClearFilter()) { this._filterDataSource(null); } this._refreshList(); @@ -665,12 +678,12 @@ const DropDownList = DropDownEditor.inherit({ this._clearSearchTimer(); const dataSource = this._dataSource; - - dataSource.searchExpr(this.option('searchExpr') || this._displayGetterExpr()); - dataSource.searchOperation(this.option('searchMode')); - dataSource.searchValue(searchValue); - - return dataSource.load().done(this._dataSourceFiltered.bind(this, searchValue)); + if(dataSource) { + dataSource.searchExpr(this.option('searchExpr') || this._displayGetterExpr()); + dataSource.searchOperation(this.option('searchMode')); + dataSource.searchValue(searchValue); + dataSource.load().done(this._dataSourceFiltered.bind(this, searchValue)); + } }, _clearFilter: function() { @@ -679,6 +692,7 @@ const DropDownList = DropDownEditor.inherit({ }, _dataSourceFiltered: function() { + this._isLastMinSearchLengthExceeded = this._isMinSearchLengthExceeded(); this._refreshList(); this._refreshPopupVisibility(); }, @@ -779,6 +793,7 @@ const DropDownList = DropDownEditor.inherit({ if(this._list) { delete this._list; } + delete this._isLastMinSearchLengthExceeded; this.callBase(); }, @@ -860,7 +875,9 @@ const DropDownList = DropDownEditor.inherit({ case 'popupWidthExtension': break; case 'selectedItem': - this._selectionChangedAction({ selectedItem: args.value }); + if(args.previousValue !== args.value) { + this._selectionChangedAction({ selectedItem: args.value }); + } break; default: this.callBase(args); diff --git a/js/ui/file_manager.d.ts b/js/ui/file_manager.d.ts index 6b001d170651..8aea8423803c 100644 --- a/js/ui/file_manager.d.ts +++ b/js/ui/file_manager.d.ts @@ -14,10 +14,6 @@ import { dxContextMenuItem } from './context_menu'; -import { - dxDataGridColumn -} from './data_grid'; - import { dxToolbarItem } from './toolbar'; @@ -61,12 +57,12 @@ export interface dxFileManagerOptions extends WidgetOptions { /** * @docid dxFileManagerOptions.customizeDetailColumns * @type function - * @type_function_param1 columns:Array - * @type_function_return Array + * @type_function_param1 columns:Array + * @type_function_return Array * @prevFileNamespace DevExpress.ui * @public */ - customizeDetailColumns?: ((columns: Array) => Array); + customizeDetailColumns?: ((columns: Array) => Array); /** * @docid dxFileManagerOptions.customizeThumbnail * @type function diff --git a/js/ui/file_manager/file_items_controller.js b/js/ui/file_manager/file_items_controller.js index 89a2eb34106e..dedccca2ad07 100644 --- a/js/ui/file_manager/file_items_controller.js +++ b/js/ui/file_manager/file_items_controller.js @@ -113,7 +113,7 @@ export default class FileItemsController { if(requireRaiseSelectedDirectory && this._isInitialized) { if(!this._dataLoading) { - this._raiseDataLoading(); + this._raiseDataLoading('navigation'); } this._raiseSelectedDirectoryChanged(directoryInfo); } @@ -337,7 +337,7 @@ export default class FileItemsController { return this._executeDataLoad(() => { return this._refreshDeferred = this._refreshInternal(); - }); + }, 'refresh'); } _refreshInternal() { @@ -393,7 +393,7 @@ export default class FileItemsController { } _setCurrentDirectoryByPathParts(pathParts, useKeys) { - return this._executeDataLoad(() => this._setCurrentDirectoryByPathPartsInternal(pathParts, useKeys)); + return this._executeDataLoad(() => this._setCurrentDirectoryByPathPartsInternal(pathParts, useKeys), 'navigation'); } _setCurrentDirectoryByPathPartsInternal(pathParts, useKeys) { @@ -406,12 +406,12 @@ export default class FileItemsController { }); } - _executeDataLoad(action) { + _executeDataLoad(action, operation) { this._dataLoading = true; this._dataLoadingDeferred = new Deferred(); if(this._isInitialized) { - this._raiseDataLoading(); + this._raiseDataLoading(operation); } return action().always(() => { @@ -540,9 +540,9 @@ export default class FileItemsController { } } - _raiseDataLoading() { + _raiseDataLoading(operation) { if(this._options.onDataLoading) { - this._options.onDataLoading(); + this._options.onDataLoading({ operation }); } } diff --git a/js/ui/file_manager/ui.file_manager.dialog.delete_item.js b/js/ui/file_manager/ui.file_manager.dialog.delete_item.js new file mode 100644 index 000000000000..7983fdf8f431 --- /dev/null +++ b/js/ui/file_manager/ui.file_manager.dialog.delete_item.js @@ -0,0 +1,49 @@ +import $ from '../../core/renderer'; +import { extend } from '../../core/utils/extend'; + +import messageLocalization from '../../localization/message'; + +import FileManagerDialogBase from './ui.file_manager.dialog.js'; + +const FILE_MANAGER_DIALOG_DELETE_ITEM = 'dx-filemanager-dialog-delete-item'; +const FILE_MANAGER_DIALOG_DELETE_ITEM_POPUP = 'dx-filemanager-dialog-delete-item-popup'; // TODO ensure needed + +class FileManagerDeleteItemDialog extends FileManagerDialogBase { + + show({ itemName, itemCount }) { + const text = itemCount === 1 + ? messageLocalization.format('dxFileManager-dialogDeleteItemSingleItemConfirmation', itemName) + : messageLocalization.format('dxFileManager-dialogDeleteItemMultipleItemsConfirmation', itemCount); + + if(this._$text) { + this._$text.text(text); + } else { + this._initialText = text; + } + + super.show(); + } + + _getDialogOptions() { + return extend(super._getDialogOptions(), { + title: messageLocalization.format('dxFileManager-dialogDeleteItemTitle'), + buttonText: messageLocalization.format('dxFileManager-dialogDeleteItemButtonText'), + contentCssClass: FILE_MANAGER_DIALOG_DELETE_ITEM, + popupCssClass: FILE_MANAGER_DIALOG_DELETE_ITEM_POPUP + }); + } + + _createContentTemplate(element) { + super._createContentTemplate(element); + + this._$text = $('
') + .text(this._initialText) + .appendTo(this._$contentElement); + } + + _getDialogResult() { + return {}; + } +} + +module.exports = FileManagerDeleteItemDialog; diff --git a/js/ui/file_manager/ui.file_manager.dialog.js b/js/ui/file_manager/ui.file_manager.dialog.js index 32f9e052dd6b..55b3724c308b 100644 --- a/js/ui/file_manager/ui.file_manager.dialog.js +++ b/js/ui/file_manager/ui.file_manager.dialog.js @@ -51,6 +51,9 @@ class FileManagerDialogBase extends Widget { } } ], + onInitialized: ({ component }) => { + component.registerKeyHandler('enter', this._applyDialogChanges.bind(this)); + }, onHidden: this._onPopupHidden.bind(this), onShown: this._onPopupShown.bind(this) }); diff --git a/js/ui/file_manager/ui.file_manager.dialog_manager.js b/js/ui/file_manager/ui.file_manager.dialog_manager.js index d7961465f0d5..6ad821e93296 100644 --- a/js/ui/file_manager/ui.file_manager.dialog_manager.js +++ b/js/ui/file_manager/ui.file_manager.dialog_manager.js @@ -5,6 +5,7 @@ import messageLocalization from '../../localization/message'; import FileManagerNameEditorDialog from './ui.file_manager.dialog.name_editor'; import FileManagerFolderChooserDialog from './ui.file_manager.dialog.folder_chooser'; +import FileManagerDeleteItemDialog from './ui.file_manager.dialog.delete_item'; class FileManagerDialogManager { constructor($element, options) { @@ -29,13 +30,10 @@ class FileManagerDialogManager { onClosed: this._options['onDialogClosed'] }); - this._confirmationDialog = { // TODO implement this dialog - show: () => { - setTimeout(() => { - this._options['onDialogClosed']({ dialogResult: {} }); - }); - } - }; + const $deleteItemDialog = $('
').appendTo(this._$element); + this._deleteItemDialog = new FileManagerDeleteItemDialog($deleteItemDialog, { + onClosed: this._options['onDialogClosed'] + }); } getCopyDialog() { @@ -56,8 +54,8 @@ class FileManagerDialogManager { return this._createItemDialog; } - getConfirmationDialog() { - return this._confirmationDialog; + getDeleteItemDialog() { + return this._deleteItemDialog; } } diff --git a/js/ui/file_manager/ui.file_manager.editing.js b/js/ui/file_manager/ui.file_manager.editing.js index da5bf5ddeda0..50b00c1160f7 100644 --- a/js/ui/file_manager/ui.file_manager.editing.js +++ b/js/ui/file_manager/ui.file_manager.editing.js @@ -253,7 +253,9 @@ class FileManagerEditingControl extends Widget { _tryDelete(itemInfos) { itemInfos = itemInfos || this._model.getMultipleSelectedItems(); - return this._showDialog(this._dialogManager.getConfirmationDialog()) + const itemName = itemInfos[0].fileItem.name; + const itemCount = itemInfos.length; + return this._showDialog(this._dialogManager.getDeleteItemDialog(), { itemName, itemCount }) .then(() => this._controller.deleteItems(itemInfos)); } diff --git a/js/ui/file_manager/ui.file_manager.files_tree_view.js b/js/ui/file_manager/ui.file_manager.files_tree_view.js index e1799c031768..c728a3fc2dc7 100644 --- a/js/ui/file_manager/ui.file_manager.files_tree_view.js +++ b/js/ui/file_manager/ui.file_manager.files_tree_view.js @@ -35,7 +35,7 @@ class FileManagerFilesTreeView extends Widget { keyExpr: 'getInternalKey', parentIdExpr: 'parentDirectory.getInternalKey', displayExpr: itemInfo => itemInfo.getDisplayName(), - hasItemsExpr: 'fileItem.hasSubDirs', + hasItemsExpr: 'fileItem.hasSubDirectories', onItemClick: this._createActionByOption('onDirectoryClick'), onItemExpanded: e => this._onFilesTreeViewItemExpanded(e), onItemCollapsed: e => this._onFilesTreeViewItemCollapsed(e), diff --git a/js/ui/file_manager/ui.file_manager.item_list.details.js b/js/ui/file_manager/ui.file_manager.item_list.details.js index 8959c238e667..7059dc664884 100644 --- a/js/ui/file_manager/ui.file_manager.item_list.details.js +++ b/js/ui/file_manager/ui.file_manager.item_list.details.js @@ -1,7 +1,7 @@ import $ from '../../core/renderer'; import { extend } from '../../core/utils/extend'; import { extendAttributes } from './ui.file_manager.common'; -import { isString, isFunction } from '../../core/utils/type'; +import { isString, isFunction, isDefined } from '../../core/utils/type'; import messageLocalization from '../../localization/message'; import DataGrid from '../data_grid/ui.data_grid'; @@ -303,9 +303,15 @@ class FileManagerDetailsItemList extends FileManagerItemListBase { _onFocusedRowChanged(e) { if(!this._isMultipleSelectionMode()) { - this._selectItemSingleSelection(e.row.data); + this._selectItemSingleSelection(e.row?.data); } - this._raiseFocusedItemChanged(e); + + const fileSystemItem = e.row?.data.fileItem || null; + this._raiseFocusedItemChanged({ + item: fileSystemItem, + itemKey: fileSystemItem?.key, + itemElement: e.rowElement + }); } _onFilesViewOptionChanged({ fullName }) { @@ -353,7 +359,7 @@ class FileManagerDetailsItemList extends FileManagerItemListBase { } _selectItemSingleSelection(fileItemInfo) { - if(!this._focusedItem || this._focusedItem.fileItem.key !== fileItemInfo.fileItem.key) { + if(!this._focusedItem || !fileItemInfo || this._focusedItem.fileItem.key !== fileItemInfo.fileItem.key) { const oldFocusedItem = this._focusedItem; this._focusedItem = fileItemInfo; @@ -362,10 +368,17 @@ class FileManagerDetailsItemList extends FileManagerItemListBase { deselectedKeys.push(oldFocusedItem.fileItem.key); } + const selectedItems = []; + const selectedKeys = []; + if(fileItemInfo) { + selectedItems.push(fileItemInfo.fileItem); + selectedKeys.push(fileItemInfo.fileItem.key); + } + this._raiseSelectionChanged({ - selectedItems: [ fileItemInfo.fileItem ], - selectedItemKeys: [ fileItemInfo.fileItem.key ], - currentSelectedItemKeys: [ fileItemInfo.fileItem.key ], + selectedItems, + selectedItemKeys: selectedKeys, + currentSelectedItemKeys: [...selectedKeys], currentDeselectedItemKeys: deselectedKeys }); } @@ -388,11 +401,27 @@ class FileManagerDetailsItemList extends FileManagerItemListBase { } clearSelection() { - this._filesView.clearSelection(); + if(this._isMultipleSelectionMode()) { + this._filesView.clearSelection(); + } else { + this._filesView.option('focusedRowIndex', -1); + } } - refresh() { - this._filesView.option('dataSource', this._createDataSource()); + refresh(options) { + const actualOptions = { + dataSource: this._createDataSource() + }; + + if(options && Object.prototype.hasOwnProperty.call(options, 'focusedItemKey')) { + if(isDefined(options.focusedItemKey)) { + actualOptions.focusedRowKey = options.focusedItemKey; + } else { + actualOptions.focusedRowIndex = -1; + } + } + + this._filesView.option(actualOptions); } getSelectedItems() { diff --git a/js/ui/file_manager/ui.file_manager.item_list.thumbnails.js b/js/ui/file_manager/ui.file_manager.item_list.thumbnails.js index 24a47cc63b5d..8c8c2b3f8586 100644 --- a/js/ui/file_manager/ui.file_manager.item_list.thumbnails.js +++ b/js/ui/file_manager/ui.file_manager.item_list.thumbnails.js @@ -118,8 +118,13 @@ class FileManagerThumbnailsItemList extends FileManagerItemListBase { this._tryRaiseSelectionChanged({ selectedItemInfos, selectedItems, selectedItemKeys, currentSelectedItemKeys, currentDeselectedItemKeys }); } - _onItemListFocusedItemChanged(e) { - this._raiseFocusedItemChanged(e); + _onItemListFocusedItemChanged({ item, itemElement }) { + const fileSystemItem = item?.fileItem || null; + this._raiseFocusedItemChanged({ + item: fileSystemItem, + itemKey: fileSystemItem?.key, + itemElement: itemElement || undefined + }); } _setSelectedItemKeys(itemKeys) { @@ -130,9 +135,18 @@ class FileManagerThumbnailsItemList extends FileManagerItemListBase { this._itemList.option('focusedItemKey', itemKey); } - refresh() { + refresh(options) { this.clearSelection(); - this._itemList.option('dataSource', this._createDataSource()); + + const actualOptions = { + dataSource: this._createDataSource() + }; + + if(options && Object.prototype.hasOwnProperty.call(options, 'focusedItemKey')) { + actualOptions.focusedItemKey = options.focusedItemKey; + } + + this._itemList.option(actualOptions); } _deselectItem(item) { diff --git a/js/ui/file_manager/ui.file_manager.items_list.thumbnails.list_box.js b/js/ui/file_manager/ui.file_manager.items_list.thumbnails.list_box.js index c71c569d5a01..660f508b4a9d 100644 --- a/js/ui/file_manager/ui.file_manager.items_list.thumbnails.list_box.js +++ b/js/ui/file_manager/ui.file_manager.items_list.thumbnails.list_box.js @@ -2,6 +2,7 @@ import $ from '../../core/renderer'; import { extend } from '../../core/utils/extend'; import { find } from '../../core/utils/array'; import { isDefined } from '../../core/utils/type'; +import { Deferred } from '../../core/utils/deferred'; import holdEvent from '../../events/hold'; import { addNamespace } from '../../events/utils'; @@ -26,6 +27,8 @@ class FileManagerThumbnailListBox extends CollectionWidget { _initMarkup() { this._initActions(); + this._lockFocusedItemProcessing = false; + this.$element().addClass(FILE_MANAGER_THUMBNAILS_VIEW_PORT_CLASS); this._renderItemsContainer(); @@ -104,7 +107,7 @@ class FileManagerThumbnailListBox extends CollectionWidget { }, enter(e) { this._beforeKeyProcessing(e); - this._onItemEnterKeyPressed(this._getFocusedItem()); + this._actions.onItemEnterKeyPressed(this._getFocusedItem()); }, A(e) { this._beforeKeyProcessing(e); @@ -284,22 +287,33 @@ class FileManagerThumbnailListBox extends CollectionWidget { } _syncFocusedItemKey() { - if(this._dataSource && this._dataSource.isLoading()) { - return; + if(!this._syncFocusedItemKeyDeferred) { + this._syncFocusedItemKeyDeferred = new Deferred(); } - const focusedItemKey = this.option('focusedItemKey'); - if(!isDefined(focusedItemKey)) { - return; + const deferred = this._syncFocusedItemKeyDeferred; + + if(this._dataSource && this._dataSource.isLoading()) { + return deferred.promise(); } - const items = this.option('items'); - const focusedItem = find(items, item => this.keyOf(item) === focusedItemKey); - if(focusedItem) { - this._focusItem(focusedItem); + const focusedItemKey = this.option('focusedItemKey'); + if(isDefined(focusedItemKey)) { + const items = this.option('items'); + const focusedItem = find(items, item => this.keyOf(item) === focusedItemKey); + if(focusedItem) { + this._focusItem(focusedItem); + deferred.resolve(); + } else { + this.option('focusedItemKey', undefined); + deferred.reject(); + } } else { - this.option('focusedItemKey', undefined); + deferred.resolve(); } + + this._syncFocusedItemKeyDeferred = null; + return deferred.promise(); } _onFocusedItemChanged() { @@ -307,7 +321,9 @@ class FileManagerThumbnailListBox extends CollectionWidget { const newFocusedItemKey = this.keyOf(focusedItem); const oldFocusedItemKey = this.option('focusedItemKey'); if(newFocusedItemKey !== oldFocusedItemKey) { + this._lockFocusedItemProcessing = true; this.option('focusedItemKey', newFocusedItemKey); + this._lockFocusedItemProcessing = false; this._raiseFocusedItemChanged(focusedItem); } } @@ -367,13 +383,20 @@ class FileManagerThumbnailListBox extends CollectionWidget { super._optionChanged(args); break; case 'focusedItemKey': + if(this._lockFocusedItemProcessing) { + break; + } + if(isDefined(args.value)) { - this._syncFocusedItemKey(); - this._onFocusedItemChanged(); + this._syncFocusedItemKey().done(() => { + const focusedItem = this._getFocusedItem(); + this._raiseFocusedItemChanged(focusedItem); + }); } else { this.option('focusedElement', null); this._raiseFocusedItemChanged(null); } + break; case 'onItemEnterKeyPressed': case 'onFocusedItemChanged': diff --git a/js/ui/file_manager/ui.file_manager.js b/js/ui/file_manager/ui.file_manager.js index 69b8a480971f..f61d03eeb102 100644 --- a/js/ui/file_manager/ui.file_manager.js +++ b/js/ui/file_manager/ui.file_manager.js @@ -46,6 +46,7 @@ class FileManager extends Widget { this._firstItemViewLoad = true; this._lockSelectionProcessing = false; this._lockFocusedItemProcessing = false; + this._itemKeyToFocus = undefined; this._controller = new FileItemsController({ currentPath: this.option('currentPath'), @@ -230,10 +231,13 @@ class FileManager extends Widget { _onItemViewFocusedItemChanged(e) { this._lockFocusedItemProcessing = true; - this.option('focusedItemKey', e.focusedItemKey); + this.option('focusedItemKey', e.itemKey); this._lockFocusedItemProcessing = false; - this._actions.onFocusedItemChanged(e); + this._actions.onFocusedItemChanged({ + item: e.item, + itemElement: e.itemElement + }); } _onAdaptiveStateChanged({ enabled }) { @@ -362,6 +366,7 @@ class FileManager extends Widget { parentDirItem.isParentFolder = true; parentDirItem.name = '..'; parentDirItem.relativeName = '..'; + parentDirItem.key = []; const itemsCopy = [...items]; itemsCopy.unshift({ @@ -674,8 +679,15 @@ class FileManager extends Widget { } } - _onDataLoading() { - this._itemView.refresh(); + _onDataLoading({ operation }) { + let options = null; + + if(operation === 'navigation') { + options = { focusedItemKey: this._itemKeyToFocus }; + this._itemKeyToFocus = undefined; + } + + this._itemView.refresh(options); } _onSelectedDirectoryChanged() { @@ -734,6 +746,10 @@ class FileManager extends Widget { return; } + if(fileItem.isParentFolder) { + this._itemKeyToFocus = this._getCurrentDirectory().fileItem.key; + } + const newCurrentDirectory = fileItem.isParentFolder ? this._getCurrentDirectory().parentDirectory : fileItemInfo; this._setCurrentDirectory(newCurrentDirectory); diff --git a/js/ui/file_manager/ui.file_manager.toolbar.js b/js/ui/file_manager/ui.file_manager.toolbar.js index 9fe1d1865564..75c27a9c2ea6 100644 --- a/js/ui/file_manager/ui.file_manager.toolbar.js +++ b/js/ui/file_manager/ui.file_manager.toolbar.js @@ -373,7 +373,7 @@ class FileManagerToolbar extends Widget { _fileToolbarHasEffectiveItems(fileItems) { const items = this._fileToolbar.option('items'); - return items.some(({ name }) => name !== 'clear' && name !== 'refresh' && this._commandManager.isCommandAvailable(name, fileItems)); + return items.some(item => this._isFileToolbarItemAvailable(item, fileItems)); } _executeCommand(command) { @@ -395,6 +395,11 @@ class FileManagerToolbar extends Widget { return this._commandManager.isCommandAvailable(toolbarItem.name, fileItems); } + _isFileToolbarItemAvailable({ name, visible }, fileItems) { + return !this._isDefaultItem(name) && ensureDefined(visible, true) || + name !== 'clear' && name !== 'refresh' && this._commandManager.isCommandAvailable(name, fileItems); + } + _updateItemInToolbar(toolbar, commandName, options) { toolbar.beginUpdate(); diff --git a/js/ui/form/ui.form.js b/js/ui/form/ui.form.js index 32c662cd5b89..c0e9a71b77e9 100644 --- a/js/ui/form/ui.form.js +++ b/js/ui/form/ui.form.js @@ -962,6 +962,17 @@ const Form = Widget.inherit({ if(layoutManager) { const fullOptionName = getFullOptionName(nameParts[endPartIndex], optionName); + if(optionName === 'visible') { // T874843 + const formItems = this.option(getFullOptionName(itemPath, 'items')); + if(formItems && formItems.length) { + const layoutManagerItems = layoutManager.option('items'); + formItems.forEach((item, index) => { + const layoutItem = layoutManagerItems[index]; + layoutItem.visibleIndex = item.visibleIndex; + }); + } + } + this._setLayoutManagerItemOption(layoutManager, fullOptionName, value, itemPath); return true; } diff --git a/js/ui/gantt.d.ts b/js/ui/gantt.d.ts index 2838084790fb..5a33428df049 100644 --- a/js/ui/gantt.d.ts +++ b/js/ui/gantt.d.ts @@ -49,14 +49,14 @@ export interface dxGanttOptions extends WidgetOptions { * @prevFileNamespace DevExpress.ui * @public */ - editing?: { allowDependencyAdding?: boolean, allowDependencyDeleting?: boolean, allowDependencyUpdating?: boolean, allowResourceAdding?: boolean, allowResourceDeleting?: boolean, allowResourceUpdating?: boolean, allowTaskAdding?: boolean, allowTaskDeleting?: boolean, allowTaskUpdating?: boolean, enabled?: boolean }; + editing?: { allowDependencyAdding?: boolean, allowDependencyDeleting?: boolean, allowResourceAdding?: boolean, allowResourceDeleting?: boolean, allowResourceUpdating?: boolean, allowTaskAdding?: boolean, allowTaskDeleting?: boolean, allowTaskUpdating?: boolean, enabled?: boolean }; /** * @docid dxGanttOptions.validation * @type Object * @prevFileNamespace DevExpress.ui * @public */ - validation?: { enableDependencyValidation?: boolean, autoUpdateParentTasks?: boolean }; + validation?: { validateDependencies?: boolean, autoUpdateParentTasks?: boolean }; /** * @docid dxGanttOptions.onSelectionChanged * @extends Action @@ -182,12 +182,12 @@ export interface dxGanttToolbar { export interface dxGanttToolbarItem extends dxToolbarItem { /** - * @docid dxGanttToolbarItem.formatName + * @docid dxGanttToolbarItem.name * @type Enums.GanttToolbarItem|string * @prevFileNamespace DevExpress.ui * @public */ - formatName?: 'separator' | 'undo' | 'redo' | 'zoomIn' | 'zoomOut' | string; + name?: 'separator' | 'undo' | 'redo' | 'zoomIn' | 'zoomOut' | string; /** * @docid dxGanttToolbarItem.location * @default "before" diff --git a/js/ui/gantt/ui.gantt.bars.js b/js/ui/gantt/ui.gantt.bars.js index 31bfc6638a6f..539ca0fe0499 100644 --- a/js/ui/gantt/ui.gantt.bars.js +++ b/js/ui/gantt/ui.gantt.bars.js @@ -103,8 +103,8 @@ export class GanttToolbar extends Bar { if(typeof item === 'string') { return this._createItemByText(item); } else { - if(item.formatName) { - return extend(this._createItemByText(item.formatName), item); + if(item.name) { + return extend(this._createItemByText(item.name), item); } else { return extend(this._getDefaultItemOptions(), item); } diff --git a/js/ui/gantt/ui.gantt.js b/js/ui/gantt/ui.gantt.js index 8b4fc64e2dd7..79a066fb716c 100644 --- a/js/ui/gantt/ui.gantt.js +++ b/js/ui/gantt/ui.gantt.js @@ -687,12 +687,6 @@ class Gantt extends Widget { * @default true */ allowDependencyDeleting: true, - /** - * @name dxGanttOptions.editing.allowDependencyUpdating - * @type boolean - * @default true - */ - allowDependencyUpdating: true, /** * @name dxGanttOptions.editing.allowResourceAdding * @type boolean @@ -714,11 +708,11 @@ class Gantt extends Widget { }, validation: { /** - * @name dxGanttOptions.validation.enableDependencyValidation + * @name dxGanttOptions.validation.validateDependencies * @type boolean * @default false */ - enableDependencyValidation: false, + validateDependencies: false, /** * @name dxGanttOptions.validation.autoUpdateParentTasks * @type boolean diff --git a/js/ui/gantt/ui.gantt.view.js b/js/ui/gantt/ui.gantt.view.js index 3bdaec018e30..da05cadf80ba 100644 --- a/js/ui/gantt/ui.gantt.view.js +++ b/js/ui/gantt/ui.gantt.view.js @@ -18,7 +18,7 @@ export class GanttView extends Widget { showResources: this.option('showResources'), taskTitlePosition: this._getTaskTitlePosition(this.option('taskTitlePosition')), allowSelectTask: this.option('allowSelection'), - editing: this.option('editing'), + editing: this._parseEditingSettings(this.option('editing')), validation: this.option('validation'), stripLines: { stripLines: this.option('stripLines') }, areHorizontalBordersEnabled: this.option('showRowLines'), @@ -104,6 +104,19 @@ export class GanttView extends Widget { return undefined; } } + _parseEditingSettings(value) { + return { + enabled: value.enabled, + allowDependencyDelete: value.allowDependencyDeleting, + allowDependencyInsert: value.allowDependencyAdding, + allowTaskDelete: value.allowTaskDeleting, + allowTaskInsert: value.allowTaskAdding, + allowTaskUpdate: value.allowTaskUpdating, + allowResourceDelete: value.allowResourceDeleting, + allowResourceInsert: value.allowResourceAdding, + allowResourceUpdate: value.allowResourceUpdating + }; + } _optionChanged(args) { switch(args.name) { @@ -130,7 +143,7 @@ export class GanttView extends Widget { this._selectTask(args.value); break; case 'editing': - this._ganttViewCore.setEditingSettings(args.value); + this._ganttViewCore.setEditingSettings(this._parseEditingSettings(args.value)); break; case 'validation': this._ganttViewCore.setValidationSettings(args.value); diff --git a/js/ui/grid_core/ui.grid_core.adaptivity.js b/js/ui/grid_core/ui.grid_core.adaptivity.js index 7d99b4d3eb21..330257cce1cf 100644 --- a/js/ui/grid_core/ui.grid_core.adaptivity.js +++ b/js/ui/grid_core/ui.grid_core.adaptivity.js @@ -92,6 +92,7 @@ const AdaptiveColumnsController = modules.ViewController.inherit({ const displayValue = gridCoreUtils.getDisplayValue(column, value, cellOptions.data, cellOptions.rowType); const text = gridCoreUtils.formatValue(displayValue, column); const isCellOrBatchEditMode = this._editingController.isCellOrBatchEditMode(); + const rowsView = that._rowsView; if(column.allowEditing && that.getController('keyboardNavigation').isKeyboardEnabled()) { $container.attr('tabIndex', that.option('tabIndex')); @@ -104,7 +105,10 @@ const AdaptiveColumnsController = modules.ViewController.inherit({ if(column.cellTemplate) { const templateOptions = extend({}, cellOptions, { value: value, displayValue: displayValue, text: text, column: column }); - that._rowsView.renderTemplate($container, column.cellTemplate, templateOptions, !!$container.closest(getWindow().document).length); + const isDomElement = !!$container.closest(getWindow().document).length; + rowsView.renderTemplate($container, column.cellTemplate, templateOptions, isDomElement).done(() => { + rowsView._cellPrepared($container, cellOptions); + }); } else { container = $container.get(0); if(column.encodeHtml) { @@ -123,9 +127,9 @@ const AdaptiveColumnsController = modules.ViewController.inherit({ $container.addClass(FORM_ITEM_MODIFIED); } } - } - that.getView('rowsView')._cellPrepared($container, cellOptions); + rowsView._cellPrepared($container, cellOptions); + } }, _getTemplate: function(item, cellOptions) { @@ -409,7 +413,9 @@ const AdaptiveColumnsController = modules.ViewController.inherit({ rowsCount = view.getRowsCount(); const $rowElements = view._getRowElements(); for(rowIndex = 0; rowIndex < rowsCount; rowIndex++) { - if(rowIndex !== editFormRowIndex || viewName !== ROWS_VIEW) { + const cancelClassAdding = rowIndex === editFormRowIndex && viewName === ROWS_VIEW && this.option('editing.mode') !== 'popup'; + + if(!cancelClassAdding) { currentVisibleIndex = viewName === COLUMN_HEADERS_VIEW ? this._columnsController.getVisibleIndex(column.index, rowIndex) : visibleIndex; if(currentVisibleIndex >= 0) { $cellElement = $rowElements.eq(rowIndex).children().eq(currentVisibleIndex); diff --git a/js/ui/grid_core/ui.grid_core.column_fixing.js b/js/ui/grid_core/ui.grid_core.column_fixing.js index 1265bd50f51e..e095d872e75e 100644 --- a/js/ui/grid_core/ui.grid_core.column_fixing.js +++ b/js/ui/grid_core/ui.grid_core.column_fixing.js @@ -178,6 +178,12 @@ const baseFixedColumns = { $cell.toggleClass(FIRST_CELL_CLASS, options.columnIndex === transparentColumnIndex); } + const isRowAltStyle = that.option('rowAlternationEnabled') && options.isAltRow; + // T823783, T852898, T865179, T875201 + if(browser.mozilla && options.column.fixed && options.rowType !== 'group' && !isRowAltStyle) { + $cell.addClass(FIXED_COL_CLASS); + } + return $cell; }, diff --git a/js/ui/grid_core/ui.grid_core.columns_view.js b/js/ui/grid_core/ui.grid_core.columns_view.js index 7d10d4af3cae..efdc24f2d939 100644 --- a/js/ui/grid_core/ui.grid_core.columns_view.js +++ b/js/ui/grid_core/ui.grid_core.columns_view.js @@ -17,6 +17,7 @@ import { getDefaultAlignment } from '../../core/utils/position'; import modules from './ui.grid_core.modules'; import { checkChanges } from './ui.grid_core.utils'; import columnStateMixin from './ui.grid_core.column_state_mixin'; +import { when, Deferred } from '../../core/utils/deferred'; const SCROLL_CONTAINER_CLASS = 'scroll-container'; const GROUP_SPACE_CLASS = 'group-space'; @@ -25,7 +26,6 @@ const TABLE_CLASS = 'table'; const TABLE_FIXED_CLASS = 'table-fixed'; const CONTENT_FIXED_CLASS = 'content-fixed'; const ROW_CLASS = 'dx-row'; -const FIXED_COL_CLASS = 'dx-col-fixed'; const GROUP_ROW_CLASS = 'dx-group-row'; const DETAIL_ROW_CLASS = 'dx-master-detail-row'; const FILTER_ROW_CLASS = 'filter-row'; @@ -188,11 +188,6 @@ exports.ColumnsView = modules.View.inherit(columnStateMixin).inherit({ } } - // T823783, T852898, T865179 - if(browser.mozilla && options.column.fixed && options.rowType !== 'group' && !options.isAltRow) { - $cell.addClass(FIXED_COL_CLASS); - } - return $cell; }, @@ -391,21 +386,20 @@ exports.ColumnsView = modules.View.inherit(columnStateMixin).inherit({ templateParameters = templates.shift(); const options = templateParameters.options; - const model = options.model; const doc = domAdapter.getDocument(); if(!isAsync || $(options.container).closest(doc).length) { templateParameters.template.render(options); - - if(model && model.column) { - this._updateCell(options.container, model); - } } if(isAsync && (new Date() - date) > 30) { this._renderDelayedTemplatesCoreAsync(templates); break; } } + + if(!templates.length && this._delayedTemplates.length) { + this.renderDelayedTemplates(); + } }, _processTemplate: function(template) { @@ -418,6 +412,7 @@ exports.ColumnsView = modules.View.inherit(columnStateMixin).inherit({ allowRenderToDetachedContainer: template.allowRenderToDetachedContainer, render: function(options) { template.render(options.container, options.model); + options.deferred && options.deferred.resolve(); } }; } else if(typeUtils.isFunction(template)) { @@ -427,6 +422,7 @@ exports.ColumnsView = modules.View.inherit(columnStateMixin).inherit({ if(renderedTemplate && (renderedTemplate.nodeType || typeUtils.isRenderer(renderedTemplate))) { options.container.append(renderedTemplate); } + options.deferred && options.deferred.resolve(); } }; } else { @@ -451,26 +447,35 @@ exports.ColumnsView = modules.View.inherit(columnStateMixin).inherit({ const renderingTemplate = that._processTemplate(template, options); const column = options.column; const isDataRow = options.rowType === 'data'; - let async; + const templateDeferred = new Deferred(); + const templateOptions = { + container: container, + model: options, + deferred: templateDeferred, + onRendered: () => { + templateDeferred.resolve(); + } + }; if(renderingTemplate) { options.component = that.component; - async = column && ( + const async = column && ( (column.renderAsync && isDataRow) || that.option('renderAsync') && (column.renderAsync !== false && (column.command || column.showEditorAlways) && isDataRow || options.rowType === 'filter') ); if((renderingTemplate.allowRenderToDetachedContainer || allowRenderToDetachedContainer) && !async) { - renderingTemplate.render({ container: container, model: options }); - return true; + renderingTemplate.render(templateOptions); } else { - that._delayedTemplates.push({ template: renderingTemplate, options: { container: container, model: options }, async: async }); + that._delayedTemplates.push({ template: renderingTemplate, options: templateOptions, async: async }); } + } else { + templateDeferred.reject(); } - return false; + return templateDeferred.promise(); }, _getBodies: function(tableElement) { @@ -640,9 +645,9 @@ exports.ColumnsView = modules.View.inherit(columnStateMixin).inherit({ _renderCellContent: function($cell, options) { const template = this._getCellTemplate(options); - if((!template || this.renderTemplate($cell, template, options))) { + when(!template || this.renderTemplate($cell, template, options)).done(() => { this._updateCell($cell, options); - } + }); }, _getCellTemplate: function() { }, diff --git a/js/ui/grid_core/ui.grid_core.data_source_adapter.js b/js/ui/grid_core/ui.grid_core.data_source_adapter.js index 59cbc1838a37..48bcea10d1a4 100644 --- a/js/ui/grid_core/ui.grid_core.data_source_adapter.js +++ b/js/ui/grid_core/ui.grid_core.data_source_adapter.js @@ -78,6 +78,7 @@ module.exports = gridCore.Controller.inherit((function() { that._cachedPagesData = createEmptyPagesData(); that._lastOperationTypes = {}; that._eventsStrategy = dataSource._eventsStrategy; + that._skipCorrection = 0; that.changed = Callbacks(); @@ -136,6 +137,7 @@ module.exports = gridCore.Controller.inherit((function() { if(isReload || operationTypes.reload) { that._currentTotalCount = 0; + that._skipCorrection = 0; that._isLastPage = !dataSource.paginate(); that._hasLastPage = that._isLastPage; } @@ -206,8 +208,15 @@ module.exports = gridCore.Controller.inherit((function() { return !dataSource.paginate() || change.type !== 'insert' || change.index !== undefined; }); + const oldItemCount = this.itemsCount(); + arrayUtils.applyBatch(keyInfo, this._items, changes, groupCount, true); arrayUtils.applyBatch(keyInfo, dataSource.items(), changes, groupCount, true); + + if(this._currentTotalCount > 0) { + this._skipCorrection += this.itemsCount() - oldItemCount; + } + changes.splice(0, changes.length); }, _handlePush: function(changes) { @@ -516,7 +525,7 @@ module.exports = gridCore.Controller.inherit((function() { return this._isLastPage; }, totalCount: function() { - return parseInt(this._currentTotalCount || this._dataSource.totalCount()); + return parseInt(this._currentTotalCount + this._skipCorrection || this._dataSource.totalCount()); }, itemsCount: function() { return this._dataSource.items().length; diff --git a/js/ui/grid_core/ui.grid_core.editing.js b/js/ui/grid_core/ui.grid_core.editing.js index d0d2d5a4689a..156c66b518cc 100644 --- a/js/ui/grid_core/ui.grid_core.editing.js +++ b/js/ui/grid_core/ui.grid_core.editing.js @@ -22,7 +22,7 @@ import Form from '../form'; import holdEvent from '../../events/hold'; import { when, Deferred, fromPromise } from '../../core/utils/deferred'; import commonUtils from '../../core/utils/common'; -import iconUtils from '../../core/utils/icon'; +import * as iconUtils from '../../core/utils/icon'; import Scrollable from '../scroll_view/ui.scrollable'; import deferredUtils from '../../core/utils/deferred'; @@ -1819,7 +1819,7 @@ const EditingController = modules.ViewController.inherit((function() { _prepareEditDataParams: function(options, value, text) { const that = this; const newData = {}; - const oldData = options.data; + const oldData = options.row?.data; const rowKey = options.key; const $cellElement = $(options.cellElement); const editMode = getEditMode(that); @@ -1890,6 +1890,9 @@ const EditingController = modules.ViewController.inherit((function() { const showEditorAlways = options.column.showEditorAlways; const isUpdateInCellMode = editMode === EDIT_MODE_CELL && options.row && !options.row.isNewRow; const focusPreviousEditingCell = showEditorAlways && !forceUpdateRow && isUpdateInCellMode && that.hasEditData() && !that.isEditCell(options.rowIndex, options.columnIndex); + const columns = that._columnsController.getVisibleColumns(); + const isCustomCalculateCellValue = columns.some((column) => column.calculateCellValue !== column.defaultCalculateCellValue); + let focusCellAfterRowUpdate = false; if(focusPreviousEditingCell) { that._focusEditingCell(); @@ -1900,14 +1903,18 @@ const EditingController = modules.ViewController.inherit((function() { that._addEditData(params, options.row); that._updateEditButtons(); + if(editMode === EDIT_MODE_CELL && (isCustomSetCellValue || isCustomCalculateCellValue)) { + forceUpdateRow = focusCellAfterRowUpdate = true; + } + if(showEditorAlways && !forceUpdateRow) { if(isUpdateInCellMode) { - that._editRowIndex = options.rowIndex + that._dataController.getRowIndexOffset(); + that._editRowIndex = options.row.rowIndex + that._dataController.getRowIndexOffset(); + that._editColumnIndex = options.columnIndex; return that.saveEditData(); } else if(editMode === EDIT_MODE_BATCH) { - const columns = that._columnsController.getVisibleColumns(); - forceUpdateRow = isCustomSetCellValue || columns.some((column) => column.calculateCellValue !== column.defaultCalculateCellValue); + forceUpdateRow = isCustomSetCellValue || isCustomCalculateCellValue; } } @@ -1915,6 +1922,7 @@ const EditingController = modules.ViewController.inherit((function() { if(row) { if(forceUpdateRow || isCustomSetCellValue) { that._updateEditRow(row, forceUpdateRow, isCustomSetCellValue); + focusCellAfterRowUpdate && that._focusEditingCell(); } else if(row.update) { row.update(); } @@ -2018,7 +2026,7 @@ const EditingController = modules.ViewController.inherit((function() { const $container = $(container); const column = item.column; const editorType = getEditorType(item); - const rowData = detailCellOptions.row && detailCellOptions.row.data; + const rowData = detailCellOptions?.row.data; const cellOptions = extend({}, detailCellOptions, { data: rowData, cellElement: null, @@ -2035,10 +2043,9 @@ const EditingController = modules.ViewController.inherit((function() { cellOptions.value = column.calculateCellValue(rowData); const template = that._getFormEditItemTemplate.bind(that)(cellOptions, column); - - if(that._rowsView.renderTemplate($container, template, cellOptions, !!$container.closest(getWindow().document).length)) { + that._rowsView.renderTemplate($container, template, cellOptions, !!$container.closest(getWindow().document).length).done(() => { that._rowsView._updateCell($container, cellOptions); - } + }); return cellOptions; }, @@ -2054,7 +2061,7 @@ const EditingController = modules.ViewController.inherit((function() { }, function() { let $editorElement = $container.find('.dx-widget').first(); let validator = $editorElement.data('dxValidator'); - const validatorOptions = validator && validator.option(); + const validatorOptions = validator?.option(); $container.contents().remove(); cellOptions = that.renderFormEditTemplate.bind(that)(cellOptions, item, options.component, $container); @@ -2099,7 +2106,7 @@ const EditingController = modules.ViewController.inherit((function() { }); } else { forEachFormItems(items, (item) => { - const itemId = item && (item.name || item.dataField); + const itemId = item?.name || item?.dataField; if(itemId) { isCustomEditorType[itemId] = !!item.editorType; @@ -2233,6 +2240,7 @@ const EditingController = modules.ViewController.inherit((function() { eventsEngine.on($button, addNamespace('click', EDITING_NAMESPACE), that.createAction(function(e) { button.onClick.call(button, extend({}, e, { row: options.row, column: options.column })); e.event.preventDefault(); + e.event.stopPropagation(); })); options.rtlEnabled ? $container.prepend($button, ' ') : $container.append($button, ' '); } diff --git a/js/ui/grid_core/ui.grid_core.filter_custom_operations.js b/js/ui/grid_core/ui.grid_core.filter_custom_operations.js index 4b8075c113c7..252ce446f73a 100644 --- a/js/ui/grid_core/ui.grid_core.filter_custom_operations.js +++ b/js/ui/grid_core/ui.grid_core.filter_custom_operations.js @@ -53,13 +53,13 @@ function baseOperation(grid) { column = extend({}, column, { filterType: 'include', filterValues: [value] }); const dataSourceOptions = headerFilterController.getDataSource(column); dataSourceOptions.paginate = false; - const headerFilterDataSource = headerFilter && headerFilter.dataSource; - if(!headerFilterDataSource && lookup.items) { - dataSourceOptions.store = lookup.items; - } const dataSource = new DataSource(dataSourceOptions); const result = new deferredUtils.Deferred(); + const key = dataSource.store().key(); + if(key) { + dataSource.filter([key, '=', fieldInfo.value]); + } dataSource.load().done(items => { result.resolve(getSelectedItemsTexts(items)[0]); }); diff --git a/js/ui/grid_core/ui.grid_core.filter_row.js b/js/ui/grid_core/ui.grid_core.filter_row.js index 6a38e8309cd6..c9706d9dd527 100644 --- a/js/ui/grid_core/ui.grid_core.filter_row.js +++ b/js/ui/grid_core/ui.grid_core.filter_row.js @@ -414,7 +414,9 @@ const ColumnHeadersViewFilterRowExtender = (function() { if(column.command) { $cell.html(' '); } else if(column.allowFiltering) { - that.renderTemplate($cell, that._renderFilterCell.bind(that), options); + that.renderTemplate($cell, that._renderFilterCell.bind(that), options).done(() => { + that._updateCell($cell, options); + }); return; } } diff --git a/js/ui/grid_core/ui.grid_core.focus.js b/js/ui/grid_core/ui.grid_core.focus.js index b2d34b9d8172..5aa9a2a173ec 100644 --- a/js/ui/grid_core/ui.grid_core.focus.js +++ b/js/ui/grid_core/ui.grid_core.focus.js @@ -256,8 +256,6 @@ exports.FocusController = core.ViewController.inherit((function() { const focusedRowIndex = this.getFocusedRowIndexByKey(key); if(this._isValidFocusedRowIndex(focusedRowIndex)) { - this.getController('keyboardNavigation').setFocusedRowIndex(focusedRowIndex); - if(this.option('focusedRowEnabled')) { dataController.updateItems({ changeType: 'updateFocusedRow', @@ -267,6 +265,8 @@ exports.FocusController = core.ViewController.inherit((function() { this.getView('rowsView').scrollToRowElement(key); } + this.getController('keyboardNavigation').setFocusedRowIndex(focusedRowIndex); + deferred && deferred.resolve(focusedRowIndex); } else { deferred && deferred.resolve(-1); @@ -450,8 +450,9 @@ module.exports = { _updateFocusedCellPosition: function($cell, direction) { const prevRowIndex = this.option('focusedRowIndex'); const prevColumnIndex = this.option('focusedColumnIndex'); + const position = this.callBase($cell, direction); - if(this.callBase($cell, direction)) { + if(position && position.columnIndex >= 0) { this._fireFocusedCellChanged($cell, prevColumnIndex, prevRowIndex); } } diff --git a/js/ui/grid_core/ui.grid_core.header_filter.js b/js/ui/grid_core/ui.grid_core.header_filter.js index 79135dee0867..c7b171539bd1 100644 --- a/js/ui/grid_core/ui.grid_core.header_filter.js +++ b/js/ui/grid_core/ui.grid_core.header_filter.js @@ -158,8 +158,7 @@ const HeaderFilterController = modules.ViewController.inherit((function() { const that = this; let filter; let cutoffLevel; - let origPostProcess; - let dataSource = that._dataController.dataSource(); + const dataSource = that._dataController.dataSource(); const group = gridCoreUtils.getHeaderFilterGroupParameters(column, dataSource && dataSource.remoteOperations().grouping); const headerFilterDataSource = column.headerFilter && column.headerFilter.dataSource; const headerFilterOptions = that.option('headerFilter'); @@ -174,12 +173,16 @@ const HeaderFilterController = modules.ViewController.inherit((function() { options.dataSource = normalizeDataSourceOptions(headerFilterDataSource); } else if(column.lookup) { isLookup = true; - dataSource = column.lookup.dataSource; - if(isFunction(dataSource) && !isWrapped(dataSource)) { - dataSource = dataSource({}); + let lookupDataSourceOptions; + if(column.lookup.items) { + lookupDataSourceOptions = column.lookup.items; + } else { + lookupDataSourceOptions = column.lookup.dataSource; + if(isFunction(lookupDataSourceOptions) && !isWrapped(lookupDataSourceOptions)) { + lookupDataSourceOptions = lookupDataSourceOptions({}); + } } - dataSource = normalizeDataSourceOptions(dataSource); - options.dataSource = dataSource; + options.dataSource = normalizeDataSourceOptions(lookupDataSourceOptions); } else { cutoffLevel = Array.isArray(group) ? group.length - 1 : 0; @@ -214,7 +217,7 @@ const HeaderFilterController = modules.ViewController.inherit((function() { headerFilterDataSource.call(column, options); } - origPostProcess = options.dataSource.postProcess; + const origPostProcess = options.dataSource.postProcess; options.dataSource.postProcess = function(data) { let items = data; diff --git a/js/ui/grid_core/ui.grid_core.header_filter_core.js b/js/ui/grid_core/ui.grid_core.header_filter_core.js index e095d5d8fbf5..bad92c188037 100644 --- a/js/ui/grid_core/ui.grid_core.header_filter_core.js +++ b/js/ui/grid_core/ui.grid_core.header_filter_core.js @@ -288,7 +288,9 @@ exports.HeaderFilterView = modules.View.inherit({ const selectedItems = e.component.option('selectedItems'); if(!e.component._selectedItemsUpdating && !e.component.option('searchValue') && !options.isFilterBuilder) { - if(selectedItems.length === 0 && items.length && (!options.filterValues || options.filterValues.length <= 1)) { + const filterValues = options.filterValues || []; + const isExclude = options.filterType === 'exclude'; + if(selectedItems.length === 0 && items.length && (filterValues.length <= 1 || isExclude && filterValues.length === items.length - 1)) { options.filterType = 'include'; options.filterValues = []; } else if(selectedItems.length === items.length) { diff --git a/js/ui/grid_core/ui.grid_core.keyboard_navigation.js b/js/ui/grid_core/ui.grid_core.keyboard_navigation.js index 36a6ce9fcdca..420e8685b77b 100644 --- a/js/ui/grid_core/ui.grid_core.keyboard_navigation.js +++ b/js/ui/grid_core/ui.grid_core.keyboard_navigation.js @@ -166,8 +166,7 @@ const KeyboardNavigationController = core.ViewController.inherit({ _setRowsViewAttributes: function($rowsView) { const isGridEmpty = !this._dataController.getVisibleRows().length; if(isGridEmpty) { - const tabIndex = this.option('tabindex') || 0; - $rowsView.attr('tabindex', tabIndex); + this._applyTabIndexToElement($rowsView); } }, @@ -232,7 +231,7 @@ const KeyboardNavigationController = core.ViewController.inherit({ this._isNeedFocus = true; this._isNeedScroll = true; - this._updateFocusedCellPosition(this._getCellElementFromTarget(originalEvent.target)); + this._updateFocusedCellPositionByTarget(originalEvent.target); if(!isHandled) { switch(e.keyName) { @@ -513,7 +512,7 @@ const KeyboardNavigationController = core.ViewController.inherit({ isOriginalHandlerRequired = true; } else { if(this._focusedCellPosition.rowIndex === undefined && $(eventTarget).hasClass(ROW_CLASS)) { - this._updateFocusedCellPosition($(eventTarget).children().first()); + this._updateFocusedCellPosition($cell); } elementType = this._getElementType(eventTarget); @@ -751,7 +750,7 @@ const KeyboardNavigationController = core.ViewController.inherit({ if($parent.hasClass(FREESPACE_ROW_CLASS)) { this._updateFocusedCellPosition($target); - this._focusedView.element().attr('tabindex', 0); + this._applyTabIndexToElement(this._focusedView.element()); this._focusedView.focus(); } else if(!this._isMasterDetailCell($target) && !isEditingRow) { this._clickTargetCellHandler(event, $target); @@ -899,7 +898,6 @@ const KeyboardNavigationController = core.ViewController.inherit({ return; } - const $prevFocusedCell = this._getFocusedCell(); const focusedView = this._focusedView; const $focusViewElement = focusedView && focusedView.element(); let $focusElement; @@ -915,10 +913,15 @@ const KeyboardNavigationController = core.ViewController.inherit({ $focusElement = $cell; this._updateFocusedCellPosition($cell); } - - $prevFocusedCell && $prevFocusedCell.is('td') && $prevFocusedCell.not($focusElement).removeAttr('tabIndex'); - if($focusElement) { + if($focusViewElement) { + $focusViewElement + .find('.dx-row[tabIndex], .dx-row > td[tabindex]') + .not($focusElement) + .removeClass(CELL_FOCUS_DISABLED_CLASS) + .removeAttr('tabIndex'); + } + eventsEngine.one($focusElement, 'blur', e => { if(e.relatedTarget) { $focusElement.removeClass(CELL_FOCUS_DISABLED_CLASS); @@ -929,10 +932,8 @@ const KeyboardNavigationController = core.ViewController.inherit({ eventsEngine.trigger($focusElement, 'focus'); } if(disableFocus) { - $focusViewElement && $focusViewElement.find('.' + CELL_FOCUS_DISABLED_CLASS + '[tabIndex]').not($focusElement).removeClass(CELL_FOCUS_DISABLED_CLASS).removeAttr('tabIndex'); $focusElement.addClass(CELL_FOCUS_DISABLED_CLASS); } else { - $focusViewElement && $focusViewElement.find('.' + CELL_FOCUS_DISABLED_CLASS + ':not(.' + MASTER_DETAIL_CELL_CLASS + ')').removeClass(CELL_FOCUS_DISABLED_CLASS); this.getController('editorFactory').focus($focusElement); } } @@ -992,6 +993,16 @@ const KeyboardNavigationController = core.ViewController.inherit({ return $(this._getCell(this._focusedCellPosition)); }, + _updateFocusedCellPositionByTarget: function(target) { + const elementType = this._getElementType(target); + if(elementType === 'row' && isDefined(this._focusedCellPosition?.columnIndex)) { + const $row = $(target); + this._focusedView && isGroupRow($row) && this.setFocusedRowIndex(this._getRowIndex($row)); + } else { + this._updateFocusedCellPosition(this._getCellElementFromTarget(target)); + } + }, + _updateFocusedCellPosition: function($cell, direction) { const position = this._getCellPosition($cell, direction); if(position) { @@ -1006,11 +1017,12 @@ const KeyboardNavigationController = core.ViewController.inherit({ let rowIndex; let columnIndex; const $row = isElementDefined($cell) && $cell.closest('tr'); + const rowsView = this.getView('rowsView'); - if(isElementDefined($row) && that._focusedView) { + if(isElementDefined($row)) { rowIndex = that._getRowIndex($row); - columnIndex = that._focusedView.getCellIndex($cell, rowIndex); + columnIndex = rowsView.getCellIndex($cell, rowIndex); if(direction) { columnIndex = direction === 'previous' ? columnIndex - 1 : columnIndex + 1; @@ -1215,7 +1227,7 @@ const KeyboardNavigationController = core.ViewController.inherit({ columnIndex: nextColumnIndex, rowIndex: rowIndex }; - const visibleRows = this.component.getVisibleRows(); + const visibleRows = this.getController('data').getVisibleRows(); const row = visibleRows && visibleRows[rowIndex]; const isLastRow = this._isLastRow(rowIndex); @@ -1575,8 +1587,8 @@ const KeyboardNavigationController = core.ViewController.inherit({ }, _applyTabIndexToElement: function($element) { - const tabIndex = this.option('tabIndex'); - $element.attr('tabIndex', isDefined(tabIndex) ? tabIndex : 0); + const tabIndex = this.option('tabIndex') || 0; + $element.attr('tabindex', isDefined(tabIndex) ? tabIndex : 0); }, _getCell: function(cellPosition) { @@ -1589,12 +1601,8 @@ const KeyboardNavigationController = core.ViewController.inherit({ }, _getRowIndex: function($row) { - const focusedView = this._focusedView; - let rowIndex = -1; - - if(focusedView) { - rowIndex = focusedView.getRowIndex($row); - } + const rowsView = this.getView('rowsView'); + let rowIndex = rowsView.getRowIndex($row); if(rowIndex >= 0) { rowIndex += this.getController('data').getRowIndexOffset(); @@ -1697,7 +1705,15 @@ const KeyboardNavigationController = core.ViewController.inherit({ }, _getCellElementFromTarget: function(target) { - return $(target).closest(`.${ROW_CLASS} > td`); + const elementType = this._getElementType(target); + const $targetElement = $(target); + let $cell; + if(elementType === 'cell') { + $cell = $targetElement.closest(`.${ROW_CLASS} > td`); + } else { + $cell = $targetElement.children().not('.' + COMMAND_EXPAND_CLASS).first(); + } + return $cell; }, _getRowsViewElement: function() { @@ -1814,30 +1830,27 @@ module.exports = { } }, updateFocusElementTabIndex: function(cellElements) { - const that = this; + const keyboardController = this.getController('keyboardNavigation'); const $row = cellElements.eq(0).parent(); - let columnIndex = that.option('focusedColumnIndex'); - const tabIndex = that.option('tabIndex') || 0; + let columnIndex = this.option('focusedColumnIndex'); if(!columnIndex || columnIndex < 0) { columnIndex = 0; } if(isGroupRow($row)) { - $row.attr('tabIndex', tabIndex); + keyboardController._applyTabIndexToElement($row); } else { - that._updateFocusedCellTabIndex(cellElements, columnIndex); + this._updateFocusedCellTabIndex(cellElements, columnIndex); } }, _updateFocusedCellTabIndex: function(cellElements, columnIndex) { - const that = this; let $cell; - const tabIndex = that.option('tabIndex') || 0; - const keyboardNavigation = that.getController('keyboardNavigation'); - const oldFocusedView = keyboardNavigation._focusedView; + const keyboardController = this.getController('keyboardNavigation'); + const oldFocusedView = keyboardController._focusedView; const cellElementsLength = cellElements ? cellElements.length : -1; - keyboardNavigation._focusedView = that; + keyboardController._focusedView = this; if(cellElementsLength > 0) { if(cellElementsLength <= columnIndex) { @@ -1845,17 +1858,17 @@ module.exports = { } for(let i = columnIndex; i < cellElementsLength; ++i) { $cell = $(cellElements[i]); - if(!keyboardNavigation._isMasterDetailCell($cell)) { - if(keyboardNavigation._isCellValid($cell) && isCellElement($cell)) { - $cell.attr('tabIndex', tabIndex); - keyboardNavigation.setCellFocusType(); + if(!keyboardController._isMasterDetailCell($cell)) { + if(keyboardController._isCellValid($cell) && isCellElement($cell)) { + keyboardController._applyTabIndexToElement($cell); + keyboardController.setCellFocusType(); break; } } } } - keyboardNavigation._focusedView = oldFocusedView; + keyboardController._focusedView = oldFocusedView; }, renderDelayedTemplates: function(change) { diff --git a/js/ui/grid_core/ui.grid_core.virtual_scrolling.js b/js/ui/grid_core/ui.grid_core.virtual_scrolling.js index 4bdd53691c10..7caebb289b07 100644 --- a/js/ui/grid_core/ui.grid_core.virtual_scrolling.js +++ b/js/ui/grid_core/ui.grid_core.virtual_scrolling.js @@ -248,6 +248,8 @@ const VirtualScrollingDataSourceAdapterExtender = (function() { storeLoadOptions.skip = that.pageIndex() * that.pageSize(); } } + } else if(isAppendMode(that) && storeLoadOptions.skip) { + storeLoadOptions.skip += that._skipCorrection; } return that.callBase.apply(that, arguments); }, diff --git a/js/ui/hierarchical_collection/ui.hierarchical_collection_widget.js b/js/ui/hierarchical_collection/ui.hierarchical_collection_widget.js index 8e960f24ea2e..ab91cb399866 100644 --- a/js/ui/hierarchical_collection/ui.hierarchical_collection_widget.js +++ b/js/ui/hierarchical_collection/ui.hierarchical_collection_widget.js @@ -3,7 +3,7 @@ import { compileGetter, compileSetter } from '../../core/utils/data'; import { extend } from '../../core/utils/extend'; import { each } from '../../core/utils/iterator'; import devices from '../../core/devices'; -import iconUtils from '../../core/utils/icon'; +import { getImageContainer } from '../../core/utils/icon'; import HierarchicalDataAdapter from './ui.data_adapter'; import CollectionWidget from '../collection/ui.collection_widget.edit'; import { BindableTemplate } from '../../core/templates/bindable_template'; @@ -93,7 +93,7 @@ const HierarchicalCollectionWidget = CollectionWidget.inherit({ }, _getIconContainer: function(itemData) { - return itemData.icon ? iconUtils.getImageContainer(itemData.icon) : undefined; + return itemData.icon ? getImageContainer(itemData.icon) : undefined; }, _getTextContainer: function(itemData) { diff --git a/js/ui/html_editor.d.ts b/js/ui/html_editor.d.ts index a9a91e2709ad..7a62ce5eb13b 100644 --- a/js/ui/html_editor.d.ts +++ b/js/ui/html_editor.d.ts @@ -423,7 +423,7 @@ export interface dxHtmlEditorToolbar { * @prevFileNamespace DevExpress.ui * @public */ - items?: Array; + items?: Array; /** * @docid dxHtmlEditorToolbar.multiline * @type boolean @@ -441,7 +441,7 @@ export interface dxHtmlEditorToolbarItem extends dxToolbarItem { * @prevFileNamespace DevExpress.ui * @public */ - formatName?: 'background' | 'bold' | 'color' | 'italic' | 'link' | 'image' | 'strike' | 'subscript' | 'superscript' | 'underline' | 'blockquote' | 'header' | 'increaseIndent' | 'decreaseIndent' | 'orderedList' | 'bulletList' | 'alignLeft' | 'alignCenter' | 'alignRight' | 'alignJustify' | 'codeBlock' | 'variable' | 'separator' | 'undo' | 'redo' | 'clear' | string; + formatName?: 'background' | 'bold' | 'color' | 'font' | 'italic' | 'link' | 'image' | 'size' | 'strike' | 'subscript' | 'superscript' | 'underline' | 'blockquote' | 'header' | 'increaseIndent' | 'decreaseIndent' | 'orderedList' | 'bulletList' | 'alignLeft' | 'alignCenter' | 'alignRight' | 'alignJustify' | 'codeBlock' | 'variable' | 'separator' | 'undo' | 'redo' | 'clear' | string; /** * @docid dxHtmlEditorToolbarItem.formatValues * @type Array diff --git a/js/ui/html_editor/modules/resizing.js b/js/ui/html_editor/modules/resizing.js index 264ffddb6781..b25829e87220 100644 --- a/js/ui/html_editor/modules/resizing.js +++ b/js/ui/html_editor/modules/resizing.js @@ -1,10 +1,11 @@ import $ from '../../../core/renderer'; import eventsEngine from '../../../events/core/events_engine'; import { name as ClickEvent } from '../../../events/click'; -import { addNamespace } from '../../../events/utils'; +import { addNamespace, normalizeKeyName } from '../../../events/utils'; import { move } from '../../../animation/translator'; import devices from '../../../core/devices'; import Resizable from '../../resizable'; +import Quill from 'quill'; const DX_RESIZE_FRAME_CLASS = 'dx-resize-frame'; const DX_TOUCH_DEVICE_CLASS = 'dx-touch-device'; @@ -68,7 +69,15 @@ export default class ResizingModule { showFrame() { this._$resizeFrame.show(); - eventsEngine.on(this.quill.root, KEYDOWN_EVENT, this.hideFrame.bind(this)); + eventsEngine.on(this.quill.root, KEYDOWN_EVENT, this._handleFrameKeyDown.bind(this)); + } + + _handleFrameKeyDown(e) { + const keyName = normalizeKeyName(e); + if(keyName === 'del' || keyName === 'backspace') { + this._deleteImage(); + } + this.hideFrame(); } hideFrame() { @@ -127,6 +136,12 @@ export default class ResizingModule { }); } + _deleteImage() { + if(this._isAllowedTarget(this._$target)) { + Quill.find(this._$target).deleteAt(0); + } + } + option(option, value) { if(option === 'mediaResizing') { Object.keys(value).forEach((optionName) => this.option(optionName, value[optionName])); diff --git a/js/ui/lookup.js b/js/ui/lookup.js index 4fe513461703..f1d00e0abc7a 100644 --- a/js/ui/lookup.js +++ b/js/ui/lookup.js @@ -320,6 +320,20 @@ const Lookup = DropDownList.inherit({ ]); }, + _init: function() { + this.callBase(); + + this._createScrollAction(); + }, + + _createScrollAction: function() { + this._scrollAction = this._createActionByOption('onScroll'); + }, + + _scrollHandler: function(e) { + this._scrollAction(e); + }, + _initTemplates: function() { this.callBase(); this._templateManager.addDefaultTemplates({ @@ -811,7 +825,7 @@ const Lookup = DropDownList.inherit({ pulledDownText: this.option('pulledDownText'), refreshingText: this.option('refreshingText'), pageLoadingText: this.option('pageLoadingText'), - onScroll: this.option('onScroll'), + onScroll: this._scrollHandler.bind(this), onPullRefresh: this.option('onPullRefresh'), onPageLoading: this.option('onPageLoading'), pageLoadMode: this.option('pageLoadMode'), @@ -973,7 +987,6 @@ const Lookup = DropDownList.inherit({ case 'pulledDownText': case 'refreshingText': case 'pageLoadingText': - case 'onScroll': case 'onPullRefresh': case 'onPageLoading': case 'nextButtonText': @@ -981,6 +994,9 @@ const Lookup = DropDownList.inherit({ case 'groupTemplate': this._setListOption(name); break; + case 'onScroll': + this._createScrollAction(); + break; case 'pageLoadMode': this._setListOption('pageLoadMode', this.option('pageLoadMode')); break; diff --git a/js/ui/number_box/number_box.base.js b/js/ui/number_box/number_box.base.js index 90687ad53c44..dca39434c219 100644 --- a/js/ui/number_box/number_box.base.js +++ b/js/ui/number_box/number_box.base.js @@ -270,11 +270,11 @@ const NumberBoxBase = TextEditor.inherit({ const min = this.option('min'); const max = this.option('max'); - if(min !== undefined) { + if(typeUtils.isDefined(min)) { value = Math.max(min, value); } - if(max !== undefined) { + if(typeUtils.isDefined(max)) { value = Math.min(max, value); } diff --git a/js/ui/number_box/number_box.mask.js b/js/ui/number_box/number_box.mask.js index 12baf44b8e23..84456cb67772 100644 --- a/js/ui/number_box/number_box.mask.js +++ b/js/ui/number_box/number_box.mask.js @@ -5,13 +5,13 @@ const browser = require('../../core/utils/browser'); const devices = require('../../core/devices'); const fitIntoRange = require('../../core/utils/math').fitIntoRange; const inRange = require('../../core/utils/math').inRange; -const escapeRegExp = require('../../core/utils/common').escapeRegExp; const number = require('../../localization/number'); const maskCaret = require('./number_box.caret'); const getLDMLFormat = require('../../localization/ldml/number').getFormat; const NumberBoxBase = require('./number_box.base'); const eventUtils = require('../../events/utils'); const typeUtils = require('../../core/utils/type'); +const { ensureDefined, escapeRegExp } = require('../../core/utils/common'); const NUMBER_FORMATTER_NAMESPACE = 'dxNumberFormatter'; const MOVE_FORWARD = 1; @@ -23,10 +23,6 @@ const INPUT_EVENT = 'input'; const CARET_TIMEOUT_DURATION = browser.msie ? 300 : 0; // If we move caret before the second click, IE can prevent browser text selection on double click -const ensureDefined = function(value, defaultValue) { - return value === undefined ? defaultValue : value; -}; - const NumberBoxMask = NumberBoxBase.inherit({ _getDefaultOptions: function() { diff --git a/js/ui/overlay/utils.js b/js/ui/overlay/utils.js index 5eee5024706f..dcd940c7cfe5 100644 --- a/js/ui/overlay/utils.js +++ b/js/ui/overlay/utils.js @@ -1,12 +1,20 @@ import $ from '../../core/renderer'; import { getWindow } from '../../core/utils/window'; +import { isNumeric } from '../../core/utils/type'; const WINDOW_HEIGHT_PERCENT = 0.9; -export const getElementMaxHeightByWindow = $element => { +export const getElementMaxHeightByWindow = ($element, startLocation) => { const window = getWindow(); - const offsetTop = $element.offset().top - $(window).scrollTop(); - const offsetBottom = $(window).innerHeight() - offsetTop - $element.outerHeight(); - return Math.max(offsetTop, offsetBottom) * WINDOW_HEIGHT_PERCENT; + let actualOffset; + if(isNumeric(startLocation)) { + actualOffset = $(window).innerHeight() - startLocation + $(window).scrollTop(); + } else { + const offsetTop = $element.offset().top - $(window).scrollTop(); + const offsetBottom = $(window).innerHeight() - offsetTop - $element.outerHeight(); + actualOffset = Math.max(offsetTop, offsetBottom); + } + + return actualOffset * WINDOW_HEIGHT_PERCENT; }; diff --git a/js/ui/pivot_grid/remote_store.js b/js/ui/pivot_grid/remote_store.js index 8c3f0fbf543c..886db7ba830e 100644 --- a/js/ui/pivot_grid/remote_store.js +++ b/js/ui/pivot_grid/remote_store.js @@ -290,7 +290,7 @@ function parseResult(data, total, descriptions, result) { if(level >= descriptions.rows.length) { if(item) { columnPath[columnLevel] = item.key + ''; - columnItem = getItem(item, 'column', columnPath, columnLevel, descriptions.columns[columnPath.length - 1]); + columnItem = getItem(item, 'column', columnPath, columnLevel, descriptions.columns[columnLevel]); rowItem = rowHash[rowPath.slice(0, rowLevel + 1).join('/')]; } else { result.columns.push({}); @@ -298,7 +298,7 @@ function parseResult(data, total, descriptions, result) { } else { if(item) { rowPath[rowLevel] = item.key + ''; - rowItem = getItem(item, 'row', rowPath, rowLevel, descriptions.rows[rowPath.length - 1]); + rowItem = getItem(item, 'row', rowPath, rowLevel, descriptions.rows[rowLevel]); columnItem = columnHash[columnPath.slice(0, columnLevel + 1).join('/')]; } else { result.rows.push({}); diff --git a/js/ui/pivot_grid/ui.pivot_grid.field_chooser.js b/js/ui/pivot_grid/ui.pivot_grid.field_chooser.js index 829ae6818a8d..3d4f23e41582 100644 --- a/js/ui/pivot_grid/ui.pivot_grid.field_chooser.js +++ b/js/ui/pivot_grid/ui.pivot_grid.field_chooser.js @@ -509,6 +509,7 @@ const FieldChooser = BaseFieldChooser.inherit({ const treeView = that._createComponent(container, TreeView, { dataSource: that._createFieldsDataSource(dataSource), showCheckBoxesMode: 'normal', + expandNodesRecursive: false, searchEnabled: that.option('allowSearch'), searchTimeout: that.option('searchTimeout'), itemTemplate: function(itemData, itemIndex, itemElement) { diff --git a/js/ui/popover.js b/js/ui/popover.js index d1a4e0134543..4c0eb69902da 100644 --- a/js/ui/popover.js +++ b/js/ui/popover.js @@ -7,7 +7,6 @@ const eventsEngine = require('../events/core/events_engine'); const registerComponent = require('../core/component_registrator'); const commonUtils = require('../core/utils/common'); const extend = require('../core/utils/extend').extend; -const browser = require('../core/utils/browser'); const translator = require('../animation/translator'); const positionUtils = require('../animation/position'); const typeUtils = require('../core/utils/type'); @@ -51,8 +50,6 @@ const SIDE_BORDER_WIDTH_STYLES = { 'bottom': 'borderBottomWidth' }; -const isFirefox = browser.mozilla; - const getEventNameByOption = function(optionValue) { return typeUtils.isObject(optionValue) ? optionValue.name : optionValue; }; @@ -514,15 +511,6 @@ const Popover = Popup.inherit({ return horizontalWeight > verticalWeight ? at.h : at.v; }, - _resetContentHeight: function() { - this.callBase(); - if(isFirefox) { // T655040 - const originalOverflow = this._$popupContent.css('overflow'); - this._$popupContent.css('overflow', 'visible'); - this._$popupContent.css('overflow', originalOverflow); - } - }, - _isVerticalSide: function(side) { side = side || this._positionSide; return side === 'top' || side === 'bottom'; diff --git a/js/ui/popup.js b/js/ui/popup.js index e26aa7446e19..a503c575fee8 100644 --- a/js/ui/popup.js +++ b/js/ui/popup.js @@ -487,7 +487,8 @@ const Popup = Overlay.inherit({ _resetContentHeight: function() { this._$popupContent.css({ - 'height': 'auto' + 'height': 'auto', + 'maxHeight': 'none' }); }, diff --git a/js/ui/resizable.js b/js/ui/resizable.js index abe254bb0e5a..eba81530ca47 100644 --- a/js/ui/resizable.js +++ b/js/ui/resizable.js @@ -98,6 +98,7 @@ const Resizable = DOMComponent.inherit({ }, _renderHandles: function() { + this._handles = []; const handles = this.option('handles'); if(handles === 'none') { @@ -114,21 +115,19 @@ const Resizable = DOMComponent.inherit({ inArray('bottom', directions) + 1 && inArray('left', directions) + 1 && this._renderHandle('corner-bottom-left'); inArray('top', directions) + 1 && inArray('right', directions) + 1 && this._renderHandle('corner-top-right'); inArray('top', directions) + 1 && inArray('left', directions) + 1 && this._renderHandle('corner-top-left'); + this._attachEventHandlers(); }, _renderHandle: function(handleName) { - const $element = this.$element(); - const $handle = $('
'); - - $handle + const $handle = $('
') .addClass(RESIZABLE_HANDLE_CLASS) .addClass(RESIZABLE_HANDLE_CLASS + '-' + handleName) - .appendTo($element); + .appendTo(this.$element()); - this._attachEventHandlers($handle); + this._handles.push($handle); }, - _attachEventHandlers: function($handle) { + _attachEventHandlers: function() { if(this.option('disabled')) { return; } @@ -138,12 +137,24 @@ const Resizable = DOMComponent.inherit({ handlers[DRAGSTART_EVENT_NAME] = this._dragHandler.bind(this); handlers[DRAGSTART_END_EVENT_NAME] = this._dragEndHandler.bind(this); - eventsEngine.on($handle, handlers, { - direction: 'both', - immediate: true + this._handles.forEach(function(handleElement) { + eventsEngine.on(handleElement, handlers, { + direction: 'both', + immediate: true + }); + }); + }, + + _detachEventHandlers: function() { + this._handles.forEach(function(handleElement) { + eventsEngine.off(handleElement); }); }, + _toggleEventHandlers: function(shouldAttachEvents) { + shouldAttachEvents ? this._attachEventHandlers() : this._detachEventHandlers(); + }, + _dragStartHandler: function(e) { const $element = this.$element(); if($element.is('.dx-state-disabled, .dx-state-disabled *')) { @@ -412,6 +423,9 @@ const Resizable = DOMComponent.inherit({ _optionChanged: function(args) { switch(args.name) { case 'disabled': + this._toggleEventHandlers(!args.value); + this.callBase(args); + break; case 'handles': this._invalidate(); break; diff --git a/js/ui/scheduler/constants.js b/js/ui/scheduler/constants.js index 0e7642780be6..1927fff990e3 100644 --- a/js/ui/scheduler/constants.js +++ b/js/ui/scheduler/constants.js @@ -2,5 +2,4 @@ export const LIST_ITEM_DATA_KEY = 'dxListItemData'; export const FIXED_CONTAINER_CLASS = 'dx-scheduler-fixed-appointments'; export const LIST_ITEM_CLASS = 'dx-list-item'; export const REDUCED_APPOINTMENT_CLASS = 'dx-scheduler-appointment-reduced'; -export const COMPACT_APPOINTMENT_CLASS = 'dx-scheduler-appointment-compact'; export const RECURRENCE_APPOINTMENT_CLASS = 'dx-scheduler-appointment-recurrence'; diff --git a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.agenda.js b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.agenda.js index f7874628a744..44ae2b43e991 100644 --- a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.agenda.js +++ b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.agenda.js @@ -137,9 +137,6 @@ class AgendaRenderingStrategy extends BaseAppointmentsStrategy { _findIndexByKey() { } - _getMaxNeighborAppointmentCount() { - } - _markAppointmentAsVirtual() { } @@ -150,12 +147,9 @@ class AgendaRenderingStrategy extends BaseAppointmentsStrategy { return this._defaultWidth; } - getCompactAppointmentDefaultWidth() { - } - - getCompactAppointmentLeftOffset() { + getCollectorLeftOffset() { } - getCompactAppointmentTopOffset() { + getCollectorTopOffset() { } calculateRows(appointments, agendaDuration, currentDate, needClearSettings) { diff --git a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.base.js b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.base.js index 9e6b0fdcac5c..c98c3740a9b4 100644 --- a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.base.js +++ b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.base.js @@ -7,12 +7,11 @@ import { isNumeric } from '../../../core/utils/type'; import typeUtils from '../../../core/utils/type'; import themes from '../../themes'; -import utils from '../utils'; +import timeZoneUtils from '../utils.timeZone'; const toMs = dateUtils.dateToMilliseconds; const APPOINTMENT_MIN_SIZE = 2; -const COMPACT_APPOINTMENT_DEFAULT_WIDTH = 15; const APPOINTMENT_DEFAULT_HEIGHT = 20; const COMPACT_THEME_APPOINTMENT_DEFAULT_HEIGHT = 18; @@ -29,9 +28,9 @@ class BaseRenderingStrategy { return this.instance.fire('isAdaptive'); } - _correctCompactAppointmentCoordinatesInAdaptive(coordinates, isAllDay) { - coordinates.top = coordinates.top + this.getCompactAppointmentTopOffset(isAllDay); - coordinates.left = coordinates.left + this.getCompactAppointmentLeftOffset(); + _correctCollectorCoordinatesInAdaptive(coordinates, isAllDay) { + coordinates.top = coordinates.top + this.getCollectorTopOffset(isAllDay); + coordinates.left = coordinates.left + this.getCollectorLeftOffset(); } _initPositioningStrategy() { @@ -423,7 +422,7 @@ class BaseRenderingStrategy { } _skipSortedIndex(index) { - return this.instance.fire('getMaxAppointmentsPerCell') && index > this._getMaxAppointmentCountPerCell() - 1; + return index > this._getMaxAppointmentCountPerCell() - 1; } _findIndexByKey(arr, iKey, jKey, iValue, jValue) { @@ -454,7 +453,10 @@ class BaseRenderingStrategy { return result; } - _checkLongCompactAppointment() { + _checkLongCompactAppointment(item, result) { + this._splitLongCompactAppointment(item, result); + + return result; } _splitLongCompactAppointment(item, result) { @@ -539,7 +541,7 @@ class BaseRenderingStrategy { } _adjustDurationByDaylightDiff(duration, startDate, endDate) { - const daylightDiff = utils.getDaylightOffset(startDate, endDate); + const daylightDiff = timeZoneUtils.getDaylightOffset(startDate, endDate); return this._needAdjustDuration(daylightDiff) ? this._calculateDurationByDaylightDiff(duration, daylightDiff) : duration; } @@ -559,19 +561,9 @@ class BaseRenderingStrategy { }); } - _getMaxNeighborAppointmentCount() { - const overlappingMode = this.instance.fire('getMaxAppointmentsPerCell'); - if(!overlappingMode) { - const outerAppointmentWidth = this.getCompactAppointmentDefaultWidth() + this.getCompactAppointmentLeftOffset(); - return Math.floor(this.getDropDownAppointmentWidth() / outerAppointmentWidth); - } else { - return 0; - } - } - _markAppointmentAsVirtual(coordinates, isAllDay) { const countFullWidthAppointmentInCell = this._getMaxAppointmentCountPerCellByType(isAllDay); - if((coordinates.count - countFullWidthAppointmentInCell) > this._getMaxNeighborAppointmentCount()) { + if((coordinates.count - countFullWidthAppointmentInCell) > 0) { coordinates.virtual = { top: coordinates.top, left: coordinates.left, @@ -617,16 +609,12 @@ class BaseRenderingStrategy { return this._allDayHeight; } - getCompactAppointmentDefaultWidth() { - return COMPACT_APPOINTMENT_DEFAULT_WIDTH; + getCollectorTopOffset(allDay) { + return this.getPositioningStrategy().getCollectorTopOffset(allDay); } - getCompactAppointmentTopOffset(allDay) { - return this.getPositioningStrategy().getCompactAppointmentTopOffset(allDay); - } - - getCompactAppointmentLeftOffset() { - return this.getPositioningStrategy().getCompactAppointmentLeftOffset(); + getCollectorLeftOffset() { + return this.getPositioningStrategy().getCollectorLeftOffset(); } getAppointmentDataCalculator() { @@ -639,21 +627,9 @@ class BaseRenderingStrategy { let top = appointmentTop + topOffset; let width = coordinates.width; let left = coordinates.left; - let compactAppointmentDefaultSize; - let compactAppointmentLeftOffset; - const compactAppointmentTopOffset = this.getCompactAppointmentTopOffset(isAllDay); if(coordinates.isCompact) { - compactAppointmentDefaultSize = this.getCompactAppointmentDefaultWidth(); - compactAppointmentLeftOffset = this.getCompactAppointmentLeftOffset(); - - top = coordinates.top + compactAppointmentTopOffset; - left = coordinates.left + (index - appointmentCountPerCell) * (compactAppointmentDefaultSize + compactAppointmentLeftOffset) + compactAppointmentLeftOffset; - - this._isAdaptive() && this._correctCompactAppointmentCoordinatesInAdaptive(coordinates, isAllDay); - - appointmentHeight = compactAppointmentDefaultSize; - width = compactAppointmentDefaultSize; + this._isAdaptive() && this._correctCollectorCoordinatesInAdaptive(coordinates, isAllDay); this._markAppointmentAsVirtual(coordinates, isAllDay); } @@ -723,9 +699,6 @@ class BaseRenderingStrategy { const overlappingMode = this.instance.fire('getMaxAppointmentsPerCell'); let appointmentCountPerCell; - if(!overlappingMode) { - appointmentCountPerCell = 2; - } if(isNumeric(overlappingMode)) { appointmentCountPerCell = overlappingMode; } diff --git a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.horizontal.js b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.horizontal.js index 385b223a7abf..44d4732f3eb3 100644 --- a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.horizontal.js +++ b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.horizontal.js @@ -1,11 +1,9 @@ import BaseAppointmentsStrategy from './ui.scheduler.appointments.strategy.base'; import dateUtils from '../../../core/utils/date'; -const MAX_APPOINTMENT_HEIGHT = 100; const DEFAULT_APPOINTMENT_HEIGHT = 60; const MIN_APPOINTMENT_HEIGHT = 35; const DROP_DOWN_BUTTON_OFFSET = 2; -const BOTTOM_CELL_GAP = 20; const toMs = dateUtils.dateToMilliseconds; @@ -42,29 +40,9 @@ class HorizontalRenderingStrategy extends BaseAppointmentsStrategy { } _customizeAppointmentGeometry(coordinates) { - const overlappingMode = this.instance.fire('getMaxAppointmentsPerCell'); + const config = this._calculateGeometryConfig(coordinates); - if(overlappingMode) { - const config = this._calculateGeometryConfig(coordinates); - - return this._customizeCoordinates(coordinates, config.height, config.appointmentCountPerCell, config.offset); - } else { - const cellHeight = (this.getDefaultCellHeight() || this.getAppointmentMinSize()) - BOTTOM_CELL_GAP; - let height = cellHeight / coordinates.count; - - if(height > MAX_APPOINTMENT_HEIGHT) { - height = MAX_APPOINTMENT_HEIGHT; - } - - const top = coordinates.top + coordinates.index * height; - - return { - height: height, - width: coordinates.width, - top: top, - left: coordinates.left - }; - } + return this._customizeCoordinates(coordinates, config.height, config.appointmentCountPerCell, config.offset); } _getOffsets() { @@ -74,16 +52,6 @@ class HorizontalRenderingStrategy extends BaseAppointmentsStrategy { }; } - _checkLongCompactAppointment(item, result) { - const overlappingMode = this.instance.fire('getMaxAppointmentsPerCell'); - - if(overlappingMode) { - this._splitLongCompactAppointment(item, result); - - return result; - } - } - _getCompactLeftCoordinate(itemLeft, index) { const cellWidth = this.getDefaultCellWidth() || this.getAppointmentMinSize(); diff --git a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.horizontal_month.js b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.horizontal_month.js index 115a67dd6bc2..712c1d75da7b 100644 --- a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.horizontal_month.js +++ b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.horizontal_month.js @@ -82,12 +82,6 @@ class HorizontalMonthRenderingStrategy extends HorizontalMonthLineAppointmentsSt return this._getAppointmentDefaultHeight(); } - _checkLongCompactAppointment(item, result) { - this._splitLongCompactAppointment(item, result); - - return result; - } - _columnCondition(a, b) { const conditions = this._getConditions(a, b); return conditions.rowCondition || conditions.columnCondition || conditions.cellPositionCondition; @@ -101,12 +95,6 @@ class HorizontalMonthRenderingStrategy extends HorizontalMonthLineAppointmentsSt return super._getSortedPositions(map, true); } - _customizeAppointmentGeometry(coordinates) { - const config = this._calculateGeometryConfig(coordinates); - - return this._customizeCoordinates(coordinates, config.height, config.appointmentCountPerCell, config.offset); - } - _getDefaultRatio() { return MONTH_APPOINTMENT_HEIGHT_RATIO; } diff --git a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.vertical.js b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.vertical.js index e38d062a1cbb..21bbff5958d2 100644 --- a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.vertical.js +++ b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.vertical.js @@ -1,14 +1,8 @@ import BaseAppointmentsStrategy from './ui.scheduler.appointments.strategy.base'; import { extend } from '../../../core/utils/extend'; import { isNumeric } from '../../../core/utils/type'; -import devices from '../../../core/devices'; import dateUtils from '../../../core/utils/date'; -import utils from './../utils'; - -const WEEK_APPOINTMENT_DEFAULT_OFFSET = 25; -const WEEK_APPOINTMENT_MOBILE_OFFSET = 50; - -const APPOINTMENT_MIN_WIDTH = 5; +import timeZoneUtils from './../utils.timeZone'; const ALLDAY_APPOINTMENT_MIN_VERTICAL_OFFSET = 5; const ALLDAY_APPOINTMENT_MAX_VERTICAL_OFFSET = 20; @@ -28,9 +22,9 @@ class VerticalRenderingStrategy extends BaseAppointmentsStrategy { return deltaTime; } - _correctCompactAppointmentCoordinatesInAdaptive(coordinates, isAllDay) { + _correctCollectorCoordinatesInAdaptive(coordinates, isAllDay) { if(isAllDay) { - super._correctCompactAppointmentCoordinatesInAdaptive(coordinates, isAllDay); + super._correctCollectorCoordinatesInAdaptive(coordinates, isAllDay); } else if(this._getMaxAppointmentCountPerCellByType() === 0) { const cellHeight = this.getDefaultCellHeight(); const cellWidth = this.getDefaultCellWidth(); @@ -62,7 +56,7 @@ class VerticalRenderingStrategy extends BaseAppointmentsStrategy { const appointmentStartDate = this.startDate(item, true); const appointmentEndDate = this.endDate(item); - const isAppointmentTakesSeveralDays = !utils.isSameAppointmentDates(appointmentStartDate, appointmentEndDate); + const isAppointmentTakesSeveralDays = !timeZoneUtils.isSameAppointmentDates(appointmentStartDate, appointmentEndDate); if(allDay) { return super._getItemPosition(item); @@ -168,50 +162,19 @@ class VerticalRenderingStrategy extends BaseAppointmentsStrategy { return itemLeft + (cellBorderSize + cellWidth) * index; } - _checkLongCompactAppointment(item, result) { - this._splitLongCompactAppointment(item, result); - - return result; - } - _getVerticalAppointmentGeometry(coordinates) { - const overlappingMode = this.instance.fire('getMaxAppointmentsPerCell'); - - if(overlappingMode) { - const config = this._calculateVerticalGeometryConfig(coordinates); + const config = this._calculateVerticalGeometryConfig(coordinates); - return this._customizeVerticalCoordinates(coordinates, config.width, config.appointmentCountPerCell, config.offset); - } else { - let width = this._getAppointmentMaxWidth() / coordinates.count; - const height = coordinates.height; - const top = coordinates.top; - const left = coordinates.left + (coordinates.index * width); - - if(width < APPOINTMENT_MIN_WIDTH) { - width = APPOINTMENT_MIN_WIDTH; - } - - return { height: height, width: width, top: top, left: left, empty: this._isAppointmentEmpty(height, width) }; - } + return this._customizeVerticalCoordinates(coordinates, config.width, config.appointmentCountPerCell, config.offset); } _customizeVerticalCoordinates(coordinates, width, appointmentCountPerCell, topOffset, isAllDay) { - const index = coordinates.index; let appointmentWidth = Math.max(width / appointmentCountPerCell, width / coordinates.count); const height = coordinates.height; let appointmentLeft = coordinates.left + (coordinates.index * appointmentWidth); let top = coordinates.top; - let compactAppointmentDefaultSize; - let compactAppointmentDefaultOffset; if(coordinates.isCompact) { - compactAppointmentDefaultSize = this.getCompactAppointmentDefaultWidth(); - compactAppointmentDefaultOffset = this.getCompactAppointmentLeftOffset(); - top = coordinates.top + compactAppointmentDefaultOffset; - appointmentLeft = coordinates.left + (index - appointmentCountPerCell) * (compactAppointmentDefaultSize + compactAppointmentDefaultOffset) + compactAppointmentDefaultOffset; - appointmentWidth = compactAppointmentDefaultSize; - width = compactAppointmentDefaultSize; - this._markAppointmentAsVirtual(coordinates, isAllDay); } @@ -267,10 +230,7 @@ class VerticalRenderingStrategy extends BaseAppointmentsStrategy { } _getAppointmentMaxWidth() { - const offset = devices.current().deviceType === 'desktop' && !this.instance.fire('isAdaptive') ? WEEK_APPOINTMENT_DEFAULT_OFFSET : WEEK_APPOINTMENT_MOBILE_OFFSET; - const width = this.getDefaultCellWidth() - offset; - - return width > 0 ? width : this.getAppointmentMinSize(); + return this.getDefaultCellWidth() - this._getAppointmentDefaultOffset(); } calculateAppointmentWidth(appointment, position, isRecurring) { diff --git a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointmentsPositioning.strategy.adaptive.js b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointmentsPositioning.strategy.adaptive.js index c8f4e477adc3..09f37ccf54b2 100644 --- a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointmentsPositioning.strategy.adaptive.js +++ b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointmentsPositioning.strategy.adaptive.js @@ -16,7 +16,7 @@ class AdaptivePositioningStrategy extends BasePositioningStrategy { return COLLECTOR_ADAPTIVE_SIZE; } - getCompactAppointmentTopOffset(allDay) { + getCollectorTopOffset(allDay) { const renderingStrategy = this.getRenderingStrategy(); if(renderingStrategy.hasAllDayAppointments() && allDay) { @@ -26,7 +26,7 @@ class AdaptivePositioningStrategy extends BasePositioningStrategy { } } - getCompactAppointmentLeftOffset() { + getCollectorLeftOffset() { return (this.getRenderingStrategy().getDefaultCellWidth() - COLLECTOR_ADAPTIVE_SIZE) / 2; } diff --git a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointmentsPositioning.strategy.base.js b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointmentsPositioning.strategy.base.js index b7008f8a5182..2d90d07138ab 100644 --- a/js/ui/scheduler/rendering_strategies/ui.scheduler.appointmentsPositioning.strategy.base.js +++ b/js/ui/scheduler/rendering_strategies/ui.scheduler.appointmentsPositioning.strategy.base.js @@ -1,8 +1,8 @@ import typeUtils from '../../../core/utils/type'; const COLLECTOR_DEFAULT_WIDTH = 24; +const COLLECTOR_DEFAULT_OFFSET = 3; -const COMPACT_APPOINTMENT_DEFAULT_OFFSET = 3; const COMPACT_THEME_APPOINTMENT_DEFAULT_OFFSET = 22; const APPOINTMENT_MIN_COUNT = 1; @@ -29,12 +29,12 @@ class AppointmentPositioningStrategy { } } - getCompactAppointmentTopOffset() { - return COMPACT_APPOINTMENT_DEFAULT_OFFSET; + getCollectorTopOffset() { + return COLLECTOR_DEFAULT_OFFSET; } - getCompactAppointmentLeftOffset() { - return COMPACT_APPOINTMENT_DEFAULT_OFFSET; + getCollectorLeftOffset() { + return COLLECTOR_DEFAULT_OFFSET; } getAppointmentDefaultOffset() { diff --git a/js/ui/scheduler/ui.scheduler.appointment.js b/js/ui/scheduler/ui.scheduler.appointment.js index 66096072a391..d0e1dd10047d 100644 --- a/js/ui/scheduler/ui.scheduler.appointment.js +++ b/js/ui/scheduler/ui.scheduler.appointment.js @@ -28,7 +28,6 @@ const DIRECTION_APPOINTMENT_CLASSES = { }; const RECURRENCE_APPOINTMENT_CLASS = 'dx-scheduler-appointment-recurrence'; -const COMPACT_APPOINTMENT_CLASS = 'dx-scheduler-appointment-compact'; const REDUCED_APPOINTMENT_CLASS = 'dx-scheduler-appointment-reduced'; const REDUCED_APPOINTMENT_ICON = 'dx-scheduler-appointment-reduced-icon'; @@ -106,7 +105,6 @@ const Appointment = DOMComponent.inherit({ this._renderAppointmentGeometry(); this._renderEmptyClass(); - this._renderCompactClass(); this._renderReducedAppointment(); this._renderAllDayClass(); this._renderDirection(); @@ -197,10 +195,6 @@ const Appointment = DOMComponent.inherit({ } }, - _renderCompactClass: function() { - this.$element().toggleClass(COMPACT_APPOINTMENT_CLASS, !!this.option('isCompact')); - }, - _renderDirection: function() { this.$element().addClass(DIRECTION_APPOINTMENT_CLASSES[this.option('direction')]); }, @@ -217,7 +211,7 @@ const Appointment = DOMComponent.inherit({ }, _renderResizable: function() { - if(this.option('allowResize') && !this.option('isCompact')) { + if(this.option('allowResize')) { this._createComponent(this.$element(), Resizable, extend(this._createResizingConfig(), this.option('resizableConfig'))); } }, diff --git a/js/ui/scheduler/ui.scheduler.appointments.js b/js/ui/scheduler/ui.scheduler.appointments.js index e29b10b6a7a0..f267634e9c89 100644 --- a/js/ui/scheduler/ui.scheduler.appointments.js +++ b/js/ui/scheduler/ui.scheduler.appointments.js @@ -20,7 +20,7 @@ import dblclickEvent from '../../events/double_click'; import messageLocalization from '../../localization/message'; import CollectionWidget from '../collection/ui.collection_widget.edit'; import { Deferred } from '../../core/utils/deferred'; -import utils from './utils.js'; +import timeZoneUtils from './utils.timeZone.js'; const APPOINTMENT_SETTINGS_NAME = 'dxAppointmentSettings'; @@ -484,7 +484,7 @@ const SchedulerAppointments = CollectionWidget.inherit({ this._applyResourceDataAttr($appointment); const data = this._getItemData($appointment); const geometry = this.invoke('getAppointmentGeometry', settings); - const allowResize = !settings.isCompact && this.option('allowResize') && (!typeUtils.isDefined(settings.skipResizing) || typeUtils.isString(settings.skipResizing)); + const allowResize = this.option('allowResize') && (!typeUtils.isDefined(settings.skipResizing) || typeUtils.isString(settings.skipResizing)); const allowDrag = this.option('allowDrag'); const allDay = settings.allDay; this.invoke('setCellDataCacheAlias', this._currentAppointmentSettings, geometry); @@ -611,12 +611,12 @@ const SchedulerAppointments = CollectionWidget.inherit({ if(isStartDateChanged) { startTime = needCorrectDates ? this._correctStartDateByDelta(startDate, deltaTime) : startDate.getTime() - deltaTime; - startTime += utils.getTimezoneOffsetChangeInMs(startDate, endDate, startTime, endDate); + startTime += timeZoneUtils.getTimezoneOffsetChangeInMs(startDate, endDate, startTime, endDate); endTime = endDate.getTime(); } else { startTime = startDate.getTime(); endTime = needCorrectDates ? this._correctEndDateByDelta(endDate, deltaTime) : endDate.getTime() + deltaTime; - endTime += utils.getTimezoneOffsetChangeInMs(startDate, endDate, startDate, endTime); + endTime -= timeZoneUtils.getTimezoneOffsetChangeInMs(startDate, endDate, startDate, endTime); } return [startTime, endTime]; diff --git a/js/ui/scheduler/ui.scheduler.js b/js/ui/scheduler/ui.scheduler.js index e082d55ccd2a..a3189e5fa719 100644 --- a/js/ui/scheduler/ui.scheduler.js +++ b/js/ui/scheduler/ui.scheduler.js @@ -49,12 +49,13 @@ import { BindableTemplate } from '../../core/templates/bindable_template'; import themes from '../themes'; import browser from '../../core/utils/browser'; import { touch } from '../../core/utils/support'; -import utils from './utils'; +import timeZoneUtils from './utils.timeZone'; const when = deferredUtils.when; const Deferred = deferredUtils.Deferred; const toMs = dateUtils.dateToMilliseconds; +const MINUTES_IN_HOUR = 60; const WIDGET_CLASS = 'dx-scheduler'; const WIDGET_SMALL_CLASS = `${WIDGET_CLASS}-small`; @@ -801,8 +802,13 @@ const Scheduler = Widget.inherit({ break; case 'currentView': this._processCurrentView(); + + this.fire('validateDayHours'); + this.getLayoutManager().initRenderingStrategy(this._getAppointmentsRenderingStrategy()); + this._validateCellDuration(); + this._appointments.option({ items: [], allowDrag: this._allowDragging(), @@ -845,6 +851,7 @@ const Scheduler = Widget.inherit({ break; case 'startDayHour': case 'endDayHour': + this.fire('validateDayHours'); this._appointments.option('items', []); this._updateOption('workSpace', name, value); this._appointments.repaint(); @@ -893,6 +900,7 @@ const Scheduler = Widget.inherit({ }); break; case 'cellDuration': + this._validateCellDuration(); this._appointments.option('items', []); if(this._readyToRenderAppointments) { this._updateOption('workSpace', 'hoursInterval', value / 60); @@ -1060,7 +1068,7 @@ const Scheduler = Widget.inherit({ }, _getTimezoneOffsetByOption: function(date) { - return utils.calculateTimezoneByValue(this.option('timeZone'), date); + return timeZoneUtils.calculateTimezoneByValue(this.option('timeZone'), date); }, getCorrectedDatesByDaylightOffsets: function(originalStartDate, dates, appointmentData) { @@ -1072,7 +1080,7 @@ const Scheduler = Widget.inherit({ dates = dates.map((date) => { const convertedDate = this.fire('convertDateByTimezoneBack', new Date(date.getTime()), startDateTimeZone); - return utils.getCorrectedDateByDaylightOffsets(convertedOriginalStartDate, convertedDate, date, this.option('timeZone'), startDateTimeZone); + return timeZoneUtils.getCorrectedDateByDaylightOffsets(convertedOriginalStartDate, convertedDate, date, this.option('timeZone'), startDateTimeZone); }); } @@ -1446,6 +1454,9 @@ const Scheduler = Widget.inherit({ _initMarkup: function() { this.callBase(); + this.fire('validateDayHours'); + this._validateCellDuration(); + this._processCurrentView(); this._renderHeader(); @@ -1642,6 +1653,16 @@ const Scheduler = Widget.inherit({ }); }, + _validateCellDuration: function() { + const endDayHour = this._getCurrentViewOption('endDayHour'); + const startDayHour = this._getCurrentViewOption('startDayHour'); + const cellDuration = this._getCurrentViewOption('cellDuration'); + + if((endDayHour - startDayHour) * MINUTES_IN_HOUR % cellDuration !== 0) { + errors.log('W1015'); + } + }, + _getCurrentViewType: function() { return this._currentView.type || this._currentView; }, @@ -1861,7 +1882,7 @@ const Scheduler = Widget.inherit({ }, _cleanPopup: function() { - this._appointmentPopup.dispose(); + this._appointmentPopup && this._appointmentPopup.dispose(); }, _convertDatesByTimezoneBack: function(applyAppointmentTimezone, sourceAppointmentData, targetAppointmentData) { @@ -1995,7 +2016,7 @@ const Scheduler = Widget.inherit({ appointmentStartDate.getMilliseconds()); const timezoneDiff = targetStartDate.getTimezoneOffset() - exceptionStartDate.getTimezoneOffset(); - exceptionStartDate = new Date(exceptionStartDate.getTime() - timezoneDiff * toMs('minute')); + exceptionStartDate = new Date(exceptionStartDate.getTime() + timezoneDiff * toMs('minute')); return dateSerialization.serializeDate(exceptionStartDate, UTC_FULL_DATE_FORMAT); }, @@ -2053,7 +2074,7 @@ const Scheduler = Widget.inherit({ } } - endDate = new Date(endDate.getTime() - utils.getTimezoneOffsetChangeInMs(targetStartDate, targetEndDate, date, endDate)); + endDate = new Date(endDate.getTime() - timeZoneUtils.getTimezoneOffsetChangeInMs(targetStartDate, targetEndDate, date, endDate)); this.fire('setField', 'endDate', updatedData, endDate); this._resourcesManager.setResourcesToItem(updatedData, cellData.groups); @@ -2317,10 +2338,9 @@ const Scheduler = Widget.inherit({ const startDate = this.fire('getField', 'startDate', appointmentData); const exceptions = recurrenceException.split(','); const startDateTimeZone = this.fire('getField', 'startDateTimeZone', appointmentData); - const exceptionByStartDate = this.fire('convertDateByTimezone', startDate, startDateTimeZone); for(let i = 0; i < exceptions.length; i++) { - exceptions[i] = this._convertRecurrenceException(exceptions[i], exceptionByStartDate, startDateTimeZone); + exceptions[i] = this._convertRecurrenceException(exceptions[i], startDate, startDateTimeZone); } recurrenceException = exceptions.join(); @@ -2329,13 +2349,16 @@ const Scheduler = Widget.inherit({ return recurrenceException; }, - _convertRecurrenceException: function(exception, exceptionByStartDate, startDateTimeZone) { - exception = exception.replace(/\s/g, ''); - exception = dateSerialization.deserializeDate(exception); - exception = this.fire('convertDateByTimezone', exception, startDateTimeZone); - exception.setHours(exceptionByStartDate.getHours()); - exception = dateSerialization.serializeDate(exception, FULL_DATE_FORMAT); - return exception; + _convertRecurrenceException: function(exceptionString, startDate, startDateTimeZone) { + exceptionString = exceptionString.replace(/\s/g, ''); + + const exceptionDate = dateSerialization.deserializeDate(exceptionString); + const convertedStartDate = this.fire('convertDateByTimezone', startDate, startDateTimeZone); + let convertedExceptionDate = this.fire('convertDateByTimezone', exceptionDate, startDateTimeZone); + + convertedExceptionDate = timeZoneUtils.correctRecurrenceExceptionByTimezone(convertedExceptionDate, convertedStartDate, this.option('timeZone'), startDateTimeZone); + exceptionString = dateSerialization.serializeDate(convertedExceptionDate, FULL_DATE_FORMAT); + return exceptionString; }, dayHasAppointment: function(day, appointment, trimTime) { @@ -2424,7 +2447,7 @@ const Scheduler = Widget.inherit({ }, hideAppointmentPopup: function(saveChanges) { - if(this._appointmentPopup.isVisible()) { + if(this._appointmentPopup && this._appointmentPopup.isVisible()) { saveChanges && this._appointmentPopup.saveChanges(); this._appointmentPopup.hide(); } @@ -2451,7 +2474,7 @@ const Scheduler = Widget.inherit({ }, hideAppointmentTooltip: function() { - this._appointmentTooltip.hide(); + this._appointmentTooltip && this._appointmentTooltip.hide(); }, scrollToTime: function(hours, minutes, date) { diff --git a/js/ui/scheduler/ui.scheduler.navigator.js b/js/ui/scheduler/ui.scheduler.navigator.js index 7d133c66b1b5..f6bd8c4dd689 100644 --- a/js/ui/scheduler/ui.scheduler.navigator.js +++ b/js/ui/scheduler/ui.scheduler.navigator.js @@ -384,7 +384,7 @@ const SchedulerNavigator = Widget.inherit({ const popoverContainer = $('
').addClass(CALENDAR_POPOVER_CLASS); this._popover = this._createComponent(popoverContainer, overlayType, { - onContentReady: this._popoverContentReadyHandler.bind(this), + contentTemplate: () => this._createPopupContent(), defaultOptionsRules: [ { device: function() { @@ -409,10 +409,10 @@ const SchedulerNavigator = Widget.inherit({ this._popover.$element().appendTo(this.$element()); }, - _popoverContentReadyHandler: function() { - this._calendar = this._createComponent($('
'), Calendar, this._calendarOptions()); - this._calendar.$element().addClass(CALENDAR_CLASS); - this._popover.$content().append(this._calendar.$element()); + _createPopupContent: function() { + const result = $('
').addClass(CALENDAR_CLASS); + this._calendar = this._createComponent(result, Calendar, this._calendarOptions()); + return result; }, _calendarOptions: function() { diff --git a/js/ui/scheduler/ui.scheduler.subscribes.js b/js/ui/scheduler/ui.scheduler.subscribes.js index 60f9df5c8b65..82afc13736b9 100644 --- a/js/ui/scheduler/ui.scheduler.subscribes.js +++ b/js/ui/scheduler/ui.scheduler.subscribes.js @@ -4,6 +4,7 @@ import recurrenceUtils from './utils.recurrence'; import typeUtils from '../../core/utils/type'; import dateUtils from '../../core/utils/date'; import { each } from '../../core/utils/iterator'; +import errors from '../widget/ui.errors'; import translator from '../../animation/translator'; import { grep } from '../../core/utils/common'; import { extend } from '../../core/utils/extend'; @@ -11,7 +12,7 @@ import { inArray } from '../../core/utils/array'; import SchedulerTimezones from './timezones/ui.scheduler.timezones'; import { Deferred } from '../../core/utils/deferred'; import dateLocalization from '../../localization/date'; -import utils from './utils'; +import timeZoneUtils from './utils.timeZone'; const MINUTES_IN_HOUR = 60; const toMs = dateUtils.dateToMilliseconds; @@ -718,7 +719,7 @@ const subscribes = { getComplexOffsets: function(scheduler, date, appointmentTimezone) { const clientTimezoneOffset = -this.getClientTimezoneOffset(date) / toMs('hour'); const commonTimezoneOffset = scheduler._getTimezoneOffsetByOption(date); - let appointmentTimezoneOffset = utils.calculateTimezoneByValue(appointmentTimezone, date); + let appointmentTimezoneOffset = timeZoneUtils.calculateTimezoneByValue(appointmentTimezone, date); if(typeof appointmentTimezoneOffset !== 'number') { appointmentTimezoneOffset = clientTimezoneOffset; @@ -780,7 +781,7 @@ const subscribes = { result = ceilQuantityOfDays * visibleDayDuration; } else { - const isDifferentDates = !utils.isSameAppointmentDates(startDate, endDate); + const isDifferentDates = !timeZoneUtils.isSameAppointmentDates(startDate, endDate); const floorQuantityOfDays = Math.floor(appointmentDuration / dayDuration); let tailDuration; @@ -833,6 +834,15 @@ const subscribes = { const dragBehavior = this.getWorkSpace().dragBehavior; dragBehavior && dragBehavior.moveBack(); - } + }, + + validateDayHours: function() { + const endDayHour = this._getCurrentViewOption('endDayHour'); + const startDayHour = this._getCurrentViewOption('startDayHour'); + + if(startDayHour >= endDayHour) { + throw errors.Error('E1058'); + } + }, }; module.exports = subscribes; diff --git a/js/ui/scheduler/utils.recurrence.js b/js/ui/scheduler/utils.recurrence.js index 8c8667d6ff87..625e1be0b5f9 100644 --- a/js/ui/scheduler/utils.recurrence.js +++ b/js/ui/scheduler/utils.recurrence.js @@ -25,6 +25,9 @@ const intervalMap = { const resultUtils = {}; +// Wrong date needs to mark specific date as incorrect. +const wrongDateTime = new Date(0, 0, 0).getTime(); + const dateSetterMap = { 'bysecond': function(date, value) { date.setSeconds(value); @@ -53,8 +56,11 @@ const dateSetterMap = { } } else { - date.setDate(value); - correctDate(date, value); + if(value <= dateUtils.getLastMonthDay(date)) { + date.setDate(value); + } else { + markWrongDate(date); + } } }, 'byday': function(date, byDay, appointmentWeekStart, frequency, firstDayOfWeek) { @@ -725,12 +731,14 @@ function getDatesByCount(dateRules, startDate, recurrenceStartDate, rule) { const dates = getDatesByRules(dateRules, date, rule); const checkedDates = []; - let i; - for(i = 0; i < dates.length; i++) { - if(dates[i].getTime() >= recurrenceStartDate.getTime()) { - checkedDates.push(dates[i]); + dates.forEach(checkedDate => { + if(!isWrongDate(checkedDate)) { + if(checkedDate.getTime() >= recurrenceStartDate.getTime()) { + checkedDates.push(checkedDate); + } } - } + }); + const length = checkedDates.length; counter = counter + length; @@ -740,9 +748,7 @@ function getDatesByCount(dateRules, startDate, recurrenceStartDate, rule) { checkedDates.splice(length - delCount, delCount); } - for(i = 0; i < checkedDates.length; i++) { - result.push(checkedDates[i]); - } + checkedDates.forEach(checkedDate => result.push(checkedDate)); let interval = rule.interval; @@ -788,6 +794,14 @@ function checkDateByRule(date, rules, weekStart) { return result || !rules.length; } +function markWrongDate(date) { + date.setTime(wrongDateTime); +} + +function isWrongDate(date) { + return date.getTime() === wrongDateTime; +} + const getRecurrenceString = function(object) { if(!object || !object.freq) { return; diff --git a/js/ui/scheduler/utils.js b/js/ui/scheduler/utils.timeZone.js similarity index 80% rename from js/ui/scheduler/utils.js rename to js/ui/scheduler/utils.timeZone.js index 136c6aaea83b..77430e31aaf2 100644 --- a/js/ui/scheduler/utils.js +++ b/js/ui/scheduler/utils.timeZone.js @@ -2,6 +2,7 @@ import dateUtils from '../../core/utils/date'; import SchedulerTimezones from './timezones/ui.scheduler.timezones'; const toMs = dateUtils.dateToMilliseconds; +const MINUTES_IN_HOUR = 60; const getTimezoneOffsetChangeInMinutes = (startDate, endDate, updatedStartDate, updatedEndDate) => { return getDaylightOffset(updatedStartDate, updatedEndDate) - getDaylightOffset(startDate, endDate); @@ -46,6 +47,18 @@ const getCorrectedDateByDaylightOffsets = (convertedOriginalStartDate, converted return new Date(date.getTime() - diff * toMs('hour')); }; +const correctRecurrenceExceptionByTimezone = (exception, exceptionByStartDate, timeZone, startDateTimeZone) => { + let timezoneOffset = (exception.getTimezoneOffset() - exceptionByStartDate.getTimezoneOffset()) / MINUTES_IN_HOUR; + + if(startDateTimeZone) { + timezoneOffset = _getDaylightOffsetByTimezone(exceptionByStartDate, exception, startDateTimeZone); + } else if(timeZone) { + timezoneOffset = _getDaylightOffsetByTimezone(exceptionByStartDate, exception, timeZone); + } + + return new Date(exception.getTime() + timezoneOffset * toMs('hour')); +}; + const isTimezoneChangeInDate = (date) => { const startDayDate = new Date((new Date(date)).setHours(0, 0, 0, 0)); const endDayDate = new Date((new Date(date)).setHours(23, 59, 59, 0)); @@ -67,7 +80,8 @@ const utils = { calculateTimezoneByValue: calculateTimezoneByValue, getCorrectedDateByDaylightOffsets: getCorrectedDateByDaylightOffsets, isTimezoneChangeInDate: isTimezoneChangeInDate, - isSameAppointmentDates: isSameAppointmentDates + isSameAppointmentDates: isSameAppointmentDates, + correctRecurrenceExceptionByTimezone: correctRecurrenceExceptionByTimezone }; module.exports = utils; diff --git a/js/ui/scheduler/workspaces/ui.scheduler.agenda.js b/js/ui/scheduler/workspaces/ui.scheduler.agenda.js index 0d868a3800bf..5b6ceef2bc4e 100644 --- a/js/ui/scheduler/workspaces/ui.scheduler.agenda.js +++ b/js/ui/scheduler/workspaces/ui.scheduler.agenda.js @@ -461,6 +461,10 @@ const SchedulerAgenda = SchedulerWorkSpace.inherit({ return new Date(result); }, + getEndViewDateByEndDayHour: function() { + return this.getEndViewDate(); + }, + getCoordinatesByDate: function() { return { top: 0, diff --git a/js/ui/scheduler/workspaces/ui.scheduler.work_space.js b/js/ui/scheduler/workspaces/ui.scheduler.work_space.js index 824ca0f6bd3a..b8fc2bd38fbb 100644 --- a/js/ui/scheduler/workspaces/ui.scheduler.work_space.js +++ b/js/ui/scheduler/workspaces/ui.scheduler.work_space.js @@ -29,7 +29,7 @@ const tableCreator = require('../ui.scheduler.table_creator'); const VerticalShader = require('../shaders/ui.scheduler.current_time_shader.vertical'); const AppointmentDragBehavior = require('../appointmentDragBehavior'); const FIXED_CONTAINER_CLASS = require('../constants').FIXED_CONTAINER_CLASS; -const utils = require('../utils'); +const timeZoneUtils = require('../utils.timeZone'); const COMPONENT_CLASS = 'dx-scheduler-work-space'; const GROUPED_WORKSPACE_CLASS = 'dx-scheduler-work-space-grouped'; @@ -40,7 +40,6 @@ const WORKSPACE_WITH_BOTH_SCROLLS_CLASS = 'dx-scheduler-work-space-both-scrollba const WORKSPACE_WITH_COUNT_CLASS = 'dx-scheduler-work-space-count'; const WORKSPACE_WITH_GROUP_BY_DATE_CLASS = 'dx-scheduler-work-space-group-by-date'; const WORKSPACE_WITH_ODD_CELLS_CLASS = 'dx-scheduler-work-space-odd-cells'; -const WORKSPACE_WITH_OVERLAPPING_CLASS = 'dx-scheduler-work-space-overlapping'; const TIME_PANEL_CLASS = 'dx-scheduler-time-panel'; const TIME_PANEL_CELL_CLASS = 'dx-scheduler-time-panel-cell'; @@ -443,12 +442,15 @@ const SchedulerWorkSpace = Widget.inherit({ _optionChanged: function(args) { switch(args.name) { + case 'startDayHour': + case 'endDayHour': + this.invoke('validateDayHours'); + this._cleanWorkSpace(); + break; case 'dateCellTemplate': case 'resourceCellTemplate': case 'dataCellTemplate': case 'timeCellTemplate': - case 'startDayHour': - case 'endDayHour': case 'hoursInterval': case 'firstDayOfWeek': case 'currentDate': @@ -539,7 +541,6 @@ const SchedulerWorkSpace = Widget.inherit({ this._toggleWorkSpaceCountClass(); this._toggleGroupByDateClass(); this._toggleWorkSpaceWithOddCells(); - this._toggleWorkSpaceOverlappingClass(); this.$element() .addClass(COMPONENT_CLASS) @@ -596,14 +597,6 @@ const SchedulerWorkSpace = Widget.inherit({ return this.option('hoursInterval') === 0.5; }, - _toggleWorkSpaceOverlappingClass: function() { - this.$element().toggleClass(WORKSPACE_WITH_OVERLAPPING_CLASS, this._isWorkSpaceWithOverlapping()); - }, - - _isWorkSpaceWithOverlapping: function() { - return this.invoke('getMaxAppointmentsPerCell') !== null; - }, - _toggleGroupingDirectionClass: function() { this.$element().toggleClass(VERTICAL_GROUPED_WORKSPACE_CLASS, this._isVerticalGroupedWorkSpace()); }, @@ -1461,7 +1454,7 @@ const SchedulerWorkSpace = Widget.inherit({ _getDateWithSkippedDST: function() { let result = new Date(this.getStartViewDate()); - if(utils.isTimezoneChangeInDate(result)) { + if(timeZoneUtils.isTimezoneChangeInDate(result)) { result = new Date(result.setDate(result.getDate() + 1)); } return result; @@ -1488,7 +1481,7 @@ const SchedulerWorkSpace = Widget.inherit({ _getTimeCellDateAdjustedDST: function(i) { let startViewDate = new Date(this.getStartViewDate()); - if(utils.isTimezoneChangeInDate(startViewDate)) { + if(timeZoneUtils.isTimezoneChangeInDate(startViewDate)) { startViewDate = new Date(startViewDate.setDate(startViewDate.getDate() + 1)); } @@ -2243,7 +2236,7 @@ const SchedulerWorkSpace = Widget.inherit({ getDateRange: function() { return [ this.getStartViewDate(), - this.getEndViewDate() + this.getEndViewDateByEndDayHour() ]; }, @@ -2374,12 +2367,22 @@ const SchedulerWorkSpace = Widget.inherit({ return this._adjustEndViewDateByDaylightDiff(dateOfLastViewCell, endDateOfLastViewCell); }, + getEndViewDateByEndDayHour: function() { + const dateOfLastViewCell = this.getDateOfLastViewCell(); + const endTime = dateUtils.dateTimeFromDecimal(this.option('endDayHour')); + + const endDateOfLastViewCell = new Date(dateOfLastViewCell.setHours(endTime.hours, endTime.minutes)); + + return this._adjustEndViewDateByDaylightDiff(dateOfLastViewCell, endDateOfLastViewCell); + + }, + calculateEndViewDate: function(dateOfLastViewCell) { return new Date(dateOfLastViewCell.getTime() + this.getCellDuration()); }, _adjustEndViewDateByDaylightDiff: function(startDate, endDate) { - const daylightDiff = utils.getDaylightOffsetInMs(startDate, endDate); + const daylightDiff = timeZoneUtils.getDaylightOffsetInMs(startDate, endDate); const endDateOfLastViewCell = new Date(endDate.getTime() - daylightDiff); diff --git a/js/ui/scroll_view/ui.scrollable.simulated.js b/js/ui/scroll_view/ui.scrollable.simulated.js index 0d7ed52d62bc..96a899ef8eaf 100644 --- a/js/ui/scroll_view/ui.scrollable.simulated.js +++ b/js/ui/scroll_view/ui.scrollable.simulated.js @@ -530,6 +530,7 @@ const Scroller = Class.inherit({ }, _cursorEnterHandler: function() { + this._resetScaleRatio(); this._updateScrollbar(); this._scrollbar.cursorEnter(); diff --git a/js/ui/select_box.js b/js/ui/select_box.js index f278f88f9cc8..4f8f0437b9cd 100644 --- a/js/ui/select_box.js +++ b/js/ui/select_box.js @@ -548,6 +548,7 @@ const SelectBox = DropDownList.inherit({ const shouldCancelSearch = this._wasSearch() && !this.option('acceptCustomValue') && this.option('searchEnabled') && + this.option('opened') && !this._isOverlayNestedTarget(e.relatedTarget); if(shouldCancelSearch) { this._searchCanceled(); @@ -836,11 +837,6 @@ const SelectBox = DropDownList.inherit({ case 'useInkRipple': this._invalidate(); break; - case 'selectedItem': - if(args.previousValue !== args.value) { - this.callBase(args); - } - break; case 'allowClearing': break; default: diff --git a/js/ui/sortable.js b/js/ui/sortable.js index 991c99d41d71..961c7d8b7d0f 100644 --- a/js/ui/sortable.js +++ b/js/ui/sortable.js @@ -3,6 +3,7 @@ import registerComponent from '../core/component_registrator'; import { extend } from '../core/utils/extend'; import Draggable from './draggable'; import { getPublicElement } from '../core/utils/dom'; +import { getWindow } from '../core/utils/window'; import translator from '../animation/translator'; import fx from '../animation/fx'; @@ -157,13 +158,29 @@ const Sortable = Draggable.inherit({ } }, + _isInsideTargetDraggable: function(event) { + const $targetDraggable = this._getTargetDraggable().$element(); + const $scrollable = this._getScrollable($targetDraggable); + + if($scrollable) { + const offset = $scrollable.offset(); + const validY = offset.top + $scrollable.height() >= event.pageY && offset.top <= event.pageY; + const validX = offset.left + $scrollable.width() >= event.pageX && offset.left <= event.pageX; + + return validY && validX; + } + + return true; + }, + dragEnd: function(sourceEvent) { const $sourceElement = this._getSourceElement(); const sourceDraggable = this._getSourceDraggable(); const isSourceDraggable = sourceDraggable.NAME !== this.NAME; const toIndex = this.option('toIndex'); + const isInsideTargetDraggable = this._isInsideTargetDraggable(sourceEvent.event); - if(toIndex !== null && toIndex >= 0) { + if(toIndex !== null && toIndex >= 0 && isInsideTargetDraggable) { let cancelAdd; let cancelRemove; @@ -398,20 +415,27 @@ const Sortable = Draggable.inherit({ this._updateItemPoints(); }, - _makeWidthCorrection: function($item, width) { + _getScrollable: function($element) { const that = this; - that._$scrollable = null; + let $scrollable; - $item.parents().toArray().some(function(element) { - const $element = $(element); + $element.parents().toArray().some(function(parent) { + const $parent = $(parent); - if(that._horizontalScrollHelper.isScrollable($element)) { - that._$scrollable = $element; + if(that._horizontalScrollHelper.isScrollable($parent) || that._verticalScrollHelper.isScrollable($parent)) { + $scrollable = $parent; return true; } }); + return $scrollable; + }, + + _makeWidthCorrection: function($item, width) { + const that = this; + that._$scrollable = that._getScrollable($item); + if(that._$scrollable && that._$scrollable.width() < width) { const scrollableWidth = that._$scrollable.width(); const offsetLeft = $item.offset().left - that._$scrollable.offset().left; @@ -557,8 +581,10 @@ const Sortable = Draggable.inherit({ const isVerticalOrientation = this._isVerticalOrientation(); const start = isVerticalOrientation ? 'top' : 'left'; const end = isVerticalOrientation ? 'bottom' : 'right'; + const window = getWindow(); + const pageOffset = isVerticalOrientation ? window.pageYOffset : window.pageXOffset; - if(position[start] < clientRect[start] || position[start] > clientRect[end]) { + if(position[start] < (clientRect[start] + pageOffset) || position[start] > (clientRect[end] + pageOffset)) { return false; } } diff --git a/js/ui/tabs.js b/js/ui/tabs.js index 56e2f52bdae6..7a178f3fec22 100644 --- a/js/ui/tabs.js +++ b/js/ui/tabs.js @@ -16,6 +16,7 @@ import Scrollable from './scroll_view/ui.scrollable'; import { default as CollectionWidget } from './collection/ui.collection_widget.live_update'; import { getImageContainer } from '../core/utils/icon'; import { BindableTemplate } from '../core/templates/bindable_template'; +import { Deferred, when } from '../core/utils/deferred'; const TABS_CLASS = 'dx-tabs'; const TABS_WRAPPER_CLASS = 'dx-tabs-wrapper'; @@ -155,6 +156,17 @@ const Tabs = CollectionWidget.inherit({ }); }, + _createItemByTemplate: function _createItemByTemplate(itemTemplate, renderArgs) { + const { itemData, container, index } = renderArgs; + this._deferredTemplates[index] = new Deferred(); + return itemTemplate.render({ + model: itemData, + container, + index, + onRendered: () => this._deferredTemplates[index].resolve() + }); + }, + _itemClass: function() { return TABS_ITEM_CLASS; }, @@ -168,6 +180,7 @@ const Tabs = CollectionWidget.inherit({ }, _initMarkup: function() { + this._deferredTemplates = []; this.callBase(); this.setAria('role', 'tab', this.itemElements()); @@ -178,8 +191,11 @@ const Tabs = CollectionWidget.inherit({ _render: function() { this.callBase(); + this._deferRenderScrolling(); + }, - this._renderScrolling(); + _deferRenderScrolling() { + when.apply(this, this._deferredTemplates).done(() => this._renderScrolling()); }, _renderScrolling: function() { @@ -406,6 +422,7 @@ const Tabs = CollectionWidget.inherit({ }, _clean: function() { + this._deferredTemplates = []; this._cleanScrolling(); this.callBase(); }, @@ -438,7 +455,7 @@ const Tabs = CollectionWidget.inherit({ _afterItemElementInserted() { this.callBase(); - this._renderScrolling(); + this._deferRenderScrolling(); }, _afterItemElementDeleted($item, deletedActionArgs) { diff --git a/js/ui/tag_box.js b/js/ui/tag_box.js index b89546fff488..2d9006aa20e2 100644 --- a/js/ui/tag_box.js +++ b/js/ui/tag_box.js @@ -736,7 +736,7 @@ const TagBox = SelectBox.inherit({ const selectedItemsAlreadyLoaded = filteredItems.length === values.length; const d = new Deferred(); - if(selectedItemsAlreadyLoaded) { + if(!this._isDataSourceChanged && selectedItemsAlreadyLoaded) { return d.resolve(filteredItems).promise(); } else { const dataSource = this._dataSource; @@ -750,6 +750,7 @@ const TagBox = SelectBox.inherit({ .store() .load({ filter, customQueryParams, expand }) .done((data, extra) => { + this._isDataSourceChanged = false; if(this._disposed) { d.reject(); return; @@ -1273,6 +1274,13 @@ const TagBox = SelectBox.inherit({ }, + _dataSourceChangedHandler: function() { + if(this._list) { + this._isDataSourceChanged = true; + } + this.callBase.apply(this, arguments); + }, + _applyButtonHandler: function() { this.option('value', this._getSortedListValues()); this._clearTextValue(); diff --git a/js/ui/validator.js b/js/ui/validator.js index 167b4d26cc14..2cbf1d018038 100644 --- a/js/ui/validator.js +++ b/js/ui/validator.js @@ -265,7 +265,9 @@ const Validator = DOMComponent.inherit({ }, _applyValidationResult(result, adapter) { - const validatedAction = this._createActionByOption('onValidated'); + const validatedAction = this._createActionByOption('onValidated', { + excludeValidators: ['readOnly'], + }); result.validator = this; this._updateValidationResult(result); adapter.applyValidationResults && adapter.applyValidationResults(this._validationInfo.result); diff --git a/js/ui/widget/ui.errors.js b/js/ui/widget/ui.errors.js index 411e723f4251..ce45efacd26a 100644 --- a/js/ui/widget/ui.errors.js +++ b/js/ui/widget/ui.errors.js @@ -236,6 +236,11 @@ module.exports = errorUtils(errors.ERROR_MESSAGES, { // NOTE: // E1057 is reserved. See https://js.devexpress.com/Documentation/19_2/ApiReference/UI_Widgets/Errors_and_Warnings/#E1057 + /** + * @name ErrorsUIWidgets.E1058 + */ + E1058: 'The "startDayHour" must be earlier than the "endDayHour"', + /** * @name ErrorsUIWidgets.W1001 */ @@ -299,5 +304,10 @@ module.exports = errorUtils(errors.ERROR_MESSAGES, { /** * @name ErrorsUIWidgets.W1014 */ - W1014: 'The Floating Action Button exceeds the recommended speed dial action count. If you need to display more speed dial actions, increase the maxSpeedDialActionCount option value in the global config.' + W1014: 'The Floating Action Button exceeds the recommended speed dial action count. If you need to display more speed dial actions, increase the maxSpeedDialActionCount option value in the global config.', + + /** + * @name ErrorsUIWidgets.W1015 + */ + W1015: 'The "cellDuration" should divide the range from the "startDayHour" to the "endDayHour" into even intervals' }); diff --git a/js/viz/axes/base_axis.js b/js/viz/axes/base_axis.js index 996f5de82f2f..5f678022fcd9 100644 --- a/js/viz/axes/base_axis.js +++ b/js/viz/axes/base_axis.js @@ -75,7 +75,7 @@ function getTickGenerator(options, incidentOccurred, skipTickGeneration, rangeIs incidentOccurred: incidentOccurred, - firstDayOfWeek: options.workWeek && options.workWeek[0], + firstDayOfWeek: options.workWeek?.[0], skipTickGeneration: skipTickGeneration, skipCalculationLimits: options.skipCalculationLimits, @@ -458,12 +458,11 @@ Axis.prototype = { let text = lineLabelOptions.text; const options = that._options; const labelOptions = options.label; - let coords; that._checkAlignmentConstantLineLabels(lineLabelOptions); text = isDefined(text) ? text : that.formatLabel(parsedValue, labelOptions); - coords = that._getConstantLineLabelsCoords(value, lineLabelOptions); + const coords = that._getConstantLineLabelsCoords(value, lineLabelOptions); return that._drawConstantLineLabelText(text, coords.x, coords.y, lineLabelOptions, group); }, @@ -656,13 +655,10 @@ Axis.prototype = { const renderer = that._renderer; const classSelector = that._axisCssPrefix; const constantLinesClass = classSelector + 'constant-lines'; - let insideGroup; - let outsideGroup1; - let outsideGroup2; - insideGroup = renderer.g().attr({ 'class': constantLinesClass }); - outsideGroup1 = renderer.g().attr({ 'class': constantLinesClass }); - outsideGroup2 = renderer.g().attr({ 'class': constantLinesClass }); + const insideGroup = renderer.g().attr({ 'class': constantLinesClass }); + const outsideGroup1 = renderer.g().attr({ 'class': constantLinesClass }); + const outsideGroup2 = renderer.g().attr({ 'class': constantLinesClass }); return { inside: insideGroup, @@ -974,6 +970,14 @@ Axis.prototype = { return _abs(getLog(value, options.logarithmBase, allowNegatives, linearThreshold) - getLog(prevValue, options.logarithmBase, allowNegatives, linearThreshold)); }, + getCanvasRange() { + const translator = this._translator; + return { + startValue: translator.from(translator.translate('canvas_position_start')), + endValue: translator.from(translator.translate('canvas_position_end')) + }; + }, + _processCanvas: function(canvas) { return canvas; }, @@ -1377,6 +1381,12 @@ Axis.prototype = { }; }, + estimateTickInterval: function(canvas) { + const that = this; + that.updateCanvas(canvas); + return that._tickInterval !== that._getTicks(that.adjustViewport(that._seriesData), _noop, true).tickInterval; + }, + setTicks: function(ticks) { const majors = ticks.majorTicks || []; this._majorTicks = majors.map(createMajorTick(this, this._renderer, this._getSkippedCategory(majors))); @@ -2405,9 +2415,6 @@ Axis.prototype = { const options = that._options; const widthAxis = options.visible ? options.width : 0; let ticks; - let maxText; - let text; - let box; const indent = withIndents ? options.label.indentFromAxis + (options.tick.length * 0.5) : 0; let tickInterval; const viewportRange = that._getViewportRange(); @@ -2425,7 +2432,7 @@ Axis.prototype = { ticks = ticks.ticks; } - maxText = ticks.reduce(function(prevLabel, tick, index) { + const maxText = ticks.reduce(function(prevLabel, tick, index) { const label = that.formatLabel(tick, options.label, viewportRange, undefined, tickInterval, ticks); if(prevLabel.length < label.length) { return label; @@ -2434,8 +2441,8 @@ Axis.prototype = { } }, that.formatLabel(ticks[0], options.label, viewportRange, undefined, tickInterval, ticks)); - text = that._renderer.text(maxText, 0, 0).css(that._textFontStyles).attr(that._textOptions).append(that._renderer.root); - box = text.getBBox(); + const text = that._renderer.text(maxText, 0, 0).css(that._textFontStyles).attr(that._textOptions).append(that._renderer.root); + const box = text.getBBox(); text.remove(); return { x: box.x, y: box.y, width: box.width + indent, height: box.height + indent }; @@ -2579,6 +2586,7 @@ Axis.prototype = { isHorizontal: this._isHorizontal, shiftZeroValue: !this.isArgumentAxis, interval: options.semiDiscreteInterval, + firstDayOfWeek: options.workWeek?.[0], stick: this._getStick(), breaksSize: options.breakStyle ? options.breakStyle.width : 0 }; diff --git a/js/viz/chart.js b/js/viz/chart.js index 5bd19a09889f..23afd61c396f 100644 --- a/js/viz/chart.js +++ b/js/viz/chart.js @@ -26,6 +26,7 @@ const DEFAULT_PANES = [{ name: DEFAULT_PANE_NAME, border: {} }]; +const DISCRETE = 'discrete'; const _isArray = Array.isArray; import { isDefined as _isDefined } from '../core/utils/type'; @@ -380,6 +381,109 @@ function axisAnimationEnabled(drawOptions, pointsToAnimation) { return drawOptions.animate && pointsCount <= drawOptions.animationPointsLimit; } +function collectMarkersInfoBySeries(allSeries, filteredSeries, argAxis) { + let points = []; + const overloadedSeries = {}; + const argVisualRange = argAxis.visualRange(); + const argTranslator = argAxis.getTranslator(); + const argViewPortFilter = getViewPortFilter(argVisualRange || {}); + filteredSeries.forEach(s => { + const valAxis = s.getValueAxis(); + const valVisualRange = valAxis.getCanvasRange(); + const valTranslator = valAxis.getTranslator(); + const seriesIndex = allSeries.indexOf(s); + const valViewPortFilter = getViewPortFilter(valVisualRange || {}); + + overloadedSeries[seriesIndex] = {}; + filteredSeries.forEach(sr => overloadedSeries[seriesIndex][allSeries.indexOf(sr)] = 0); + const seriesPoints = []; + + s.getPoints().filter(p => { + return p.getOptions().visible && argViewPortFilter(p.argument) && + (valViewPortFilter(p.getMinValue(true)) || valViewPortFilter(p.getMaxValue(true))); + }).forEach(p => { + const tp = { + seriesIndex: seriesIndex, + argument: p.argument, + value: p.getMaxValue(true), + size: p.bubbleSize || p.getOptions().size + }; + if(p.getMinValue(true) !== p.getMaxValue(true)) { + const mp = _extend({}, tp); + mp.value = p.getMinValue(true); + mp.x = argTranslator.to(mp.argument, 1); + mp.y = valTranslator.to(mp.value, 1); + seriesPoints.push(mp); + } + tp.x = argTranslator.to(tp.argument, 1); + tp.y = valTranslator.to(tp.value, 1); + seriesPoints.push(tp); + }); + + overloadedSeries[seriesIndex].pointsCount = seriesPoints.length; + overloadedSeries[seriesIndex].total = 0; + overloadedSeries[seriesIndex].continuousSeries = 0; + points = points.concat(seriesPoints); + }); + return { points, overloadedSeries }; +} + +function applyAutoHidePointMarkers(allSeries, filteredSeries, overloadedSeries, argAxis) { + const argAxisType = argAxis.getOptions().type; + filteredSeries.forEach(s => { + const seriesIndex = allSeries.indexOf(s); + s.autoHidePointMarkers = false; + const tickCount = argAxis.getTicksValues().majorTicksValues.length; + if(s.autoHidePointMarkersEnabled() && (argAxisType === DISCRETE || overloadedSeries[seriesIndex].pointsCount > tickCount)) { + for(const index in overloadedSeries[seriesIndex]) { + const i = parseInt(index); + if(isNaN(i) || overloadedSeries[seriesIndex].total / overloadedSeries[seriesIndex].continuousSeries < 3) { + continue; + } + if(i === seriesIndex) { + if(overloadedSeries[i][i] * 2 >= overloadedSeries[i].pointsCount) { + s.autoHidePointMarkers = true; + break; + } + } else if(overloadedSeries[seriesIndex].total >= overloadedSeries[seriesIndex].pointsCount) { + s.autoHidePointMarkers = true; + break; + } + } + } + }); +} + +function updateMarkersInfo({ overloadedSeries, points }) { + let isContinuousSeries = false; + for(let i = 0; i < points.length - 1; i++) { + const curPoint = points[i]; + const size = curPoint.size; + if(_isDefined(curPoint.x) && _isDefined(curPoint.y)) { + for(let j = i + 1; j < points.length; j++) { + const nextPoint = points[j]; + const next_x = _isDefined(nextPoint) ? nextPoint.x : null; + const next_y = _isDefined(nextPoint) ? nextPoint.y : null; + + if(!_isDefined(next_x) || Math.abs(curPoint.x - next_x) >= size) { + isContinuousSeries &= j !== i + 1; + break; + } else { + const distance = _isDefined(next_x) && _isDefined(next_y) && Math.sqrt(Math.pow(curPoint.x - next_x, 2) + Math.pow(curPoint.y - next_y, 2)); + if(distance && distance < size) { + overloadedSeries[curPoint.seriesIndex][nextPoint.seriesIndex]++; + overloadedSeries[curPoint.seriesIndex].total++; + if(!isContinuousSeries) { + overloadedSeries[curPoint.seriesIndex].continuousSeries++; + isContinuousSeries = true; + } + } + } + } + } + } +} + // utilities used in axes rendering const dxChart = AdvancedChart.inherit({ @@ -698,7 +802,7 @@ const dxChart = AdvancedChart.inherit({ min = getLog(min, businessRange.base); max = getLog(max, businessRange.base); } - const viewportDistance = businessRange.axisType === 'discrete' ? getCategoriesInfo(businessRange.categories, min, max).categories.length : Math.abs(max - min); + const viewportDistance = businessRange.axisType === DISCRETE ? getCategoriesInfo(businessRange.categories, min, max).categories.length : Math.abs(max - min); let precision = getPrecision(viewportDistance); precision = precision > 1 ? Math.pow(10, precision - 2) : 1; const zoomChanged = Math.round((that._zoomLength - viewportDistance) * precision) / precision !== 0; @@ -800,116 +904,27 @@ const dxChart = AdvancedChart.inherit({ _applyPointMarkersAutoHiding() { const that = this; + const allSeries = that.series; if(!that._themeManager.getOptions('autoHidePointMarkers')) { - that.series.forEach(s => s.autoHidePointMarkers = false); + allSeries.forEach(s => s.autoHidePointMarkers = false); return; } - that.panes.forEach(pane => { - const series = that.series.filter(s => s.pane === pane.name && s.usePointsToDefineAutoHiding()); + that.panes.forEach(({ name }) => { + const series = allSeries.filter(s => s.pane === name && s.usePointsToDefineAutoHiding()); const argAxis = that.getArgumentAxis(); const argVisualRange = argAxis.visualRange(); - const argTranslator = argAxis.getTranslator(); - const argAxisType = argAxis.getOptions().type; - const argViewPortFilter = getViewPortFilter(argVisualRange || {}); - let points = []; - const overloadedSeries = {}; - - series.forEach(s => { - const valAxis = s.getValueAxis(); - const valVisualRange = valAxis.visualRange(); - const valTranslator = valAxis.getTranslator(); - const seriesIndex = that.series.indexOf(s); - const valViewPortFilter = getViewPortFilter(valVisualRange || {}); - - overloadedSeries[seriesIndex] = {}; - series.forEach(sr => overloadedSeries[seriesIndex][that.series.indexOf(sr)] = 0); - const seriesPoints = []; - - s.getPoints().filter(p => { - return p.getOptions().visible && argViewPortFilter(p.argument) && - (valViewPortFilter(p.getMinValue(true)) || valViewPortFilter(p.getMaxValue(true))); - }).forEach(p => { - const tp = { - seriesIndex: seriesIndex, - argument: p.argument, - value: p.getMaxValue(true), - size: p.bubbleSize || p.getOptions().size - }; - if(p.getMinValue(true) !== p.getMaxValue(true)) { - const mp = _extend({}, tp); - mp.value = p.getMinValue(true); - mp.x = argTranslator.to(mp.argument, 1); - mp.y = valTranslator.to(mp.value, 1); - seriesPoints.push(mp); - } - tp.x = argTranslator.to(tp.argument, 1); - tp.y = valTranslator.to(tp.value, 1); - seriesPoints.push(tp); - }); + const argAxisIsDiscrete = argAxis.getOptions().type === DISCRETE; - overloadedSeries[seriesIndex].pointsCount = seriesPoints.length; - overloadedSeries[seriesIndex].total = 0; - overloadedSeries[seriesIndex].continuousSeries = 0; - points = points.concat(seriesPoints); - }); + const markersInfo = collectMarkersInfoBySeries(allSeries, series, argAxis); - const sortingCallback = argAxisType === 'discrete' ? + const sortingCallback = argAxisIsDiscrete ? (p1, p2) => argVisualRange.categories.indexOf(p1.argument) - argVisualRange.categories.indexOf(p2.argument) : (p1, p2) => p1.argument - p2.argument; - points.sort(sortingCallback); - - - let isContinuousSeries = false; - for(let i = 0; i < points.length - 1; i++) { - const curPoint = points[i]; - const size = curPoint.size; - if(_isDefined(curPoint.x) && _isDefined(curPoint.y)) { - for(let j = i + 1; j < points.length; j++) { - const nextPoint = points[j]; - const next_x = _isDefined(nextPoint) ? nextPoint.x : null; - const next_y = _isDefined(nextPoint) ? nextPoint.y : null; - - if(!_isDefined(next_x) || Math.abs(curPoint.x - next_x) >= size) { - isContinuousSeries &= j !== i + 1; - break; - } else { - const distance = _isDefined(next_x) && _isDefined(next_y) && Math.sqrt(Math.pow(curPoint.x - next_x, 2) + Math.pow(curPoint.y - next_y, 2)); - if(distance && distance < size) { - overloadedSeries[curPoint.seriesIndex][nextPoint.seriesIndex]++; - overloadedSeries[curPoint.seriesIndex].total++; - if(!isContinuousSeries) { - overloadedSeries[curPoint.seriesIndex].continuousSeries++; - isContinuousSeries = true; - } - } - } - } - } - } + markersInfo.points.sort(sortingCallback); - series.forEach(s => { - const seriesIndex = that.series.indexOf(s); - s.autoHidePointMarkers = false; - const tickCount = argAxis.getTicksValues().majorTicksValues.length; - if(s.autoHidePointMarkersEnabled() && (argAxisType === 'discrete' || overloadedSeries[seriesIndex].pointsCount > tickCount)) { - for(const index in overloadedSeries[seriesIndex]) { - const i = parseInt(index); - if(isNaN(i) || overloadedSeries[seriesIndex].total / overloadedSeries[seriesIndex].continuousSeries < 3) { - continue; - } - if(i === seriesIndex) { - if(overloadedSeries[i][i] * 2 >= overloadedSeries[i].pointsCount) { - s.autoHidePointMarkers = true; - break; - } - } else if(overloadedSeries[seriesIndex].total >= overloadedSeries[seriesIndex].pointsCount) { - s.autoHidePointMarkers = true; - break; - } - } - } - }); + updateMarkersInfo(markersInfo); + applyAutoHidePointMarkers(allSeries, series, markersInfo.overloadedSeries, argAxis); }); }, @@ -925,10 +940,14 @@ const dxChart = AdvancedChart.inherit({ const that = this; const rotated = that._isRotated(); const synchronizeMultiAxes = that._themeManager.getOptions('synchronizeMultiAxes'); - const extendedArgAxes = (that._scrollBar ? [that._scrollBar] : []).concat(that._argumentAxes); - const verticalAxes = rotated ? extendedArgAxes : that._valueAxes; - const horizontalAxes = rotated ? that._valueAxes : extendedArgAxes; + const scrollBar = that._scrollBar ? [that._scrollBar] : []; + const extendedArgAxes = scrollBar.concat(that._argumentAxes); + const verticalAxes = rotated ? that._argumentAxes : that._valueAxes; + const verticalElements = rotated ? extendedArgAxes : that._valueAxes; + const horizontalAxes = rotated ? that._valueAxes : that._argumentAxes; + const horizontalElements = rotated ? that._valueAxes : extendedArgAxes; const allAxes = verticalAxes.concat(horizontalAxes); + const allElements = allAxes.concat(scrollBar); that._normalizePanesHeight(); that._updatePanesCanvases(drawOptions); @@ -965,26 +984,36 @@ const dxChart = AdvancedChart.inherit({ } let vAxesMargins = { panes: {} }; - let hAxesMargins = getHorizontalAxesMargins(horizontalAxes, axis => axis.estimateMargins(panesCanvases[axis.pane])); + let hAxesMargins = getHorizontalAxesMargins(horizontalElements, axis => axis.estimateMargins(panesCanvases[axis.pane])); panesCanvases = shrinkCanvases(rotated, panesCanvases, paneSizes, vAxesMargins, hAxesMargins); - drawAxesWithTicks(verticalAxes, !rotated && synchronizeMultiAxes, panesCanvases, panesBorderOptions); - vAxesMargins = getVerticalAxesMargins(verticalAxes); - panesCanvases = shrinkCanvases(rotated, panesCanvases, paneSizes, vAxesMargins, hAxesMargins); + const drawAxesAndSetCanvases = (isHorizontal) => { + const axes = isHorizontal ? horizontalAxes : verticalAxes; + const condition = (isHorizontal ? rotated : !rotated) && synchronizeMultiAxes; + drawAxesWithTicks(axes, condition, panesCanvases, panesBorderOptions); + if(isHorizontal) { + hAxesMargins = getHorizontalAxesMargins(horizontalElements, getAxisMargins); + } else { + vAxesMargins = getVerticalAxesMargins(verticalElements); + } + panesCanvases = shrinkCanvases(rotated, panesCanvases, paneSizes, vAxesMargins, hAxesMargins); + }; - drawAxesWithTicks(horizontalAxes, rotated && synchronizeMultiAxes, panesCanvases, panesBorderOptions); - hAxesMargins = getHorizontalAxesMargins(horizontalAxes, getAxisMargins); - panesCanvases = shrinkCanvases(rotated, panesCanvases, paneSizes, vAxesMargins, hAxesMargins); + drawAxesAndSetCanvases(false); + drawAxesAndSetCanvases(true); + if(that._estimateTickIntervals(verticalAxes, panesCanvases)) { + drawAxesAndSetCanvases(false); + } let oldTitlesWidth = calculateTitlesWidth(verticalAxes); const visibleSeries = that._getVisibleSeries(); const pointsToAnimation = that._getPointsToAnimation(visibleSeries); - performActionOnAxes(allAxes, 'updateSize', panesCanvases, axisAnimationEnabled(drawOptions, pointsToAnimation)); + performActionOnAxes(allElements, 'updateSize', panesCanvases, axisAnimationEnabled(drawOptions, pointsToAnimation)); - horizontalAxes.forEach(shiftAxis('top', 'bottom')); - verticalAxes.forEach(shiftAxis('left', 'right')); + horizontalElements.forEach(shiftAxis('top', 'bottom')); + verticalElements.forEach(shiftAxis('left', 'right')); that._renderScaleBreaks(); @@ -1010,7 +1039,7 @@ const dxChart = AdvancedChart.inherit({ panesCanvases = shrinkCanvases(rotated, panesCanvases, paneSizes, vAxesMargins, hAxesMargins); - performActionOnAxes(allAxes, 'updateSize', panesCanvases, false, false); + performActionOnAxes(allElements, 'updateSize', panesCanvases, false, false); oldTitlesWidth = calculateTitlesWidth(verticalAxes); } }); @@ -1022,6 +1051,10 @@ const dxChart = AdvancedChart.inherit({ return cleanPanesCanvases; }, + _estimateTickIntervals(axes, canvases) { + return axes.some(axis => axis.estimateTickInterval(canvases[axis.pane])); + }, + checkForMoreSpaceForPanesCanvas() { const that = this; const rotated = that._isRotated(); @@ -1404,7 +1437,7 @@ const dxChart = AdvancedChart.inherit({ getVisibleArgumentBounds: function() { const translator = this._argumentAxes[0].getTranslator(); const range = translator.getBusinessRange(); - const isDiscrete = range.axisType === 'discrete'; + const isDiscrete = range.axisType === DISCRETE; const categories = range.categories; return { diff --git a/js/viz/chart_components/scroll_bar.js b/js/viz/chart_components/scroll_bar.js index 56c352531f57..a69d01047484 100644 --- a/js/viz/chart_components/scroll_bar.js +++ b/js/viz/chart_components/scroll_bar.js @@ -176,15 +176,11 @@ ScrollBar.prototype = { }, // Axis like functions - draw: noop, - shift: noop, hideTitle: noop, hideOuterElements: noop, - - prepareAnimation: noop, // Axis like functions setPosition: function(min, max) { diff --git a/js/viz/core/themes/generic.light.js b/js/viz/core/themes/generic.light.js index e88c724e6b6e..5256a090fbe3 100644 --- a/js/viz/core/themes/generic.light.js +++ b/js/viz/core/themes/generic.light.js @@ -1348,18 +1348,15 @@ registerTheme({ visible: true, paddingLeftRight: 5, paddingTopBottom: 4, - stroke: '#000000', - 'stroke-width': 1, - 'stroke-opacity': 0.3, font: { color: '#ffffff', - weight: 300 + weight: 600 }, shadow: { - opacity: 0.8, + opacity: 0.6, offsetX: 0, offsetY: 1, - blur: 1, + blur: 2, color: '#000000' }, wordWrap: 'normal', diff --git a/js/viz/core/tooltip.js b/js/viz/core/tooltip.js index f8392628870b..fcd3b24215d5 100644 --- a/js/viz/core/tooltip.js +++ b/js/viz/core/tooltip.js @@ -37,7 +37,6 @@ function getSpecialFormatOptions(options, specialFormat) { function Tooltip(params) { const that = this; let renderer; - let root; that._eventTrigger = params.eventTrigger; that._widgetRoot = params.widgetRoot; @@ -48,7 +47,7 @@ function Tooltip(params) { .addClass(params.cssClass); that._renderer = renderer = new rendererModule.Renderer({ pathModified: params.pathModified, container: that._wrapper[0] }); - root = renderer.root; + const root = renderer.root; root.attr({ 'pointer-events': 'none' }); // svg text @@ -89,7 +88,7 @@ Tooltip.prototype = { that._options = options; that._textFontStyles = vizUtils.patchFontOptions(options.font); - that._textFontStyles.color = options.font.color; + that._textFontStyles.color = that._textFontStyles.fill; that._wrapper.css({ 'zIndex': options.zIndex }); that._customizeTooltip = options.customizeTooltip; @@ -214,7 +213,7 @@ Tooltip.prototype = { } state.color = customize.color || options.color; state.borderColor = customize.borderColor || (options.border || {}).color; - state.textColor = customize.fontColor || (options.font || {}).color; + state.textColor = customize.fontColor || (this._textFontStyles || {}).color; return !!state.text || !!state.html || !!this._template; }, diff --git a/js/viz/pie_chart.js b/js/viz/pie_chart.js index 8ddf86d523e7..48a1133c886e 100644 --- a/js/viz/pie_chart.js +++ b/js/viz/pie_chart.js @@ -370,8 +370,8 @@ const dxPieChart = BaseChart.inherit({ let labelsWereOverlapped; let wordWrapApplied; do { - labelsWereOverlapped = this._resolveLabelOverlapping(resolveLabelOverlapping); wordWrapApplied = this._adjustSeriesLabels(resolveLabelOverlapping === 'shift'); + labelsWereOverlapped = this._resolveLabelOverlapping(resolveLabelOverlapping); } while((labelsWereOverlapped || wordWrapApplied) && ++iterationCount < MAX_RESOLVE_ITERATION_COUNT); }, diff --git a/js/viz/range_selector/range_selector.js b/js/viz/range_selector/range_selector.js index ea70b9a3d90f..cbc83cd9ddcc 100644 --- a/js/viz/range_selector/range_selector.js +++ b/js/viz/range_selector/range_selector.js @@ -293,6 +293,10 @@ function updateTickIntervals(scaleOptions, screenDelta, incidentOccurred, range) return result; } +function getFirstDayOfWeek(options) { + return options.workWeek?.[0]; +} + function calculateTranslatorRange(seriesDataSource, scaleOptions) { let minValue; let maxValue; @@ -305,6 +309,7 @@ function calculateTranslatorRange(seriesDataSource, scaleOptions) { let translatorRange = seriesDataSource ? seriesDataSource.getBoundRange().arg : new rangeModule.Range(); let rangeForCategories; const isDate = scaleOptions.valueType === 'datetime'; + const firstDayOfWeek = getFirstDayOfWeek(scaleOptions); const minRange = scaleOptions.minRange; if(scaleOptions.type === DISCRETE) { @@ -322,14 +327,14 @@ function calculateTranslatorRange(seriesDataSource, scaleOptions) { } if(scaleOptions.type === SEMIDISCRETE) { - startValue = scaleOptions.startValue = correctValueByInterval(scaleOptions.startValue, isDate, minRange); - endValue = scaleOptions.endValue = correctValueByInterval(scaleOptions.endValue, isDate, minRange); + startValue = scaleOptions.startValue = correctValueByInterval(scaleOptions.startValue, isDate, minRange, firstDayOfWeek); + endValue = scaleOptions.endValue = correctValueByInterval(scaleOptions.endValue, isDate, minRange, firstDayOfWeek); - translatorRange.minVisible = correctValueByInterval(translatorRange.minVisible, isDate, minRange); - translatorRange.maxVisible = correctValueByInterval(translatorRange.maxVisible, isDate, minRange); + translatorRange.minVisible = correctValueByInterval(translatorRange.minVisible, isDate, minRange, firstDayOfWeek); + translatorRange.maxVisible = correctValueByInterval(translatorRange.maxVisible, isDate, minRange, firstDayOfWeek); - translatorRange.min = correctValueByInterval(translatorRange.min, isDate, minRange); - translatorRange.max = correctValueByInterval(translatorRange.max, isDate, minRange); + translatorRange.min = correctValueByInterval(translatorRange.min, isDate, minRange, firstDayOfWeek); + translatorRange.max = correctValueByInterval(translatorRange.max, isDate, minRange, firstDayOfWeek); } if(_isDefined(startValue) && _isDefined(endValue)) { @@ -495,10 +500,10 @@ function prepareScaleOptions(scaleOption, calculatedValueType, incidentOccurred, return scaleOption; } -function correctValueByInterval(value, isDate, interval) { +function correctValueByInterval(value, isDate, interval, firstDayOfWeek) { if(_isDefined(value)) { value = isDate - ? correctDateWithUnitBeginning(new Date(value), interval) + ? correctDateWithUnitBeginning(new Date(value), interval, null, firstDayOfWeek) : adjust(_floor(adjust(value / interval)) * interval); } return value; @@ -508,6 +513,7 @@ function getIntervalCustomTicks(options) { let min = options.startValue; let max = options.endValue; const isDate = options.valueType === 'datetime'; + const firstDayOfWeek = getFirstDayOfWeek(options); const tickInterval = options.tickInterval; const res = { intervals: [] @@ -522,8 +528,8 @@ function getIntervalCustomTicks(options) { if(tickInterval !== options.minorTickInterval) { res.altIntervals = res.intervals; - min = correctValueByInterval(min, isDate, tickInterval); - max = correctValueByInterval(max, isDate, tickInterval); + min = correctValueByInterval(min, isDate, tickInterval, firstDayOfWeek); + max = correctValueByInterval(max, isDate, tickInterval, firstDayOfWeek); res.intervals = getSequenceByInterval(min, max, tickInterval); res.intervals[0] = res.altIntervals[0]; diff --git a/js/viz/sparklines/bullet.js b/js/viz/sparklines/bullet.js index d5a5b3fbc74e..99a80add10b9 100644 --- a/js/viz/sparklines/bullet.js +++ b/js/viz/sparklines/bullet.js @@ -106,7 +106,7 @@ const dxBullet = BaseSparkline.inherit({ that._ranges = { arg: { - invert: options.inverted, + invert: options.rtlEnabled ? !options.inverted : options.inverted, min: options.startScaleValue, max: options.endScaleValue, axisType: 'continuous', diff --git a/js/viz/translators/interval_translator.js b/js/viz/translators/interval_translator.js index f8d9e462fc8e..38865983d62b 100644 --- a/js/viz/translators/interval_translator.js +++ b/js/viz/translators/interval_translator.js @@ -18,7 +18,7 @@ module.exports = { } else { value = new Date(value.getTime()); } - value = dateUtils.correctDateWithUnitBeginning(value, interval); + value = dateUtils.correctDateWithUnitBeginning(value, interval, null, this._options.firstDayOfWeek); } else { value = adjust(floor(adjust(value / interval)) * interval, interval); } diff --git a/js/viz/tree_map/common.js b/js/viz/tree_map/common.js index 49c1e067f86a..0f3a2cf9cee1 100644 --- a/js/viz/tree_map/common.js +++ b/js/viz/tree_map/common.js @@ -7,10 +7,7 @@ exports.buildRectAppearance = function(option) { exports.buildTextAppearance = function(options, filter) { return { - attr: options['stroke-width'] ? { - stroke: options.stroke, 'stroke-width': options['stroke-width'], 'stroke-opacity': options['stroke-opacity'], - filter: filter - } : {}, + attr: { filter }, css: _patchFontOptions(options.font) }; }; diff --git a/package.json b/package.json index b6fdf3e422eb..388fee39a401 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "devextreme", - "version": "20.1.1", + "version": "20.1.2", "description": "HTML5 JavaScript Component Suite for Responsive Web Development", "keywords": [ "html5", @@ -28,8 +28,8 @@ "types": "./bundles/dx.all.d.ts", "license": "SEE LICENSE IN README.md", "dependencies": { - "devexpress-diagram": "0.2.32", - "devexpress-gantt": "0.1.13", + "devexpress-diagram": "0.2.39", + "devexpress-gantt": "0.1.18", "jszip": "^2.0.0 || ^3.0.0", "quill": "^1.3.7", "showdown": "^1.8.6", @@ -40,9 +40,11 @@ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-proposal-optional-chaining": "^7.8.3", "@babel/preset-env": "^7.8.7", + "@types/enzyme": "^3.10.5", "@types/jest": "^24.0.24", "@types/jquery": "^2.0.34", "@types/react": "16.9.16", + "@typescript-eslint/eslint-plugin": "^2.29.0", "angular": "^1.6.10", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^10.0.3", @@ -58,13 +60,18 @@ "cldrjs": "^0.5.0", "cssom": "^0.4.4", "del": "^2.2.2", - "devextreme-generator": "^1.0.36", "devextreme-cldr-data": "^1.0.2", + "devextreme-generator": "1.0.63", "devextreme-internal-tools": "~1.2.24", "enzyme": "^3.11.0", "enzyme-adapter-preact-pure": "^2.2.0", "eslint": "^6.8.0", + "eslint-config-airbnb-typescript": "^7.2.1", + "eslint-config-devextreme": "^0.1.0", + "eslint-plugin-import": "^2.20.2", "eslint-plugin-jest": "^23.6.0", + "eslint-plugin-jest-formatting": "^1.2.0", + "eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-qunit": "^4.0.0", "eslint-plugin-react": "^7.18.0", "eslint-plugin-spellcheck": "0.0.11", @@ -75,7 +82,7 @@ "gulp-babel": "^8.0.0", "gulp-clean-css": "^4.2.0", "gulp-concat": "^2.6.0", - "gulp-dart-sass": "^0.9.1", + "gulp-dart-sass": "^1.0.0", "gulp-eol": "^0.1.2", "gulp-eslint": "^6.0.0", "gulp-file": "^0.4.0", @@ -100,7 +107,7 @@ "hogan.js": "3.0.2", "intl": "^1.2.5", "jest": "^24.9.0", - "jquery": "^3.4.1", + "jquery": "^3.5.0", "jquery.1": "^1.0.0", "jquery.2": "^1.0.0", "jquery.tmpl": "0.0.2", @@ -135,11 +142,8 @@ "testcafe": "^1.2.0", "through2": "^2.0.1", "ts-jest": "^24.2.0", - "tslint": "^6.0.0", - "underscore": "^1.9.2", - "tslint-config-airbnb": "^5.11.2", - "tslint-react": "^4.2.0", "typescript": "^3.7.2", + "underscore": "^1.9.2", "vinyl-named": "^1.1.0", "webpack": "^3.10.0", "webpack-stream": "^3.2.0", @@ -152,13 +156,17 @@ "scripts": { "lint": "npm-run-all -p -c lint-js lint-css", "lint-js": "eslint .", - "lint-ts": "tslint -p tsconfig.json", + "lint-ts": "eslint ./js/renovation/*.{ts,tsx} ./js/renovation/**/*.{ts,tsx} ./testing/jest/**/*.{ts,tsx}", "lint-css": "stylelint styles", "lint-staged": "lint-staged && npm run lint-ts", "build": "dotnet build build/build-dotnet.sln && gulp default", "build-dist": "dotnet build build/build-dotnet.sln && gulp default --uglify", "build-themes": "gulp style-compiler-themes", "build-themebuilder-assets": "gulp style-compiler-tb-assets", + "build:react": "gulp generate-react", + "build:react:watch": "gulp generate-react-watch", + "build:angular": "gulp generate-angular", + "build:angular:watch": "gulp generate-angular-watch", "dev": "gulp dev", "transpile-tests": "gulp transpile-tests", "test-env": "node testing/launch", diff --git a/playground/react/.gitignore b/playground/react/.gitignore new file mode 100644 index 000000000000..7912fc48c7d7 --- /dev/null +++ b/playground/react/.gitignore @@ -0,0 +1 @@ +/src/artifacts diff --git a/playground/react/App.js b/playground/react/App.js deleted file mode 100644 index aca27defcb34..000000000000 --- a/playground/react/App.js +++ /dev/null @@ -1,15 +0,0 @@ -/* eslint-disable */ - -import React from 'react'; -import Button from 'devextreme/renovation/button'; - -const App = () => { - return + ); +} + +export default App; diff --git a/playground/react/src/index.tsx b/playground/react/src/index.tsx new file mode 100644 index 000000000000..1cd9d96643bc --- /dev/null +++ b/playground/react/src/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import './artifacts/css/dx.common.css'; +import './artifacts/css/dx.light.css'; +import App from './App'; +import * as serviceWorker from './serviceWorker'; + +ReactDOM.render( + , + document.getElementById('root') +); + +serviceWorker.unregister(); diff --git a/playground/react/src/react-app-env.d.ts b/playground/react/src/react-app-env.d.ts new file mode 100644 index 000000000000..981cd73472c7 --- /dev/null +++ b/playground/react/src/react-app-env.d.ts @@ -0,0 +1,66 @@ +/// +/// +/// + +declare namespace NodeJS { + interface ProcessEnv { + readonly NODE_ENV: 'development' | 'production' | 'test'; + readonly PUBLIC_URL: string; + } +} + +declare module '*.bmp' { + const src: string; + export default src; +} + +declare module '*.gif' { + const src: string; + export default src; +} + +declare module '*.jpg' { + const src: string; + export default src; +} + +declare module '*.jpeg' { + const src: string; + export default src; +} + +declare module '*.png' { + const src: string; + export default src; +} + +declare module '*.webp' { + const src: string; + export default src; +} + +declare module '*.svg' { + import * as React from 'react'; + + export const ReactComponent: React.FunctionComponent & { title?: string }>; + + const src: string; + export default src; +} + +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module '*.module.scss' { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module '*.module.sass' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/playground/react/src/serviceWorker.js b/playground/react/src/serviceWorker.js new file mode 100644 index 000000000000..b04b771a8261 --- /dev/null +++ b/playground/react/src/serviceWorker.js @@ -0,0 +1,141 @@ +// This optional code is used to register a service worker. +// register() is not called by default. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on subsequent visits to a page, after all the +// existing tabs open on the page have been closed, since previously cached +// resources are updated in the background. + +// To learn more about the benefits of this model and instructions on how to +// opt-in, read https://bit.ly/CRA-PWA + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.0/8 are considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +export function register(config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://bit.ly/CRA-PWA' + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl, config) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl, config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl, { + headers: { 'Service-Worker': 'script' }, + }) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if ( + response.status === 404 || + (contentType != null && contentType.indexOf('javascript') === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready + .then(registration => { + registration.unregister(); + }) + .catch(error => { + console.error(error.message); + }); + } +} diff --git a/playground/react/tsconfig.json b/playground/react/tsconfig.json new file mode 100644 index 000000000000..79f335c64d55 --- /dev/null +++ b/playground/react/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext", + "es7", + "es2017.object", + "dom" + ], + "allowJs": true, + "skipLibCheck": true, + "declaration": false, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "noImplicitUseStrict": true, + "noImplicitAny": false, + "forceConsistentCasingInFileNames": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react", + "checkJs": false + }, + "include": [ + "src" + ], + "exclude": [ + "src/artifacts" + ] +} diff --git a/playground/vue/.gitignore b/playground/vue/.gitignore new file mode 100644 index 000000000000..f572dfb708b4 --- /dev/null +++ b/playground/vue/.gitignore @@ -0,0 +1,22 @@ +.DS_Store +node_modules +/dist +/src/artifacts + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/playground/vue/README.md b/playground/vue/README.md new file mode 100644 index 000000000000..86acb157f8b8 --- /dev/null +++ b/playground/vue/README.md @@ -0,0 +1,14 @@ +# devextreme-vue-playground + +## Project setup +```bash +npm install +npx gulp generate-vue +``` + +### Compiles and hot-reloads for development +```bash +cd ./playground/vue +npm install +npm run serve +``` diff --git a/playground/vue/babel.config.js b/playground/vue/babel.config.js new file mode 100644 index 000000000000..983142510d8b --- /dev/null +++ b/playground/vue/babel.config.js @@ -0,0 +1,10 @@ +module.exports = { + presets: [ + '@vue/cli-plugin-babel/preset', + '@babel/preset-env' + ], + 'plugins': [ + '@babel/plugin-proposal-nullish-coalescing-operator', + '@babel/plugin-proposal-optional-chaining', + ], +}; diff --git a/playground/vue/package-lock.json b/playground/vue/package-lock.json new file mode 100644 index 000000000000..6698d7931d8e --- /dev/null +++ b/playground/vue/package-lock.json @@ -0,0 +1,11232 @@ +{ + "name": "devextreme-vue-playground", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/compat-data": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.9.6.tgz", + "integrity": "sha512-5QPTrNen2bm7RBc7dsOmcA5hbrS4O2Vhmk5XOL4zWW/zD/hV0iinpefDlkm+tBBy8kDtFaaeEvmAqt+nURAV2g==", + "dev": true, + "requires": { + "browserslist": "^4.11.1", + "invariant": "^2.2.4", + "semver": "^5.5.0" + } + }, + "@babel/core": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.6.tgz", + "integrity": "sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.6", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helpers": "^7.9.6", + "@babel/parser": "^7.9.6", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.9.6", + "@babel/types": "^7.9.6", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + } + }, + "@babel/generator": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.6.tgz", + "integrity": "sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==", + "dev": true, + "requires": { + "@babel/types": "^7.9.6", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", + "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz", + "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.9.6.tgz", + "integrity": "sha512-x2Nvu0igO0ejXzx09B/1fGBxY9NXQlBW2kZsSxCJft+KHN8t9XWzIvFxtPHnBOAXpVsdxZKZFbRUC8TsNKajMw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.9.6", + "browserslist": "^4.11.1", + "invariant": "^2.2.4", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.9.6.tgz", + "integrity": "sha512-6N9IeuyHvMBRyjNYOMJHrhwtu4WJMrYf8hVbEHD3pbbbmNOk1kmXSQs7bA4dYDUaIx4ZEzdnvo6NwC3WHd/Qow==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.9.6", + "@babel/helper-split-export-declaration": "^7.8.3" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz", + "integrity": "sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-regex": "^7.8.3", + "regexpu-core": "^4.7.0" + } + }, + "@babel/helper-define-map": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz", + "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/types": "^7.8.3", + "lodash": "^4.17.13" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", + "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-function-name": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz", + "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.9.5" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz", + "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", + "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-module-imports": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", + "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-module-transforms": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", + "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-simple-access": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/template": "^7.8.6", + "@babel/types": "^7.9.0", + "lodash": "^4.17.13" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", + "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", + "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", + "dev": true, + "requires": { + "lodash": "^4.17.13" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", + "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-wrap-function": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-replace-supers": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz", + "integrity": "sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/traverse": "^7.9.6", + "@babel/types": "^7.9.6" + } + }, + "@babel/helper-simple-access": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", + "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", + "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", + "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.6.tgz", + "integrity": "sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.9.6", + "@babel/types": "^7.9.6" + } + }, + "@babel/highlight": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz", + "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz", + "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz", + "integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.8.3.tgz", + "integrity": "sha512-e3RvdvS4qPJVTe288DlXjwKflpfy1hr0j5dz5WpIYYeP7vQZg2WfAEIp8k5/Lwis/m5REXEteIz6rrcDtXXG7w==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-decorators": "^7.8.3" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz", + "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz", + "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz", + "integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.6.tgz", + "integrity": "sha512-Ga6/fhGqA9Hj+y6whNpPv8psyaK5xzrQwSPsGPloVkvmH+PqW1ixdnfJ9uIO06OjQNYol3PMnfmJ8vfZtkzF+A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.9.5" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz", + "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz", + "integrity": "sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.8", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.8.3.tgz", + "integrity": "sha512-8Hg4dNNT9/LcA1zQlfwuKR8BUc/if7Q7NkTam9sGTcJphLwpf2g4S42uhspQrIrR+dpzE0dtTqBVFoHl8GtnnQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz", + "integrity": "sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz", + "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz", + "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", + "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", + "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", + "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz", + "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "lodash": "^4.17.13" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz", + "integrity": "sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-define-map": "^7.8.3", + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-split-export-declaration": "^7.8.3", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz", + "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz", + "integrity": "sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz", + "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz", + "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz", + "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz", + "integrity": "sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz", + "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz", + "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz", + "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.6.tgz", + "integrity": "sha512-zoT0kgC3EixAyIAU+9vfaUVKTv9IxBDSabgHoUCBP6FqEJ+iNiN7ip7NBKcYqbfUDfuC2mFCbM7vbu4qJgOnDw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.6.tgz", + "integrity": "sha512-7H25fSlLcn+iYimmsNe3uK1at79IE6SKW9q0/QeEHTMC9MdOZ+4bA+T1VFB5fgOqBWoqlifXRzYD0JPdmIrgSQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-simple-access": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.6.tgz", + "integrity": "sha512-NW5XQuW3N2tTHim8e1b7qGy7s0kZ2OH3m5octc49K1SdAKGxYxeIx7hiIz05kS1R2R+hOWcsr1eYwcGhrdHsrg==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.8.3", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz", + "integrity": "sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz", + "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz", + "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", + "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.3" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz", + "integrity": "sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz", + "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz", + "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz", + "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.6.tgz", + "integrity": "sha512-qcmiECD0mYOjOIt8YHNsAP1SxPooC/rDmfmiSK9BNY72EitdSc7l44WTEklaWuFtbOEBjNhWWyph/kOImbNJ4w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "resolve": "^1.8.1", + "semver": "^5.5.1" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz", + "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz", + "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz", + "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-regex": "^7.8.3" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz", + "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz", + "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz", + "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/preset-env": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.6.tgz", + "integrity": "sha512-0gQJ9RTzO0heXOhzftog+a/WyOuqMrAIugVYxMYf83gh1CQaQDjMtsOpqOwXyDL/5JcWsrCm8l4ju8QC97O7EQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.9.6", + "@babel/helper-compilation-targets": "^7.9.6", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-proposal-async-generator-functions": "^7.8.3", + "@babel/plugin-proposal-dynamic-import": "^7.8.3", + "@babel/plugin-proposal-json-strings": "^7.8.3", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-proposal-numeric-separator": "^7.8.3", + "@babel/plugin-proposal-object-rest-spread": "^7.9.6", + "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", + "@babel/plugin-proposal-optional-chaining": "^7.9.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.8.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.8.3", + "@babel/plugin-transform-async-to-generator": "^7.8.3", + "@babel/plugin-transform-block-scoped-functions": "^7.8.3", + "@babel/plugin-transform-block-scoping": "^7.8.3", + "@babel/plugin-transform-classes": "^7.9.5", + "@babel/plugin-transform-computed-properties": "^7.8.3", + "@babel/plugin-transform-destructuring": "^7.9.5", + "@babel/plugin-transform-dotall-regex": "^7.8.3", + "@babel/plugin-transform-duplicate-keys": "^7.8.3", + "@babel/plugin-transform-exponentiation-operator": "^7.8.3", + "@babel/plugin-transform-for-of": "^7.9.0", + "@babel/plugin-transform-function-name": "^7.8.3", + "@babel/plugin-transform-literals": "^7.8.3", + "@babel/plugin-transform-member-expression-literals": "^7.8.3", + "@babel/plugin-transform-modules-amd": "^7.9.6", + "@babel/plugin-transform-modules-commonjs": "^7.9.6", + "@babel/plugin-transform-modules-systemjs": "^7.9.6", + "@babel/plugin-transform-modules-umd": "^7.9.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", + "@babel/plugin-transform-new-target": "^7.8.3", + "@babel/plugin-transform-object-super": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.9.5", + "@babel/plugin-transform-property-literals": "^7.8.3", + "@babel/plugin-transform-regenerator": "^7.8.7", + "@babel/plugin-transform-reserved-words": "^7.8.3", + "@babel/plugin-transform-shorthand-properties": "^7.8.3", + "@babel/plugin-transform-spread": "^7.8.3", + "@babel/plugin-transform-sticky-regex": "^7.8.3", + "@babel/plugin-transform-template-literals": "^7.8.3", + "@babel/plugin-transform-typeof-symbol": "^7.8.4", + "@babel/plugin-transform-unicode-regex": "^7.8.3", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.9.6", + "browserslist": "^4.11.1", + "core-js-compat": "^3.6.2", + "invariant": "^2.2.2", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/preset-modules": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", + "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz", + "integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/traverse": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.6.tgz", + "integrity": "sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.6", + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz", + "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.5", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@hapi/address": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", + "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==", + "dev": true + }, + "@hapi/bourne": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz", + "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==", + "dev": true + }, + "@hapi/hoek": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz", + "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==", + "dev": true + }, + "@hapi/joi": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz", + "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", + "dev": true, + "requires": { + "@hapi/address": "2.x.x", + "@hapi/bourne": "1.x.x", + "@hapi/hoek": "8.x.x", + "@hapi/topo": "3.x.x" + } + }, + "@hapi/topo": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz", + "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", + "dev": true, + "requires": { + "@hapi/hoek": "^8.3.0" + } + }, + "@intervolga/optimize-cssnano-plugin": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@intervolga/optimize-cssnano-plugin/-/optimize-cssnano-plugin-1.0.6.tgz", + "integrity": "sha512-zN69TnSr0viRSU6cEDIcuPcP67QcpQ6uHACg58FiN9PDrU6SLyGW3MR4tiISbYxy1kDWAVPwD+XwQTWE5cigAA==", + "dev": true, + "requires": { + "cssnano": "^4.0.0", + "cssnano-preset-default": "^4.0.0", + "postcss": "^7.0.0" + } + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "@soda/friendly-errors-webpack-plugin": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.1.tgz", + "integrity": "sha512-cWKrGaFX+rfbMrAxVv56DzhPNqOJPZuNIS2HGMELtgGzb+vsMzyig9mml5gZ/hr2BGtSLV+dP2LUEuAL8aG2mQ==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "error-stack-parser": "^2.0.0", + "string-width": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "@soda/get-current-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@soda/get-current-script/-/get-current-script-1.0.0.tgz", + "integrity": "sha512-9GvTek+7cVw7r+L7TNGOG1astZJWXz2h5q4BqMXl28KN+24iSCm1xo+RhZOZvwdT3bzNe9hD7riJc/lBoO7mgg==", + "dev": true + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "13.13.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.5.tgz", + "integrity": "sha512-3ySmiBYJPqgjiHA7oEaIo2Rzz0HrOZ7yrNO5HWyaE5q0lQ3BppDZ3N53Miz8bw2I7gh1/zir2MGVZBvpb1zq9g==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/q": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", + "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", + "dev": true + }, + "@vue/babel-helper-vue-jsx-merge-props": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz", + "integrity": "sha512-6tyf5Cqm4m6v7buITuwS+jHzPlIPxbFzEhXR5JGZpbrvOcp1hiQKckd305/3C7C36wFekNTQSxAtgeM0j0yoUw==", + "dev": true + }, + "@vue/babel-plugin-transform-vue-jsx": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.1.2.tgz", + "integrity": "sha512-YfdaoSMvD1nj7+DsrwfTvTnhDXI7bsuh+Y5qWwvQXlD24uLgnsoww3qbiZvWf/EoviZMrvqkqN4CBw0W3BWUTQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", + "html-tags": "^2.0.0", + "lodash.kebabcase": "^4.1.1", + "svg-tags": "^1.0.0" + } + }, + "@vue/babel-preset-app": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-4.3.1.tgz", + "integrity": "sha512-iNkySkbRWXGUA+Cvzj+/gEP0Y0uVAwwzfn21S7hkggSeIg9LJyZ+QzdxgKO0wgi01yTdb2mYWgeLQAfHZ65aew==", + "dev": true, + "requires": { + "@babel/core": "^7.9.0", + "@babel/helper-compilation-targets": "^7.8.7", + "@babel/helper-module-imports": "^7.8.3", + "@babel/plugin-proposal-class-properties": "^7.8.3", + "@babel/plugin-proposal-decorators": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-jsx": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.9.0", + "@babel/preset-env": "^7.9.0", + "@babel/runtime": "^7.9.2", + "@vue/babel-preset-jsx": "^1.1.2", + "babel-plugin-dynamic-import-node": "^2.3.0", + "core-js": "^3.6.4", + "core-js-compat": "^3.6.4" + } + }, + "@vue/babel-preset-jsx": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-preset-jsx/-/babel-preset-jsx-1.1.2.tgz", + "integrity": "sha512-zDpVnFpeC9YXmvGIDSsKNdL7qCG2rA3gjywLYHPCKDT10erjxF4U+6ay9X6TW5fl4GsDlJp9bVfAVQAAVzxxvQ==", + "dev": true, + "requires": { + "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", + "@vue/babel-sugar-functional-vue": "^1.1.2", + "@vue/babel-sugar-inject-h": "^1.1.2", + "@vue/babel-sugar-v-model": "^1.1.2", + "@vue/babel-sugar-v-on": "^1.1.2" + } + }, + "@vue/babel-sugar-functional-vue": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.1.2.tgz", + "integrity": "sha512-YhmdJQSVEFF5ETJXzrMpj0nkCXEa39TvVxJTuVjzvP2rgKhdMmQzlJuMv/HpadhZaRVMCCF3AEjjJcK5q/cYzQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@vue/babel-sugar-inject-h": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.1.2.tgz", + "integrity": "sha512-VRSENdTvD5htpnVp7i7DNuChR5rVMcORdXjvv5HVvpdKHzDZAYiLSD+GhnhxLm3/dMuk8pSzV+k28ECkiN5m8w==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@vue/babel-sugar-v-model": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.1.2.tgz", + "integrity": "sha512-vLXPvNq8vDtt0u9LqFdpGM9W9IWDmCmCyJXuozlq4F4UYVleXJ2Fa+3JsnTZNJcG+pLjjfnEGHci2339Kj5sGg==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", + "camelcase": "^5.0.0", + "html-tags": "^2.0.0", + "svg-tags": "^1.0.0" + } + }, + "@vue/babel-sugar-v-on": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.1.2.tgz", + "integrity": "sha512-T8ZCwC8Jp2uRtcZ88YwZtZXe7eQrJcfRq0uTFy6ShbwYJyz5qWskRFoVsdTi9o0WEhmQXxhQUewodOSCUPVmsQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", + "camelcase": "^5.0.0" + } + }, + "@vue/cli-overlay": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-4.3.1.tgz", + "integrity": "sha512-UA399aWHhre2VHrQFQSJhFLrFMqOYQ8ly+Ni6T+cpCjOwssjiaqaqrG5YiZBAqDwQvjrtYori4lU66qrY5DVhA==", + "dev": true + }, + "@vue/cli-plugin-babel": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-babel/-/cli-plugin-babel-4.3.1.tgz", + "integrity": "sha512-tBqu0v1l4LfWX8xuJmofpp+8xQzKddFNxdLmeVDOX/omDBQX0qaVDeMUtRxxSTazI06SKr605SnUQoa35qwbvw==", + "dev": true, + "requires": { + "@babel/core": "^7.9.0", + "@vue/babel-preset-app": "^4.3.1", + "@vue/cli-shared-utils": "^4.3.1", + "babel-loader": "^8.1.0", + "cache-loader": "^4.1.0", + "thread-loader": "^2.1.3", + "webpack": "^4.0.0" + } + }, + "@vue/cli-plugin-router": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.3.1.tgz", + "integrity": "sha512-m0ntr5R6q62oNMODgoyHAVAd/sDtsH15GdBrScZsPNeyHxmzmNBDlsNM38yYGGY064zDRRWif15d1yaTREybrA==", + "dev": true, + "requires": { + "@vue/cli-shared-utils": "^4.3.1" + } + }, + "@vue/cli-plugin-vuex": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.3.1.tgz", + "integrity": "sha512-mukwOlhZGBJhkqO2b3wHFFHjK5aP00b1WUHdrOfLR7M18euhaTyb4kA5nwZwEOmU3EzZx6kHzSFCRy/XaMkLug==", + "dev": true + }, + "@vue/cli-service": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-4.3.1.tgz", + "integrity": "sha512-CsNGfHe+9oKZdRwJmweQ0KsMYM27ssg1eNQqRKL/t+IgDLO3Tu86uaOOCLn4ZAaU5oxxpq4aSFvz+A0YxQRSWw==", + "dev": true, + "requires": { + "@intervolga/optimize-cssnano-plugin": "^1.0.5", + "@soda/friendly-errors-webpack-plugin": "^1.7.1", + "@soda/get-current-script": "^1.0.0", + "@vue/cli-overlay": "^4.3.1", + "@vue/cli-plugin-router": "^4.3.1", + "@vue/cli-plugin-vuex": "^4.3.1", + "@vue/cli-shared-utils": "^4.3.1", + "@vue/component-compiler-utils": "^3.0.2", + "@vue/preload-webpack-plugin": "^1.1.0", + "@vue/web-component-wrapper": "^1.2.0", + "acorn": "^7.1.0", + "acorn-walk": "^7.1.1", + "address": "^1.1.2", + "autoprefixer": "^9.7.5", + "browserslist": "^4.11.1", + "cache-loader": "^4.1.0", + "case-sensitive-paths-webpack-plugin": "^2.3.0", + "cli-highlight": "^2.1.4", + "clipboardy": "^2.3.0", + "cliui": "^6.0.0", + "copy-webpack-plugin": "^5.1.1", + "css-loader": "^3.4.2", + "cssnano": "^4.1.10", + "debug": "^4.1.1", + "default-gateway": "^5.0.5", + "dotenv": "^8.2.0", + "dotenv-expand": "^5.1.0", + "file-loader": "^4.2.0", + "fs-extra": "^7.0.1", + "globby": "^9.2.0", + "hash-sum": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "launch-editor-middleware": "^2.2.1", + "lodash.defaultsdeep": "^4.6.1", + "lodash.mapvalues": "^4.6.0", + "lodash.transform": "^4.6.0", + "mini-css-extract-plugin": "^0.9.0", + "minimist": "^1.2.5", + "pnp-webpack-plugin": "^1.6.4", + "portfinder": "^1.0.25", + "postcss-loader": "^3.0.0", + "ssri": "^7.1.0", + "terser-webpack-plugin": "^2.3.5", + "thread-loader": "^2.1.3", + "url-loader": "^2.2.0", + "vue-loader": "^15.9.1", + "vue-style-loader": "^4.1.2", + "webpack": "^4.0.0", + "webpack-bundle-analyzer": "^3.6.1", + "webpack-chain": "^6.4.0", + "webpack-dev-server": "^3.10.3", + "webpack-merge": "^4.2.2" + }, + "dependencies": { + "acorn": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", + "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", + "dev": true + }, + "cacache": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", + "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==", + "dev": true, + "requires": { + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "minipass": "^3.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "p-map": "^3.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^2.7.1", + "ssri": "^7.0.0", + "unique-filename": "^1.1.1" + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "serialize-javascript": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz", + "integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "ssri": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", + "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "minipass": "^3.1.1" + } + }, + "terser-webpack-plugin": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.6.tgz", + "integrity": "sha512-I8IDsQwZrqjdmOicNeE8L/MhwatAap3mUrtcAKJuilsemUNcX+Hier/eAzwStVqhlCxq0aG3ni9bK/0BESXkTg==", + "dev": true, + "requires": { + "cacache": "^13.0.1", + "find-cache-dir": "^3.3.1", + "jest-worker": "^25.4.0", + "p-limit": "^2.3.0", + "schema-utils": "^2.6.6", + "serialize-javascript": "^3.0.0", + "source-map": "^0.6.1", + "terser": "^4.6.12", + "webpack-sources": "^1.4.3" + } + } + } + }, + "@vue/cli-shared-utils": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.3.1.tgz", + "integrity": "sha512-lcfRalou7Z9jZgIh9PeTIpwDK7RIjr9OxfLGwbdR8czUZYUeUa67zVEMJD0OPYh/CCoREtzNbVfLPb/IYYxWEA==", + "dev": true, + "requires": { + "@hapi/joi": "^15.0.1", + "chalk": "^2.4.2", + "execa": "^1.0.0", + "launch-editor": "^2.2.1", + "lru-cache": "^5.1.1", + "node-ipc": "^9.1.1", + "open": "^6.3.0", + "ora": "^3.4.0", + "read-pkg": "^5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "semver": "^6.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@vue/component-compiler-utils": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.1.2.tgz", + "integrity": "sha512-QLq9z8m79mCinpaEeSURhnNCN6djxpHw0lpP/bodMlt5kALfONpryMthvnrQOlTcIKoF+VoPi+lPHUYeDFPXug==", + "dev": true, + "requires": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.14", + "postcss-selector-parser": "^6.0.2", + "prettier": "^1.18.2", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "@vue/preload-webpack-plugin": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.1.tgz", + "integrity": "sha512-8VCoJeeH8tCkzhkpfOkt+abALQkS11OIHhte5MBzYaKMTqK0A3ZAKEUVAffsOklhEv7t0yrQt696Opnu9oAx+w==", + "dev": true + }, + "@vue/web-component-wrapper": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vue/web-component-wrapper/-/web-component-wrapper-1.2.0.tgz", + "integrity": "sha512-Xn/+vdm9CjuC9p3Ae+lTClNutrVhsXpzxvoTXXtoys6kVRX9FkueSUAqSWAyZntmVLlR4DosBV4pH8y5Z/HbUw==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "acorn-walk": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", + "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", + "dev": true + }, + "address": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", + "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==", + "dev": true + }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "arch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz", + "integrity": "sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "9.7.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.6.tgz", + "integrity": "sha512-F7cYpbN7uVVhACZTeeIeealwdGM6wMtfWARVLTy5xmKtgVdBNJvbDRoCK3YO1orcs7gv/KwYlb3iXwu9Ug9BkQ==", + "dev": true, + "requires": { + "browserslist": "^4.11.1", + "caniuse-lite": "^1.0.30001039", + "chalk": "^2.4.2", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.27", + "postcss-value-parser": "^4.0.3" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", + "dev": true + }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + } + }, + "babel-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "dev": true, + "requires": { + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "schema-utils": "^2.6.5" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bfj": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz", + "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "check-types": "^8.0.3", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-IUTD/REb78Z2eodka1QZyyEk66pciRcP6Sroka0aI3tG/iwIdYLrBD62RsubR7vqdt3WyX8p4jxeatzmRSphtA==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + }, + "dependencies": { + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + } + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } + } + }, + "browserify-sign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.1.0.tgz", + "integrity": "sha512-VYxo7cDCeYUoBZ0ZCy4UyEUCP3smyBd4DRQM5nrFS1jJjPJjX7rP3oLRpPoWfkhQfyJ0I9ZbHbKafrFD/SGlrg==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.2", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-json/-/buffer-json-2.0.0.tgz", + "integrity": "sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cache-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cache-loader/-/cache-loader-4.1.0.tgz", + "integrity": "sha512-ftOayxve0PwKzBF/GLsZNC9fJBXl8lkZE3TOsjkboHfVHVkL39iUEs1FO07A33mizmci5Dudt38UZrrYXDtbhw==", + "dev": true, + "requires": { + "buffer-json": "^2.0.0", + "find-cache-dir": "^3.0.0", + "loader-utils": "^1.2.3", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.1", + "schema-utils": "^2.0.0" + }, + "dependencies": { + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001055", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001055.tgz", + "integrity": "sha512-MbwsBmKrBSKIWldfdIagO5OJWZclpJtS4h0Jrk/4HFrXJxTdVdH23Fd+xCiHriVGvYcWyW8mR/CPsYajlH8Iuw==", + "dev": true + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", + "integrity": "sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "check-types": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", + "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-highlight": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.4.tgz", + "integrity": "sha512-s7Zofobm20qriqDoU9sXptQx0t2R9PEgac92mENNm7xaEe1hn71IIMsXMK+6encA6WRCWWxIGQbipr3q998tlQ==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "highlight.js": "^9.6.0", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^5.1.1", + "yargs": "^15.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cli-spinners": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.3.0.tgz", + "integrity": "sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==", + "dev": true + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "clipboardy": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz", + "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==", + "dev": true, + "requires": { + "arch": "^2.1.1", + "execa": "^1.0.0", + "is-wsl": "^2.1.1" + }, + "dependencies": { + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + } + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dev": true, + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", + "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "dev": true, + "requires": { + "bluebird": "^3.1.1" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz", + "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==", + "dev": true, + "requires": { + "cacache": "^12.0.3", + "find-cache-dir": "^2.1.0", + "glob-parent": "^3.1.0", + "globby": "^7.1.1", + "is-glob": "^4.0.1", + "loader-utils": "^1.2.3", + "minimatch": "^3.0.4", + "normalize-path": "^3.0.0", + "p-limit": "^2.2.1", + "schema-utils": "^1.0.0", + "serialize-javascript": "^2.1.2", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + } + } + }, + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" + }, + "core-js-compat": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", + "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "dev": true, + "requires": { + "browserslist": "^4.8.5", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + } + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + } + }, + "css-loader": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.5.3.tgz", + "integrity": "sha512-UEr9NH5Lmi7+dguAm+/JSPovNjYbm2k3TK58EiwQHzOHH5Jfq1Y+XoP2bQO6TMn7PptMd0opxxedAWcaSTRKHw==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.27", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.0.3", + "schema-utils": "^2.6.6", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "dev": true + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-what": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz", + "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssnano": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", + "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.7", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "cssnano-preset-default": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", + "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", + "dev": true, + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.2", + "postcss-unique-selectors": "^4.0.1" + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "dev": true + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "dev": true + }, + "cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "dev": true + }, + "csso": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz", + "integrity": "sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==", + "dev": true, + "requires": { + "css-tree": "1.0.0-alpha.39" + }, + "dependencies": { + "css-tree": { + "version": "1.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz", + "integrity": "sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==", + "dev": true, + "requires": { + "mdn-data": "2.0.6", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz", + "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", + "dev": true + }, + "default-gateway": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-5.0.5.tgz", + "integrity": "sha512-z2RnruVmj8hVMmAnEJMTIJNijhKCDiGjbLP+BHJFOT7ld3Bo5qcIBpVYDniqhbMIIf+jZDlkP2MkPXiQy/DBLA==", + "dev": true, + "requires": { + "execa": "^3.3.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", + "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-finally": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", + "dev": true + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", + "dev": true + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "easy-stack": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", + "integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.433", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.433.tgz", + "integrity": "sha512-C0gcgwB8RpPAq2Ia6teihNOHOfNzGy4jJXgjIXSmKdt6O2xrJM8CPjA8jTFyo97KozVrZ8oH2FUCixC6Hnuk2g==", + "dev": true + }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.2.tgz", + "integrity": "sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw==", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "error-stack-parser": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", + "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", + "dev": true, + "requires": { + "stackframe": "^1.1.1" + } + }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "eslint-plugin-vue": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-6.2.2.tgz", + "integrity": "sha512-Nhc+oVAHm0uz/PkJAWscwIT4ijTrK5fqNqz9QB1D35SbbuMG1uB6Yr5AJpvPSWg+WOw7nYNswerYh0kOk64gqQ==", + "dev": true, + "requires": { + "natural-compare": "^1.4.0", + "semver": "^5.6.0", + "vue-eslint-parser": "^7.0.0" + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", + "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "event-pubsub": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", + "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.3.tgz", + "integrity": "sha512-HyaFeyfTa18nYjft59vEPsvuq6ZVcrCC1rBw6Fx8ZV9NcuUITBNCnTOyr0tHHkkHn//d+lzhsL1YybgtLQ7lng==", + "dev": true + }, + "events": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", + "dev": true + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.3.0.tgz", + "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz", + "integrity": "sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==", + "dev": true, + "requires": { + "debug": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + } + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "hash-sum": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", + "dev": true + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true + }, + "highlight.js": { + "version": "9.18.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz", + "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", + "dev": true + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", + "dev": true + }, + "html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "dev": true + }, + "html-entities": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", + "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==", + "dev": true + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + } + } + }, + "html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", + "dev": true + }, + "html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", + "dev": true, + "requires": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + } + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "http-proxy": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", + "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "inquirer": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", + "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + }, + "dependencies": { + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + } + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dev": true, + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-docker": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", + "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-svg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "dev": true, + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "javascript-stringify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.0.1.tgz", + "integrity": "sha512-yV+gqbd5vaOYjqlbk16EG89xB5udgjqQF3C5FAORDg4f/IS1Yc5ERCv5e/57yBcfJYw05V5JyIXabhwb75Xxow==", + "dev": true + }, + "jest-worker": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", + "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-message": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", + "integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=", + "dev": true + }, + "js-queue": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", + "integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", + "dev": true, + "requires": { + "easy-stack": "^1.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "launch-editor": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.2.1.tgz", + "integrity": "sha512-On+V7K2uZK6wK7x691ycSUbLD/FyKKelArkbaAMSSJU8JmqmhwN2+mnJDNINuJWSrh2L0kDk+ZQtbC/gOWUwLw==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "shell-quote": "^1.6.1" + } + }, + "launch-editor-middleware": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.2.1.tgz", + "integrity": "sha512-s0UO2/gEGiCgei3/2UN3SMuUj1phjQN8lcpnvgLSz26fAzNWPQ6Nf/kF5IFClnfU2ehp6LrmKdMU/beveO+2jg==", + "dev": true, + "requires": { + "launch-editor": "^2.2.1" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "dev": true, + "requires": { + "leven": "^3.1.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "lodash.defaultsdeep": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", + "dev": true + }, + "lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", + "dev": true + }, + "lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.transform": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.transform/-/lodash.transform-4.6.0.tgz", + "integrity": "sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "loglevel": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz", + "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } + } + }, + "mime": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.5.tgz", + "integrity": "sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w==", + "dev": true + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", + "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "minipass": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz", + "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz", + "integrity": "sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-forge": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", + "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", + "dev": true + }, + "node-ipc": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", + "integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", + "dev": true, + "requires": { + "event-pubsub": "4.3.0", + "js-message": "1.0.5", + "js-queue": "2.0.0" + } + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-releases": { + "version": "1.1.55", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.55.tgz", + "integrity": "sha512-H3R3YR/8TjT5WPin/wOoHOUPHgvj8leuU/Keta/rwelEQN9pA/S2Dx8/se4pZ2LBxSd0nAGzsNzhqwa77v7F1w==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + }, + "object-is": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", + "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "opener": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", + "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "dev": true + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "ora": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", + "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-spinners": "^2.0.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "requires": { + "retry": "^0.12.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "parse5-htmlparser2-tree-adapter": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-5.1.1.tgz", + "integrity": "sha512-CF+TKjXqoqyDwHqBhFQ+3l5t83xYi6fVT1tQNg+Ye0JRLnTxWvIroCjEp1A0k4lneHNBGnICUf0cfYVYGEazqw==", + "dev": true, + "requires": { + "parse5": "^5.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + } + } + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "pnp-webpack-plugin": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", + "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==", + "dev": true, + "requires": { + "ts-pnp": "^1.1.6" + } + }, + "portfinder": { + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz", + "integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.30.tgz", + "integrity": "sha512-nu/0m+NtIzoubO+xdAlwZl/u5S5vi/y6BCsoL8D+8IxsD3XvBS8X4YEADNIVXKVuQvduiucnRv+vPIqj56EGMQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-calc": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz", + "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==", + "dev": true, + "requires": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-load-config": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", + "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dev": true, + "requires": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz", + "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==", + "dev": true, + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.16", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.0" + } + }, + "postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dev": true, + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dev": true, + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dev": true, + "requires": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-selector-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", + "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", + "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", + "dev": true, + "requires": { + "is-svg": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true, + "optional": true + }, + "pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "dev": true, + "requires": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz", + "integrity": "sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4", + "private": "^0.1.8" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "regexpu-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", + "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + } + }, + "regjsgen": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", + "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", + "dev": true + }, + "regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "renderkid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", + "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "dev": true, + "requires": { + "css-select": "^1.1.0", + "dom-converter": "^0.2", + "htmlparser2": "^3.3.0", + "strip-ansi": "^3.0.0", + "utila": "^0.4.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "request-promise-core": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", + "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "request-promise-native": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", + "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", + "dev": true, + "requires": { + "request-promise-core": "1.1.3", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", + "dev": true + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sass": { + "version": "1.26.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.5.tgz", + "integrity": "sha512-FG2swzaZUiX53YzZSjSakzvGtlds0lcbF+URuU9mxOv7WBh7NhXEVDa4kPKN4hN6fC2TkOTOKqiqp6d53N9X5Q==", + "dev": true, + "requires": { + "chokidar": ">=2.0.0 <4.0.0" + } + }, + "sass-loader": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz", + "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.2.3", + "neo-async": "^2.6.1", + "schema-utils": "^2.6.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "schema-utils": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz", + "integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==", + "dev": true, + "requires": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", + "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", + "dev": true, + "requires": { + "node-forge": "0.9.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "dev": true + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sockjs": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", + "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.4.0", + "websocket-driver": "0.6.5" + } + }, + "sockjs-client": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "dev": true, + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "stackframe": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.1.1.tgz", + "integrity": "sha512-0PlYhdKh6AfFxRyK/v+6/k+/mMfyiEBbTM5L94D0ZytQnJ166wuwoTYLHFWGbs2dpA8Rgq763KGWmN1EQEYHRQ==", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + } + }, + "string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + } + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true + }, + "stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "dev": true + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "terser": { + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.13.tgz", + "integrity": "sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^2.1.2", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "thread-loader": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/thread-loader/-/thread-loader-2.1.3.tgz", + "integrity": "sha512-wNrVKH2Lcf8ZrWxDF/khdlLlsTMczdcwPA9VEK4c2exlEPynYWxi9op3nPTo5lAnDIkE0rQEB3VBP+4Zncc9Hg==", + "dev": true, + "requires": { + "loader-runner": "^2.3.1", + "loader-utils": "^1.1.0", + "neo-async": "^2.6.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, + "toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "dev": true + }, + "ts-pnp": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", + "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==", + "dev": true + }, + "tslib": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.2.tgz", + "integrity": "sha512-tTSkux6IGPnUGUd1XAZHcpu85MOkIl5zX49pO+jfsie3eP0B6pyhOlLXm3cAC6T7s+euSDDUUV+Acop5WmtkVg==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dev": true, + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-loader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-2.3.0.tgz", + "integrity": "sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "mime": "^2.4.4", + "schema-utils": "^2.5.0" + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "vue": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz", + "integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==" + }, + "vue-eslint-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.0.0.tgz", + "integrity": "sha512-yR0dLxsTT7JfD2YQo9BhnQ6bUTLsZouuzt9SKRP7XNaZJV459gvlsJo4vT2nhZ/2dH9j3c53bIx9dnqU2prM9g==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-scope": "^5.0.0", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "lodash": "^4.17.15" + }, + "dependencies": { + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + } + } + }, + "vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true + }, + "vue-loader": { + "version": "15.9.2", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.2.tgz", + "integrity": "sha512-oXBubaY//CYEISBlHX+c2YPJbmOH68xXPXjFv4MAgPqQvUsnjrBAjCJi8HXZ/r/yfn0tPL5VZj1Zcp8mJPI8VA==", + "dev": true, + "requires": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "dev": true + } + } + }, + "vue-style-loader": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz", + "integrity": "sha512-0ip8ge6Gzz/Bk0iHovU9XAUQaFt/G2B61bnWa2tCcqqdgfHs1lF9xXorFbE55Gmy92okFT+8bfmySuUOu13vxQ==", + "dev": true, + "requires": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "dev": true + } + } + }, + "vue-template-compiler": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz", + "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==", + "dev": true, + "requires": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true + }, + "watchpack": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz", + "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==", + "dev": true, + "requires": { + "chokidar": "^2.1.8", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webpack": { + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.43.0.tgz", + "integrity": "sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.6.1", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "webpack-bundle-analyzer": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.7.0.tgz", + "integrity": "sha512-mETdjZ30a3Yf+NTB/wqTgACK7rAYQl5uxKK0WVTNmF0sM3Uv8s3R58YZMW7Rhu0Lk2Rmuhdj5dcH5Q76zCDVdA==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.15", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + }, + "dependencies": { + "acorn": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", + "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", + "dev": true + } + } + }, + "webpack-chain": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-6.4.0.tgz", + "integrity": "sha512-f97PYqxU+9/u0IUqp/ekAHRhBD1IQwhBv3wlJo2nvyELpr2vNnUqO3XQEk+qneg0uWGP54iciotszpjfnEExFA==", + "dev": true, + "requires": { + "deepmerge": "^1.5.2", + "javascript-stringify": "^2.0.1" + } + }, + "webpack-dev-middleware": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "dev": true, + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + } + }, + "webpack-dev-server": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", + "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.7", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.20", + "sockjs-client": "1.4.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + }, + "webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "websocket-driver": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "dev": true, + "requires": { + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "yargs": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.1" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/playground/vue/package.json b/playground/vue/package.json new file mode 100644 index 000000000000..87350b4593a8 --- /dev/null +++ b/playground/vue/package.json @@ -0,0 +1,32 @@ +{ + "name": "devextreme-vue-playground", + "version": "0.1.0", + "private": true, + "scripts": { + "serve": "vue-cli-service serve", + "build": "vue-cli-service build", + "lint": "vue-cli-service lint" + }, + "dependencies": { + "core-js": "^3.6.4", + "vue": "^2.6.11" + }, + "devDependencies": { + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-proposal-optional-chaining": "^7.9.0", + "@babel/preset-env": "^7.9.6", + "@vue/cli-plugin-babel": "~4.3.0", + "@vue/cli-service": "~4.3.0", + "babel-eslint": "^10.1.0", + "eslint": "^6.7.2", + "eslint-plugin-vue": "^6.2.2", + "sass": "^1.26.5", + "sass-loader": "^8.0.2", + "vue-template-compiler": "^2.6.11" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not dead" + ] +} diff --git a/playground/vue/public/favicon.ico b/playground/vue/public/favicon.ico new file mode 100644 index 000000000000..df36fcfb7258 Binary files /dev/null and b/playground/vue/public/favicon.ico differ diff --git a/playground/vue/public/index.html b/playground/vue/public/index.html new file mode 100644 index 000000000000..412352865635 --- /dev/null +++ b/playground/vue/public/index.html @@ -0,0 +1,17 @@ + + + + + + + + <%= htmlWebpackPlugin.options.title %> + + + +
+ + + diff --git a/playground/vue/src/App.vue b/playground/vue/src/App.vue new file mode 100644 index 000000000000..5b1454c220f1 --- /dev/null +++ b/playground/vue/src/App.vue @@ -0,0 +1,35 @@ + + + + + + + diff --git a/playground/vue/src/assets/logo.png b/playground/vue/src/assets/logo.png new file mode 100644 index 000000000000..f3d2503fc2a4 Binary files /dev/null and b/playground/vue/src/assets/logo.png differ diff --git a/playground/vue/src/main.js b/playground/vue/src/main.js new file mode 100644 index 000000000000..ebca04e71bb6 --- /dev/null +++ b/playground/vue/src/main.js @@ -0,0 +1,10 @@ +/* eslint-disable */ +import Vue from 'vue'; +import App from './App.vue'; + +Vue.config.productionTip = false; + +new Vue({ + render: h => h(App), +}).$mount('#app'); +/* eslint-enable */ diff --git a/playground/vue/tsconfig.json b/playground/vue/tsconfig.json new file mode 100644 index 000000000000..08c824a2e6b1 --- /dev/null +++ b/playground/vue/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "allowJs": true + } +} diff --git a/styles/widgets/base/diagram.less b/styles/widgets/base/diagram.less index 80e298e0e3ed..87acf4e95796 100644 --- a/styles/widgets/base/diagram.less +++ b/styles/widgets/base/diagram.less @@ -15,6 +15,14 @@ .diagram-icon-colored(data-uri('image/svg+xml;charset=UTF-8', 'images/widgets/common/diagram/connector-begin-arrow.svg'), @toolbar-color, 1.56 * @icon-size, @icon-size); } + .dx-diagram-i-connector-begin-outlined-triangle { + .diagram-icon-colored(data-uri('image/svg+xml;charset=UTF-8', 'images/widgets/common/diagram/connector-begin-outlined-triangle.svg'), @toolbar-color, 1.56 * @icon-size, @icon-size); + } + + .dx-diagram-i-connector-begin-filled-triangle { + .diagram-icon-colored(data-uri('image/svg+xml;charset=UTF-8', 'images/widgets/common/diagram/connector-begin-filled-triangle.svg'), @toolbar-color, 1.56 * @icon-size, @icon-size); + } + .dx-diagram-i-connector-end-none { .diagram-icon-colored(data-uri('image/svg+xml;charset=UTF-8', 'images/widgets/common/diagram/connector-end-none.svg'), @toolbar-color, 1.56 * @icon-size, @icon-size); } @@ -23,6 +31,14 @@ .diagram-icon-colored(data-uri('image/svg+xml;charset=UTF-8', 'images/widgets/common/diagram/connector-end-arrow.svg'), @toolbar-color, 1.56 * @icon-size, @icon-size); } + .dx-diagram-i-connector-end-outlined-triangle { + .diagram-icon-colored(data-uri('image/svg+xml;charset=UTF-8', 'images/widgets/common/diagram/connector-end-outlined-triangle.svg'), @toolbar-color, 1.56 * @icon-size, @icon-size); + } + + .dx-diagram-i-connector-end-filled-triangle { + .diagram-icon-colored(data-uri('image/svg+xml;charset=UTF-8', 'images/widgets/common/diagram/connector-end-filled-triangle.svg'), @toolbar-color, 1.56 * @icon-size, @icon-size); + } + .dx-diagram-i-connector-orthogonal { .diagram-icon-colored(data-uri('image/svg+xml;charset=UTF-8', 'images/widgets/common/diagram/connector-orthogonal.svg'), @toolbar-color, 1.56 * @icon-size, @icon-size); } diff --git a/styles/widgets/base/scheduler.less b/styles/widgets/base/scheduler.less index 751c67ef3bcb..e135fa481f3e 100644 --- a/styles/widgets/base/scheduler.less +++ b/styles/widgets/base/scheduler.less @@ -624,6 +624,23 @@ .dx-scheduler-work-space-week, .dx-scheduler-work-space-work-week, .dx-scheduler-work-space-day { + .dx-scheduler-all-day-appointment .dx-scheduler-appointment-content { + padding: 0 7px; + } + + .dx-scheduler-appointment-reduced { + .dx-scheduler-appointment-content { + padding-right: @SCHEDULER_APPOINTMENT_REDUCED_CONTENT_PADDING; + } + } + + .dx-scheduler-appointment-reduced:not(.dx-scheduler-all-day-appointment) { + .dx-scheduler-appointment-reduced-icon, + .dx-scheduler-appointment-recurrence-icon { + top: 9px; + } + } + .dx-scheduler-date-time-indicator { margin-left: @SCHEDULER_LEFT_COLUMN_WIDTH; height: @SCHEDULER_TIME_INDICATOR_SIZE; @@ -651,6 +668,22 @@ } } + &.dx-scheduler-work-space-odd-cells { + .dx-scheduler-date-table-row:nth-child(odd) .dx-scheduler-date-table-cell { + border-top: @SCHEDULER_ACCENT_BORDER; + } + + .dx-scheduler-date-table-row:first-child .dx-scheduler-date-table-cell { + border-top: none; + } + + .dx-scheduler-time-panel-row:nth-child(2n) .dx-scheduler-time-panel-cell { + &:after { + background-color: @SCHEDULER_ACCENT_BORDER_COLOR; + } + } + } + &.dx-scheduler-work-space-both-scrollbar { .dx-scheduler-date-time-shader, .dx-scheduler-date-time-indicator, @@ -822,13 +855,6 @@ height: @SCHEDULER_WORKSPACE_DATE_TABLE_CELL_HEIGHT; } - .dx-scheduler-date-table-cell, - .dx-scheduler-all-day-table-cell { - > div { - pointer-events: none; - } - } - .dx-scheduler-date-table-cell, .dx-scheduler-header-panel-cell, .dx-scheduler-time-panel-cell, @@ -879,27 +905,6 @@ } } - // stylelint-disable-next-line no-duplicate-selectors - .dx-scheduler-work-space-day, - .dx-scheduler-work-space-week, - .dx-scheduler-work-space-work-week { - &.dx-scheduler-work-space-odd-cells { - .dx-scheduler-date-table-row:nth-child(odd) .dx-scheduler-date-table-cell { - border-top: @SCHEDULER_ACCENT_BORDER; - } - - .dx-scheduler-date-table-row:first-child .dx-scheduler-date-table-cell { - border-top: none; - } - - .dx-scheduler-time-panel-row:nth-child(2n) .dx-scheduler-time-panel-cell { - &:after { - background-color: @SCHEDULER_ACCENT_BORDER_COLOR; - } - } - } - } - .dx-scheduler-work-space-day { &:not(.dx-scheduler-work-space-vertical-grouped) { .dx-scheduler-scrollable-offset-mixin(0, @SCHEDULER_ALL_DAY_TABLE_CELL_HEIGHT, @SCHEDULER_COLLAPSED_ALL_DAY_TABLE_CELL_HEIGHT); @@ -949,14 +954,6 @@ } } - .dx-scheduler-work-space-week, - .dx-scheduler-work-space-work-week, - .dx-scheduler-work-space-day.dx-scheduler-work-space-overlapping { - .dx-scheduler-all-day-appointment .dx-scheduler-appointment-content { - padding: 0 7px; - } - } - .dx-scheduler-scrollable-fixed-content { height: 100%; } @@ -1052,12 +1049,6 @@ .dx-scheduler-all-day-title { display: none; } - - .dx-scheduler-appointment-reduced.dx-scheduler-appointment-head { - .dx-scheduler-appointment-recurrence-icon { - right: @SCHEDULER_RECURRENCE_ICON_OFFSET; - } - } } .dx-scheduler-timeline { // stylelint-disable-line no-duplicate-selectors @@ -1745,6 +1736,10 @@ &.dx-scheduler-focused-cell { box-shadow: @SCHEDULER_FOCUSED_CELL_SHADOW; } + + > div { + pointer-events: none; + } } .dx-scheduler-date-table-droppable-cell { @@ -1838,28 +1833,6 @@ } } - // stylelint-disable-next-line no-duplicate-selectors - .dx-scheduler-work-space-week, - .dx-scheduler-work-space-work-week, - .dx-scheduler-work-space-day { - .dx-scheduler-appointment-reduced { - .dx-scheduler-appointment-content { - padding-right: @SCHEDULER_APPOINTMENT_REDUCED_CONTENT_PADDING; - } - } - - .dx-scheduler-appointment-reduced-icon { - top: 9px; - } - - .dx-scheduler-appointment-head { - .dx-scheduler-appointment-recurrence-icon { - top: calc(35% - 3px); - right: @SCHEDULER_RECURRENCE_ICON_OFFSET; - } - } - } - .dx-scheduler-timeline .dx-scheduler-appointment, .dx-scheduler-work-space-month .dx-scheduler-appointment, .dx-scheduler-all-day-appointment { @@ -1897,11 +1870,6 @@ } } - .dx-scheduler-all-day-appointment .dx-scheduler-appointment-reduced-icon { - position: absolute; - top: 15%; - } - .dx-scheduler-appointment { // stylelint-disable-line no-duplicate-selectors &.dx-scheduler-appointment-body, &.dx-scheduler-appointment-tail { @@ -1918,18 +1886,25 @@ background-repeat: no-repeat; top: 3px; right: @SCHEDULER_REDUCED_ICON_OFFSET; - display: none; + display: block; &.dx-icon-repeat { font-size: 18px; } } - .dx-scheduler-appointment-recurrence { + .dx-scheduler-all-day-appointment .dx-scheduler-appointment-reduced-icon { + position: absolute; + top: 15%; + } + + .dx-scheduler-appointment-reduced.dx-scheduler-appointment-head { .dx-scheduler-appointment-recurrence-icon { - display: block; + right: @SCHEDULER_RECURRENCE_ICON_OFFSET; } + } + .dx-scheduler-appointment-recurrence { .dx-scheduler-appointment-content { padding: @SCHEDULER_APPOINTMENT_RECURRENCE_CONTENT_PADDING; @@ -2557,7 +2532,7 @@ } } - .dx-scheduler-work-space:not(.dx-scheduler-timeline):not(.dx-scheduler-work-space-month):not(.dx-scheduler-work-space-vertical-grouped) { + .dx-scheduler-work-space:not(.dx-scheduler-timeline):not(.dx-scheduler-work-space-month):not(.dx-scheduler-work-space-vertical-grouped):not(.dx-scheduler-work-space-both-scrollbar) { .dx-scheduler-date-table { float: right; margin-left: 0; @@ -2570,7 +2545,7 @@ } .dx-scheduler-appointment-recurrence-icon { - left: @SCHEDULER_RECURRENCE_ICON_OFFSET; + left: @SCHEDULER_REDUCED_ICON_OFFSET; right: auto; } @@ -2582,6 +2557,17 @@ } } + .dx-scheduler-work-space-week, + .dx-scheduler-work-space-work-week, + .dx-scheduler-work-space-day { + .dx-scheduler-appointment-reduced { + .dx-scheduler-appointment-content { + padding-right: 5px; + padding-left: @SCHEDULER_APPOINTMENT_REDUCED_CONTENT_PADDING; + } + } + } + .dx-scheduler-work-space-week .dx-scheduler-all-day-table-cell, .dx-scheduler-work-space-work-week .dx-scheduler-all-day-table-cell { border-right: @SCHEDULER_BASE_BORDER; @@ -2622,7 +2608,8 @@ border-right: none; } - &.dx-scheduler-work-space.dx-scheduler-work-space-month { + &.dx-scheduler-work-space.dx-scheduler-work-space-month, + &.dx-scheduler-work-space.dx-scheduler-timeline { .dx-scheduler-date-table-scrollable { margin-right: 0; } @@ -2633,6 +2620,7 @@ .dx-scheduler-all-day-table-row .dx-scheduler-all-day-table-cell { &:first-child { border-right: none; + border-left: @SCHEDULER_BASE_BORDER; } } } diff --git a/styles/widgets/common/fileManager.less b/styles/widgets/common/fileManager.less index 1ca0c62a61c5..e834684297ab 100644 --- a/styles/widgets/common/fileManager.less +++ b/styles/widgets/common/fileManager.less @@ -444,6 +444,12 @@ max-height: 180px; } +.dx-filemanager-dialog-delete-item-popup > .dx-overlay-content { + max-width: 340px; + min-height: 180px; + max-height: 180px; +} + .dx-filemanager-progresspanel { box-sizing: border-box; padding: 5px; diff --git a/styles/widgets/generic/color-schemes/carmine/generic.carmine.less b/styles/widgets/generic/color-schemes/carmine/generic.carmine.less index 93a99a910866..d29753e0ac17 100644 --- a/styles/widgets/generic/color-schemes/carmine/generic.carmine.less +++ b/styles/widgets/generic/color-schemes/carmine/generic.carmine.less @@ -876,18 +876,6 @@ */ @scrollable-scrollbar-active-bg: fade(darken(@scrollable-bg, 25%), 20%); -@scrollable-scrollbar-thumb-bg: darken(@scrollable-bg, 54%); - -@scrollable-scrollbar-msie-base-color: @scrollable-scrollbar-thumb-bg; -@scrollable-scrollbar-msie-arrow-color: @scrollable-scrollbar-msie-base-color; -@scrollable-scrollbar-msie-shadow-color: @scrollable-bg; -@scrollable-scrollbar-msie-track-color: @scrollable-bg; - -@scrollbar-button-horizontal-decrement-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-start.png') no-repeat; -@scrollbar-button-horizontal-increment-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-end.png') no-repeat; -@scrollbar-button-vertical-decrement-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-up.png') no-repeat; -@scrollbar-button-vertical-increment-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-down.png') no-repeat; - // ScrollView @scrollview-shadow-color: fade(@base-shadow-color, 37%); diff --git a/styles/widgets/generic/color-schemes/contrast/generic.contrast.less b/styles/widgets/generic/color-schemes/contrast/generic.contrast.less index 3254314ad705..2baea9448e9a 100644 --- a/styles/widgets/generic/color-schemes/contrast/generic.contrast.less +++ b/styles/widgets/generic/color-schemes/contrast/generic.contrast.less @@ -719,12 +719,6 @@ @scrollable-scroll-shadow-color: fade(@scrollable-bg, 20%); @scrollable-scrollbar-bg: fade(@base-inverted-bg, 0%); -@scrollable-scrollbar-thumb-bg: @base-inverted-bg; - -@scrollable-scrollbar-msie-base-color: @scrollable-scrollbar-thumb-bg; -@scrollable-scrollbar-msie-arrow-color: @scrollable-scrollbar-msie-base-color; -@scrollable-scrollbar-msie-shadow-color: @scrollable-bg; -@scrollable-scrollbar-msie-track-color: @scrollable-bg; /** * @name 20. Scrollbar background color @@ -732,12 +726,6 @@ */ @scrollable-scrollbar-active-bg: fade(@base-accent, 80%); -@scrollbar-button-horizontal-decrement-bg: data-uri('images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-horizontal-arrow-start.png') no-repeat; -@scrollbar-button-horizontal-increment-bg: data-uri('images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-horizontal-arrow-end.png') no-repeat; -@scrollbar-button-vertical-decrement-bg: data-uri('images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-vertical-arrow-up.png') no-repeat; -@scrollbar-button-vertical-increment-bg: data-uri('images/widgets/generic/color-schemes/contrast/scrollable/scrollbar-vertical-arrow-down.png') no-repeat; - - // Toolbar /** diff --git a/styles/widgets/generic/color-schemes/dark/generic.dark.less b/styles/widgets/generic/color-schemes/dark/generic.dark.less index eba89b63991e..45d6f6658595 100644 --- a/styles/widgets/generic/color-schemes/dark/generic.dark.less +++ b/styles/widgets/generic/color-schemes/dark/generic.dark.less @@ -884,18 +884,6 @@ */ @scrollable-scrollbar-active-bg: fade(lighten(@scrollable-bg, 25%), 20%); -@scrollable-scrollbar-thumb-bg: lighten(@scrollable-bg, 54%); - -@scrollable-scrollbar-msie-base-color: @scrollable-scrollbar-thumb-bg; -@scrollable-scrollbar-msie-arrow-color: @scrollable-scrollbar-msie-base-color; -@scrollable-scrollbar-msie-shadow-color: @scrollable-bg; -@scrollable-scrollbar-msie-track-color: @scrollable-bg; - -@scrollbar-button-horizontal-decrement-bg: data-uri('images/widgets/generic/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-start.png') no-repeat; -@scrollbar-button-horizontal-increment-bg: data-uri('images/widgets/generic/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-end.png') no-repeat; -@scrollbar-button-vertical-decrement-bg: data-uri('images/widgets/generic/color-schemes/dark/scrollable/scrollbar-vertical-arrow-up.png') no-repeat; -@scrollbar-button-vertical-increment-bg: data-uri('images/widgets/generic/color-schemes/dark/scrollable/scrollbar-vertical-arrow-down.png') no-repeat; - // ScrollView @scrollview-shadow-color: fade(@base-shadow-color, 37%); diff --git a/styles/widgets/generic/color-schemes/darkmoon/generic.darkmoon.less b/styles/widgets/generic/color-schemes/darkmoon/generic.darkmoon.less index 375e0286254a..68fb8eedc4eb 100644 --- a/styles/widgets/generic/color-schemes/darkmoon/generic.darkmoon.less +++ b/styles/widgets/generic/color-schemes/darkmoon/generic.darkmoon.less @@ -892,18 +892,6 @@ */ @scrollable-scrollbar-active-bg: fade(lighten(@base-bg, 25%), 20%); -@scrollable-scrollbar-thumb-bg: lighten(@scrollable-bg, 54%); - -@scrollable-scrollbar-msie-base-color: @scrollable-scrollbar-thumb-bg; -@scrollable-scrollbar-msie-arrow-color: @scrollable-scrollbar-msie-base-color; -@scrollable-scrollbar-msie-shadow-color: @scrollable-bg; -@scrollable-scrollbar-msie-track-color: @scrollable-bg; - -@scrollbar-button-horizontal-decrement-bg: data-uri('images/widgets/generic/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-start.png') no-repeat; -@scrollbar-button-horizontal-increment-bg: data-uri('images/widgets/generic/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-end.png') no-repeat; -@scrollbar-button-vertical-decrement-bg: data-uri('images/widgets/generic/color-schemes/dark/scrollable/scrollbar-vertical-arrow-up.png') no-repeat; -@scrollbar-button-vertical-increment-bg: data-uri('images/widgets/generic/color-schemes/dark/scrollable/scrollbar-vertical-arrow-down.png') no-repeat; - // ScrollView @scrollview-shadow-color: fade(@base-shadow-color, 37%); diff --git a/styles/widgets/generic/color-schemes/darkviolet/generic.darkviolet.less b/styles/widgets/generic/color-schemes/darkviolet/generic.darkviolet.less index b9dcdb109e70..bc83e8d56119 100644 --- a/styles/widgets/generic/color-schemes/darkviolet/generic.darkviolet.less +++ b/styles/widgets/generic/color-schemes/darkviolet/generic.darkviolet.less @@ -876,24 +876,12 @@ @scrollable-scrollbar-bg: transparent; -@scrollbar-button-horizontal-decrement-bg: data-uri('images/widgets/generic/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-start.png') no-repeat; -@scrollbar-button-horizontal-increment-bg: data-uri('images/widgets/generic/color-schemes/dark/scrollable/scrollbar-horizontal-arrow-end.png') no-repeat; -@scrollbar-button-vertical-decrement-bg: data-uri('images/widgets/generic/color-schemes/dark/scrollable/scrollbar-vertical-arrow-up.png') no-repeat; -@scrollbar-button-vertical-increment-bg: data-uri('images/widgets/generic/color-schemes/dark/scrollable/scrollbar-vertical-arrow-down.png') no-repeat; - /** * @name 20. Scrollbar background color * @type color */ @scrollable-scrollbar-active-bg: fade(#4d4d68, 50%); -@scrollable-scrollbar-thumb-bg: lighten(@scrollable-bg, 54%); - -@scrollable-scrollbar-msie-base-color: @scrollable-scrollbar-thumb-bg; -@scrollable-scrollbar-msie-arrow-color: @scrollable-scrollbar-msie-base-color; -@scrollable-scrollbar-msie-shadow-color: @scrollable-bg; -@scrollable-scrollbar-msie-track-color: @scrollable-bg; - // ScrollView @scrollview-shadow-color: fade(@base-shadow-color, 37%); diff --git a/styles/widgets/generic/color-schemes/greenmist/generic.greenmist.less b/styles/widgets/generic/color-schemes/greenmist/generic.greenmist.less index 2f6e69ac88f3..026f02b3f3db 100644 --- a/styles/widgets/generic/color-schemes/greenmist/generic.greenmist.less +++ b/styles/widgets/generic/color-schemes/greenmist/generic.greenmist.less @@ -879,18 +879,6 @@ */ @scrollable-scrollbar-active-bg: fade(darken(@base-bg, 9%), 70%); -@scrollable-scrollbar-thumb-bg: darken(@scrollable-bg, 54%); - -@scrollable-scrollbar-msie-base-color: @scrollable-scrollbar-thumb-bg; -@scrollable-scrollbar-msie-arrow-color: @scrollable-scrollbar-msie-base-color; -@scrollable-scrollbar-msie-shadow-color: @scrollable-bg; -@scrollable-scrollbar-msie-track-color: @scrollable-bg; - -@scrollbar-button-horizontal-decrement-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-start.png') no-repeat; -@scrollbar-button-horizontal-increment-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-end.png') no-repeat; -@scrollbar-button-vertical-decrement-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-up.png') no-repeat; -@scrollbar-button-vertical-increment-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-down.png') no-repeat; - // ScrollView @scrollview-shadow-color: fade(@base-shadow-color, 37%); diff --git a/styles/widgets/generic/color-schemes/light/generic.light.less b/styles/widgets/generic/color-schemes/light/generic.light.less index 27798f78b259..bdb6cf4cdb5b 100644 --- a/styles/widgets/generic/color-schemes/light/generic.light.less +++ b/styles/widgets/generic/color-schemes/light/generic.light.less @@ -878,18 +878,6 @@ */ @scrollable-scrollbar-active-bg: fade(darken(@scrollable-bg, 25%), 20%); -@scrollable-scrollbar-thumb-bg: darken(@scrollable-bg, 54%); - -@scrollable-scrollbar-msie-base-color: @scrollable-scrollbar-thumb-bg; -@scrollable-scrollbar-msie-arrow-color: @scrollable-scrollbar-msie-base-color; -@scrollable-scrollbar-msie-shadow-color: @scrollable-bg; -@scrollable-scrollbar-msie-track-color: @scrollable-bg; - -@scrollbar-button-horizontal-decrement-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-start.png') no-repeat; -@scrollbar-button-horizontal-increment-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-end.png') no-repeat; -@scrollbar-button-vertical-decrement-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-up.png') no-repeat; -@scrollbar-button-vertical-increment-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-down.png') no-repeat; - // ScrollView @scrollview-shadow-color: fade(@base-shadow-color, 37%); diff --git a/styles/widgets/generic/color-schemes/softblue/generic.softblue.less b/styles/widgets/generic/color-schemes/softblue/generic.softblue.less index 6dbbcaddc9e4..474156041523 100644 --- a/styles/widgets/generic/color-schemes/softblue/generic.softblue.less +++ b/styles/widgets/generic/color-schemes/softblue/generic.softblue.less @@ -888,18 +888,6 @@ */ @scrollable-scrollbar-active-bg: fade(darken(@scrollable-bg, 25%), 20%); -@scrollable-scrollbar-thumb-bg: darken(@scrollable-bg, 54%); - -@scrollable-scrollbar-msie-base-color: @scrollable-scrollbar-thumb-bg; -@scrollable-scrollbar-msie-arrow-color: @scrollable-scrollbar-msie-base-color; -@scrollable-scrollbar-msie-shadow-color: @scrollable-bg; -@scrollable-scrollbar-msie-track-color: @scrollable-bg; - -@scrollbar-button-horizontal-decrement-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-start.png') no-repeat; -@scrollbar-button-horizontal-increment-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-horizontal-arrow-end.png') no-repeat; -@scrollbar-button-vertical-decrement-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-up.png') no-repeat; -@scrollbar-button-vertical-increment-bg: data-uri('images/widgets/generic/color-schemes/light/scrollable/scrollbar-vertical-arrow-down.png') no-repeat; - // ScrollView @scrollview-shadow-color: fade(@base-shadow-color, 37%); diff --git a/styles/widgets/generic/diagram.generic.less b/styles/widgets/generic/diagram.generic.less index 691b268ad24f..ad23feac5e87 100644 --- a/styles/widgets/generic/diagram.generic.less +++ b/styles/widgets/generic/diagram.generic.less @@ -71,12 +71,12 @@ .connector-point-mark, .connector-selection, .connector-multi-selection, - .extension-line line, - .extension-line text { + .extension-line path { stroke: @button-default-bg; } - .connector-side-mark { + .connector-side-mark, + .extension-line text { fill: @button-default-bg; } diff --git a/styles/widgets/generic/gantt.generic.less b/styles/widgets/generic/gantt.generic.less index 5e8555cc496a..1945ad1b3dde 100644 --- a/styles/widgets/generic/gantt.generic.less +++ b/styles/widgets/generic/gantt.generic.less @@ -91,9 +91,9 @@ padding-top: @GANTT_TASK_WRAPPER_PADDING_TOP; .dx-gantt-task, - .dx-gantt-titleOut, - .dx-gantt-taskRes { + .dx-gantt-titleOut { height: @GANTT_TASK_HEIGHT; + line-height: @GANTT_TASK_HEIGHT; } .dx-gantt-task { @@ -115,6 +115,7 @@ &.dx-gantt-parent { height: @GANTT_TASK_HEIGHT - 2px; + line-height: @GANTT_TASK_HEIGHT - 2px; background-color: @gantt-parent-task-bg; border-bottom-right-radius: 0; border-bottom-left-radius: 0; @@ -179,27 +180,29 @@ } } - .dx-gantt-taskRes { - color: #fff; - background-color: #999; - border-radius: @base-border-radius; - margin-left: 21px; - padding-left: 4px; - padding-right: 4px; - } - .dx-gantt-titleIn { color: #fff; padding: 0 @GANTT_TASK_TITLE_PADDING_LEFT; } + } - .dx-gantt-titleOut { - padding-right: 20px; - } + .dx-gantt-taskRes { + height: @GANTT_TASK_HEIGHT; + line-height: @GANTT_TASK_HEIGHT; + color: #fff; + background-color: #999; + border-radius: @base-border-radius; + margin-left: 21px; + padding-left: 4px; + padding-right: 4px; + } + + .dx-gantt-titleOut { + padding-right: 20px; } .dx-gantt-milestone { - background-image: linear-gradient(135deg, @base-border-color, @base-text-color); + background-color: @base-text-color; width: @GANTT_MILESTONE_SIZE; height: @GANTT_MILESTONE_SIZE; } diff --git a/styles/widgets/generic/switch.generic.less b/styles/widgets/generic/switch.generic.less index fff253be3c80..7c0025f3d9ce 100644 --- a/styles/widgets/generic/switch.generic.less +++ b/styles/widgets/generic/switch.generic.less @@ -14,7 +14,7 @@ @GENERIC_SWITCH_HANDLE_WIDTH: 14px; @GENERIC_SWITCH_HANDLE_OFFSET: 1px; @GENERIC_SWITCH_CONTAINER_ADJUSTMENT: 4px; - @GENERIC_SWITCH_FONT_SIZE: 9px; + @GENERIC_SWITCH_FONT_SIZE: 8px; @GENERIC_SWITCH_ON_OFF_ADDING_PADDING: 0; } diff --git a/styles/widgets/material/diagram.material.less b/styles/widgets/material/diagram.material.less index 335902986046..e579f48fdf68 100644 --- a/styles/widgets/material/diagram.material.less +++ b/styles/widgets/material/diagram.material.less @@ -83,12 +83,12 @@ .connector-point-mark, .connector-selection, .connector-multi-selection, - .extension-line line, - .extension-line text { + .extension-line path { stroke: @button-default-bg; } - .connector-side-mark { + .connector-side-mark, + .extension-line text { fill: @button-default-bg; } diff --git a/styles/widgets/material/gantt.material.less b/styles/widgets/material/gantt.material.less index db9c4ee736fd..55de89ea9d2a 100644 --- a/styles/widgets/material/gantt.material.less +++ b/styles/widgets/material/gantt.material.less @@ -59,8 +59,7 @@ padding-top: @GANTT_TASK_WRAPPER_PADDING_TOP; .dx-gantt-task, - .dx-gantt-titleOut, - .dx-gantt-taskRes { + .dx-gantt-titleOut { height: @GANTT_TASK_HEIGHT; line-height: @GANTT_TASK_HEIGHT; } @@ -84,6 +83,7 @@ &.dx-gantt-parent { height: @GANTT_TASK_HEIGHT - 2px; + line-height: @GANTT_TASK_HEIGHT - 2px; background-color: @gantt_parent_task_background_color; border-bottom-right-radius: 0; border-bottom-left-radius: 0; @@ -148,27 +148,29 @@ } } - .dx-gantt-taskRes { - color: #fff; - background-color: #999; - border-radius: @base-border-radius; - margin-left: 21px; - padding-left: 4px; - padding-right: 4px; - } - .dx-gantt-titleIn { color: #fff; padding: 0 @GANTT_TASK_TITLE_PADDING_LEFT; } + } - .dx-gantt-titleOut { - padding-right: 20px; - } + .dx-gantt-taskRes { + height: @GANTT_TASK_HEIGHT; + line-height: @GANTT_TASK_HEIGHT; + color: #fff; + background-color: #999; + border-radius: @base-border-radius; + margin-left: 21px; + padding-left: 4px; + padding-right: 4px; + } + + .dx-gantt-titleOut { + padding-right: 20px; } .dx-gantt-milestone { - background-image: linear-gradient(135deg, @base-border-color, @base-text-color); + background-color: @base-text-color; width: @GANTT_MILESTONE_SIZE; height: @GANTT_MILESTONE_SIZE; } diff --git a/styles/widgets/material/scheduler.material.less b/styles/widgets/material/scheduler.material.less index e94d5df15df6..4babf1dfa63d 100644 --- a/styles/widgets/material/scheduler.material.less +++ b/styles/widgets/material/scheduler.material.less @@ -227,8 +227,7 @@ .dx-scheduler-work-space-week, .dx-scheduler-work-space-work-week, -.dx-scheduler-work-space-day, -.dx-scheduler-work-space-day.dx-scheduler-work-space-overlapping { +.dx-scheduler-work-space-day { .dx-scheduler-all-day-appointment { .dx-scheduler-appointment-content { padding: @MATERIAL_SCHEDULER_APPOINTMENT_CONTENT_PADDING; diff --git a/styles/widgets/material/toolbar.material.less b/styles/widgets/material/toolbar.material.less index e6610a6b2b4c..ecee6cb90980 100644 --- a/styles/widgets/material/toolbar.material.less +++ b/styles/widgets/material/toolbar.material.less @@ -14,10 +14,14 @@ .dx-size-default() { @MATERIAL_TOOLBAR_HEIGHT: 76px; + @MATERIAL_TOOLBAR_MENU_ITEM_LINE_HEIGHT: 21px; + @MATERIAL_TOOLBAR_MENU_VERTICAL_PADDING: 6px; } .dx-size-compact() { @MATERIAL_TOOLBAR_HEIGHT: 56px; + @MATERIAL_TOOLBAR_MENU_ITEM_LINE_HEIGHT: 12px; + @MATERIAL_TOOLBAR_MENU_VERTICAL_PADDING: 5px; } @@ -203,6 +207,34 @@ border-radius: 50%; } } + + .dx-menu { + .dx-menu-item { + color: @toolbar-color; + font-size: @MATERIAL_BASE_FONT_SIZE; + font-weight: 500; + + .dx-menu-item-content { + line-height: @MATERIAL_TOOLBAR_MENU_ITEM_LINE_HEIGHT; + padding: @MATERIAL_TOOLBAR_MENU_VERTICAL_PADDING @MATERIAL_MENU_HORIZONTAL_PADDING; + } + + &.dx-menu-item-has-text.dx-menu-item-has-icon { + .dx-icon { + .dx-icon-margin(6px); + } + } + + &.dx-state-hover { + background-color: @button-normal-hover-bg; + } + + &.dx-state-focused, + &.dx-state-active { + background-color: @button-normal-focused-bg; + } + } + } } .dx-toolbar-menu-action .dx-button.dx-button-has-icon:not(.dx-button-has-text) { diff --git a/testing/functional/model/dataGrid.ts b/testing/functional/model/dataGrid.ts index e13d6d5e3fc8..5f67cabd0476 100644 --- a/testing/functional/model/dataGrid.ts +++ b/testing/functional/model/dataGrid.ts @@ -10,6 +10,7 @@ const CLASS = { dataRow: 'dx-data-row', groupRow: 'dx-group-row', commandEdit: 'dx-command-edit', + commandExpand: 'dx-command-expand', commandLink: 'dx-link', editCell: 'dx-editor-cell', focused: 'dx-focused', @@ -41,7 +42,8 @@ const CLASS = { addRowButton: 'addrow-button', insertedRow: 'dx-row-inserted', editedRow: 'dx-edit-row', - saveButton: 'save-button' + saveButton: 'save-button', + groupExpanded: 'group-opened' }; const addWidgetPrefix = function(widgetName: string, className: string) { @@ -201,11 +203,17 @@ class DataRow extends DxElement { } class GroupRow extends DxElement { + widgetName: string; isFocusedRow: Promise; + isFocused: Promise; + isExpanded: Promise; - constructor(element: Selector) { + constructor(element: Selector, widgetName: string) { super(element); + this.widgetName = widgetName; this.isFocusedRow = this.element.hasClass(CLASS.focusedRow); + this.isFocused = this.element.hasClass(CLASS.focused); + this.isExpanded = this.element.find(`.${CLASS.commandExpand} .${addWidgetPrefix(this.widgetName, CLASS.groupExpanded)}`).exists } getCell(index: number): DataCell { @@ -318,7 +326,7 @@ export default class DataGrid extends Widget { } getGroupRow(index: number): GroupRow { - return new GroupRow(this.element.find(`.${CLASS.groupRow}`).nth(index)); + return new GroupRow(this.element.find(`.${CLASS.groupRow}`).nth(index), this.name); } getFocusedRow(): Selector { diff --git a/testing/functional/tests/dataGrid/keyboardNavigation.ts b/testing/functional/tests/dataGrid/keyboardNavigation.ts index d07ade09b6d7..f5a2a506ad2d 100644 --- a/testing/functional/tests/dataGrid/keyboardNavigation.ts +++ b/testing/functional/tests/dataGrid/keyboardNavigation.ts @@ -6,8 +6,8 @@ import { Selector } from 'testcafe'; fixture `Keyboard Navigation` .page(url(__dirname, '../container.html')); -test("Cell should not highlighted after editing another cell when startEditAction: dblClick and editing.mode: batch", async t => { -const dataGrid = new DataGrid("#container"); +test('Cell should not highlighted after editing another cell when startEditAction: dblClick and editing.mode: batch', async t => { +const dataGrid = new DataGrid('#container'); await t .expect(dataGrid.getDataCell(0, 1).isFocused).notOk() @@ -29,21 +29,21 @@ const dataGrid = new DataGrid("#container"); .expect(dataGrid.getDataCell(0, 1).element.focused).ok() .expect(dataGrid.getDataCell(0, 1).isFocused).notOk() .expect(dataGrid.getDataCell(1, 1).isFocused).notOk(); -}).before(() => createWidget("dxDataGrid", { +}).before(() => createWidget('dxDataGrid', { dataSource: [ - { name: "Alex", phone: "555555", room: 1 }, - { name: "Dan", phone: "553355", room: 2 } + { name: 'Alex', phone: '555555', room: 1 }, + { name: 'Dan', phone: '553355', room: 2 } ], - columns:["name","phone","room"], + columns:['name','phone','room'], editing: { - mode: "batch", + mode: 'batch', allowUpdating: true, - startEditAction: "dblClick" + startEditAction: 'dblClick' } })); -test("Cell should highlighted after editing another cell when startEditAction is 'dblClick' and editing mode is 'cell'", async t => { - const dataGrid = new DataGrid("#container"); +test('Cell should highlighted after editing another cell when startEditAction is "dblClick" and editing mode is "cell"', async t => { + const dataGrid = new DataGrid('#container'); await t .expect(dataGrid.getDataCell(0, 1).isFocused).notOk() @@ -66,39 +66,39 @@ test("Cell should highlighted after editing another cell when startEditAction is .expect(dataGrid.getDataCell(0, 1).element.focused).ok() .expect(dataGrid.getDataCell(0, 1).isFocused).ok() .expect(dataGrid.getDataCell(1, 1).isFocused).notOk(); -}).before(() => createWidget("dxDataGrid", { +}).before(() => createWidget('dxDataGrid', { dataSource: [ - { name: "Alex", phone: "555555", room: 1 }, - { name: "Dan", phone: "553355", room: 2 } + { name: 'Alex', phone: '555555', room: 1 }, + { name: 'Dan', phone: '553355', room: 2 } ], - columns:["name","phone","room"], + columns:['name','phone','room'], editing: { - mode: "cell", + mode: 'cell', allowUpdating: true, - startEditAction: "dblClick" + startEditAction: 'dblClick' }, onFocusedCellChanging: e => e.isHighlighted = true })); -test("Cell should be focused after Enter key press if enterKeyDirection is 'none' and enterKeyAction is 'moveFocus'", async t => { - const dataGrid = new DataGrid("#container"); +test('Cell should be focused after Enter key press if enterKeyDirection is "none" and enterKeyAction is "moveFocus"', async t => { + const dataGrid = new DataGrid('#container'); const dataCell = dataGrid.getDataCell(0, 0).element; const commandCell = dataGrid.getDataCell(0, 1).element; await t .click(dataCell) - .pressKey("esc") + .pressKey('esc') .expect(dataCell.focused).ok() - .pressKey("enter") + .pressKey('enter') .expect(dataCell.focused).ok() - .pressKey("enter") + .pressKey('enter') .expect(dataCell.focused).ok() - .pressKey("tab enter") + .pressKey('tab enter') .expect(commandCell.focused).ok() .expect(dataGrid.getDataRow(0).isRemoved).ok(); -}).before(() => createWidget("dxDataGrid", { +}).before(() => createWidget('dxDataGrid', { height: 200, width: 200, dataSource: [ @@ -106,55 +106,55 @@ test("Cell should be focused after Enter key press if enterKeyDirection is 'none { id: 1 } ], editing: { - mode: "batch", + mode: 'batch', allowUpdating: true, allowDeleting: true }, keyboardNavigation: { - enterKeyAction: "moveFocus", - enterKeyDirection: "none" + enterKeyAction: 'moveFocus', + enterKeyDirection: 'none' } })); -test("TextArea should be focused on editing start", async t => { - const dataGrid = new DataGrid("#container"), +test('TextArea should be focused on editing start', async t => { + const dataGrid = new DataGrid('#container'), commandCell = dataGrid.getDataCell(1, 3).element, dataCell = dataGrid.getDataCell(1, 0), - getTextArea = () => dataCell.element.find(".text-area-1"); + getTextArea = () => dataCell.element.find('.text-area-1'); await t // act, assert - .click(commandCell.find(".dx-link-edit")) + .click(commandCell.find('.dx-link-edit')) .expect(dataCell.isEditCell).ok() .expect(getTextArea().exists).ok() .expect(getTextArea().focused).ok(); -}).before(() => createWidget("dxDataGrid", { +}).before(() => createWidget('dxDataGrid', { dataSource: [ - { id: 0, name: "Alex" }, - { id: 1, name: "Bob" } + { id: 0, name: 'Alex' }, + { id: 1, name: 'Bob' } ], editing: { - mode: "row", + mode: 'row', allowUpdating: true }, columns: [ { - dataField: "name", - editCellTemplate: cell => $(cell).append($(" Link - `)); const view = this.getView('rowsView'); diff --git a/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.keyboardKeys.tests.js b/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.keyboardKeys.tests.js index 0fc6f9d1976d..a59a83acd893 100644 --- a/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.keyboardKeys.tests.js +++ b/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.keyboardKeys.tests.js @@ -53,10 +53,7 @@ QUnit.module('Keyboard keys', { this.clock = sinon.useFakeTimers(); }, afterEach: function() { - if(this.dispose) { - this.dispose(); - } - + this.dispose(); this.clock.restore(); } }, function() { @@ -1585,14 +1582,14 @@ QUnit.module('Keyboard keys', { assert.ok(isLeftArrow, 'default behaviour is worked'); }); - QUnit.testInActiveWindow('onKeyDown should fire event if grid is empty (T837977)', function(assert) { + QUnit.testInActiveWindow('onKeyDown should fire if grid is empty (T837977)', function(assert) { // arrange let keyDownFiresCount = 0; this.options = { dataSource: [], onKeyDown: () => ++keyDownFiresCount, - tabindex: 111 + tabIndex: 111 }; setupModules(this, { initViews: true }); @@ -2212,6 +2209,51 @@ QUnit.module('Keyboard keys', { }); }); + QUnit.testInActiveWindow('Focus should be moved outside grid after tabbing throw all group rows (T870114)', function(assert) { + // arrange + this.columns = [ + { visible: true, command: 'expand', cssClass: 'dx-command-expand' }, + { caption: 'Column 1', visible: true, dataField: 'Column1' }, + { caption: 'Column 2', visible: true, dataField: 'Column2' } + ]; + + this.dataControllerOptions = { + pageCount: 10, + pageIndex: 0, + pageSize: 10, + items: [ + { values: ['group 1'], rowType: 'group', key: ['group 1'], groupIndex: 0 }, + { values: ['group 2'], rowType: 'group', key: ['group 2'], groupIndex: 0 } + ] + }; + + setupModules(this); + + // act + this.gridView.render($('#container')); + + const $groupRows = $('#container').find('.dx-group-row'); + + $groupRows.eq(0).focus(); + this.clock.tick(); + + // act + this.triggerKeyDown('tab', false, false, $groupRows.eq(0)); + + // assert + assert.equal($(':focus').get(0), $groupRows.get(1), 'second group row is focused'); + assert.deepEqual(this.keyboardNavigationController._focusedCellPosition, { + rowIndex: 1, + columnIndex: 1 + }); + + // act + const isPreventDefaultCalled = this.triggerKeyDown('tab', false, false, $groupRows.eq(1)).preventDefault; + + // assert + assert.ok(!isPreventDefaultCalled, 'preventDefault is not called'); + }); + QUnit.testInActiveWindow('DataGrid should skip group rows after tab navigation from the editing cell (T714142, T715092)', function(assert) { // arrange this.columns = [ @@ -3563,7 +3605,7 @@ QUnit.module('Keyboard keys', { // act this.gridView.render($('#container')); - this.focusFirstCell(); + $(this.getCellElement(0, 0)).trigger(CLICK_EVENT); this.triggerKeyDown('rightArrow'); @@ -3949,4 +3991,90 @@ QUnit.module('Keyboard keys', { assert.strictEqual(this.pageIndex(), 2, 'pageIndex'); assert.deepEqual(this.keyboardNavigationController._focusedCellPosition, { columnIndex: 0, rowIndex: 4 }, 'focused position'); }); + + QUnit.testInActiveWindow('Focused cell position should be updated when the Enter key is pressed on the first focused group row (T869799)', function(assert) { + // arrange + this.columns = [ + { visible: true, command: 'expand', cssClass: 'dx-command-expand' }, + { caption: 'Column 1', visible: true, dataField: 'Column1' }, + { caption: 'Column 2', visible: true, dataField: 'Column2' } + ]; + + this.dataControllerOptions = { + pageCount: 10, + pageIndex: 0, + pageSize: 10, + items: [ + { values: ['group 1'], rowType: 'group', key: ['group 1'], groupIndex: 0 } + ] + }; + + setupModules(this); + + // act + this.gridView.render($('#container')); + + const $firstGroupRow = $(this.getRowElement(0)); + $firstGroupRow.focus(); + this.clock.tick(); + + // assert + assert.ok($firstGroupRow.hasClass('dx-focused'), 'the first group row is marked as focused'); + assert.equal($(':focus').get(0), $firstGroupRow.get(0), 'the first group row is focused'); + assert.deepEqual(this.keyboardNavigationController._focusedCellPosition, { }); + + // act + this.triggerKeyDown('enter', false, false, $firstGroupRow.get(0)); + + // assert + assert.deepEqual(this.keyboardNavigationController._focusedCellPosition, { + rowIndex: 0, + columnIndex: 1 + }); + }); + + QUnit.testInActiveWindow('The second group row should be focused when the arrow down key is pressed on the first group row (T869799)', function(assert) { + // arrange + this.columns = [ + { visible: true, command: 'expand', cssClass: 'dx-command-expand' }, + { caption: 'Column 1', visible: true, dataField: 'Column1' }, + { caption: 'Column 2', visible: true, dataField: 'Column2' } + ]; + + this.dataControllerOptions = { + pageCount: 10, + pageIndex: 0, + pageSize: 10, + items: [ + { values: ['group 1'], rowType: 'group', key: ['group 1'], groupIndex: 0 }, + { values: ['group 2'], rowType: 'group', key: ['group 2'], groupIndex: 0 } + ] + }; + + setupModules(this); + + // act + this.gridView.render($('#container')); + + const $firstGroupRow = $(this.getRowElement(0)); + $firstGroupRow.focus(); + this.clock.tick(); + + // assert + assert.ok($firstGroupRow.hasClass('dx-focused'), 'the first group row is marked as focused'); + assert.equal($(':focus').get(0), $firstGroupRow.get(0), 'the first group row is focused'); + + // act + this.triggerKeyDown('downArrow', false, false, $firstGroupRow.get(0)); + this.clock.tick(); + const $secondGroupRow = $(this.getRowElement(1)); + + // assert + assert.ok($secondGroupRow.hasClass('dx-focused'), 'the second group row is marked as focused'); + assert.equal($(':focus').get(0), $secondGroupRow.get(0), 'the second group row is focused'); + assert.deepEqual(this.keyboardNavigationController._focusedCellPosition, { + rowIndex: 1, + columnIndex: 1 + }); + }); }); diff --git a/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.realControllers.tests.js b/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.realControllers.tests.js index fae7ba525e5f..1c6aae87c77f 100644 --- a/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.realControllers.tests.js +++ b/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.realControllers.tests.js @@ -1,5 +1,6 @@ QUnit.testStart(function() { const markup = ` +
`; @@ -56,7 +57,8 @@ QUnit.module('Real DataController and ColumnsController', { setupDataGridModules(this, [ 'data', 'columns', 'columnHeaders', 'rows', 'editorFactory', 'gridView', 'editing', 'focus', - 'keyboardNavigation', 'validating', 'masterDetail', 'selection' + 'keyboardNavigation', 'validating', 'masterDetail', 'selection', + 'grouping' ], { initViews: true }); @@ -70,6 +72,7 @@ QUnit.module('Real DataController and ColumnsController', { this.clock = sinon.useFakeTimers(); }, afterEach: function() { + this.dispose(); this.clock.restore(); } }, function() { @@ -344,7 +347,7 @@ QUnit.module('Real DataController and ColumnsController', { }); // T692137 - QUnit.testInActiveWindow('Focus should not be lost after several clicks on the same cell', function(assert) { + QUnit.testInActiveWindow('Cell should not lost focus after several clicks on the same cell', function(assert) { // arrange this.$element = function() { return $('#container'); @@ -806,7 +809,7 @@ QUnit.module('Real DataController and ColumnsController', { assert.ok(!this.gridView.component.editorFactoryController.focus(), 'no focus overlay'); }); - QUnit.testInActiveWindow('Focus must be saved after paging', function(assert) { + QUnit.testInActiveWindow('Focus should be preserved after paging', function(assert) { // arrange const that = this; that.$element = function() { @@ -845,7 +848,7 @@ QUnit.module('Real DataController and ColumnsController', { assert.equal(this.keyboardNavigationController._focusedCellPosition.rowIndex, 1, 'rowIndex'); }); - QUnit.testInActiveWindow('freespace cells should not have a focus', function(assert) { + QUnit.testInActiveWindow('freespace cells should not have focus', function(assert) { // arrange const that = this; that.$element = function() { @@ -975,7 +978,7 @@ QUnit.module('Real DataController and ColumnsController', { assert.ok(this.keyboardNavigationController._focusedCellPosition, 'focusedCellPosition'); }); - QUnit.testInActiveWindow('Focus must be saved after paging if last row cell selected and rowCount of the last page < then of the previus page', function(assert) { + QUnit.testInActiveWindow('Focus should be preserved after paging if the last row cell selected and rowCount of the last page < then of the previus page', function(assert) { // arrange let $cell; @@ -1249,6 +1252,143 @@ QUnit.module('Real DataController and ColumnsController', { assert.ok(that.editingController.isEditCell(1, 0), 'the first cell of the second row is editable'); }); + QUnit.test('Previous navigation elements should not have "tabindex" if grouping and navigation action is click (T870120)', function(assert) { + // arrange + const $container = $('#container'); + this.data = [ + { name: 'Alex', phone: 'John' }, + { name: 'Dan', phone: 'Skip' } + ]; + this.options = { + grouping: { + autoExpandAll: true + }, + columns: [ + 'name', + { + dataField: 'phone', + groupIndex: 0 + } + ], + tabIndex: 111 + }; + + this.setupModule(); + this.gridView.render($container); + + this.clock.tick(); + + const $rowsView = this.rowsView.element(); + + // act + $(this.getCellElement(0, 1)).trigger(CLICK_EVENT); + + // act + $(this.getCellElement(1, 1)).trigger(CLICK_EVENT); + // assert + assert.equal($rowsView.find('[tabindex]').length, 1, 'Only one element with tabindex'); + assert.equal($(this.getCellElement(1, 1)).attr('tabindex'), 111, 'Cell[1, 1] has tabindex'); + + // act + $(this.getCellElement(2, 1)).trigger(CLICK_EVENT); + + // act + $(this.getCellElement(3, 1)).trigger(CLICK_EVENT); + // assert + assert.equal($rowsView.find('[tabindex]').length, 1, 'Only one element with tabindex'); + assert.equal($(this.getCellElement(3, 1)).attr('tabindex'), 111, 'Cell[3, 1] has tabindex'); + }); + + QUnit.test('Previous navigation elements should not have "tabindex" if grouping and navigation action is tab (T870120)', function(assert) { + // arrange + const $container = $('#container'); + this.data = [ + { name: 'Alex', phone: 'John' }, + { name: 'Dan', phone: 'Skip' } + ]; + this.options = { + grouping: { + autoExpandAll: false + }, + editing: {}, + columns: [ + 'name', + { + dataField: 'phone', + groupIndex: 0 + } + ], + tabIndex: 111 + }; + + this.setupModule(); + this.gridView.render($container); + + this.clock.tick(); + + const $rowsView = this.rowsView.element(); + $(this.getCellElement(0, 1)).trigger(CLICK_EVENT); + + // act + this.triggerKeyDown('tab', false, false, $(this.getRowElement(0))); + + // assert + assert.equal($rowsView.find('[tabindex]').length, 1, 'Only one element with tabindex'); + assert.equal($(this.getRowElement(1)).attr('tabindex'), 111, 'Row 1 has tabindex'); + }); + + QUnit.test('Previous navigation elements should not have "tabindex" if grouping, focusedRowEnabled and navigation action is click (T870120)', function(assert) { + // arrange + const $container = $('#container'); + this.data = [ + { name: 'Alex', phone: 'John' }, + { name: 'Dan', phone: 'Skip' } + ]; + this.options = { + focusedRowEnabled: true, + grouping: { + autoExpandAll: true + }, + columns: [ + 'name', + { + dataField: 'phone', + groupIndex: 0 + } + ], + tabIndex: 111 + }; + + this.setupModule(); + this.gridView.render($container); + + this.clock.tick(); + + const $rowsView = this.rowsView.element(); + + // act + $(this.getCellElement(0, 1)).trigger(CLICK_EVENT); + // assert + assert.equal($rowsView.find('[tabindex]').length, 1, 'Only one element with tabindex'); + assert.equal($(this.getRowElement(0)).attr('tabindex'), 111, 'Row[0] has tabindex'); + + // act + $(this.getCellElement(1, 1)).trigger(CLICK_EVENT); + // assert + assert.equal($rowsView.find('[tabindex]').length, 1, 'Only one element with tabindex'); + assert.equal($(this.getRowElement(1)).attr('tabindex'), 111, 'Row[1] has tabindex'); + + // act + $(this.getCellElement(2, 1)).trigger(CLICK_EVENT); + // assert + + // act + $(this.getCellElement(3, 1)).trigger(CLICK_EVENT); + // assert + assert.equal($rowsView.find('[tabindex]').length, 1, 'Only one element with tabindex'); + assert.equal($(this.getRowElement(3)).attr('tabindex'), 111, 'Row[3] has tabindex'); + }); + ['click', 'dblClick'].forEach(startEditAction => { ['cell', 'batch'].forEach(editMode => { QUnit.test(`Focus overlay should not be hidden after click the save editor cell if editing.mode: ${editMode}, editing.startEditAction is ${startEditAction}`, function(assert) { diff --git a/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.rowsView.tests.js b/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.rowsView.tests.js index 2f1fb0e2ff68..25039ec6eb40 100644 --- a/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.rowsView.tests.js +++ b/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.rowsView.tests.js @@ -36,7 +36,6 @@ QUnit.module('Rows view', { this.createRowsView = function(rows, dataController, columns, initDefaultOptions) { let i; - let columnsController; dataController = dataController || new MockDataController({ items: rows }); @@ -46,7 +45,7 @@ QUnit.module('Rows view', { columns.push({}); } } - columnsController = new MockColumnsController(columns); + const columnsController = new MockColumnsController(columns); this.options = { disabled: false, @@ -83,6 +82,9 @@ QUnit.module('Rows view', { this.clock = sinon.useFakeTimers(); }, afterEach: function() { + if(this.dispose) { + this.dispose(); + } this.clock.restore(); } }, function() { @@ -90,14 +92,13 @@ QUnit.module('Rows view', { QUnit.testInActiveWindow('Focused cell from free space row when view is rendered', function(assert) { // arrange const $container = $('#container'); - let origUpdateFocus; setupModules(this); this.gridView.render($container); this.keyboardNavigationController._focusedView = this.rowsView; this.keyboardNavigationController._isNeedFocus = true; - origUpdateFocus = this.keyboardNavigationController._updateFocus; + const origUpdateFocus = this.keyboardNavigationController._updateFocus; this.keyboardNavigationController._updateFocus = function() { origUpdateFocus.apply(this, arguments); diff --git a/testing/tests/DevExpress.ui.widgets.editors/autocomplete.tests.js b/testing/tests/DevExpress.ui.widgets.editors/autocomplete.tests.js index c67870da0348..b1aa04681e1b 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/autocomplete.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/autocomplete.tests.js @@ -353,6 +353,9 @@ QUnit.module('dxAutocomplete', { } return deferred; + }, + byKey(key) { + return key; } } }); @@ -833,6 +836,9 @@ QUnit.module('dxAutocomplete', { load(loadOptions) { searchedString = loadOptions.searchValue; return ['item 1', 'item 2', 'item 3']; + }, + byKey(key) { + return key; } }, filterOperator: 'startswith' @@ -1312,6 +1318,37 @@ QUnit.module('regressions', { assert.equal(called, 6); }); + QUnit.test('onSelectionChanged event should trigger on item selection', function(assert) { + const valueChangedStub = sinon.stub(); + const selectionChangedStub = sinon.stub(); + + this.instance.option({ + onValueChanged: valueChangedStub, + onSelectionChanged: selectionChangedStub + }); + + this.keyboard + .type('2') + .press('arrowDown') + .press('enter'); + + assert.strictEqual(valueChangedStub.callCount, 2); + assert.strictEqual(valueChangedStub.firstCall.args[0].value, '2'); + assert.strictEqual(valueChangedStub.secondCall.args[0].value, 'item 2'); + assert.strictEqual(selectionChangedStub.callCount, 1); + assert.strictEqual(selectionChangedStub.lastCall.args[0].selectedItem, 'item 2'); + + + this.keyboard + .caret(6) + .press('backspace'); + + assert.strictEqual(valueChangedStub.callCount, 3); + assert.strictEqual(valueChangedStub.lastCall.args[0].value, 'item '); + assert.strictEqual(selectionChangedStub.callCount, 2); + assert.strictEqual(selectionChangedStub.lastCall.args[0].selectedItem, null); + }); + QUnit.test('clear button should save valueChangeEvent', function(assert) { const valueChangedHandler = sinon.spy(); diff --git a/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js b/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js index b69f1099a973..efcd713060ea 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js @@ -1391,6 +1391,141 @@ QUnit.module('Options', { this.calendar.option('value', new Date(2002, 2, 2)); }); + QUnit.test('firstDayOfWeek option', function(assert) { + const getFirstWeekDayCell = () => { + return getCurrentViewInstance(this.calendar).$element().find('th').get(0); + }; + + let $firstWeekDayCell = getFirstWeekDayCell(); + assert.strictEqual($firstWeekDayCell.abbr, 'Sunday', 'first day of week is correct'); + + this.calendar.option('firstDayOfWeek', 1); + + $firstWeekDayCell = getFirstWeekDayCell(); + assert.strictEqual($firstWeekDayCell.abbr, 'Monday', 'first day of week is correct after runtime option change'); + }); + + QUnit.test('dateSerializationFormat option', function(assert) { + this.calendar.option({ + dateSerializationFormat: 'yyyy-MM-dd', + currentDate: new Date(2020, 0, 0) + }); + + const $cell = this.$element.find(toSelector(CALENDAR_CELL_CLASS)).eq(4); + $($cell).trigger('dxclick'); + + const selectedFormattedValue = '2019-11-28'; + const value = this.calendar.option('value'); + assert.strictEqual(value, selectedFormattedValue, 'value format is correct after dateSerializationFormat option runtime change'); + }); + + QUnit.test('cellTemplate option', function(assert) { + this.calendar.option({ + cellTemplate: function() { + return 'Custom template'; + }, + currentDate: new Date(2020, 0, 0) + }); + + const $cell = this.$element.find(toSelector(CALENDAR_CELL_CLASS)).eq(4); + const cellContent = $cell.text(); + + assert.strictEqual(cellContent, 'Custom template', 'cell content is correct after cellTemplate runtime change'); + }); + + QUnit.test('showTodayButton option', function(assert) { + const getTodayButton = () => this.$element.find(toSelector(CALENDAR_TODAY_BUTTON_CLASS)).get(0); + + this.calendar.option('showTodayButton', true); + + let $todayButton = getTodayButton(); + assert.strictEqual($todayButton.text, 'Today', 'todayButton is rendered after showTodayButton runtime change to true'); + + this.calendar.option('showTodayButton', false); + $todayButton = getTodayButton(); + assert.strictEqual($todayButton, undefined, 'todayButton is not rendered after showTodayButton runtime change to false'); + }); + + QUnit.test('onCellClick option runtime change', function(assert) { + const getCellElement = () => this.$element.find(toSelector(CALENDAR_CELL_CLASS)).eq(4); + + const firstClickHandler = sinon.spy(); + const secondClickHandler = sinon.spy(); + + this.calendar.option({ + currentDate: new Date(2010, 10, 10), + focusStateEnabled: true, + onCellClick: firstClickHandler + }); + + $(getCellElement()).trigger('dxclick'); + assert.ok(firstClickHandler.calledOnce, 'firstClickHandler is called once'); + + this.calendar.option('onCellClick', secondClickHandler); + + $(getCellElement()).trigger('dxclick'); + assert.ok(secondClickHandler.calledOnce, 'secondClickHandler is called once after onCellClick runtime option change'); + }); + + QUnit.test('onCellClick option - subscription by "on" method', function(assert) { + const getCellElement = () => this.$element.find(toSelector(CALENDAR_CELL_CLASS)).eq(4); + + const clickHandler = sinon.spy(); + + this.calendar.option({ + currentDate: new Date(2010, 10, 10), + focusStateEnabled: true + }); + this.calendar.on('cellClick', clickHandler); + + $(getCellElement()).trigger('dxclick'); + assert.ok(clickHandler.calledOnce, 'cellClick is called'); + + this.calendar.off('cellClick', clickHandler); + + $(getCellElement()).trigger('dxclick'); + assert.ok(clickHandler.calledOnce, 'cellClick is not called second time'); + }); + + QUnit.test('onContouredChanged option runtime change', function(assert) { + const firstHandler = sinon.spy(); + const secondHandler = sinon.spy(); + + this.reinit({ + value: null, + onContouredChanged: firstHandler, + focusStateEnabled: true + }); + + assert.ok(firstHandler.calledOnce, 'first handler has been called'); + + this.calendar.option('onContouredChanged', secondHandler); + this.$element.trigger('focusin'); + triggerKeydown(this.$element, UP_ARROW_KEY_CODE, true); + + assert.ok(secondHandler.calledOnce, 'second handler has been called'); + }); + + QUnit.test('onContouredChanged option - subscription by "on" method', function(assert) { + const goNextView = () => { + $(this.$element.find(toSelector(CALENDAR_NAVIGATOR_NEXT_VIEW_CLASS))).trigger('dxclick'); + }; + + const handler = sinon.spy(); + this.reinit({ + value: null, + focusStateEnabled: true + }); + + this.calendar.on('contouredChanged', handler); + goNextView(); + assert.ok(handler.calledOnce, 'handler is called'); + + this.calendar.off('contouredChanged', handler); + goNextView(); + assert.ok(handler.calledOnce, 'handler is not called second time'); + }); + QUnit.test('onCellClick return not \'undefined\' after click on cell', function(assert) { const clickHandler = sinon.spy(noop); diff --git a/testing/tests/DevExpress.ui.widgets.editors/colorBox.tests.js b/testing/tests/DevExpress.ui.widgets.editors/colorBox.tests.js index a77cd72ad432..f4856d6d6615 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/colorBox.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/colorBox.tests.js @@ -166,14 +166,15 @@ QUnit.module('Color Box', { QUnit.test('Click on apply button', function(assert) { const onValueChangedHandler = sinon.spy(noop); - const onApplyButtonClickHandler = sinon.spy(noop); showColorBox.call(this, { - onValueChanged: onValueChangedHandler, - onApplyButtonClick: onApplyButtonClickHandler + onValueChanged: onValueChangedHandler }); - const $overlayContent = getColorBoxOverlayContent(); const $applyButton = $overlayContent.find('.dx-colorview-buttons-container .dx-colorview-apply-button'); const colorBoxInstance = this.element.dxColorBox('instance'); const newColor = '#A600F3'.toLowerCase(); + const $overlayContent = getColorBoxOverlayContent(); + const $applyButton = $overlayContent.find('.dx-colorview-buttons-container .dx-colorview-apply-button'); + const colorBoxInstance = this.element.dxColorBox('instance'); + const newColor = '#A600F3'.toLowerCase(); colorBoxInstance._colorView.option('value', newColor); @@ -183,20 +184,17 @@ QUnit.module('Color Box', { assert.equal($('.' + COLOR_BOX_INPUT_CLASS).val(), newColor); assert.ok(onValueChangedHandler.calledOnce); assert.ok($('.' + COLOR_BOX_OVERLAY_CLASS).is(':hidden')); - assert.ok(onApplyButtonClickHandler.calledOnce); }); QUnit.test('Click on cancel button', function(assert) { - const spy = sinon.spy(noop); - showColorBox.call(this, { - value: '#ff0000', - onCancelButtonClick: spy + value: '#ff0000' }); this.updateColorInput('hex', 'f0f0f0'); - const $overlayContent = getColorBoxOverlayContent(); const $cancelButton = $overlayContent.find('.dx-colorview-buttons-container .dx-colorview-cancel-button'); + const $overlayContent = getColorBoxOverlayContent(); + const $cancelButton = $overlayContent.find('.dx-colorview-buttons-container .dx-colorview-cancel-button'); $($cancelButton).trigger('dxclick'); assert.ok($('.' + COLOR_BOX_OVERLAY_CLASS).is(':hidden')); @@ -207,8 +205,6 @@ QUnit.module('Color Box', { b: 0, hex: '#ff0000' }, assert); - - assert.ok(spy.calledOnce); }); QUnit.test('Cancel event should work right when color was changed', function(assert) { @@ -424,14 +420,10 @@ QUnit.module('Color Box', { QUnit.test('Option changes', function(assert) { const colorBox = showColorBox.call(this).dxColorBox('instance'); - const onCancelButtonClick = noop; - const onApplyButtonClick = noop; $.each([ { name: 'value', value: '#ff0000' }, { name: 'editAlphaChannel', value: true }, - { name: 'onCancelButtonClick', value: onCancelButtonClick }, - { name: 'onApplyButtonClick', value: onApplyButtonClick }, { name: 'rtlEnabled', value: true }, { name: 'keyStep', value: 10 } ], function(_, option) { diff --git a/testing/tests/DevExpress.ui.widgets.editors/datebox.tests.js b/testing/tests/DevExpress.ui.widgets.editors/datebox.tests.js index 8fbd182ff320..d9396305f3ba 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/datebox.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/datebox.tests.js @@ -1024,8 +1024,7 @@ QUnit.module('merging dates', moduleConfig, () => { .val('1:1:16 AM') .trigger('change'); - const date = new Date(2000, 6, 31, 1, 1, 16); - assert.deepEqual(this.instance.option('value'), date); + assert.strictEqual(this.instance.option('value').getSeconds(), 16); }); QUnit.test('mergeDates must merge milliseconds when type is \'time\'', function(assert) { @@ -1040,9 +1039,7 @@ QUnit.module('merging dates', moduleConfig, () => { .val('16') .trigger('change'); - const now = new Date(); - const date = new Date(2000, 6, 31, now.getHours(), now.getMinutes(), now.getSeconds(), 16); - assert.deepEqual(this.instance.option('value'), date); + assert.strictEqual(this.instance.option('value').getMilliseconds(), 16); }); }); @@ -4609,6 +4606,53 @@ QUnit.module('datebox validation', {}, () => { assert.ok(dateBox.option('isValid')); }); + + QUnit.test('datebox should pass Date value to the validationCallback by default', function(assert) { + const validationCallback = sinon.stub().returns(true); + const $dateBox = $('#dateBox').dxDateBox({ + type: 'date', + value: null, + pickerType: 'calendar' + }).dxValidator({ + validationRules: [{ + type: 'custom', + validationCallback + }] + }); + const keyboard = keyboardMock($dateBox.find(`.${TEXTEDITOR_INPUT_CLASS}`)); + + keyboard + .type('10/10/2020') + .change(); + + const { value } = validationCallback.lastCall.args[0]; + assert.ok(validationCallback.calledOnce, 'validationCallback called once'); + assert.ok(typeUtils.isDate(value), 'value type is Date'); + }); + + QUnit.test('datebox should pass string value to the validationCallback when "dateSerializationFormat" defined', function(assert) { + const validationCallback = sinon.stub().returns(true); + const $dateBox = $('#dateBox').dxDateBox({ + type: 'date', + value: null, + pickerType: 'calendar', + dateSerializationFormat: 'yyyy-MM-dd' + }).dxValidator({ + validationRules: [{ + type: 'custom', + validationCallback + }] + }); + const keyboard = keyboardMock($dateBox.find(`.${TEXTEDITOR_INPUT_CLASS}`)); + + keyboard + .type('10/10/2020') + .change(); + + const { value } = validationCallback.lastCall.args[0]; + assert.ok(validationCallback.calledOnce, 'validationCallback called once'); + assert.strictEqual(value, '2020-10-10', 'String value passed'); + }); }); QUnit.module('DateBox number and string value support', { diff --git a/testing/tests/DevExpress.ui.widgets.editors/dropDownBox.tests.js b/testing/tests/DevExpress.ui.widgets.editors/dropDownBox.tests.js index 9c690fb8ec57..bbf1f776fac1 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/dropDownBox.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/dropDownBox.tests.js @@ -487,6 +487,116 @@ QUnit.module('popup options', moduleConfig, () => { } }); + QUnit.test('maxHeight should be 90% of bottom offset if popup has been rendered at the bottom already (T874949)', function(assert) { + this.$element.dxDropDownBox({ + contentTemplate: () => { + const content = $('
'); + + setTimeout(() => { + $('#dd-content').height(300); + }); + + return content; + } + }); + + const scrollTop = sinon.stub(renderer.fn, 'scrollTop').returns(0); + const windowHeight = $(window).height(); + const offset = sinon.stub(renderer.fn, 'offset').returns({ left: 0, top: windowHeight - 200 }); + const instance = this.$element.dxDropDownBox('instance'); + + try { + instance.open(); + + this.clock.tick(); + const popup = $('.dx-popup').dxPopup('instance'); + const maxHeight = popup.option('maxHeight'); + + assert.roughEqual(Math.floor(maxHeight()), (200 - this.$element.height()) * 0.9, 3, 'maxHeight is correct'); + } finally { + scrollTop.restore(); + offset.restore(); + } + }); + + QUnit.test('maxHeight should be 90% to bottom bound if popup has been rendered at the top already (T874949)', function(assert) { + + let startPopupHeight; + this.$element.dxDropDownBox({ + contentTemplate: (e) => { + const content = $('
'); + + setTimeout(() => { + startPopupHeight = $(e.component.content()).parent('.dx-overlay-content').height(); + $('#dd-content').height(300); + }); + + return content; + } + }); + + const elementHeight = this.$element.height(); + const scrollTop = sinon.stub(renderer.fn, 'scrollTop').returns(0); + const windowHeight = $(window).height(); + this.$element.css('margin-top', windowHeight - elementHeight - 1); + const instance = this.$element.dxDropDownBox('instance'); + + try { + instance.open(); + + this.clock.tick(); + const popup = $('.dx-popup').dxPopup('instance'); + const maxHeight = popup.option('maxHeight'); + + assert.roughEqual(Math.floor(maxHeight()), (1 + startPopupHeight + elementHeight) * 0.9, 3, 'maxHeight is correct'); + } finally { + scrollTop.restore(); + this.$element.css('margin-top', 0); + } + }); + + QUnit.test('maxHeight should be recalculated if popup has been reopened after content change (T874949)', function(assert) { + const contentHeight = 90; + + const windowHeight = $(window).height(); + const marginTop = Math.max(windowHeight - 50, 200); + this.$element.dxDropDownBox({ + contentTemplate: (e) => { + const content = $('
'); + + setTimeout(() => { + $('#dd-content').height(contentHeight); + }); + + return content; + } + }); + + const scrollTop = sinon.stub(renderer.fn, 'scrollTop').returns(0); + + this.$element.css('margin-top', marginTop); + const instance = this.$element.dxDropDownBox('instance'); + + try { + instance.open(); + + this.clock.tick(); + const popup = $('.dx-popup').dxPopup('instance'); + const maxHeight = popup.option('maxHeight'); + + instance.close(); + instance.open(); + this.clock.tick(); + const overlayContentHeight = $(popup.content()).outerHeight(); + assert.roughEqual(Math.floor(maxHeight()), (windowHeight - (marginTop - overlayContentHeight)) * 0.9, 3, 'maxHeight is correct'); + + } finally { + this.$element.css('margin-top', 0); + $('#container').css('min-height', 0); + scrollTop.restore(); + } + }); + QUnit.test('Dropdownbox popup should change height according to the content', function(assert) { if(isIE11) { assert.expect(0); diff --git a/testing/tests/DevExpress.ui.widgets.editors/dropDownList.tests.js b/testing/tests/DevExpress.ui.widgets.editors/dropDownList.tests.js index 9713dabcb9de..2c8a7ce3fb04 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/dropDownList.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/dropDownList.tests.js @@ -1018,7 +1018,7 @@ QUnit.module('selectedItem', moduleConfig, () => { assert.strictEqual(dropDownList.option('selectedItem'), null, 'Value should be reset'); }); - QUnit.test('onSelectionChanged action should not be fired when selectedItem was not changed', function(assert) { + QUnit.test('onSelectionChanged action should not be fired after dataSource has been updated and selectedItem was not changed', function(assert) { const selectionChangedHandler = sinon.spy(); const dropDownList = $('#dropDownList').dxDropDownList({ dataSource: [], @@ -1033,6 +1033,23 @@ QUnit.module('selectedItem', moduleConfig, () => { assert.strictEqual(selectionChangedHandler.callCount, 0, 'selectionChanged action was not fired'); }); + + QUnit.test('selectionChanged should not fire if selectedItem was not changed', function(assert) { + const selectionChangedHandler = sinon.stub(); + const items = [{ name: 'item1' }, { name: 'item2' }]; + const instance = $('#dropDownList').dxDropDownList({ + dataSource: items, + value: items[0], + onSelectionChanged: selectionChangedHandler, + displayExpr: 'name' + }).dxDropDownList('instance'); + + assert.strictEqual(instance.option('selectedItem'), items[0], 'selectedItem is correct on init'); + + instance.option('selectedItem', items[0]); + assert.strictEqual(instance.option('selectedItem'), items[0], 'selectedItem was not changed'); + assert.equal(selectionChangedHandler.callCount, 1, 'selectionChanged should not fire twice'); + }); }); QUnit.module('popup', moduleConfig, () => { @@ -1310,6 +1327,37 @@ QUnit.module('dataSource integration', moduleConfig, function() { this.clock.tick(loadDelay / 2); assert.ok($loadPanel.is(':hidden'), 'load panel is not visible when loading has been finished'); }); + + QUnit.test('dataSource should not be reloaded while minSearchLength is not exceeded (T876423)', function(assert) { + const loadStub = sinon.stub().returns([{ name: 'test 1' }, { name: 'test 2' }, { name: 'test 3' }]); + + const $dropDownList = $('#dropDownList').dxDropDownList({ + dataSource: { + load: loadStub, + byKey: (id) => { return { name: id }; } + }, + searchEnabled: true, + deferRendering: false, + showDataBeforeSearch: true, + valueExpr: 'name', + displayExpr: 'name', + searchExpr: 'name', + searchTimeout: 0, + minSearchLength: 3 + }); + + const $input = $dropDownList.find('.' + TEXTEDITOR_INPUT_CLASS); + const kb = keyboardMock($input); + + kb.type('123'); + + assert.strictEqual(loadStub.callCount, 2); + + kb.press('backspace') + .press('backspace'); + + assert.strictEqual(loadStub.callCount, 3); + }); }); QUnit.module('action options', moduleConfig, () => { diff --git a/testing/tests/DevExpress.ui.widgets.editors/htmlEditorParts/resizingModule.tests.js b/testing/tests/DevExpress.ui.widgets.editors/htmlEditorParts/resizingModule.tests.js index 5653d745c559..678fcf50c0bc 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/htmlEditorParts/resizingModule.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/htmlEditorParts/resizingModule.tests.js @@ -212,6 +212,26 @@ module('Resizing module', moduleConfig, () => { assert.notOk($resizeFrame.is(':visible'), 'Frame element isn\'t visible'); }); + ['Backspace', 'Delete'].forEach((deleteKey) => { + test(`${deleteKey} keydown event should remove target image hide resize frame (T878203)`, function(assert) { + this.$element.prepend($(document.createTextNode('text'))); + $(this.$element).dxHtmlEditor({ + mediaResizing: { + enabled: true + } + }); + + this.options.enabled = true; + const $image = $(this.$element).find('img'); + $image.trigger(clickEvent); + + $image.trigger($.Event('keydown', { key: deleteKey })); + + assert.strictEqual($(this.$element).find('img').length, 0, 'Image is removed'); + }); + }); + + test('scroll event should update resize frame position', function(assert) { this.options.enabled = true; const resizingInstance = new Resizing(this.quillMock, this.options); diff --git a/testing/tests/DevExpress.ui.widgets.editors/lookup.tests.js b/testing/tests/DevExpress.ui.widgets.editors/lookup.tests.js index 8e6b8232bade..03ccc1644bd5 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/lookup.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/lookup.tests.js @@ -3399,3 +3399,82 @@ QUnit.module('default options', { }); }); +QUnit.module('Events', { + before: function() { + this.items = [...new Array(50)].map((item, index) => index + 1); + }, + beforeEach: function() { + fx.off = true; + this.clock = sinon.useFakeTimers(); + + this.element = $('#lookup'); + this.options = { + items: this.items + }; + + this.createLookup = function() { + this.instance = this.element.dxLookup(this.options).dxLookup('instance'); + }; + this.togglePopup = function() { + this.instance.open(); + + this.$list = $('.dx-list'); + this.list = this.$list.dxList('instance'); + }; + this.triggerScrollEvent = function() { + this.$list.find('.dx-scrollable-container').trigger('scroll'); + }; + }, + afterEach: function() { + this.clock.restore(); + fx.off = false; + } +}, function() { + QUnit.test('onScroll, handler attached via option', function(assert) { + const scrollStub = sinon.stub(); + this.options.onScroll = scrollStub; + this.createLookup(); + this.togglePopup(); + + this.triggerScrollEvent(); + + assert.ok(scrollStub.calledOnce, 'onScroll event handled'); + }); + + QUnit.test('onScroll, handler attached via "on" method', function(assert) { + const scrollStub = sinon.stub(); + this.createLookup(); + this.instance.on('scroll', scrollStub); + this.togglePopup(); + + this.triggerScrollEvent(); + + assert.ok(scrollStub.calledOnce, 'onScroll event handled'); + }); + + QUnit.test('detach "onScroll" event handler', function(assert) { + const scrollStub = sinon.stub(); + this.createLookup(); + this.instance.on('scroll', scrollStub); + this.togglePopup(); + + this.instance.off('scroll', scrollStub); + this.triggerScrollEvent(); + + assert.ok(scrollStub.notCalled, 'onScroll event handler detached'); + }); + + QUnit.test('change "onScroll" handler runtime', function(assert) { + const initialScrollStub = sinon.stub(); + const newScrollStub = sinon.stub(); + this.options.onScroll = initialScrollStub; + this.createLookup(); + this.togglePopup(); + + this.instance.option('onScroll', newScrollStub); + this.triggerScrollEvent(); + + assert.ok(initialScrollStub.notCalled, 'initial handled does not invoked'); + assert.ok(newScrollStub.calledOnce, 'onScroll event handled'); + }); +}); diff --git a/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/common.tests.js b/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/common.tests.js index 38783a636de4..589fb557d3b7 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/common.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/common.tests.js @@ -785,6 +785,21 @@ QUnit.module('basics', {}, () => { assert.ok($numberBox.hasClass(INVALID_CLASS), 'widget is invalid'); }); + + QUnit.test('It should be possible to set negative value via scroll when min is null', function(assert) { + const $numberBox = $('#numberbox').dxNumberBox({ + min: null, + value: 0, + showSpinButtons: true + }); + + const instance = $numberBox.dxNumberBox('instance'); + const $spinDown = $numberBox.find('.' + SPIN_DOWN_CLASS); + + $spinDown.trigger('dxpointerdown'); + + assert.strictEqual(instance.option('value'), -1, 'value is not set to negative number'); + }); }); QUnit.module('submit element', {}, () => { diff --git a/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/mask.tests.js b/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/mask.tests.js index 4f593263d5e2..08158909bc12 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/mask.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/mask.tests.js @@ -824,6 +824,24 @@ QUnit.module('format: text input', moduleConfig, () => { assert.deepEqual(this.keyboard.caret(), { start: 3, end: 3 }, 'caret position is correct'); }); + QUnit.test('It should be possible to set negative value when min is null and format is defined (T876378)', function(assert) { + this.instance.option({ + min: null, + format: '#,##0.##', + valueChangeEvent: 'keyup', + value: 2 + }); + + this.input.focus(); + this.clock.tick(CARET_TIMEOUT_DURATION); + + this.keyboard.caret(1); + this.clock.tick(CARET_TIMEOUT_DURATION); + this.keyboard.type('-'); + + assert.strictEqual(this.instance.option('value'), -2, 'value is set to negative number'); + }); + QUnit.test('don\'t replace selected text after enter pressed', function(assert) { this.instance.option({ format: '#0.00', diff --git a/testing/tests/DevExpress.ui.widgets.editors/selectBox.tests.js b/testing/tests/DevExpress.ui.widgets.editors/selectBox.tests.js index 44cdd8e70dce..c3327a90972c 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/selectBox.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/selectBox.tests.js @@ -473,6 +473,31 @@ QUnit.module('functionality', moduleSetup, () => { assert.deepEqual(selectBox._list.option('items'), []); }); + QUnit.test('no exceptions after dataSource reset during typing', function(assert) { + try { + const $element = $('#selectBox'); + const selectBox = $element.dxSelectBox({ + dataSource: ['one', 'two'], + searchTimeout: 0, + searchEnabled: true + }).dxSelectBox('instance'); + + const $input = $element.find(toSelector(TEXTEDITOR_INPUT_CLASS)); + const keyboard = keyboardMock($input); + + keyboard + .focus() + .type('o'); + + selectBox.option('dataSource', null); + + keyboard.type('n'); + assert.ok(true, 'no errors'); + } catch(e) { + assert.ok(false, `The '${e.message}' is raised`); + } + }); + QUnit.test('list item obtained focus only after press on control key', function(assert) { if(devices.real().deviceType !== 'desktop') { assert.ok(true, 'test does not actual for mobile devices'); @@ -2822,6 +2847,44 @@ QUnit.module('search', moduleSetup, () => { assert.equal($(instance.content()).find(toSelector(LIST_ITEM_CLASS)).length, 3, 'filter was cleared'); }); + QUnit.testInActiveWindow('Filter should not be canceled after focusout if event target is not in editor\'s overlay (T838753)', function(assert) { + const items = ['111', '222', '333']; + + const $selectBox = $('#selectBox').dxSelectBox({ + searchTimeout: 0, + items, + searchEnabled: true, + applyValueMode: 'useButtons' + }); + + const instance = $selectBox.dxSelectBox('instance'); + const $input = $selectBox.find(toSelector(TEXTEDITOR_INPUT_CLASS)); + + keyboardMock($input).type('1'); + $input.trigger($.Event('focusout', { relatedTarget: $(instance.content()).parent().find('.dx-toolbar-items-container') })); + + assert.equal($(instance.content()).find(toSelector(LIST_ITEM_CLASS)).length, 1, 'filter is not cleared'); + }); + + QUnit.testInActiveWindow('Filter should not be canceled after focusout if the widget is closed (T876423)', function(assert) { + const items = ['111', '222', '333']; + + const $selectBox = $('#selectBox').dxSelectBox({ + searchTimeout: 0, + items, + searchEnabled: true + }); + + const instance = $selectBox.dxSelectBox('instance'); + const $input = $selectBox.find(toSelector(TEXTEDITOR_INPUT_CLASS)); + + keyboardMock($input).type('1'); + instance.close(); + $input.trigger('focusout'); + + assert.equal($(instance.content()).find(toSelector(LIST_ITEM_CLASS)).length, 1, 'filter is not clear'); + }); + QUnit.testInActiveWindow('Unfiltered editor should not be load data on blur (T873258)', function(assert) { const loadStub = sinon.stub().returns([1, 2, 3]); const $selectBox = $('#selectBox').dxSelectBox({ diff --git a/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js b/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js index cbb44f5f989d..5c8a063d19ca 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js @@ -587,6 +587,35 @@ QUnit.module('tags', moduleSetup, () => { assert.equal($tag.text(), '', 'tag has correct text'); }); + QUnit.test('Tag should repaint tags on \'repaint\' if dataSource is reloaded (T873372)', function(assert) { + let items = [{ name: 'one', value: 1 }, { name: 'two', value: 2 }]; + const dataSource = new DataSource({ + store: new CustomStore({ + key: 'id', + load: function(loadOptions) { + const deferred = $.Deferred(); + deferred.resolve(items); + return deferred.promise(); + } + }), + paginate: true + }); + const $tagBox = $('#tagBox').dxTagBox({ + dataSource, + displayExpr: 'name', + valueExpr: 'value', + value: [1] + }); + const tagBox = $tagBox.dxTagBox('instance'); + this.clock.tick(); + items = [{ name: 'updated', value: 1 }]; + dataSource.reload(); + tagBox.repaint(); + + const $tag = $tagBox.find('.' + TAGBOX_TAG_CLASS); + assert.equal($tag.text(), 'updated', 'tag has updated text'); + }); + QUnit.test('onValueChanged has dxclick event on remove button click', function(assert) { const $element = $('#tagBox').dxTagBox({ value: ['123'], @@ -1505,7 +1534,7 @@ QUnit.module('showSelectionControls', moduleSetup, () => { assert.deepEqual($tagBox.dxTagBox('option', 'value'), [2], 'value is reset'); }); - QUnit.test('click on selected item causes item uncheck', function(assert) { + QUnit.test('list items are selected on render', function(assert) { $('#tagBox').dxTagBox({ items: [1, 2, 3], value: [1, 2], diff --git a/testing/tests/DevExpress.ui.widgets.editors/validator.editors.tests.js b/testing/tests/DevExpress.ui.widgets.editors/validator.editors.tests.js index 25c6d2b07245..ddcce09d1b09 100644 --- a/testing/tests/DevExpress.ui.widgets.editors/validator.editors.tests.js +++ b/testing/tests/DevExpress.ui.widgets.editors/validator.editors.tests.js @@ -409,5 +409,38 @@ QUnit.module('Editors Standard Adapter', { assert.ok(this.fixture.$element.hasClass('dx-valid'), 'valid mark is rendered'); }); + + QUnit.test('Editor(read-only) - validating and validated events of a validator should be raised (T873862)', function(assert) { + // arrange + this.fixture.createTextEditor({ + value: 'test', + readOnly: true + }); + const validatingHandler = sinon.stub(); + const validatedHandler = sinon.stub(); + const validator = this.fixture.createValidator({ + adapter: null, + validationRules: [ + { + type: 'async', + validationCallback: function() { + return new Deferred().resolve().promise(); + } + } + ] + }); + validator.on('validating', validatingHandler); + validator.on('validated', validatedHandler); + const done = assert.async(); + + const result = validator.validate(); + result.complete.then(() => { + // assert + assert.ok(validatingHandler.calledOnce, 'validating was called'); + assert.ok(validatedHandler.calledOnce, 'validated was called'); + + done(); + }); + }); }); diff --git a/testing/tests/DevExpress.ui.widgets.form/form.tests.js b/testing/tests/DevExpress.ui.widgets.form/form.tests.js index 4bc86876850b..e925e2a0fd96 100644 --- a/testing/tests/DevExpress.ui.widgets.form/form.tests.js +++ b/testing/tests/DevExpress.ui.widgets.form/form.tests.js @@ -2112,6 +2112,542 @@ QUnit.test('Changing the item\'s option via the itemOption when these options ar assert.strictEqual($('#form').find('.test-class').length, 1, 'cssClass of item'); }); +QUnit.module('visible/visibleIndex', () => { + QUnit.test('item1.visible:true (no visibleIndex)', function(assert) { + const form = $('#form').dxForm({ + items: [ + { dataField: 'field1', visible: true }, + { dataField: 'field2', visible: true } ] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field1', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field2', 'inputs'); + }); + + QUnit.test('item1.visible:true (sequential visibleIndex starting from 0)', function(assert) { + const form = $('#form').dxForm({ + items: [ + { dataField: 'field1', visible: true, visibleIndex: 0 }, + { dataField: 'field2', visible: true, visibleIndex: 1 } ] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field1', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field2', 'inputs'); + }); + + QUnit.test('item1.visible:true (sequantial visibleIndex starting from 0 does not fit with items order)', function(assert) { + const form = $('#form').dxForm({ + items: [ + { dataField: 'field1', visible: true, visibleIndex: 1 }, + { dataField: 'field2', visible: true, visibleIndex: 0 } ] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field1', 'inputs'); + }); + + QUnit.test('item1.visible:true (non sequensial visibleIndex starting from 2)', function(assert) { + const form = $('#form').dxForm({ + items: [ + { dataField: 'field1', visible: true, visibleIndex: 2 }, + { dataField: 'field2', visible: true, visibleIndex: 3 } ] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field1', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field2', 'inputs'); + }); + + QUnit.test('item1.visible:true (non sequantial visibleIndex starting from 2 does not fit with items order)', function(assert) { + const form = $('#form').dxForm({ + items: [ + { dataField: 'field1', visible: true, visibleIndex: 5 }, + { dataField: 'field2', visible: true, visibleIndex: 2 } ] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field1', 'inputs'); + }); + + QUnit.test('item1.visible:false -> item1.visible:true (no visibleIndex)', function(assert) { + const form = $('#form').dxForm({ + items: [ + { dataField: 'field1', visible: false }, + { dataField: 'field2', visible: true } + ] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field1', 'inputs_1'); + assert.equal($inputs_2.eq(1).attr('name'), 'field2', 'inputs_2'); + }); + + QUnit.test('item1.visible:false -> item1.visible:true (sequential visibleIndex starting from 0)', function(assert) { + const form = $('#form').dxForm({ + items: [ + { dataField: 'field1', visible: false, visibleIndex: 0 }, + { dataField: 'field2', visible: true, visibleIndex: 1 } ] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field1', 'inputs_2'); + assert.equal($inputs_2.eq(1).attr('name'), 'field2', 'inputs_2'); + }); + + QUnit.test('item1.visible:false -> item1.visible:true (sequantial visibleIndex starting from 0 does not fit with items order)', function(assert) { + const form = $('#form').dxForm({ + items: [ + { dataField: 'field1', visible: false, visibleIndex: 1 }, + { dataField: 'field2', visible: true, visibleIndex: 0 } ] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field2', 'inputs_2'); + assert.equal($inputs_2.eq(1).attr('name'), 'field1', 'inputs_2'); + }); + + QUnit.test('item1.visible:false -> item1.visible:true (non sequensial visibleIndex starting from 2)', function(assert) { + const form = $('#form').dxForm({ + items: [ + { dataField: 'field1', visible: false, visibleIndex: 2 }, + { dataField: 'field2', visible: true, visibleIndex: 5 } ] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field1', 'inputs_2'); + assert.equal($inputs_2.eq(1).attr('name'), 'field2', 'inputs_2'); + }); + + QUnit.test('item1.visible:false -> item1.visible:true (non sequantial visibleIndex starting from 2 does not fit with items order)', function(assert) { + const form = $('#form').dxForm({ + items: [ + { dataField: 'field1', visible: false, visibleIndex: 5 }, + { dataField: 'field2', visible: true, visibleIndex: 2 } ] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field2', 'inputs_2'); + assert.equal($inputs_2.eq(1).attr('name'), 'field1', 'inputs_2'); + }); + + QUnit.test('group.item1.visible:true (no visibleIndex)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'group', + colCount: 1, + items: [ + { dataField: 'field1', visible: true }, + { dataField: 'field2', visible: true } ] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field1', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field2', 'inputs'); + }); + + QUnit.test('group.item1.visible:true (sequential visibleIndex starting from 0)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'group', + colCount: 1, + items: [ + { dataField: 'field1', visible: true, visibleIndex: 0 }, + { dataField: 'field2', visible: true, visibleIndex: 1 } ] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field1', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field2', 'inputs'); + }); + + QUnit.test('group.item1.visible:true (sequantial visibleIndex starting from 0 does not fit with items order)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'group', + colCount: 1, + items: [ + { dataField: 'field1', visible: true, visibleIndex: 1 }, + { dataField: 'field2', visible: true, visibleIndex: 0 } ] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field1', 'inputs'); + }); + + QUnit.test('group.item1.visible:true (non sequensial visibleIndex starting from 2)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'group', + colCount: 1, + items: [ + { dataField: 'field1', visible: true, visibleIndex: 2 }, + { dataField: 'field2', visible: true, visibleIndex: 3 } ] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field1', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field2', 'inputs'); + }); + + QUnit.test('group.item1.visible:true (non sequantial visibleIndex starting from 2 does not fit with items order)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'group', + colCount: 1, + items: [ + { dataField: 'field1', visible: true, visibleIndex: 5 }, + { dataField: 'field2', visible: true, visibleIndex: 2 } ] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field1', 'inputs'); + }); + + QUnit.test('group.item1.visible:false -> group.item1.visible:true (no visibleIndex)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'group', + colCount: 1, + name: 'group', + items: [ + { dataField: 'field1', visible: false }, + { dataField: 'field2', visible: true } ] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('group.field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field1', 'inputs_1'); + assert.equal($inputs_2.eq(1).attr('name'), 'field2', 'inputs_2'); + }); + + QUnit.test('group.item1.visible:false -> group.item1.visible:true (sequential visibleIndex starting from 0)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'group', + colCount: 1, + name: 'group', + items: [ + { dataField: 'field1', visible: false, visibleIndex: 0 }, + { dataField: 'field2', visible: true, visibleIndex: 1 } ] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('group.field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field1', 'inputs_2'); + assert.equal($inputs_2.eq(1).attr('name'), 'field2', 'inputs_2'); + }); + + QUnit.test('group.item1.visible:false -> group.item1.visible:true (sequantial visibleIndex starting from 0 does not fit with items order)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'group', + colCount: 1, + name: 'group', + items: [ + { dataField: 'field1', visible: false, visibleIndex: 1 }, + { dataField: 'field2', visible: true, visibleIndex: 0 } ] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('group.field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field2', 'inputs_2'); + assert.equal($inputs_2.eq(1).attr('name'), 'field1', 'inputs_2'); + }); + + QUnit.test('group.item1.visible:false -> group.item1.visible:true (non sequensial visibleIndex starting from 2)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'group', + colCount: 1, + name: 'group', + items: [ + { dataField: 'field1', visible: false, visibleIndex: 2 }, + { dataField: 'field2', visible: true, visibleIndex: 5 } ] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('group.field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field1', 'inputs_2'); + assert.equal($inputs_2.eq(1).attr('name'), 'field2', 'inputs_2'); + }); + + QUnit.test('group.item1.visible:false -> group.item1.visible:true (non sequantial visibleIndex starting from 2 does not fit with items order)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'group', + colCount: 1, + name: 'group', + items: [ + { dataField: 'field1', visible: false, visibleIndex: 5 }, + { dataField: 'field2', visible: true, visibleIndex: 2 } ] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('group.field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field2', 'inputs_2'); + assert.equal($inputs_2.eq(1).attr('name'), 'field1', 'inputs_2'); + }); + + QUnit.test('tabbedGroup.item1.visible:true (no visibleIndex)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'tabbed', + tabs: [ { title: 'tab', items: [ + { dataField: 'field1', visible: true }, + { dataField: 'field2', visible: true } ] + }] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field1', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field2', 'inputs'); + }); + + QUnit.test('tabbedGroup.item1.visible:true (sequential visibleIndex starting from 0)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'tabbed', + tabs: [ { title: 'tab', items: [ + { dataField: 'field1', visible: true, visibleIndex: 0 }, + { dataField: 'field2', visible: true, visibleIndex: 1 } ] + }] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field1', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field2', 'inputs'); + }); + + QUnit.test('tabbedGroup.item1.visible:true (sequantial visibleIndex starting from 0 does not fit with items order)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'tabbed', + tabs: [{ + title: 'tab', items: [ + { dataField: 'field1', visible: true, visibleIndex: 1 }, + { dataField: 'field2', visible: true, visibleIndex: 0 }] + }] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field1', 'inputs'); + }); + + QUnit.test('tabbedGroup.item1.visible:true (non sequensial visibleIndex starting from 2)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'tabbed', + tabs: [{ + title: 'tab', items: [ + { dataField: 'field1', visible: true, visibleIndex: 2 }, + { dataField: 'field2', visible: true, visibleIndex: 3 }] + }] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field1', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field2', 'inputs'); + }); + + QUnit.test('tabbedGroup.item1.visible:true (non sequantial visibleIndex starting from 2 does not fit with items order)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'tabbed', + tabs: [{ + title: 'tab', items: [ + { dataField: 'field1', visible: true, visibleIndex: 5 }, + { dataField: 'field2', visible: true, visibleIndex: 2 }] + }] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2', 'inputs'); + assert.equal($inputs.eq(1).attr('name'), 'field1', 'inputs'); + }); + + QUnit.test('tabbedGroup.item1.visible:false -> tabbedGroup.item1.visible:true (no visibleIndex)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'tabbed', + name: 'tabbed', + tabs: [{ + title: 'tab', items: [ + { dataField: 'field1', visible: false }, + { dataField: 'field2', visible: true }] + }] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('tabbed.tab.field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field1', 'inputs_1'); + assert.equal($inputs_2.eq(1).attr('name'), 'field2', 'inputs_2'); + }); + + QUnit.test('tabbedGroup.item1.visible:false -> tabbedGroup.item1.visible:true (sequential visibleIndex starting from 0)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'tabbed', + name: 'tabbed', + tabs: [{ + title: 'tab', items: [ + { dataField: 'field1', visible: false, visibleIndex: 0 }, + { dataField: 'field2', visible: true, visibleIndex: 1 }] + }] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('tabbed.tab.field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field1', 'inputs_2'); + assert.equal($inputs_2.eq(1).attr('name'), 'field2', 'inputs_2'); + }); + + QUnit.test('tabbedGroup.item1.visible:false -> tabbedGroup.item1.visible:true (sequantial visibleIndex starting from 0 does not fit with items order)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'tabbed', + name: 'tabbed', + tabs: [{ + title: 'tab', items: [ + { dataField: 'field1', visible: false, visibleIndex: 1 }, + { dataField: 'field2', visible: true, visibleIndex: 0 }] + }] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('tabbed.tab.field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field2', 'inputs_2'); + assert.equal($inputs_2.eq(1).attr('name'), 'field1', 'inputs_2'); + }); + + QUnit.test('tabbedGroup.item1.visible:false -> tabbedGroup.item1.visible:true (non sequensial visibleIndex starting from 2)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'tabbed', + name: 'tabbed', + tabs: [{ + title: 'tab', items: [ + { dataField: 'field1', visible: false, visibleIndex: 2 }, + { dataField: 'field2', visible: true, visibleIndex: 5 }] + }] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('tabbed.tab.field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field1', 'inputs_2'); + assert.equal($inputs_2.eq(1).attr('name'), 'field2', 'inputs_2'); + }); + + QUnit.test('tabbedGroup.item1.visible:false -> tabbedGroup.item1.visible:true (non sequantial visibleIndex starting from 2 does not fit with items order)', function(assert) { + const form = $('#form').dxForm({ + items: [{ + itemType: 'tabbed', + name: 'tabbed', + tabs: [{ + title: 'tab', items: [ + { dataField: 'field1', visible: false, visibleIndex: 5 }, + { dataField: 'field2', visible: true, visibleIndex: 2 }] + }] + }] + }).dxForm('instance'); + + const $inputs = form.$element().find('input'); + assert.equal($inputs.eq(0).attr('name'), 'field2'); + + form.itemOption('tabbed.tab.field1', 'visible', true); + + const $inputs_2 = form.$element().find('input'); + assert.equal($inputs_2.eq(0).attr('name'), 'field2', 'inputs_2'); + assert.equal($inputs_2.eq(1).attr('name'), 'field1', 'inputs_2'); + }); +}); + QUnit.test('resetValues - old test', function(assert) { const form = $('#form').dxForm({ formData: { diff --git a/testing/tests/DevExpress.ui.widgets.pivotGrid/fieldChooser.tests.js b/testing/tests/DevExpress.ui.widgets.pivotGrid/fieldChooser.tests.js index 1d8f3fdb1dba..4243eec36628 100644 --- a/testing/tests/DevExpress.ui.widgets.pivotGrid/fieldChooser.tests.js +++ b/testing/tests/DevExpress.ui.widgets.pivotGrid/fieldChooser.tests.js @@ -2179,80 +2179,6 @@ QUnit.module('Base Field chooser', { }); -// T852897 -QUnit.test('Custom texts.emptyValue in header filter', function(assert) { - const that = this; - let listItems; - let fieldElements; - const fields = [ - { caption: 'Field 1', area: 'column', index: 0, areaIndex: 0, allowSorting: true, allowFiltering: true } - ]; - const dataSourceOptions = { - columnFields: fields, - fieldValues: [ - [{ value: 1, text: '1' }, { value: 2, text: '' }] - ] - }; - - this.setup(dataSourceOptions, { - headerFilter: { - texts: { - emptyValue: 'Test' - } - } - }); - - $.each(fields, function(_, field) { - that.$container.append(that.fieldChooser.renderField(field)); - }); - - fieldElements = that.$container.find('.dx-area-field'); - - // act - fieldElements.first().find('.dx-header-filter').trigger('dxclick'); - this.clock.tick(500); - - // assert - listItems = $('.dx-list').dxList('instance').option('items'); - - assert.equal(listItems.length, 2, 'header filter items'); - assert.equal(listItems[1].text, 'Test'); -}); - -// T852897 -QUnit.test('Default texts.emptyValue in header filter', function(assert) { - const that = this; - let listItems; - let fieldElements; - const fields = [ - { caption: 'Field 1', area: 'column', index: 0, areaIndex: 0, allowSorting: true, allowFiltering: true } - ]; - const dataSourceOptions = { - columnFields: fields, - fieldValues: [ - [{ value: 1, text: '' }, { value: 2, text: '2' }] - ] - }; - - this.setup(dataSourceOptions); - - $.each(fields, function(_, field) { - that.$container.append(that.fieldChooser.renderField(field)); - }); - - fieldElements = that.$container.find('.dx-area-field'); - - // act - fieldElements.first().find('.dx-header-filter').trigger('dxclick'); - this.clock.tick(500); - - // assert - listItems = $('.dx-list').dxList('instance').option('items'); - - assert.equal(listItems.length, 2, 'header filter items'); - assert.equal(listItems[0].text, '(Blanks)'); -}); - QUnit.module('applyChangesMode: onDemand', { beforeEach: function() { this.$container = $('#container'); @@ -2291,6 +2217,56 @@ QUnit.module('applyChangesMode: onDemand', { assert.deepEqual(state.fields.length, 3, 'start state'); }); + QUnit.test('Tree should not recursive expand parent nodes (T866559)', function(assert) { + this.setup({ + fields: [ + { + 'dimension': 'Measures', + 'dataField': '[Measures].[Customer Count]', + 'caption': 'Customer Count', + 'displayFolder': 'Internet Customers', + 'isMeasure': true + }, + { + 'dimension': 'Date', + 'dataField': '[Date].[Date]', + 'caption': 'Date.Date', + 'displayFolder': '', + 'isMeasure': false + } + ] + }); + const dataSource = this.fieldChooser.getDataSource(); + dataSource.load(); + this.clock.tick(500); + + const treeView = this.fieldChooser.$element().find('.dx-treeview').dxTreeView('instance'); + let treeViewItems = treeView.option('dataSource'); + + treeView.expandItem(treeViewItems[0]); + this.clock.tick(500); + + treeView.expandItem(treeViewItems[0].items[0]); + this.clock.tick(500); + + assert.ok(treeView.getNodes()[0].children[0].expanded, 'node is expanded'); + + treeView.selectItem(treeViewItems[0].items[0].items[0]); + + treeViewItems = treeView.option('dataSource'); + treeView.collapseItem(treeViewItems[0]); + this.clock.tick(500); + + assert.notOk(treeView.getNodes()[0].expanded, 'first node is collapsed'); + + treeView.selectItem(treeViewItems[1]); + + assert.notOk(treeView.getNodes()[0].expanded, 'first node is collapsed'); + + assert.ok(treeView.getNodes()[0].children[0].selected, 'first node is selected'); + assert.ok(treeView.getNodes()[1].selected, 'second node is selected'); + }); + QUnit.test('change position between areas', function(assert) { const dataSource = {}; dataSource.fields = [ @@ -2579,4 +2555,3 @@ QUnit.module('applyChangesMode: onDemand', { }); }); - diff --git a/testing/tests/DevExpress.ui.widgets.pivotGrid/store.remote.tests.js b/testing/tests/DevExpress.ui.widgets.pivotGrid/store.remote.tests.js index b94b621949ac..25ca60dc3f6c 100644 --- a/testing/tests/DevExpress.ui.widgets.pivotGrid/store.remote.tests.js +++ b/testing/tests/DevExpress.ui.widgets.pivotGrid/store.remote.tests.js @@ -350,13 +350,56 @@ QUnit.module('Loading root data', moduleConfig, () => { assert.deepEqual(data[headerName][0].value, new Date('1996/07/04')); }); }); + + QUnit.test(`Do not parse string data if next field dataType is number for ${headerName} (T853201)`, function(assert) { + this.store = new RemoteStore({ + load: function() { + return $.Deferred().resolve([{ + 'key': 'Expenses', + 'items': [{ + 'key': 'FY 2018 Actual', + 'items': [{ 'key': 1, 'items': null, 'count': 1 }], + 'count': null, + }, { + 'key': 'FY 2018 Budget', + 'items': [{ 'key': 1, 'items': null, 'count': 1 }], + 'count': null, + }], + 'count': null + }]); + } + }); + + this.load({ + columns: [], + rows: [], + values: [], + [headerName]: [{ + dataField: 'AccountType', + dataType: 'string', + expanded: true + }, { + dataField: 'DataSet', + dataType: 'string', + expanded: true + }, { + dataField: 'Period', + dataType: 'number', + }] + }).done(function(data) { + const headerItems = data[headerName]; + assert.strictEqual(headerItems[0].value, 'Expenses'); + assert.strictEqual(headerItems[0].children[0].value, 'FY 2018 Actual'); + assert.strictEqual(headerItems[0].children[0].children[0].value, 1); + assert.strictEqual(headerItems[0].children[1].value, 'FY 2018 Budget'); + assert.strictEqual(headerItems[0].children[1].children[0].value, 1); + }); + }); }); QUnit.test('Key method should return key field name', function(assert) { assert.strictEqual(this.store.key(), 'OrderID'); }); - - }); QUnit.module('Summary calculation', moduleConfig, () => { diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/appointment.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/appointment.tests.js index e07627ddd024..3b2d8c93f9a1 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/appointment.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/appointment.tests.js @@ -40,13 +40,6 @@ QUnit.test('Scheduler appointment should be initialized', function(assert) { assert.ok(this.instance instanceof SchedulerAppointment, 'dxSchedulerAppointment was initialized'); }); -QUnit.test('Scheduler appointment has css-class \'dx-scheduler-appointment-compact\'', function(assert) { - assert.notOk(this.instance.$element().hasClass('dx-scheduler-appointment-compact'), 'appointment doesn\'t have css-class'); - - this.instance.option('isCompact', true); - assert.ok(this.instance.$element().hasClass('dx-scheduler-appointment-compact'), 'appointment has right class'); -}); - QUnit.test('Scheduler appointment has right direction css-class', function(assert) { assert.notOk(this.instance.$element().hasClass('dx-scheduler-appointment-horizontal'), 'appointment doesn\'t have css-class'); assert.ok(this.instance.$element().hasClass('dx-scheduler-appointment-vertical'), 'appointment has right class'); diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/appointmentPopup.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/appointmentPopup.tests.js index 1be5139ac877..c26af1c0d234 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/appointmentPopup.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/appointmentPopup.tests.js @@ -31,7 +31,7 @@ const checkFormWithRecurrenceEditor = (assert, instance, visibility) => { const createInstance = function(options) { const defaultOption = { dataSource: [], - maxAppointmentsPerCell: null + maxAppointmentsPerCell: 2 }; const instance = $('#scheduler').dxScheduler($.extend(defaultOption, options)).dxScheduler('instance'); return new SchedulerTestWrapper(instance); @@ -41,7 +41,7 @@ const moduleOptions = { beforeEach: function() { this.instance = $('#scheduler').dxScheduler({ dataSource: [], - maxAppointmentsPerCell: null, + maxAppointmentsPerCell: 2, }).dxScheduler('instance'); fx.off = true; this.tasks = [ diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/common.markup.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/common.markup.tests.js index ae99dcabdb4a..720e5aaf66ad 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/common.markup.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/common.markup.tests.js @@ -168,20 +168,6 @@ QUnit.module('Scheduler with config', { assert.equal($header.find('.dx-tab').eq(1).text(), 'Week'); }); - QUnit.test('Workspace shouldn\'t have specific class if maxAppointmentsPerCell=null', function(assert) { - this.createInstance({ - currentView: 'Week', - maxAppointmentsPerCell: null, - views: [{ - type: 'week', - name: 'Week', - }] - }); - - const $workSpace = this.instance.getWorkSpace().$element(); - assert.notOk($workSpace.hasClass('dx-scheduler-work-space-overlapping'), 'workspace hasn\'t class'); - }); - QUnit.test('Scheduler should not fail when crossScrollingEnabled is set', function(assert) { this.createInstance(); diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/common.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/common.tests.js index 5f5c4b5867dd..349fe6d457e3 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/common.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/common.tests.js @@ -32,65 +32,66 @@ QUnit.testStart(function() { $('#qunit-fixture').html('
'); }); -(function() { - QUnit.module('Initialization', { - beforeEach: function() { - this.clock = sinon.useFakeTimers(); +QUnit.module('Initialization', { + beforeEach: function() { + this.clock = sinon.useFakeTimers(); + sinon.spy(errors, 'log'); - this.instance = $('#scheduler').dxScheduler().dxScheduler('instance'); + this.createInstance = function(options) { + this.instance = $('#scheduler').dxScheduler(options).dxScheduler('instance'); this.scheduler = new SchedulerTestWrapper(this.instance); + }; - this.checkDateTime = function(assert, actualDate, expectedDate, messagePrefix) { - assert.equal(actualDate.getHours(), expectedDate.getHours(), messagePrefix + 'Hours\'re OK'); - assert.equal(actualDate.getMinutes(), expectedDate.getMinutes(), messagePrefix + 'Minutes\'re OK'); - assert.equal(actualDate.getSeconds(), expectedDate.getSeconds(), messagePrefix + 'Seconds\'re OK'); - assert.equal(actualDate.getMilliseconds(), expectedDate.getMilliseconds(), messagePrefix + 'Milliseconds\'re OK'); - }; - fx.off = true; - this.tasks = [ - { - text: 'Task 1', - startDate: new Date(2015, 1, 9, 1, 0), - endDate: new Date(2015, 1, 9, 2, 0) - }, - { - text: 'Task 2', - startDate: new Date(2015, 1, 9, 11, 0), - endDate: new Date(2015, 1, 9, 12, 0) - } - ]; - }, - afterEach: function() { - this.clock.restore(); - fx.off = false; - } - }); - + this.checkDateTime = function(assert, actualDate, expectedDate, messagePrefix) { + assert.equal(actualDate.getHours(), expectedDate.getHours(), messagePrefix + 'Hours\'re OK'); + assert.equal(actualDate.getMinutes(), expectedDate.getMinutes(), messagePrefix + 'Minutes\'re OK'); + assert.equal(actualDate.getSeconds(), expectedDate.getSeconds(), messagePrefix + 'Seconds\'re OK'); + assert.equal(actualDate.getMilliseconds(), expectedDate.getMilliseconds(), messagePrefix + 'Milliseconds\'re OK'); + }; + fx.off = true; + this.tasks = [ + { + text: 'Task 1', + startDate: new Date(2015, 1, 9, 1, 0), + endDate: new Date(2015, 1, 9, 2, 0) + }, + { + text: 'Task 2', + startDate: new Date(2015, 1, 9, 11, 0), + endDate: new Date(2015, 1, 9, 12, 0) + } + ]; + }, + afterEach: function() { + errors.log.restore(); + this.clock.restore(); + fx.off = false; + } +}, () => { QUnit.test('Scheduler should have task model instance', function(assert) { const data = new DataSource({ store: this.tasks }); - - this.instance.option({ dataSource: data }); + this.createInstance({ dataSource: data }); assert.ok(this.instance._appointmentModel instanceof dxSchedulerAppointmentModel, 'Task model is initialized on scheduler init'); assert.ok(this.instance._appointmentModel._dataSource instanceof DataSource, 'Task model has data source instance'); }); QUnit.test('Scheduler should work correctly when wrong timeZone was set', function(assert) { - this.instance.option({ timeZone: 'Wrong/timeZone' }); - + this.createInstance({ timeZone: 'Wrong/timeZone' }); assert.ok(true, 'Widget works correctly'); }); QUnit.test('Scheduler shouldn\'t have paginate in default DataSource', function(assert) { - this.instance.option({ dataSource: this.tasks }); + this.createInstance({ dataSource: this.tasks }); assert.notOk(this.instance._appointmentModel._dataSource.paginate(), 'Paginate is false'); }); QUnit.test('Rendering inside invisible element', function(assert) { try { + this.createInstance(); domUtils.triggerHidingEvent($('#scheduler')); $('#scheduler').hide(); this.instance.option({ @@ -111,6 +112,7 @@ QUnit.testStart(function() { }); QUnit.test('Data expressions should be compiled on init', function(assert) { + this.createInstance(); const dataAccessors = this.instance._dataAccessors; $.each([ @@ -128,147 +130,8 @@ QUnit.testStart(function() { }); }); - QUnit.test('Data expressions should be recompiled on optionChanged', function(assert) { - const repaintStub = sinon.stub(this.instance, 'repaint'); - - try { - this.instance.option({ - 'startDateExpr': '_startDate', - 'endDateExpr': '_endDate', - 'startDateTimeZoneExpr': '_startDateTimeZone', - 'endDateTimeZoneExpr': '_endDateTimeZone', - 'textExpr': '_text', - 'descriptionExpr': '_description', - 'allDayExpr': '_allDay', - 'recurrenceRuleExpr': '_recurrenceRule', - 'recurrenceExceptionExpr': '_recurrenceException' - }); - - const data = { - startDate: new Date(2017, 2, 22), - endDate: new Date(2017, 2, 23), - startDateTimeZone: 'America/Los_Angeles', - endDateTimeZone: 'America/Los_Angeles', - text: 'a', - description: 'b', - allDay: true, - recurrenceRule: 'abc', - recurrenceException: 'def' - }; - const appointment = { - _startDate: data.startDate, - _endDate: data.endDate, - _startDateTimeZone: data.startDateTimeZone, - _endDateTimeZone: data.endDateTimeZone, - _text: data.text, - _description: data.description, - _allDay: data.allDay, - _recurrenceRule: data.recurrenceRule, - _recurrenceException: data.recurrenceException - }; - - const dataAccessors = this.instance._dataAccessors; - - $.each(dataAccessors.getter, function(name, getter) { - assert.equal(dataAccessors.getter[name](appointment), data[name], 'getter for ' + name + ' is OK'); - }); - - $.each(dataAccessors.setter, function(name, getter) { - dataAccessors.setter[name](appointment, 'xyz'); - assert.equal(appointment['_' + name], 'xyz', 'setter for ' + name + ' is OK'); - }); - } finally { - repaintStub.restore(); - } - }); - - QUnit.test('Data expressions should be recompiled on optionChanged and passed to appointmentModel', function(assert) { - const repaintStub = sinon.stub(this.instance, 'repaint'); - - try { - const appointmentModel = this.instance.getAppointmentModel(); - - this.instance.option({ - 'startDateExpr': '_startDate', - 'endDateExpr': '_endDate', - 'startDateTimeZoneExpr': '_startDateTimeZone', - 'endDateTimeZoneExpr': '_endDateTimeZone', - 'textExpr': '_text', - 'descriptionExpr': '_description', - 'allDayExpr': '_allDay', - 'recurrenceRuleExpr': '_recurrenceRule', - 'recurrenceExceptionExpr': '_recurrenceException' - }); - - const dataAccessors = this.instance._dataAccessors; - - assert.deepEqual($.extend({ resources: {} }, dataAccessors.getter), appointmentModel._dataAccessors.getter, 'dataAccessors getters were passed to appointmentModel'); - assert.deepEqual($.extend({ resources: {} }, dataAccessors.setter), appointmentModel._dataAccessors.setter, 'dataAccessors setters were passed to appointmentModel'); - assert.deepEqual(dataAccessors.expr, appointmentModel._dataAccessors.expr, 'dataExpressions were passed to appointmentModel'); - } finally { - repaintStub.restore(); - } - }); - - QUnit.test('Appointment should be rendered correctly after expression changing', function(assert) { - this.instance.option({ - dataSource: [{ - text: 'a', - StartDate: new Date(2015, 6, 8, 8, 0), - endDate: new Date(2015, 6, 8, 17, 0), - allDay: true - }], - currentDate: new Date(2015, 6, 8) - }); - - this.instance.option('startDateExpr', 'StartDate'); - this.clock.tick(); - assert.equal(this.instance.$element().find('.dx-scheduler-appointment').length, 1, 'Appointment is rendered'); - }); - - QUnit.test('Sheduler should be repainted after data expression option changing', function(assert) { - const repaintStub = sinon.stub(this.instance, 'repaint'); - - try { - this.instance.option({ - 'startDateExpr': '_startDate', - 'endDateExpr': '_endDate', - 'startDateTimeZoneExpr': '_startDateTimeZone', - 'endDateTimeZoneExpr': '_endDateTimeZone', - 'textExpr': '_text', - 'descriptionExpr': '_description', - 'allDayExpr': '_allDay', - 'recurrenceRuleExpr': '_recurrenceRule', - 'recurrenceExceptionExpr': '_recurrenceException' - }); - - assert.equal(repaintStub.callCount, 9, 'Scheduler was repainted'); - } finally { - repaintStub.restore(); - } - }); - - QUnit.test('Sheduler should have correct default template after data expression option changing', function(assert) { - this.instance.option({ - dataSource: [{ - text: 'a', - TEXT: 'New Text', - startDate: new Date(2015, 6, 8, 8, 0), - endDate: new Date(2015, 6, 8, 17, 0), - allDay: true - }], - currentDate: new Date(2015, 6, 8) - }); - - this.instance.option({ - textExpr: 'TEXT' - }); - - assert.equal(this.instance.$element().find('.dx-scheduler-appointment-title').eq(0).text(), 'New Text', 'Appointment template is correct'); - }); - QUnit.test('RecurrenceRule expression should not be compiled, if recurrenceRuleExpr = null', function(assert) { - this.instance.option({ + this.createInstance({ 'startDateExpr': '_startDate', 'endDateExpr': '_endDate', 'textExpr': '_text', @@ -284,7 +147,7 @@ QUnit.testStart(function() { }); QUnit.test('appointmentCollectorTemplate rendering args should be correct', function(assert) { - this.instance.option({ + this.createInstance({ dataSource: [{ startDate: new Date(2015, 4, 24, 9, 10), endDate: new Date(2015, 4, 24, 11, 1), @@ -308,7 +171,66 @@ QUnit.testStart(function() { currentView: 'month' }); }); -})('Initialization'); + + [ + { startDayHour: 0, endDayHour: 0 }, + { startDayHour: 2, endDayHour: 0 } + ].forEach(dayHours => { + QUnit.test(`Generate error if startDayHour: ${dayHours.startDayHour} >= endDayHour: ${dayHours.endDayHour}`, function(assert) { + assert.throws( + () => { + this.createInstance({ + currentDate: new Date(2015, 4, 24), + views: ['day'], + currentView: 'day', + startDayHour: dayHours.startDayHour, + endDayHour: dayHours.endDayHour + }); + }, + e => /E1058/.test(e.message), + 'E1058 Error message' + ); + this.clock.tick(1000); + }); + }); + + [ + { startDayHour: 0, endDayHour: 24, cellDuration: 95 }, + { startDayHour: 8, endDayHour: 24, cellDuration: 90 } + ].forEach(config => { + QUnit.test(`Generate warning if cellDuration: ${config.cellDuration} could not divide the range from startDayHour: ${config.startDayHour} to the endDayHour: ${config.endDayHour} into even intervals`, function(assert) { + this.createInstance({ + currentDate: new Date(2015, 4, 24), + views: ['day'], + currentView: 'day', + startDayHour: config.startDayHour, + endDayHour: config.endDayHour, + cellDuration: config.cellDuration + }); + + assert.equal(errors.log.callCount, 1, 'warning has been called once'); + assert.equal(errors.log.getCall(0).args[0], 'W1015', 'warning has correct error id'); + }); + }); + + [ + { startDayHour: 0, endDayHour: 24, cellDuration: 60 }, + { startDayHour: 8, endDayHour: 24, cellDuration: 10 } + ].forEach(config => { + QUnit.test(`Warning should not be generated if cellDuration: ${config.cellDuration} could divide the range from startDayHour: ${config.startDayHour} to the endDayHour: ${config.endDayHour} into even intervals`, function(assert) { + this.createInstance({ + currentDate: new Date(2015, 4, 24), + views: ['day'], + currentView: 'day', + startDayHour: config.startDayHour, + endDayHour: config.endDayHour, + cellDuration: config.cellDuration + }); + + assert.equal(errors.log.callCount, 0, 'there are not any warnings'); + }); + }); +}); (function() { @@ -323,6 +245,7 @@ QUnit.testStart(function() { this.instance = $('#scheduler').dxScheduler($.extend({ showCurrentTimeIndicator: false }, options)).dxScheduler('instance'); + this.scheduler = new SchedulerTestWrapper(this.instance); }; this.clock = sinon.useFakeTimers(); @@ -977,7 +900,7 @@ QUnit.testStart(function() { QUnit.test('Scheduler getWorkSpaceDateTableOffset should return right dateTable offset, crossScrollingEnabled=true, rtl mode', function(assert) { this.createInstance({ - dataSource: 'day', + currentView: 'day', currentDate: new Date(2015, 10, 3), crossScrollingEnabled: true, rtlEnabled: true @@ -989,6 +912,19 @@ QUnit.testStart(function() { assert.equal(offset, timePanelWidth, 'Date Table offset is correct'); }); + QUnit.test('Scheduler dateTable should have right position, crossScrollingEnabled=true, rtl mode', function(assert) { + this.createInstance({ + currentView: 'day', + currentDate: new Date(2015, 10, 3), + crossScrollingEnabled: true, + rtlEnabled: true + }); + + const dateTable = this.scheduler.workSpace.getDateTable(); + + assert.equal(dateTable.position().left, 0, 'Date Table left is correct'); + }); + QUnit.test('Timezone offset calculation(T388304)', function(assert) { [{ tz: 'Europe/Belgrade', offset: 1, daylightOffset: 2, daylightDate: new Date(2016, 4, 10), date: new Date(2016, 10, 20) }, { tz: 'Asia/Ashgabat', offset: 5, daylightOffset: 5, daylightDate: new Date(2016, 4, 10), date: new Date(2016, 10, 20) }, @@ -1161,11 +1097,12 @@ QUnit.testStart(function() { }; this.clock = sinon.useFakeTimers(); - + sinon.spy(errors, 'log'); fx.off = true; }, afterEach: function() { this.clock.restore(); + errors.log.restore(); fx.off = false; } }); @@ -1229,12 +1166,10 @@ QUnit.testStart(function() { height: 500 }); - const warningHandler = sinon.spy(errors, 'log'); - this.instance.scrollToTime(12, 0, new Date(2015, 1, 16)); - assert.equal(warningHandler.callCount, 1, 'warning has been called once'); - assert.equal(warningHandler.getCall(0).args[0], 'W1008', 'warning has correct error id'); + assert.equal(errors.log.callCount, 1, 'warning has been called once'); + assert.equal(errors.log.getCall(0).args[0], 'W1008', 'warning has correct error id'); }); QUnit.test('Check scrolling to time for timeline view', function(assert) { @@ -1337,12 +1272,156 @@ QUnit.testStart(function() { this.scheduler = new SchedulerTestWrapper(this.instance); }; this.clock = sinon.useFakeTimers(); + sinon.spy(errors, 'log'); }, afterEach: function() { this.clock.restore(); + errors.log.restore(); + } + }); + + QUnit.test('Data expressions should be recompiled on optionChanged', function(assert) { + this.createInstance(); + const repaintStub = sinon.stub(this.instance, 'repaint'); + + try { + this.instance.option({ + 'startDateExpr': '_startDate', + 'endDateExpr': '_endDate', + 'startDateTimeZoneExpr': '_startDateTimeZone', + 'endDateTimeZoneExpr': '_endDateTimeZone', + 'textExpr': '_text', + 'descriptionExpr': '_description', + 'allDayExpr': '_allDay', + 'recurrenceRuleExpr': '_recurrenceRule', + 'recurrenceExceptionExpr': '_recurrenceException' + }); + + const data = { + startDate: new Date(2017, 2, 22), + endDate: new Date(2017, 2, 23), + startDateTimeZone: 'America/Los_Angeles', + endDateTimeZone: 'America/Los_Angeles', + text: 'a', + description: 'b', + allDay: true, + recurrenceRule: 'abc', + recurrenceException: 'def' + }; + const appointment = { + _startDate: data.startDate, + _endDate: data.endDate, + _startDateTimeZone: data.startDateTimeZone, + _endDateTimeZone: data.endDateTimeZone, + _text: data.text, + _description: data.description, + _allDay: data.allDay, + _recurrenceRule: data.recurrenceRule, + _recurrenceException: data.recurrenceException + }; + + const dataAccessors = this.instance._dataAccessors; + + $.each(dataAccessors.getter, function(name, getter) { + assert.equal(dataAccessors.getter[name](appointment), data[name], 'getter for ' + name + ' is OK'); + }); + + $.each(dataAccessors.setter, function(name, getter) { + dataAccessors.setter[name](appointment, 'xyz'); + assert.equal(appointment['_' + name], 'xyz', 'setter for ' + name + ' is OK'); + }); + } finally { + repaintStub.restore(); + } + }); + + QUnit.test('Data expressions should be recompiled on optionChanged and passed to appointmentModel', function(assert) { + this.createInstance(); + const repaintStub = sinon.stub(this.instance, 'repaint'); + + try { + const appointmentModel = this.instance.getAppointmentModel(); + + this.instance.option({ + 'startDateExpr': '_startDate', + 'endDateExpr': '_endDate', + 'startDateTimeZoneExpr': '_startDateTimeZone', + 'endDateTimeZoneExpr': '_endDateTimeZone', + 'textExpr': '_text', + 'descriptionExpr': '_description', + 'allDayExpr': '_allDay', + 'recurrenceRuleExpr': '_recurrenceRule', + 'recurrenceExceptionExpr': '_recurrenceException' + }); + + const dataAccessors = this.instance._dataAccessors; + + assert.deepEqual($.extend({ resources: {} }, dataAccessors.getter), appointmentModel._dataAccessors.getter, 'dataAccessors getters were passed to appointmentModel'); + assert.deepEqual($.extend({ resources: {} }, dataAccessors.setter), appointmentModel._dataAccessors.setter, 'dataAccessors setters were passed to appointmentModel'); + assert.deepEqual(dataAccessors.expr, appointmentModel._dataAccessors.expr, 'dataExpressions were passed to appointmentModel'); + } finally { + repaintStub.restore(); + } + }); + + QUnit.test('Appointment should be rendered correctly after expression changing', function(assert) { + this.createInstance({ + dataSource: [{ + text: 'a', + StartDate: new Date(2015, 6, 8, 8, 0), + endDate: new Date(2015, 6, 8, 17, 0), + allDay: true + }], + currentDate: new Date(2015, 6, 8) + }); + + this.instance.option('startDateExpr', 'StartDate'); + this.clock.tick(); + assert.equal(this.instance.$element().find('.dx-scheduler-appointment').length, 1, 'Appointment is rendered'); + }); + + QUnit.test('Sheduler should be repainted after data expression option changing', function(assert) { + this.createInstance(); + const repaintStub = sinon.stub(this.instance, 'repaint'); + + try { + this.instance.option({ + 'startDateExpr': '_startDate', + 'endDateExpr': '_endDate', + 'startDateTimeZoneExpr': '_startDateTimeZone', + 'endDateTimeZoneExpr': '_endDateTimeZone', + 'textExpr': '_text', + 'descriptionExpr': '_description', + 'allDayExpr': '_allDay', + 'recurrenceRuleExpr': '_recurrenceRule', + 'recurrenceExceptionExpr': '_recurrenceException' + }); + + assert.equal(repaintStub.callCount, 9, 'Scheduler was repainted'); + } finally { + repaintStub.restore(); } }); + QUnit.test('Sheduler should have correct default template after data expression option changing', function(assert) { + this.createInstance({ + dataSource: [{ + text: 'a', + TEXT: 'New Text', + startDate: new Date(2015, 6, 8, 8, 0), + endDate: new Date(2015, 6, 8, 17, 0), + allDay: true + }], + currentDate: new Date(2015, 6, 8) + }); + + this.instance.option({ + textExpr: 'TEXT' + }); + + assert.equal(this.instance.$element().find('.dx-scheduler-appointment-title').eq(0).text(), 'New Text', 'Appointment template is correct'); + }); + QUnit.test('Changing of \'currentView\' option after initializing should work correctly', function(assert) { this.createInstance({ currentDate: new Date(2018, 0, 30), @@ -1592,7 +1671,7 @@ QUnit.testStart(function() { currentDate: new Date(2015, 1, 9), currentView: 'month', dataSource: data, - maxAppointmentsPerCell: null, + maxAppointmentsPerCell: 2, height: 500, width: 800 }); @@ -1823,8 +1902,6 @@ QUnit.testStart(function() { }); assert.equal(this.instance.option('maxAppointmentsPerCell'), 'auto', 'Default Option value is right'); - const $workSpace = this.instance.getWorkSpace().$element(); - assert.ok($workSpace.hasClass('dx-scheduler-work-space-overlapping'), 'workspace has right class'); }); QUnit.test('cellDuration is passed to workspace', function(assert) { @@ -2080,6 +2157,138 @@ QUnit.testStart(function() { assert.equal(resourceCounter, 2, 'Resources was reloaded one more time after dataSource option changing'); }); + + [ + { startDayHour: 0, endDayHour: 0 }, + { startDayHour: 2, endDayHour: 0 } + ].forEach(dayHours => { + QUnit.test(`Generate error if option changed to startDayHour: ${dayHours.startDayHour} >= endDayHour: ${dayHours.endDayHour}`, function(assert) { + this.createInstance({ + currentDate: new Date(2015, 4, 24), + views: ['day'], + currentView: 'day', + startDayHour: 8, + endDayHour: 12 + }); + + assert.throws( + () => { + this.instance.option('startDayHour', dayHours.startDayHour); + this.instance.option('endDayHour', dayHours.endDayHour); + }, + e => /E1058/.test(e.message), + 'E1058 Error message' + ); + }); + + QUnit.test(`Generate error if workSpace option changed to startDayHour: ${dayHours.startDayHour} >= endDayHour: ${dayHours.endDayHour}`, function(assert) { + this.createInstance({ + currentDate: new Date(2015, 4, 24), + views: [{ + name: 'day', + type: 'day' + }], + currentView: 'day', + startDayHour: 8, + endDayHour: 12 + }); + + assert.throws( + () => { + const instance = this.instance; + instance.option('views[0].startDayHour', dayHours.startDayHour); + instance.option('views[0].endDayHour', dayHours.endDayHour); + }, + e => /E1058/.test(e.message), + 'E1058 Error message' + ); + }); + + QUnit.test(`Generate error if currentView changed to view.startDayHour: ${dayHours.startDayHour} >= view.endDayHour: ${dayHours.endDayHour}`, function(assert) { + this.createInstance({ + currentDate: new Date(2015, 4, 24), + dataSource: [ + { + startDate: new Date(2015, 4, 24, 0), + endDate: new Date(2015, 4, 24, 2), + allDay: true + } + ], + views: [{ + name: 'day', + type: 'day' + }, { + name: 'week', + type: 'week', + startDayHour: dayHours.startDayHour, + endDayHour: dayHours.endDayHour + }], + currentView: 'day', + startDayHour: 8, + endDayHour: 12 + }); + + assert.throws( + () => { + this.instance.option('currentView', 'week'); + }, + e => /E1058/.test(e.message), + 'E1058 Error message' + ); + }); + }); + + [ + { startDayHour: 0, endDayHour: 24, cellDuration: 95 }, + { startDayHour: 8, endDayHour: 24, cellDuration: 90 } + ].forEach(config => { + QUnit.test(`Options changing, generate warning if cellDuration: ${config.cellDuration} could not divide the range from startDayHour: ${config.startDayHour} to the endDayHour: ${config.endDayHour} into even intervals`, function(assert) { + this.createInstance({ + currentDate: new Date(2015, 4, 24), + views: ['day'], + currentView: 'day', + startDayHour: 8, + endDayHour: 12 + }); + this.instance.option({ + startDayHour: config.startDayHour, + endDayHour: config.endDayHour, + cellDuration: config.cellDuration + }); + + assert.equal(errors.log.callCount, 1, 'warning has been called once'); + assert.equal(errors.log.getCall(0).args[0], 'W1015', 'warning has correct error id'); + }); + }); + + [ + { currentView: 'WEEK1' }, + { currentView: 'WEEK2' } + ].forEach(view => { + QUnit.test(`View changing, generate warning if cellDuration: ${config.cellDuration} could not divide the range from startDayHour: ${config.startDayHour} to the endDayHour: ${config.endDayHour} into even intervals`, function(assert) { + this.createInstance({ + currentDate: new Date(2015, 4, 24), + views: ['day', + { + type: 'week', + name: 'WEEK1', + cellDuration: 7 + }, + { + type: 'week', + name: 'WEEK2', + cellDuration: 95 + }], + currentView: 'day', + startDayHour: 8, + endDayHour: 24 + }); + this.instance.option('currentView', view.currentView); + + assert.equal(errors.log.callCount, 1, 'warning has been called once'); + assert.equal(errors.log.getCall(0).args[0], 'W1015', 'warning has correct error id'); + }); + }); })('Options'); (function() { @@ -2816,8 +3025,8 @@ QUnit.testStart(function() { }], }), views: ['month'], - maxAppointmentsPerCell: null, currentView: 'month', + maxAppointmentsPerCell: 1, onAppointmentRendered: function(args) { assert.equal($(args.appointmentElement).find('.dx-scheduler-appointment-reduced-icon').length, 1, 'Appointment reduced icon is applied'); }, @@ -2860,8 +3069,8 @@ QUnit.testStart(function() { }], }), views: ['month'], - maxAppointmentsPerCell: null, currentView: 'month', + height: 600, onAppointmentRendered: function(args) { assert.ok(true, 'Appointment was rendered'); }, @@ -2894,7 +3103,6 @@ QUnit.testStart(function() { views: ['timelineWeek'], currentView: 'timelineWeek', cellDuration: 60, - maxAppointmentsPerCell: null, onAppointmentRendered: function(args) { assert.ok(true, 'Appointment was rendered'); }, @@ -3047,8 +3255,8 @@ QUnit.testStart(function() { }), views: ['month'], currentView: 'month', - maxAppointmentsPerCell: null, currentDate: new Date(2015, 2, 9), + height: 600, onAppointmentClick: function(e) { assert.deepEqual(isRenderer(e.appointmentElement), !!config().useJQuery, 'appointmentElement is correct'); assert.deepEqual($(e.appointmentElement)[0], $item[0], 'appointmentElement is correct'); @@ -3124,7 +3332,6 @@ QUnit.testStart(function() { currentDate: new Date(2015, 2, 9), startDateExpr: 'start.date', endDateExpr: 'end.date', - maxAppointmentsPerCell: null, recurrenceRuleExpr: 'recurrence.rule', onAppointmentClick: function(e) { const targetedAppointmentData = e.targetedAppointmentData; @@ -3225,7 +3432,7 @@ QUnit.testStart(function() { }), views: ['month'], currentView: 'month', - maxAppointmentsPerCell: null, + height: 600, currentDate: new Date(2015, 2, 9), onAppointmentContextMenu: function(e) { assert.deepEqual(isRenderer(e.appointmentElement), !!config().useJQuery, 'appointmentElement is correct'); @@ -3849,7 +4056,6 @@ QUnit.testStart(function() { this.createInstance({ width: 300, currentView: 'week', - maxAppointmentsPerCell: null, dataSource: [{ text: 'a', startDate: new Date(2015, 6, 5, 0, 0), @@ -4293,29 +4499,6 @@ QUnit.testStart(function() { assert.ok(result, 'Appointment takes all day'); }); - QUnit.test('Workspace should have an specific class if view.maxAppointmentsPerCell is set', function(assert) { - this.createInstance({ - currentView: 'Week', - views: [{ - type: 'week', - name: 'Week', - maxAppointmentsPerCell: 3 - }, - { - type: 'day', - name: 'day', - maxAppointmentsPerCell: null - }] - }); - - let $workSpace = this.instance.getWorkSpace().$element(); - assert.ok($workSpace.hasClass('dx-scheduler-work-space-overlapping'), 'workspace has correct class'); - - this.instance.option('currentView', 'day'); - $workSpace = this.instance.getWorkSpace().$element(); - assert.notOk($workSpace.hasClass('dx-scheduler-work-space-overlapping'), 'workspace hasn\'t class'); - }); - QUnit.module('Options for Material theme in components', { beforeEach: function() { this.origIsMaterial = themes.isMaterial; diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/contentReadyEvent.js b/testing/tests/DevExpress.ui.widgets.scheduler/contentReadyEvent.js index b791ede0ddcd..53dae9bc062a 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/contentReadyEvent.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/contentReadyEvent.js @@ -158,7 +158,6 @@ QUnit.module('onContentReady event', moduleConfig, () => { currentView: 'week', width: 800, dataSource: dataSource, - maxAppointmentsPerCell: null, onContentReady: e => { const element = e.component; const $header = element.getHeader().$element(); @@ -180,7 +179,6 @@ QUnit.module('onContentReady event', moduleConfig, () => { currentDate: new Date(2016, 2, 15), views: ['week'], currentView: 'week', - maxAppointmentsPerCell: null, width: 800, dataSource: [] }); @@ -209,7 +207,6 @@ QUnit.module('onContentReady event', moduleConfig, () => { currentDate: new Date(2016, 2, 15), views: ['week'], currentView: 'week', - maxAppointmentsPerCell: null, width: 800, dataSource: [appointment] }); diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/helpers.js b/testing/tests/DevExpress.ui.widgets.scheduler/helpers.js index 50a57650213c..387a41c3baa0 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/helpers.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/helpers.js @@ -9,6 +9,13 @@ export const TOOLBAR_BOTTOM_LOCATION = 'bottom'; const SCHEDULER_ID = 'scheduler'; const TEST_ROOT_ELEMENT_ID = 'qunit-fixture'; +export const CLASSES = { + resizableHandle: { + left: '.dx-resizable-handle-left', + right: '.dx-resizable-handle-right' + } +}; + export const initTestMarkup = () => $(`#${TEST_ROOT_ELEMENT_ID}`).html(`
Task Template
`); export const createWrapper = (option) => new SchedulerTestWrapper($(`#${SCHEDULER_ID}`).dxScheduler(option).dxScheduler('instance')); @@ -205,7 +212,7 @@ export class SchedulerTestWrapper { } return this.workSpace.getCells().eq(rowIndex); }, - + getCellPosition: (rowIndex, cellIndex) => this.workSpace.getCell(rowIndex, cellIndex).position(), getAllDayCells: () => $('.dx-scheduler-all-day-table-cell'), getAllDayCell: (index) => this.workSpace.getAllDayCells().eq(index), getCellWidth: () => this.workSpace.getCells().eq(0).outerWidth(), @@ -231,12 +238,21 @@ export class SchedulerTestWrapper { this.navigator = { getNavigator: () => $('.dx-scheduler-navigator'), - getCaption: () => $('.dx-scheduler-navigator').find('.dx-scheduler-navigator-caption').text(), + getCaptionElement: () => { + return this.navigator.getNavigator().find('.dx-scheduler-navigator-caption'); + }, + getCaption: () => this.navigator.getCaptionElement().text(), clickOnPrevButton: () => { this.navigator.getNavigator().find('.dx-scheduler-navigator-previous').trigger('dxclick'); }, clickOnNextButton: () => { this.navigator.getNavigator().find('.dx-scheduler-navigator-next').trigger('dxclick'); + }, + click: () => { + this.navigator.getCaptionElement().trigger('dxclick'); + }, + isPopupVisible: () => { + return $('.dx-scheduler-navigator-calendar-popover > .dx-overlay-content').is(':visible'); } }, diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.RTL.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.RTL.tests.js index e789d43438bd..cfab977238c8 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.RTL.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.RTL.tests.js @@ -111,8 +111,7 @@ module('RTL', moduleConfig, () => { startDate: new Date(2015, 1, 9, 1, 0), endDate: new Date(2015, 1, 9, 2, 0) }], - rtlEnabled: true, - maxAppointmentsPerCell: null + rtlEnabled: true }); }; @@ -122,7 +121,7 @@ module('RTL', moduleConfig, () => { const cell = scheduler.workSpace.getCell(8); const appointment = scheduler.appointments.getAppointment(); - assert.equal(appointment.position().left + appointment.outerWidth(), cell.position().left + cell.outerWidth(), 'task position is correct'); + assert.roughEqual(appointment.position().left + appointment.outerWidth(), cell.position().left + cell.outerWidth(), 1.1, 'task position is correct'); }); test('Week view', function(assert) { @@ -131,7 +130,7 @@ module('RTL', moduleConfig, () => { const cell = scheduler.workSpace.getCell(1); const appointment = scheduler.appointments.getAppointment(); - assert.equal(Math.round(appointment.position().left + appointment.outerWidth()), Math.round(cell.position().left + cell.outerWidth()), 'task position is correct'); + assert.roughEqual(Math.round(appointment.position().left + appointment.outerWidth()), Math.round(cell.position().left + cell.outerWidth()), 1.1, 'task position is correct'); }); test('Month view', function(assert) { @@ -140,7 +139,7 @@ module('RTL', moduleConfig, () => { const cell = scheduler.workSpace.getCell(1); const appointment = scheduler.appointments.getAppointment(); - assert.roughEqual(appointment.position().left + appointment.outerWidth(), cell.position().left + cell.outerWidth(), 1, 'task position is correct'); + assert.roughEqual(appointment.position().left + appointment.outerWidth(), cell.position().left + cell.outerWidth(), 1.1, 'task position is correct'); }); }); }); diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.allDayAppointments.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.allDayAppointments.tests.js index f2fe6ad96294..a85c1f4740f3 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.allDayAppointments.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.allDayAppointments.tests.js @@ -9,7 +9,6 @@ import ArrayStore from 'data/array_store'; import CustomStore from 'data/custom_store'; import Query from 'data/query'; import dataUtils from 'core/element_data'; -import devices from 'core/devices'; import { SchedulerTestWrapper } from './helpers.js'; import 'common.css!'; @@ -23,19 +22,10 @@ QUnit.testStart(function() {
'); }); -const APPOINTMENT_DEFAULT_OFFSET = 25; -const APPOINTMENT_MOBILE_OFFSET = 50; - -function getOffset() { - if(devices.current().deviceType !== 'desktop') { - return APPOINTMENT_MOBILE_OFFSET; - } else { - return APPOINTMENT_DEFAULT_OFFSET; - } -} +const APPOINTMENT_DEFAULT_LEFT_OFFSET = 26; const createInstance = function(options) { - const instance = $('#scheduler').dxScheduler($.extend(options, { maxAppointmentsPerCell: null })).dxScheduler('instance'); + const instance = $('#scheduler').dxScheduler(options).dxScheduler('instance'); return new SchedulerTestWrapper(instance); }; @@ -43,7 +33,7 @@ QUnit.module('Integration: allDay appointments', { beforeEach: function() { fx.off = true; this.createInstance = function(options) { - this.instance = $('#scheduler').dxScheduler($.extend(options, { maxAppointmentsPerCell: null })).dxScheduler('instance'); + this.instance = $('#scheduler').dxScheduler(options).dxScheduler('instance'); }; this.clock = sinon.useFakeTimers(); @@ -330,7 +320,8 @@ QUnit.test('Height of appointment should be correct after dragged into the all d currentDate: new Date(2015, 1, 9), dataSource: data, currentView: 'week', - editing: true + editing: true, + maxAppointmentsPerCell: 'unlimited' }); const $element = $(this.instance.$element()); @@ -371,23 +362,19 @@ QUnit.test('Height of allDay appointment should be correct, 3 appts in cell', fu ] }); - this.createInstance({ + const scheduler = createInstance({ currentDate: new Date(2015, 1, 9), dataSource: data, currentView: 'week', - editing: true + editing: true, + maxAppointmentsPerCell: 2 }); - const $element = $(this.instance.$element()); - const $appointments = $element.find('.dx-scheduler-appointment'); - const firstPosition = translator.locate($appointments.eq(0)); + assert.roughEqual(scheduler.appointments.getAppointmentHeight(0), 25, 1.5, 'Appointment has correct height'); + assert.roughEqual(scheduler.appointments.getAppointmentHeight(1), 25, 1.5, 'Appointment has correct height'); + assert.roughEqual(scheduler.appointments.getAppointmentPosition(0).top, 25, 1.5, 'Appointment has correct top'); - assert.roughEqual($appointments.eq(0).outerHeight(), 25, 1.5, 'Appointment has correct height'); - assert.roughEqual($appointments.eq(1).outerHeight(), 25, 1.5, 'Appointment has correct height'); - assert.roughEqual(firstPosition.top, 25, 1.5, 'Appointment has correct top'); - - assert.roughEqual($appointments.eq(2).outerWidth(), 15, 1.1, 'Compact appointment has correct width'); - assert.roughEqual($appointments.eq(2).outerHeight(), 15, 1.1, 'Compact appointment has correct height'); + assert.equal(scheduler.appointments.compact.getButtonCount(), 1, 'Appointment collector is rendered'); }); QUnit.test('allDayExpanded option of workspace should be updated after dragged off from the all day container', function(assert) { @@ -741,12 +728,7 @@ QUnit.test('DropDown appointment should be removed correctly when needed', funct const items = [ { text: '1', startDate: new Date(2015, 4, 25), endDate: new Date(2015, 4, 25, 1), allDay: true }, { text: '2', startDate: new Date(2015, 4, 25), endDate: new Date(2015, 4, 25, 1), allDay: true }, - { text: '3', startDate: new Date(2015, 4, 25), endDate: new Date(2015, 4, 25, 1), allDay: true }, - { text: '4', startDate: new Date(2015, 4, 25), endDate: new Date(2015, 4, 25, 1), allDay: true }, - { text: '5', startDate: new Date(2015, 4, 25), endDate: new Date(2015, 4, 25, 1), allDay: true }, - { text: '6', startDate: new Date(2015, 4, 25), endDate: new Date(2015, 4, 25, 1), allDay: true }, - { text: '7', startDate: new Date(2015, 4, 25), endDate: new Date(2015, 4, 25, 1), allDay: true }, - { text: '8', startDate: new Date(2015, 4, 25), endDate: new Date(2015, 4, 25, 1), allDay: true } + { text: '3', startDate: new Date(2015, 4, 25), endDate: new Date(2015, 4, 25, 1), allDay: true } ]; this.instance.option('dataSource', items); @@ -754,7 +736,7 @@ QUnit.test('DropDown appointment should be removed correctly when needed', funct let $dropDown = this.instance.$element().find('.dx-scheduler-appointment-collector'); assert.equal($dropDown.length, 1, 'Dropdown appointment was rendered'); - this.instance.deleteAppointment(items[7]); + this.instance.deleteAppointment(items[2]); $dropDown = this.instance.$element().find('.dx-scheduler-appointment-collector'); assert.equal($dropDown.length, 0, 'Dropdown appointment was removed'); @@ -790,7 +772,8 @@ QUnit.test('New allDay appointment should have correct height', function(assert) currentDate: new Date(2015, 2, 10), dataSource: data, currentView: 'week', - showAllDayPanel: true + showAllDayPanel: true, + maxAppointmentsPerCell: 'unlimited' }); const newItem = { startDate: new Date(2015, 2, 10, 1), allDay: true, text: 'caption', endDate: new Date(2015, 2, 10, 1, 30) }; @@ -849,7 +832,8 @@ QUnit.test('AllDay appointment is visible on month view, if showAllDayPanel = fa }], currentView: 'week', views: ['day', 'week', 'month'], - showAllDayPanel: false + showAllDayPanel: false, + maxAppointmentsPerCell: 1 }); assert.equal(this.instance.$element().find('.dx-scheduler-all-day-appointment').length, 0, 'AllDay appointments are not visible on \'week\' view'); @@ -866,7 +850,8 @@ QUnit.test('AllDay appointment should have correct height', function(assert) { currentDate: new Date(2015, 2, 10), dataSource: [appointment], currentView: 'week', - showAllDayPanel: true + showAllDayPanel: true, + maxAppointmentsPerCell: 'unlimited' }); const appointmentHeight = $(this.instance.$element()).find('.dx-scheduler-all-day-appointment').outerHeight(); @@ -904,7 +889,8 @@ QUnit.test('AllDay appointment should have correct height after changing view', currentDate: new Date(2015, 2, 4), dataSource: [appointment], currentView: 'week', - showAllDayPanel: true + showAllDayPanel: true, + maxAppointmentsPerCell: 'unlimited' }); const allDayPanelHeight = $(this.instance.$element()).find('.dx-scheduler-all-day-table-cell').eq(0).get(0).getBoundingClientRect().height; @@ -1069,6 +1055,7 @@ QUnit.test('AllDay appointment should be displayed correctly after changing view this.createInstance({ currentDate: new Date(2015, 2, 4), currentView: 'day', + maxAppointmentsPerCell: 'unlimited', dataSource: new DataSource({ store: new CustomStore({ load: function(options) { @@ -1102,6 +1089,7 @@ QUnit.test('AllDay appointment should be displayed correctly after changing date this.createInstance({ currentDate: new Date(2015, 2, 4), currentView: 'day', + maxAppointmentsPerCell: 'unlimited', dataSource: new DataSource({ store: new CustomStore({ load: function(options) { @@ -1198,7 +1186,8 @@ QUnit.test('DblClick on appointment should call scheduler.showAppointmentPopup f this.createInstance({ currentDate: new Date(2015, 2, 4), currentView: 'month', - dataSource: data + dataSource: data, + maxAppointmentsPerCell: 1 }); this.clock.tick(); @@ -1227,42 +1216,43 @@ QUnit.test('AllDay appointment has right startDate and endDate', function(assert }); QUnit.test('All-day & common appointments should have a right sorting', function(assert) { - this.createInstance({ + const scheduler = createInstance({ currentDate: new Date(2016, 1, 10), currentView: 'day', width: 800, + maxAppointmentsPerCell: 3, dataSource: [ { - text: 'Full 1', + text: 'A', startDate: new Date(2016, 1, 10, 9, 0), endDate: new Date(2016, 1, 10, 11, 30), allDay: true }, { - text: 'Full 2', + text: 'B', startDate: new Date(2016, 1, 10, 12, 0), endDate: new Date(2016, 1, 10, 13, 0), allDay: true }, { - text: 'Short 1', + text: 'C', startDate: new Date(2016, 1, 10, 12, 0), endDate: new Date(2016, 1, 10, 13, 0), allDay: true }, { - text: 'Short 2', + text: 'D', startDate: new Date(2016, 1, 10, 12, 0), endDate: new Date(2016, 1, 10, 13, 0), allDay: true }, { - text: 'Short 3', + text: 'E', startDate: new Date(2016, 1, 10, 12, 0), endDate: new Date(2016, 1, 10, 13, 0), allDay: true }, { - text: 'Short 4', + text: 'F', startDate: new Date(2016, 1, 10, 12, 0), endDate: new Date(2016, 1, 10, 13, 0), allDay: true @@ -1275,72 +1265,16 @@ QUnit.test('All-day & common appointments should have a right sorting', function ] }); - const $element = $(this.instance.$element()); - const $appointments = $element.find('.dx-scheduler-appointment-compact'); - const $simpleAppointment = $element.find('.dx-scheduler-appointment').last(); - const cellWidth = $element.find('.dx-scheduler-date-table-cell').outerWidth(); - const offset = getOffset(); - - assert.equal(dataUtils.data($appointments.get(0), 'dxItemData').text, 'Short 1', 'Data is right'); - assert.equal(dataUtils.data($appointments.get(1), 'dxItemData').text, 'Short 2', 'Data is right'); - assert.equal(dataUtils.data($appointments.get(2), 'dxItemData').text, 'Short 3', 'Data is right'); - assert.equal(dataUtils.data($appointments.get(3), 'dxItemData').text, 'Short 4', 'Data is right'); - - assert.roughEqual(translator.locate($simpleAppointment).left, 100, 1.001, 'Appointment position is OK'); - assert.roughEqual($simpleAppointment.outerWidth(), cellWidth - offset, 1.001, 'Appointment size is OK'); -}); - -QUnit.test('All-day appointments should have a right sorting', function(assert) { - this.createInstance({ - currentDate: new Date(2016, 1, 10), - currentView: 'day', - width: 800, - dataSource: [ - { - text: 'Full 1', - startDate: new Date(2016, 1, 10, 9, 0), - endDate: new Date(2016, 1, 10, 11, 30), - allDay: true - }, { - text: 'Full 2', - startDate: new Date(2016, 1, 10, 12, 0), - endDate: new Date(2016, 1, 10, 13, 0), - allDay: true - }, - { - text: 'Short 1', - startDate: new Date(2016, 1, 10, 12, 0), - endDate: new Date(2016, 1, 10, 13, 0), - allDay: true - }, - - { - text: 'Short 2', - startDate: new Date(2016, 1, 10, 12, 0), - endDate: new Date(2016, 1, 10, 13, 0), - allDay: true - }, { - text: 'Short 3', - startDate: new Date(2016, 1, 10, 12, 0), - endDate: new Date(2016, 1, 10, 13, 0), - allDay: true - }, - { - text: 'Short 4', - startDate: new Date(2016, 1, 10, 12, 0), - endDate: new Date(2016, 1, 10, 13, 0), - allDay: true - } - ] - }); + const cellWidth = scheduler.workSpace.getCellWidth(); - const $element = $(this.instance.$element()); - const $appointments = $element.find('.dx-scheduler-appointment-compact'); + assert.equal(scheduler.appointments.getTitleText(0), 'A', 'Text is right'); + assert.equal(scheduler.appointments.getTitleText(1), 'B', 'Text is right'); + assert.equal(scheduler.appointments.getTitleText(2), 'C', 'Text is right'); + assert.equal(scheduler.appointments.getTitleText(3), 'Simple appointment', 'Text is right'); - assert.equal(dataUtils.data($appointments.get(0), 'dxItemData').text, 'Short 1', 'Data is right'); - assert.equal(dataUtils.data($appointments.get(1), 'dxItemData').text, 'Short 2', 'Data is right'); - assert.equal(dataUtils.data($appointments.get(2), 'dxItemData').text, 'Short 3', 'Data is right'); - assert.equal(dataUtils.data($appointments.get(3), 'dxItemData').text, 'Short 4', 'Data is right'); + assert.roughEqual(scheduler.appointments.getAppointmentPosition(3).left, 100, 1.001, 'Appointment position is OK'); + assert.roughEqual(scheduler.appointments.getAppointmentPosition(3).top, 100, 1.001, 'Appointment position is OK'); + assert.roughEqual(scheduler.appointments.getAppointmentWidth(3), cellWidth - APPOINTMENT_DEFAULT_LEFT_OFFSET, 1.001, 'Appointment size is OK'); }); QUnit.test('dropDown appointment should have correct container & position', function(assert) { @@ -1406,6 +1340,7 @@ QUnit.test('AllDay appointments should have correct height, groupOrientation = v groupOrientation: 'vertical', currentView: 'day', showAllDayPanel: true, + maxAppointmentsPerCell: 'unlimited', groups: ['ownerId'], resources: [ { diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentCollector.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentCollector.tests.js index a9c09b739d49..d038f07f5cf2 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentCollector.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentCollector.tests.js @@ -879,6 +879,8 @@ QUnit.module('Integration: Appointments Collector, adaptivityEnabled = true', { QUnit.test('Ordinary appointment count depends on scheduler width on week view', function(assert) { this.createInstance(); + this.instance.option('width', 600); + this.instance.option('dataSource', [{ startDate: new Date(2019, 2, 4), text: 'a', endDate: new Date(2019, 2, 4, 0, 30) }, { startDate: new Date(2019, 2, 4), text: 'b', endDate: new Date(2019, 2, 4, 0, 30) }]); this.instance.option('currentView', 'week'); @@ -899,12 +901,14 @@ QUnit.module('Integration: Appointments Collector, adaptivityEnabled = true', { QUnit.test('Ordinary appointments should have correct sizes on week view', function(assert) { this.createInstance(); + this.instance.option('width', 700); + this.instance.option('dataSource', [{ startDate: new Date(2019, 2, 4), text: 'a', endDate: new Date(2019, 2, 4, 0, 30) }, { startDate: new Date(2019, 2, 4), text: 'b', endDate: new Date(2019, 2, 4, 0, 30) }]); this.instance.option('currentView', 'week'); const $appointment = this.scheduler.appointments.getAppointment(0); - assert.roughEqual($appointment.outerWidth(), 70, 1.001, 'Width is OK'); + assert.roughEqual($appointment.outerWidth(), 50, 1.001, 'Width is OK'); assert.roughEqual($appointment.outerHeight(), 50, 1.001, 'Height is OK'); this.instance.option('width', 1000); @@ -922,6 +926,8 @@ QUnit.module('Integration: Appointments Collector, adaptivityEnabled = true', { QUnit.test('Adaptive collector should have correct coordinates on week view', function(assert) { this.createInstance(); + this.instance.option('width', 700); + this.instance.option('dataSource', [{ startDate: new Date(2019, 2, 4), text: 'a', endDate: new Date(2019, 2, 4, 0, 30) }, { startDate: new Date(2019, 2, 4), text: 'b', endDate: new Date(2019, 2, 4, 0, 30) }]); this.instance.option('currentView', 'week'); @@ -955,6 +961,8 @@ QUnit.module('Integration: Appointments Collector, adaptivityEnabled = true', { QUnit.test('Adaptive collector should have correct sizes on week view', function(assert) { this.createInstance(); + this.instance.option('width', 700); + this.instance.option('dataSource', [{ startDate: new Date(2019, 2, 4), text: 'a', endDate: new Date(2019, 2, 4, 0, 30) }, { startDate: new Date(2019, 2, 4), text: 'b', endDate: new Date(2019, 2, 4, 0, 30) }]); this.instance.option('currentView', 'week'); diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js index bef507d7eee2..c7039e736c2e 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js @@ -502,7 +502,7 @@ module('Integration: Appointment tooltip', moduleConfig, () => { store: getSampleData() }); - const scheduler = createScheduler({ currentDate: new Date(2015, 1, 9), dataSource: data, currentView: 'month', maxAppointmentsPerCell: null }); + const scheduler = createScheduler({ currentDate: new Date(2015, 1, 9), dataSource: data, currentView: 'month' }); scheduler.appointments.click(1); diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointments.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointments.tests.js index c798b04ca521..b66e8adbbf39 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointments.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointments.tests.js @@ -8,14 +8,13 @@ import fx from 'animation/fx'; import pointerMock from '../../helpers/pointerMock.js'; import Color from 'color'; import tooltip from 'ui/tooltip/ui.tooltip'; -import devices from 'core/devices'; import config from 'core/config'; import dragEvents from 'events/drag'; import { DataSource } from 'data/data_source/data_source'; import CustomStore from 'data/custom_store'; import dataUtils from 'core/element_data'; import dateSerialization from 'core/utils/date_serialization'; -import { SchedulerTestWrapper, initTestMarkup, createWrapper } from './helpers.js'; +import { SchedulerTestWrapper, initTestMarkup, createWrapper, CLASSES } from './helpers.js'; import 'ui/scheduler/ui.scheduler'; import 'ui/switch'; @@ -27,17 +26,8 @@ QUnit.testStart(() => initTestMarkup()); const DATE_TABLE_CELL_CLASS = 'dx-scheduler-date-table-cell'; const APPOINTMENT_CLASS = 'dx-scheduler-appointment'; -const APPOINTMENT_DEFAULT_OFFSET = 25; -const APPOINTMENT_MOBILE_OFFSET = 50; - - -function getOffset() { - if(devices.current().deviceType !== 'desktop') { - return APPOINTMENT_MOBILE_OFFSET; - } else { - return APPOINTMENT_DEFAULT_OFFSET; - } -} +const APPOINTMENT_DEFAULT_LEFT_OFFSET = 26; +const APPOINTMENT_DEFAULT_TOP_OFFSET = 26; QUnit.module('T712431', () => { // TODO: there is a test for T712431 bug, when replace table layout on div layout, the test will also be useless @@ -71,7 +61,7 @@ QUnit.module('Integration: Appointments', { beforeEach: function() { fx.off = true; this.createInstance = function(options) { - this.instance = $('#scheduler').dxScheduler($.extend(options, { maxAppointmentsPerCell: options && options.maxAppointmentsPerCell || null })).dxScheduler('instance'); + this.instance = $('#scheduler').dxScheduler($.extend(options, { height: options && options.height || 600 })).dxScheduler('instance'); this.clock.tick(300); this.instance.focus(); @@ -454,11 +444,11 @@ QUnit.test('Scheduler tasks should have a right dimensions for month view', func }); this.clock.tick(); - const $appointment = $(this.instance.$element()).find('.' + APPOINTMENT_CLASS).eq(0); - const $cell = $(this.instance.$element()).find('.' + DATE_TABLE_CELL_CLASS).eq(0); + const cellHeight = this.scheduler.workSpace.getCellHeight(); + const cellWidth = this.scheduler.workSpace.getCellWidth(); - assert.roughEqual($appointment.height(), $cell.outerHeight() * 0.6 / 2, 2, 'Task has a right height'); - assert.roughEqual($appointment.outerWidth(), $cell.outerWidth(), 1.001, 'Task has a right width'); + assert.roughEqual(this.scheduler.appointments.getAppointmentHeight(0), (cellHeight - 30) / 4, 2, 'Task has a right height'); + assert.roughEqual(this.scheduler.appointments.getAppointmentWidth(0), cellWidth, 1.001, 'Task has a right width'); }); QUnit.test('Scheduler tasks should have a right height when currentView is changed', function(assert) { @@ -474,11 +464,11 @@ QUnit.test('Scheduler tasks should have a right height when currentView is chang this.instance.option('currentView', 'month'); - const $appointment = $(this.instance.$element()).find('.' + APPOINTMENT_CLASS).eq(0); - const $cell = $(this.instance.$element()).find('.' + DATE_TABLE_CELL_CLASS).eq(0); + const cellHeight = this.scheduler.workSpace.getCellHeight(); + const cellWidth = this.scheduler.workSpace.getCellWidth(); - assert.roughEqual($appointment.height(), $cell.outerHeight() * 0.6 / 2, 2, 'Task has a right height'); - assert.roughEqual($appointment.outerWidth(), $cell.outerWidth(), 1.001, 'Task has a right width'); + assert.roughEqual(this.scheduler.appointments.getAppointmentHeight(0), (cellHeight - 30) / 4, 2, 'Task has a right height'); + assert.roughEqual(this.scheduler.appointments.getAppointmentWidth(0), cellWidth, 1.001, 'Task has a right width'); }); QUnit.test('Short tasks should have a right height (T725948)', function(assert) { @@ -513,6 +503,7 @@ QUnit.test('Two not rival appointments with fractional coordinates should have c currentDate: new Date(2015, 1, 9), views: ['month'], currentView: 'month', + height: 600, width: 720 }); @@ -681,6 +672,7 @@ QUnit.test('Appointments should be rendered correctly when resourses store is as views: ['month'], dataSource: appointments, width: 840, + height: 600, currentView: 'month', firstDayOfWeek: 1, groups: ['roomId'], @@ -1933,15 +1925,14 @@ QUnit.test('Task with resources should contain a right data attr if field contai }); QUnit.test('Appointment width should depend on cell width', function(assert) { - this.createInstance({ - currentDate: new Date(2015, 2, 18) + currentDate: new Date(2015, 2, 18), + maxAppointmentsPerCell: 'auto' }); const workSpace = this.instance.getWorkSpace(); const defaultGetCellWidthMethod = workSpace.getCellWidth; const CELL_WIDTH = 777; - const offset = getOffset(); workSpace.getCellWidth = function() { return CELL_WIDTH; @@ -1951,7 +1942,7 @@ QUnit.test('Appointment width should depend on cell width', function(assert) { { id: 1, text: 'Item 1', startDate: new Date(2015, 2, 18), endDate: new Date(2015, 2, 18, 0, 30) } ]); - assert.equal(this.instance.$element().find('.' + APPOINTMENT_CLASS).first().outerWidth(), CELL_WIDTH - offset, 'Appointment width is OK'); + assert.equal(this.scheduler.appointments.getAppointmentWidth(), CELL_WIDTH - APPOINTMENT_DEFAULT_LEFT_OFFSET, 'Appointment width is OK'); } finally { workSpace.getCellWidth = defaultGetCellWidthMethod; @@ -2412,14 +2403,14 @@ QUnit.test('Appointment should have right position on timeline month view', func currentDate: new Date(2016, 1, 1), currentView: 'timelineMonth', firstDayOfWeek: 0, - dataSource: [appointment] + dataSource: [appointment], + maxAppointmentsPerCell: 'unlimited' }); - const $appointment = $(this.instance.$element()).find('.' + APPOINTMENT_CLASS).eq(0); - const $targetCell = this.instance.$element().find('.' + DATE_TABLE_CELL_CLASS).eq(2); + const targetCellPosition = this.scheduler.workSpace.getCellPosition(0, 2); - assert.roughEqual($appointment.position().top, $targetCell.position().top, 1.001, 'appointment top is correct'); - assert.roughEqual($appointment.position().left, $targetCell.position().left, 1.001, 'appointment left is correct'); + assert.roughEqual(this.scheduler.appointments.getAppointmentPosition().top, targetCellPosition.top, 1.001, 'appointment top is correct'); + assert.roughEqual(this.scheduler.appointments.getAppointmentPosition().left, targetCellPosition.left, 1.001, 'appointment left is correct'); }); QUnit.test('Rival appointments should have right position on timeline month view', function(assert) { @@ -2441,6 +2432,7 @@ QUnit.test('Rival appointments should have right position on timeline month view dataSource: data, views: ['timelineMonth'], currentView: 'timelineMonth', + maxAppointmentsPerCell: 'unlimited', currentDate: new Date(2018, 11, 3), firstDayOfWeek: 0, startDayHour: 8, @@ -2470,15 +2462,15 @@ QUnit.test('Rival long appointments should have right position on timeline month dataSource: data, views: ['timelineMonth'], currentView: 'timelineMonth', + maxAppointmentsPerCell: 2, currentDate: new Date(2018, 11, 3), firstDayOfWeek: 0, startDayHour: 8, endDayHour: 20 }); - const $secondAppointment = this.instance.$element().find('.' + APPOINTMENT_CLASS).eq(1); - - assert.equal($secondAppointment.position().top, 40, 'Second appointment top is ok'); + assert.equal(this.scheduler.appointments.getAppointmentPosition(0).top, APPOINTMENT_DEFAULT_TOP_OFFSET, 'Long appointment top is ok'); + assert.roughEqual(this.scheduler.appointments.getAppointmentPosition(1).top, this.scheduler.appointments.getAppointmentHeight() + APPOINTMENT_DEFAULT_TOP_OFFSET, 1, 'Second appointment top is ok'); }); QUnit.test('Long appointment part should not be rendered on timeline month view (T678380)', function(assert) { @@ -3125,11 +3117,10 @@ QUnit.test('Appointments should be rendered correctly in vertical grouped worksp const cellHeight = $(this.instance.$element()).find('.' + DATE_TABLE_CELL_CLASS).eq(0).outerHeight(); const cellPosition = $(this.instance.$element()).find('.' + DATE_TABLE_CELL_CLASS).eq(5).position().left; - const monthTopOffset = cellHeight * 0.4; - assert.roughEqual($appointments.eq(0).position().top, cellHeight * 2 + monthTopOffset, 1, 'correct top position'); + assert.roughEqual($appointments.eq(0).position().top, cellHeight * 2 + APPOINTMENT_DEFAULT_TOP_OFFSET, 1, 'correct top position'); assert.roughEqual($appointments.eq(0).position().left, cellPosition, 1.5, 'correct left position'); - assert.roughEqual($appointments.eq(1).position().top, cellHeight * 8 + monthTopOffset, 3.5, 'correct top position'); + assert.roughEqual($appointments.eq(1).position().top, cellHeight * 8 + APPOINTMENT_DEFAULT_TOP_OFFSET, 3.5, 'correct top position'); assert.roughEqual($appointments.eq(1).position().left, cellPosition, 1.5, 'correct left position'); }); @@ -3287,38 +3278,94 @@ QUnit.test('Appointment should be resized correctly to left side in horizontal g }); // Timezone-sensitive test, use US/Pacific for proper testing -QUnit.test('Appointment should have correct dates after resizing through timezone change (T835544)', function(assert) { - this.createInstance({ - dataSource: [{ - text: 'Staff Productivity Report', - startDate: '2019-11-04T00:00', - endDate: '2019-11-06T00:00', - }], - views: ['timelineMonth'], - currentView: 'timelineMonth', - currentDate: new Date(2019, 10, 1), - height: 300, - startDayHour: 0, - }); +[{ + handle: CLASSES.resizableHandle.left, + direction: -1, + currentDate: new Date(2019, 10, 1), + appointment: { + startDate: '2019-11-04T00:00', + endDate: '2019-11-06T00:00', + }, + expectedValue: '12:00 AM - 12:00 AM', + expectedTooltipValue: 'November 3 12:00 AM - November 6 12:00 AM', + scrollDate: new Date(2019, 10, 1), + text: 'in case drag left handle to winter DST' +}, { + handle: CLASSES.resizableHandle.left, + direction: -1, + currentDate: new Date(2019, 2, 10), + appointment: { + startDate: '2019-03-11T00:00', + endDate: '2019-03-13T00:00', + }, + expectedValue: '12:00 AM - 12:00 AM', + expectedTooltipValue: 'March 10 12:00 AM - March 13 12:00 AM', + scrollDate: new Date(2019, 2, 10), + text: 'in case drag left handle to summer DST' +}, { + handle: CLASSES.resizableHandle.right, + direction: 1, + currentDate: new Date(2019, 10, 1), + appointment: { + startDate: '2019-11-01T00:00', + endDate: '2019-11-03T00:00', + }, + expectedValue: '12:00 AM - 12:00 AM', + expectedTooltipValue: 'November 1 12:00 AM - November 4 12:00 AM', + scrollDate: new Date(2019, 10, 1), + text: 'in case drag right handle to winter DST' +}, { + handle: CLASSES.resizableHandle.right, + direction: 1, + currentDate: new Date(2019, 2, 10), + appointment: { + startDate: '2019-03-08T00:00', + endDate: '2019-03-10T00:00', + }, + expectedValue: '12:00 AM - 12:00 AM', + expectedTooltipValue: 'March 8 12:00 AM - March 11 12:00 AM', + scrollDate: new Date(2019, 2, 7), + text: 'in case drag right handle to summer DST' +}].forEach(testCase => { + QUnit.test(`Appointment should have correct dates after resizing ${testCase.text} (T835544)`, function(assert) { + this.createInstance({ + editing: { + allowResizing: true + }, + dataSource: [{ + text: 'Staff Productivity Report', + startDate: testCase.appointment.startDate, + endDate: testCase.appointment.endDate, + }], + views: ['timelineMonth'], + currentView: 'timelineMonth', + currentDate: new Date(testCase.currentDate), + height: 300, + startDayHour: 0, + }); - const cellWidth = this.scheduler.workSpace.getCellWidth(); - let pointer = pointerMock($(this.scheduler.appointments.getAppointment()).find('.dx-resizable-handle-left').eq(0)).start(); + this.scheduler.instance.scrollToTime(0, 0, new Date(testCase.scrollDate)); - pointer.dragStart().drag(-(cellWidth), 0); - pointer.dragEnd(); + const { getAppointment, getDateText } = this.scheduler.appointments; - let appointmentContent = this.scheduler.appointments.getAppointment().find('.dx-scheduler-appointment-content-date').text(); + const cellWidth = this.scheduler.workSpace.getCellWidth(); + let pointer = pointerMock($(getAppointment()).find(testCase.handle).eq(0)).start(); - assert.equal(appointmentContent, '12:00 AM - 12:00 AM', 'Dates are correct'); + pointer.dragStart().drag(testCase.direction * cellWidth, 0); + pointer.dragEnd(); - pointer = pointerMock($(this.scheduler.appointments.getAppointment()).find('.dx-resizable-handle-left').eq(0)).start(); + assert.equal(getDateText(), testCase.expectedValue, 'Dates should correct after resizing'); - pointer.dragStart().drag((cellWidth), 0); - pointer.dragEnd(); + this.scheduler.appointments.click(); + assert.equal(this.scheduler.tooltip.getDateText(), testCase.expectedTooltipValue, 'Dates in tooltip should correct'); - appointmentContent = this.scheduler.appointments.getAppointment().find('.dx-scheduler-appointment-content-date').text(); + pointer = pointerMock($(getAppointment()).find(testCase.handle).eq(0)).start(); - assert.equal(appointmentContent, '12:00 AM - 12:00 AM', 'Dates are correct'); + pointer.dragStart().drag(-testCase.direction * cellWidth, 0); + pointer.dragEnd(); + + assert.equal(getDateText(), testCase.expectedValue, 'Dates should correct'); + }); }); QUnit.test('Tail of long appointment should have a right position, groupByDate = true', function(assert) { diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentsVertical.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentsVertical.tests.js index b5452d513680..2739f2352675 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentsVertical.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentsVertical.tests.js @@ -16,10 +16,7 @@ import 'ui/switch'; import { SchedulerTestWrapper } from './helpers.js'; const createInstance = function(options) { - const defaultOption = { - maxAppointmentsPerCell: null - }; - const instance = $('#scheduler').dxScheduler($.extend(defaultOption, options)).dxScheduler('instance'); + const instance = $('#scheduler').dxScheduler(options).dxScheduler('instance'); return new SchedulerTestWrapper(instance); }; @@ -27,7 +24,6 @@ import translator from 'animation/translator'; import fx from 'animation/fx'; import pointerMock from '../../helpers/pointerMock.js'; import Color from 'color'; -import devices from 'core/devices'; import dragEvents from 'events/drag'; import { DataSource } from 'data/data_source/data_source'; import subscribes from 'ui/scheduler/ui.scheduler.subscribes'; @@ -36,22 +32,13 @@ import dataUtils from 'core/element_data'; const DATE_TABLE_CELL_CLASS = 'dx-scheduler-date-table-cell'; const APPOINTMENT_CLASS = 'dx-scheduler-appointment'; -const APPOINTMENT_DEFAULT_OFFSET = 25; -const APPOINTMENT_MOBILE_OFFSET = 50; - -const getOffset = () => { - if(devices.current().deviceType !== 'desktop') { - return APPOINTMENT_MOBILE_OFFSET; - } else { - return APPOINTMENT_DEFAULT_OFFSET; - } -}; +const APPOINTMENT_DEFAULT_LEFT_OFFSET = 26; QUnit.module('Integration: Appointments on vertical views (day, week, workWeek)', { beforeEach: function() { fx.off = true; this.createInstance = function(options) { - this.instance = $('#scheduler').dxScheduler($.extend(options, { maxAppointmentsPerCell: options && options.maxAppointmentsPerCell || null })).dxScheduler('instance'); + this.instance = $('#scheduler').dxScheduler(options).dxScheduler('instance'); }; this.getAppointmentColor = function($task, checkedProperty) { checkedProperty = checkedProperty || 'backgroundColor'; @@ -413,14 +400,13 @@ QUnit.test('Two vertical neighbor appointments should be placed correctly', func const $commonAppointments = this.instance.$element().find('.dx-scheduler-scrollable-appointments .dx-scheduler-appointment'); const $allDayAppts = this.instance.$element().find('.dx-scheduler-all-day-appointment'); const cellWidth = this.instance.$element().find('.' + DATE_TABLE_CELL_CLASS).eq(0).outerWidth(); - const appointmentOffset = getOffset(); assert.roughEqual(translator.locate($commonAppointments.eq(0)).left, 100, 2.001, 'Left position is OK'); assert.roughEqual(translator.locate($commonAppointments.eq(1)).left, 100, 2.001, 'Left position is OK'); assert.roughEqual(translator.locate($allDayAppts.eq(0)).left, 100, 2.001, 'Left position is OK'); - assert.roughEqual($commonAppointments.eq(0).outerWidth(), cellWidth - appointmentOffset, 1.001, 'Width is OK'); - assert.roughEqual($commonAppointments.eq(1).outerWidth(), cellWidth - appointmentOffset, 1.001, 'Width is OK'); + assert.roughEqual($commonAppointments.eq(0).outerWidth(), cellWidth - APPOINTMENT_DEFAULT_LEFT_OFFSET, 1.001, 'Width is OK'); + assert.roughEqual($commonAppointments.eq(1).outerWidth(), cellWidth - APPOINTMENT_DEFAULT_LEFT_OFFSET, 1.001, 'Width is OK'); assert.roughEqual($allDayAppts.eq(0).outerWidth(), cellWidth, 1.001, 'Width is OK'); }); @@ -433,7 +419,8 @@ QUnit.test('Appointment size should depend on neighbor appointments', function(a this.createInstance({ currentView: 'week', currentDate: new Date(2015, 2, 4), - dataSource: items + dataSource: items, + maxAppointmentsPerCell: 'unlimited' }); const $appointments = this.instance.$element().find('.' + APPOINTMENT_CLASS); @@ -502,11 +489,31 @@ QUnit.test('Appointments should have correct position, rtl mode, editing=false', }); const $appointment = $(this.instance.$element()).find('.' + APPOINTMENT_CLASS).eq(0); - const appointmentOffset = getOffset(); - assert.roughEqual($appointment.position().left, appointmentOffset, 2, 'Appointment left is correct on init'); + assert.roughEqual($appointment.position().left, APPOINTMENT_DEFAULT_LEFT_OFFSET, 2, 'Appointment left is correct on init'); }); +QUnit.test('Appointments should be filtered correctly, if cellDuration is set and timetable is divided with a remainder (T854826)', function(assert) { + const appointments = [{ + AppointmentId: 8, + text: 'Appointment', + startDate: '2017-05-24T14:30:00', + endDate: '2017-05-24T15:45:00' + }]; + + const scheduler = createInstance({ + currentDate: new Date(2017, 4, 23), + startDayHour: 8, + endDayHour: 24, + cellDuration: 150, + views: ['day'], + currentView: 'day', + firstDayOfWeek: 1, + dataSource: appointments + }); + + assert.equal(scheduler.appointments.getAppointmentCount(), 0, 'Appointments was filtered correctly'); +}); QUnit.test('Appointment should have correct height, when startDayHour is decimal', function(assert) { const appointments = [{ @@ -536,8 +543,7 @@ QUnit.test('dropDown appointment should not compact class on vertical view', fun this.createInstance({ currentDate: new Date(2015, 4, 25), views: [{ type: 'week', name: 'week' }], - currentView: 'week', - maxAppointmentsPerCell: 'auto' + currentView: 'week' }); this.instance.option('dataSource', [ @@ -726,11 +732,10 @@ QUnit.test('Appointment should have right width on mobile devices & desktop in w currentView: 'week' }); - const expectedOffset = getOffset(); const $appointments = this.instance.$element().find('.' + APPOINTMENT_CLASS); const cellWidth = this.instance.$element().find('.' + DATE_TABLE_CELL_CLASS).eq(0).outerWidth(); - assert.roughEqual($appointments.eq(0).outerWidth(), cellWidth - expectedOffset, 1.001, 'Width is OK'); + assert.roughEqual($appointments.eq(0).outerWidth(), cellWidth - APPOINTMENT_DEFAULT_LEFT_OFFSET, 1.001, 'Width is OK'); }); QUnit.test('Appointments should be rendered correctly in vertical grouped workspace Day', function(assert) { @@ -859,8 +864,7 @@ QUnit.test('Appointments should be rendered correctly in vertical grouped worksp endDayHour: 15, cellDuration: 60, showAllDayPanel: true, - width: 2000, - maxAppointmentsPerCell: 'auto' + width: 2000 }); const $appointments = $(this.instance.$element()).find('.' + APPOINTMENT_CLASS); @@ -913,8 +917,7 @@ QUnit.test('Rival allDay appointments from different groups should be rendered c startDayHour: 9, endDayHour: 15, cellDuration: 60, - showAllDayPanel: true, - maxAppointmentsPerCell: 'auto' + showAllDayPanel: true }); const $appointments = $(this.instance.$element()).find('.' + APPOINTMENT_CLASS); @@ -968,8 +971,7 @@ QUnit.test('Rival allDay appointments from same groups should be rendered correc startDayHour: 9, endDayHour: 15, cellDuration: 60, - showAllDayPanel: true, - maxAppointmentsPerCell: 'auto' + showAllDayPanel: true }); const $appointments = $(this.instance.$element()).find('.' + APPOINTMENT_CLASS); @@ -999,16 +1001,14 @@ QUnit.test('Rival appointments from one group should be rendered correctly in ve ] } ], + width: 800, currentDate: new Date(2018, 4, 21), startDayHour: 9, endDayHour: 15, cellDuration: 60, - showAllDayPanel: true, - maxAppointmentsPerCell: null + showAllDayPanel: true }); - const defaultWidthStub = sinon.stub(this.instance.getRenderingStrategyInstance(), '_getAppointmentMaxWidth').returns(50); - this.instance.option('dataSource', [ { text: '1', @@ -1030,14 +1030,12 @@ QUnit.test('Rival appointments from one group should be rendered correctly in ve const cellHeight = $(this.instance.$element()).find('.' + DATE_TABLE_CELL_CLASS).first().outerHeight(); assert.roughEqual($appointments.eq(0).position().top, 8.5 * cellHeight, 1.5, 'correct top position of appointment'); - assert.roughEqual($appointments.eq(0).outerWidth(), 50, 2, 'correct size of appointment'); - assert.equal($appointments.eq(0).position().left, 314, 'correct left position of appointment'); + assert.roughEqual($appointments.eq(0).outerWidth(), 59, 2, 'correct size of appointment'); + assert.roughEqual($appointments.eq(0).position().left, 285, 1.1, 'correct left position of appointment'); assert.roughEqual($appointments.eq(1).position().top, 8.5 * cellHeight, 1.5, 'correct top position of appointment'); - assert.roughEqual($appointments.eq(1).outerWidth(), 50, 2, 'correct size of appointment'); - assert.equal($appointments.eq(1).position().left, 428, 'correct left position of appointment'); - - defaultWidthStub.restore(); + assert.roughEqual($appointments.eq(1).outerWidth(), 59, 2, 'correct size of appointment'); + assert.roughEqual($appointments.eq(1).position().left, 370, 1.1, 'correct left position of appointment'); }); QUnit.test('Appointment in bottom cell should be rendered cirrectly in vertical grouped workspace Week', function(assert) { @@ -1070,8 +1068,7 @@ QUnit.test('Appointment in bottom cell should be rendered cirrectly in vertical endDayHour: 15, width: 2000, cellDuration: 60, - showAllDayPanel: true, - maxAppointmentsPerCell: 'auto' + showAllDayPanel: true }); const $appointments = $(this.instance.$element()).find('.' + APPOINTMENT_CLASS); diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentsWithTimezone.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentsWithTimezone.tests.js index ef7bc44f37f8..443676fcf83b 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentsWithTimezone.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentsWithTimezone.tests.js @@ -16,7 +16,7 @@ import 'generic_light.css!'; import 'ui/scheduler/ui.scheduler'; const createInstance = function(options) { - const instance = $('#scheduler').dxScheduler($.extend(options, { maxAppointmentsPerCell: options && options.maxAppointmentsPerCell || null })).dxScheduler('instance'); + const instance = $('#scheduler').dxScheduler(options).dxScheduler('instance'); return new SchedulerTestWrapper(instance); }; @@ -39,7 +39,7 @@ QUnit.module('Integration: Appointments rendering when timezone is set', { beforeEach: function() { fx.off = true; this.createInstance = function(options) { - this.instance = $('#scheduler').dxScheduler($.extend(options, { maxAppointmentsPerCell: options && options.maxAppointmentsPerCell || null })).dxScheduler('instance'); + this.instance = $('#scheduler').dxScheduler(options).dxScheduler('instance'); }; this.clock = sinon.useFakeTimers(); @@ -1175,7 +1175,8 @@ QUnit.test('Appointment should have right width in workspace with timezone', fun firstDayOfWeek: 1, startDayHour: 3, endDayHour: 24, - timeZone: 'Asia/Ashkhabad' + timeZone: 'Asia/Ashkhabad', + height: 600 }); this.instance.addAppointment({ diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.dateNavigator.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.dateNavigator.tests.js index 7883b250930f..8f3ca4de3bb1 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.dateNavigator.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.dateNavigator.tests.js @@ -3,6 +3,8 @@ import fx from 'animation/fx'; import 'ui/scheduler/ui.scheduler'; import 'common.css!'; import 'generic_light.css!'; +import dxPopup from 'ui/popup'; +import { createWrapper } from './helpers.js'; QUnit.testStart(function() { $('#qunit-fixture').html( @@ -151,6 +153,21 @@ QUnit.module('Integration: Date navigator with min and max values', moduleConfig }); QUnit.module('Integration: Date navigator', moduleConfig, function() { + [false, true].forEach(value => { + QUnit.test(`Date navigator should be correctly work, if deferRendering property set to ${value} (T874944)`, function(assert) { + dxPopup.defaultOptions({ + options: { + deferRendering: value + } + }); + + const scheduler = createWrapper(); + scheduler.navigator.click(); + + assert.ok(scheduler.navigator.isPopupVisible(), 'Navigator popup should be visible without errors'); + }); + }); + QUnit.test('Click on the \'next\' button should update currentDate', function(assert) { this.createInstance({ currentDate: new Date(2015, 1, 9) }); diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.dstAppointments.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.dstAppointments.tests.js index 5d0ef2e30337..c3dbbbaed2da 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.dstAppointments.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.dstAppointments.tests.js @@ -434,7 +434,6 @@ QUnit.module('Appointments with DST/STD cases', moduleConfig, () => { const scheduler = createWrapper({ currentDate: new Date(2018, 2, 11), views: ['timelineDay'], - maxAppointmentsPerCell: null, currentView: 'timelineDay', dataSource: [{ text: 'DST', @@ -462,7 +461,6 @@ QUnit.module('Appointments with DST/STD cases', moduleConfig, () => { currentDate: new Date(2018, 2, 12), views: ['timelineDay'], currentView: 'timelineDay', - maxAppointmentsPerCell: null, dataSource: [{ text: 'DST', startDate: startDate, @@ -488,7 +486,6 @@ QUnit.module('Appointments with DST/STD cases', moduleConfig, () => { currentView: 'timelineWeek', cellDuration: 60, currentDate: currentDate, - maxAppointmentsPerCell: null, dataSource: [{ text: 'DST', startDate: startDate, @@ -511,7 +508,6 @@ QUnit.module('Appointments with DST/STD cases', moduleConfig, () => { currentDate: currentDate, views: ['timelineDay'], currentView: 'timelineDay', - maxAppointmentsPerCell: null, dataSource: [{ text: 'DST', startDate: startDate, @@ -528,6 +524,24 @@ QUnit.module('Appointments with DST/STD cases', moduleConfig, () => { assert.roughEqual(scheduler.appointments.getAppointment(0).outerWidth(), scheduler.workSpace.getCellWidth() * duration, 2.001, 'Appt width is correct after the day of the time ajusting'); }); + QUnit.test('Appointment should be rendered correctly if end date appointment coincided translation on STD', function(assert) { + const scheduler = createWrapper({ + dataSource: [{ + text: 'November 4', + startDate: new Date(2018, 10, 4, 18, 0), + endDate: new Date(2018, 10, 5, 0, 0), + }], + views: ['month'], + currentView: 'month', + currentDate: new Date(2018, 10, 1), + firstDayOfWeek: 0, + cellDuration: 60, + height: 800 + }); + + assert.roughEqual(scheduler.appointments.getAppointment(0).outerWidth(), scheduler.workSpace.getCellWidth(), 2.001, 'Appointment width is correct after translation oт STD'); + }); + QUnit.test('Recurrence exception should not be rendered if exception goes after adjusting AEST-> AEDT (T619455)', function(assert) { const tzOffsetStub = sinon.stub(subscribes, 'getClientTimezoneOffset').returns(-39600000); try { @@ -556,21 +570,48 @@ QUnit.module('Appointments with DST/STD cases', moduleConfig, () => { } }); - QUnit.test('Appointment should rendered correctly if end date appointment coincided translation oт STD', function(assert) { + QUnit.test('Recurrence exception should be adjusted by scheduler timezone after deleting of the single appt', function(assert) { const scheduler = createWrapper({ dataSource: [{ - text: 'November 4', - startDate: new Date(2018, 10, 4, 18, 0), - endDate: new Date(2018, 10, 5, 0, 0), + text: 'Recruiting students', + startDate: new Date(2018, 2, 26, 10, 0), + endDate: new Date(2018, 2, 26, 11, 0), + recurrenceRule: 'FREQ=DAILY' }], - views: ['month'], - currentView: 'month', - currentDate: new Date(2018, 10, 1), - firstDayOfWeek: 0, - cellDuration: 60, - height: 800 + views: ['day'], + currentView: 'day', + currentDate: new Date(2018, 3, 1), + timeZone: 'Australia/Sydney', + recurrenceEditMode: 'occurrence' }); - assert.roughEqual(scheduler.appointments.getAppointment(0).outerWidth(), scheduler.workSpace.getCellWidth(), 2.001, 'Appointment width is correct after translation oт STD'); + scheduler.appointments.click(); + this.clock.tick(300); + scheduler.tooltip.clickOnDeleteButton(); + + assert.equal(scheduler.appointments.getAppointmentCount(), 0, 'Appointment was deleted'); + }); + + QUnit.test('Recurrence exception should be adjusted by appointment timezone after deleting of the single appt', function(assert) { + const scheduler = createWrapper({ + dataSource: [{ + text: 'Recruiting students', + startDate: new Date(2018, 2, 26, 10, 0), + endDate: new Date(2018, 2, 26, 11, 0), + recurrenceRule: 'FREQ=DAILY', + startDateTimeZone: 'Australia/Canberra', + endDateTimeZone: 'Australia/Canberra' + }], + views: ['day'], + currentView: 'day', + currentDate: new Date(2018, 3, 1), + recurrenceEditMode: 'occurrence' + }); + + scheduler.appointments.click(); + this.clock.tick(300); + scheduler.tooltip.clickOnDeleteButton(); + + assert.equal(scheduler.appointments.getAppointmentCount(), 0, 'Appointment was deleted'); }); }); diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.multiWeekAppointments.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.multiWeekAppointments.tests.js index f5b128db2035..ad789583b444 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.multiWeekAppointments.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.multiWeekAppointments.tests.js @@ -33,7 +33,7 @@ QUnit.module('Integration: Multi-Week appointments', { beforeEach: function() { fx.off = true; this.createInstance = function(options) { - this.instance = $('#scheduler').dxScheduler($.extend(options, { maxAppointmentsPerCell: null })).dxScheduler('instance'); + this.instance = $('#scheduler').dxScheduler($.extend(options, { height: 600 })).dxScheduler('instance'); }; }, afterEach: function() { @@ -175,58 +175,6 @@ QUnit.test('Max allowed position of appointment should be calculated correctly ( assert.roughEqual($appointment.outerWidth(), Math.floor($cell.outerWidth()), 1.001, 'Appointment width is OK'); }); -QUnit.test('Compact parts of long appointments should be located properly (grouped mode)', function(assert) { - this.createInstance({ - currentDate: new Date(2016, 6, 20), - views: ['month'], - currentView: 'month', - firstDayOfWeek: 1, - height: 500, - dataSource: [{ - text: 'first', - ownerId: [1], - startDate: new Date(2016, 6, 16, 0, 0), - endDate: new Date(2016, 6, 19, 0, 0) - }, { - text: 'second', - ownerId: [1], - startDate: new Date(2016, 6, 16, 0, 0), - endDate: new Date(2016, 6, 19, 0, 0) - }, { - text: 'third', - ownerId: [1], - startDate: new Date(2016, 6, 16, 0, 0), - endDate: new Date(2016, 6, 19, 0, 0) - }], - groups: ['ownerId'], - resources: [ - { - field: 'ownerId', - allowMultiple: true, - dataSource: [ - { - text: 'first', - id: 1 - }, - { - text: 'second', - id: 2 - } - ] - } - ] - }); - const $appointments = $(this.instance.$element()).find('.dx-scheduler-appointment'); - const $compactAppts = $appointments.filter('.dx-scheduler-appointment-compact'); - - assert.equal($appointments.length, 7, 'Appointment part quantity is right'); - assert.equal($compactAppts.length, 3, 'Quantity of compact appointment parts is right'); - - assert.roughEqual($compactAppts.eq(0).position().top, 124, 2.001, 'Position of compact appointment part is right'); - assert.roughEqual($compactAppts.eq(1).position().top, 124, 2.001, 'Position of compact appointment part is right'); - assert.roughEqual($compactAppts.eq(2).position().top, 183, 2.501, 'Position of compact appointment part is right'); -}); - QUnit.test('Appointment should have a special icon and class if it greater than work space width', function(assert) { this.createInstance({ currentDate: new Date(2015, 4, 6), diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.recurringAppointments.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.recurringAppointments.tests.js index 37b43922d8a7..ee9e355ef96a 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.recurringAppointments.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.recurringAppointments.tests.js @@ -78,6 +78,7 @@ QUnit.test('Tasks should be duplicated according to recurrence rule, if firstDay }); QUnit.test('Tasks should be duplicated according to recurrence rule and recurrence exception', function(assert) { + // NOTE: recurrenceException in date format will be converted in dateTime with 00.00 time when processing const tasks = [ { text: 'One', startDate: new Date(2015, 2, 16), endDate: new Date(2015, 2, 16, 2), recurrenceRule: 'FREQ=DAILY', recurrenceException: '20150317' } ]; @@ -884,6 +885,48 @@ QUnit.test('Recurring appt should be rendered correctly after setting recurrence assert.equal(appointments.length, 2, 'appt was rendered correctly'); }); +QUnit.test('Recurrence exception time should be considered when recurrent appointment rendering (T862204)', function(assert) { + const task = { + text: 'No Recruiting students', + roomId: [5], + startDate: new Date(2017, 4, 15, 11, 0), + endDate: new Date(2017, 4, 15, 12, 0), + recurrenceRule: 'FREQ=DAILY;COUNT=3', + recurrenceException: '20170516T070000Z' + }; + + this.createInstance({ + dataSource: [task], + views: ['month'], + currentView: 'month', + currentDate: new Date(2017, 4, 25) + }); + + assert.equal(this.scheduler.appointments.getAppointmentCount(), 3, 'Correct appointment count is rendered'); +}); + +QUnit.test('Recurrence exception time should be considered when recurrent appointment rendering and timezones are set', function(assert) { + const task = { + text: 'No Recruiting students', + roomId: [5], + startDate: new Date(2017, 4, 15, 11, 0), + endDate: new Date(2017, 4, 15, 12, 0), + recurrenceRule: 'FREQ=DAILY;COUNT=3', + recurrenceException: '20170516T070000Z', + startDateTimeZone: 'Etc/UTC' + }; + + this.createInstance({ + dataSource: [task], + timeZone: 'Europe/Paris', + views: ['month'], + currentView: 'month', + currentDate: new Date(2017, 4, 25) + }); + + assert.equal(this.scheduler.appointments.getAppointmentCount(), 3, 'Correct appointment count is rendered'); +}); + QUnit.test('The second appointment in recurring series in Month view should have correct width', function(assert) { this.createInstance({ dataSource: [{ @@ -987,12 +1030,9 @@ QUnit.test('Reduced reccuring appt should have right left position in first colu currentView: 'month' }); - const $appointment = this.instance.$element().find('.dx-scheduler-appointment'); const $reducedAppointment = this.instance.$element().find('.dx-scheduler-appointment-reduced'); - const compactClass = 'dx-scheduler-appointment-compact'; assert.equal($reducedAppointment.eq(1).position().left, 0, 'first appt has right left position'); - assert.notOk($appointment.eq(7).hasClass(compactClass), 'next appt isn\'t compact'); }); QUnit.test('Reduced reccuring appt should have right left position in first column in grouped Month view', function(assert) { @@ -1019,13 +1059,10 @@ QUnit.test('Reduced reccuring appt should have right left position in first colu ] }); - const $appointment = this.instance.$element().find('.dx-scheduler-appointment'); const $reducedAppointment = this.instance.$element().find('.dx-scheduler-appointment-reduced'); - const compactClass = 'dx-scheduler-appointment-compact'; const cellWidth = this.instance.$element().find('.dx-scheduler-date-table-cell').outerWidth(); assert.roughEqual($reducedAppointment.eq(1).position().left, cellWidth * 7, 2.5, 'first appt in 2d group has right left position'); - assert.notOk($appointment.eq(7).hasClass(compactClass), 'appt isn\'t compact'); }); QUnit.test('Recurrence exception should be adjusted by scheduler timezone', function(assert) { @@ -1081,56 +1118,6 @@ QUnit.test('T697037. Recurrence exception date should equal date of appointment, $('.dx-dialog-buttons .dx-button').eq(1).trigger('dxclick'); }); -QUnit.test('Recurrence exception should be adjusted by scheduler timezone after deleting of the single appt', function(assert) { - this.createInstance({ - dataSource: [{ - text: 'Recruiting students', - startDate: new Date(2018, 2, 26, 10, 0), - endDate: new Date(2018, 2, 26, 11, 0), - recurrenceRule: 'FREQ=DAILY' - }], - views: ['day'], - currentView: 'day', - currentDate: new Date(2018, 2, 27), - timeZone: 'Australia/Sydney' - }); - - this.scheduler.appointments.click(); - this.clock.tick(300); - - this.scheduler.tooltip.clickOnDeleteButton(); - $('.dx-dialog-buttons .dx-button').eq(1).trigger('dxclick'); - - const $appointment = this.instance.$element().find('.dx-scheduler-appointment'); - assert.notOk($appointment.length, 'appt is deleted'); -}); - -QUnit.test('Recurrence exception should be adjusted by appointment timezone after deleting of the single appt', function(assert) { - this.createInstance({ - dataSource: [{ - text: 'Recruiting students', - startDate: new Date(2018, 2, 26, 10, 0), - endDate: new Date(2018, 2, 26, 11, 0), - recurrenceRule: 'FREQ=DAILY', - startDateTimeZone: 'Australia/Canberra', - endDateTimeZone: 'Australia/Canberra' - }], - views: ['day'], - currentView: 'day', - currentDate: new Date(2018, 3, 1) - }); - - this.scheduler.appointments.click(); - this.clock.tick(300); - - this.scheduler.tooltip.clickOnDeleteButton(); - $('.dx-dialog-buttons .dx-button').eq(1).trigger('dxclick'); - - const $appointment = this.instance.$element().find('.dx-scheduler-appointment'); - - assert.notOk($appointment.length, 'appt is deleted'); -}); - QUnit.test('Single changed appointment should be rendered correctly in specified timeZone', function(assert) { const tzOffsetStub = sinon.stub(subscribes, 'getClientTimezoneOffset').returns(-10800000); try { diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.timeline.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.timeline.tests.js index ae7302ebf34e..448612cc9db4 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.timeline.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.timeline.tests.js @@ -244,7 +244,6 @@ QUnit.test('Scheduler should update scroll position if appointment is not visibl QUnit.test('Appointments should have a right order on timeline month(lots of appts)', function(assert) { const scheduler = createInstance({ currentDate: new Date(2016, 1, 2), - maxAppointmentsPerCell: null, dataSource: new DataSource([ { 'text': 'Google AdWords Strategy', @@ -294,7 +293,8 @@ QUnit.test('Appointments should have a right order on timeline month(lots of app ]), views: ['timelineMonth'], currentView: 'timelineMonth', - height: 800, + maxAppointmentsPerCell: 'unlimited', + height: 505, startDayHour: 8, endDayHour: 20, cellDuration: 60, diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/integration.workSpace.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/integration.workSpace.tests.js index cbd898d7191a..bbfb856e5766 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/integration.workSpace.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/integration.workSpace.tests.js @@ -33,7 +33,7 @@ QUnit.module('Integration: Work space', { beforeEach: function() { fx.off = true; this.createInstance = function(options) { - this.instance = $('#scheduler').dxScheduler($.extend(options, { maxAppointmentsPerCell: null })).dxScheduler('instance'); + this.instance = $('#scheduler').dxScheduler(options).dxScheduler('instance'); this.scheduler = new SchedulerTestWrapper(this.instance); }; }, @@ -701,7 +701,9 @@ QUnit.test('Appointments in month view should be sorted same as in all-day secti this.createInstance({ dataSource: items, currentDate: new Date(2016, 1, 11), - currentView: 'week' + currentView: 'week', + height: 600, + maxAppointmentsPerCell: 'unlimited' }); const allDayAppointments = this.instance.$element().find('.dx-scheduler-appointment'); diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/layoutManager.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/layoutManager.tests.js index 3c0d09c6cadf..54fded98f541 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/layoutManager.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/layoutManager.tests.js @@ -14,20 +14,10 @@ import HorizontalAppointmentsStrategy from 'ui/scheduler/rendering_strategies/ui import HorizontalMonthLineAppointmentsStrategy from 'ui/scheduler/rendering_strategies/ui.scheduler.appointments.strategy.horizontal_month_line'; import Color from 'color'; import dataUtils from 'core/element_data'; -import devices from 'core/devices'; import CustomStore from 'data/custom_store'; import { SchedulerTestWrapper } from './helpers.js'; -const APPOINTMENT_DEFAULT_OFFSET = 25; -const APPOINTMENT_MOBILE_OFFSET = 50; - -const getOffset = () => { - if(devices.current().deviceType !== 'desktop') { - return APPOINTMENT_MOBILE_OFFSET; - } else { - return APPOINTMENT_DEFAULT_OFFSET; - } -}; +const APPOINTMENT_DEFAULT_LEFT_OFFSET = 26; const checkAppointmentUpdatedCallbackArgs = (assert, actual, expected) => { assert.deepEqual(actual.old, expected.old, 'Old data is OK'); @@ -42,7 +32,7 @@ QUnit.testStart(function() { const moduleOptions = { beforeEach: function() { this.createInstance = options => { - this.instance = $('#scheduler').dxScheduler($.extend(options, { editing: true, maxAppointmentsPerCell: null })).dxScheduler('instance'); + this.instance = $('#scheduler').dxScheduler($.extend(options, { editing: true, height: options && options.height || 600 })).dxScheduler('instance'); this.scheduler = new SchedulerTestWrapper(this.instance); }; }, @@ -233,6 +223,7 @@ QUnit.test('Appointment should have a correct min width', function(assert) { currentDate: new Date(2015, 1, 9, 8), views: ['timelineWeek'], currentView: 'timelineWeek', + maxAppointmentsPerCell: 1, dataSource: [ { startDate: new Date(2015, 1, 9, 8, 1, 1), @@ -244,7 +235,7 @@ QUnit.test('Appointment should have a correct min width', function(assert) { const $appointment = $(this.instance.$element().find('.dx-scheduler-appointment')); - assert.equal($appointment.outerWidth(), 5, 'Appointment has a right width'); + assert.equal($appointment.outerWidth(), 4, 'Appointment has a right width'); }); QUnit.test('Long appointment tail should have a correct min height', function(assert) { @@ -273,6 +264,7 @@ QUnit.test('Appointment has right sortedIndex', function(assert) { currentDate: new Date(2015, 9, 16), currentView: 'month', focusStateEnabled: true, + height: 600, dataSource: [ { text: 'Appointment 1', startDate: new Date(2015, 9, 16, 9), endDate: new Date(2015, 9, 16, 11) }, { text: 'Appointment 2', startDate: new Date(2015, 9, 17, 8), endDate: new Date(2015, 9, 17, 10) }, @@ -290,34 +282,6 @@ QUnit.test('Appointment has right sortedIndex', function(assert) { assert.equal(dataUtils.data($appointments.get(3), 'dxAppointmentSettings').sortedIndex, 3, 'app has right sortedIndex'); }); -// NOTE: check sortedIndex for long appt parts -QUnit.test('Compact parts of long appointment shouldn\'t have sortedIndex', function(assert) { - this.createInstance( - { - currentDate: new Date(2015, 2, 4, 2), - focusStateEnabled: true, - views: ['month'], - currentView: 'month', - height: 500, - dataSource: [ - { text: 'Appointment 1', startDate: new Date(2015, 2, 4, 2), endDate: new Date(2015, 2, 5, 3), allDay: true }, - { text: 'Appointment 2', startDate: new Date(2015, 2, 4, 2), endDate: new Date(2015, 2, 5, 12), allDay: true }, - { text: 'Appointment 3', startDate: new Date(2015, 2, 4, 2), endDate: new Date(2015, 2, 8, 2), allDay: true } - ] - } - ); - - const $appointments = $(this.instance.$element().find('.dx-scheduler-appointment')); - - assert.equal(dataUtils.data($appointments.get(0), 'dxAppointmentSettings').sortedIndex, 0, 'app has sortedIndex'); - assert.equal(dataUtils.data($appointments.get(1), 'dxAppointmentSettings').sortedIndex, 1, 'app has sortedIndex'); - assert.equal(dataUtils.data($appointments.get(2), 'dxAppointmentSettings').sortedIndex, 2, 'app has sortedIndex'); - assert.equal(dataUtils.data($appointments.get(3), 'dxAppointmentSettings').sortedIndex, null, 'app has sortedIndex'); - assert.equal(dataUtils.data($appointments.get(4), 'dxAppointmentSettings').sortedIndex, null, 'app has sortedIndex'); - assert.equal(dataUtils.data($appointments.get(5), 'dxAppointmentSettings').sortedIndex, null, 'app has sortedIndex'); - assert.equal(dataUtils.data($appointments.get(6), 'dxAppointmentSettings').sortedIndex, 3, 'app has sortedIndex'); -}); - QUnit.test('AllDay appointment should be displayed right when endDate > startDate and duration < 24', function(assert) { this.createInstance( { @@ -340,49 +304,21 @@ QUnit.test('Two rival appointments should have correct positions', function(asse { currentDate: new Date(2015, 1, 9), currentView: 'month', - height: 500, dataSource: [ { text: 'Appointment 1', startDate: new Date(2015, 1, 1, 8), endDate: new Date(2015, 1, 1, 10) }, { text: 'Appointment 1', startDate: new Date(2015, 1, 1, 8), endDate: new Date(2015, 1, 1, 10) } ] } ); + assert.equal(this.scheduler.appointments.getAppointmentCount(), 2, 'All appointments are rendered'); - const $appointment = $(this.instance.$element().find('.dx-scheduler-appointment')); - const $tableCell = $(this.instance.$element().find('.dx-scheduler-date-table-cell').eq(0)); - - assert.equal($appointment.length, 2, 'All appointments are rendered'); - - const firstAppointmentPosition = translator.locate($appointment.eq(0)); - const secondAppointmentPosition = translator.locate($appointment.eq(1)); - - assert.equal(firstAppointmentPosition.left, 0, 'appointment is rendered in right place'); - assert.roughEqual(firstAppointmentPosition.top, 26, 1.5, 'appointment is rendered in right place'); - assert.roughEqual($appointment.eq(0).outerWidth(), $tableCell.outerWidth(), 1.1, 'appointment has a right size'); + assert.equal(this.scheduler.appointments.getAppointmentPosition(0).left, 0, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentPosition(0).top, 26, 1.5, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentWidth(0), this.scheduler.workSpace.getCellWidth(), 1.1, 'appointment has a right size'); - assert.equal(secondAppointmentPosition.left, 0, 'appointment is rendered in right place'); - assert.roughEqual(secondAppointmentPosition.top, 46, 1.5, 'appointment is rendered in right place'); - assert.roughEqual($appointment.eq(1).outerWidth(), $tableCell.outerWidth(), 1.1, 'appointment has a right size'); -}); - -QUnit.test('Collapsing appointments should have specific class', function(assert) { - this.createInstance( - { - currentDate: new Date(2015, 1, 9), - currentView: 'month', - height: 600, - dataSource: [ - { text: 'Appointment 1', startDate: new Date(2015, 1, 9, 8), endDate: new Date(2015, 1, 9, 12) }, - { text: 'Appointment 2', startDate: new Date(2015, 1, 9, 9), endDate: new Date(2015, 1, 9, 12) }, - { text: 'Appointment 3', startDate: new Date(2015, 1, 9, 11), endDate: new Date(2015, 1, 9, 12) } - ] - } - ); - - const $appointment = $(this.instance.$element().find('.dx-scheduler-appointment')); - assert.ok(!$appointment.eq(0).hasClass('dx-scheduler-appointment-empty'), 'appointment has not the class'); - assert.ok(!$appointment.eq(1).hasClass('dx-scheduler-appointment-empty'), 'appointment has not the class'); - assert.ok($appointment.eq(2).hasClass('dx-scheduler-appointment-empty'), 'appointment has the class'); + assert.equal(this.scheduler.appointments.getAppointmentPosition(1).left, 0, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentPosition(1).top, 54, 1.5, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentWidth(1), this.scheduler.workSpace.getCellWidth(), 1.1, 'appointment has a right size'); }); QUnit.test('Four rival appointments should have correct positions', function(assert) { @@ -390,7 +326,8 @@ QUnit.test('Four rival appointments should have correct positions', function(ass { currentDate: new Date(2015, 1, 9), currentView: 'month', - height: 500, + height: 600, + maxAppointmentsPerCell: 4, dataSource: [ { text: 'Appointment 1', startDate: new Date(2015, 1, 1, 8), endDate: new Date(2015, 1, 1, 12) }, { text: 'Appointment 2', startDate: new Date(2015, 1, 1, 9), endDate: new Date(2015, 1, 1, 12) }, @@ -400,31 +337,24 @@ QUnit.test('Four rival appointments should have correct positions', function(ass } ); - const $appointment = $(this.instance.$element().find('.dx-scheduler-appointment')); - const $tableCell = $(this.instance.$element().find('.dx-scheduler-date-table-cell').eq(0)); - const firstAppointmentPosition = translator.locate($appointment.eq(0)); - const secondAppointmentPosition = translator.locate($appointment.eq(1)); - const thirdAppointmentPosition = translator.locate($appointment.eq(2)); - const fourthAppointmentPosition = translator.locate($appointment.eq(3)); + assert.equal(this.scheduler.appointments.getAppointmentCount(), 4, 'All appointments are rendered'); - assert.equal($appointment.length, 4, 'All appointments are rendered'); + assert.equal(this.scheduler.appointments.getAppointmentPosition(0).left, 0, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentPosition(0).top, 26, 1.5, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentWidth(0), this.scheduler.workSpace.getCellWidth(), 1.1, 'appointment has a right size'); - assert.equal(firstAppointmentPosition.left, 0, 'appointment is rendered in right place'); - assert.roughEqual(firstAppointmentPosition.top, 26, 1.5, 'appointment is rendered in right place'); - assert.roughEqual($appointment.eq(0).outerWidth(), $tableCell.outerWidth(), 1.1, 'appointment has a right size'); + assert.equal(this.scheduler.appointments.getAppointmentPosition(1).left, 0, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentPosition(1).top, 40, 1.5, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentWidth(1), this.scheduler.workSpace.getCellWidth(), 1.1, 'appointment has a right size'); - assert.equal(secondAppointmentPosition.left, 0, 'appointment is rendered in right place'); - assert.roughEqual(secondAppointmentPosition.top, 46, 1.5, 'appointment is rendered in right place'); - assert.roughEqual($appointment.eq(1).outerWidth(), $tableCell.outerWidth(), 1.1, 'appointment has a right size'); + assert.equal(this.scheduler.appointments.getAppointmentPosition(2).left, 0, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentPosition(2).top, 68, 1.5, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentWidth(2), this.scheduler.workSpace.getCellWidth(), 1.1, 'appointment has a right size'); + + assert.equal(this.scheduler.appointments.getAppointmentPosition(3).left, 0, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentPosition(3).top, 54, 1.5, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentWidth(3), this.scheduler.workSpace.getCellWidth(), 1.1, 'appointment has a right size'); - assert.roughEqual(thirdAppointmentPosition.left, 21, 1.5, 'appointment is rendered in right place'); - assert.roughEqual(thirdAppointmentPosition.top, 3, 1.5, 'appointment is rendered in right place'); - assert.equal($appointment.eq(2).outerHeight(), 15, 'appointment has a right size'); - assert.equal($appointment.eq(2).outerWidth(), 15, 'appointment has a right size'); - assert.roughEqual(fourthAppointmentPosition.left, 3, 1, 'appointment is rendered in right place'); - assert.roughEqual(fourthAppointmentPosition.top, 3, 1.5, 'appointment is rendered in right place'); - assert.equal($appointment.eq(3).outerHeight(), 15, 'appointment has a right size'); - assert.equal($appointment.eq(3).outerWidth(), 15, 'appointment has a right size'); }); QUnit.test('Rival duplicated appointments should have correct positions', function(assert) { @@ -432,7 +362,7 @@ QUnit.test('Rival duplicated appointments should have correct positions', functi { currentDate: new Date(2015, 1, 9), currentView: 'month', - height: 500, + height: 700, dataSource: [ { text: 'Appointment 1', startDate: new Date(2015, 1, 1, 8), endDate: new Date(2015, 1, 1, 10) }, { text: 'Appointment 2', startDate: new Date(2015, 1, 1, 8), endDate: new Date(2015, 1, 2, 9) } @@ -452,33 +382,10 @@ QUnit.test('Rival duplicated appointments should have correct positions', functi assert.roughEqual($appointment.eq(0).outerWidth(), $tableCell.outerWidth(), 1.1, 'appointment has a right size'); assert.equal(secondAppointmentPosition.left, 0, 'appointment is rendered in right place'); - assert.roughEqual(secondAppointmentPosition.top, 46, 1.5, 'appointment is rendered in right place'); + assert.roughEqual(secondAppointmentPosition.top, 50, 1.5, 'appointment is rendered in right place'); assert.roughEqual($appointment.eq(1).outerWidth(), $tableCell.outerWidth() * 2, 1.5, 'appointment has a right size'); }); -QUnit.test('More than 3 small appointments should be grouped', function(assert) { - const items = []; - let i = 8; - while(i > 0) { - items.push({ text: i, startDate: new Date(2015, 1, 9), endDate: new Date(2015, 1, 10) }); - i--; - } - - this.createInstance( - { - currentDate: new Date(2015, 1, 9), - currentView: 'month', - height: 500, - width: 600, - dataSource: items - } - ); - - const $appointments = $(this.instance.$element().find('.dx-scheduler-appointment')); - - assert.equal($appointments.length, 2, 'Small appointments are grouped'); -}); - QUnit.test('Appointments should be rendered without errors (T816873)', function(assert) { this.createInstance( { @@ -707,7 +614,8 @@ QUnit.test('More than 3 cloned appointments should be grouped', function(assert) currentDate: new Date(2015, 1, 9), currentView: 'month', height: 500, - dataSource: items + dataSource: items, + maxAppointmentsPerCell: 2 } ); @@ -740,6 +648,7 @@ QUnit.test('Grouped appointments schould have correct colors', function(assert) currentView: 'month', height: 500, dataSource: items, + maxAppointmentsPerCell: 2, resources: [ { field: 'roomId', @@ -781,6 +690,7 @@ QUnit.test('Grouped appointments schould have correct colors when resourses stor currentView: 'month', height: 500, dataSource: items, + maxAppointmentsPerCell: 2, resources: [ { field: 'roomId', @@ -836,33 +746,6 @@ QUnit.test('Grouped appointments should be reinitialized if datasource is change assert.equal($dropDownMenu.length, 1, 'DropDown appointments are refreshed'); }); -QUnit.test('Parts of long compact appt should have right positions', function(assert) { - const items = [ { text: 'Task 1', startDate: new Date(2015, 2, 4, 2, 0), endDate: new Date(2015, 2, 5, 3, 0) }, - { text: 'Task 2', startDate: new Date(2015, 2, 4, 7, 0), endDate: new Date(2015, 2, 5, 12, 0) }, - { text: 'Task 3', startDate: new Date(2015, 2, 4, 12, 0), endDate: new Date(2015, 2, 7, 2, 0) } ]; - - this.createInstance( - { - currentDate: new Date(2015, 2, 4), - currentView: 'month', - height: 500, - dataSource: items - } - ); - - const $appointment = $(this.instance.$element().find('.dx-scheduler-appointment')); - const tableCellWidth = this.instance.$element().find('.dx-scheduler-date-table-cell').eq(0).outerWidth(); - const gap = 3; - - for(let i = 2; i < $appointment.length; i++) { - const appointmentPosition = translator.locate($appointment.eq(i)); - - assert.deepEqual($appointment.eq(i).outerWidth(), 15, 'appointment has a right size'); - assert.roughEqual(appointmentPosition.top, gap, 1.5, 'part has right position'); - assert.roughEqual(appointmentPosition.left, gap + 3 * tableCellWidth + tableCellWidth * (i - 2), 3, 'part has right position'); - } -}); - QUnit.module('Horizontal Strategy', moduleOptions); QUnit.test('Start date of appointment should be changed when resize is finished', function(assert) { @@ -1192,9 +1075,8 @@ QUnit.test('Four rival appointments should have correct positions', function(ass { currentDate: new Date(2015, 1, 9), currentView: 'timelineDay', - maxAppointmentsPerCell: null, - height: 500, dataSource: items, + maxAppointmentsPerCell: 'unlimited', startDayHour: 1 } ); @@ -1224,9 +1106,9 @@ QUnit.test('Four rival appointments should have correct sizes', function(assert) { currentDate: new Date(2015, 1, 9), currentView: 'timelineDay', - height: 530, dataSource: items, - startDayHour: 1 + startDayHour: 1, + maxAppointmentsPerCell: 'unlimited' } ); @@ -1234,16 +1116,16 @@ QUnit.test('Four rival appointments should have correct sizes', function(assert) const tableCellWidth = this.instance.$element().find('.dx-scheduler-date-table-cell').eq(0).outerWidth() * 2; assert.equal($appointment.eq(0).outerWidth(), tableCellWidth, 'appointment has a right size'); - assert.equal($appointment.eq(0).outerHeight(), 100, 'appointment has a right size'); + assert.roughEqual($appointment.eq(0).outerHeight(), 123.25, 1, 'appointment has a right size'); assert.equal($appointment.eq(1).outerWidth(), tableCellWidth, 'appointment has a right size'); - assert.equal($appointment.eq(1).outerHeight(), 100, 'appointment has a right size'); + assert.roughEqual($appointment.eq(1).outerHeight(), 123.25, 1, 'appointment has a right size'); assert.equal($appointment.eq(2).outerWidth(), tableCellWidth, 'appointment has a right size'); - assert.equal($appointment.eq(2).outerHeight(), 100, 'appointment has a right size'); + assert.roughEqual($appointment.eq(2).outerHeight(), 123.25, 1, 'appointment has a right size'); assert.equal($appointment.eq(3).outerWidth(), tableCellWidth, 'appointment has a right size'); - assert.equal($appointment.eq(3).outerHeight(), 100, 'appointment has a right size'); + assert.roughEqual($appointment.eq(3).outerHeight(), 123.25, 1, 'appointment has a right size'); }); QUnit.test('Recurrence appointment should be rendered correctly on timelineWeek (T701534)', function(assert) { @@ -1316,19 +1198,13 @@ QUnit.test('Four rival all day appointments should have correct sizes', function const $appointments = $(this.instance.$element().find('.dx-scheduler-all-day-appointment')); - assert.equal($appointments.length, 4, 'All appointments are rendered'); + assert.equal($appointments.length, 2, 'All appointments are rendered'); assert.roughEqual($appointments.eq(0).outerWidth(), 798, 1.1, 'appointment has a right width'); assert.roughEqual($appointments.eq(0).outerHeight(), 24, 2, 'appointment has a right height'); assert.roughEqual($appointments.eq(1).outerWidth(), 798, 1.1, 'appointment has a right width'); assert.roughEqual($appointments.eq(1).outerHeight(), 24, 2, 'appointment has a right height'); - - assert.roughEqual($appointments.eq(2).outerWidth(), 15, 1.1, 'appointment has a right width'); - assert.roughEqual($appointments.eq(2).outerHeight(), 15, 1.1, 'appointment has a right height'); - - assert.roughEqual($appointments.eq(3).outerWidth(), 15, 1.1, 'appointment has a right width'); - assert.roughEqual($appointments.eq(3).outerHeight(), 15, 1.1, 'appointment has a right height'); }); QUnit.test('Dates of allDay appointment should be changed when resize is finished, week view RTL mode', function(assert) { @@ -1457,7 +1333,7 @@ QUnit.test('Two rival appointments should have correct positions, vertical strat const $tableCell = $(this.instance.$element().find('.dx-scheduler-date-table-cell').eq(0)); const cellHeight = $tableCell.get(0).getBoundingClientRect().height; const cellWidth = $tableCell.get(0).getBoundingClientRect().width; - const offset = getOffset(); + const offset = APPOINTMENT_DEFAULT_LEFT_OFFSET; assert.equal($appointment.length, 2, 'All appointments are rendered'); @@ -1492,7 +1368,7 @@ QUnit.test('Three rival appointments with two columns should have correct positi const $tableCell = $(this.instance.$element().find('.dx-scheduler-date-table-cell').eq(0)); const cellHeight = $tableCell.get(0).getBoundingClientRect().height; const cellWidth = $tableCell.get(0).getBoundingClientRect().width; - const offset = getOffset(); + const offset = APPOINTMENT_DEFAULT_LEFT_OFFSET; const firstAppointmentPosition = translator.locate($appointment.eq(0)); const secondAppointmentPosition = translator.locate($appointment.eq(1)); const thirdAppointmentPosition = translator.locate($appointment.eq(2)); @@ -1516,9 +1392,9 @@ QUnit.test('Four rival appointments with three columns should have correct posit { currentDate: new Date(2015, 1, 9), currentView: 'week', - height: 500, width: 900, startDayHour: 8, + maxAppointmentsPerCell: 'unlimited', dataSource: [ { text: 'Appointment 1', startDate: new Date(2015, 1, 9, 8), endDate: new Date(2015, 1, 9, 11) }, { text: 'Appointment 2', startDate: new Date(2015, 1, 9, 9), endDate: new Date(2015, 1, 9, 10) }, @@ -1528,26 +1404,24 @@ QUnit.test('Four rival appointments with three columns should have correct posit } ); - const $appointment = $(this.instance.$element().find('.dx-scheduler-appointment')); - const $tableCell = $(this.instance.$element().find('.dx-scheduler-date-table-cell').eq(0)); - const cellHeight = $tableCell.get(0).getBoundingClientRect().height; - const cellWidth = $tableCell.get(0).getBoundingClientRect().width; - const offset = getOffset(); - const expectedAppWidth = (cellWidth - offset) / 3; + const cellHeight = this.scheduler.workSpace.getCellHeight(); + const cellWidth = this.scheduler.workSpace.getCellWidth(); + const unlimitedModeOffset = 5; + const expectedAppWidth = (cellWidth - unlimitedModeOffset) / 3; - assert.equal($appointment.length, 4, 'All appointments are rendered'); + assert.equal(this.scheduler.appointments.getAppointmentCount(), 4, 'All appointments are rendered'); - assert.deepEqual(translator.locate($appointment.eq(0)), { top: 0, left: cellWidth + 100 }, 'appointment is rendered in right place'); - assert.roughEqual($appointment.eq(0).outerWidth(), expectedAppWidth, 1, 'appointment has a right size'); + assert.deepEqual(this.scheduler.appointments.getAppointmentPosition(0), { top: 0, left: cellWidth + 100 }, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentWidth(0), expectedAppWidth, 1, 'appointment has a right size'); - assert.deepEqual(translator.locate($appointment.eq(1)), { top: 2 * cellHeight, left: cellWidth + 100 + 2 * expectedAppWidth }, 'appointment is rendered in right place'); - assert.roughEqual($appointment.eq(1).outerWidth(), expectedAppWidth, 1, 'appointment has a right size'); + assert.deepEqual(this.scheduler.appointments.getAppointmentPosition(1), { top: 2 * cellHeight, left: cellWidth + 100 + 2 * expectedAppWidth }, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentWidth(1), expectedAppWidth, 1, 'appointment has a right size'); - assert.deepEqual(translator.locate($appointment.eq(2)), { top: 0, left: cellWidth + 100 + expectedAppWidth }, 'appointment is rendered in right place'); - assert.roughEqual($appointment.eq(2).outerWidth(), expectedAppWidth, 1, 'appointment has a right size'); + assert.deepEqual(this.scheduler.appointments.getAppointmentPosition(2), { top: 0, left: cellWidth + 100 + expectedAppWidth }, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentWidth(2), expectedAppWidth, 1, 'appointment has a right size'); - assert.deepEqual(translator.locate($appointment.eq(3)), { top: 4 * cellHeight, left: cellWidth + 100 + expectedAppWidth }, 'appointment is rendered in right place'); - assert.roughEqual($appointment.eq(3).outerWidth(), expectedAppWidth, 1, 'appointment has a right size'); + assert.deepEqual(this.scheduler.appointments.getAppointmentPosition(3), { top: 4 * cellHeight, left: cellWidth + 100 + expectedAppWidth }, 'appointment is rendered in right place'); + assert.roughEqual(this.scheduler.appointments.getAppointmentWidth(3), expectedAppWidth, 1, 'appointment has a right size'); }); QUnit.test('Rival duplicated appointments should have correct positions', function(assert) { @@ -1566,20 +1440,17 @@ QUnit.test('Rival duplicated appointments should have correct positions', functi } ); - const $appointment = $(this.instance.$element().find('.dx-scheduler-appointment')); - const $tableCell = $(this.instance.$element().find('.dx-scheduler-date-table-cell').eq(0)); - const cellWidth = $tableCell.outerWidth(); - const offset = getOffset(); + const cellWidth = this.scheduler.workSpace.getCellWidth(); + const offset = APPOINTMENT_DEFAULT_LEFT_OFFSET; - assert.equal($appointment.length, 3, 'All appointments are rendered'); - assert.deepEqual(translator.locate($appointment.eq(0)), { top: 0, left: cellWidth + 100 }, 'appointment is rendered in right place'); - assert.equal($appointment.eq(0).outerWidth(), cellWidth - offset, 'appointment has a right size'); + assert.equal(this.scheduler.appointments.getAppointmentCount(), 2, 'All appointments are rendered'); + assert.deepEqual(this.scheduler.appointments.getAppointmentPosition(0), { top: 0, left: cellWidth + 100 }, 'appointment is rendered in right place'); + assert.equal(this.scheduler.appointments.getAppointmentWidth(0), cellWidth - offset, 'appointment has a right size'); - assert.deepEqual(translator.locate($appointment.eq(1)), { top: 0, left: 2 * cellWidth + 100 }, 'appointment is rendered in right place'); - assert.roughEqual($appointment.eq(1).outerWidth(), (cellWidth - offset) / 2, 1, 'appointment has a right size'); + assert.deepEqual(this.scheduler.appointments.getAppointmentPosition(1), { top: 0, left: 2 * cellWidth + 100 }, 'appointment is rendered in right place'); + assert.equal(this.scheduler.appointments.getAppointmentWidth(1), cellWidth - offset, 'appointment has a right size'); - assert.deepEqual(translator.locate($appointment.eq(2)), { top: 0, left: 2 * cellWidth + 100 + (cellWidth - offset) / 2 }, 'appointment is rendered in right place'); - assert.roughEqual($appointment.eq(2).outerWidth(), (cellWidth - offset) / 2, 1, 'appointment has a right size'); + assert.equal(this.scheduler.appointments.compact.getButtonCount(), 1, 'Compact button is rendered'); }); QUnit.test('More than 3 all-day appointments should be grouped', function(assert) { @@ -1629,12 +1500,12 @@ QUnit.test('Two rival all day appointments should have correct sizes and positio assert.equal(firstAppointmentPosition.top, 0, 'appointment is rendered in right place'); assert.roughEqual(firstAppointmentPosition.left, 100, 1, 'appointment is rendered in right place'); assert.roughEqual($appointment.eq(0).outerWidth(), 798, 1.1, 'appointment has a right width'); - assert.roughEqual($appointment.eq(0).outerHeight(), 37, 1.1, 'appointment has a right height'); + assert.roughEqual($appointment.eq(0).outerHeight(), 24.5, 1.1, 'appointment has a right height'); - assert.roughEqual(secondAppointmentPosition.top, 37, 1, 'appointment is rendered in right place'); + assert.roughEqual(secondAppointmentPosition.top, 24.5, 1, 'appointment is rendered in right place'); assert.roughEqual(secondAppointmentPosition.left, 100, 1, 'appointment is rendered in right place'); assert.roughEqual($appointment.eq(1).outerWidth(), 798, 1.1, 'appointment has a right width'); - assert.roughEqual($appointment.eq(1).outerHeight(), 37, 1.1, 'appointment has a right height'); + assert.roughEqual($appointment.eq(1).outerHeight(), 24.5, 1.1, 'appointment has a right height'); }); QUnit.test('All day appointments should have correct left position, vertical strategy, rtl mode', function(assert) { @@ -1660,35 +1531,6 @@ QUnit.test('All day appointments should have correct left position, vertical str assert.roughEqual(appointmentPosition.left, $allDayCell.outerWidth() * 2, 2, 'Appointment left coordinate has been adjusted '); }); -QUnit.test('Parts of long compact appt should have right positions', function(assert) { - this.createInstance( - { - currentDate: new Date(2015, 2, 4), - currentView: 'week', - height: 500, - width: 900, - startDayHour: 8, - dataSource: [ - { text: 'Task 1', startDate: new Date(2015, 2, 4, 2, 0), endDate: new Date(2015, 2, 4, 3, 0), allDay: true }, - { text: 'Task 2', startDate: new Date(2015, 2, 4, 7, 0), endDate: new Date(2015, 2, 4, 12, 0), allDay: true }, - { text: 'Task 4', startDate: new Date(2015, 2, 4, 2, 0), endDate: new Date(2015, 2, 8, 2, 0), allDay: true }] - } - ); - - const $appointment = $(this.instance.$element().find('.dx-scheduler-appointment')); - const gap = 3; - const cellBorderOffset = 1; - const cellWidth = this.instance.$element().find('.dx-scheduler-all-day-table-cell').eq(0).outerWidth(); - - for(let i = 2; i < $appointment.length; i++) { - const appointmentPosition = translator.locate($appointment.eq(i)); - - assert.equal($appointment.eq(i).outerWidth(), 15, 'appointment has a right size'); - assert.equal(appointmentPosition.top, gap, 'Appointment top is OK'); - assert.roughEqual(appointmentPosition.left, (cellBorderOffset + cellWidth) * (i + 1) + 100, 3, 'Appointment left is OK'); - } -}); - QUnit.module('Appointments Keyboard Navigation', { beforeEach: function() { moduleOptions.beforeEach.apply(this); @@ -2420,18 +2262,19 @@ QUnit.test('Full-size appointment should have minWidth, narrow width', function( { currentDate: new Date(2015, 2, 4), currentView: 'week', + maxAppointmentsPerCell: 'unlimited', views: [{ type: 'week' }], - width: 200, + width: 160, dataSource: items } ); const $appointments = $(this.instance.$element()).find('.dx-scheduler-appointment'); - assert.equal($appointments.eq(0).get(0).getBoundingClientRect().width, 5, 'Appointment has min width'); - assert.equal($appointments.eq(1).get(0).getBoundingClientRect().width, 5, 'Appointment has min width'); + assert.roughEqual($appointments.eq(0).get(0).getBoundingClientRect().width, 5, 1.1, 'Appointment has min width'); + assert.roughEqual($appointments.eq(1).get(0).getBoundingClientRect().width, 5, 1.1, 'Appointment has min width'); }); QUnit.test('Full-size appointment count depends on maxAppointmentsPerCell option, \'auto\' mode, narrow width', function(assert) { @@ -2511,7 +2354,7 @@ QUnit.test('Full-size appointment should have correct size, \'auto\' mode', func const $appointment = $(this.instance.$element().find('.dx-scheduler-appointment')).eq(0); const tableCellWidth = this.instance.$element().find('.dx-scheduler-date-table-cell').eq(0).outerWidth(); const appointmentWidth = $appointment.outerWidth(); - const offset = getOffset(); + const offset = APPOINTMENT_DEFAULT_LEFT_OFFSET; assert.roughEqual(appointmentWidth, tableCellWidth - offset, 1.5, 'appointment is full-size'); }); @@ -2627,28 +2470,6 @@ QUnit.test('Full-size appointment count depends on maxAppointmentsPerCell option assert.equal($dropDownMenu.length, 0, 'ddAppointment isn\'t rendered'); }); -QUnit.test('Appointments should not have specific class if maxAppointmentsPerCell=null', function(assert) { - const items = [ - { text: 'Task 2', startDate: new Date(2015, 2, 1, 0, 0), endDate: new Date(2015, 2, 1, 2, 0) }]; - - this.createInstance( - { - currentDate: new Date(2015, 2, 4), - currentView: 'week', - width: 800, - views: [{ - type: 'week', - maxAppointmentsPerCell: null - }], - height: 600, - dataSource: items - } - ); - - const $appointment = $(this.instance.$element().find('.dx-scheduler-appointment')); - assert.ok(!$appointment.eq(0).hasClass('dx-scheduler-appointment-empty'), 'appointment has not the class'); -}); - QUnit.test('_isAppointmentEmpty should work correctly in different strategies', function(assert) { this.createInstance({ views: ['timelineDay', 'week'], diff --git a/testing/tests/DevExpress.ui.widgets.scheduler/utils.recurrence.tests.js b/testing/tests/DevExpress.ui.widgets.scheduler/utils.recurrence.tests.js index ac03c985238b..37756f1ccc34 100644 --- a/testing/tests/DevExpress.ui.widgets.scheduler/utils.recurrence.tests.js +++ b/testing/tests/DevExpress.ui.widgets.scheduler/utils.recurrence.tests.js @@ -508,10 +508,18 @@ QUnit.test('get date by month recurrence with BYMONTHDAY at 31', function(assert assert.deepEqual(dates, [new Date(2015, 2, 31), new Date(2015, 4, 31)], 'dates are right'); }); -QUnit.test('get date by month recurrence with BYMONTHDAY at 31', function(assert) { - const dates = recurrenceUtils.getDatesByRecurrence({ rule: 'FREQ=DAILY;BYMONTHDAY=31', start: new Date(2015, 1, 15), min: new Date(2015, 1, 15), max: new Date(2015, 5, 5) }); +QUnit.test('get date by month recurrence with BYMONTHDAY=31, FREQ=MONTHLY and skiping dates with last day of month < 31', function(assert) { + const start = new Date(2020, 2, 31, 9, 30); + + const dates = recurrenceUtils.getDatesByRecurrence({ + rule: 'FREQ=MONTHLY;BYMONTHDAY=31;COUNT=3', + start: start, + end: new Date(2020, 2, 31, 11), + min: new Date(2020, 2, 1), + max: new Date(2020, 10, 1) + }); - assert.deepEqual(dates, [new Date(2015, 2, 31), new Date(2015, 4, 31)], 'dates are right'); + assert.deepEqual(dates, [start, new Date(2020, 4, 31, 9, 30), new Date(2020, 6, 31, 9, 30)], 'Recurrence dates has no months with the last day < 31'); }); QUnit.test('get days of the week by byDay rule', function(assert) { diff --git a/testing/tests/DevExpress.ui.widgets.treeList/adaptiveColumns.tests.js b/testing/tests/DevExpress.ui.widgets.treeList/adaptiveColumns.tests.js index 647dd9458354..9be11237aafd 100644 --- a/testing/tests/DevExpress.ui.widgets.treeList/adaptiveColumns.tests.js +++ b/testing/tests/DevExpress.ui.widgets.treeList/adaptiveColumns.tests.js @@ -58,24 +58,25 @@ QUnit.module('API', { afterEach: function() { this.clock.restore(); } -}); +}, () => { -QUnit.test('The detail adaptive row should have the node property', function(assert) { + QUnit.test('The detail adaptive row should have the node property', function(assert) { // arrange - $('.dx-treelist').width(200); + $('.dx-treelist').width(200); - setupTreeList(this); - this.rowsView.render($('#container')); - this.resizingController.updateDimensions(); - this.clock.tick(); + setupTreeList(this); + this.rowsView.render($('#container')); + this.resizingController.updateDimensions(); + this.clock.tick(); - // act - this.adaptiveColumnsController.expandAdaptiveDetailRow(1); - this.clock.tick(); + // act + this.adaptiveColumnsController.expandAdaptiveDetailRow(1); + this.clock.tick(); - // assert - const rows = this.getVisibleRows(); - assert.ok($('.dx-adaptive-detail-row').length, 'render field items'); - assert.strictEqual(rows[1].rowType, 'detailAdaptive', 'detail adaptive row'); - assert.deepEqual(rows[1].node, rows[0].node, 'detail adaptive row has the node property'); + // assert + const rows = this.getVisibleRows(); + assert.ok($('.dx-adaptive-detail-row').length, 'render field items'); + assert.strictEqual(rows[1].rowType, 'detailAdaptive', 'detail adaptive row'); + assert.deepEqual(rows[1].node, rows[0].node, 'detail adaptive row has the node property'); + }); }); diff --git a/testing/tests/DevExpress.ui.widgets.treeList/dataController.tests.js b/testing/tests/DevExpress.ui.widgets.treeList/dataController.tests.js index 15bb748f57ae..9232c82a6a0a 100644 --- a/testing/tests/DevExpress.ui.widgets.treeList/dataController.tests.js +++ b/testing/tests/DevExpress.ui.widgets.treeList/dataController.tests.js @@ -45,1201 +45,1202 @@ function foreachNodes(nodes, func) { } } -QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownModule }); +QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownModule }, () => { -QUnit.test('No initialization', function(assert) { - assert.deepEqual(this.dataController.items(), []); - assert.ok(!this.dataController.isLoading()); - assert.ok(this.dataController.isLoaded()); -}); + QUnit.test('No initialization', function(assert) { + assert.deepEqual(this.dataController.items(), []); + assert.ok(!this.dataController.isLoading()); + assert.ok(this.dataController.isLoaded()); + }); -QUnit.test('Paginate should be disabled', function(assert) { + QUnit.test('Paginate should be disabled', function(assert) { // arrange, act - this.applyOptions({ - dataSource: [] - }); + this.applyOptions({ + dataSource: [] + }); - // assert - assert.notOk(this.dataController.dataSource().paginate(), 'paginate is disabled'); -}); + // assert + assert.notOk(this.dataController.dataSource().paginate(), 'paginate is disabled'); + }); -QUnit.test('Paginate should be enabled when virtual scrolling is enabled', function(assert) { + QUnit.test('Paginate should be enabled when virtual scrolling is enabled', function(assert) { // arrange, act - this.applyOptions({ - dataSource: [], - scrolling: { - mode: 'virtual' - } - }); + this.applyOptions({ + dataSource: [], + scrolling: { + mode: 'virtual' + } + }); - // assert - assert.ok(this.dataController.dataSource().paginate(), 'paginate is enabled'); -}); + // assert + assert.ok(this.dataController.dataSource().paginate(), 'paginate is enabled'); + }); -QUnit.test('Initialize from dataSource with plain structure', function(assert) { + QUnit.test('Initialize from dataSource with plain structure', function(assert) { // arrange - let items; - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 } - ]; - const dataSource = createDataSource(array); + let items; + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 } + ]; + const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); + this.dataController.setDataSource(dataSource); - // act - dataSource.load(); - - // TODO: remove when implemented expandAllEnabled - this.expandRow(2); - - // assert - items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); - - assert.equal(items[0].rowType, 'data', 'rowType of first item'); - assert.equal(items[0].key, 1, 'key of first item'); - assert.deepEqual(items[0].data, { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, 'data of first item'); - assert.deepEqual(items[0].values, ['Category1', '55-55-55', 1, 0], 'values of first item'); - assert.equal(items[0].node.children.length, 0, 'count children of first item'); - assert.notOk(items[0].node.hasChildren, 'first item hasn\'t children'); - assert.equal(items[0].level, 0, 'level of first item'); - assert.equal(items[0].node.parent.key, 0, 'first item has root parentKey'); - - assert.equal(items[1].rowType, 'data', 'rowType of second item'); - assert.equal(items[1].key, 2, 'key of second item'); - assert.deepEqual(items[1].data, { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, 'data of second item'); - assert.deepEqual(items[1].values, ['Category2', '98-75-21', 2, 0], 'values of second item'); - assert.equal(items[1].node.children.length, 1, 'count children of second item'); - assert.ok(items[1].node.hasChildren, 'first item has children'); - assert.equal(items[1].node.level, 0, 'level of second item'); - assert.equal(items[1].node.parent.key, 0, 'second item has root parentKey'); - assert.deepEqual(items[1].node.children[0], items[2].node, 'child of second item'); - - assert.equal(items[2].rowType, 'data', 'rowType of second item'); - assert.equal(items[2].key, 3, 'key of second item'); - assert.deepEqual(items[2].data, { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, 'data of second item'); - assert.deepEqual(items[2].values, ['SubCategory1', '55-66-77', 3, 2], 'values of second item'); - assert.equal(items[2].node.children.length, 0, 'count children of second item'); - assert.notOk(items[2].node.hasChildren, 'first item hasn\'t children'); - assert.equal(items[2].level, 1, 'level of second item'); - assert.equal(items[2].node.parent.key, items[1].key, 'second item has parentKey'); -}); + // act + dataSource.load(); -QUnit.test('root node should have correct key and level', function(assert) { - // arrange - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 } - ]; - const dataSource = createDataSource(array); + // TODO: remove when implemented expandAllEnabled + this.expandRow(2); + + // assert + items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); + + assert.equal(items[0].rowType, 'data', 'rowType of first item'); + assert.equal(items[0].key, 1, 'key of first item'); + assert.deepEqual(items[0].data, { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, 'data of first item'); + assert.deepEqual(items[0].values, ['Category1', '55-55-55', 1, 0], 'values of first item'); + assert.equal(items[0].node.children.length, 0, 'count children of first item'); + assert.notOk(items[0].node.hasChildren, 'first item hasn\'t children'); + assert.equal(items[0].level, 0, 'level of first item'); + assert.equal(items[0].node.parent.key, 0, 'first item has root parentKey'); + + assert.equal(items[1].rowType, 'data', 'rowType of second item'); + assert.equal(items[1].key, 2, 'key of second item'); + assert.deepEqual(items[1].data, { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, 'data of second item'); + assert.deepEqual(items[1].values, ['Category2', '98-75-21', 2, 0], 'values of second item'); + assert.equal(items[1].node.children.length, 1, 'count children of second item'); + assert.ok(items[1].node.hasChildren, 'first item has children'); + assert.equal(items[1].node.level, 0, 'level of second item'); + assert.equal(items[1].node.parent.key, 0, 'second item has root parentKey'); + assert.deepEqual(items[1].node.children[0], items[2].node, 'child of second item'); + + assert.equal(items[2].rowType, 'data', 'rowType of second item'); + assert.equal(items[2].key, 3, 'key of second item'); + assert.deepEqual(items[2].data, { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, 'data of second item'); + assert.deepEqual(items[2].values, ['SubCategory1', '55-66-77', 3, 2], 'values of second item'); + assert.equal(items[2].node.children.length, 0, 'count children of second item'); + assert.notOk(items[2].node.hasChildren, 'first item hasn\'t children'); + assert.equal(items[2].level, 1, 'level of second item'); + assert.equal(items[2].node.parent.key, items[1].key, 'second item has parentKey'); + }); + + QUnit.test('root node should have correct key and level', function(assert) { + // arrange + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 } + ]; + const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); + this.dataController.setDataSource(dataSource); - // act - dataSource.load(); + // act + dataSource.load(); - // assert - const rootNode = this.getRootNode(); - assert.strictEqual(rootNode.children.length, 2, 'root node children count'); - assert.strictEqual(rootNode.key, 0, 'root node key'); - assert.strictEqual(rootNode.level, -1, 'root node level'); -}); + // assert + const rootNode = this.getRootNode(); + assert.strictEqual(rootNode.children.length, 2, 'root node children count'); + assert.strictEqual(rootNode.key, 0, 'root node key'); + assert.strictEqual(rootNode.level, -1, 'root node level'); + }); -// T514552 -QUnit.test('nodes should not be recreated after expand', function(assert) { + // T514552 + QUnit.test('nodes should not be recreated after expand', function(assert) { // arrange - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 } - ]; - const dataSource = createDataSource(array); + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 } + ]; + const dataSource = createDataSource(array); - const nodesInitialized = sinon.stub(); + const nodesInitialized = sinon.stub(); - this.applyOptions({ - onNodesInitialized: nodesInitialized - }); + this.applyOptions({ + onNodesInitialized: nodesInitialized + }); - this.dataController.setDataSource(dataSource); + this.dataController.setDataSource(dataSource); - dataSource.load(); - const rootNode = this.getRootNode(); + dataSource.load(); + const rootNode = this.getRootNode(); - // act - this.expandRow(2); + // act + this.expandRow(2); - // assert - assert.strictEqual(nodesInitialized.callCount, 1, 'nodesInitialized called once on first load'); - assert.strictEqual(this.getRootNode(), rootNode, 'root node is not changed'); -}); + // assert + assert.strictEqual(nodesInitialized.callCount, 1, 'nodesInitialized called once on first load'); + assert.strictEqual(this.getRootNode(), rootNode, 'root node is not changed'); + }); -// T635433 -QUnit.test('sorting should not be reapplied after expand', function(assert) { + // T635433 + QUnit.test('sorting should not be reapplied after expand', function(assert) { // arrange - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, - { name: 'Category2', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'Category1', phone: '98-75-21', id: 2, parentId: 0 } - ]; - const dataSource = createDataSource(array); + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, + { name: 'Category2', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'Category1', phone: '98-75-21', id: 2, parentId: 0 } + ]; + const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); + this.dataController.setDataSource(dataSource); - dataSource.load(); + dataSource.load(); - let calculateSortValueCalled = false; + let calculateSortValueCalled = false; - this.columnOption('name', 'calculateSortValue', function(data) { - calculateSortValueCalled = true; - return data.name; - }); + this.columnOption('name', 'calculateSortValue', function(data) { + calculateSortValueCalled = true; + return data.name; + }); - // act - this.columnOption('name', 'sortOrder', 'asc'); + // act + this.columnOption('name', 'sortOrder', 'asc'); - // assert - assert.ok(calculateSortValueCalled, 'sorting is applied'); + // assert + assert.ok(calculateSortValueCalled, 'sorting is applied'); - // act - calculateSortValueCalled = false; - this.expandRow(2); + // act + calculateSortValueCalled = false; + this.expandRow(2); - // assert - assert.notOk(calculateSortValueCalled, 'sorting is not reapplied'); - assert.equal(this.getVisibleRows()[0].data.name, 'Category1', 'rows are sorted'); -}); + // assert + assert.notOk(calculateSortValueCalled, 'sorting is not reapplied'); + assert.equal(this.getVisibleRows()[0].data.name, 'Category1', 'rows are sorted'); + }); -QUnit.test('nodes should be recreated after change sorting', function(assert) { + QUnit.test('nodes should be recreated after change sorting', function(assert) { // arrange - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 } - ]; - const dataSource = createDataSource(array); + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 } + ]; + const dataSource = createDataSource(array); - const nodesInitialized = sinon.stub(); + const nodesInitialized = sinon.stub(); - this.applyOptions({ - onNodesInitialized: nodesInitialized - }); + this.applyOptions({ + onNodesInitialized: nodesInitialized + }); - this.dataController.setDataSource(dataSource); + this.dataController.setDataSource(dataSource); - dataSource.load(); - const rootNode = this.getRootNode(); + dataSource.load(); + const rootNode = this.getRootNode(); - // act - dataSource.sort({ selector: 'name', desc: false }); - dataSource.load(); + // act + dataSource.sort({ selector: 'name', desc: false }); + dataSource.load(); - // assert - assert.strictEqual(nodesInitialized.callCount, 2, 'nodesInitialized called after change sorting'); - assert.notStrictEqual(this.getRootNode(), rootNode, 'root node is changed'); -}); + // assert + assert.strictEqual(nodesInitialized.callCount, 2, 'nodesInitialized called after change sorting'); + assert.notStrictEqual(this.getRootNode(), rootNode, 'root node is changed'); + }); -QUnit.test('Initialize from dataSource with hierarchical structure', function(assert) { + QUnit.test('Initialize from dataSource with hierarchical structure', function(assert) { // arrange - let items; - const array = [ - { name: 'Category1', phone: '55-55-55' }, - { name: 'Category2', phone: '98-75-21', items: [ - { name: 'SubCategory1', phone: '55-66-77' }, - { name: 'SubCategory2', phone: '56-76-79' }] - } - ]; - const dataSource = createDataSource(array); + let items; + const array = [ + { name: 'Category1', phone: '55-55-55' }, + { name: 'Category2', phone: '98-75-21', items: [ + { name: 'SubCategory1', phone: '55-66-77' }, + { name: 'SubCategory2', phone: '56-76-79' }] + } + ]; + const dataSource = createDataSource(array); - this.applyOptions({ - itemsExpr: 'items', - dataStructure: 'tree' - }); - this.dataController.setDataSource(dataSource); + this.applyOptions({ + itemsExpr: 'items', + dataStructure: 'tree' + }); + this.dataController.setDataSource(dataSource); - // act - dataSource.load(); + // act + dataSource.load(); - // TODO: remove when implemented expandedRowKeys - this.expandRow(2); + // TODO: remove when implemented expandedRowKeys + this.expandRow(2); - // assert - items = this.dataController.items(); - assert.equal(items.length, 4, 'count items'); + // assert + items = this.dataController.items(); + assert.equal(items.length, 4, 'count items'); - assert.equal(items[0].key, 1, 'key of first item'); - assert.deepEqual(items[0].data, { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, 'data of first item'); - assert.equal(items[0].level, 0, 'level of first item'); + assert.equal(items[0].key, 1, 'key of first item'); + assert.deepEqual(items[0].data, { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, 'data of first item'); + assert.equal(items[0].level, 0, 'level of first item'); - assert.equal(items[1].key, 2, 'key of second item'); - assert.deepEqual(items[1].data, { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, 'data of second item'); - assert.equal(items[1].level, 0, 'level of second item'); + assert.equal(items[1].key, 2, 'key of second item'); + assert.deepEqual(items[1].data, { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, 'data of second item'); + assert.equal(items[1].level, 0, 'level of second item'); - assert.equal(items[2].key, 3, 'key of third item'); - assert.deepEqual(items[2].data, { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, 'data of third item'); - assert.equal(items[2].level, 1, 'level of third item'); - assert.equal(items[2].node.parent.key, items[1].key, 'third item has parentKey'); + assert.equal(items[2].key, 3, 'key of third item'); + assert.deepEqual(items[2].data, { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, 'data of third item'); + assert.equal(items[2].level, 1, 'level of third item'); + assert.equal(items[2].node.parent.key, items[1].key, 'third item has parentKey'); - assert.equal(items[3].key, 4, 'key of fourth item'); - assert.deepEqual(items[3].data, { name: 'SubCategory2', phone: '56-76-79', id: 4, parentId: 2 }, 'data of fourth item'); - assert.equal(items[3].level, 1, 'level of fourth item'); - assert.equal(items[3].node.parent.key, items[1].key, 'fourth item has parentKey'); -}); + assert.equal(items[3].key, 4, 'key of fourth item'); + assert.deepEqual(items[3].data, { name: 'SubCategory2', phone: '56-76-79', id: 4, parentId: 2 }, 'data of fourth item'); + assert.equal(items[3].level, 1, 'level of fourth item'); + assert.equal(items[3].node.parent.key, items[1].key, 'fourth item has parentKey'); + }); -QUnit.test('Initialize from dataSource with hierarchical structure when \'keyExpr\' option is specified', function(assert) { + QUnit.test('Initialize from dataSource with hierarchical structure when \'keyExpr\' option is specified', function(assert) { // arrange - let items; - const array = [ - { name: 'Category1', phone: '55-55-55', key: 'key1' }, - { name: 'Category2', phone: '98-75-21', key: 'key2', items: [ - { name: 'SubCategory1', phone: '55-66-77', key: 'key3' }, - { name: 'SubCategory2', phone: '56-76-79', key: 'key4' }] - } - ]; - const dataSource = createDataSource(array); + let items; + const array = [ + { name: 'Category1', phone: '55-55-55', key: 'key1' }, + { name: 'Category2', phone: '98-75-21', key: 'key2', items: [ + { name: 'SubCategory1', phone: '55-66-77', key: 'key3' }, + { name: 'SubCategory2', phone: '56-76-79', key: 'key4' }] + } + ]; + const dataSource = createDataSource(array); - this.applyOptions({ - itemsExpr: 'items', - dataStructure: 'tree', - keyExpr: 'key' - }); - this.dataController.setDataSource(dataSource); + this.applyOptions({ + itemsExpr: 'items', + dataStructure: 'tree', + keyExpr: 'key' + }); + this.dataController.setDataSource(dataSource); - // act - dataSource.load(); + // act + dataSource.load(); - // TODO: remove when implemented expandedRowKeys - this.expandRow('key2'); + // TODO: remove when implemented expandedRowKeys + this.expandRow('key2'); - // assert - items = this.dataController.items(); - assert.equal(items.length, 4, 'count items'); + // assert + items = this.dataController.items(); + assert.equal(items.length, 4, 'count items'); - assert.equal(items[0].key, 'key1', 'key of first item'); - assert.deepEqual(items[0].data, { name: 'Category1', phone: '55-55-55', key: 'key1', parentId: 0 }, 'data of first item'); - assert.equal(items[0].level, 0, 'level of first item'); + assert.equal(items[0].key, 'key1', 'key of first item'); + assert.deepEqual(items[0].data, { name: 'Category1', phone: '55-55-55', key: 'key1', parentId: 0 }, 'data of first item'); + assert.equal(items[0].level, 0, 'level of first item'); - assert.equal(items[1].key, 'key2', 'key of second item'); - assert.deepEqual(items[1].data, { name: 'Category2', phone: '98-75-21', key: 'key2', parentId: 0 }, 'data of second item'); - assert.equal(items[1].level, 0, 'level of second item'); + assert.equal(items[1].key, 'key2', 'key of second item'); + assert.deepEqual(items[1].data, { name: 'Category2', phone: '98-75-21', key: 'key2', parentId: 0 }, 'data of second item'); + assert.equal(items[1].level, 0, 'level of second item'); - assert.equal(items[2].key, 'key3', 'key of third item'); - assert.deepEqual(items[2].data, { name: 'SubCategory1', phone: '55-66-77', key: 'key3', parentId: 'key2' }, 'data of third item'); - assert.equal(items[2].level, 1, 'level of third item'); - assert.equal(items[2].node.parent.key, items[1].key, 'third item has parentKey'); + assert.equal(items[2].key, 'key3', 'key of third item'); + assert.deepEqual(items[2].data, { name: 'SubCategory1', phone: '55-66-77', key: 'key3', parentId: 'key2' }, 'data of third item'); + assert.equal(items[2].level, 1, 'level of third item'); + assert.equal(items[2].node.parent.key, items[1].key, 'third item has parentKey'); - assert.equal(items[3].key, 'key4', 'key of fourth item'); - assert.deepEqual(items[3].data, { name: 'SubCategory2', phone: '56-76-79', key: 'key4', parentId: 'key2' }, 'data of fourth item'); - assert.equal(items[3].level, 1, 'level of fourth item'); - assert.equal(items[3].node.parent.key, items[1].key, 'fourth item has parentKey'); -}); + assert.equal(items[3].key, 'key4', 'key of fourth item'); + assert.deepEqual(items[3].data, { name: 'SubCategory2', phone: '56-76-79', key: 'key4', parentId: 'key2' }, 'data of fourth item'); + assert.equal(items[3].level, 1, 'level of fourth item'); + assert.equal(items[3].node.parent.key, items[1].key, 'fourth item has parentKey'); + }); -QUnit.test('Initialize from dataSource when there is key of store (without the specified \'keyEpxr\' option)', function(assert) { + QUnit.test('Initialize from dataSource when there is key of store (without the specified \'keyEpxr\' option)', function(assert) { // arrange - let items; - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'Category2', phone: '98-75-21', id: 2 } - ]; - const dataSource = createDataSource(array, { key: 'id' }); + let items; + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'Category2', phone: '98-75-21', id: 2 } + ]; + const dataSource = createDataSource(array, { key: 'id' }); - this.applyOptions({ keyExpr: null }); - this.dataController.setDataSource(dataSource); + this.applyOptions({ keyExpr: null }); + this.dataController.setDataSource(dataSource); - // act - dataSource.load(); + // act + dataSource.load(); - // TODO: remove when implemented expandAllEnabled - this.expandRow(2); + // TODO: remove when implemented expandAllEnabled + this.expandRow(2); - // assert - items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); + // assert + items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); - assert.equal(items[0].key, 1, 'key of first item'); - assert.equal(items[0].node.children.length, 0, 'count children of first item'); - assert.notOk(items[0].node.parent.key, 'first item hasn\'t parentKey'); + assert.equal(items[0].key, 1, 'key of first item'); + assert.equal(items[0].node.children.length, 0, 'count children of first item'); + assert.notOk(items[0].node.parent.key, 'first item hasn\'t parentKey'); - assert.equal(items[1].key, 2, 'key of second item'); - assert.equal(items[1].node.children.length, 1, 'count children of second item'); - assert.deepEqual(items[1].node.children[0], items[2].node, 'child of second item'); - assert.notOk(items[1].node.parent.key, 'second item hasn\'t parentKey'); + assert.equal(items[1].key, 2, 'key of second item'); + assert.equal(items[1].node.children.length, 1, 'count children of second item'); + assert.deepEqual(items[1].node.children[0], items[2].node, 'child of second item'); + assert.notOk(items[1].node.parent.key, 'second item hasn\'t parentKey'); - assert.equal(items[2].key, 3, 'key of second item'); - assert.equal(items[2].node.children.length, 0, 'count children of third item'); - assert.equal(items[2].node.parent.key, items[1].key, 'third item has parentKey'); -}); + assert.equal(items[2].key, 3, 'key of second item'); + assert.equal(items[2].node.children.length, 0, 'count children of third item'); + assert.equal(items[2].node.parent.key, items[1].key, 'third item has parentKey'); + }); -QUnit.test('Checking key of store when dataSource as array', function(assert) { + QUnit.test('Checking key of store when dataSource as array', function(assert) { // arrange - let dataSource; - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'Category2', phone: '98-75-21', id: 2 } - ]; + let dataSource; + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'Category2', phone: '98-75-21', id: 2 } + ]; - // act - this.applyOptions({ dataSource: array }); + // act + this.applyOptions({ dataSource: array }); - // assert - dataSource = this.getDataSource(); - assert.equal(dataSource.store().key(), 'id', 'key of store'); -}); + // assert + dataSource = this.getDataSource(); + assert.equal(dataSource.store().key(), 'id', 'key of store'); + }); -QUnit.test('Exception when key of store not equal \'keyExpr\' option value', function(assert) { + QUnit.test('Exception when key of store not equal \'keyExpr\' option value', function(assert) { // arrange - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'Category2', phone: '98-75-21', id: 2 } - ]; - const dataSource = createDataSource(array, { key: 'name' }); + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'Category2', phone: '98-75-21', id: 2 } + ]; + const dataSource = createDataSource(array, { key: 'name' }); - // act, assert - try { - this.dataController.setDataSource(dataSource); - assert.ok(false, 'exception should be rised'); - } catch(e) { - assert.ok(e.message.indexOf('E1044') >= 0, 'name of error'); - } -}); + // act, assert + try { + this.dataController.setDataSource(dataSource); + assert.ok(false, 'exception should be rised'); + } catch(e) { + assert.ok(e.message.indexOf('E1044') >= 0, 'name of error'); + } + }); -QUnit.test('Error on loading when key is not specified in data', function(assert) { + QUnit.test('Error on loading when key is not specified in data', function(assert) { // arrange - const dataErrors = []; - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'Category2', phone: '98-75-21', id: 2 } - ]; - const dataSource = createDataSource(array); + const dataErrors = []; + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'Category2', phone: '98-75-21', id: 2 } + ]; + const dataSource = createDataSource(array); - this.applyOptions({ - keyExpr: 'key' - }); - this.dataController.setDataSource(dataSource); - this.dataController.dataErrorOccurred.add(function(e) { - dataErrors.push(e); - }); + this.applyOptions({ + keyExpr: 'key' + }); + this.dataController.setDataSource(dataSource); + this.dataController.dataErrorOccurred.add(function(e) { + dataErrors.push(e); + }); - // act - dataSource.load(); + // act + dataSource.load(); - // assert - assert.equal(dataErrors.length, 1, 'count error'); - assert.equal(dataErrors[0].__id, 'E1046', 'error id'); -}); + // assert + assert.equal(dataErrors.length, 1, 'count error'); + assert.equal(dataErrors[0].__id, 'E1046', 'error id'); + }); -QUnit.test('Update items', function(assert) { + QUnit.test('Update items', function(assert) { // arrange - let items; - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'Category2', phone: '98-75-21', id: 2 } - ]; - const dataSource = createDataSource(array); + let items; + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'Category2', phone: '98-75-21', id: 2 } + ]; + const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); - dataSource.load(); - this.expandRow(2); // TODO: remove when implemented expandAllEnabled + this.dataController.setDataSource(dataSource); + dataSource.load(); + this.expandRow(2); // TODO: remove when implemented expandAllEnabled - // act - this.dataController.updateItems(); + // act + this.dataController.updateItems(); - // assert - items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); -}); + // assert + items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); + }); -QUnit.test('Initialize from dataSource with plain structure when virtual scrolling enabled', function(assert) { + QUnit.test('Initialize from dataSource with plain structure when virtual scrolling enabled', function(assert) { - // arrange - let items; - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 5, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, - { name: 'Category3', phone: '98-75-22', id: 3, parentId: 0 }, - { name: 'Category4', phone: '98-75-23', id: 4, parentId: 0 } - ]; - const dataSource = createDataSource(array, {}, { pageSize: 3 }); + // arrange + let items; + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 5, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, + { name: 'Category3', phone: '98-75-22', id: 3, parentId: 0 }, + { name: 'Category4', phone: '98-75-23', id: 4, parentId: 0 } + ]; + const dataSource = createDataSource(array, {}, { pageSize: 3 }); - // act - this.applyOptions({ - scrolling: { - mode: 'virtual', - preventPreload: true - }, - dataSource: dataSource - }); - - // assert - assert.equal(this.dataController.totalItemsCount(), 4, 'totalItemsCount'); - items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); - - assert.equal(items[0].key, 1, 'key of first item'); - assert.equal(items[1].key, 2, 'key of second item'); - assert.equal(items[2].key, 3, 'key of third item'); -}); + // act + this.applyOptions({ + scrolling: { + mode: 'virtual', + preventPreload: true + }, + dataSource: dataSource + }); -QUnit.test('Initialize when remoteOperations and virtual scrolling are enabled and two pages are loaded', function(assert) { - // arrange - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 5, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, - { name: 'Category3', phone: '98-75-22', id: 3, parentId: 0 }, - { name: 'Category4', phone: '98-75-23', id: 4, parentId: 0 } - ]; - const dataSource = createDataSource(array, {}, { pageSize: 2 }); + // assert + assert.equal(this.dataController.totalItemsCount(), 4, 'totalItemsCount'); + items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); - // act - this.applyOptions({ - scrolling: { - mode: 'virtual' - }, - remoteOperations: { filtering: true }, - autoExpandAll: true, - dataSource: dataSource - }); - - // assert - assert.equal(this.dataController.totalItemsCount(), 5, 'totalItemsCount'); - assert.equal(this.getVisibleRows().length, 4, 'row count'); - assert.strictEqual(this.getVisibleRows()[0].node, this.getNodeByKey(1), 'first node instance is correct'); -}); + assert.equal(items[0].key, 1, 'key of first item'); + assert.equal(items[1].key, 2, 'key of second item'); + assert.equal(items[2].key, 3, 'key of third item'); + }); -QUnit.test('Expand node when virtual scrolling enabled', function(assert) { + QUnit.test('Initialize when remoteOperations and virtual scrolling are enabled and two pages are loaded', function(assert) { // arrange - let items; - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 5, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, - { name: 'Category3', phone: '98-75-22', id: 3, parentId: 0 }, - { name: 'Category4', phone: '98-75-23', id: 4, parentId: 0 } - ]; - const dataSource = createDataSource(array, {}, { pageSize: 3 }); + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 5, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, + { name: 'Category3', phone: '98-75-22', id: 3, parentId: 0 }, + { name: 'Category4', phone: '98-75-23', id: 4, parentId: 0 } + ]; + const dataSource = createDataSource(array, {}, { pageSize: 2 }); - this.applyOptions({ - scrolling: { - mode: 'virtual', - preventPreload: true - }, - dataSource: dataSource + // act + this.applyOptions({ + scrolling: { + mode: 'virtual' + }, + remoteOperations: { filtering: true }, + autoExpandAll: true, + dataSource: dataSource + }); + + // assert + assert.equal(this.dataController.totalItemsCount(), 5, 'totalItemsCount'); + assert.equal(this.getVisibleRows().length, 4, 'row count'); + assert.strictEqual(this.getVisibleRows()[0].node, this.getNodeByKey(1), 'first node instance is correct'); }); - // act - this.expandRow(2); + QUnit.test('Expand node when virtual scrolling enabled', function(assert) { + // arrange + let items; + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 5, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, + { name: 'Category3', phone: '98-75-22', id: 3, parentId: 0 }, + { name: 'Category4', phone: '98-75-23', id: 4, parentId: 0 } + ]; + const dataSource = createDataSource(array, {}, { pageSize: 3 }); + + this.applyOptions({ + scrolling: { + mode: 'virtual', + preventPreload: true + }, + dataSource: dataSource + }); - // assert - assert.equal(this.dataController.totalItemsCount(), 5, 'totalItemsCount'); - items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); + // act + this.expandRow(2); - assert.equal(items[0].key, 1, 'key of first item'); - assert.equal(items[1].key, 2, 'key of second item'); - assert.equal(items[2].key, 5, 'key of third item'); -}); + // assert + assert.equal(this.dataController.totalItemsCount(), 5, 'totalItemsCount'); + items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); -QUnit.test('Get total items count', function(assert) { - // arrange - this.applyOptions({ - dataSource: [ - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'SubCategory1', phone: '35-35-35', id: 2, parentId: 1 }, - { name: 'SubCategory2', phone: '45-45-45', id: 3, parentId: 1 } - ] + assert.equal(items[0].key, 1, 'key of first item'); + assert.equal(items[1].key, 2, 'key of second item'); + assert.equal(items[2].key, 5, 'key of third item'); }); - // act - this.dataController.load(); + QUnit.test('Get total items count', function(assert) { + // arrange + this.applyOptions({ + dataSource: [ + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'SubCategory1', phone: '35-35-35', id: 2, parentId: 1 }, + { name: 'SubCategory2', phone: '45-45-45', id: 3, parentId: 1 } + ] + }); - // assert - assert.equal(this.dataController.totalItemsCount(), 1, 'count visible items'); - assert.equal(this.dataController.totalCount(), 3, 'count all items'); -}); + // act + this.dataController.load(); + + // assert + assert.equal(this.dataController.totalItemsCount(), 1, 'count visible items'); + assert.equal(this.dataController.totalCount(), 3, 'count all items'); + }); -QUnit.test('Getting key when there are keyExpr and store hasn\'t key', function(assert) { + QUnit.test('Getting key when there are keyExpr and store hasn\'t key', function(assert) { // arrange - const array = [ - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'Category2', phone: '98-75-21', id: 2 } - ]; + const array = [ + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 }, + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'Category2', phone: '98-75-21', id: 2 } + ]; - // act - this.applyOptions({ - dataSource: { - load: function() { - return $.Deferred().resolve(array); + // act + this.applyOptions({ + dataSource: { + load: function() { + return $.Deferred().resolve(array); + } } - } - }); + }); - // assert - assert.equal(this.getDataSource().store().key(), undefined, 'store hasn\'t key'); - assert.equal(this.keyOf(array[0]), 3, 'key of first item'); -}); + // assert + assert.equal(this.getDataSource().store().key(), undefined, 'store hasn\'t key'); + assert.equal(this.keyOf(array[0]), 3, 'key of first item'); + }); -// T511779 -QUnit.test('The expandRowKeys should be not changed when loading data when there is a filter', function(assert) { + // T511779 + QUnit.test('The expandRowKeys should be not changed when loading data when there is a filter', function(assert) { // arrange - const that = this; - let expandedRowKeys = []; + const that = this; + let expandedRowKeys = []; - // act - that.applyOptions({ - dataSource: { - store: { - type: 'array', - data: [ - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'SubCategory1', phone: '55-55-55', id: 2, parentId: 1 }, - { name: 'Category2', phone: '98-75-21', id: 3, parentId: 0 }, - { name: 'SubCategory2', phone: '55-66-77', id: 4, parentId: 3 }, - ], - onLoading: function() { - expandedRowKeys = that.option('expandedRowKeys').slice(0); - } + // act + that.applyOptions({ + dataSource: { + store: { + type: 'array', + data: [ + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'SubCategory1', phone: '55-55-55', id: 2, parentId: 1 }, + { name: 'Category2', phone: '98-75-21', id: 3, parentId: 0 }, + { name: 'SubCategory2', phone: '55-66-77', id: 4, parentId: 3 }, + ], + onLoading: function() { + expandedRowKeys = that.option('expandedRowKeys').slice(0); + } + }, + filter: ['name', '=', 'SubCategory2'] }, - filter: ['name', '=', 'SubCategory2'] - }, - expandNodesOnFiltering: true, - expandedRowKeys: [1] - }); + expandNodesOnFiltering: true, + expandedRowKeys: [1] + }); - // assert - assert.deepEqual(expandedRowKeys, [1], 'expandedRowKeys value when data isn\'t loaded'); - assert.deepEqual(that.option('expandedRowKeys'), [3], 'expandedRowKeys value when data is loaded'); -}); + // assert + assert.deepEqual(expandedRowKeys, [1], 'expandedRowKeys value when data isn\'t loaded'); + assert.deepEqual(that.option('expandedRowKeys'), [3], 'expandedRowKeys value when data is loaded'); + }); -QUnit.test('TreeList should not throw exception on filtering if focused row is not in filter condition (T724482)', function(assert) { + QUnit.test('TreeList should not throw exception on filtering if focused row is not in filter condition (T724482)', function(assert) { // arrange - const clock = sinon.useFakeTimers(); - this.applyOptions({ - remoteOperations: true, - dataSource: [ - { id: 1, parentId: 0 }, - { id: 2, parentId: 1 }, - { id: 3, parentId: 0 }, - { id: 4, parentId: 3 } - ], - parentIdExpr: 'parentId', - keyExpr: 'id', - expandNodesOnFiltering: true, - focusedRowEnabled: true, - focusedRowKey: 4, - loadPanel: { - enabled: true + const clock = sinon.useFakeTimers(); + this.applyOptions({ + remoteOperations: true, + dataSource: [ + { id: 1, parentId: 0 }, + { id: 2, parentId: 1 }, + { id: 3, parentId: 0 }, + { id: 4, parentId: 3 } + ], + parentIdExpr: 'parentId', + keyExpr: 'id', + expandNodesOnFiltering: true, + focusedRowEnabled: true, + focusedRowKey: 4, + loadPanel: { + enabled: true + } + }); + + // act, assert + try { + this.getDataSource().filter(['id', '=', 2]); + this.getDataSource().load(); + clock.tick(); + } catch(e) { + assert.ok(false, e); } + // assert + assert.notOk(this.dataController.isLoading(), 'Is loading'); + assert.equal(this.dataController.getVisibleRows().length, 2, 'Visible rows count'); + clock.restore(); }); - // act, assert - try { - this.getDataSource().filter(['id', '=', 2]); - this.getDataSource().load(); - clock.tick(); - } catch(e) { - assert.ok(false, e); - } - // assert - assert.notOk(this.dataController.isLoading(), 'Is loading'); - assert.equal(this.dataController.getVisibleRows().length, 2, 'Visible rows count'); - clock.restore(); -}); - -QUnit.test('Get node by key', function(assert) { + QUnit.test('Get node by key', function(assert) { // arrange - let rows; - let node; + let rows; + let node; - this.applyOptions({ - dataSource: [ - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'SubCategory1', phone: '35-35-35', id: 2, parentId: 1 }, - { name: 'SubCategory2', phone: '45-45-45', id: 3, parentId: 1 } - ] - }); + this.applyOptions({ + dataSource: [ + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'SubCategory1', phone: '35-35-35', id: 2, parentId: 1 }, + { name: 'SubCategory2', phone: '45-45-45', id: 3, parentId: 1 } + ] + }); - // act - this.dataController.load(); - - // assert - rows = this.getVisibleRows(); - node = this.getNodeByKey(1); - assert.equal(rows.length, 1, 'count row'); - assert.strictEqual(node, rows[0].node, 'equal to the node of a first row'); - assert.deepEqual(node.data, { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, 'node by key is \'1\''); -}); + // act + this.dataController.load(); + + // assert + rows = this.getVisibleRows(); + node = this.getNodeByKey(1); + assert.equal(rows.length, 1, 'count row'); + assert.strictEqual(node, rows[0].node, 'equal to the node of a first row'); + assert.deepEqual(node.data, { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, 'node by key is \'1\''); + }); -QUnit.test('Get node by key when node is hidden', function(assert) { + QUnit.test('Get node by key when node is hidden', function(assert) { // arrange - let rows; - let node; + let rows; + let node; - this.applyOptions({ - dataSource: [ - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'SubCategory1', phone: '35-35-35', id: 2, parentId: 1 }, - { name: 'SubCategory2', phone: '45-45-45', id: 3, parentId: 1 } - ] - }); + this.applyOptions({ + dataSource: [ + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'SubCategory1', phone: '35-35-35', id: 2, parentId: 1 }, + { name: 'SubCategory2', phone: '45-45-45', id: 3, parentId: 1 } + ] + }); - // act - this.dataController.load(); - - // assert - rows = this.getVisibleRows(); - node = this.getNodeByKey(3); - assert.equal(rows.length, 1, 'count row'); - assert.notStrictEqual(node, rows[0].node, 'not equal to the node of a first row'); - assert.deepEqual(node.data, { name: 'SubCategory2', phone: '45-45-45', id: 3, parentId: 1 }, 'node by key is \'3\''); -}); + // act + this.dataController.load(); + + // assert + rows = this.getVisibleRows(); + node = this.getNodeByKey(3); + assert.equal(rows.length, 1, 'count row'); + assert.notStrictEqual(node, rows[0].node, 'not equal to the node of a first row'); + assert.deepEqual(node.data, { name: 'SubCategory2', phone: '45-45-45', id: 3, parentId: 1 }, 'node by key is \'3\''); + }); -QUnit.test('There are no exceptions on getting node when hasn\'t datasource', function(assert) { + QUnit.test('There are no exceptions on getting node when hasn\'t datasource', function(assert) { // arrange - this.dataController.setDataSource(undefined); + this.dataController.setDataSource(undefined); - // act, assert - assert.equal(this.getNodeByKey(1), undefined, 'no exceptions'); -}); + // act, assert + assert.equal(this.getNodeByKey(1), undefined, 'no exceptions'); + }); -QUnit.test('Call forEachNode method when the first parameter as the array of nodes', function(assert) { + QUnit.test('Call forEachNode method when the first parameter as the array of nodes', function(assert) { // arrange - let rootNode; - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 } - ]; - const dataSource = createDataSource(array); - const spy = sinon.spy(); + let rootNode; + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 } + ]; + const dataSource = createDataSource(array); + const spy = sinon.spy(); - this.dataController.setDataSource(dataSource); - dataSource.load(); - rootNode = this.dataController.getRootNode(); + this.dataController.setDataSource(dataSource); + dataSource.load(); + rootNode = this.dataController.getRootNode(); - // act - this.dataController.forEachNode(rootNode.children, spy); + // act + this.dataController.forEachNode(rootNode.children, spy); - // assert - assert.strictEqual(spy.callCount, 3); - assert.deepEqual(spy.getCall(0).args[0], this.dataController.getNodeByKey(1)); - assert.deepEqual(spy.getCall(1).args[0], this.dataController.getNodeByKey(2)); - assert.deepEqual(spy.getCall(2).args[0], this.dataController.getNodeByKey(3)); -}); + // assert + assert.strictEqual(spy.callCount, 3); + assert.deepEqual(spy.getCall(0).args[0], this.dataController.getNodeByKey(1)); + assert.deepEqual(spy.getCall(1).args[0], this.dataController.getNodeByKey(2)); + assert.deepEqual(spy.getCall(2).args[0], this.dataController.getNodeByKey(3)); + }); -QUnit.test('Call forEachNode method when the first parameter as node', function(assert) { + QUnit.test('Call forEachNode method when the first parameter as node', function(assert) { // arrange - let rootNode; - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 } - ]; - const dataSource = createDataSource(array); - const spy = sinon.spy(); + let rootNode; + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 } + ]; + const dataSource = createDataSource(array); + const spy = sinon.spy(); - this.dataController.setDataSource(dataSource); - dataSource.load(); - rootNode = this.dataController.getRootNode(); + this.dataController.setDataSource(dataSource); + dataSource.load(); + rootNode = this.dataController.getRootNode(); - // act - this.dataController.forEachNode(rootNode, spy); - - // assert - assert.strictEqual(spy.callCount, 4); - assert.deepEqual(spy.getCall(0).args[0], rootNode); - assert.deepEqual(spy.getCall(1).args[0], this.dataController.getNodeByKey(1)); - assert.deepEqual(spy.getCall(2).args[0], this.dataController.getNodeByKey(2)); - assert.deepEqual(spy.getCall(3).args[0], this.dataController.getNodeByKey(3)); -}); + // act + this.dataController.forEachNode(rootNode, spy); + + // assert + assert.strictEqual(spy.callCount, 4); + assert.deepEqual(spy.getCall(0).args[0], rootNode); + assert.deepEqual(spy.getCall(1).args[0], this.dataController.getNodeByKey(1)); + assert.deepEqual(spy.getCall(2).args[0], this.dataController.getNodeByKey(2)); + assert.deepEqual(spy.getCall(3).args[0], this.dataController.getNodeByKey(3)); + }); -QUnit.test('Call forEachNode method with one parameter', function(assert) { + QUnit.test('Call forEachNode method with one parameter', function(assert) { // arrange - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, - { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 } - ]; - const dataSource = createDataSource(array); - const spy = sinon.spy(); + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'Category2', phone: '98-75-21', id: 2, parentId: 0 }, + { name: 'SubCategory1', phone: '55-66-77', id: 3, parentId: 2 } + ]; + const dataSource = createDataSource(array); + const spy = sinon.spy(); - this.dataController.setDataSource(dataSource); - dataSource.load(); + this.dataController.setDataSource(dataSource); + dataSource.load(); - // act - this.dataController.forEachNode(spy); + // act + this.dataController.forEachNode(spy); - // assert - assert.strictEqual(spy.callCount, 3); - assert.deepEqual(spy.getCall(0).args[0], this.dataController.getNodeByKey(1)); - assert.deepEqual(spy.getCall(1).args[0], this.dataController.getNodeByKey(2)); - assert.deepEqual(spy.getCall(2).args[0], this.dataController.getNodeByKey(3)); -}); + // assert + assert.strictEqual(spy.callCount, 3); + assert.deepEqual(spy.getCall(0).args[0], this.dataController.getNodeByKey(1)); + assert.deepEqual(spy.getCall(1).args[0], this.dataController.getNodeByKey(2)); + assert.deepEqual(spy.getCall(2).args[0], this.dataController.getNodeByKey(3)); + }); -// T621620 -QUnit.test('Initialize from dataSource with hierarchical structure when \'parentIdExpr\' option is specified', function(assert) { + // T621620 + QUnit.test('Initialize from dataSource with hierarchical structure when \'parentIdExpr\' option is specified', function(assert) { // arrange - let items; - const array = [ - { name: 'Category1', phone: '55-55-55', key: 'key1' }, - { name: 'Category2', phone: '98-75-21', key: 'key2', parentId: 'key1', items: [ - { name: 'SubCategory1', phone: '55-66-77', key: 'key3', parentId: 'key2' }, - { name: 'SubCategory2', phone: '56-76-79', key: 'key4', parentId: 'key2' }] - } - ]; - const dataSource = createDataSource(array); + let items; + const array = [ + { name: 'Category1', phone: '55-55-55', key: 'key1' }, + { name: 'Category2', phone: '98-75-21', key: 'key2', parentId: 'key1', items: [ + { name: 'SubCategory1', phone: '55-66-77', key: 'key3', parentId: 'key2' }, + { name: 'SubCategory2', phone: '56-76-79', key: 'key4', parentId: 'key2' }] + } + ]; + const dataSource = createDataSource(array); + + this.applyOptions({ + itemsExpr: 'items', + dataStructure: 'tree', + keyExpr: 'key', + parentIdExpr: 'parentId', + expandedRowKeys: ['key2'] + }); + this.dataController.setDataSource(dataSource); - this.applyOptions({ - itemsExpr: 'items', - dataStructure: 'tree', - keyExpr: 'key', - parentIdExpr: 'parentId', - expandedRowKeys: ['key2'] - }); - this.dataController.setDataSource(dataSource); + // act + dataSource.load(); - // act - dataSource.load(); + // assert + items = this.dataController.items(); + assert.equal(items.length, 4, 'count items'); - // assert - items = this.dataController.items(); - assert.equal(items.length, 4, 'count items'); + assert.equal(items[0].key, 'key1', 'key of first item'); + assert.deepEqual(items[0].data, { name: 'Category1', phone: '55-55-55', key: 'key1', parentId: 0 }, 'data of first item'); + assert.equal(items[0].level, 0, 'level of first item'); - assert.equal(items[0].key, 'key1', 'key of first item'); - assert.deepEqual(items[0].data, { name: 'Category1', phone: '55-55-55', key: 'key1', parentId: 0 }, 'data of first item'); - assert.equal(items[0].level, 0, 'level of first item'); + assert.equal(items[1].key, 'key2', 'key of second item'); + assert.deepEqual(items[1].data, { name: 'Category2', phone: '98-75-21', key: 'key2', parentId: 0 }, 'data of second item'); + assert.equal(items[1].level, 0, 'level of second item'); - assert.equal(items[1].key, 'key2', 'key of second item'); - assert.deepEqual(items[1].data, { name: 'Category2', phone: '98-75-21', key: 'key2', parentId: 0 }, 'data of second item'); - assert.equal(items[1].level, 0, 'level of second item'); + assert.equal(items[2].key, 'key3', 'key of third item'); + assert.deepEqual(items[2].data, { name: 'SubCategory1', phone: '55-66-77', key: 'key3', parentId: 'key2' }, 'data of third item'); + assert.equal(items[2].level, 1, 'level of third item'); + assert.equal(items[2].node.parent.key, items[1].key, 'third item has parentKey'); - assert.equal(items[2].key, 'key3', 'key of third item'); - assert.deepEqual(items[2].data, { name: 'SubCategory1', phone: '55-66-77', key: 'key3', parentId: 'key2' }, 'data of third item'); - assert.equal(items[2].level, 1, 'level of third item'); - assert.equal(items[2].node.parent.key, items[1].key, 'third item has parentKey'); + assert.equal(items[3].key, 'key4', 'key of fourth item'); + assert.deepEqual(items[3].data, { name: 'SubCategory2', phone: '56-76-79', key: 'key4', parentId: 'key2' }, 'data of fourth item'); + assert.equal(items[3].level, 1, 'level of fourth item'); + assert.equal(items[3].node.parent.key, items[1].key, 'fourth item has parentKey'); + }); - assert.equal(items[3].key, 'key4', 'key of fourth item'); - assert.deepEqual(items[3].data, { name: 'SubCategory2', phone: '56-76-79', key: 'key4', parentId: 'key2' }, 'data of fourth item'); - assert.equal(items[3].level, 1, 'level of fourth item'); - assert.equal(items[3].node.parent.key, items[1].key, 'fourth item has parentKey'); -}); + // T622381 + QUnit.test('Nodes should be expanded after refresh method is called at boot time (when autoExpandAll is true)', function(assert) { + // arrange + let items; + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, + { name: 'SubCategory1', phone: '98-75-21', id: 2, parentId: 1 }, + { name: 'SubCategory2', phone: '55-66-77', id: 3, parentId: 2 }, + ]; + const clock = sinon.useFakeTimers(); + + try { + this.applyOptions({ autoExpandAll: true, dataSource: array, loadingTimeout: 30 }); + + // act + this.refresh(); + clock.tick(60); + + // assert + items = this.dataController.items(); + assert.strictEqual(items.length, 3, 'item count'); + } finally { + clock.restore(); + } + }); -// T622381 -QUnit.test('Nodes should be expanded after refresh method is called at boot time (when autoExpandAll is true)', function(assert) { + // T713250 + QUnit.test('Initialize when data as classes with a hierarchical structure', function(assert) { // arrange - let items; - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1, parentId: 0 }, - { name: 'SubCategory1', phone: '98-75-21', id: 2, parentId: 1 }, - { name: 'SubCategory2', phone: '55-66-77', id: 3, parentId: 2 }, - ]; - const clock = sinon.useFakeTimers(); + function Person(id, items) { + this._id = id; + this._items = items; + } + Object.defineProperty(Person.prototype, 'ID', { + configurable: true, + enumerable: false, + get: function() { return this._id; }, + set: function(value) { this._id = value; } + }); + Object.defineProperty(Person.prototype, 'items', { + configurable: true, + enumerable: false, + get: function() { return this._items; }, + set: function(value) { this._items = value; } + }); - try { - this.applyOptions({ autoExpandAll: true, dataSource: array, loadingTimeout: 30 }); + const dataSource = [ + new Person(1, [ + new Person(2, [ + new Person(3), + new Person(4), + new Person(5), + ]) + ]) + ]; // act - this.refresh(); - clock.tick(60); + this.applyOptions({ + autoExpandAll: true, + dataSource: dataSource, + dataStructure: 'tree', + keyExpr: 'ID', + itemsExpr: 'items' + }); // assert - items = this.dataController.items(); - assert.strictEqual(items.length, 3, 'item count'); - } finally { - clock.restore(); - } -}); - -// T713250 -QUnit.test('Initialize when data as classes with a hierarchical structure', function(assert) { - // arrange - function Person(id, items) { - this._id = id; - this._items = items; - } - Object.defineProperty(Person.prototype, 'ID', { - configurable: true, - enumerable: false, - get: function() { return this._id; }, - set: function(value) { this._id = value; } - }); - Object.defineProperty(Person.prototype, 'items', { - configurable: true, - enumerable: false, - get: function() { return this._items; }, - set: function(value) { this._items = value; } - }); - - const dataSource = [ - new Person(1, [ - new Person(2, [ - new Person(3), - new Person(4), - new Person(5), - ]) - ]) - ]; + const rows = this.getVisibleRows(); + assert.strictEqual(rows.length, 5, 'row count'); - // act - this.applyOptions({ - autoExpandAll: true, - dataSource: dataSource, - dataStructure: 'tree', - keyExpr: 'ID', - itemsExpr: 'items' - }); - - // assert - const rows = this.getVisibleRows(); - assert.strictEqual(rows.length, 5, 'row count'); - - assert.strictEqual(rows[0].node.key, 1, 'key of the first node'); - assert.strictEqual(rows[0].node.level, 0, 'level of the first node'); + assert.strictEqual(rows[0].node.key, 1, 'key of the first node'); + assert.strictEqual(rows[0].node.level, 0, 'level of the first node'); - assert.strictEqual(rows[1].node.key, 2, 'key of the second node'); - assert.strictEqual(rows[1].node.level, 1, 'level of the second node'); + assert.strictEqual(rows[1].node.key, 2, 'key of the second node'); + assert.strictEqual(rows[1].node.level, 1, 'level of the second node'); - assert.strictEqual(rows[2].node.key, 3, 'key of the third node'); - assert.strictEqual(rows[2].node.level, 2, 'level of the third node'); + assert.strictEqual(rows[2].node.key, 3, 'key of the third node'); + assert.strictEqual(rows[2].node.level, 2, 'level of the third node'); - assert.strictEqual(rows[3].node.key, 4, 'key of the fourth node'); - assert.strictEqual(rows[3].node.level, 2, 'level of the fourth node'); + assert.strictEqual(rows[3].node.key, 4, 'key of the fourth node'); + assert.strictEqual(rows[3].node.level, 2, 'level of the fourth node'); - assert.strictEqual(rows[4].node.key, 5, 'key of the fifth node'); - assert.strictEqual(rows[4].node.level, 2, 'level of the fifth node'); + assert.strictEqual(rows[4].node.key, 5, 'key of the fifth node'); + assert.strictEqual(rows[4].node.level, 2, 'level of the fifth node'); + }); }); +QUnit.module('Expand/Collapse nodes', { beforeEach: setupModule, afterEach: teardownModule }, () => { -QUnit.module('Expand/Collapse nodes', { beforeEach: setupModule, afterEach: teardownModule }); - -QUnit.test('Expand node (plain structure)', function(assert) { + QUnit.test('Expand node (plain structure)', function(assert) { // arrange - let items; - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } - ]; - const dataSource = createDataSource(array); + let items; + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } + ]; + const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); - dataSource.load(); + this.dataController.setDataSource(dataSource); + dataSource.load(); - // assert - items = this.dataController.items(); - assert.equal(items.length, 1, 'count items'); - assert.notOk(items[0].isExpanded, 'first item is collapsed'); + // assert + items = this.dataController.items(); + assert.equal(items.length, 1, 'count items'); + assert.notOk(items[0].isExpanded, 'first item is collapsed'); - // act - this.expandRow(1); + // act + this.expandRow(1); - // assert - items = this.dataController.items(); - assert.equal(items.length, 2, 'count items'); - assert.ok(items[0].isExpanded, 'first item is expanded'); -}); + // assert + items = this.dataController.items(); + assert.equal(items.length, 2, 'count items'); + assert.ok(items[0].isExpanded, 'first item is expanded'); + }); -QUnit.test('Expand expanded node (plain structure)', function(assert) { + QUnit.test('Expand expanded node (plain structure)', function(assert) { // arrange - let items; - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } - ]; - const dataSource = createDataSource(array); + let items; + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } + ]; + const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); - dataSource.load(); + this.dataController.setDataSource(dataSource); + dataSource.load(); - this.expandRow(1); + this.expandRow(1); - // act - this.expandRow(1); + // act + this.expandRow(1); - // assert - items = this.dataController.items(); - assert.ok(this.isRowExpanded(1), 'row is expanded'); - assert.equal(items.length, 2, 'count items'); - assert.ok(items[0].isExpanded, 'first item is expanded'); -}); + // assert + items = this.dataController.items(); + assert.ok(this.isRowExpanded(1), 'row is expanded'); + assert.equal(items.length, 2, 'count items'); + assert.ok(items[0].isExpanded, 'first item is expanded'); + }); -QUnit.test('Collapse node (plain structure)', function(assert) { + QUnit.test('Collapse node (plain structure)', function(assert) { // arrange - let items; - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } - ]; - const dataSource = createDataSource(array); + let items; + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } + ]; + const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); - dataSource.load(); + this.dataController.setDataSource(dataSource); + dataSource.load(); - // assert - items = this.dataController.items(); - assert.equal(items.length, 1, 'count items'); - assert.notOk(items[0].isExpanded, 'first item is collapsed'); + // assert + items = this.dataController.items(); + assert.equal(items.length, 1, 'count items'); + assert.notOk(items[0].isExpanded, 'first item is collapsed'); - // arrange - this.expandRow(1); + // arrange + this.expandRow(1); - // assert - items = this.dataController.items(); - assert.equal(items.length, 2, 'count items'); - assert.ok(items[0].isExpanded, 'first item is expanded'); + // assert + items = this.dataController.items(); + assert.equal(items.length, 2, 'count items'); + assert.ok(items[0].isExpanded, 'first item is expanded'); - // act - this.collapseRow(1); + // act + this.collapseRow(1); - // assert - items = this.dataController.items(); - assert.equal(items.length, 1, 'count items'); - assert.notOk(items[0].isExpanded, 'first item is collapsed'); -}); + // assert + items = this.dataController.items(); + assert.equal(items.length, 1, 'count items'); + assert.notOk(items[0].isExpanded, 'first item is collapsed'); + }); -QUnit.test('Set expanded nodes by expandedRowKeys - first level', function(assert) { + QUnit.test('Set expanded nodes by expandedRowKeys - first level', function(assert) { // arrange - let items; - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } - ]; - const dataSource = createDataSource(array); + let items; + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } + ]; + const dataSource = createDataSource(array); - this.applyOptions({ expandedRowKeys: [1] }); - this.dataController.setDataSource(dataSource); - dataSource.load(); + this.applyOptions({ expandedRowKeys: [1] }); + this.dataController.setDataSource(dataSource); + dataSource.load(); - // assert - items = this.dataController.items(); - assert.equal(items.length, 2, 'count items'); - assert.ok(items[0].isExpanded, 'first item is expanded'); -}); + // assert + items = this.dataController.items(); + assert.equal(items.length, 2, 'count items'); + assert.ok(items[0].isExpanded, 'first item is expanded'); + }); -QUnit.test('Set expanded nodes by expandedRowKeys - internal level', function(assert) { + QUnit.test('Set expanded nodes by expandedRowKeys - internal level', function(assert) { // arrange - let items; - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 }, - { name: 'SubCategory1-1', phone: '55-66-77', id: 3, parentId: 2 } - ]; - const dataSource = createDataSource(array); + let items; + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 }, + { name: 'SubCategory1-1', phone: '55-66-77', id: 3, parentId: 2 } + ]; + const dataSource = createDataSource(array); - this.applyOptions({ expandedRowKeys: [2] }); - this.dataController.setDataSource(dataSource); - dataSource.load(); + this.applyOptions({ expandedRowKeys: [2] }); + this.dataController.setDataSource(dataSource); + dataSource.load(); - // assert - items = this.dataController.items(); - assert.equal(items.length, 1, 'count items'); - assert.notOk(items[0].isExpanded, 'first item is collapsed'); + // assert + items = this.dataController.items(); + assert.equal(items.length, 1, 'count items'); + assert.notOk(items[0].isExpanded, 'first item is collapsed'); - // act - this.expandRow(1); + // act + this.expandRow(1); - // assert - items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); - assert.ok(items[0].isExpanded, 'first item is expanded'); -}); + // assert + items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); + assert.ok(items[0].isExpanded, 'first item is expanded'); + }); -QUnit.test('Update expandedRowKeys', function(assert) { + QUnit.test('Update expandedRowKeys', function(assert) { // arrange - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } - ]; - const dataSource = createDataSource(array); + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } + ]; + const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); - dataSource.load(); + this.dataController.setDataSource(dataSource); + dataSource.load(); - let expandedRowKeys = this.dataController.option('expandedRowKeys'); + let expandedRowKeys = this.dataController.option('expandedRowKeys'); - // act - this.expandRow(1); + // act + this.expandRow(1); - // assert - assert.equal(this.dataController.option('expandedRowKeys').length, 1, 'count of expanded items'); - assert.equal(this.dataController.option('expandedRowKeys')[0], 1, 'first item is expanded'); - assert.notStrictEqual(this.dataController.option('expandedRowKeys'), expandedRowKeys, 'expandedRowKeys has a different instance'); + // assert + assert.equal(this.dataController.option('expandedRowKeys').length, 1, 'count of expanded items'); + assert.equal(this.dataController.option('expandedRowKeys')[0], 1, 'first item is expanded'); + assert.notStrictEqual(this.dataController.option('expandedRowKeys'), expandedRowKeys, 'expandedRowKeys has a different instance'); - // arrange - expandedRowKeys = this.dataController.option('expandedRowKeys'); + // arrange + expandedRowKeys = this.dataController.option('expandedRowKeys'); - // act - this.collapseRow(1); + // act + this.collapseRow(1); - // assert - assert.notOk(this.dataController.option('expandedRowKeys').length, 'count of expanded items'); - assert.notStrictEqual(this.dataController.option('expandedRowKeys'), expandedRowKeys, 'expandedRowKeys has a different instance'); -}); + // assert + assert.notOk(this.dataController.option('expandedRowKeys').length, 'count of expanded items'); + assert.notStrictEqual(this.dataController.option('expandedRowKeys'), expandedRowKeys, 'expandedRowKeys has a different instance'); + }); -QUnit.test('Expand/collapse events', function(assert) { + QUnit.test('Expand/collapse events', function(assert) { // arrange - const that = this; - let events = []; - const options = []; - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } - ]; - const dataSource = createDataSource(array); + const that = this; + let events = []; + const options = []; + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } + ]; + const dataSource = createDataSource(array); - $.each(['onRowExpanding', 'onRowExpanded', 'onRowCollapsing', 'onRowCollapsed'], function(index, name) { - options[name] = function(e) { - events.push({ name: name, key: e.key }); - }; - }); - that.applyOptions(options); - that.dataController.setDataSource(dataSource); - dataSource.load(); + $.each(['onRowExpanding', 'onRowExpanded', 'onRowCollapsing', 'onRowCollapsed'], function(index, name) { + options[name] = function(e) { + events.push({ name: name, key: e.key }); + }; + }); + that.applyOptions(options); + that.dataController.setDataSource(dataSource); + dataSource.load(); - // act - that.expandRow(1); + // act + that.expandRow(1); - // assert - let items = that.dataController.items(); - assert.strictEqual(items.length, 2, 'count item'); - assert.deepEqual(events, [ - { name: 'onRowExpanding', key: 1 }, - { name: 'onRowExpanded', key: 1 } - ], 'expand events'); + // assert + let items = that.dataController.items(); + assert.strictEqual(items.length, 2, 'count item'); + assert.deepEqual(events, [ + { name: 'onRowExpanding', key: 1 }, + { name: 'onRowExpanded', key: 1 } + ], 'expand events'); - // arrange - events = []; + // arrange + events = []; - // act - that.collapseRow(1); - - // assert - items = that.dataController.items(); - assert.strictEqual(items.length, 1); - assert.deepEqual(events, [ - { name: 'onRowCollapsing', key: 1 }, - { name: 'onRowCollapsed', key: 1 } - ], 'collapse events'); -}); + // act + that.collapseRow(1); -QUnit.test('Cancel expand row on an expanding event', function(assert) { - // arrange - const that = this; - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } - ]; - const dataSource = createDataSource(array); + // assert + items = that.dataController.items(); + assert.strictEqual(items.length, 1); + assert.deepEqual(events, [ + { name: 'onRowCollapsing', key: 1 }, + { name: 'onRowCollapsed', key: 1 } + ], 'collapse events'); + }); + + QUnit.test('Cancel expand row on an expanding event', function(assert) { + // arrange + const that = this; + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } + ]; + const dataSource = createDataSource(array); + + that.applyOptions({ + onRowExpanding: function(e) { + e.cancel = true; + } + }); + that.dataController.setDataSource(dataSource); + dataSource.load(); - that.applyOptions({ - onRowExpanding: function(e) { - e.cancel = true; - } + // act + that.expandRow(1); + + // assert + const items = that.dataController.items(); + assert.equal(items.length, 1, 'count item'); }); - that.dataController.setDataSource(dataSource); - dataSource.load(); - // act - that.expandRow(1); + QUnit.test('Cancel collapse row on a collapsing event', function(assert) { + // arrange + const that = this; + const array = [ + { name: 'Category1', phone: '55-55-55', id: 1 }, + { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } + ]; + const dataSource = createDataSource(array); - // assert - const items = that.dataController.items(); - assert.equal(items.length, 1, 'count item'); -}); + that.applyOptions({ + onRowCollapsing: function(e) { + e.cancel = true; + } + }); + that.dataController.setDataSource(dataSource); + dataSource.load(); + that.expandRow(1); -QUnit.test('Cancel collapse row on a collapsing event', function(assert) { - // arrange - const that = this; - const array = [ - { name: 'Category1', phone: '55-55-55', id: 1 }, - { name: 'SubCategory1', phone: '55-66-77', id: 2, parentId: 1 } - ]; - const dataSource = createDataSource(array); + // assert + let items = that.dataController.items(); + assert.equal(items.length, 2, 'count item'); - that.applyOptions({ - onRowCollapsing: function(e) { - e.cancel = true; - } + // act + that.collapseRow(1); + + // assert + items = that.dataController.items(); + assert.equal(items.length, 2, 'count item'); }); - that.dataController.setDataSource(dataSource); - dataSource.load(); - that.expandRow(1); - // assert - let items = that.dataController.items(); - assert.equal(items.length, 2, 'count item'); + // T819031 + QUnit.test('All nested children should be loaded when expanding nodes with expandRow method', function(assert) { + // arrange + const clock = sinon.useFakeTimers(); - // act - that.collapseRow(1); + const loadSpy = sinon.spy((loadOptions) => { + let result = []; - // assert - items = that.dataController.items(); - assert.equal(items.length, 2, 'count item'); -}); + loadOptions.parentIds.forEach(function(parentId) { + const items = itemsByParentId[parentId]; -// T819031 -QUnit.test('All nested children should be loaded when expanding nodes with expandRow method', function(assert) { - // arrange - const clock = sinon.useFakeTimers(); + if(items) { + result = result.concat(items); + } + }); - const loadSpy = sinon.spy((loadOptions) => { - let result = []; + return result; + }); - loadOptions.parentIds.forEach(function(parentId) { - const items = itemsByParentId[parentId]; + const itemsByParentId = { + '0': [{ id: 1, parentId: 0, name: 'Name 1' }], + '1': [{ id: 2, parentId: 1, name: 'Name 2' }], + '2': [{ id: 3, parentId: 2, name: 'Name 3' }], + '3': [{ id: 4, parentId: 3, name: 'Name 4' }] + }; - if(items) { - result = result.concat(items); + this.applyOptions({ + loadingTimeout: 30, + remoteOperations: { filtering: true }, + dataSource: { + load: loadSpy } }); + clock.tick(30); + loadSpy.reset(); - return result; - }); + // act + this.expandRow(1); + this.expandRow(2); + this.expandRow(3); + clock.tick(30); - const itemsByParentId = { - '0': [{ id: 1, parentId: 0, name: 'Name 1' }], - '1': [{ id: 2, parentId: 1, name: 'Name 2' }], - '2': [{ id: 3, parentId: 2, name: 'Name 3' }], - '3': [{ id: 4, parentId: 3, name: 'Name 4' }] - }; + // assert + const rows = this.getVisibleRows(); + assert.strictEqual(loadSpy.callCount, 1, 'load call count'); + assert.deepEqual(loadSpy.getCall(0).args[0].parentIds, [1, 2, 3], 'load arg - parentIds'); + assert.strictEqual(rows.length, 4, 'row count'); + assert.strictEqual(rows[0].data.id, 1, 'first node'); + assert.strictEqual(rows[1].data.id, 2, 'second node'); + assert.strictEqual(rows[2].data.id, 3, 'third node'); + assert.strictEqual(rows[3].data.id, 4, 'fourth node'); - this.applyOptions({ - loadingTimeout: 30, - remoteOperations: { filtering: true }, - dataSource: { - load: loadSpy - } + clock.restore(); }); - clock.tick(30); - loadSpy.reset(); - - // act - this.expandRow(1); - this.expandRow(2); - this.expandRow(3); - clock.tick(30); - - // assert - const rows = this.getVisibleRows(); - assert.strictEqual(loadSpy.callCount, 1, 'load call count'); - assert.deepEqual(loadSpy.getCall(0).args[0].parentIds, [1, 2, 3], 'load arg - parentIds'); - assert.strictEqual(rows.length, 4, 'row count'); - assert.strictEqual(rows[0].data.id, 1, 'first node'); - assert.strictEqual(rows[1].data.id, 2, 'second node'); - assert.strictEqual(rows[2].data.id, 3, 'third node'); - assert.strictEqual(rows[3].data.id, 4, 'fourth node'); - - clock.restore(); }); QUnit.module('Sorting', { beforeEach: function() { @@ -1260,97 +1261,97 @@ QUnit.module('Sorting', { beforeEach: function() { options: options }); }; -}, afterEach: teardownModule }); +}, afterEach: teardownModule }, () => { -QUnit.test('Initial sorting should be applied', function(assert) { + QUnit.test('Initial sorting should be applied', function(assert) { // act - this.setupTreeList({ - dataSource: this.items, - columns: [{ dataField: 'name', sortOrder: 'asc' }, { dataField: 'age' }] - }); + this.setupTreeList({ + dataSource: this.items, + columns: [{ dataField: 'name', sortOrder: 'asc' }, { dataField: 'age' }] + }); - // assert - const items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); - assert.equal(items[0].data.name, 'Name 1', 'item 0 name value'); - assert.equal(items[1].data.name, 'Name 2', 'item 1 name value'); - assert.equal(items[2].data.name, 'Name 3', 'item 2 name value'); -}); + // assert + const items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); + assert.equal(items[0].data.name, 'Name 1', 'item 0 name value'); + assert.equal(items[1].data.name, 'Name 2', 'item 1 name value'); + assert.equal(items[2].data.name, 'Name 3', 'item 2 name value'); + }); -QUnit.test('Initial sorting by several columns should be applied', function(assert) { + QUnit.test('Initial sorting by several columns should be applied', function(assert) { // act - this.setupTreeList({ - dataSource: this.items, - columns: [{ dataField: 'name', sortOrder: 'asc', sortIndex: 1 }, { dataField: 'age', sortOrder: 'asc', sortIndex: 0 }] - }); - - // assert - const items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); - assert.equal(items[0].data.age, 18, 'item 0 age value'); - assert.equal(items[0].data.name, 'Name 2', 'item 0 name value'); - assert.equal(items[1].data.age, 19, 'item 0 age value'); - assert.equal(items[1].data.name, 'Name 1', 'item 1 name value'); - assert.equal(items[2].data.age, 19, 'item 0 age value'); - assert.equal(items[2].data.name, 'Name 3', 'item 2 name value'); -}); + this.setupTreeList({ + dataSource: this.items, + columns: [{ dataField: 'name', sortOrder: 'asc', sortIndex: 1 }, { dataField: 'age', sortOrder: 'asc', sortIndex: 0 }] + }); -QUnit.test('Initial sorting for second level should be applied', function(assert) { - this.setupTreeList({ - dataSource: this.items, - columns: [{ dataField: 'name', sortOrder: 'asc' }, { dataField: 'age' }] - }); + // assert + const items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); + assert.equal(items[0].data.age, 18, 'item 0 age value'); + assert.equal(items[0].data.name, 'Name 2', 'item 0 name value'); + assert.equal(items[1].data.age, 19, 'item 0 age value'); + assert.equal(items[1].data.name, 'Name 1', 'item 1 name value'); + assert.equal(items[2].data.age, 19, 'item 0 age value'); + assert.equal(items[2].data.name, 'Name 3', 'item 2 name value'); + }); + + QUnit.test('Initial sorting for second level should be applied', function(assert) { + this.setupTreeList({ + dataSource: this.items, + columns: [{ dataField: 'name', sortOrder: 'asc' }, { dataField: 'age' }] + }); - // act - this.expandRow(1); - - // assert - const items = this.dataController.items(); - assert.equal(items.length, 6, 'count items'); - assert.equal(items[3].data.name, 'Name 4', 'item 3 name value'); - assert.equal(items[4].data.name, 'Name 5', 'item 4 name value'); - assert.equal(items[5].data.name, 'Name 6', 'item 5 name value'); -}); + // act + this.expandRow(1); -QUnit.test('sortOrder changing by columnOption should be applied', function(assert) { - this.setupTreeList({ - dataSource: this.items + // assert + const items = this.dataController.items(); + assert.equal(items.length, 6, 'count items'); + assert.equal(items[3].data.name, 'Name 4', 'item 3 name value'); + assert.equal(items[4].data.name, 'Name 5', 'item 4 name value'); + assert.equal(items[5].data.name, 'Name 6', 'item 5 name value'); }); - // act - this.columnOption('name', 'sortOrder', 'desc'); - - // assert - const items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 0 name value'); - assert.equal(items[1].data.name, 'Name 2', 'item 1 name value'); - assert.equal(items[2].data.name, 'Name 1', 'item 2 name value'); -}); + QUnit.test('sortOrder changing by columnOption should be applied', function(assert) { + this.setupTreeList({ + dataSource: this.items + }); -QUnit.test('Sorting when there is filter', function(assert) { - // arrange - this.setupTreeList({ - dataSource: this.items, - columns: [{ dataField: 'name' }, { dataField: 'age', filterValue: '19' }] + // act + this.columnOption('name', 'sortOrder', 'desc'); + + // assert + const items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 0 name value'); + assert.equal(items[1].data.name, 'Name 2', 'item 1 name value'); + assert.equal(items[2].data.name, 'Name 1', 'item 2 name value'); }); - // assert - let items = this.dataController.items(); - assert.equal(items.length, 2, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 0 name value'); - assert.equal(items[1].data.name, 'Name 1', 'item 1 name value'); + QUnit.test('Sorting when there is filter', function(assert) { + // arrange + this.setupTreeList({ + dataSource: this.items, + columns: [{ dataField: 'name' }, { dataField: 'age', filterValue: '19' }] + }); - // act - this.columnOption('name', 'sortOrder', 'asc'); + // assert + let items = this.dataController.items(); + assert.equal(items.length, 2, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 0 name value'); + assert.equal(items[1].data.name, 'Name 1', 'item 1 name value'); - // assert - items = this.dataController.items(); - assert.equal(items.length, 2, 'count items'); - assert.equal(items[0].data.name, 'Name 1', 'item 0 name value'); - assert.equal(items[1].data.name, 'Name 3', 'item 1 name value'); -}); + // act + this.columnOption('name', 'sortOrder', 'asc'); + // assert + items = this.dataController.items(); + assert.equal(items.length, 2, 'count items'); + assert.equal(items[0].data.name, 'Name 1', 'item 0 name value'); + assert.equal(items[1].data.name, 'Name 3', 'item 1 name value'); + }); +}); QUnit.module('Remote Operations', { beforeEach: function() { this.items = [ @@ -1375,1092 +1376,1093 @@ QUnit.module('Remote Operations', { beforeEach: function() { }); }; this.clock = sinon.useFakeTimers(); -}, afterEach: teardownModule }); +}, afterEach: teardownModule }, () => { -QUnit.test('Initial load with sorting', function(assert) { + QUnit.test('Initial load with sorting', function(assert) { // arrange, act - const loadingArgs = []; - - this.setupTreeList({ - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); + const loadingArgs = []; + + this.setupTreeList({ + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } } - } - }, - columns: [{ dataField: 'name', sortOrder: 'asc' }, { dataField: 'age' }] - }); + }, + columns: [{ dataField: 'name', sortOrder: 'asc' }, { dataField: 'age' }] + }); - // assert - assert.deepEqual(loadingArgs, [ - { - filter: ['parentId', '=', 0], - group: null, - sort: [ - { - desc: false, - selector: 'name' - } - ], - parentIds: [0], - userData: {} - } - ], 'loading arguments'); + // assert + assert.deepEqual(loadingArgs, [ + { + filter: ['parentId', '=', 0], + group: null, + sort: [ + { + desc: false, + selector: 'name' + } + ], + parentIds: [0], + userData: {} + } + ], 'loading arguments'); - const items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); - assert.equal(items[0].data.name, 'Name 1', 'item 0 name value'); - assert.equal(items[1].data.name, 'Name 2', 'item 1 name value'); - assert.equal(items[2].data.name, 'Name 3', 'item 2 name value'); -}); + const items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); + assert.equal(items[0].data.name, 'Name 1', 'item 0 name value'); + assert.equal(items[1].data.name, 'Name 2', 'item 1 name value'); + assert.equal(items[2].data.name, 'Name 3', 'item 2 name value'); + }); -QUnit.test('Initial load with second level expanded key when loading data on demand', function(assert) { + QUnit.test('Initial load with second level expanded key when loading data on demand', function(assert) { // arrange, act - const loadingArgs = []; + const loadingArgs = []; - const itemsByParentId = { - '0': [{ id: 1, parentId: 0, name: 'Name 1' }], - '1': [{ id: 2, parentId: 1, name: 'Name 2' }], - '2': [{ id: 3, parentId: 2, name: 'Name 3' }] - }; + const itemsByParentId = { + '0': [{ id: 1, parentId: 0, name: 'Name 1' }], + '1': [{ id: 2, parentId: 1, name: 'Name 2' }], + '2': [{ id: 3, parentId: 2, name: 'Name 3' }] + }; - this.setupTreeList({ - expandedRowKeys: [2], - remoteOperations: { filtering: true }, - dataSource: { - load: function(loadOptions) { - loadingArgs.push(loadOptions); - let result = []; - loadOptions.parentIds.forEach(function(parentId) { - const items = itemsByParentId[parentId]; - if(items) { - result = result.concat(items); - } - }); + this.setupTreeList({ + expandedRowKeys: [2], + remoteOperations: { filtering: true }, + dataSource: { + load: function(loadOptions) { + loadingArgs.push(loadOptions); + let result = []; + loadOptions.parentIds.forEach(function(parentId) { + const items = itemsByParentId[parentId]; + if(items) { + result = result.concat(items); + } + }); - return result; - }, - useDefaultSearch: true - } - }); + return result; + }, + useDefaultSearch: true + } + }); - // assert - assert.deepEqual(loadingArgs, [{ - filter: [['parentId', '=', 0], 'or', ['parentId', '=', 2]], - parentIds: [0, 2], - userData: {} - }], 'loading arguments'); + // assert + assert.deepEqual(loadingArgs, [{ + filter: [['parentId', '=', 0], 'or', ['parentId', '=', 2]], + parentIds: [0, 2], + userData: {} + }], 'loading arguments'); - const items = this.dataController.items(); - assert.equal(items.length, 1, 'count items'); - assert.equal(items[0].data.name, 'Name 1', 'item 0 name value'); -}); + const items = this.dataController.items(); + assert.equal(items.length, 1, 'count items'); + assert.equal(items[0].data.name, 'Name 1', 'item 0 name value'); + }); -QUnit.test('Change sort order if sorting is local', function(assert) { + QUnit.test('Change sort order if sorting is local', function(assert) { // arrange, act - let loadingArgs = []; - - this.setupTreeList({ - remoteOperations: { - filtering: true - }, - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); + let loadingArgs = []; + + this.setupTreeList({ + remoteOperations: { + filtering: true + }, + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } } - } - }, - columns: [{ dataField: 'name', sortOrder: 'asc' }, { dataField: 'age' }] - }); + }, + columns: [{ dataField: 'name', sortOrder: 'asc' }, { dataField: 'age' }] + }); - loadingArgs = []; + loadingArgs = []; - // act - this.columnOption('name', 'sortOrder', 'desc'); + // act + this.columnOption('name', 'sortOrder', 'desc'); - // assert - assert.deepEqual(loadingArgs, [], 'no loadings during local sorting'); + // assert + assert.deepEqual(loadingArgs, [], 'no loadings during local sorting'); - const items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 0 name value'); - assert.equal(items[1].data.name, 'Name 2', 'item 1 name value'); - assert.equal(items[2].data.name, 'Name 1', 'item 2 name value'); -}); + const items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 0 name value'); + assert.equal(items[1].data.name, 'Name 2', 'item 1 name value'); + assert.equal(items[2].data.name, 'Name 1', 'item 2 name value'); + }); -QUnit.test('Expand first row when there is sorting', function(assert) { + QUnit.test('Expand first row when there is sorting', function(assert) { // arrange - let loadingArgs = []; - - this.setupTreeList({ - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); + let loadingArgs = []; + + this.setupTreeList({ + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } } + }, + columns: [{ dataField: 'name', sortOrder: 'asc' }, { dataField: 'age' }] + }); + loadingArgs = []; + + // act + this.expandRow(1); + + // assert + assert.deepEqual(loadingArgs, [ + { + filter: ['parentId', '=', 1], + group: null, + sort: [ + { + desc: false, + selector: 'name' + } + ], + parentIds: [1], + userData: {} } - }, - columns: [{ dataField: 'name', sortOrder: 'asc' }, { dataField: 'age' }] + ], 'loading arguments'); + + const items = this.dataController.items(); + assert.equal(items.length, 5, 'count items'); + assert.equal(items[0].data.name, 'Name 1', 'item 0 name value'); + assert.equal(items[1].data.name, 'Name 2', 'item 1 name value'); + assert.equal(items[2].data.name, 'Name 3', 'item 2 name value'); + assert.equal(items[3].data.name, 'Name 5', 'item 3 name value'); + assert.equal(items[4].data.name, 'Name 6', 'item 4 name value'); }); - loadingArgs = []; - // act - this.expandRow(1); + // T547036 + QUnit.test('Change sort order after collapse expanded row', function(assert) { + // arrange + this.setupTreeList({ + dataSource: this.items, + columns: [{ dataField: 'name', sortOrder: 'asc' }, { dataField: 'age' }] + }); - // assert - assert.deepEqual(loadingArgs, [ - { - filter: ['parentId', '=', 1], - group: null, - sort: [ - { - desc: false, - selector: 'name' - } - ], - parentIds: [1], - userData: {} - } - ], 'loading arguments'); - - const items = this.dataController.items(); - assert.equal(items.length, 5, 'count items'); - assert.equal(items[0].data.name, 'Name 1', 'item 0 name value'); - assert.equal(items[1].data.name, 'Name 2', 'item 1 name value'); - assert.equal(items[2].data.name, 'Name 3', 'item 2 name value'); - assert.equal(items[3].data.name, 'Name 5', 'item 3 name value'); - assert.equal(items[4].data.name, 'Name 6', 'item 4 name value'); -}); + // act + this.expandRow(1); + this.collapseRow(1); + this.columnOption('name', 'sortOrder', 'desc'); + this.expandRow(1); -// T547036 -QUnit.test('Change sort order after collapse expanded row', function(assert) { - // arrange - this.setupTreeList({ - dataSource: this.items, - columns: [{ dataField: 'name', sortOrder: 'asc' }, { dataField: 'age' }] - }); + // assert - // act - this.expandRow(1); - this.collapseRow(1); - this.columnOption('name', 'sortOrder', 'desc'); - this.expandRow(1); - - // assert - - const items = this.dataController.items(); - assert.equal(items.length, 5, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 0 name value'); - assert.equal(items[1].data.name, 'Name 6', 'item 1 name value'); - assert.equal(items[2].data.name, 'Name 5', 'item 2 name value'); - assert.equal(items[3].data.name, 'Name 2', 'item 3 name value'); - assert.equal(items[4].data.name, 'Name 1', 'item 4 name value'); -}); + const items = this.dataController.items(); + assert.equal(items.length, 5, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 0 name value'); + assert.equal(items[1].data.name, 'Name 6', 'item 1 name value'); + assert.equal(items[2].data.name, 'Name 5', 'item 2 name value'); + assert.equal(items[3].data.name, 'Name 2', 'item 3 name value'); + assert.equal(items[4].data.name, 'Name 1', 'item 4 name value'); + }); -QUnit.test('Initial load when autoExpandAll', function(assert) { + QUnit.test('Initial load when autoExpandAll', function(assert) { // arrange, act - const loadingArgs = []; - - this.setupTreeList({ - autoExpandAll: true, - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); + const loadingArgs = []; + + this.setupTreeList({ + autoExpandAll: true, + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } } } - } - }); + }); - // assert - assert.equal(this.option('expandedRowKeys').length, 3, 'expandedRowKeys is assigned'); - assert.deepEqual(loadingArgs, [ - { - group: null, - userData: {} - } - ], 'loading arguments'); + // assert + assert.equal(this.option('expandedRowKeys').length, 3, 'expandedRowKeys is assigned'); + assert.deepEqual(loadingArgs, [ + { + group: null, + userData: {} + } + ], 'loading arguments'); - const items = this.dataController.items(); - assert.equal(items.length, 7, 'all items are visible'); - assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); - assert.equal(items[1].data.name, 'Name 6', 'item 2 name value'); - assert.equal(items[6].data.name, 'Name 2', 'item 7 name value'); -}); + const items = this.dataController.items(); + assert.equal(items.length, 7, 'all items are visible'); + assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); + assert.equal(items[1].data.name, 'Name 6', 'item 2 name value'); + assert.equal(items[6].data.name, 'Name 2', 'item 7 name value'); + }); -QUnit.test('collapseRow when autoExpandAll', function(assert) { + QUnit.test('collapseRow when autoExpandAll', function(assert) { // arrange - this.setupTreeList({ - autoExpandAll: true, - dataSource: this.items - }); + this.setupTreeList({ + autoExpandAll: true, + dataSource: this.items + }); - // act - this.collapseRow(1); + // act + this.collapseRow(1); - // assert - const items = this.dataController.items(); - assert.strictEqual(items.length, 4, 'all items are visible'); - assert.strictEqual(items[0].data.name, 'Name 3', 'item 1 name value'); - assert.strictEqual(items[0].isExpanded, false, 'item 1 is not expanded'); -}); + // assert + const items = this.dataController.items(); + assert.strictEqual(items.length, 4, 'all items are visible'); + assert.strictEqual(items[0].data.name, 'Name 3', 'item 1 name value'); + assert.strictEqual(items[0].isExpanded, false, 'item 1 is not expanded'); + }); -// T554475 -QUnit.test('refresh after collapseRow when autoExpandAll', function(assert) { + // T554475 + QUnit.test('refresh after collapseRow when autoExpandAll', function(assert) { // arrange - this.setupTreeList({ - autoExpandAll: true, - dataSource: this.items - }); + this.setupTreeList({ + autoExpandAll: true, + dataSource: this.items + }); - this.collapseRow(1); + this.collapseRow(1); - // act - this.refresh(); + // act + this.refresh(); - // assert - const items = this.dataController.items(); - assert.strictEqual(items.length, 4, 'all items are visible'); - assert.strictEqual(items[0].data.name, 'Name 3', 'item 1 name value'); - assert.strictEqual(items[0].isExpanded, false, 'item 1 is not expanded'); -}); + // assert + const items = this.dataController.items(); + assert.strictEqual(items.length, 4, 'all items are visible'); + assert.strictEqual(items[0].data.name, 'Name 3', 'item 1 name value'); + assert.strictEqual(items[0].isExpanded, false, 'item 1 is not expanded'); + }); -QUnit.skip('Initial load when dataSource has filter and filterMode is standard', function(assert) { + QUnit.skip('Initial load when dataSource has filter and filterMode is standard', function(assert) { // arrange, act - let loadingArgs = []; - - this.setupTreeList({ - filterMode: 'standard', - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); - } - }, - filter: ['age', '=', 19] - } - }); + const loadingArgs = []; + + this.setupTreeList({ + filterMode: 'standard', + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } + }, + filter: ['age', '=', 19] + } + }); - // assert - assert.deepEqual(loadingArgs, [ - { - filter: [['parentId', '=', 0], 'and', ['age', '=', 19]], - group: null, - sort: null, - parentIds: [0], - userData: {} - } - ], 'loading arguments'); + // assert + assert.deepEqual(loadingArgs, [ + { + filter: [['parentId', '=', 0], 'and', ['age', '=', 19]], + group: null, + sort: null, + parentIds: [0], + userData: {} + } + ], 'loading arguments'); - let items = this.dataController.items(); - assert.equal(items.length, 2, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); - assert.equal(items[1].data.name, 'Name 1', 'item 2 name value'); -}); + const items = this.dataController.items(); + assert.equal(items.length, 2, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); + assert.equal(items[1].data.name, 'Name 1', 'item 2 name value'); + }); -QUnit.test('Initial load when dataSource has filter and filterMode is withAncestors (default)', function(assert) { + QUnit.test('Initial load when dataSource has filter and filterMode is withAncestors (default)', function(assert) { // arrange, act - const loadingArgs = []; - - const arrayStore = new ArrayStore({ - data: this.items - }); + const loadingArgs = []; - this.setupTreeList({ - expandNodesOnFiltering: true, - dataSource: { - load: function(loadOptions) { - const d = $.Deferred(); - loadingArgs.push(loadOptions); - setTimeout(function() { - arrayStore.load(loadOptions).done(function(data) { - d.resolve(data); - }).fail(d.reject); - }, 10); + const arrayStore = new ArrayStore({ + data: this.items + }); - return d; - }, - useDefaultSearch: true, - filter: ['age', '=', 19] - } - }); + this.setupTreeList({ + expandNodesOnFiltering: true, + dataSource: { + load: function(loadOptions) { + const d = $.Deferred(); + loadingArgs.push(loadOptions); + setTimeout(function() { + arrayStore.load(loadOptions).done(function(data) { + d.resolve(data); + }).fail(d.reject); + }, 10); + + return d; + }, + useDefaultSearch: true, + filter: ['age', '=', 19] + } + }); - this.clock.tick(10); - this.clock.tick(10); - - // assert - assert.equal(this.option('expandedRowKeys').length, 2, 'expandedRowKeys count'); - assert.deepEqual(loadingArgs, [{ - filter: ['age', '=', 19], - group: null, - sort: null, - userData: {} - }, { - filter: ['id', '=', 5], - group: null, - sort: null, - userData: {} - }], 'loading arguments'); - - const items = this.dataController.items(); - assert.equal(items.length, 4, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); - assert.equal(items[0].level, 0, 'item 1 level'); - assert.equal(items[1].data.name, 'Name 5', 'item 2 name value'); - assert.equal(items[1].level, 1, 'item 2 level'); - assert.equal(items[2].data.name, 'Name 7', 'item 3 name value'); - assert.equal(items[2].level, 2, 'item 3 level'); - assert.equal(items[3].data.name, 'Name 1', 'item 4 name value'); - assert.strictEqual(items[3].node.hasChildren, false, 'item 4 name hasChildren'); - assert.equal(items[3].node.children.length, 0, 'item 4 name children length'); - assert.equal(items[3].level, 0, 'item 4 level'); -}); + this.clock.tick(10); + this.clock.tick(10); -// T698573 -QUnit.test('Collapse node when dataSource has filter and filterMode is withAncestors (default)', function(assert) { + // assert + assert.equal(this.option('expandedRowKeys').length, 2, 'expandedRowKeys count'); + assert.deepEqual(loadingArgs, [{ + filter: ['age', '=', 19], + group: null, + sort: null, + userData: {} + }, { + filter: ['id', '=', 5], + group: null, + sort: null, + userData: {} + }], 'loading arguments'); + + const items = this.dataController.items(); + assert.equal(items.length, 4, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); + assert.equal(items[0].level, 0, 'item 1 level'); + assert.equal(items[1].data.name, 'Name 5', 'item 2 name value'); + assert.equal(items[1].level, 1, 'item 2 level'); + assert.equal(items[2].data.name, 'Name 7', 'item 3 name value'); + assert.equal(items[2].level, 2, 'item 3 level'); + assert.equal(items[3].data.name, 'Name 1', 'item 4 name value'); + assert.strictEqual(items[3].node.hasChildren, false, 'item 4 name hasChildren'); + assert.equal(items[3].node.children.length, 0, 'item 4 name children length'); + assert.equal(items[3].level, 0, 'item 4 level'); + }); + + // T698573 + QUnit.test('Collapse node when dataSource has filter and filterMode is withAncestors (default)', function(assert) { // arrange, act - let loadingArgs = []; - - const arrayStore = new ArrayStore({ - data: this.items - }); - - this.setupTreeList({ - expandNodesOnFiltering: true, - hasItemsExpr: function() { - return true; - }, - dataSource: { - load: function(loadOptions) { - const d = $.Deferred(); - loadingArgs.push(loadOptions); - setTimeout(function() { - arrayStore.load(loadOptions).done(function(data) { - d.resolve(data); - }).fail(d.reject); - }); + let loadingArgs = []; + + const arrayStore = new ArrayStore({ + data: this.items + }); - return d; + this.setupTreeList({ + expandNodesOnFiltering: true, + hasItemsExpr: function() { + return true; }, - filter: ['age', '=', 19] - } - }); + dataSource: { + load: function(loadOptions) { + const d = $.Deferred(); + loadingArgs.push(loadOptions); + setTimeout(function() { + arrayStore.load(loadOptions).done(function(data) { + d.resolve(data); + }).fail(d.reject); + }); - this.clock.tick(); + return d; + }, + filter: ['age', '=', 19] + } + }); - assert.equal(loadingArgs.length, 2, 'two loading on init'); + this.clock.tick(); - // act - loadingArgs = []; - this.collapseRow(1); - this.clock.tick(); - - // assert - const items = this.dataController.items(); - assert.equal(loadingArgs.length, 0, 'no loadings on collapse row'); - assert.strictEqual(items.length, 2, 'item count'); - assert.strictEqual(this.isRowExpanded(items[0].key), false, 'item 0 is collapsed'); - assert.strictEqual(items[0].node.children.length, 1, 'item 0 children'); - assert.strictEqual(items[0].node.hasChildren, true, 'item 0 hasChildren'); - assert.strictEqual(items[1].node.children.length, 0, 'item 1 children'); - assert.strictEqual(items[1].node.hasChildren, false, 'item 1 hasChildren'); -}); + assert.equal(loadingArgs.length, 2, 'two loading on init'); -QUnit.test('Filter changing should expand nodes', function(assert) { - // arrange, act - this.setupTreeList({ - expandNodesOnFiltering: true, - dataSource: this.items + // act + loadingArgs = []; + this.collapseRow(1); + this.clock.tick(); + + // assert + const items = this.dataController.items(); + assert.equal(loadingArgs.length, 0, 'no loadings on collapse row'); + assert.strictEqual(items.length, 2, 'item count'); + assert.strictEqual(this.isRowExpanded(items[0].key), false, 'item 0 is collapsed'); + assert.strictEqual(items[0].node.children.length, 1, 'item 0 children'); + assert.strictEqual(items[0].node.hasChildren, true, 'item 0 hasChildren'); + assert.strictEqual(items[1].node.children.length, 0, 'item 1 children'); + assert.strictEqual(items[1].node.hasChildren, false, 'item 1 hasChildren'); }); - // act - const dataSource = this.getDataSource(); + QUnit.test('Filter changing should expand nodes', function(assert) { + // arrange, act + this.setupTreeList({ + expandNodesOnFiltering: true, + dataSource: this.items + }); - dataSource.filter(['age', '=', 19]); - dataSource.load(); + // act + const dataSource = this.getDataSource(); - // assert - assert.equal(this.option('expandedRowKeys').length, 2, 'expandedRowKeys count'); - assert.equal(this.dataController.items().length, 4, 'count items'); -}); + dataSource.filter(['age', '=', 19]); + dataSource.load(); -QUnit.test('Initial load when dataSource has filter and filterMode is withAncestors (default) when remoteOperations false', function(assert) { - // arrange, act - const loadingArgs = []; - - this.setupTreeList({ - expandNodesOnFiltering: true, - remoteOperations: false, - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); - } - }, - filter: ['age', '=', 19] - } + // assert + assert.equal(this.option('expandedRowKeys').length, 2, 'expandedRowKeys count'); + assert.equal(this.dataController.items().length, 4, 'count items'); }); - // assert - assert.deepEqual(loadingArgs, [{ - userData: {} - }], 'loading arguments'); - - const items = this.dataController.items(); - assert.equal(items.length, 4, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); - assert.equal(items[0].level, 0, 'item 1 level'); - assert.equal(items[1].data.name, 'Name 5', 'item 2 name value'); - assert.equal(items[1].level, 1, 'item 2 level'); - assert.equal(items[2].data.name, 'Name 7', 'item 3 name value'); - assert.equal(items[2].level, 2, 'item 3 level'); - assert.equal(items[3].data.name, 'Name 1', 'item 4 name value'); - assert.strictEqual(items[3].node.hasChildren, false, 'item 4 name hasChildren'); - assert.equal(items[3].node.children.length, 1, 'item 4 name children length'); - assert.strictEqual(items[3].node.children[0].visible, false, 'item 4 name children 0 visible'); - assert.equal(items[3].level, 0, 'item 4 level'); -}); + QUnit.test('Initial load when dataSource has filter and filterMode is withAncestors (default) when remoteOperations false', function(assert) { + // arrange, act + const loadingArgs = []; + + this.setupTreeList({ + expandNodesOnFiltering: true, + remoteOperations: false, + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } + }, + filter: ['age', '=', 19] + } + }); -QUnit.test('Initial load when dataSource has filter and allow expand filtered items in onNodesInitialized', function(assert) { + // assert + assert.deepEqual(loadingArgs, [{ + userData: {} + }], 'loading arguments'); + + const items = this.dataController.items(); + assert.equal(items.length, 4, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); + assert.equal(items[0].level, 0, 'item 1 level'); + assert.equal(items[1].data.name, 'Name 5', 'item 2 name value'); + assert.equal(items[1].level, 1, 'item 2 level'); + assert.equal(items[2].data.name, 'Name 7', 'item 3 name value'); + assert.equal(items[2].level, 2, 'item 3 level'); + assert.equal(items[3].data.name, 'Name 1', 'item 4 name value'); + assert.strictEqual(items[3].node.hasChildren, false, 'item 4 name hasChildren'); + assert.equal(items[3].node.children.length, 1, 'item 4 name children length'); + assert.strictEqual(items[3].node.children[0].visible, false, 'item 4 name children 0 visible'); + assert.equal(items[3].level, 0, 'item 4 level'); + }); + + QUnit.test('Initial load when dataSource has filter and allow expand filtered items in onNodesInitialized', function(assert) { // arrange, act - this.setupTreeList({ - expandNodesOnFiltering: true, - remoteOperations: false, - dataSource: { - store: this.items, - filter: ['age', '=', 19] - }, - columns: [{ dataField: 'age', dataType: 'number' }], // TODO - onNodesInitialized: function(e) { - foreachNodes(e.root.children, function(node) { - if(node.visible && !node.hasChildren && node.children.length) { - node.hasChildren = true; - node.children.forEach(function(node) { - node.visible = true; - }); - } - }); - } - }); + this.setupTreeList({ + expandNodesOnFiltering: true, + remoteOperations: false, + dataSource: { + store: this.items, + filter: ['age', '=', 19] + }, + columns: [{ dataField: 'age', dataType: 'number' }], // TODO + onNodesInitialized: function(e) { + foreachNodes(e.root.children, function(node) { + if(node.visible && !node.hasChildren && node.children.length) { + node.hasChildren = true; + node.children.forEach(function(node) { + node.visible = true; + }); + } + }); + } + }); - // assert - assert.deepEqual(this.option('expandedRowKeys'), [5, 1], 'expandedRowKeys'); - let items = this.dataController.items(); - assert.equal(items.length, 4, 'count items'); - assert.strictEqual(items[3].node.hasChildren, true, 'item 4 name hasChildren'); - assert.equal(items[3].node.children.length, 1, 'item 4 name children length'); - assert.strictEqual(items[3].node.children[0].visible, true, 'item 4 name children 0 visible'); + // assert + assert.deepEqual(this.option('expandedRowKeys'), [5, 1], 'expandedRowKeys'); + let items = this.dataController.items(); + assert.equal(items.length, 4, 'count items'); + assert.strictEqual(items[3].node.hasChildren, true, 'item 4 name hasChildren'); + assert.equal(items[3].node.children.length, 1, 'item 4 name children length'); + assert.strictEqual(items[3].node.children[0].visible, true, 'item 4 name children 0 visible'); - // act - this.expandRow(items[3].key); + // act + this.expandRow(items[3].key); - // assert - assert.deepEqual(this.option('expandedRowKeys'), [5, 1, 2], 'expandedRowKeys'); - items = this.dataController.items(); - assert.equal(items.length, 5, 'count items'); - assert.strictEqual(items[4].node.parent, items[3].node, 'item 5 is child of item 4'); -}); + // assert + assert.deepEqual(this.option('expandedRowKeys'), [5, 1, 2], 'expandedRowKeys'); + items = this.dataController.items(); + assert.equal(items.length, 5, 'count items'); + assert.strictEqual(items[4].node.parent, items[3].node, 'item 5 is child of item 4'); + }); -QUnit.test('Initial load when dataSource has filter and allow expand filtered items and expand they in onNodesInitialized', function(assert) { + QUnit.test('Initial load when dataSource has filter and allow expand filtered items and expand they in onNodesInitialized', function(assert) { // arrange, act - let isExpanding = false; - const that = this; - - this.setupTreeList({ - expandNodesOnFiltering: true, - remoteOperations: false, - dataSource: { - store: this.items, - filter: ['age', '=', 19] - }, - columns: [{ dataField: 'age', dataType: 'number' }], // TODO - onRowExpanding: function(e) { - isExpanding = true; - }, - onRowExpanded: function(e) { - isExpanding = false; - }, - onRowCollapsing: function(e) { - isExpanding = true; - }, - onRowCollapsed: function(e) { - isExpanding = false; - }, - onNodesInitialized: function(e) { - foreachNodes(e.root.children, function(node) { - if(node.visible && !node.hasChildren && node.children.length) { - if(!isExpanding) { - that.expandRow(node.key); + let isExpanding = false; + const that = this; + + this.setupTreeList({ + expandNodesOnFiltering: true, + remoteOperations: false, + dataSource: { + store: this.items, + filter: ['age', '=', 19] + }, + columns: [{ dataField: 'age', dataType: 'number' }], // TODO + onRowExpanding: function(e) { + isExpanding = true; + }, + onRowExpanded: function(e) { + isExpanding = false; + }, + onRowCollapsing: function(e) { + isExpanding = true; + }, + onRowCollapsed: function(e) { + isExpanding = false; + }, + onNodesInitialized: function(e) { + foreachNodes(e.root.children, function(node) { + if(node.visible && !node.hasChildren && node.children.length) { + if(!isExpanding) { + that.expandRow(node.key); + } + node.hasChildren = true; + node.children.forEach(function(node) { + node.visible = true; + }); } - node.hasChildren = true; - node.children.forEach(function(node) { - node.visible = true; - }); - } - }); - } - }); + }); + } + }); - // assert - assert.deepEqual(this.option('expandedRowKeys'), [5, 1, 2], 'expandedRowKeys'); - let items = this.dataController.items(); - assert.equal(items.length, 5, 'count items'); - assert.strictEqual(items[4].node.parent, items[3].node, 'item 5 is child of item 4'); + // assert + assert.deepEqual(this.option('expandedRowKeys'), [5, 1, 2], 'expandedRowKeys'); + let items = this.dataController.items(); + assert.equal(items.length, 5, 'count items'); + assert.strictEqual(items[4].node.parent, items[3].node, 'item 5 is child of item 4'); - // act - this.collapseRow(items[3].key); - - // assert - assert.deepEqual(this.option('expandedRowKeys'), [5, 1], 'expandedRowKeys'); - items = this.dataController.items(); - assert.equal(items.length, 4, 'count items'); - assert.strictEqual(items[3].node.hasChildren, true, 'item 4 name hasChildren'); - assert.equal(items[3].node.children.length, 1, 'item 4 name children length'); - assert.strictEqual(items[3].node.children[0].visible, true, 'item 4 name children 0 visible'); -}); + // act + this.collapseRow(items[3].key); -QUnit.test('Initial load when expandNodesOnFiltering and no filter', function(assert) { - // arrange, act - this.setupTreeList({ - expandNodesOnFiltering: true, - dataSource: this.items + // assert + assert.deepEqual(this.option('expandedRowKeys'), [5, 1], 'expandedRowKeys'); + items = this.dataController.items(); + assert.equal(items.length, 4, 'count items'); + assert.strictEqual(items[3].node.hasChildren, true, 'item 4 name hasChildren'); + assert.equal(items[3].node.children.length, 1, 'item 4 name children length'); + assert.strictEqual(items[3].node.children[0].visible, true, 'item 4 name children 0 visible'); }); - // assert - const items = this.dataController.items(); - assert.equal(items.length, 3, 'only first level items are visible'); -}); - -QUnit.test('Initial load when expandNodesOnFiltering and dataSource has filter and filterMode is matchOnly', function(assert) { + QUnit.test('Initial load when expandNodesOnFiltering and no filter', function(assert) { // arrange, act - this.setupTreeList({ - filterMode: 'matchOnly', - expandNodesOnFiltering: true, - dataSource: { - store: this.items, - filter: ['age', '=', 19] - } - }); + this.setupTreeList({ + expandNodesOnFiltering: true, + dataSource: this.items + }); - // assert - const items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); - assert.equal(items[0].level, 0, 'item 1 level'); - assert.equal(items[1].data.name, 'Name 7', 'item 2 name value'); - assert.equal(items[1].level, 1, 'item 2 level'); - assert.equal(items[1].node.level, 2, 'item 2 node level'); - assert.equal(items[2].data.name, 'Name 1', 'item 3 name value'); - assert.equal(items[2].level, 0, 'item 3 level'); -}); + // assert + const items = this.dataController.items(); + assert.equal(items.length, 3, 'only first level items are visible'); + }); -QUnit.test('Initial load dataSource has filter and filterMode matchOnly is emulated using onNodesInitialized', function(assert) { + QUnit.test('Initial load when expandNodesOnFiltering and dataSource has filter and filterMode is matchOnly', function(assert) { // arrange, act - const that = this; - this.setupTreeList({ - onNodesInitialized: function(e) { - const filter = that.getCombinedFilter(); + this.setupTreeList({ + filterMode: 'matchOnly', + expandNodesOnFiltering: true, + dataSource: { + store: this.items, + filter: ['age', '=', 19] + } + }); - if(!filter) return; + // assert + const items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); + assert.equal(items[0].level, 0, 'item 1 level'); + assert.equal(items[1].data.name, 'Name 7', 'item 2 name value'); + assert.equal(items[1].level, 1, 'item 2 level'); + assert.equal(items[1].node.level, 2, 'item 2 node level'); + assert.equal(items[2].data.name, 'Name 1', 'item 3 name value'); + assert.equal(items[2].level, 0, 'item 3 level'); + }); + + QUnit.test('Initial load dataSource has filter and filterMode matchOnly is emulated using onNodesInitialized', function(assert) { + // arrange, act + const that = this; + this.setupTreeList({ + onNodesInitialized: function(e) { + const filter = that.getCombinedFilter(); - foreachNodes(e.root.children, function(node) { - if(node.visible && !query([node.data]).filter(filter).toArray().length) { - node.visible = false; - } - }); - }, - expandNodesOnFiltering: true, - dataSource: { - store: this.items, - filter: ['age', '=', 19] - } - }); + if(!filter) return; - // assert - const items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); - assert.equal(items[0].level, 0, 'item 1 level'); - assert.equal(items[1].data.name, 'Name 7', 'item 2 name value'); - assert.equal(items[1].level, 1, 'item 2 level'); - assert.equal(items[1].node.level, 2, 'item 2 node level'); - assert.equal(items[2].data.name, 'Name 1', 'item 3 name value'); - assert.equal(items[2].level, 0, 'item 3 level'); -}); + foreachNodes(e.root.children, function(node) { + if(node.visible && !query([node.data]).filter(filter).toArray().length) { + node.visible = false; + } + }); + }, + expandNodesOnFiltering: true, + dataSource: { + store: this.items, + filter: ['age', '=', 19] + } + }); -QUnit.test('Initial load when expandNodesOnFiltering disabled and dataSource has filter and filterMode is matchOnly', function(assert) { + // assert + const items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); + assert.equal(items[0].level, 0, 'item 1 level'); + assert.equal(items[1].data.name, 'Name 7', 'item 2 name value'); + assert.equal(items[1].level, 1, 'item 2 level'); + assert.equal(items[1].node.level, 2, 'item 2 node level'); + assert.equal(items[2].data.name, 'Name 1', 'item 3 name value'); + assert.equal(items[2].level, 0, 'item 3 level'); + }); + + QUnit.test('Initial load when expandNodesOnFiltering disabled and dataSource has filter and filterMode is matchOnly', function(assert) { // arrange, act - this.setupTreeList({ - filterMode: 'matchOnly', - expandNodesOnFiltering: false, - dataSource: { - store: this.items, - filter: ['age', '=', 19] - } - }); + this.setupTreeList({ + filterMode: 'matchOnly', + expandNodesOnFiltering: false, + dataSource: { + store: this.items, + filter: ['age', '=', 19] + } + }); - // assert - const items = this.dataController.items(); - assert.equal(items.length, 2, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); - assert.equal(items[0].level, 0, 'item 1 level'); - assert.equal(items[1].data.name, 'Name 1', 'item 2 name value'); - assert.equal(items[1].level, 0, 'item 2 level'); -}); + // assert + const items = this.dataController.items(); + assert.equal(items.length, 2, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); + assert.equal(items[0].level, 0, 'item 1 level'); + assert.equal(items[1].data.name, 'Name 1', 'item 2 name value'); + assert.equal(items[1].level, 0, 'item 2 level'); + }); -QUnit.test('Initial load when dataSource has filter and filterMode is matchOnly and root nodes area hidden', function(assert) { + QUnit.test('Initial load when dataSource has filter and filterMode is matchOnly and root nodes area hidden', function(assert) { // arrange, act - this.setupTreeList({ - filterMode: 'matchOnly', - expandNodesOnFiltering: true, - dataSource: { - store: { - type: 'array', - data: this.items - }, - filter: ['age', '<', 19] - } - }); + this.setupTreeList({ + filterMode: 'matchOnly', + expandNodesOnFiltering: true, + dataSource: { + store: { + type: 'array', + data: this.items + }, + filter: ['age', '<', 19] + } + }); - // assert - const items = this.dataController.items(); - assert.equal(items.length, 4, 'count items'); - assert.equal(items[0].data.name, 'Name 2', 'item 1 name value'); - assert.equal(items[0].level, 0, 'item 1 level'); - assert.equal(items[1].data.name, 'Name 6', 'item 2 name value'); - assert.equal(items[1].level, 0, 'item 2 level'); - assert.equal(items[2].data.name, 'Name 5', 'item 3 name value'); - assert.equal(items[2].level, 0, 'item 3 level'); - assert.equal(items[3].data.name, 'Name 4', 'item 4 name value'); - assert.equal(items[3].level, 0, 'item 4 level'); -}); + // assert + const items = this.dataController.items(); + assert.equal(items.length, 4, 'count items'); + assert.equal(items[0].data.name, 'Name 2', 'item 1 name value'); + assert.equal(items[0].level, 0, 'item 1 level'); + assert.equal(items[1].data.name, 'Name 6', 'item 2 name value'); + assert.equal(items[1].level, 0, 'item 2 level'); + assert.equal(items[2].data.name, 'Name 5', 'item 3 name value'); + assert.equal(items[2].level, 0, 'item 3 level'); + assert.equal(items[3].data.name, 'Name 4', 'item 4 name value'); + assert.equal(items[3].level, 0, 'item 4 level'); + }); + + QUnit.test('Initial load when filterMode is matchOnly and remoteOperations is false', function(assert) { + // arrange, act + this.setupTreeList({ + filterMode: 'matchOnly', + expandNodesOnFiltering: true, + remoteOperations: false, + dataSource: { + store: this.items, + filter: ['age', '=', 19] + } + }); -QUnit.test('Initial load when filterMode is matchOnly and remoteOperations is false', function(assert) { + // assert + const items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); + assert.equal(items[0].level, 0, 'item 1 level'); + assert.equal(items[1].data.name, 'Name 7', 'item 2 name value'); + assert.equal(items[1].level, 1, 'item 2 level'); + assert.equal(items[2].data.name, 'Name 1', 'item 3 name value'); + assert.equal(items[2].level, 0, 'item 3 level'); + }); + + // T515374 + QUnit.test('Initial load when dataSource has filter whose length is more than available (filterMode is withAncestors)', function(assert) { // arrange, act - this.setupTreeList({ - filterMode: 'matchOnly', - expandNodesOnFiltering: true, - remoteOperations: false, - dataSource: { - store: this.items, - filter: ['age', '=', 19] - } - }); + const loadingArgs = []; - // assert - const items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); - assert.equal(items[0].level, 0, 'item 1 level'); - assert.equal(items[1].data.name, 'Name 7', 'item 2 name value'); - assert.equal(items[1].level, 1, 'item 2 level'); - assert.equal(items[2].data.name, 'Name 1', 'item 3 name value'); - assert.equal(items[2].level, 0, 'item 3 level'); -}); + const arrayStore = new ArrayStore({ + data: this.items + }); -// T515374 -QUnit.test('Initial load when dataSource has filter whose length is more than available (filterMode is withAncestors)', function(assert) { - // arrange, act - const loadingArgs = []; - - const arrayStore = new ArrayStore({ - data: this.items - }); - - this.setupTreeList({ - maxFilterLengthInRequest: 0, - expandNodesOnFiltering: true, - dataSource: { - load: function(loadOptions) { - const d = $.Deferred(); - loadingArgs.push(loadOptions); - setTimeout(function() { - arrayStore.load(loadOptions).done(function(data) { - d.resolve(data); - }).fail(d.reject); - }, 10); - - return d; - }, - useDefaultSearch: true, - filter: ['age', '=', 19] - } - }); + this.setupTreeList({ + maxFilterLengthInRequest: 0, + expandNodesOnFiltering: true, + dataSource: { + load: function(loadOptions) { + const d = $.Deferred(); + loadingArgs.push(loadOptions); + setTimeout(function() { + arrayStore.load(loadOptions).done(function(data) { + d.resolve(data); + }).fail(d.reject); + }, 10); + + return d; + }, + useDefaultSearch: true, + filter: ['age', '=', 19] + } + }); - this.clock.tick(10); - this.clock.tick(10); - - // assert - assert.equal(this.option('expandedRowKeys').length, 2, 'expandedRowKeys count'); - assert.deepEqual(loadingArgs, [{ - filter: ['age', '=', 19], - group: null, - sort: null, - userData: {} - }, { - filter: null, - group: null, - sort: null, - userData: {} - }], 'loading arguments'); - - const items = this.dataController.items(); - assert.equal(items.length, 4, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); - assert.equal(items[0].level, 0, 'item 1 level'); - assert.equal(items[1].data.name, 'Name 5', 'item 2 name value'); - assert.equal(items[1].level, 1, 'item 2 level'); - assert.equal(items[2].data.name, 'Name 7', 'item 3 name value'); - assert.equal(items[2].level, 2, 'item 3 level'); - assert.equal(items[3].data.name, 'Name 1', 'item 4 name value'); - assert.strictEqual(items[3].node.hasChildren, false, 'item 4 name hasChildren'); - assert.equal(items[3].node.children.length, 0, 'item 4 name children length'); - assert.equal(items[3].level, 0, 'item 4 level'); -}); + this.clock.tick(10); + this.clock.tick(10); -// T515374 -QUnit.test('Initial load when dataSource has filter whose length is more than available when remoteOperations false (filterMode is withAncestors)', function(assert) { + // assert + assert.equal(this.option('expandedRowKeys').length, 2, 'expandedRowKeys count'); + assert.deepEqual(loadingArgs, [{ + filter: ['age', '=', 19], + group: null, + sort: null, + userData: {} + }, { + filter: null, + group: null, + sort: null, + userData: {} + }], 'loading arguments'); + + const items = this.dataController.items(); + assert.equal(items.length, 4, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); + assert.equal(items[0].level, 0, 'item 1 level'); + assert.equal(items[1].data.name, 'Name 5', 'item 2 name value'); + assert.equal(items[1].level, 1, 'item 2 level'); + assert.equal(items[2].data.name, 'Name 7', 'item 3 name value'); + assert.equal(items[2].level, 2, 'item 3 level'); + assert.equal(items[3].data.name, 'Name 1', 'item 4 name value'); + assert.strictEqual(items[3].node.hasChildren, false, 'item 4 name hasChildren'); + assert.equal(items[3].node.children.length, 0, 'item 4 name children length'); + assert.equal(items[3].level, 0, 'item 4 level'); + }); + + // T515374 + QUnit.test('Initial load when dataSource has filter whose length is more than available when remoteOperations false (filterMode is withAncestors)', function(assert) { // arrange, act - const loadingArgs = []; - - const arrayStore = new ArrayStore({ - data: this.items - }); - - this.setupTreeList({ - maxFilterLengthInRequest: 0, - remoteOperations: false, - expandNodesOnFiltering: true, - dataSource: { - load: function(loadOptions) { - const d = $.Deferred(); - loadingArgs.push(loadOptions); - setTimeout(function() { - arrayStore.load(loadOptions).done(function(data) { - d.resolve(data); - }).fail(d.reject); - }, 10); - - return d; - }, - useDefaultSearch: true, - filter: ['age', '=', 19] - } - }); + const loadingArgs = []; - this.clock.tick(10); - this.clock.tick(10); - - // assert - assert.equal(this.option('expandedRowKeys').length, 2, 'expandedRowKeys count'); - assert.deepEqual(loadingArgs, [{ - userData: {} - }], 'loading arguments'); - - const items = this.dataController.items(); - assert.equal(items.length, 4, 'count items'); - assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); - assert.equal(items[0].level, 0, 'item 1 level'); - assert.equal(items[1].data.name, 'Name 5', 'item 2 name value'); - assert.equal(items[1].level, 1, 'item 2 level'); - assert.equal(items[2].data.name, 'Name 7', 'item 3 name value'); - assert.equal(items[2].level, 2, 'item 3 level'); - assert.equal(items[3].data.name, 'Name 1', 'item 4 name value'); - assert.strictEqual(items[3].node.hasChildren, false, 'item 4 name hasChildren'); - assert.equal(items[3].node.children.length, 1, 'item 4 name children length'); - assert.strictEqual(items[3].node.children[0].visible, false, 'item 4 name children 0 visible'); - assert.equal(items[3].level, 0, 'item 4 level'); -}); + const arrayStore = new ArrayStore({ + data: this.items + }); -QUnit.test('expand -> collapse -> expand row', function(assert) { - // arrange - let items; - let loadingArgs = []; - - this.setupTreeList({ - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); - } + this.setupTreeList({ + maxFilterLengthInRequest: 0, + remoteOperations: false, + expandNodesOnFiltering: true, + dataSource: { + load: function(loadOptions) { + const d = $.Deferred(); + loadingArgs.push(loadOptions); + setTimeout(function() { + arrayStore.load(loadOptions).done(function(data) { + d.resolve(data); + }).fail(d.reject); + }, 10); + + return d; + }, + useDefaultSearch: true, + filter: ['age', '=', 19] } - } - }); - loadingArgs = []; + }); - // act - this.expandRow(1); + this.clock.tick(10); + this.clock.tick(10); - // assert - assert.equal(loadingArgs.length, 1, 'count load'); - assert.deepEqual(loadingArgs, [ - { - filter: ['parentId', '=', 1], - group: null, - sort: null, - parentIds: [1], + // assert + assert.equal(this.option('expandedRowKeys').length, 2, 'expandedRowKeys count'); + assert.deepEqual(loadingArgs, [{ userData: {} - } - ], 'loading arguments'); + }], 'loading arguments'); + + const items = this.dataController.items(); + assert.equal(items.length, 4, 'count items'); + assert.equal(items[0].data.name, 'Name 3', 'item 1 name value'); + assert.equal(items[0].level, 0, 'item 1 level'); + assert.equal(items[1].data.name, 'Name 5', 'item 2 name value'); + assert.equal(items[1].level, 1, 'item 2 level'); + assert.equal(items[2].data.name, 'Name 7', 'item 3 name value'); + assert.equal(items[2].level, 2, 'item 3 level'); + assert.equal(items[3].data.name, 'Name 1', 'item 4 name value'); + assert.strictEqual(items[3].node.hasChildren, false, 'item 4 name hasChildren'); + assert.equal(items[3].node.children.length, 1, 'item 4 name children length'); + assert.strictEqual(items[3].node.children[0].visible, false, 'item 4 name children 0 visible'); + assert.equal(items[3].level, 0, 'item 4 level'); + }); + + QUnit.test('expand -> collapse -> expand row', function(assert) { + // arrange + let items; + let loadingArgs = []; + + this.setupTreeList({ + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } + } + } + }); + loadingArgs = []; - items = this.dataController.items(); - assert.equal(items.length, 5, 'count items'); + // act + this.expandRow(1); - // act - this.collapseRow(1); + // assert + assert.equal(loadingArgs.length, 1, 'count load'); + assert.deepEqual(loadingArgs, [ + { + filter: ['parentId', '=', 1], + group: null, + sort: null, + parentIds: [1], + userData: {} + } + ], 'loading arguments'); - // assert - assert.equal(loadingArgs.length, 1, 'count load'); - items = this.dataController.items(); - assert.equal(items.length, 3, 'count items'); + items = this.dataController.items(); + assert.equal(items.length, 5, 'count items'); - // act - this.expandRow(1); + // act + this.collapseRow(1); - // assert - assert.equal(loadingArgs.length, 1, 'count load'); - items = this.dataController.items(); - assert.equal(items.length, 5, 'count items'); -}); + // assert + assert.equal(loadingArgs.length, 1, 'count load'); + items = this.dataController.items(); + assert.equal(items.length, 3, 'count items'); -QUnit.test('Checking the \'hasChildren\' property of the node', function(assert) { - // arrange, act - this.setupTreeList({ - dataSource: this.items + // act + this.expandRow(1); + + // assert + assert.equal(loadingArgs.length, 1, 'count load'); + items = this.dataController.items(); + assert.equal(items.length, 5, 'count items'); }); - // assert - assert.ok(this.dataController.items()[0].node.hasChildren, 'first item has children'); -}); + QUnit.test('Checking the \'hasChildren\' property of the node', function(assert) { + // arrange, act + this.setupTreeList({ + dataSource: this.items + }); -QUnit.test('Checking the \'hasChildren\' property of the node after expand', function(assert) { - // arrange - this.setupTreeList({ - dataSource: this.items + // assert + assert.ok(this.dataController.items()[0].node.hasChildren, 'first item has children'); }); - // assert - assert.ok(this.dataController.items()[2].node.hasChildren, 'third item has children'); + QUnit.test('Checking the \'hasChildren\' property of the node after expand', function(assert) { + // arrange + this.setupTreeList({ + dataSource: this.items + }); + + // assert + assert.ok(this.dataController.items()[2].node.hasChildren, 'third item has children'); - // act - this.expandRow(3); + // act + this.expandRow(3); - // assert - assert.notOk(this.dataController.items()[2].node.hasChildren, 'third item hasn\'t children'); -}); + // assert + assert.notOk(this.dataController.items()[2].node.hasChildren, 'third item hasn\'t children'); + }); -QUnit.test('Checking the \'hasChildren\' property of the node when it specified', function(assert) { + QUnit.test('Checking the \'hasChildren\' property of the node when it specified', function(assert) { // arrange - this.items[2].hasItems = false; - this.setupTreeList({ - dataSource: this.items, - hasItemsExpr: 'hasItems' - }); + this.items[2].hasItems = false; + this.setupTreeList({ + dataSource: this.items, + hasItemsExpr: 'hasItems' + }); - // assert - assert.notOk(this.dataController.items()[2].node.hasChildren, 'third item hasn\'t children'); -}); + // assert + assert.notOk(this.dataController.items()[2].node.hasChildren, 'third item hasn\'t children'); + }); -// T585731 -QUnit.test('Checking the \'hasChildren\' property of the node after expand when key as Guid', function(assert) { + // T585731 + QUnit.test('Checking the \'hasChildren\' property of the node after expand when key as Guid', function(assert) { // arrange - let items; - const keys = [new Guid('26992b5c-7d63-89ec-2138-33dd5d244798'), new Guid('1c88aa8d-eaf7-d7b6-4906-ce07a3bdc1cb')]; + let items; + const keys = [new Guid('26992b5c-7d63-89ec-2138-33dd5d244798'), new Guid('1c88aa8d-eaf7-d7b6-4906-ce07a3bdc1cb')]; - this.setupTreeList({ - dataSource: [ - { id: keys[0], parentId: 0 }, - { id: keys[1], parentId: new Guid(keys[0]) } - ] - }); + this.setupTreeList({ + dataSource: [ + { id: keys[0], parentId: 0 }, + { id: keys[1], parentId: new Guid(keys[0]) } + ] + }); - // act - this.expandRow(keys[0]); + // act + this.expandRow(keys[0]); - // assert - items = this.dataController.items(); - assert.strictEqual(items.length, 2, 'item count'); - assert.ok(items[0].node.hasChildren, 'fist item has children'); - assert.ok(items[1].node.hasChildren, 'second item has children'); -}); + // assert + items = this.dataController.items(); + assert.strictEqual(items.length, 2, 'item count'); + assert.ok(items[0].node.hasChildren, 'fist item has children'); + assert.ok(items[1].node.hasChildren, 'second item has children'); + }); -QUnit.test('loadDescendants', function(assert) { + QUnit.test('loadDescendants', function(assert) { // arrange - const loadingArgs = []; - - this.setupTreeList({ - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); + const loadingArgs = []; + + this.setupTreeList({ + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } } } - } - }); + }); - // act - this.loadDescendants(); - - // assert - assert.strictEqual(loadingArgs.length, 4, 'count of load'); - assert.deepEqual(loadingArgs[0].parentIds, [0], 'parentIds argument of the first load'); - assert.deepEqual(loadingArgs[1].parentIds, [1, 2, 3], 'parentIds argument of the second load'); - assert.deepEqual(loadingArgs[2].parentIds, [4, 5, 6], 'parentIds argument of the third load'); - assert.deepEqual(loadingArgs[3].parentIds, [7], 'parentIds argument of the fourth load'); -}); + // act + this.loadDescendants(); + + // assert + assert.strictEqual(loadingArgs.length, 4, 'count of load'); + assert.deepEqual(loadingArgs[0].parentIds, [0], 'parentIds argument of the first load'); + assert.deepEqual(loadingArgs[1].parentIds, [1, 2, 3], 'parentIds argument of the second load'); + assert.deepEqual(loadingArgs[2].parentIds, [4, 5, 6], 'parentIds argument of the third load'); + assert.deepEqual(loadingArgs[3].parentIds, [7], 'parentIds argument of the fourth load'); + }); -QUnit.test('loadDescendants with key', function(assert) { + QUnit.test('loadDescendants with key', function(assert) { // arrange - const loadingArgs = []; - - this.setupTreeList({ - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); + const loadingArgs = []; + + this.setupTreeList({ + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } } } - } - }); + }); - // act - this.loadDescendants(2); + // act + this.loadDescendants(2); - // assert - assert.strictEqual(loadingArgs.length, 3, 'count of load'); - assert.deepEqual(loadingArgs[0].parentIds, [0], 'parentIds argument of the first load'); - assert.deepEqual(loadingArgs[1].parentIds, [2], 'parentIds argument of the second load'); - assert.deepEqual(loadingArgs[2].parentIds, [6], 'parentIds argument of the third load'); -}); + // assert + assert.strictEqual(loadingArgs.length, 3, 'count of load'); + assert.deepEqual(loadingArgs[0].parentIds, [0], 'parentIds argument of the first load'); + assert.deepEqual(loadingArgs[1].parentIds, [2], 'parentIds argument of the second load'); + assert.deepEqual(loadingArgs[2].parentIds, [6], 'parentIds argument of the third load'); + }); -QUnit.test('loadDescendants with several keys', function(assert) { + QUnit.test('loadDescendants with several keys', function(assert) { // arrange - const loadingArgs = []; - - this.setupTreeList({ - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); + const loadingArgs = []; + + this.setupTreeList({ + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } } } - } - }); + }); - // act - this.loadDescendants([1, 2]); - - // assert - assert.strictEqual(loadingArgs.length, 4, 'count of load'); - assert.deepEqual(loadingArgs[0].parentIds, [0], 'parentIds argument of the first load'); - assert.deepEqual(loadingArgs[1].parentIds, [1, 2], 'parentIds argument of the second load'); - assert.deepEqual(loadingArgs[2].parentIds, [4, 5, 6], 'parentIds argument of the third load'); - assert.deepEqual(loadingArgs[3].parentIds, [7], 'parentIds argument of the fourth load'); -}); + // act + this.loadDescendants([1, 2]); -QUnit.test('loadDescendants without deep pass', function(assert) { + // assert + assert.strictEqual(loadingArgs.length, 4, 'count of load'); + assert.deepEqual(loadingArgs[0].parentIds, [0], 'parentIds argument of the first load'); + assert.deepEqual(loadingArgs[1].parentIds, [1, 2], 'parentIds argument of the second load'); + assert.deepEqual(loadingArgs[2].parentIds, [4, 5, 6], 'parentIds argument of the third load'); + assert.deepEqual(loadingArgs[3].parentIds, [7], 'parentIds argument of the fourth load'); + }); + + QUnit.test('loadDescendants without deep pass', function(assert) { // arrange - const loadingArgs = []; - - this.setupTreeList({ - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); + const loadingArgs = []; + + this.setupTreeList({ + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } } } - } - }); + }); - // act - this.loadDescendants(2, true); + // act + this.loadDescendants(2, true); - // assert - assert.strictEqual(loadingArgs.length, 2, 'count of load'); - assert.deepEqual(loadingArgs[0].parentIds, [0], 'parentIds argument of the first load'); - assert.deepEqual(loadingArgs[1].parentIds, [2], 'parentIds argument of the second load'); -}); + // assert + assert.strictEqual(loadingArgs.length, 2, 'count of load'); + assert.deepEqual(loadingArgs[0].parentIds, [0], 'parentIds argument of the first load'); + assert.deepEqual(loadingArgs[1].parentIds, [2], 'parentIds argument of the second load'); + }); -QUnit.test('loadDescendants - the load should not called when expanding row', function(assert) { + QUnit.test('loadDescendants - the load should not called when expanding row', function(assert) { // arrange - const loadingArgs = []; - - this.setupTreeList({ - expandedRowKeys: [0], - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); + const loadingArgs = []; + + this.setupTreeList({ + expandedRowKeys: [0], + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } } } - } - }); + }); - this.loadDescendants(2); + this.loadDescendants(2); - // assert - assert.strictEqual(loadingArgs.length, 3, 'count of load'); + // assert + assert.strictEqual(loadingArgs.length, 3, 'count of load'); - // act - this.expandRow(2); + // act + this.expandRow(2); - // assert - assert.strictEqual(loadingArgs.length, 3, 'count of load'); -}); + // assert + assert.strictEqual(loadingArgs.length, 3, 'count of load'); + }); -QUnit.test('loadDescendants without args - the load should not called when expanding row', function(assert) { + QUnit.test('loadDescendants without args - the load should not called when expanding row', function(assert) { // arrange - const loadingArgs = []; - - this.setupTreeList({ - expandedRowKeys: [0], - dataSource: { - store: { - type: 'array', - data: this.items, - onLoading: function(e) { - loadingArgs.push(e); + const loadingArgs = []; + + this.setupTreeList({ + expandedRowKeys: [0], + dataSource: { + store: { + type: 'array', + data: this.items, + onLoading: function(e) { + loadingArgs.push(e); + } } } - } - }); + }); - this.loadDescendants(); + this.loadDescendants(); - // assert - assert.strictEqual(loadingArgs.length, 4, 'count of load'); + // assert + assert.strictEqual(loadingArgs.length, 4, 'count of load'); - // act - this.expandRow(2); + // act + this.expandRow(2); - // assert - assert.strictEqual(loadingArgs.length, 4, 'count of load'); + // assert + assert.strictEqual(loadingArgs.length, 4, 'count of load'); + }); }); QUnit.module('Load data on demand', { beforeEach: function() { @@ -2478,134 +2480,135 @@ QUnit.module('Load data on demand', { beforeEach: function() { options: options }); }; -}, afterEach: teardownModule }); +}, afterEach: teardownModule }, () => { -QUnit.test('Initialize load', function(assert) { + QUnit.test('Initialize load', function(assert) { // arrange, act - const loadOptions = []; - - this.setupTreeList({ - dataSource: { - load: function(e) { - const d = $.Deferred(); - const nodes = []; - const parentIds = e.parentIds; - - if(parentIds) { - for(let i = 0; i < parentIds.length; i++) { - nodes.push({ id: i + 1, parentId: parentIds[i], field1: 'test1', field2: 'test2', field3: 'test3' }); - nodes.push({ id: i + 2, parentId: parentIds[i], field1: 'test4', field2: 'test5', field3: 'test6' }); + const loadOptions = []; + + this.setupTreeList({ + dataSource: { + load: function(e) { + const d = $.Deferred(); + const nodes = []; + const parentIds = e.parentIds; + + if(parentIds) { + for(let i = 0; i < parentIds.length; i++) { + nodes.push({ id: i + 1, parentId: parentIds[i], field1: 'test1', field2: 'test2', field3: 'test3' }); + nodes.push({ id: i + 2, parentId: parentIds[i], field1: 'test4', field2: 'test5', field3: 'test6' }); + } } - } - loadOptions.push(e); + loadOptions.push(e); - return d.resolve(nodes); + return d.resolve(nodes); + } } - } - }); + }); - // assert - const items = this.dataController.items(); - assert.deepEqual(loadOptions[0].parentIds, [0], 'parentIds'); - assert.equal(items.length, 2, 'count item'); -}); + // assert + const items = this.dataController.items(); + assert.deepEqual(loadOptions[0].parentIds, [0], 'parentIds'); + assert.equal(items.length, 2, 'count item'); + }); -QUnit.test('Expand row', function(assert) { + QUnit.test('Expand row', function(assert) { // arrange - let items; - const loadOptions = []; - const data = { - 0: [ - { id: 1, parentId: 0, field1: 'test1', field2: 'test2', field3: 'test3' }, - { id: 2, parentId: 0, field1: 'test4', field2: 'test5', field3: 'test6' } - ], - 2: [{ id: 3, parentId: 2, field1: 'test7', field2: 'test8', field3: 'test9' }] - }; - - this.setupTreeList({ - dataSource: { - load: function(e) { - const d = $.Deferred(); - const result = []; - const parentIds = e.parentIds; + let items; + const loadOptions = []; + const data = { + 0: [ + { id: 1, parentId: 0, field1: 'test1', field2: 'test2', field3: 'test3' }, + { id: 2, parentId: 0, field1: 'test4', field2: 'test5', field3: 'test6' } + ], + 2: [{ id: 3, parentId: 2, field1: 'test7', field2: 'test8', field3: 'test9' }] + }; - if(parentIds) { - for(let i = 0; i < parentIds.length; i++) { - result.push.apply(result, data[parentIds[i]]); + this.setupTreeList({ + dataSource: { + load: function(e) { + const d = $.Deferred(); + const result = []; + const parentIds = e.parentIds; + + if(parentIds) { + for(let i = 0; i < parentIds.length; i++) { + result.push.apply(result, data[parentIds[i]]); + } } - } - loadOptions.push(e); + loadOptions.push(e); - return d.resolve(result); + return d.resolve(result); + } } - } - }); + }); - // assert - items = this.dataController.items(); - assert.equal(items.length, 2, 'count item'); - assert.deepEqual(loadOptions[0].parentIds, [0], 'parentIds'); + // assert + items = this.dataController.items(); + assert.equal(items.length, 2, 'count item'); + assert.deepEqual(loadOptions[0].parentIds, [0], 'parentIds'); - // act - this.expandRow(2); + // act + this.expandRow(2); - // assert - items = this.dataController.items(); - assert.equal(items.length, 3, 'count item'); - assert.deepEqual(loadOptions[1].parentIds, [2], 'parentIds'); -}); + // assert + items = this.dataController.items(); + assert.equal(items.length, 3, 'count item'); + assert.deepEqual(loadOptions[1].parentIds, [2], 'parentIds'); + }); -// T604935 -QUnit.test('loadOptions.parendIds should be correct when expanding several nodes', function(assert) { + // T604935 + QUnit.test('loadOptions.parendIds should be correct when expanding several nodes', function(assert) { // arrange - let items; - const loadOptions = []; - const data = { - 0: [ - { id: 1, parentId: 0, field1: 'test1', field2: 'test2', field3: 'test3' }, - { id: 2, parentId: 0, field1: 'test4', field2: 'test5', field3: 'test6' } - ], - 1: [], - 2: [{ id: 3, parentId: 2, field1: 'test7', field2: 'test8', field3: 'test9' }] - }; - - this.setupTreeList({ - dataSource: { - load: function(e) { - const d = $.Deferred(); - const result = []; - const parentIds = e.parentIds; + let items; + const loadOptions = []; + const data = { + 0: [ + { id: 1, parentId: 0, field1: 'test1', field2: 'test2', field3: 'test3' }, + { id: 2, parentId: 0, field1: 'test4', field2: 'test5', field3: 'test6' } + ], + 1: [], + 2: [{ id: 3, parentId: 2, field1: 'test7', field2: 'test8', field3: 'test9' }] + }; - if(parentIds) { - for(let i = 0; i < parentIds.length; i++) { - result.push.apply(result, data[parentIds[i]]); + this.setupTreeList({ + dataSource: { + load: function(e) { + const d = $.Deferred(); + const result = []; + const parentIds = e.parentIds; + + if(parentIds) { + for(let i = 0; i < parentIds.length; i++) { + result.push.apply(result, data[parentIds[i]]); + } } - } - loadOptions.push(e); + loadOptions.push(e); - return d.resolve(result); + return d.resolve(result); + } } - } - }); + }); - // act - this.expandRow(1); + // act + this.expandRow(1); - // assert - items = this.dataController.items(); - assert.equal(items.length, 2, 'item count'); - assert.deepEqual(loadOptions[1].parentIds, [1], 'parentIds'); + // assert + items = this.dataController.items(); + assert.equal(items.length, 2, 'item count'); + assert.deepEqual(loadOptions[1].parentIds, [1], 'parentIds'); - // act - this.expandRow(2); + // act + this.expandRow(2); - // assert - items = this.dataController.items(); - assert.equal(items.length, 3, 'item count'); - assert.deepEqual(loadOptions[2].parentIds, [2], 'parentIds'); + // assert + items = this.dataController.items(); + assert.equal(items.length, 3, 'item count'); + assert.deepEqual(loadOptions[2].parentIds, [2], 'parentIds'); + }); }); QUnit.module('Filtering', { beforeEach: function() { @@ -2618,137 +2621,137 @@ QUnit.module('Filtering', { beforeEach: function() { options: options }); }; -}, afterEach: teardownModule }); +}, afterEach: teardownModule }, () => { -QUnit.test('Search should work correctly with hierarchical structure', function(assert) { + QUnit.test('Search should work correctly with hierarchical structure', function(assert) { // arrange - let items; + let items; - // act - this.setupTreeList({ - itemsExpr: 'items', - dataStructure: 'tree', - dataSource: [ - { name: 'Alex', items: [{ name: 'Bob' }] }, - { name: 'Tom', items: [{ name: 'John' }] } - ], - searchPanel: { - text: 'Bob' - } - }); + // act + this.setupTreeList({ + itemsExpr: 'items', + dataStructure: 'tree', + dataSource: [ + { name: 'Alex', items: [{ name: 'Bob' }] }, + { name: 'Tom', items: [{ name: 'John' }] } + ], + searchPanel: { + text: 'Bob' + } + }); - // assert - items = this.dataController.items(); - assert.strictEqual(items.length, 2, 'item count'); - assert.deepEqual(items[0].data, { 'id': 1, 'name': 'Alex', 'parentId': 0 }, 'first item'); - assert.deepEqual(items[1].data, { 'id': 2, 'name': 'Bob', 'parentId': 1 }, 'second item'); -}); + // assert + items = this.dataController.items(); + assert.strictEqual(items.length, 2, 'item count'); + assert.deepEqual(items[0].data, { 'id': 1, 'name': 'Alex', 'parentId': 0 }, 'first item'); + assert.deepEqual(items[1].data, { 'id': 2, 'name': 'Bob', 'parentId': 1 }, 'second item'); + }); -QUnit.test('Search when filterMode is \'fullBranch\'', function(assert) { + QUnit.test('Search when filterMode is \'fullBranch\'', function(assert) { // arrange, act - this.setupTreeList({ - dataSource: [ + this.setupTreeList({ + dataSource: [ + { id: 1, parentId: 0, test: 'Test 1' }, + { id: 2, parentId: 0, test: 'Test 2' }, + { id: 3, parentId: 2, test: 'Test 3' }, + { id: 4, parentId: 3, test: 'Test 4' }, + { id: 5, parentId: 3, test: 'Test 5' }, + { id: 6, parentId: 0, test: 'Test 6' } + ], + keyExpr: 'id', + parentIdExpr: 'parentId', + filterMode: 'fullBranch', + expandNodesOnFiltering: true, + searchPanel: { + text: 'Test 3' + } + }); + + // assert + let items = this.dataController.items(); + assert.strictEqual(items.length, 2, 'item count'); + assert.deepEqual(items[0].data, { id: 2, parentId: 0, test: 'Test 2' }, 'first item'); + assert.deepEqual(items[0].level, 0, 'level of the first item'); + assert.deepEqual(items[1].data, { id: 3, parentId: 2, test: 'Test 3' }, 'second item'); + assert.deepEqual(items[1].level, 1, 'level of the second item'); + assert.deepEqual(items[1].level, 1, 'level of the second item'); + assert.ok(items[1].node.hasChildren, 'second item has children'); + + // act + this.expandRow(3); + + // assert + items = this.dataController.items(); + assert.strictEqual(items.length, 4, 'item count'); + assert.deepEqual(this.option('expandedRowKeys'), [2, 3], 'expandedRowKyes'); + assert.deepEqual(items[0].data, { id: 2, parentId: 0, test: 'Test 2' }, 'first item'); + assert.deepEqual(items[0].level, 0, 'level of the first item'); + assert.deepEqual(items[1].data, { id: 3, parentId: 2, test: 'Test 3' }, 'second item'); + assert.deepEqual(items[1].level, 1, 'level of the second item'); + assert.deepEqual(items[1].level, 1, 'level of the second item'); + assert.ok(items[1].node.hasChildren, 'second item has children'); + assert.deepEqual(items[2].data, { id: 4, parentId: 3, test: 'Test 4' }, 'third item'); + assert.deepEqual(items[2].level, 2, 'level of the third item'); + assert.deepEqual(items[3].data, { id: 5, parentId: 3, test: 'Test 5' }, 'fourth item'); + assert.deepEqual(items[3].level, 2, 'level of the fourth item'); + }); + + QUnit.test('Search with filterMode is \'fullBranch\' when remote data source', function(assert) { + // arrange + const store = new ArrayStore([ { id: 1, parentId: 0, test: 'Test 1' }, { id: 2, parentId: 0, test: 'Test 2' }, { id: 3, parentId: 2, test: 'Test 3' }, { id: 4, parentId: 3, test: 'Test 4' }, { id: 5, parentId: 3, test: 'Test 5' }, { id: 6, parentId: 0, test: 'Test 6' } - ], - keyExpr: 'id', - parentIdExpr: 'parentId', - filterMode: 'fullBranch', - expandNodesOnFiltering: true, - searchPanel: { - text: 'Test 3' - } - }); - - // assert - let items = this.dataController.items(); - assert.strictEqual(items.length, 2, 'item count'); - assert.deepEqual(items[0].data, { id: 2, parentId: 0, test: 'Test 2' }, 'first item'); - assert.deepEqual(items[0].level, 0, 'level of the first item'); - assert.deepEqual(items[1].data, { id: 3, parentId: 2, test: 'Test 3' }, 'second item'); - assert.deepEqual(items[1].level, 1, 'level of the second item'); - assert.deepEqual(items[1].level, 1, 'level of the second item'); - assert.ok(items[1].node.hasChildren, 'second item has children'); - - // act - this.expandRow(3); - - // assert - items = this.dataController.items(); - assert.strictEqual(items.length, 4, 'item count'); - assert.deepEqual(this.option('expandedRowKeys'), [2, 3], 'expandedRowKyes'); - assert.deepEqual(items[0].data, { id: 2, parentId: 0, test: 'Test 2' }, 'first item'); - assert.deepEqual(items[0].level, 0, 'level of the first item'); - assert.deepEqual(items[1].data, { id: 3, parentId: 2, test: 'Test 3' }, 'second item'); - assert.deepEqual(items[1].level, 1, 'level of the second item'); - assert.deepEqual(items[1].level, 1, 'level of the second item'); - assert.ok(items[1].node.hasChildren, 'second item has children'); - assert.deepEqual(items[2].data, { id: 4, parentId: 3, test: 'Test 4' }, 'third item'); - assert.deepEqual(items[2].level, 2, 'level of the third item'); - assert.deepEqual(items[3].data, { id: 5, parentId: 3, test: 'Test 5' }, 'fourth item'); - assert.deepEqual(items[3].level, 2, 'level of the fourth item'); -}); - -QUnit.test('Search with filterMode is \'fullBranch\' when remote data source', function(assert) { - // arrange - const store = new ArrayStore([ - { id: 1, parentId: 0, test: 'Test 1' }, - { id: 2, parentId: 0, test: 'Test 2' }, - { id: 3, parentId: 2, test: 'Test 3' }, - { id: 4, parentId: 3, test: 'Test 4' }, - { id: 5, parentId: 3, test: 'Test 5' }, - { id: 6, parentId: 0, test: 'Test 6' } - ]); + ]); - // act - this.setupTreeList({ - dataSource: { - load: (loadOptions) => store.load(loadOptions) - }, - remoteOperations: true, - keyExpr: 'id', - parentIdExpr: 'parentId', - filterMode: 'fullBranch', - expandNodesOnFiltering: true, - searchPanel: { - text: 'Test 3' - } - }); + // act + this.setupTreeList({ + dataSource: { + load: (loadOptions) => store.load(loadOptions) + }, + remoteOperations: true, + keyExpr: 'id', + parentIdExpr: 'parentId', + filterMode: 'fullBranch', + expandNodesOnFiltering: true, + searchPanel: { + text: 'Test 3' + } + }); - // assert - let items = this.dataController.items(); - assert.strictEqual(items.length, 2, 'item count'); - assert.deepEqual(items[0].data, { id: 2, parentId: 0, test: 'Test 2' }, 'first item'); - assert.deepEqual(items[0].level, 0, 'level of the first item'); - assert.deepEqual(items[1].data, { id: 3, parentId: 2, test: 'Test 3' }, 'second item'); - assert.deepEqual(items[1].level, 1, 'level of the second item'); - assert.deepEqual(items[1].level, 1, 'level of the second item'); - assert.ok(items[1].node.hasChildren, 'second item has children'); + // assert + let items = this.dataController.items(); + assert.strictEqual(items.length, 2, 'item count'); + assert.deepEqual(items[0].data, { id: 2, parentId: 0, test: 'Test 2' }, 'first item'); + assert.deepEqual(items[0].level, 0, 'level of the first item'); + assert.deepEqual(items[1].data, { id: 3, parentId: 2, test: 'Test 3' }, 'second item'); + assert.deepEqual(items[1].level, 1, 'level of the second item'); + assert.deepEqual(items[1].level, 1, 'level of the second item'); + assert.ok(items[1].node.hasChildren, 'second item has children'); - // act - this.expandRow(3); - - // assert - items = this.dataController.items(); - assert.strictEqual(items.length, 4, 'item count'); - assert.deepEqual(this.option('expandedRowKeys'), [2, 3], 'expandedRowKyes'); - assert.deepEqual(items[0].data, { id: 2, parentId: 0, test: 'Test 2' }, 'first item'); - assert.deepEqual(items[0].level, 0, 'level of the first item'); - assert.deepEqual(items[1].data, { id: 3, parentId: 2, test: 'Test 3' }, 'second item'); - assert.deepEqual(items[1].level, 1, 'level of the second item'); - assert.deepEqual(items[1].level, 1, 'level of the second item'); - assert.ok(items[1].node.hasChildren, 'second item has children'); - assert.deepEqual(items[2].data, { id: 4, parentId: 3, test: 'Test 4' }, 'third item'); - assert.deepEqual(items[2].level, 2, 'level of the third item'); - assert.deepEqual(items[3].data, { id: 5, parentId: 3, test: 'Test 5' }, 'fourth item'); - assert.deepEqual(items[3].level, 2, 'level of the fourth item'); -}); + // act + this.expandRow(3); -QUnit.test('FullBranch mode. Expansion of the filtered node should work when expandNodesOnFiltering is false', function(assert) { + // assert + items = this.dataController.items(); + assert.strictEqual(items.length, 4, 'item count'); + assert.deepEqual(this.option('expandedRowKeys'), [2, 3], 'expandedRowKyes'); + assert.deepEqual(items[0].data, { id: 2, parentId: 0, test: 'Test 2' }, 'first item'); + assert.deepEqual(items[0].level, 0, 'level of the first item'); + assert.deepEqual(items[1].data, { id: 3, parentId: 2, test: 'Test 3' }, 'second item'); + assert.deepEqual(items[1].level, 1, 'level of the second item'); + assert.deepEqual(items[1].level, 1, 'level of the second item'); + assert.ok(items[1].node.hasChildren, 'second item has children'); + assert.deepEqual(items[2].data, { id: 4, parentId: 3, test: 'Test 4' }, 'third item'); + assert.deepEqual(items[2].level, 2, 'level of the third item'); + assert.deepEqual(items[3].data, { id: 5, parentId: 3, test: 'Test 5' }, 'fourth item'); + assert.deepEqual(items[3].level, 2, 'level of the fourth item'); + }); + + QUnit.test('FullBranch mode. Expansion of the filtered node should work when expandNodesOnFiltering is false', function(assert) { // arrange /* eslint-disable */ const data = new ArrayStore([ @@ -2759,39 +2762,39 @@ QUnit.test('FullBranch mode. Expansion of the filtered node should work when exp ]); /* eslint-enable */ - this.setupTreeList({ - dataSource: data, - keyExpr: 'id', - parentIdExpr: 'parentId', - filterMode: 'fullBranch', - expandNodesOnFiltering: false, - searchPanel: { - text: 'Test 2' - } - }); + this.setupTreeList({ + dataSource: data, + keyExpr: 'id', + parentIdExpr: 'parentId', + filterMode: 'fullBranch', + expandNodesOnFiltering: false, + searchPanel: { + text: 'Test 2' + } + }); - // act - this.expandRow(1); + // act + this.expandRow(1); - // assert - let items = this.dataController.items(); - assert.strictEqual(items.length, 2, 'item count'); - assert.deepEqual(items[0].data, { id: 1, parentId: 0, test: 'Test 1' }, 'first item'); - assert.deepEqual(items[1].data, { id: 2, parentId: 1, test: 'Test 2' }, 'second item'); + // assert + let items = this.dataController.items(); + assert.strictEqual(items.length, 2, 'item count'); + assert.deepEqual(items[0].data, { id: 1, parentId: 0, test: 'Test 1' }, 'first item'); + assert.deepEqual(items[1].data, { id: 2, parentId: 1, test: 'Test 2' }, 'second item'); - // act - this.expandRow(2); - - // assert - items = this.dataController.items(); - assert.strictEqual(items.length, 4, 'item count'); - assert.deepEqual(items[0].data, { id: 1, parentId: 0, test: 'Test 1' }, 'first item'); - assert.deepEqual(items[1].data, { id: 2, parentId: 1, test: 'Test 2' }, 'second item'); - assert.deepEqual(items[2].data, { id: 3, parentId: 2, test: 'Test 3' }, 'third item'); - assert.deepEqual(items[3].data, { id: 4, parentId: 2, test: 'Test 4' }, 'fourth item'); -}); + // act + this.expandRow(2); + + // assert + items = this.dataController.items(); + assert.strictEqual(items.length, 4, 'item count'); + assert.deepEqual(items[0].data, { id: 1, parentId: 0, test: 'Test 1' }, 'first item'); + assert.deepEqual(items[1].data, { id: 2, parentId: 1, test: 'Test 2' }, 'second item'); + assert.deepEqual(items[2].data, { id: 3, parentId: 2, test: 'Test 3' }, 'third item'); + assert.deepEqual(items[3].data, { id: 4, parentId: 2, test: 'Test 4' }, 'fourth item'); + }); -QUnit.test('FullBranch mode. The order of nodes should not be changed after expanding nodes when expandNodesOnFiltering is false', function(assert) { + QUnit.test('FullBranch mode. The order of nodes should not be changed after expanding nodes when expandNodesOnFiltering is false', function(assert) { // arrange /* eslint-disable */ const store = new ArrayStore([ @@ -2803,43 +2806,43 @@ QUnit.test('FullBranch mode. The order of nodes should not be changed after expa ]); /* eslint-enable */ - this.setupTreeList({ - dataSource: { - load: (loadOptions) => store.load(loadOptions) - }, - remoteOperations: true, - keyExpr: 'id', - parentIdExpr: 'parentId', - filterMode: 'fullBranch', - expandNodesOnFiltering: false, - searchPanel: { - text: 'Test 3' - } - }); + this.setupTreeList({ + dataSource: { + load: (loadOptions) => store.load(loadOptions) + }, + remoteOperations: true, + keyExpr: 'id', + parentIdExpr: 'parentId', + filterMode: 'fullBranch', + expandNodesOnFiltering: false, + searchPanel: { + text: 'Test 3' + } + }); - // act - this.expandRow(1); + // act + this.expandRow(1); - // assert - let items = this.dataController.items(); - assert.strictEqual(items.length, 3, 'item count'); - assert.deepEqual(items[0].data, { id: 1, parentId: 0, test: 'Test 1' }, 'first item'); - assert.deepEqual(items[1].data, { id: 4, parentId: 1, test: 'Test 3' }, 'second item'); - assert.deepEqual(items[2].data, { id: 2, parentId: 1, test: 'Test 2' }, 'third item'); + // assert + let items = this.dataController.items(); + assert.strictEqual(items.length, 3, 'item count'); + assert.deepEqual(items[0].data, { id: 1, parentId: 0, test: 'Test 1' }, 'first item'); + assert.deepEqual(items[1].data, { id: 4, parentId: 1, test: 'Test 3' }, 'second item'); + assert.deepEqual(items[2].data, { id: 2, parentId: 1, test: 'Test 2' }, 'third item'); - // act - this.expandRow(4); - - // assert - items = this.dataController.items(); - assert.strictEqual(items.length, 4, 'item count'); - assert.deepEqual(items[0].data, { id: 1, parentId: 0, test: 'Test 1' }, 'first item'); - assert.deepEqual(items[1].data, { id: 4, parentId: 1, test: 'Test 3' }, 'second item'); - assert.deepEqual(items[2].data, { id: 5, parentId: 4, test: 'Test 4' }, 'third item'); - assert.deepEqual(items[3].data, { id: 2, parentId: 1, test: 'Test 2' }, 'fourth item'); -}); + // act + this.expandRow(4); + + // assert + items = this.dataController.items(); + assert.strictEqual(items.length, 4, 'item count'); + assert.deepEqual(items[0].data, { id: 1, parentId: 0, test: 'Test 1' }, 'first item'); + assert.deepEqual(items[1].data, { id: 4, parentId: 1, test: 'Test 3' }, 'second item'); + assert.deepEqual(items[2].data, { id: 5, parentId: 4, test: 'Test 4' }, 'third item'); + assert.deepEqual(items[3].data, { id: 2, parentId: 1, test: 'Test 2' }, 'fourth item'); + }); -QUnit.test('FullBranch mode. Children of filtered nodes should not be collapsed after sorting', function(assert) { + QUnit.test('FullBranch mode. Children of filtered nodes should not be collapsed after sorting', function(assert) { // arrange /* eslint-disable */ const store = new ArrayStore([ @@ -2850,45 +2853,45 @@ QUnit.test('FullBranch mode. Children of filtered nodes should not be collapsed ]); /* eslint-enable */ - this.setupTreeList({ - dataSource: { - load: (loadOptions) => store.load(loadOptions) - }, - keyExpr: 'id', - parentIdExpr: 'parentId', - hasItemsExpr: 'hasChildren', - filterMode: 'fullBranch', - expandNodesOnFiltering: true, - remoteOperations: true, - searchPanel: { - text: 'Test 2' - } - }); + this.setupTreeList({ + dataSource: { + load: (loadOptions) => store.load(loadOptions) + }, + keyExpr: 'id', + parentIdExpr: 'parentId', + hasItemsExpr: 'hasChildren', + filterMode: 'fullBranch', + expandNodesOnFiltering: true, + remoteOperations: true, + searchPanel: { + text: 'Test 2' + } + }); - this.expandRow(2); + this.expandRow(2); - // assert - let items = this.dataController.items(); - assert.strictEqual(items.length, 4, 'item count'); + // assert + let items = this.dataController.items(); + assert.strictEqual(items.length, 4, 'item count'); - // act - this.columnOption('test', 'sortOrder', 'asc'); - - // assert - items = this.dataController.items(); - assert.strictEqual(items.length, 4, 'item count'); - assert.strictEqual(items[0].node.hasChildren, true, 'hasChildren of the first node'); - assert.strictEqual(items[1].node.hasChildren, true, 'hasChildren of the second node'); - assert.strictEqual(items[2].node.hasChildren, true, 'hasChildren of the third node'); - assert.strictEqual(items[3].node.hasChildren, false, 'hasChildren of the fourth node'); -}); + // act + this.columnOption('test', 'sortOrder', 'asc'); + + // assert + items = this.dataController.items(); + assert.strictEqual(items.length, 4, 'item count'); + assert.strictEqual(items[0].node.hasChildren, true, 'hasChildren of the first node'); + assert.strictEqual(items[1].node.hasChildren, true, 'hasChildren of the second node'); + assert.strictEqual(items[2].node.hasChildren, true, 'hasChildren of the third node'); + assert.strictEqual(items[3].node.hasChildren, false, 'hasChildren of the fourth node'); + }); -// T724827 -QUnit.test('The filter query should be correct after resetting the filter value', function(assert) { + // T724827 + QUnit.test('The filter query should be correct after resetting the filter value', function(assert) { // arrange - let items; - let filter; - /* eslint-disable */ + let items; + let filter; + /* eslint-disable */ const store = new ArrayStore([ { id: 1, parentId: 0, name: "Name 3", age: 19 }, { id: 4, parentId: 1, name: "Name 6", age: 16 }, @@ -2900,36 +2903,36 @@ QUnit.test('The filter query should be correct after resetting the filter value' ]); /* eslint-enable */ - this.setupTreeList({ - remoteOperations: { - filtering: true - }, - dataSource: { - load: function(loadOptions) { - filter = filter || loadOptions.filter; - return store.load(loadOptions); - } - }, - columns: [{ dataField: 'name', dataType: 'string' }, { dataField: 'age', dataType: 'number', filterValue: 18 }] - }); + this.setupTreeList({ + remoteOperations: { + filtering: true + }, + dataSource: { + load: function(loadOptions) { + filter = filter || loadOptions.filter; + return store.load(loadOptions); + } + }, + columns: [{ dataField: 'name', dataType: 'string' }, { dataField: 'age', dataType: 'number', filterValue: 18 }] + }); - // assert - items = this.dataController.items(); - assert.strictEqual(items.length, 4, 'item count'); - assert.deepEqual(filter, ['age', '=', 18], 'filter'); + // assert + items = this.dataController.items(); + assert.strictEqual(items.length, 4, 'item count'); + assert.deepEqual(filter, ['age', '=', 18], 'filter'); - // act - filter = null; - this.columnOption('age', 'filterValue', undefined); + // act + filter = null; + this.columnOption('age', 'filterValue', undefined); - // assert - items = this.dataController.items(); - assert.strictEqual(items.length, 3, 'item count'); - assert.deepEqual(filter, ['parentId', '=', 0], 'filter'); -}); + // assert + items = this.dataController.items(); + assert.strictEqual(items.length, 3, 'item count'); + assert.deepEqual(filter, ['parentId', '=', 0], 'filter'); + }); -// T866113 -QUnit.test('Filtering should work correctly when data is returned as an object', function(assert) { + // T866113 + QUnit.test('Filtering should work correctly when data is returned as an object', function(assert) { // arrange /* eslint-disable */ const store = new ArrayStore([ @@ -2943,34 +2946,36 @@ QUnit.test('Filtering should work correctly when data is returned as an object', ]); /* eslint-enable */ - this.setupTreeList({ - remoteOperations: { - filtering: true - }, - dataSource: { - load: function(loadOptions) { - const d = $.Deferred(); - - store.load(loadOptions).done((items) => { - d.resolve({ - data: items, - totalCount: 7 + this.setupTreeList({ + remoteOperations: { + filtering: true + }, + dataSource: { + load: function(loadOptions) { + const d = $.Deferred(); + + store.load(loadOptions).done((items) => { + d.resolve({ + data: items, + totalCount: 7 + }); }); - }); - return d.promise(); - } - }, - columns: [{ dataField: 'name', dataType: 'string' }, { dataField: 'age', dataType: 'number' }] - }); + return d.promise(); + } + }, + columns: [{ dataField: 'name', dataType: 'string' }, { dataField: 'age', dataType: 'number' }] + }); - // act - this.columnOption('name', 'filterValue', 'Name 7'); - - // assert - const rows = this.getVisibleRows(); - assert.strictEqual(rows.length, 3, 'row count'); - assert.strictEqual(rows[0].key, 1, 'first row'); - assert.strictEqual(rows[1].key, 6, 'second row'); - assert.strictEqual(rows[2].key, 7, 'third row'); + // act + this.columnOption('name', 'filterValue', 'Name 7'); + + // assert + const rows = this.getVisibleRows(); + assert.strictEqual(rows.length, 3, 'row count'); + assert.strictEqual(rows[0].key, 1, 'first row'); + assert.strictEqual(rows[1].key, 6, 'second row'); + assert.strictEqual(rows[2].key, 7, 'third row'); + }); }); + diff --git a/testing/tests/DevExpress.ui.widgets.treeList/editing.tests.js b/testing/tests/DevExpress.ui.widgets.treeList/editing.tests.js index a32d9c48329b..a3e3d268f800 100644 --- a/testing/tests/DevExpress.ui.widgets.treeList/editing.tests.js +++ b/testing/tests/DevExpress.ui.widgets.treeList/editing.tests.js @@ -61,1192 +61,1194 @@ const teardownModule = function() { this.clock.restore(); }; -QUnit.module('Editing', { beforeEach: setupModule, afterEach: teardownModule }); +QUnit.module('Editing', { beforeEach: setupModule, afterEach: teardownModule }, () => { -QUnit.test('Edit row', function(assert) { + QUnit.test('Edit row', function(assert) { // arrange - let $rowElement; - const $testElement = $('#treeList'); + let $rowElement; + const $testElement = $('#treeList'); - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.editRow(0); + // act + this.editRow(0); - // assert - $rowElement = $testElement.find('tbody > tr').first(); - assert.ok($rowElement.hasClass('dx-edit-row'), 'edit row'); - assert.equal($rowElement.find('.dx-texteditor').length, 3, 'count editor'); - assert.ok(!$rowElement.children().first().find('.dx-treelist-icon-container').length, 'hasn\'t expand icon'); -}); + // assert + $rowElement = $testElement.find('tbody > tr').first(); + assert.ok($rowElement.hasClass('dx-edit-row'), 'edit row'); + assert.equal($rowElement.find('.dx-texteditor').length, 3, 'count editor'); + assert.ok(!$rowElement.children().first().find('.dx-treelist-icon-container').length, 'hasn\'t expand icon'); + }); -QUnit.test('Edit cell when edit mode is \'batch\'', function(assert) { + QUnit.test('Edit cell when edit mode is \'batch\'', function(assert) { // arrange - let $cellElement; - let $rowElement; - const $testElement = $('#treeList'); + let $cellElement; + let $rowElement; + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'batch' - }; + this.options.editing = { + mode: 'batch' + }; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.editCell(0, 0); + // act + this.editCell(0, 0); - // assert - $rowElement = $testElement.find('tbody > tr').first(); - $cellElement = $rowElement.children().first(); - assert.equal($rowElement.find('.dx-texteditor').length, 1, 'count editor'); - assert.ok($cellElement.hasClass('dx-editor-cell'), 'edit cell'); - assert.ok(!$cellElement.find('.dx-treelist-icon-container').length, 'hasn\'t expand icon'); - assert.ok(!$cellElement.find('.dx-datagrid-text-content').length, 'hasn\'t \'dx-datagrid-text-content\' container'); -}); + // assert + $rowElement = $testElement.find('tbody > tr').first(); + $cellElement = $rowElement.children().first(); + assert.equal($rowElement.find('.dx-texteditor').length, 1, 'count editor'); + assert.ok($cellElement.hasClass('dx-editor-cell'), 'edit cell'); + assert.ok(!$cellElement.find('.dx-treelist-icon-container').length, 'hasn\'t expand icon'); + assert.ok(!$cellElement.find('.dx-datagrid-text-content').length, 'hasn\'t \'dx-datagrid-text-content\' container'); + }); -QUnit.test('Edit cell when edit mode is \'cell\'', function(assert) { + QUnit.test('Edit cell when edit mode is \'cell\'', function(assert) { // arrange - let $cellElement; - let $rowElement; - const $testElement = $('#treeList'); + let $cellElement; + let $rowElement; + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'cell' - }; + this.options.editing = { + mode: 'cell' + }; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.editCell(0, 0); + // act + this.editCell(0, 0); - // assert - $rowElement = $testElement.find('tbody > tr').first(); - $cellElement = $testElement.find('tbody > tr').first().children().first(); - assert.equal($rowElement.find('.dx-texteditor').length, 1, 'count editor'); - assert.ok($cellElement.hasClass('dx-editor-cell'), 'edit cell'); - assert.ok(!$cellElement.find('.dx-treelist-icon-container').length, 'hasn\'t expand icon'); -}); + // assert + $rowElement = $testElement.find('tbody > tr').first(); + $cellElement = $testElement.find('tbody > tr').first().children().first(); + assert.equal($rowElement.find('.dx-texteditor').length, 1, 'count editor'); + assert.ok($cellElement.hasClass('dx-editor-cell'), 'edit cell'); + assert.ok(!$cellElement.find('.dx-treelist-icon-container').length, 'hasn\'t expand icon'); + }); -QUnit.test('Edit form', function(assert) { + QUnit.test('Edit form', function(assert) { // arrange - let $rowElement; - const $testElement = $('#treeList'); + let $rowElement; + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'form' - }; + this.options.editing = { + mode: 'form' + }; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.editRow(0); + // act + this.editRow(0); - // assert - $rowElement = $testElement.find('tbody > tr').first(); - assert.ok($rowElement.hasClass('dx-treelist-edit-form'), 'edit form'); - assert.equal($rowElement.find('.dx-texteditor').length, 3, 'count editor'); - assert.ok(!$rowElement.find('.dx-treelist-icon-container').length, 'hasn\'t expand icon'); -}); + // assert + $rowElement = $testElement.find('tbody > tr').first(); + assert.ok($rowElement.hasClass('dx-treelist-edit-form'), 'edit form'); + assert.equal($rowElement.find('.dx-texteditor').length, 3, 'count editor'); + assert.ok(!$rowElement.find('.dx-treelist-icon-container').length, 'hasn\'t expand icon'); + }); -QUnit.test('Edit popup', function(assert) { + QUnit.test('Edit popup', function(assert) { // arrange - const $testElement = $('#treeList'); - - this.options.editing = { - mode: 'popup' - }; + const $testElement = $('#treeList'); - this.setupTreeList(); - this.editingController.component.$element = function() { - return $('#treeList'); - }; + this.options.editing = { + mode: 'popup' + }; - this.rowsView.render($testElement); + this.setupTreeList(); + this.editingController.component.$element = function() { + return $('#treeList'); + }; - // act - this.editRow(0); + this.rowsView.render($testElement); - // assert - const $editPopup = $testElement.find('.dx-treelist-edit-popup'); - const editPopup = $editPopup.dxPopup('instance'); - const $editPopupContent = editPopup.$content(); + // act + this.editRow(0); - assert.equal($editPopup.length, 1, 'edit popup was rendered'); - assert.ok(editPopup.option('visible'), 'Edit popup is visible'); - assert.equal($editPopupContent.find('.dx-texteditor').length, 3, 'editors was rendered'); - assert.ok(!$editPopupContent.find('.dx-treelist-icon-container').length, 'hasn\'t expand icon'); -}); + // assert + const $editPopup = $testElement.find('.dx-treelist-edit-popup'); + const editPopup = $editPopup.dxPopup('instance'); + const $editPopupContent = editPopup.$content(); + + assert.equal($editPopup.length, 1, 'edit popup was rendered'); + assert.ok(editPopup.option('visible'), 'Edit popup is visible'); + assert.equal($editPopupContent.find('.dx-texteditor').length, 3, 'editors was rendered'); + assert.ok(!$editPopupContent.find('.dx-treelist-icon-container').length, 'hasn\'t expand icon'); + }); -QUnit.test('Insert row', function(assert) { + QUnit.test('Insert row', function(assert) { // arrange - let items; - let $rowElements; - const $testElement = $('#treeList'); - - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.insertRow(); - - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.equal($rowElements.length, 2, 'count data row'); - assert.ok($rowElements.first().hasClass('dx-row-inserted'), 'insert row'); - - // act - $testElement.find('tbody > tr').first().find('input').first().val(666); - $testElement.find('tbody > tr').first().find('input').first().trigger('change'); - this.saveEditData(); - - // assert - $rowElements = $testElement.find('tbody > .dx-data-row:not(.dx-row-inserted)'); - assert.equal($rowElements.length, 2, 'count data row'); - - items = this.dataController.items(); - assert.equal(items.length, 2, 'count item'); - assert.equal(items[1].rowType, 'data', 'rowType of second item'); - assert.deepEqual(items[1].values, ['666', undefined, undefined, null], 'values of second item'); - assert.equal(items[1].node.children.length, 0, 'count children of second item'); - assert.equal(items[1].level, 0, 'level of second item'); - assert.notOk(items[1].node.parent.key, 'second item hasn\'t parentKey'); -}); - -QUnit.test('Edit batch - add links should be rendered in rows when allowAdding is true', function(assert) { - // arrange, act - const $testElement = $('#treeList'); + let items; + let $rowElements; + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'batch', - allowAdding: true, - texts: { - addRowToNode: 'Add' - } - }; + this.setupTreeList(); + this.rowsView.render($testElement); - this.setupTreeList(); - this.rowsView.render($testElement); + // act + this.insertRow(); - // assert - const $addLinks = $testElement.find('.dx-command-edit .dx-link-add'); - assert.equal($addLinks.length, 1, 'link add is rendered'); - assert.equal($addLinks.text(), 'Add', 'Add link text'); -}); + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + assert.equal($rowElements.length, 2, 'count data row'); + assert.ok($rowElements.first().hasClass('dx-row-inserted'), 'insert row'); -QUnit.test('Add row to child should call addRow method with parentId', function(assert) { - // arrange - const $testElement = $('#treeList'); + // act + $testElement.find('tbody > tr').first().find('input').first().val(666); + $testElement.find('tbody > tr').first().find('input').first().trigger('change'); + this.saveEditData(); - this.options.editing.allowAdding = true; - this.options.editing.texts = { addRowToNode: 'Add' }; + // assert + $rowElements = $testElement.find('tbody > .dx-data-row:not(.dx-row-inserted)'); + assert.equal($rowElements.length, 2, 'count data row'); + + items = this.dataController.items(); + assert.equal(items.length, 2, 'count item'); + assert.equal(items[1].rowType, 'data', 'rowType of second item'); + assert.deepEqual(items[1].values, ['666', undefined, undefined, null], 'values of second item'); + assert.equal(items[1].node.children.length, 0, 'count children of second item'); + assert.equal(items[1].level, 0, 'level of second item'); + assert.notOk(items[1].node.parent.key, 'second item hasn\'t parentKey'); + }); - this.setupTreeList(); - this.rowsView.render($testElement); + QUnit.test('Edit batch - add links should be rendered in rows when allowAdding is true', function(assert) { + // arrange, act + const $testElement = $('#treeList'); - this.editingController.addRow = sinon.spy(); + this.options.editing = { + mode: 'batch', + allowAdding: true, + texts: { + addRowToNode: 'Add' + } + }; - // act - $testElement.find('.dx-command-edit .dx-link-add').trigger('click'); - this.clock.tick(); + this.setupTreeList(); + this.rowsView.render($testElement); - // assert - assert.ok(this.editingController.addRow.calledOnce, 'addRow is called'); - assert.deepEqual(this.editingController.addRow.args[0], [1], 'addRow arg is row key'); -}); + // assert + const $addLinks = $testElement.find('.dx-command-edit .dx-link-add'); + assert.equal($addLinks.length, 1, 'link add is rendered'); + assert.equal($addLinks.text(), 'Add', 'Add link text'); + }); -QUnit.test('AddRow method should expand row and add item after parent', function(assert) { + QUnit.test('Add row to child should call addRow method with parentId', function(assert) { // arrange - const $testElement = $('#treeList'); - - this.options.editing.allowAdding = true; - this.options.editing.texts = { addRowToNode: 'Add' }; - - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.addRow(1); - this.clock.tick(); - - // assert - const rows = this.getVisibleRows(); - - assert.strictEqual(rows.length, 3, 'three rows are rendered'); - assert.strictEqual(rows[0].key, 1, 'row 0'); - assert.strictEqual(rows[0].isExpanded, true, 'row 0 is expanded'); - assert.deepEqual(rows[1].key.parentKey, 1, 'row 1 key parentKey'); - assert.deepEqual(rows[1].key.rowIndex, 1, 'row 1 key rowIndex'); - assert.deepEqual(rows[1].isNewRow, true, 'row 1 is inserted'); - assert.deepEqual(rows[1].data, { parentId: 1 }, 'row 1 data should contains parentId'); - assert.strictEqual(rows[2].key, 2, 'row 2 key'); - assert.strictEqual(rows[2].node.parent.key, 1, 'row 2 node parent'); -}); + const $testElement = $('#treeList'); -QUnit.test('AddRow method should return Deferred with collapsed parent', function(assert) { - // arrange - this.options.editing.allowAdding = true; + this.options.editing.allowAdding = true; + this.options.editing.texts = { addRowToNode: 'Add' }; - this.setupTreeList(); - this.rowsView.render($('#treeList')); - this.clock.tick(); + this.setupTreeList(); + this.rowsView.render($testElement); - // assert - assert.equal(this.getVisibleRows().length, 1, 'one visible row'); + this.editingController.addRow = sinon.spy(); - // act - let doneExecuteCount = 0; - this.addRow(1).done(() => { - doneExecuteCount++; - }); - this.clock.tick(); + // act + $testElement.find('.dx-command-edit .dx-link-add').trigger('click'); + this.clock.tick(); - assert.equal(doneExecuteCount, 1, 'done was executed'); - assert.equal(this.getVisibleRows().length, 3, 'parent was expanded and one more row was added'); -}); + // assert + assert.ok(this.editingController.addRow.calledOnce, 'addRow is called'); + assert.deepEqual(this.editingController.addRow.args[0], [1], 'addRow arg is row key'); + }); -QUnit.test('Sequential adding of a row after adding the previous using Deferred (T844118)', function(assert) { + QUnit.test('AddRow method should expand row and add item after parent', function(assert) { // arrange - const initNewRowCalls = []; - - this.options.editing.allowAdding = true; - this.options.onInitNewRow = (e) => { - initNewRowCalls.push(e.data.parentId); - }; - - this.setupTreeList(); - this.rowsView.render($('#treeList')); - this.clock.tick(); + const $testElement = $('#treeList'); - // assert - assert.equal(this.getVisibleRows().length, 1, '1 visible row'); + this.options.editing.allowAdding = true; + this.options.editing.texts = { addRowToNode: 'Add' }; - // act - const addRowAfterDeferredResolve = (parentIds, index) => { - const parentId = parentIds[index]; + this.setupTreeList(); + this.rowsView.render($testElement); - if(parentId !== undefined) { - return this.addRow(parentId).done(() => addRowAfterDeferredResolve(parentIds, index + 1)); - } + // act + this.addRow(1); + this.clock.tick(); // assert - assert.deepEqual(parentIds, initNewRowCalls, 'for every added row sequentially calls onInitNewRow'); - - return $.Deferred().resolve(); - }; - - addRowAfterDeferredResolve([1, 2], 0); + const rows = this.getVisibleRows(); - this.clock.tick(); -}); + assert.strictEqual(rows.length, 3, 'three rows are rendered'); + assert.strictEqual(rows[0].key, 1, 'row 0'); + assert.strictEqual(rows[0].isExpanded, true, 'row 0 is expanded'); + assert.deepEqual(rows[1].key.parentKey, 1, 'row 1 key parentKey'); + assert.deepEqual(rows[1].key.rowIndex, 1, 'row 1 key rowIndex'); + assert.deepEqual(rows[1].isNewRow, true, 'row 1 is inserted'); + assert.deepEqual(rows[1].data, { parentId: 1 }, 'row 1 data should contains parentId'); + assert.strictEqual(rows[2].key, 2, 'row 2 key'); + assert.strictEqual(rows[2].node.parent.key, 1, 'row 2 node parent'); + }); -QUnit.test('AddRow method returns Deferred with using promise in onInitNewRow (T844118)', function(assert) { + QUnit.test('AddRow method should return Deferred with collapsed parent', function(assert) { // arrange - const deferred = $.Deferred(); - this.options.editing.allowAdding = true; - this.options.onInitNewRow = (e) => { - e.promise = deferred; - }; + this.options.editing.allowAdding = true; - this.setupTreeList(); - this.rowsView.render($('#treeList')); - this.clock.tick(); + this.setupTreeList(); + this.rowsView.render($('#treeList')); + this.clock.tick(); - // act - let isAddRowDone = false; - this.addRow(1).done(() => isAddRowDone = true); - this.clock.tick(); + // assert + assert.equal(this.getVisibleRows().length, 1, 'one visible row'); - // assert - assert.notOk(isAddRowDone, 'done method has not executed yet'); - deferred.resolve(); - assert.ok(isAddRowDone, 'done method has executed'); -}); + // act + let doneExecuteCount = 0; + this.addRow(1).done(() => { + doneExecuteCount++; + }); + this.clock.tick(); -// T553905 -QUnit.test('Add item in node without children (Angular)', function(assert) { - // arrange - const $testElement = $('#treeList'); - - this.options.editing.allowAdding = true; - this.options.expandedRowKeys = [1]; - this.options.loadingTimeout = 30; - - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.addRow(2); - this.dataController.optionChanged({ name: 'expandedRowKeys', value: [1, 2], previousValue: [1, 2] }); // simulate the call from ngDoCheck hook - this.clock.tick(30); - - // assert - const rows = this.getVisibleRows(); - assert.strictEqual(rows.length, 3, 'count row'); - assert.strictEqual(rows[0].key, 1, 'key of the first row'); - assert.strictEqual(rows[0].isExpanded, true, 'first row is expanded'); - assert.strictEqual(rows[1].key, 2, 'key of the second row'); - assert.strictEqual(rows[1].node.parent.key, 1, 'parent key of the second row'); - assert.deepEqual(rows[2].key.parentKey, 2, 'parent key of the third row'); - assert.deepEqual(rows[2].key.rowIndex, 2, 'rowIndex of the third row'); - assert.deepEqual(rows[2].isNewRow, true, 'third row is inserted'); - assert.deepEqual(rows[2].data, { parentId: 2 }, 'third row data should contain parentId'); -}); + assert.equal(doneExecuteCount, 1, 'done was executed'); + assert.equal(this.getVisibleRows().length, 3, 'parent was expanded and one more row was added'); + }); -QUnit.test('AddRow method witout parameters should add item at begin', function(assert) { + QUnit.test('Sequential adding of a row after adding the previous using Deferred (T844118)', function(assert) { // arrange - const $testElement = $('#treeList'); + const initNewRowCalls = []; - this.options.rootValue = 0; - this.options.editing.allowAdding = true; - this.options.editing.texts = { addRowToNode: 'Add' }; + this.options.editing.allowAdding = true; + this.options.onInitNewRow = (e) => { + initNewRowCalls.push(e.data.parentId); + }; + + this.setupTreeList(); + this.rowsView.render($('#treeList')); + this.clock.tick(); - this.setupTreeList(); - this.rowsView.render($testElement); + // assert + assert.equal(this.getVisibleRows().length, 1, '1 visible row'); - // act - this.addRow(); - this.clock.tick(); + // act + const addRowAfterDeferredResolve = (parentIds, index) => { + const parentId = parentIds[index]; - // assert - const rows = this.getVisibleRows(); + if(parentId !== undefined) { + return this.addRow(parentId).done(() => addRowAfterDeferredResolve(parentIds, index + 1)); + } - assert.strictEqual(rows.length, 2, 'rows count'); - assert.deepEqual(rows[0].key.parentKey, 0, 'row 0 key parentKey'); - assert.deepEqual(rows[0].key.rowIndex, 0, 'row 0 key rowIndex'); - assert.deepEqual(rows[0].isNewRow, true, 'row 0 is inserted'); - assert.deepEqual(rows[0].data, { parentId: 0 }, 'row 0 data should contains parentId'); - assert.strictEqual(rows[1].key, 1, 'row 1'); - assert.strictEqual(rows[1].isExpanded, false, 'row 1 is not expanded'); -}); + // assert + assert.deepEqual(parentIds, initNewRowCalls, 'for every added row sequentially calls onInitNewRow'); -QUnit.test('AddRow method witout parameters should add item at begin if rootValue is defined', function(assert) { - // arrange - const $testElement = $('#treeList'); - - this.options.rootValue = 0; - this.options.dataSource.store.data[0].parentId = 0; - this.options.editing.allowAdding = true; - this.options.editing.texts = { addRowToNode: 'Add' }; - - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.addRow(); - this.clock.tick(); - - // assert - const rows = this.getVisibleRows(); - - assert.strictEqual(rows.length, 2, 'rows count'); - assert.deepEqual(rows[0].key.parentKey, 0, 'row 0 key parentKey'); - assert.deepEqual(rows[0].key.rowIndex, 0, 'row 0 key rowIndex'); - assert.deepEqual(rows[0].isNewRow, true, 'row 0 is inserted'); - assert.deepEqual(rows[0].data, { parentId: 0 }, 'row 0 data should contains parentId'); - assert.strictEqual(rows[1].key, 1, 'row 1'); - assert.strictEqual(rows[1].isExpanded, false, 'row 1 is not expanded'); -}); + return $.Deferred().resolve(); + }; -QUnit.test('Inserted row should be reseted after collapsing when editing mode is row', function(assert) { - // arrange - const $testElement = $('#treeList'); + addRowAfterDeferredResolve([1, 2], 0); - this.options.editing.allowAdding = true; - this.options.editing.texts = { addRowToNode: 'Add' }; + this.clock.tick(); + }); - this.setupTreeList(); - this.rowsView.render($testElement); + QUnit.test('AddRow method returns Deferred with using promise in onInitNewRow (T844118)', function(assert) { + // arrange + const deferred = $.Deferred(); + this.options.editing.allowAdding = true; + this.options.onInitNewRow = (e) => { + e.promise = deferred; + }; - // act - this.addRow(1); - this.clock.tick(); + this.setupTreeList(); + this.rowsView.render($('#treeList')); + this.clock.tick(); - this.collapseRow(1); + // act + let isAddRowDone = false; + this.addRow(1).done(() => isAddRowDone = true); + this.clock.tick(); - // assert - const rows = this.getVisibleRows(); - assert.strictEqual(rows.length, 1, 'one row is rendered'); - assert.strictEqual(rows[0].key, 1, 'row 0 key'); - assert.notOk(rows[0].isNewRow, 'row 0 is not inserted'); - assert.strictEqual(this.editingController.isEditing(), false, 'editing is not active'); -}); + // assert + assert.notOk(isAddRowDone, 'done method has not executed yet'); + deferred.resolve(); + assert.ok(isAddRowDone, 'done method has executed'); + }); -QUnit.test('Edit cell on row click', function(assert) { + // T553905 + QUnit.test('Add item in node without children (Angular)', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'batch', - allowUpdating: true - }; + this.options.editing.allowAdding = true; + this.options.expandedRowKeys = [1]; + this.options.loadingTimeout = 30; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - $testElement.find('tbody td').first().trigger('dxclick'); + // act + this.addRow(2); + this.dataController.optionChanged({ name: 'expandedRowKeys', value: [1, 2], previousValue: [1, 2] }); // simulate the call from ngDoCheck hook + this.clock.tick(30); - // assert - assert.ok($testElement.find('tbody td').first().hasClass('dx-editor-cell'), 'edit cell'); -}); + // assert + const rows = this.getVisibleRows(); + assert.strictEqual(rows.length, 3, 'count row'); + assert.strictEqual(rows[0].key, 1, 'key of the first row'); + assert.strictEqual(rows[0].isExpanded, true, 'first row is expanded'); + assert.strictEqual(rows[1].key, 2, 'key of the second row'); + assert.strictEqual(rows[1].node.parent.key, 1, 'parent key of the second row'); + assert.deepEqual(rows[2].key.parentKey, 2, 'parent key of the third row'); + assert.deepEqual(rows[2].key.rowIndex, 2, 'rowIndex of the third row'); + assert.deepEqual(rows[2].isNewRow, true, 'third row is inserted'); + assert.deepEqual(rows[2].data, { parentId: 2 }, 'third row data should contain parentId'); + }); -QUnit.test('Editing with validation - save edit data', function(assert) { + QUnit.test('AddRow method witout parameters should add item at begin', function(assert) { // arrange - let items; - let $cellElement; - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'batch' - }; - this.options.columns[0].validationRules = [{ type: 'required' }]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.cellValue(0, 0, '666'); - this.saveEditData(); - - // assert - items = this.getDataSource().items(); - $cellElement = $testElement.find('tbody > .dx-data-row').first().children().first(); - assert.notOk($cellElement.hasClass('dx-treelist-invalid'), 'first cell value isn\'t valid'); - assert.equal(items[0].data.field1, '666'); -}); + this.options.rootValue = 0; + this.options.editing.allowAdding = true; + this.options.editing.texts = { addRowToNode: 'Add' }; -QUnit.test('Editing with validation - not save edit data when there are invalid', function(assert) { - // arrange - let items; - let $cellElement; - const $testElement = $('#treeList'); + this.setupTreeList(); + this.rowsView.render($testElement); - this.options.editing = { - mode: 'batch' - }; - this.options.columns[0].validationRules = [{ type: 'required' }]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.cellValue(0, 0, ''); - this.saveEditData(); - - // assert - items = this.getDataSource().items(); - $cellElement = $testElement.find('tbody > .dx-data-row').first().children().first(); - assert.ok($cellElement.hasClass('dx-treelist-invalid'), 'first cell value isn\'t valid'); - assert.equal(items[0].data.field1, 'test1'); -}); + // act + this.addRow(); + this.clock.tick(); -QUnit.test('Editing with validation - show error row on save edit data', function(assert) { - // arrange - let $rowElements; - const $testElement = $('#treeList'); + // assert + const rows = this.getVisibleRows(); - this.options.editing = { - mode: 'batch' - }; - this.options.onRowValidating = function(options) { - options.errorText = 'Test'; - }; - this.options.columns[0].validationRules = [{ type: 'required' }]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.cellValue(0, 0, ''); - this.saveEditData(); - - // assert - $rowElements = $testElement.find('tbody > tr'); - assert.equal($rowElements.length, 3, 'count row (data row + error row + freespace row)'); - assert.ok($rowElements.eq(0).hasClass('dx-data-row'), 'data row'); - assert.ok($rowElements.eq(1).hasClass('dx-error-row'), 'error row'); - assert.ok($rowElements.eq(2).hasClass('dx-freespace-row'), 'freespace row'); -}); + assert.strictEqual(rows.length, 2, 'rows count'); + assert.deepEqual(rows[0].key.parentKey, 0, 'row 0 key parentKey'); + assert.deepEqual(rows[0].key.rowIndex, 0, 'row 0 key rowIndex'); + assert.deepEqual(rows[0].isNewRow, true, 'row 0 is inserted'); + assert.deepEqual(rows[0].data, { parentId: 0 }, 'row 0 data should contains parentId'); + assert.strictEqual(rows[1].key, 1, 'row 1'); + assert.strictEqual(rows[1].isExpanded, false, 'row 1 is not expanded'); + }); -QUnit.test('Save edit data - exception when key is not specified in a store', function(assert) { + QUnit.test('AddRow method witout parameters should add item at begin if rootValue is defined', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'batch' - }; - this.options.dataSource = { - store: { - type: 'array', - data: [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) } - ] - } - }; - this.setupTreeList(); - this.rowsView.render($testElement); - this.cellValue(0, 0, 666); + this.options.rootValue = 0; + this.options.dataSource.store.data[0].parentId = 0; + this.options.editing.allowAdding = true; + this.options.editing.texts = { addRowToNode: 'Add' }; - // act, assert - try { - this.saveEditData(); - assert.ok(false, 'exception should be rised'); - } catch(e) { - assert.ok(e.message.indexOf('E1045') >= 0, 'name of error'); - } -}); + this.setupTreeList(); + this.rowsView.render($testElement); -QUnit.test('Delete data - exception when key is not specified in a store', function(assert) { - // arrange - const $testElement = $('#treeList'); + // act + this.addRow(); + this.clock.tick(); - this.options.editing = { - mode: 'batch' - }; - this.options.dataSource = { - store: { - type: 'array', - data: [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) } - ] - } - }; - this.setupTreeList(); - this.rowsView.render($testElement); - this.deleteRow(0); + // assert + const rows = this.getVisibleRows(); - // act, assert - try { - this.saveEditData(); - assert.ok(false, 'exception should be rised'); - } catch(e) { - assert.ok(e.message.indexOf('E1045') >= 0, 'name of error'); - } -}); + assert.strictEqual(rows.length, 2, 'rows count'); + assert.deepEqual(rows[0].key.parentKey, 0, 'row 0 key parentKey'); + assert.deepEqual(rows[0].key.rowIndex, 0, 'row 0 key rowIndex'); + assert.deepEqual(rows[0].isNewRow, true, 'row 0 is inserted'); + assert.deepEqual(rows[0].data, { parentId: 0 }, 'row 0 data should contains parentId'); + assert.strictEqual(rows[1].key, 1, 'row 1'); + assert.strictEqual(rows[1].isExpanded, false, 'row 1 is not expanded'); + }); -QUnit.test('Insert data - no exception when key is not specified in a store', function(assert) { + QUnit.test('Inserted row should be reseted after collapsing when editing mode is row', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.rootValue = 0; - this.options.editing = { - mode: 'batch' - }; - this.options.dataSource = { - store: { - type: 'array', - data: [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) } - ] - } - }; - this.setupTreeList(); - this.rowsView.render($testElement); - this.addRow(); + this.options.editing.allowAdding = true; + this.options.editing.texts = { addRowToNode: 'Add' }; - // act - this.saveEditData(); + this.setupTreeList(); + this.rowsView.render($testElement); - // assert - assert.ok(true, 'not exception'); -}); + // act + this.addRow(1); + this.clock.tick(); + + this.collapseRow(1); + + // assert + const rows = this.getVisibleRows(); + assert.strictEqual(rows.length, 1, 'one row is rendered'); + assert.strictEqual(rows[0].key, 1, 'row 0 key'); + assert.notOk(rows[0].isNewRow, 'row 0 is not inserted'); + assert.strictEqual(this.editingController.isEditing(), false, 'editing is not active'); + }); -QUnit.test('Edit cell when edit mode is \'batch\' and multiple selection is enabled', function(assert) { + QUnit.test('Edit cell on row click', function(assert) { // arrange - let $cellElement; - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'batch' - }; - this.options.selection = { - mode: 'multiple', - showCheckBoxesMode: 'always' - }; + this.options.editing = { + mode: 'batch', + allowUpdating: true + }; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.editCell(0, 0); + // act + $testElement.find('tbody td').first().trigger('dxclick'); - // assert - $cellElement = $testElement.find('tbody > tr').first().children().first(); - assert.ok(!$cellElement.find('.dx-select-checkbox').length, 'hasn\'t checkbox'); -}); + // assert + assert.ok($testElement.find('tbody td').first().hasClass('dx-editor-cell'), 'edit cell'); + }); -// T514550 -QUnit.test('Edit batch - inserted row should not have add link', function(assert) { + QUnit.test('Editing with validation - save edit data', function(assert) { // arrange - let $commandEditCellElement; - const $testElement = $('#treeList'); - - this.options.editing = { - mode: 'batch', - allowAdding: true - }; + let items; + let $cellElement; + const $testElement = $('#treeList'); - this.setupTreeList(); - this.rowsView.render($testElement); + this.options.editing = { + mode: 'batch' + }; + this.options.columns[0].validationRules = [{ type: 'required' }]; + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.addRow(); + // act + this.cellValue(0, 0, '666'); + this.saveEditData(); - // assert - $commandEditCellElement = $testElement.find('tbody > .dx-row-inserted').first().find('.dx-command-edit'); - assert.equal($commandEditCellElement.length, 1, 'has command edit cell'); - assert.equal($commandEditCellElement.find('.dx-link-add').length, 0, 'link add isn\'t rendered'); -}); + // assert + items = this.getDataSource().items(); + $cellElement = $testElement.find('tbody > .dx-data-row').first().children().first(); + assert.notOk($cellElement.hasClass('dx-treelist-invalid'), 'first cell value isn\'t valid'); + assert.equal(items[0].data.field1, '666'); + }); -QUnit.test('Edit batch - removed row should not have add link', function(assert) { + QUnit.test('Editing with validation - not save edit data when there are invalid', function(assert) { // arrange - let $commandEditCellElement; - const $testElement = $('#treeList'); - - this.options.editing = { - mode: 'batch', - allowAdding: true - }; + let items; + let $cellElement; + const $testElement = $('#treeList'); - this.setupTreeList(); - this.rowsView.render($testElement); + this.options.editing = { + mode: 'batch' + }; + this.options.columns[0].validationRules = [{ type: 'required' }]; + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.deleteRow(0); + // act + this.cellValue(0, 0, ''); + this.saveEditData(); - // assert - $commandEditCellElement = $testElement.find('tbody > .dx-row-removed').first().find('.dx-command-edit'); - assert.equal($commandEditCellElement.length, 1, 'has command edit cell'); - assert.equal($commandEditCellElement.find('.dx-link-add').length, 0, 'link add isn\'t rendered'); -}); + // assert + items = this.getDataSource().items(); + $cellElement = $testElement.find('tbody > .dx-data-row').first().children().first(); + assert.ok($cellElement.hasClass('dx-treelist-invalid'), 'first cell value isn\'t valid'); + assert.equal(items[0].data.field1, 'test1'); + }); -QUnit.test('Edit row with useIcons is true', function(assert) { + QUnit.test('Editing with validation - show error row on save edit data', function(assert) { // arrange - let $editCellElement; - const $testElement = $('#container'); - - this.options.editing = { - mode: 'row', - allowAdding: true, - useIcons: true, - texts: { - addRowToNode: 'Add' - } - }; + let $rowElements; + const $testElement = $('#treeList'); + + this.options.editing = { + mode: 'batch' + }; + this.options.onRowValidating = function(options) { + options.errorText = 'Test'; + }; + this.options.columns[0].validationRules = [{ type: 'required' }]; + this.setupTreeList(); + this.rowsView.render($testElement); - this.setupTreeList(); - this.rowsView.render($testElement); + // act + this.cellValue(0, 0, ''); + this.saveEditData(); - // assert - $editCellElement = $testElement.find('tbody > tr').first().children().last(); - assert.ok($editCellElement.hasClass('dx-command-edit-with-icons'), 'the edit cell has icons'); - assert.strictEqual($editCellElement.find('.dx-link').length, 1, 'icon count'); - assert.ok($editCellElement.find('.dx-link').hasClass('dx-icon-add'), 'icon add'); - assert.strictEqual($editCellElement.find('.dx-icon-add').attr('title'), 'Add', 'title of the icon add'); - assert.strictEqual($editCellElement.find('.dx-icon-add').text(), '', 'text of the icon add'); -}); + // assert + $rowElements = $testElement.find('tbody > tr'); + assert.equal($rowElements.length, 3, 'count row (data row + error row + freespace row)'); + assert.ok($rowElements.eq(0).hasClass('dx-data-row'), 'data row'); + assert.ok($rowElements.eq(1).hasClass('dx-error-row'), 'error row'); + assert.ok($rowElements.eq(2).hasClass('dx-freespace-row'), 'freespace row'); + }); -// T633865 -QUnit.test('Add row when \'keyExpr\' and \'parentIdExpr\' options are specified as functions', function(assert) { + QUnit.test('Save edit data - exception when key is not specified in a store', function(assert) { // arrange - let $rowElements; - const $testElement = $('#treeList'); - - this.options.rootValue = 0; - this.options.dataSource = this.options.dataSource.store.data; - this.options.keyExpr = function(data) { return data['id']; }; - this.options.parentIdExpr = function(data, value) { - if(arguments.length === 1) { - return data['parentId']; - } - data['parentId'] = value; - }; - this.options.editing = { - mode: 'cell', - allowAdding: true - }; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.addRow(); + const $testElement = $('#treeList'); - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.equal($rowElements.length, 2, 'count data row'); - assert.ok($rowElements.first().hasClass('dx-row-inserted'), 'insert row'); - assert.strictEqual(this.getVisibleRows()[0].data.parentId, 0, 'parentId of an inserted row'); -}); + this.options.editing = { + mode: 'batch' + }; + this.options.dataSource = { + store: { + type: 'array', + data: [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) } + ] + } + }; + this.setupTreeList(); + this.rowsView.render($testElement); + this.cellValue(0, 0, 666); + + // act, assert + try { + this.saveEditData(); + assert.ok(false, 'exception should be rised'); + } catch(e) { + assert.ok(e.message.indexOf('E1045') >= 0, 'name of error'); + } + }); -QUnit.test('TreeList should show error message on adding row if dataSource is not specified (T711831)', function(assert) { + QUnit.test('Delete data - exception when key is not specified in a store', function(assert) { // arrange - let errorCode; - let widgetName; + const $testElement = $('#treeList'); - this.options.dataSource = undefined; - this.setupTreeList(); + this.options.editing = { + mode: 'batch' + }; + this.options.dataSource = { + store: { + type: 'array', + data: [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) } + ] + } + }; + this.setupTreeList(); + this.rowsView.render($testElement); + this.deleteRow(0); - this.rowsView.render($('#treeList')); - this.getController('data').fireError = function() { - errorCode = arguments[0]; - widgetName = arguments[1]; - }; + // act, assert + try { + this.saveEditData(); + assert.ok(false, 'exception should be rised'); + } catch(e) { + assert.ok(e.message.indexOf('E1045') >= 0, 'name of error'); + } + }); - // act - const deferred = this.addRow(); + QUnit.test('Insert data - no exception when key is not specified in a store', function(assert) { + // arrange + const $testElement = $('#treeList'); - // assert - assert.equal(errorCode, 'E1052', 'error code'); - assert.equal(widgetName, 'dxTreeList', 'widget name'); - assert.equal(deferred.state(), 'rejected', 'deferred is rejected'); -}); + this.options.rootValue = 0; + this.options.editing = { + mode: 'batch' + }; + this.options.dataSource = { + store: { + type: 'array', + data: [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) } + ] + } + }; + this.setupTreeList(); + this.rowsView.render($testElement); + this.addRow(); + + // act + this.saveEditData(); -QUnit.test('Set add button for a specific row', function(assert) { + // assert + assert.ok(true, 'not exception'); + }); + + QUnit.test('Edit cell when edit mode is \'batch\' and multiple selection is enabled', function(assert) { // arrange - let $rowElements; - const $testElement = $('#treeList'); - - this.options.expandedRowKeys = [1]; - this.options.editing = { - mode: 'row', - allowAdding: function(options) { - return options.row.rowIndex % 2 === 0; - } - }; - this.setupTreeList(); + let $cellElement; + const $testElement = $('#treeList'); - // act - this.rowsView.render($testElement); + this.options.editing = { + mode: 'batch' + }; + this.options.selection = { + mode: 'multiple', + showCheckBoxesMode: 'always' + }; - // assert - $rowElements = $testElement.find('.dx-treelist-rowsview tbody > .dx-data-row'); - assert.strictEqual($rowElements.length, 2, 'row count'); - assert.strictEqual($rowElements.eq(0).find('.dx-link-add').length, 1, 'first row has the add link'); - assert.strictEqual($rowElements.eq(1).find('.dx-link-add').length, 0, 'second row hasn\'t the add link'); -}); + this.setupTreeList(); + this.rowsView.render($testElement); -// T690119 -QUnit.test('Edit cell - The editable cell should be closed after click on expand button', function(assert) { - // arrange - const $testElement = $('#treeList'); + // act + this.editCell(0, 0); - this.options.editing = { - mode: 'cell', - allowUpdating: true - }; - this.options.loadingTimeout = 0; - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2003, 2, 3) } - ]; - this.setupTreeList(); - this.clock.tick(); - this.rowsView.render($testElement); - - this.editCell(0, 0); - this.clock.tick(); - - // assert - assert.strictEqual($(this.getCellElement(0, 0)).find('.dx-texteditor').length, 1, 'has editor'); - - // act - $(this.getCellElement(1, 0)).find('.dx-treelist-collapsed').trigger('dxpointerdown'); - $(this.getCellElement(1, 0)).find('.dx-treelist-collapsed').trigger('dxclick'); - this.clock.tick(); - - // assert - assert.strictEqual($(this.getCellElement(0, 0)).find('.dx-texteditor').length, 0, 'hasn\'t editor'); -}); + // assert + $cellElement = $testElement.find('tbody > tr').first().children().first(); + assert.ok(!$cellElement.find('.dx-select-checkbox').length, 'hasn\'t checkbox'); + }); -// T697344 -QUnit.test('Removing a selected row should not throw an exception', function(assert) { + // T514550 + QUnit.test('Edit batch - inserted row should not have add link', function(assert) { // arrange - const $testElement = $('#container'); + let $commandEditCellElement; + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'row', - allowDeleting: true - }; - this.options.selection = { mode: 'multiple' }; - this.options.selectedRowKeys = [1]; + this.options.editing = { + mode: 'batch', + allowAdding: true + }; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - try { // act - this.deleteRow(0); + this.addRow(); // assert - assert.strictEqual(this.getVisibleRows().length, 0); - } catch(e) { - assert.ok(false, 'exception'); - } -}); + $commandEditCellElement = $testElement.find('tbody > .dx-row-inserted').first().find('.dx-command-edit'); + assert.equal($commandEditCellElement.length, 1, 'has command edit cell'); + assert.equal($commandEditCellElement.find('.dx-link-add').length, 0, 'link add isn\'t rendered'); + }); -QUnit.test('Selection should be updated correctly after deleting a nested node', function(assert) { + QUnit.test('Edit batch - removed row should not have add link', function(assert) { // arrange - const $testElement = $('#container'); + let $commandEditCellElement; + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'row', - allowDeleting: true - }; - this.options.selection = { mode: 'multiple', recursive: true }; - this.options.selectedRowKeys = [2]; - this.options.expandedRowKeys = [1]; - this.options.dataSource.store.data = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2003, 2, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2004, 3, 4) } - ]; - - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.deleteRow(1); - - // assert - assert.strictEqual(this.isRowSelected(1), false, 'first node is not selected'); -}); + this.options.editing = { + mode: 'batch', + allowAdding: true + }; -QUnit.test('Batch mode - Editing should not work when double-clicking on the select checkbox (startEditAction is dblClick)', function(assert) { - // arrange - const $testElement = $('#treeList'); + this.setupTreeList(); + this.rowsView.render($testElement); - this.options.editing = { - mode: 'batch', - allowUpdating: true, - startEditAction: 'dblClick' - }; - this.options.selection = { - mode: 'multiple', - showCheckBoxesMode: 'always' - }; + // act + this.deleteRow(0); - this.setupTreeList(); - this.rowsView.render($testElement); - sinon.spy(this.editingController, 'editCell'); + // assert + $commandEditCellElement = $testElement.find('tbody > .dx-row-removed').first().find('.dx-command-edit'); + assert.equal($commandEditCellElement.length, 1, 'has command edit cell'); + assert.equal($commandEditCellElement.find('.dx-link-add').length, 0, 'link add isn\'t rendered'); + }); - // act - $(this.getCellElement(0, 0)).find('.dx-select-checkbox').trigger('dxdblclick'); + QUnit.test('Edit row with useIcons is true', function(assert) { + // arrange + let $editCellElement; + const $testElement = $('#container'); - // assert - assert.strictEqual(this.editingController.editCell.callCount, 0, 'count call editCell'); - assert.notOk($(this.getCellElement(0, 0)).hasClass('dx-editor-cell'), 'cell isn\'t editable'); -}); + this.options.editing = { + mode: 'row', + allowAdding: true, + useIcons: true, + texts: { + addRowToNode: 'Add' + } + }; -[false, true].forEach(function(remoteOperations) { - // T836724 - QUnit.test('The added nodes should be displayed when there is a filter and remoteOperations is ' + remoteOperations, function(assert) { - // arrange - let $rowElements; + this.setupTreeList(); + this.rowsView.render($testElement); - const $testElement = $('#treeList'); + // assert + $editCellElement = $testElement.find('tbody > tr').first().children().last(); + assert.ok($editCellElement.hasClass('dx-command-edit-with-icons'), 'the edit cell has icons'); + assert.strictEqual($editCellElement.find('.dx-link').length, 1, 'icon count'); + assert.ok($editCellElement.find('.dx-link').hasClass('dx-icon-add'), 'icon add'); + assert.strictEqual($editCellElement.find('.dx-icon-add').attr('title'), 'Add', 'title of the icon add'); + assert.strictEqual($editCellElement.find('.dx-icon-add').text(), '', 'text of the icon add'); + }); - const store = new ArrayStore({ - data: [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, field1: 'test3', field2: 3, field3: new Date(2001, 0, 3) }, - ], - key: 'id' - }); + // T633865 + QUnit.test('Add row when \'keyExpr\' and \'parentIdExpr\' options are specified as functions', function(assert) { + // arrange + let $rowElements; + const $testElement = $('#treeList'); - this.options.expandedRowKeys = [1]; - this.options.remoteOperations = remoteOperations; - this.options.filterMode = 'fullBranch'; - this.options.searchPanel = { - visible: true, - text: 'test1' - }, + this.options.rootValue = 0; + this.options.dataSource = this.options.dataSource.store.data; + this.options.keyExpr = function(data) { return data['id']; }; + this.options.parentIdExpr = function(data, value) { + if(arguments.length === 1) { + return data['parentId']; + } + data['parentId'] = value; + }; this.options.editing = { mode: 'cell', allowAdding: true }; - this.options.dataSource = { - load: (loadOptions) => store.load(loadOptions), - insert: (values) => store.insert(values) - }; this.setupTreeList(); this.rowsView.render($testElement); - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.strictEqual($rowElements.length, 2, 'row count'); - // act - this.addRow(1); - this.saveEditData(); + this.addRow(); // assert $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.strictEqual($rowElements.length, 3, 'row count'); + assert.equal($rowElements.length, 2, 'count data row'); + assert.ok($rowElements.first().hasClass('dx-row-inserted'), 'insert row'); + assert.strictEqual(this.getVisibleRows()[0].data.parentId, 0, 'parentId of an inserted row'); + }); + + QUnit.test('TreeList should show error message on adding row if dataSource is not specified (T711831)', function(assert) { + // arrange + let errorCode; + let widgetName; + + this.options.dataSource = undefined; + this.setupTreeList(); + + this.rowsView.render($('#treeList')); + this.getController('data').fireError = function() { + errorCode = arguments[0]; + widgetName = arguments[1]; + }; // act - this.addRow(1); - this.saveEditData(); + const deferred = this.addRow(); // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.strictEqual($rowElements.length, 4, 'row count'); + assert.equal(errorCode, 'E1052', 'error code'); + assert.equal(widgetName, 'dxTreeList', 'widget name'); + assert.equal(deferred.state(), 'rejected', 'deferred is rejected'); }); -}); + QUnit.test('Set add button for a specific row', function(assert) { + // arrange + let $rowElements; + const $testElement = $('#treeList'); -['reshape', 'repaint'].forEach(function(refreshMode) { - QUnit.module('Refresh mode ' + refreshMode, { beforeEach: function() { - const that = this; - - that.loadingCount = 0; - - that.options = { - dataSource: { - store: { - type: 'array', - onLoading: function() { - that.loadingCount++; - }, - data: [ - { id: 1, field1: 'test1', hasItems: true }, - { id: 2, parentId: 1, field1: 'test2', hasItems: false } - ], - key: 'id' - } - }, - remoteOperations: { filtering: true }, - columns: [ - { dataField: 'id', allowEditing: true }, - { dataField: 'field1', allowEditing: true } - ], - keyExpr: 'id', - parentIdExpr: 'parentId', - hasItemsExpr: 'hasItems', - expandedRowKeys: [], - editing: { - refreshMode: refreshMode, - mode: 'row' + this.options.expandedRowKeys = [1]; + this.options.editing = { + mode: 'row', + allowAdding: function(options) { + return options.row.rowIndex % 2 === 0; } }; - - that.setupTreeList = function() { - setupTreeListModules(that, ['data', 'columns', 'rows', 'editing', 'editorFactory', 'search'], { - initViews: true - }); - }; - this.clock = sinon.useFakeTimers(); - }, afterEach: teardownModule }); - - QUnit.test('Insert row to leaf', function(assert) { - // arrange this.setupTreeList(); - this.expandRow(1); - this.loadingCount = 0; // act - this.addRow(2); - this.clock.tick(); - this.cellValue(2, 'field1', 'added'); - this.saveEditData(); + this.rowsView.render($testElement); // assert - const rows = this.getVisibleRows(); - assert.equal(this.loadingCount, 0, 'loading count is not changed'); - assert.equal(rows.length, 3, 'row count'); - assert.ok(rows[1].node.hasChildren, 'row 1 node hasChildren'); - assert.ok(rows[1].data.hasItems, 'row 1 hasItems is updated'); - assert.equal(rows[2].data.field1, 'added', 'row 2 data is updated'); - assert.equal(rows[2].node.parent, rows[1].node, 'row 2 node parent'); + $rowElements = $testElement.find('.dx-treelist-rowsview tbody > .dx-data-row'); + assert.strictEqual($rowElements.length, 2, 'row count'); + assert.strictEqual($rowElements.eq(0).find('.dx-link-add').length, 1, 'first row has the add link'); + assert.strictEqual($rowElements.eq(1).find('.dx-link-add').length, 0, 'second row hasn\'t the add link'); }); - QUnit.test('Insert row to root', function(assert) { - // arrange + // T690119 + QUnit.test('Edit cell - The editable cell should be closed after click on expand button', function(assert) { + // arrange + const $testElement = $('#treeList'); + + this.options.editing = { + mode: 'cell', + allowUpdating: true + }; + this.options.loadingTimeout = 0; + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2003, 2, 3) } + ]; this.setupTreeList(); - this.expandRow(1); - this.loadingCount = 0; + this.clock.tick(); + this.rowsView.render($testElement); - // act - this.addRow(); + this.editCell(0, 0); this.clock.tick(); - this.cellValue(0, 'field1', 'added'); - this.saveEditData(); // assert - const rows = this.getVisibleRows(); - const insertIndex = refreshMode === 'reshape' ? 2 : 0; - assert.equal(this.loadingCount, 0, 'loading count is not changed'); - assert.equal(rows.length, 3, 'row count'); - assert.equal(rows[insertIndex].data.field1, 'added', 'row 2 data is updated'); - assert.equal(rows[insertIndex].node.parent, this.getRootNode(), 'row 2 node parent'); - }); - - QUnit.test('Remove leaf node', function(assert) { - // arrange - this.setupTreeList(); - this.expandRow(1); - this.loadingCount = 0; + assert.strictEqual($(this.getCellElement(0, 0)).find('.dx-texteditor').length, 1, 'has editor'); // act - this.removeRow(1); + $(this.getCellElement(1, 0)).find('.dx-treelist-collapsed').trigger('dxpointerdown'); + $(this.getCellElement(1, 0)).find('.dx-treelist-collapsed').trigger('dxclick'); this.clock.tick(); - this.saveEditData(); // assert - const rows = this.getVisibleRows(); - assert.strictEqual(this.loadingCount, 0, 'loading count is not changed'); - assert.strictEqual(rows.length, 1, 'row count'); - assert.strictEqual(rows[0].node.hasChildren, false, 'row 0 node hasChildren'); - assert.strictEqual(rows[0].data.hasItems, false, 'row 0 hasItems'); + assert.strictEqual($(this.getCellElement(0, 0)).find('.dx-texteditor').length, 0, 'hasn\'t editor'); }); - QUnit.test('Remove node with children', function(assert) { - // arrange + // T697344 + QUnit.test('Removing a selected row should not throw an exception', function(assert) { + // arrange + const $testElement = $('#container'); + + this.options.editing = { + mode: 'row', + allowDeleting: true + }; + this.options.selection = { mode: 'multiple' }; + this.options.selectedRowKeys = [1]; + this.setupTreeList(); - this.expandRow(1); - this.getDataSource().store().insert({ id: 3, field1: 'test3', hasItems: false }); - this.refresh(); - this.loadingCount = 0; + this.rowsView.render($testElement); + try { // act - this.removeRow(0); - this.clock.tick(); - this.saveEditData(); + this.deleteRow(0); - // assert - const rows = this.getVisibleRows(); - assert.strictEqual(this.loadingCount, 0, 'loading count is not changed'); - assert.strictEqual(rows.length, 1, 'row count'); - assert.strictEqual(rows[0].key, 3, 'row 2 id'); - assert.strictEqual(rows[0].node.hasChildren, false, 'row 0 node hasChildren'); - assert.strictEqual(rows[0].data.hasItems, false, 'row 0 hasItems'); + // assert + assert.strictEqual(this.getVisibleRows().length, 0); + } catch(e) { + assert.ok(false, 'exception'); + } }); - QUnit.test('Update node', function(assert) { - // arrange + QUnit.test('Selection should be updated correctly after deleting a nested node', function(assert) { + // arrange + const $testElement = $('#container'); + + this.options.editing = { + mode: 'row', + allowDeleting: true + }; + this.options.selection = { mode: 'multiple', recursive: true }; + this.options.selectedRowKeys = [2]; + this.options.expandedRowKeys = [1]; + this.options.dataSource.store.data = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2003, 2, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2004, 3, 4) } + ]; + this.setupTreeList(); - this.expandRow(1); - this.loadingCount = 0; + this.rowsView.render($testElement); // act - this.cellValue(1, 'field1', 'updated'); - this.saveEditData(); + this.deleteRow(1); // assert - const rows = this.getVisibleRows(); - assert.strictEqual(this.loadingCount, 0, 'loading count is not changed'); - assert.strictEqual(rows.length, 2, 'row count'); - assert.strictEqual(rows[1].data.field1, 'updated', 'row 0 data is updated'); - assert.deepEqual(rows[1].values, [2, 'updated'], 'row 0 values are updated'); + assert.strictEqual(this.isRowSelected(1), false, 'first node is not selected'); }); - QUnit.test('Push insert with index 0', function(assert) { - // arrange + QUnit.test('Batch mode - Editing should not work when double-clicking on the select checkbox (startEditAction is dblClick)', function(assert) { + // arrange + const $testElement = $('#treeList'); + + this.options.editing = { + mode: 'batch', + allowUpdating: true, + startEditAction: 'dblClick' + }; + this.options.selection = { + mode: 'multiple', + showCheckBoxesMode: 'always' + }; + this.setupTreeList(); - this.expandRow(1); - this.loadingCount = 0; + this.rowsView.render($testElement); + sinon.spy(this.editingController, 'editCell'); // act - this.getDataSource().store().push([{ type: 'insert', data: { id: 3, parentId: 1, field1: 'test3', hasItems: false }, index: 0 }]); - this.clock.tick(); + $(this.getCellElement(0, 0)).find('.dx-select-checkbox').trigger('dxdblclick'); // assert - const rows = this.getVisibleRows(); - assert.strictEqual(this.loadingCount, 0, 'loading count is not changed'); - assert.strictEqual(rows.length, 3, 'row count'); - assert.strictEqual(rows[0].node.children.length, 2, 'row 0 children count'); - assert.strictEqual(rows[1].data.field1, 'test3', 'row 2 data is updated'); - assert.strictEqual(rows[1].node.parent, rows[0].node, 'row 2 node parent'); + assert.strictEqual(this.editingController.editCell.callCount, 0, 'count call editCell'); + assert.notOk($(this.getCellElement(0, 0)).hasClass('dx-editor-cell'), 'cell isn\'t editable'); }); - QUnit.test('Push insert with index -1', function(assert) { + [false, true].forEach(function(remoteOperations) { + // T836724 + QUnit.test('The added nodes should be displayed when there is a filter and remoteOperations is ' + remoteOperations, function(assert) { // arrange - this.setupTreeList(); - this.expandRow(1); - this.loadingCount = 0; + let $rowElements; - // act - this.getDataSource().store().push([{ type: 'insert', data: { id: 3, parentId: 1, field1: 'test3', hasItems: false }, index: -1 }]); - this.clock.tick(); + const $testElement = $('#treeList'); - // assert - const rows = this.getVisibleRows(); - assert.strictEqual(this.loadingCount, 0, 'loading count is not changed'); - assert.strictEqual(rows.length, 3, 'row count'); - assert.strictEqual(rows[0].node.children.length, 2, 'row 0 children count'); - assert.strictEqual(rows[2].data.field1, 'test3', 'row 2 data is updated'); - assert.strictEqual(rows[2].node.parent, rows[0].node, 'row 2 node parent'); + const store = new ArrayStore({ + data: [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, field1: 'test3', field2: 3, field3: new Date(2001, 0, 3) }, + ], + key: 'id' + }); + + this.options.expandedRowKeys = [1]; + this.options.remoteOperations = remoteOperations; + this.options.filterMode = 'fullBranch'; + this.options.searchPanel = { + visible: true, + text: 'test1' + }, + this.options.editing = { + mode: 'cell', + allowAdding: true + }; + this.options.dataSource = { + load: (loadOptions) => store.load(loadOptions), + insert: (values) => store.insert(values) + }; + this.setupTreeList(); + this.rowsView.render($testElement); + + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + assert.strictEqual($rowElements.length, 2, 'row count'); + + // act + this.addRow(1); + this.saveEditData(); + + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + assert.strictEqual($rowElements.length, 3, 'row count'); + + // act + this.addRow(1); + this.saveEditData(); + + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + assert.strictEqual($rowElements.length, 4, 'row count'); + }); }); +}); - QUnit.test('Push insert with index more then children count', function(assert) { - // arrange - this.setupTreeList(); - this.expandRow(1); - this.loadingCount = 0; +['reshape', 'repaint'].forEach(function(refreshMode) { + QUnit.module('Refresh mode ' + refreshMode, { + beforeEach: function() { + const that = this; + + that.loadingCount = 0; + + that.options = { + dataSource: { + store: { + type: 'array', + onLoading: function() { + that.loadingCount++; + }, + data: [ + { id: 1, field1: 'test1', hasItems: true }, + { id: 2, parentId: 1, field1: 'test2', hasItems: false } + ], + key: 'id' + } + }, + remoteOperations: { filtering: true }, + columns: [ + { dataField: 'id', allowEditing: true }, + { dataField: 'field1', allowEditing: true } + ], + keyExpr: 'id', + parentIdExpr: 'parentId', + hasItemsExpr: 'hasItems', + expandedRowKeys: [], + editing: { + refreshMode: refreshMode, + mode: 'row' + } + }; + + that.setupTreeList = function() { + setupTreeListModules(that, ['data', 'columns', 'rows', 'editing', 'editorFactory', 'search'], { + initViews: true + }); + }; + this.clock = sinon.useFakeTimers(); + }, afterEach: teardownModule }, () => { + + QUnit.test('Insert row to leaf', function(assert) { + // arrange + this.setupTreeList(); + this.expandRow(1); + this.loadingCount = 0; + + // act + this.addRow(2); + this.clock.tick(); + this.cellValue(2, 'field1', 'added'); + this.saveEditData(); + + // assert + const rows = this.getVisibleRows(); + assert.equal(this.loadingCount, 0, 'loading count is not changed'); + assert.equal(rows.length, 3, 'row count'); + assert.ok(rows[1].node.hasChildren, 'row 1 node hasChildren'); + assert.ok(rows[1].data.hasItems, 'row 1 hasItems is updated'); + assert.equal(rows[2].data.field1, 'added', 'row 2 data is updated'); + assert.equal(rows[2].node.parent, rows[1].node, 'row 2 node parent'); + }); - // act - this.getDataSource().store().push([{ type: 'insert', data: { id: 3, parentId: 1, field1: 'test3', hasItems: false }, index: 10 }]); - this.clock.tick(); + QUnit.test('Insert row to root', function(assert) { + // arrange + this.setupTreeList(); + this.expandRow(1); + this.loadingCount = 0; + + // act + this.addRow(); + this.clock.tick(); + this.cellValue(0, 'field1', 'added'); + this.saveEditData(); + + // assert + const rows = this.getVisibleRows(); + const insertIndex = refreshMode === 'reshape' ? 2 : 0; + assert.equal(this.loadingCount, 0, 'loading count is not changed'); + assert.equal(rows.length, 3, 'row count'); + assert.equal(rows[insertIndex].data.field1, 'added', 'row 2 data is updated'); + assert.equal(rows[insertIndex].node.parent, this.getRootNode(), 'row 2 node parent'); + }); - // assert - const rows = this.getVisibleRows(); - assert.strictEqual(this.loadingCount, 0, 'loading count is not changed'); - assert.strictEqual(rows.length, 3, 'row count'); - assert.strictEqual(rows[0].node.children.length, 2, 'row 0 children count'); - assert.strictEqual(rows[2].data.field1, 'test3', 'row 2 data is updated'); - assert.strictEqual(rows[2].node.parent, rows[0].node, 'row 2 node parent'); - }); + QUnit.test('Remove leaf node', function(assert) { + // arrange + this.setupTreeList(); + this.expandRow(1); + this.loadingCount = 0; + + // act + this.removeRow(1); + this.clock.tick(); + this.saveEditData(); + + // assert + const rows = this.getVisibleRows(); + assert.strictEqual(this.loadingCount, 0, 'loading count is not changed'); + assert.strictEqual(rows.length, 1, 'row count'); + assert.strictEqual(rows[0].node.hasChildren, false, 'row 0 node hasChildren'); + assert.strictEqual(rows[0].data.hasItems, false, 'row 0 hasItems'); + }); - // T836724 - QUnit.test('The added nodes should be displayed when there is a filter', function(assert) { - // arrange - let $rowElements; + QUnit.test('Remove node with children', function(assert) { + // arrange + this.setupTreeList(); + this.expandRow(1); + this.getDataSource().store().insert({ id: 3, field1: 'test3', hasItems: false }); + this.refresh(); + this.loadingCount = 0; + + // act + this.removeRow(0); + this.clock.tick(); + this.saveEditData(); + + // assert + const rows = this.getVisibleRows(); + assert.strictEqual(this.loadingCount, 0, 'loading count is not changed'); + assert.strictEqual(rows.length, 1, 'row count'); + assert.strictEqual(rows[0].key, 3, 'row 2 id'); + assert.strictEqual(rows[0].node.hasChildren, false, 'row 0 node hasChildren'); + assert.strictEqual(rows[0].data.hasItems, false, 'row 0 hasItems'); + }); - const $testElement = $('#treeList'); + QUnit.test('Update node', function(assert) { + // arrange + this.setupTreeList(); + this.expandRow(1); + this.loadingCount = 0; + + // act + this.cellValue(1, 'field1', 'updated'); + this.saveEditData(); + + // assert + const rows = this.getVisibleRows(); + assert.strictEqual(this.loadingCount, 0, 'loading count is not changed'); + assert.strictEqual(rows.length, 2, 'row count'); + assert.strictEqual(rows[1].data.field1, 'updated', 'row 0 data is updated'); + assert.deepEqual(rows[1].values, [2, 'updated'], 'row 0 values are updated'); + }); - this.options.expandedRowKeys = [1]; - this.options.filterMode = 'fullBranch'; - this.options.searchPanel = { - visible: true, - text: 'test1' - }, - this.options.editing = { - refreshMode: refreshMode, - mode: 'cell', - allowAdding: true - }; - this.options.dataSource.store.data = [ - { id: 1, field1: 'test1' }, - { id: 2, parentId: 1, field1: 'test2' }, - { id: 3, field1: 'test3' }, - ]; - this.setupTreeList(); - this.rowsView.render($testElement); + QUnit.test('Push insert with index 0', function(assert) { + // arrange + this.setupTreeList(); + this.expandRow(1); + this.loadingCount = 0; + + // act + this.getDataSource().store().push([{ type: 'insert', data: { id: 3, parentId: 1, field1: 'test3', hasItems: false }, index: 0 }]); + this.clock.tick(); + + // assert + const rows = this.getVisibleRows(); + assert.strictEqual(this.loadingCount, 0, 'loading count is not changed'); + assert.strictEqual(rows.length, 3, 'row count'); + assert.strictEqual(rows[0].node.children.length, 2, 'row 0 children count'); + assert.strictEqual(rows[1].data.field1, 'test3', 'row 2 data is updated'); + assert.strictEqual(rows[1].node.parent, rows[0].node, 'row 2 node parent'); + }); - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.strictEqual($rowElements.length, 2, 'row count'); + QUnit.test('Push insert with index -1', function(assert) { + // arrange + this.setupTreeList(); + this.expandRow(1); + this.loadingCount = 0; + + // act + this.getDataSource().store().push([{ type: 'insert', data: { id: 3, parentId: 1, field1: 'test3', hasItems: false }, index: -1 }]); + this.clock.tick(); + + // assert + const rows = this.getVisibleRows(); + assert.strictEqual(this.loadingCount, 0, 'loading count is not changed'); + assert.strictEqual(rows.length, 3, 'row count'); + assert.strictEqual(rows[0].node.children.length, 2, 'row 0 children count'); + assert.strictEqual(rows[2].data.field1, 'test3', 'row 2 data is updated'); + assert.strictEqual(rows[2].node.parent, rows[0].node, 'row 2 node parent'); + }); - // act - this.addRow(1); - this.saveEditData(); + QUnit.test('Push insert with index more then children count', function(assert) { + // arrange + this.setupTreeList(); + this.expandRow(1); + this.loadingCount = 0; + + // act + this.getDataSource().store().push([{ type: 'insert', data: { id: 3, parentId: 1, field1: 'test3', hasItems: false }, index: 10 }]); + this.clock.tick(); + + // assert + const rows = this.getVisibleRows(); + assert.strictEqual(this.loadingCount, 0, 'loading count is not changed'); + assert.strictEqual(rows.length, 3, 'row count'); + assert.strictEqual(rows[0].node.children.length, 2, 'row 0 children count'); + assert.strictEqual(rows[2].data.field1, 'test3', 'row 2 data is updated'); + assert.strictEqual(rows[2].node.parent, rows[0].node, 'row 2 node parent'); + }); - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.strictEqual($rowElements.length, 3, 'row count'); + // T836724 + QUnit.test('The added nodes should be displayed when there is a filter', function(assert) { + // arrange + let $rowElements; - // act - this.addRow(1); - this.saveEditData(); + const $testElement = $('#treeList'); - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.strictEqual($rowElements.length, 4, 'row count'); + this.options.expandedRowKeys = [1]; + this.options.filterMode = 'fullBranch'; + this.options.searchPanel = { + visible: true, + text: 'test1' + }, + this.options.editing = { + refreshMode: refreshMode, + mode: 'cell', + allowAdding: true + }; + this.options.dataSource.store.data = [ + { id: 1, field1: 'test1' }, + { id: 2, parentId: 1, field1: 'test2' }, + { id: 3, field1: 'test3' }, + ]; + this.setupTreeList(); + this.rowsView.render($testElement); + + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + assert.strictEqual($rowElements.length, 2, 'row count'); + + // act + this.addRow(1); + this.saveEditData(); + + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + assert.strictEqual($rowElements.length, 3, 'row count'); + + // act + this.addRow(1); + this.saveEditData(); + + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + assert.strictEqual($rowElements.length, 4, 'row count'); + }); }); }); diff --git a/testing/tests/DevExpress.ui.widgets.treeList/gridView.tests.js b/testing/tests/DevExpress.ui.widgets.treeList/gridView.tests.js index d15d2c684dcf..81ed490ed6d3 100644 --- a/testing/tests/DevExpress.ui.widgets.treeList/gridView.tests.js +++ b/testing/tests/DevExpress.ui.widgets.treeList/gridView.tests.js @@ -47,47 +47,49 @@ QUnit.module('Synchronize columns', { beforeEach: function() { this.createGridView = createGridView; } -}); +}, () => { -QUnit.test('Synchronization widths of columns when \'columnAutoWidth\' option is enabled', function(assert) { + QUnit.test('Synchronization widths of columns when \'columnAutoWidth\' option is enabled', function(assert) { // arrange - const done = assert.async(); - let $cellElement; - let realWidth = 0; - const columnsController = new MockColumnsController([ - { - caption: 'Column 1', - cellTemplate: function($container, options) { - return $('
', { css: { display: 'inline-block' } }).text(options.value); - } - }, - { caption: 'Column 2' } - ]); - const dataController = new MockDataController({ - items: [{ rowType: 'data', values: ['Test Test Test Test Test Test Test Test Test', 'Test'], node: { hasChildren: true }, level: 0 }] - }); - const gridView = this.createGridView({ - columnsController: columnsController, - dataController: dataController - }, { columnAutoWidth: true }); - const $testElement = $('
').width(350).appendTo($('#treeList')); - - // act - gridView.render($testElement); - columnsController.columnsChanged.fire({ - changeTypes: { columns: true, length: 1 }, - optionNames: { visibleWidth: true, length: 1 } - }); + const done = assert.async(); + let $cellElement; + let realWidth = 0; + const columnsController = new MockColumnsController([ + { + caption: 'Column 1', + cellTemplate: function($container, options) { + return $('
', { css: { display: 'inline-block' } }).text(options.value); + } + }, + { caption: 'Column 2' } + ]); + const dataController = new MockDataController({ + items: [{ rowType: 'data', values: ['Test Test Test Test Test Test Test Test Test', 'Test'], node: { hasChildren: true }, level: 0 }] + }); + const gridView = this.createGridView({ + columnsController: columnsController, + dataController: dataController + }, { columnAutoWidth: true }); + const $testElement = $('
').width(350).appendTo($('#treeList')); - // assert - // wait for a font to load - setTimeout(function() { - $cellElement = $testElement.find('.dx-treelist-rowsview').find('tbody > tr').first().children().first(); - $.each($cellElement.children(), function() { - realWidth += $(this).outerWidth(); + // act + gridView.render($testElement); + columnsController.columnsChanged.fire({ + changeTypes: { columns: true, length: 1 }, + optionNames: { visibleWidth: true, length: 1 } }); - assert.ok($cellElement.width() >= Math.round(realWidth), 'correct width of first column'); - done(); + // assert + // wait for a font to load + setTimeout(function() { + $cellElement = $testElement.find('.dx-treelist-rowsview').find('tbody > tr').first().children().first(); + $.each($cellElement.children(), function() { + realWidth += $(this).outerWidth(); + }); + + assert.ok($cellElement.width() >= Math.round(realWidth), 'correct width of first column'); + done(); + }); }); }); + diff --git a/testing/tests/DevExpress.ui.widgets.treeList/headerPanel.tests.js b/testing/tests/DevExpress.ui.widgets.treeList/headerPanel.tests.js index ff237c456756..159d45b849b6 100644 --- a/testing/tests/DevExpress.ui.widgets.treeList/headerPanel.tests.js +++ b/testing/tests/DevExpress.ui.widgets.treeList/headerPanel.tests.js @@ -44,40 +44,42 @@ const teardownModule = function() { this.dispose(); }; -QUnit.module('Header panel', { beforeEach: setupModule, afterEach: teardownModule }); +QUnit.module('Header panel', { beforeEach: setupModule, afterEach: teardownModule }, () => { -QUnit.test('Draw edit buttons', function(assert) { + QUnit.test('Draw edit buttons', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'batch', - allowUpdating: true, - allowAdding: true - }; - this.setupTreeList(); + this.options.editing = { + mode: 'batch', + allowUpdating: true, + allowAdding: true + }; + this.setupTreeList(); - // act - this.headerPanel.render($testElement); + // act + this.headerPanel.render($testElement); - // assert - assert.equal($testElement.find('.dx-treelist-addrow-button').length, 1, 'cancel button'); - assert.equal($testElement.find('.dx-treelist-save-button').length, 1, 'cancel button'); - assert.equal($testElement.find('.dx-treelist-cancel-button').length, 1, 'cancel button'); -}); + // assert + assert.equal($testElement.find('.dx-treelist-addrow-button').length, 1, 'cancel button'); + assert.equal($testElement.find('.dx-treelist-save-button').length, 1, 'cancel button'); + assert.equal($testElement.find('.dx-treelist-cancel-button').length, 1, 'cancel button'); + }); -QUnit.test('Draw column chooser button', function(assert) { + QUnit.test('Draw column chooser button', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.columnChooser = { - enabled: true - }; - this.setupTreeList(); + this.options.columnChooser = { + enabled: true + }; + this.setupTreeList(); - // act - this.headerPanel.render($testElement); + // act + this.headerPanel.render($testElement); - // assert - assert.equal($testElement.find('.dx-treelist-column-chooser-button').length, 1, 'cancel button'); + // assert + assert.equal($testElement.find('.dx-treelist-column-chooser-button').length, 1, 'cancel button'); + }); }); + diff --git a/testing/tests/DevExpress.ui.widgets.treeList/rowDragging.tests.js b/testing/tests/DevExpress.ui.widgets.treeList/rowDragging.tests.js index ae89deb33b33..da138fa8739a 100644 --- a/testing/tests/DevExpress.ui.widgets.treeList/rowDragging.tests.js +++ b/testing/tests/DevExpress.ui.widgets.treeList/rowDragging.tests.js @@ -88,63 +88,65 @@ const moduleConfig = { } }; -QUnit.module('Drag and Drop nodes', moduleConfig); +QUnit.module('Drag and Drop nodes', moduleConfig, () => { -QUnit.test('Drag and drop node', function(assert) { + QUnit.test('Drag and drop node', function(assert) { // arrange - let $draggableElement; - let $placeholderElement; - const onDragEndSpy = sinon.spy(); - const $testElement = $('#container'); - - this.options.rowDragging.onDragEnd = onDragEndSpy; - - const rowsView = this.createRowsView(); - rowsView.render($testElement); - - // act - const pointer = pointerMock(rowsView.getRowElement(0)).start().down().move(0, 70); - - // assert - $draggableElement = $('body').children('.dx-sortable-dragging'); - $placeholderElement = $('body').children('.dx-sortable-placeholder'); - assert.strictEqual($draggableElement.length, 1, 'there is dragging element'); - assert.strictEqual($placeholderElement.length, 1, 'placeholder'); - assert.notOk($placeholderElement.hasClass('dx-sortable-placeholder-inside'), 'placeholder for dropping inward'); - assert.ok($draggableElement.children().hasClass('dx-treelist'), 'dragging element is treelist'); - assert.strictEqual($draggableElement.find('.dx-data-row').length, 1, 'row count in dragging element'); - - // act - pointer.up(); - - // assert - assert.strictEqual(onDragEndSpy.callCount, 1, 'onDragEnd event is called'); - assert.notOk(onDragEndSpy.getCall(0).args[0].dropInsideItem, 'onDragEnd args - dropInsideItem'); -}); + let $draggableElement; + let $placeholderElement; + const onDragEndSpy = sinon.spy(); + const $testElement = $('#container'); + + this.options.rowDragging.onDragEnd = onDragEndSpy; + + const rowsView = this.createRowsView(); + rowsView.render($testElement); + + // act + const pointer = pointerMock(rowsView.getRowElement(0)).start().down().move(0, 70); + + // assert + $draggableElement = $('body').children('.dx-sortable-dragging'); + $placeholderElement = $('body').children('.dx-sortable-placeholder'); + assert.strictEqual($draggableElement.length, 1, 'there is dragging element'); + assert.strictEqual($placeholderElement.length, 1, 'placeholder'); + assert.notOk($placeholderElement.hasClass('dx-sortable-placeholder-inside'), 'placeholder for dropping inward'); + assert.ok($draggableElement.children().hasClass('dx-treelist'), 'dragging element is treelist'); + assert.strictEqual($draggableElement.find('.dx-data-row').length, 1, 'row count in dragging element'); + + // act + pointer.up(); + + // assert + assert.strictEqual(onDragEndSpy.callCount, 1, 'onDragEnd event is called'); + assert.notOk(onDragEndSpy.getCall(0).args[0].dropInsideItem, 'onDragEnd args - dropInsideItem'); + }); -QUnit.test('Drag and drop a node into another node', function(assert) { + QUnit.test('Drag and drop a node into another node', function(assert) { // arrange - let $placeholderElement; - const onDragEndSpy = sinon.spy(); - const $testElement = $('#container'); + let $placeholderElement; + const onDragEndSpy = sinon.spy(); + const $testElement = $('#container'); - this.options.rowDragging.onDragEnd = onDragEndSpy; + this.options.rowDragging.onDragEnd = onDragEndSpy; - const rowsView = this.createRowsView(); - rowsView.render($testElement); + const rowsView = this.createRowsView(); + rowsView.render($testElement); - // act - const pointer = pointerMock(rowsView.getRowElement(0)).start().down().move(0, 50); + // act + const pointer = pointerMock(rowsView.getRowElement(0)).start().down().move(0, 50); - // assert - $placeholderElement = $('body').children('.dx-sortable-placeholder'); - assert.strictEqual($placeholderElement.length, 1, 'placeholder'); - assert.ok($placeholderElement.hasClass('dx-sortable-placeholder-inside'), 'placeholder for dropping inward'); + // assert + $placeholderElement = $('body').children('.dx-sortable-placeholder'); + assert.strictEqual($placeholderElement.length, 1, 'placeholder'); + assert.ok($placeholderElement.hasClass('dx-sortable-placeholder-inside'), 'placeholder for dropping inward'); - // act - pointer.up(); + // act + pointer.up(); - // assert - assert.strictEqual(onDragEndSpy.callCount, 1, 'onDragEnd event is called'); - assert.ok(onDragEndSpy.getCall(0).args[0].dropInsideItem, 'onDragEnd args - dropInsideItem'); + // assert + assert.strictEqual(onDragEndSpy.callCount, 1, 'onDragEnd event is called'); + assert.ok(onDragEndSpy.getCall(0).args[0].dropInsideItem, 'onDragEnd args - dropInsideItem'); + }); }); + diff --git a/testing/tests/DevExpress.ui.widgets.treeList/rows.tests.js b/testing/tests/DevExpress.ui.widgets.treeList/rows.tests.js index 2186b3909161..cf42753a451b 100644 --- a/testing/tests/DevExpress.ui.widgets.treeList/rows.tests.js +++ b/testing/tests/DevExpress.ui.widgets.treeList/rows.tests.js @@ -44,66 +44,67 @@ const teardownModule = function() { this.dispose(); }; -QUnit.module('Rows view', { beforeEach: setupModule, afterEach: teardownModule }); +QUnit.module('Rows view', { beforeEach: setupModule, afterEach: teardownModule }, () => { -QUnit.test('Render rows when all items collapsed', function(assert) { + QUnit.test('Render rows when all items collapsed', function(assert) { // arrange - let $iconElement; - let $cellElements; - let $rowElements; - const $testElement = $('#treeList'); - - this.items = this.items.slice(0, 1); - this.items[0].isExpanded = false; - this.setupTreeList(); - - // act - this.rowsView.render($testElement); - - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - $cellElements = $rowElements.find('td'); - assert.equal($rowElements.length, 1, 'count data row'); - assert.equal($cellElements.length, 3, 'count cell'); - - $iconElement = $cellElements.eq(0).find('.dx-treelist-empty-space'); - assert.ok($cellElements.eq(0).hasClass('dx-treelist-cell-expandable'), 'cell is expandable'); - assert.equal($iconElement.length, 1, 'count empty space of first cell of first row'); - assert.ok($iconElement.eq(0).hasClass('dx-treelist-collapsed'), 'expand icon'); -}); - -QUnit.test('Render rows when all items expanded', function(assert) { + let $iconElement; + let $cellElements; + let $rowElements; + const $testElement = $('#treeList'); + + this.items = this.items.slice(0, 1); + this.items[0].isExpanded = false; + this.setupTreeList(); + + // act + this.rowsView.render($testElement); + + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + $cellElements = $rowElements.find('td'); + assert.equal($rowElements.length, 1, 'count data row'); + assert.equal($cellElements.length, 3, 'count cell'); + + $iconElement = $cellElements.eq(0).find('.dx-treelist-empty-space'); + assert.ok($cellElements.eq(0).hasClass('dx-treelist-cell-expandable'), 'cell is expandable'); + assert.equal($iconElement.length, 1, 'count empty space of first cell of first row'); + assert.ok($iconElement.eq(0).hasClass('dx-treelist-collapsed'), 'expand icon'); + }); + + QUnit.test('Render rows when all items expanded', function(assert) { // arrange - let $iconElement; - let $cellElements; - let $rowElements; - const $testElement = $('#treeList'); - - this.setupTreeList(); - - // act - this.rowsView.render($testElement); - - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - $cellElements = $rowElements.find('td'); - assert.equal($rowElements.length, 3, 'count data row'); - assert.equal($cellElements.length, 9, 'count cell'); - - $iconElement = $cellElements.eq(0).find('.dx-treelist-empty-space'); - assert.equal($iconElement.length, 1, 'count empty space of first cell of first row'); - assert.ok($iconElement.eq(0).hasClass('dx-treelist-expanded'), 'expand icon'); - - $iconElement = $cellElements.eq(3).find('.dx-treelist-empty-space'); - assert.equal($iconElement.length, 2, 'count empty space of first cell of second row'); - assert.notOk($iconElement.eq(0).hasClass('dx-treelist-expanded'), 'empty space'); - assert.ok($iconElement.eq(1).hasClass('dx-treelist-expanded'), 'expand icon'); - - $iconElement = $cellElements.eq(6).find('.dx-treelist-empty-space'); - assert.equal($iconElement.length, 3, 'count empty space of first cell of third row'); - assert.notOk($iconElement.eq(0).hasClass('dx-treelist-expanded'), 'empty space'); - assert.notOk($iconElement.eq(1).hasClass('dx-treelist-expanded'), 'empty space'); - assert.notOk($iconElement.eq(2).hasClass('dx-treelist-expanded'), 'empty space'); + let $iconElement; + let $cellElements; + let $rowElements; + const $testElement = $('#treeList'); + + this.setupTreeList(); + + // act + this.rowsView.render($testElement); + + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + $cellElements = $rowElements.find('td'); + assert.equal($rowElements.length, 3, 'count data row'); + assert.equal($cellElements.length, 9, 'count cell'); + + $iconElement = $cellElements.eq(0).find('.dx-treelist-empty-space'); + assert.equal($iconElement.length, 1, 'count empty space of first cell of first row'); + assert.ok($iconElement.eq(0).hasClass('dx-treelist-expanded'), 'expand icon'); + + $iconElement = $cellElements.eq(3).find('.dx-treelist-empty-space'); + assert.equal($iconElement.length, 2, 'count empty space of first cell of second row'); + assert.notOk($iconElement.eq(0).hasClass('dx-treelist-expanded'), 'empty space'); + assert.ok($iconElement.eq(1).hasClass('dx-treelist-expanded'), 'expand icon'); + + $iconElement = $cellElements.eq(6).find('.dx-treelist-empty-space'); + assert.equal($iconElement.length, 3, 'count empty space of first cell of third row'); + assert.notOk($iconElement.eq(0).hasClass('dx-treelist-expanded'), 'empty space'); + assert.notOk($iconElement.eq(1).hasClass('dx-treelist-expanded'), 'empty space'); + assert.notOk($iconElement.eq(2).hasClass('dx-treelist-expanded'), 'empty space'); + }); }); QUnit.module('Expand/Collapse rows', { @@ -129,104 +130,106 @@ QUnit.module('Expand/Collapse rows', { afterEach: function() { this.dispose(); } -}); +}, () => { -QUnit.test('Expand row', function(assert) { + QUnit.test('Expand row', function(assert) { // arrange - let $rowElements; - const $testElement = $('#treeList'); - - this.setupTreeList(); - this.rowsView.render($testElement); - - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.equal($rowElements.length, 1, 'count data row'); - assert.equal($rowElements.first().find('td').first().find('.dx-treelist-expanded').length, 0, 'hasn\'t expand icon'); - assert.equal($rowElements.first().find('td').first().find('.dx-treelist-collapsed').length, 1, 'has collapse icon'); - - // act - $rowElements.find('.dx-treelist-collapsed').trigger('dxclick'); - - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.equal($rowElements.length, 2, 'count data row'); - assert.equal($rowElements.first().find('td').first().find('.dx-treelist-expanded').length, 1, 'hasn\'t expand icon'); - assert.equal($rowElements.first().find('td').first().find('.dx-treelist-collapsed').length, 0, 'has collapse icon'); -}); + let $rowElements; + const $testElement = $('#treeList'); -QUnit.test('Collapse row', function(assert) { - // arrange - let $rowElements; - const $testElement = $('#treeList'); + this.setupTreeList(); + this.rowsView.render($testElement); - this.setupTreeList(); - this.rowsView.render($testElement); + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + assert.equal($rowElements.length, 1, 'count data row'); + assert.equal($rowElements.first().find('td').first().find('.dx-treelist-expanded').length, 0, 'hasn\'t expand icon'); + assert.equal($rowElements.first().find('td').first().find('.dx-treelist-collapsed').length, 1, 'has collapse icon'); - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.equal($rowElements.length, 1, 'count data row'); - assert.equal($rowElements.first().find('td').first().find('.dx-treelist-expanded').length, 0, 'hasn\'t expand icon'); - assert.equal($rowElements.first().find('td').first().find('.dx-treelist-collapsed').length, 1, 'has collapse icon'); + // act + $rowElements.find('.dx-treelist-collapsed').trigger('dxclick'); - // arrange - $rowElements.find('.dx-treelist-collapsed').trigger('dxclick'); - - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.equal($rowElements.length, 2, 'count data row'); - assert.equal($rowElements.first().find('td').first().find('.dx-treelist-expanded').length, 1, 'hasn\'t expand icon'); - assert.equal($rowElements.first().find('td').first().find('.dx-treelist-collapsed').length, 0, 'has collapse icon'); - - // act - $rowElements.first().find('.dx-treelist-expanded').trigger('dxclick'); - - // assert - $rowElements = $testElement.find('tbody > .dx-data-row'); - assert.equal($rowElements.length, 1, 'count data row'); - assert.equal($rowElements.first().find('td').first().find('.dx-treelist-expanded').length, 0, 'hasn\'t expand icon'); - assert.equal($rowElements.first().find('td').first().find('.dx-treelist-collapsed').length, 1, 'has collapse icon'); -}); + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + assert.equal($rowElements.length, 2, 'count data row'); + assert.equal($rowElements.first().find('td').first().find('.dx-treelist-expanded').length, 1, 'hasn\'t expand icon'); + assert.equal($rowElements.first().find('td').first().find('.dx-treelist-collapsed').length, 0, 'has collapse icon'); + }); -QUnit.test('Expand row on row click when edit mode is \'batch\'', function(assert) { + QUnit.test('Collapse row', function(assert) { // arrange - const $testElement = $('#treeList'); + let $rowElements; + const $testElement = $('#treeList'); + + this.setupTreeList(); + this.rowsView.render($testElement); + + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + assert.equal($rowElements.length, 1, 'count data row'); + assert.equal($rowElements.first().find('td').first().find('.dx-treelist-expanded').length, 0, 'hasn\'t expand icon'); + assert.equal($rowElements.first().find('td').first().find('.dx-treelist-collapsed').length, 1, 'has collapse icon'); + + // arrange + $rowElements.find('.dx-treelist-collapsed').trigger('dxclick'); + + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + assert.equal($rowElements.length, 2, 'count data row'); + assert.equal($rowElements.first().find('td').first().find('.dx-treelist-expanded').length, 1, 'hasn\'t expand icon'); + assert.equal($rowElements.first().find('td').first().find('.dx-treelist-collapsed').length, 0, 'has collapse icon'); + + // act + $rowElements.first().find('.dx-treelist-expanded').trigger('dxclick'); + + // assert + $rowElements = $testElement.find('tbody > .dx-data-row'); + assert.equal($rowElements.length, 1, 'count data row'); + assert.equal($rowElements.first().find('td').first().find('.dx-treelist-expanded').length, 0, 'hasn\'t expand icon'); + assert.equal($rowElements.first().find('td').first().find('.dx-treelist-collapsed').length, 1, 'has collapse icon'); + }); + + QUnit.test('Expand row on row click when edit mode is \'batch\'', function(assert) { + // arrange + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'batch', - allowUpdating: true - }; + this.options.editing = { + mode: 'batch', + allowUpdating: true + }; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - $testElement.find('tbody td').first().find('.dx-treelist-collapsed').first().trigger('dxclick'); + // act + $testElement.find('tbody td').first().find('.dx-treelist-collapsed').first().trigger('dxclick'); - // assert - assert.ok(!$testElement.find('tbody td').first().hasClass('dx-editor-cell'), 'cell isn\'t edit'); - assert.equal($testElement.find('tbody > .dx-data-row').length, 2, 'count data row'); -}); + // assert + assert.ok(!$testElement.find('tbody td').first().hasClass('dx-editor-cell'), 'cell isn\'t edit'); + assert.equal($testElement.find('tbody > .dx-data-row').length, 2, 'count data row'); + }); -QUnit.test('Collapse row on row click when edit mode is \'batch\'', function(assert) { + QUnit.test('Collapse row on row click when edit mode is \'batch\'', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.editing = { - mode: 'batch', - allowUpdating: true - }; - this.options.expandedRowKeys = [1]; + this.options.editing = { + mode: 'batch', + allowUpdating: true + }; + this.options.expandedRowKeys = [1]; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // assert - assert.equal($testElement.find('tbody > .dx-data-row').length, 2, 'count data row'); + // assert + assert.equal($testElement.find('tbody > .dx-data-row').length, 2, 'count data row'); - // act - $testElement.find('tbody td').first().find('.dx-treelist-expanded').first().trigger('dxclick'); + // act + $testElement.find('tbody td').first().find('.dx-treelist-expanded').first().trigger('dxclick'); - // assert - assert.equal($testElement.find('tbody > .dx-data-row').length, 1, 'count data row'); + // assert + assert.equal($testElement.find('tbody > .dx-data-row').length, 1, 'count data row'); + }); }); + diff --git a/testing/tests/DevExpress.ui.widgets.treeList/selection.tests.js b/testing/tests/DevExpress.ui.widgets.treeList/selection.tests.js index 5d807c1c18b3..8cc541de4de8 100644 --- a/testing/tests/DevExpress.ui.widgets.treeList/selection.tests.js +++ b/testing/tests/DevExpress.ui.widgets.treeList/selection.tests.js @@ -50,139 +50,113 @@ const teardownModule = function() { this.dispose(); }; -QUnit.module('Selection', { beforeEach: setupModule, afterEach: teardownModule }); +QUnit.module('Selection', { beforeEach: setupModule, afterEach: teardownModule }, () => { -QUnit.test('Select row', function(assert) { + QUnit.test('Select row', function(assert) { // arrange - const data = { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }; - const $testElement = $('#treeList'); + const data = { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }; + const $testElement = $('#treeList'); - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - const key = this.keyOf(data); - this.selectRows(key); + // act + const key = this.keyOf(data); + this.selectRows(key); - // assert - assert.deepEqual(this.getSelectedRowKeys(), [1]); - assert.deepEqual(this.option('selectedRowKeys'), [1]); - assert.ok(this.dataController.items()[0].isSelected); -}); + // assert + assert.deepEqual(this.getSelectedRowKeys(), [1]); + assert.deepEqual(this.option('selectedRowKeys'), [1]); + assert.ok(this.dataController.items()[0].isSelected); + }); -QUnit.test('Select row when store hasn\'t key', function(assert) { -// arrange - const data = this.options.dataSource; - const $testElement = $('#treeList'); + QUnit.test('Select row when store hasn\'t key', function(assert) { + // arrange + const data = this.options.dataSource; + const $testElement = $('#treeList'); - this.options.dataSource = { - load: function() { - return $.Deferred().resolve(data); - } - }; + this.options.dataSource = { + load: function() { + return $.Deferred().resolve(data); + } + }; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.selectRows(1); + // act + this.selectRows(1); - // assert - assert.deepEqual(this.getSelectedRowKeys(), [1], 'selected row keys'); - assert.ok($testElement.find('.dx-data-row').first().hasClass('dx-selection'), 'first row is selected'); -}); + // assert + assert.deepEqual(this.getSelectedRowKeys(), [1], 'selected row keys'); + assert.ok($testElement.find('.dx-data-row').first().hasClass('dx-selection'), 'first row is selected'); + }); -QUnit.test('Select all rows', function(assert) { + QUnit.test('Select all rows', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.selectAll(); + // act + this.selectAll(); - // assert - assert.deepEqual(this.getController('selection').isSelectAll(), true, 'select all state'); - assert.deepEqual(this.getSelectedRowKeys(), [1], 'only visible rows are selected'); - assert.deepEqual(this.option('selectedRowKeys'), [1], 'only visible rows are selected'); + // assert + assert.deepEqual(this.getController('selection').isSelectAll(), true, 'select all state'); + assert.deepEqual(this.getSelectedRowKeys(), [1], 'only visible rows are selected'); + assert.deepEqual(this.option('selectedRowKeys'), [1], 'only visible rows are selected'); - // act - this.expandRow(1); + // act + this.expandRow(1); - // assert - assert.deepEqual(this.getController('selection').isSelectAll(), undefined, 'select all state is changed after expand'); -}); + // assert + assert.deepEqual(this.getController('selection').isSelectAll(), undefined, 'select all state is changed after expand'); + }); -QUnit.test('Deselect all rows', function(assert) { + QUnit.test('Deselect all rows', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.selectedRowKeys = [1, 2]; + this.options.selectedRowKeys = [1, 2]; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.deselectAll(); + // act + this.deselectAll(); - // assert - assert.deepEqual(this.getController('selection').isSelectAll(), false, 'select all state'); - assert.deepEqual(this.getSelectedRowKeys(), [2], 'visible rows are deselected'); - assert.deepEqual(this.option('selectedRowKeys'), [2], 'visible rows are deselected'); -}); + // assert + assert.deepEqual(this.getController('selection').isSelectAll(), false, 'select all state'); + assert.deepEqual(this.getSelectedRowKeys(), [2], 'visible rows are deselected'); + assert.deepEqual(this.option('selectedRowKeys'), [2], 'visible rows are deselected'); + }); -QUnit.test('Select all rows if autoExpandAll is true', function(assert) { + QUnit.test('Select all rows if autoExpandAll is true', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.autoExpandAll = true; + this.options.autoExpandAll = true; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.selectAll(); + // act + this.selectAll(); - // assert - assert.deepEqual(this.getSelectedRowKeys(), [1, 2], 'all visible rows are selected'); - assert.deepEqual(this.option('selectedRowKeys'), [1, 2], 'all visible rows are selected'); -}); + // assert + assert.deepEqual(this.getSelectedRowKeys(), [1, 2], 'all visible rows are selected'); + assert.deepEqual(this.option('selectedRowKeys'), [1, 2], 'all visible rows are selected'); + }); -QUnit.test('Select all rows if filter is applied', function(assert) { + QUnit.test('Select all rows if filter is applied', function(assert) { // arrange - const $testElement = $('#treeList'); - - this.options.dataSource.push({ id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }); - - this.options.expandNodesOnFiltering = true; - this.options.columns[0].filterValue = 'test2'; - - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.selectAll(); - - // assert - assert.deepEqual(this.getController('selection').isSelectAll(), true, 'select all state'); - assert.deepEqual(this.getSelectedRowKeys(), [1, 2], 'all visible rows are selected'); - assert.deepEqual(this.option('selectedRowKeys'), [1, 2], 'all visible rows are selected'); -}); - -// T861403 -[true, false].forEach(recursive => { - QUnit.test(`Select all rows if filter is applied, filterMode is matchOnly and recursive=${recursive}`, function(assert) { - // arrange const $testElement = $('#treeList'); - const selectedRowKeys = recursive ? [1, 2] : [2]; - this.options.filterMode = 'matchOnly'; + this.options.dataSource.push({ id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }); + + this.options.expandNodesOnFiltering = true; this.options.columns[0].filterValue = 'test2'; - this.options.selection = { - recursive, - mode: 'multiple' - }; this.setupTreeList(); this.rowsView.render($testElement); @@ -192,418 +166,444 @@ QUnit.test('Select all rows if filter is applied', function(assert) { // assert assert.deepEqual(this.getController('selection').isSelectAll(), true, 'select all state'); - assert.deepEqual(this.getSelectedRowKeys(), selectedRowKeys, 'all visible rows are selected'); - assert.deepEqual(this.option('selectedRowKeys'), selectedRowKeys, 'all visible rows are selected'); + assert.deepEqual(this.getSelectedRowKeys(), [1, 2], 'all visible rows are selected'); + assert.deepEqual(this.option('selectedRowKeys'), [1, 2], 'all visible rows are selected'); + }); - // act - this.option('filterValue', undefined); + // T861403 + [true, false].forEach(recursive => { + QUnit.test(`Select all rows if filter is applied, filterMode is matchOnly and recursive=${recursive}`, function(assert) { + // arrange + const $testElement = $('#treeList'); + const selectedRowKeys = recursive ? [1, 2] : [2]; + + this.options.filterMode = 'matchOnly'; + this.options.columns[0].filterValue = 'test2'; + this.options.selection = { + recursive, + mode: 'multiple' + }; + + this.setupTreeList(); + this.rowsView.render($testElement); + + // act + this.selectAll(); + + // assert + assert.deepEqual(this.getController('selection').isSelectAll(), true, 'select all state'); + assert.deepEqual(this.getSelectedRowKeys(), selectedRowKeys, 'all visible rows are selected'); + assert.deepEqual(this.option('selectedRowKeys'), selectedRowKeys, 'all visible rows are selected'); + + // act + this.option('filterValue', undefined); + + // assert + assert.deepEqual(this.getController('selection').isSelectAll(), true, 'select all state'); + assert.deepEqual(this.getSelectedRowKeys(), selectedRowKeys, 'all visible rows are selected'); + assert.deepEqual(this.option('selectedRowKeys'), selectedRowKeys, 'all visible rows are selected'); + }); - // assert - assert.deepEqual(this.getController('selection').isSelectAll(), true, 'select all state'); - assert.deepEqual(this.getSelectedRowKeys(), selectedRowKeys, 'all visible rows are selected'); - assert.deepEqual(this.option('selectedRowKeys'), selectedRowKeys, 'all visible rows are selected'); + QUnit.test(`Select and deselect all rows if filter is applied, filterMode is matchOnly and recursive=${recursive}`, function(assert) { + // arrange + const $testElement = $('#treeList'); + const selectedRowKeys = recursive ? [1, 2] : [2]; + + this.options.filterMode = 'matchOnly'; + this.options.columns[0].filterValue = 'test2'; + this.options.selection = { + recursive, + mode: 'multiple' + }; + + this.setupTreeList(); + this.rowsView.render($testElement); + + // act + this.selectAll(); + + // assert + assert.deepEqual(this.getController('selection').isSelectAll(), true, 'select all state'); + assert.deepEqual(this.getSelectedRowKeys(), selectedRowKeys, 'all visible rows are selected'); + assert.deepEqual(this.option('selectedRowKeys'), selectedRowKeys, 'all visible rows are selected'); + + // act + this.deselectAll(); + + // assert + assert.deepEqual(this.getController('selection').isSelectAll(), false, 'select all state'); + assert.deepEqual(this.getSelectedRowKeys(), [], 'all visible rows are selected'); + assert.deepEqual(this.option('selectedRowKeys'), [], 'all visible rows are selected'); + + // act + this.option('filterValue', undefined); + + // assert + assert.deepEqual(this.getController('selection').isSelectAll(), false, 'select all state'); + assert.deepEqual(this.getSelectedRowKeys(), [], 'all visible rows are selected'); + assert.deepEqual(this.option('selectedRowKeys'), [], 'all visible rows are selected'); + }); }); - QUnit.test(`Select and deselect all rows if filter is applied, filterMode is matchOnly and recursive=${recursive}`, function(assert) { - // arrange + QUnit.test('getSelectedRowKeys with non-recursive selection', function(assert) { + // arrange const $testElement = $('#treeList'); - const selectedRowKeys = recursive ? [1, 2] : [2]; - - this.options.filterMode = 'matchOnly'; - this.options.columns[0].filterValue = 'test2'; - this.options.selection = { - recursive, - mode: 'multiple' - }; + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, parentId: 4, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } + ]; + this.options.expandedRowKeys = [1, 4]; + this.options.selectedRowKeys = [1, 2, 4]; this.setupTreeList(); this.rowsView.render($testElement); - // act - this.selectAll(); + // act, assert + assert.deepEqual(this.getSelectedRowKeys(), [1, 2, 4], 'actual selection'); + assert.deepEqual(this.getSelectedRowKeys('excludeRecursive'), [1], 'only top'); + assert.deepEqual(this.getSelectedRowKeys('all'), [1, 2, 4], 'actual selection'); + assert.deepEqual(this.getSelectedRowKeys('leavesOnly'), [2], 'only leaves selected'); + }); - // assert - assert.deepEqual(this.getController('selection').isSelectAll(), true, 'select all state'); - assert.deepEqual(this.getSelectedRowKeys(), selectedRowKeys, 'all visible rows are selected'); - assert.deepEqual(this.option('selectedRowKeys'), selectedRowKeys, 'all visible rows are selected'); + QUnit.test('Checkboxes should be rendered in right place', function(assert) { + // arrange + const $testElement = $('#treeList'); - // act - this.deselectAll(); + this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always' }; - // assert - assert.deepEqual(this.getController('selection').isSelectAll(), false, 'select all state'); - assert.deepEqual(this.getSelectedRowKeys(), [], 'all visible rows are selected'); - assert.deepEqual(this.option('selectedRowKeys'), [], 'all visible rows are selected'); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.option('filterValue', undefined); + const $gridCell = $testElement.find('.dx-treelist-cell-expandable').eq(0); // assert - assert.deepEqual(this.getController('selection').isSelectAll(), false, 'select all state'); - assert.deepEqual(this.getSelectedRowKeys(), [], 'all visible rows are selected'); - assert.deepEqual(this.option('selectedRowKeys'), [], 'all visible rows are selected'); + assert.equal($gridCell.find('.dx-select-checkbox').length, 1, 'Select checkbox was rendered in right place'); + assert.ok($gridCell.find('.dx-select-checkbox').parent().hasClass('dx-treelist-icon-container'), 'Checkbox inside icon container'); }); -}); - -QUnit.test('getSelectedRowKeys with non-recursive selection', function(assert) { - // arrange - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, parentId: 4, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } - ]; - this.options.expandedRowKeys = [1, 4]; - this.options.selectedRowKeys = [1, 2, 4]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act, assert - assert.deepEqual(this.getSelectedRowKeys(), [1, 2, 4], 'actual selection'); - assert.deepEqual(this.getSelectedRowKeys('excludeRecursive'), [1], 'only top'); - assert.deepEqual(this.getSelectedRowKeys('all'), [1, 2, 4], 'actual selection'); - assert.deepEqual(this.getSelectedRowKeys('leavesOnly'), [2], 'only leaves selected'); -}); -QUnit.test('Checkboxes should be rendered in right place', function(assert) { + QUnit.test('Checkboxes should not be rendered if selection is not multiple', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always' }; + this.options.selection = { mode: 'single', showCheckBoxesMode: 'always' }; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - const $gridCell = $testElement.find('.dx-treelist-cell-expandable').eq(0); + const $gridCell = $testElement.find('.dx-treelist-cell-expandable').eq(0); - // assert - assert.equal($gridCell.find('.dx-select-checkbox').length, 1, 'Select checkbox was rendered in right place'); - assert.ok($gridCell.find('.dx-select-checkbox').parent().hasClass('dx-treelist-icon-container'), 'Checkbox inside icon container'); -}); + // assert + assert.equal($gridCell.find('.dx-select-checkbox').length, 0, 'Select checkbox was not rendered'); + }); -QUnit.test('Checkboxes should not be rendered if selection is not multiple', function(assert) { + QUnit.test('Click on select checkbox should works correctly', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.selection = { mode: 'single', showCheckBoxesMode: 'always' }; + this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always' }; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - const $gridCell = $testElement.find('.dx-treelist-cell-expandable').eq(0); + // act + const $selectCheckbox = $testElement.find('.dx-treelist-cell-expandable').eq(0).find('.dx-select-checkbox').eq(0); + $selectCheckbox.trigger('dxclick'); - // assert - assert.equal($gridCell.find('.dx-select-checkbox').length, 0, 'Select checkbox was not rendered'); -}); + // assert + assert.equal($selectCheckbox.dxCheckBox('instance').option('value'), true, 'Select checkbox value is OK'); + assert.deepEqual(this.option('selectedRowKeys'), [1], 'Right row is selected'); + assert.ok(this.dataController.items()[0].isSelected, 'Right row is selected'); + }); -QUnit.test('Click on select checkbox should works correctly', function(assert) { + QUnit.test('Click on selectAll checkbox should works correctly', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); + + this.options.showColumnHeaders = true; + this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always', allowSelectAll: true }; + this.setupTreeList(); + this.columnHeadersView.render($testElement); + this.rowsView.render($testElement); + + // act + const $checkbox = $('.dx-header-row').find('.dx-checkbox'); + $checkbox.trigger('dxclick'); + + // assert + assert.equal($checkbox.dxCheckBox('instance').option('value'), true, 'SelectAll checkbox value is OK'); + assert.deepEqual(this.option('selectedRowKeys'), [1], 'Right rows are selected'); + }); - this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always' }; + QUnit.test('Click on selectAll checkbox should works correctly when sorting is enabled', function(assert) { + // arrange + const $testElement = $('#treeList'); + const clock = sinon.useFakeTimers(); - this.setupTreeList(); - this.rowsView.render($testElement); + this.options.showColumnHeaders = true; + this.options.sorting = { + mode: 'single' + }; + this.options.columns[0].allowSorting = true; + this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always', allowSelectAll: true }; + this.setupTreeList(); + this.columnHeadersView.render($testElement); + this.rowsView.render($testElement); - // act - const $selectCheckbox = $testElement.find('.dx-treelist-cell-expandable').eq(0).find('.dx-select-checkbox').eq(0); - $selectCheckbox.trigger('dxclick'); + // act + const $checkbox = $('.dx-header-row').find('.dx-checkbox'); + $checkbox.trigger('dxclick'); + clock.tick(); - // assert - assert.equal($selectCheckbox.dxCheckBox('instance').option('value'), true, 'Select checkbox value is OK'); - assert.deepEqual(this.option('selectedRowKeys'), [1], 'Right row is selected'); - assert.ok(this.dataController.items()[0].isSelected, 'Right row is selected'); -}); + // assert + assert.equal($checkbox.dxCheckBox('instance').option('value'), true, 'SelectAll checkbox value is OK'); + assert.equal($testElement.find('tbody > tr > td').first().find('.dx-sort-up, .dx-sort-down').length, 0, 'sort not applied'); + clock.restore(); + }); -QUnit.test('Click on selectAll checkbox should works correctly', function(assert) { + QUnit.test('Click on selectAll checkbox should check row checkboxes', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.showColumnHeaders = true; - this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always', allowSelectAll: true }; - this.setupTreeList(); - this.columnHeadersView.render($testElement); - this.rowsView.render($testElement); + this.options.showColumnHeaders = true; + this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always', allowSelectAll: true }; + this.setupTreeList(); + this.columnHeadersView.render($testElement); + this.rowsView.render($testElement); - // act - const $checkbox = $('.dx-header-row').find('.dx-checkbox'); - $checkbox.trigger('dxclick'); + // act + const $checkbox = $('.dx-header-row').find('.dx-checkbox'); + $checkbox.trigger('dxclick'); - // assert - assert.equal($checkbox.dxCheckBox('instance').option('value'), true, 'SelectAll checkbox value is OK'); - assert.deepEqual(this.option('selectedRowKeys'), [1], 'Right rows are selected'); -}); + // assert + const $selectCheckbox = $testElement.find('.dx-treelist-cell-expandable').eq(0).find('.dx-select-checkbox').eq(0); + assert.equal($selectCheckbox.dxCheckBox('instance').option('value'), true, 'Select checkbox value is OK'); + }); -QUnit.test('Click on selectAll checkbox should works correctly when sorting is enabled', function(assert) { + QUnit.test('Reordering column, selection', function(assert) { // arrange - const $testElement = $('#treeList'); - const clock = sinon.useFakeTimers(); + const $testElement = $('#treeList'); + this.options.allowColumnReordering = true; + this.options.showColumnHeaders = true; + this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always', allowSelectAll: true }; + this.setupTreeList(); + this.columnHeadersView.render($testElement); + this.rowsView.render($testElement); - this.options.showColumnHeaders = true; - this.options.sorting = { - mode: 'single' - }; - this.options.columns[0].allowSorting = true; - this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always', allowSelectAll: true }; - this.setupTreeList(); - this.columnHeadersView.render($testElement); - this.rowsView.render($testElement); - - // act - const $checkbox = $('.dx-header-row').find('.dx-checkbox'); - $checkbox.trigger('dxclick'); - clock.tick(); - - // assert - assert.equal($checkbox.dxCheckBox('instance').option('value'), true, 'SelectAll checkbox value is OK'); - assert.equal($testElement.find('tbody > tr > td').first().find('.dx-sort-up, .dx-sort-down').length, 0, 'sort not applied'); - clock.restore(); -}); + // act + const $checkbox = $('.dx-header-row').find('.dx-checkbox'); + $checkbox.trigger('dxclick'); + this.columnsController.moveColumn(0, 3); + + // assert + const $selectCheckbox = $testElement.find('.dx-treelist-cell-expandable').eq(0).find('.dx-select-checkbox').eq(0); + assert.equal($selectCheckbox.dxCheckBox('instance').option('value'), true, 'Select checkbox value is OK'); + }); -QUnit.test('Click on selectAll checkbox should check row checkboxes', function(assert) { + QUnit.test('Checking state selectAll checkbox - deselect row after select All', function(assert) { // arrange - const $testElement = $('#treeList'); + let $selectAllCheckBox; + const $testElement = $('#treeList'); - this.options.showColumnHeaders = true; - this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always', allowSelectAll: true }; - this.setupTreeList(); - this.columnHeadersView.render($testElement); - this.rowsView.render($testElement); + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 2, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, parentId: 2, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) }, + { id: 6, parentId: 1, field1: 'test6', field2: 6, field3: new Date(2002, 1, 6) }, + { id: 7, parentId: 6, field1: 'test7', field2: 7, field3: new Date(2002, 1, 7) }, + { id: 8, parentId: 6, field1: 'test8', field2: 8, field3: new Date(2002, 1, 8) }, + { id: 9, parentId: 6, field1: 'test9', field2: 9, field3: new Date(2002, 1, 9) }, + ]; + this.options.expandedRowKeys = [1]; + this.options.showColumnHeaders = true; + this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always', allowSelectAll: true }; + this.setupTreeList(); + this.columnHeadersView.render($testElement); + this.rowsView.render($testElement); + this.selectAll(); - // act - const $checkbox = $('.dx-header-row').find('.dx-checkbox'); - $checkbox.trigger('dxclick'); + // assert + $selectAllCheckBox = $testElement.find('.dx-header-row').children().first().find('.dx-select-checkbox'); + assert.ok($selectAllCheckBox.hasClass('dx-checkbox-checked'), 'selectAll checkbox is checked'); - // assert - const $selectCheckbox = $testElement.find('.dx-treelist-cell-expandable').eq(0).find('.dx-select-checkbox').eq(0); - assert.equal($selectCheckbox.dxCheckBox('instance').option('value'), true, 'Select checkbox value is OK'); -}); + // act + this.deselectRows(2); -QUnit.test('Reordering column, selection', function(assert) { - // arrange - const $testElement = $('#treeList'); - this.options.allowColumnReordering = true; - this.options.showColumnHeaders = true; - this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always', allowSelectAll: true }; - this.setupTreeList(); - this.columnHeadersView.render($testElement); - this.rowsView.render($testElement); - - // act - const $checkbox = $('.dx-header-row').find('.dx-checkbox'); - $checkbox.trigger('dxclick'); - this.columnsController.moveColumn(0, 3); - - // assert - const $selectCheckbox = $testElement.find('.dx-treelist-cell-expandable').eq(0).find('.dx-select-checkbox').eq(0); - assert.equal($selectCheckbox.dxCheckBox('instance').option('value'), true, 'Select checkbox value is OK'); -}); + // assert + $selectAllCheckBox = $testElement.find('.dx-header-row').children().first().find('.dx-select-checkbox'); + assert.ok($selectAllCheckBox.hasClass('dx-checkbox-indeterminate'), 'selectAll checkbox is indeterminate'); + }); -QUnit.test('Checking state selectAll checkbox - deselect row after select All', function(assert) { + QUnit.test('Checking state selectAll checkbox - select all when there is filter', function(assert) { // arrange - let $selectAllCheckBox; - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 2, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, parentId: 2, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) }, - { id: 6, parentId: 1, field1: 'test6', field2: 6, field3: new Date(2002, 1, 6) }, - { id: 7, parentId: 6, field1: 'test7', field2: 7, field3: new Date(2002, 1, 7) }, - { id: 8, parentId: 6, field1: 'test8', field2: 8, field3: new Date(2002, 1, 8) }, - { id: 9, parentId: 6, field1: 'test9', field2: 9, field3: new Date(2002, 1, 9) }, - ]; - this.options.expandedRowKeys = [1]; - this.options.showColumnHeaders = true; - this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always', allowSelectAll: true }; - this.setupTreeList(); - this.columnHeadersView.render($testElement); - this.rowsView.render($testElement); - this.selectAll(); - - // assert - $selectAllCheckBox = $testElement.find('.dx-header-row').children().first().find('.dx-select-checkbox'); - assert.ok($selectAllCheckBox.hasClass('dx-checkbox-checked'), 'selectAll checkbox is checked'); - - // act - this.deselectRows(2); - - // assert - $selectAllCheckBox = $testElement.find('.dx-header-row').children().first().find('.dx-select-checkbox'); - assert.ok($selectAllCheckBox.hasClass('dx-checkbox-indeterminate'), 'selectAll checkbox is indeterminate'); -}); + let $selectAllCheckBox; + const $testElement = $('#treeList'); -QUnit.test('Checking state selectAll checkbox - select all when there is filter', function(assert) { - // arrange - let $selectAllCheckBox; - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 2, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, parentId: 2, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) }, - { id: 6, parentId: 1, field1: 'test6', field2: 6, field3: new Date(2002, 1, 6) }, - { id: 7, parentId: 6, field1: 'test7', field2: 7, field3: new Date(2002, 1, 7) }, - { id: 8, parentId: 6, field1: 'test8', field2: 8, field3: new Date(2002, 1, 8) }, - { id: 9, parentId: 6, field1: 'test9', field2: 9, field3: new Date(2002, 1, 9) }, - ]; - this.options.showColumnHeaders = true; - this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always', allowSelectAll: true }; - this.options.columns[0].filterValue = 'test5'; - this.setupTreeList(); - this.columnHeadersView.render($testElement); - this.rowsView.render($testElement); - - // act - this.selectAll(); - - // assert - $selectAllCheckBox = $testElement.find('.dx-header-row').children().first().find('.dx-select-checkbox'); - assert.ok($selectAllCheckBox.hasClass('dx-checkbox-checked'), 'selectAll checkbox is checked'); -}); + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 2, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, parentId: 2, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) }, + { id: 6, parentId: 1, field1: 'test6', field2: 6, field3: new Date(2002, 1, 6) }, + { id: 7, parentId: 6, field1: 'test7', field2: 7, field3: new Date(2002, 1, 7) }, + { id: 8, parentId: 6, field1: 'test8', field2: 8, field3: new Date(2002, 1, 8) }, + { id: 9, parentId: 6, field1: 'test9', field2: 9, field3: new Date(2002, 1, 9) }, + ]; + this.options.showColumnHeaders = true; + this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always', allowSelectAll: true }; + this.options.columns[0].filterValue = 'test5'; + this.setupTreeList(); + this.columnHeadersView.render($testElement); + this.rowsView.render($testElement); + + // act + this.selectAll(); + + // assert + $selectAllCheckBox = $testElement.find('.dx-header-row').children().first().find('.dx-select-checkbox'); + assert.ok($selectAllCheckBox.hasClass('dx-checkbox-checked'), 'selectAll checkbox is checked'); + }); -QUnit.test('Not select row when click by expanding icon', function(assert) { + QUnit.test('Not select row when click by expanding icon', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - $testElement.find('tbody > tr').first().find('.dx-treelist-collapsed').trigger('dxclick'); + // act + $testElement.find('tbody > tr').first().find('.dx-treelist-collapsed').trigger('dxclick'); - // assert - assert.equal(this.option('selectedRowKeys'), undefined, 'checking the \'selectedRowKeys\' option - should be empty'); - assert.notOk(this.dataController.items()[0].isSelected, 'row isn\'t selected'); -}); + // assert + assert.equal(this.option('selectedRowKeys'), undefined, 'checking the \'selectedRowKeys\' option - should be empty'); + assert.notOk(this.dataController.items()[0].isSelected, 'row isn\'t selected'); + }); -QUnit.testInActiveWindow('Focused border is not displayed around expandable cell when row is selected', function(assert) { + QUnit.testInActiveWindow('Focused border is not displayed around expandable cell when row is selected', function(assert) { // arrange - const clock = sinon.useFakeTimers(); - const $testElement = $('#treeList'); + const clock = sinon.useFakeTimers(); + const $testElement = $('#treeList'); - this.element = function() { - return $testElement; - }; + this.element = function() { + return $testElement; + }; - this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always' }; + this.options.selection = { mode: 'multiple', showCheckBoxesMode: 'always' }; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // act - const $expandableCell = $testElement.find('.dx-treelist-cell-expandable').first(); - const $selectCheckbox = $expandableCell.find('.dx-select-checkbox').first(); + // act + const $expandableCell = $testElement.find('.dx-treelist-cell-expandable').first(); + const $selectCheckbox = $expandableCell.find('.dx-select-checkbox').first(); - $selectCheckbox.focus(); - clock.tick(); + $selectCheckbox.focus(); + clock.tick(); - // assert - assert.ok(!$expandableCell.hasClass('dx-focused')); - clock.restore(); -}); + // assert + assert.ok(!$expandableCell.hasClass('dx-focused')); + clock.restore(); + }); -// T742205 -QUnit.test('The load method should not be called on an attempt to select loaded nodes when they are collapsed', function(assert) { + // T742205 + QUnit.test('The load method should not be called on an attempt to select loaded nodes when they are collapsed', function(assert) { // arrange - const $testElement = $('#treeList'); - const store = new ArrayStore([ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) } - ]); - const load = sinon.spy((loadOptions) => { - return store.load(loadOptions).promise(); - }); + const $testElement = $('#treeList'); + const store = new ArrayStore([ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) } + ]); + const load = sinon.spy((loadOptions) => { + return store.load(loadOptions).promise(); + }); - this.options.cacheEnabled = true; - this.options.expandedRowKeys = [1]; - this.options.remoteOperations = { filtering: true }; - this.options.dataSource = { - load: load - }; + this.options.cacheEnabled = true; + this.options.expandedRowKeys = [1]; + this.options.remoteOperations = { filtering: true }; + this.options.dataSource = { + load: load + }; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // assert - assert.strictEqual(this.getVisibleRows().length, 2, 'row count'); + // assert + assert.strictEqual(this.getVisibleRows().length, 2, 'row count'); - this.collapseRow(1); + this.collapseRow(1); - // assert - assert.strictEqual(this.getVisibleRows().length, 1, 'row count'); + // assert + assert.strictEqual(this.getVisibleRows().length, 1, 'row count'); - // act - load.reset(); - this.selectRows([2]); + // act + load.reset(); + this.selectRows([2]); - // assert - assert.strictEqual(load.callCount, 0, 'load isn\'t called'); -}); + // assert + assert.strictEqual(load.callCount, 0, 'load isn\'t called'); + }); -// T742205, T751539 -QUnit.test('selection for nested node should works', function(assert) { + // T742205, T751539 + QUnit.test('selection for nested node should works', function(assert) { // arrange - const $testElement = $('#treeList'); + const $testElement = $('#treeList'); - this.options.cacheEnabled = true; - this.options.expandedRowKeys = [1]; - this.options.dataSource = [ - { id: 1, field1: 'test1' }, - { id: 2, field1: 'test2' }, - { id: 3, parentId: 1, field1: 'test3' } - ]; + this.options.cacheEnabled = true; + this.options.expandedRowKeys = [1]; + this.options.dataSource = [ + { id: 1, field1: 'test1' }, + { id: 2, field1: 'test2' }, + { id: 3, parentId: 1, field1: 'test3' } + ]; - this.setupTreeList(); - this.rowsView.render($testElement); + this.setupTreeList(); + this.rowsView.render($testElement); - // assert - this.selectionController.changeItemSelection(1); + // assert + this.selectionController.changeItemSelection(1); - // assert - assert.deepEqual(this.getSelectedRowKeys(), [3], 'selected row keys'); - assert.strictEqual(this.getVisibleRows()[1].isSelected, true, 'row 1 is selected'); -}); + // assert + assert.deepEqual(this.getSelectedRowKeys(), [3], 'selected row keys'); + assert.strictEqual(this.getVisibleRows()[1].isSelected, true, 'row 1 is selected'); + }); -// T858312 -QUnit.test('The getSelectedRowsData method should work correctly when calling navigateToRow in the onNodesInitialized event', function(assert) { + // T858312 + QUnit.test('The getSelectedRowsData method should work correctly when calling navigateToRow in the onNodesInitialized event', function(assert) { // arrange - const $testElement = $('#treeList'); - const clock = sinon.useFakeTimers(); + const $testElement = $('#treeList'); + const clock = sinon.useFakeTimers(); - this.options.loadingTimeout = 30; - this.options.autoNavigateToFocusedRow = true; - this.options.onNodesInitialized = (e) => { - this.navigateToRow(2); - }; + this.options.loadingTimeout = 30; + this.options.autoNavigateToFocusedRow = true; + this.options.onNodesInitialized = (e) => { + this.navigateToRow(2); + }; - this.setupTreeList(); - clock.tick(60); - this.rowsView.render($testElement); + this.setupTreeList(); + clock.tick(60); + this.rowsView.render($testElement); - // assert - assert.ok(this.getNodeByKey(1), 'node with key "1" exists'); - assert.ok(this.getNodeByKey(2), 'node with key "2" exists'); + // assert + assert.ok(this.getNodeByKey(1), 'node with key "1" exists'); + assert.ok(this.getNodeByKey(2), 'node with key "2" exists'); - // act - this.selectRows(2); + // act + this.selectRows(2); - // assert - assert.deepEqual(this.getSelectedRowKeys(), [2], 'getSelectedRowKeys'); - assert.deepEqual(this.option('selectedRowKeys'), [2], 'selectedRowKeys'); - assert.deepEqual(this.getSelectedRowsData(), [{ id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }], 'getSelectedRowsData'); + // assert + assert.deepEqual(this.getSelectedRowKeys(), [2], 'getSelectedRowKeys'); + assert.deepEqual(this.option('selectedRowKeys'), [2], 'selectedRowKeys'); + assert.deepEqual(this.getSelectedRowsData(), [{ id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }], 'getSelectedRowsData'); - clock.restore(); + clock.restore(); + }); }); - QUnit.module('Recursive selection', { beforeEach: function() { setupModule.call(this); @@ -613,828 +613,830 @@ QUnit.module('Recursive selection', { }; }, afterEach: teardownModule -}); +}, () => { -QUnit.test('Selecting row', function(assert) { + QUnit.test('Selecting row', function(assert) { // arrange - let items; - const $testElement = $('#treeList'); + let items; + const $testElement = $('#treeList'); - this.options.expandedRowKeys = [1]; - this.setupTreeList(); - this.rowsView.render($testElement); + this.options.expandedRowKeys = [1]; + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.selectRows(1); + // act + this.selectRows(1); - // assert - items = this.dataController.items(); - assert.deepEqual(this.option('selectedRowKeys'), [1], 'selected row keys'); - assert.ok(items[0].isSelected, 'first item is selected'); - assert.ok(items[1].isSelected, 'second item is selected'); -}); + // assert + items = this.dataController.items(); + assert.deepEqual(this.option('selectedRowKeys'), [1], 'selected row keys'); + assert.ok(items[0].isSelected, 'first item is selected'); + assert.ok(items[1].isSelected, 'second item is selected'); + }); -QUnit.test('Deselecting row', function(assert) { + QUnit.test('Deselecting row', function(assert) { // arrange - let items; - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } - ]; - - this.options.expandedRowKeys = [1]; - this.options.selectedRowKeys = [1]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.deselectRows(2); - - // assert - items = this.dataController.items(); - assert.deepEqual(this.option('selectedRowKeys'), [3, 4], 'selected row keys'); - assert.strictEqual(items[0].isSelected, undefined, 'selection state of the first item is indeterminate'); - assert.notOk(items[1].isSelected, 'second item isn\'t selected'); - assert.ok(items[2].isSelected, 'first item is selected'); - assert.ok(items[3].isSelected, 'second item is selected'); -}); + let items; + const $testElement = $('#treeList'); -QUnit.test('Selecting a row when several of his children are selected', function(assert) { - // arrange - let items; - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } - ]; - this.options.expandedRowKeys = [1]; - this.options.selectedRowKeys = [3, 4]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.selectRows(1); - - // assert - items = this.dataController.items(); - assert.deepEqual(this.option('selectedRowKeys'), [1], 'selected row keys'); - assert.ok(items[0].isSelected, 'first item is selected'); - assert.ok(items[1].isSelected, 'second item is selected'); - assert.ok(items[2].isSelected, 'third item is selected'); - assert.ok(items[3].isSelected, 'fourth item is selected'); -}); + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } + ]; -QUnit.test('Deselecting the row when all children are selected', function(assert) { - // arrange - let items; - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) } - ]; - this.options.expandedRowKeys = [1]; - this.options.selectedRowKeys = [2, 3]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.deselectRows(1); - - // assert - items = this.dataController.items(); - assert.deepEqual(this.option('selectedRowKeys'), [], 'selected row keys'); - assert.notOk(items[0].isSelected, 'first item isn\'t selected'); - assert.notOk(items[1].isSelected, 'second item isn\'t selected'); - assert.notOk(items[2].isSelected, 'third item isn\'t selected'); -}); + this.options.expandedRowKeys = [1]; + this.options.selectedRowKeys = [1]; + this.setupTreeList(); + this.rowsView.render($testElement); -QUnit.test('Select All', function(assert) { - // arrange - let items; - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, field1: 'test5', field2: 5, field3: new Date(2001, 0, 5) } - ]; - this.options.expandedRowKeys = [1]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.selectAll(); - - // assert - items = this.dataController.items(); - assert.deepEqual(this.option('selectedRowKeys'), [1, 5], 'selected row keys'); - assert.ok(items[0].isSelected, 'first item is selected'); - assert.ok(items[1].isSelected, 'second item is selected'); - assert.ok(items[2].isSelected, 'third item is selected'); - assert.ok(items[3].isSelected, 'fourth item is selected'); - assert.ok(items[4].isSelected, 'fifth item is selected'); -}); + // act + this.deselectRows(2); -QUnit.test('Select All when several rows are selected', function(assert) { - // arrange - let items; - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, field1: 'test5', field2: 5, field3: new Date(2001, 0, 5) } - ]; - this.options.expandedRowKeys = [1]; - this.options.selectedRowKeys = [2, 3]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.selectAll(); - - // assert - items = this.dataController.items(); - assert.deepEqual(this.option('selectedRowKeys'), [1, 5], 'selected row keys'); - assert.ok(items[0].isSelected, 'first item is selected'); - assert.ok(items[1].isSelected, 'second item is selected'); - assert.ok(items[2].isSelected, 'third item is selected'); - assert.ok(items[3].isSelected, 'fourth item is selected'); - assert.ok(items[4].isSelected, 'fifth item is selected'); -}); + // assert + items = this.dataController.items(); + assert.deepEqual(this.option('selectedRowKeys'), [3, 4], 'selected row keys'); + assert.strictEqual(items[0].isSelected, undefined, 'selection state of the first item is indeterminate'); + assert.notOk(items[1].isSelected, 'second item isn\'t selected'); + assert.ok(items[2].isSelected, 'first item is selected'); + assert.ok(items[3].isSelected, 'second item is selected'); + }); -QUnit.test('Deselect All', function(assert) { + QUnit.test('Selecting a row when several of his children are selected', function(assert) { // arrange - let items; - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } - ]; - this.options.expandedRowKeys = [1]; - this.options.selectedRowKeys = [2, 3, 4]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.deselectAll(); - - // assert - items = this.dataController.items(); - assert.deepEqual(this.option('selectedRowKeys'), [], 'selected row keys'); - assert.notOk(items[0].isSelected, 'first item isn\'t selected'); - assert.notOk(items[1].isSelected, 'second item isn\'t selected'); - assert.notOk(items[2].isSelected, 'third item isn\'t selected'); - assert.notOk(items[3].isSelected, 'fourth item isn\'t selected'); -}); + let items; + const $testElement = $('#treeList'); + + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } + ]; + this.options.expandedRowKeys = [1]; + this.options.selectedRowKeys = [3, 4]; + this.setupTreeList(); + this.rowsView.render($testElement); + + // act + this.selectRows(1); + + // assert + items = this.dataController.items(); + assert.deepEqual(this.option('selectedRowKeys'), [1], 'selected row keys'); + assert.ok(items[0].isSelected, 'first item is selected'); + assert.ok(items[1].isSelected, 'second item is selected'); + assert.ok(items[2].isSelected, 'third item is selected'); + assert.ok(items[3].isSelected, 'fourth item is selected'); + }); -QUnit.test('Checking arguments of the \'onSelectionChanged\' event when select row', function(assert) { + QUnit.test('Deselecting the row when all children are selected', function(assert) { // arrange - const selectionChangedArgs = []; - const $testElement = $('#treeList'); - const items = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } - ]; - - this.options.dataSource = items; - this.options.expandedRowKeys = [1]; - this.options.selectedRowKeys = [2]; - this.options.onSelectionChanged = function(e) { - selectionChangedArgs.push(e); - }; - this.setupTreeList(); - this.rowsView.render($testElement); + let items; + const $testElement = $('#treeList'); - assert.deepEqual(this.option('selectedRowKeys'), [2], 'selected row keys'); + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) } + ]; + this.options.expandedRowKeys = [1]; + this.options.selectedRowKeys = [2, 3]; + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.selectRows(1); + // act + this.deselectRows(1); - // assert - assert.strictEqual(selectionChangedArgs.length, 1, 'count call \'onSelectionChanged\' event'); - assert.deepEqual(selectionChangedArgs[0].selectedRowKeys, [1], 'selected row keys'); - assert.deepEqual(selectionChangedArgs[0].selectedRowsData, [items[0]], 'selected rows data'); - assert.deepEqual(selectionChangedArgs[0].currentSelectedRowKeys, [1], 'current selected row keys'); - assert.deepEqual(selectionChangedArgs[0].currentDeselectedRowKeys, [], 'current deselected row keys'); -}); + // assert + items = this.dataController.items(); + assert.deepEqual(this.option('selectedRowKeys'), [], 'selected row keys'); + assert.notOk(items[0].isSelected, 'first item isn\'t selected'); + assert.notOk(items[1].isSelected, 'second item isn\'t selected'); + assert.notOk(items[2].isSelected, 'third item isn\'t selected'); + }); -QUnit.test('Checking arguments of the \'onSelectionChanged\' event when deselect row', function(assert) { + QUnit.test('Select All', function(assert) { // arrange - const selectionChangedArgs = []; - const $testElement = $('#treeList'); - const items = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } - ]; - - this.options.dataSource = items; - this.options.expandedRowKeys = [1]; - this.options.selectedRowKeys = [1]; - this.options.onSelectionChanged = function(e) { - selectionChangedArgs.push(e); - }; - this.setupTreeList(); - this.rowsView.render($testElement); + let items; + const $testElement = $('#treeList'); - assert.deepEqual(this.option('selectedRowKeys'), [1], 'selected row keys'); + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, field1: 'test5', field2: 5, field3: new Date(2001, 0, 5) } + ]; + this.options.expandedRowKeys = [1]; + this.setupTreeList(); + this.rowsView.render($testElement); - // act - this.deselectRows(2); + // act + this.selectAll(); - // assert - assert.strictEqual(selectionChangedArgs.length, 1, 'count call \'onSelectionChanged\' event'); - assert.deepEqual(selectionChangedArgs[0].selectedRowKeys, [3, 4], 'selected row keys'); - assert.deepEqual(selectionChangedArgs[0].selectedRowsData, [items[2], items[3]], 'selected rows data'); - assert.deepEqual(selectionChangedArgs[0].currentSelectedRowKeys, [], 'current selected row keys'); - assert.deepEqual(selectionChangedArgs[0].currentDeselectedRowKeys, [2], 'current deselected row keys'); -}); + // assert + items = this.dataController.items(); + assert.deepEqual(this.option('selectedRowKeys'), [1, 5], 'selected row keys'); + assert.ok(items[0].isSelected, 'first item is selected'); + assert.ok(items[1].isSelected, 'second item is selected'); + assert.ok(items[2].isSelected, 'third item is selected'); + assert.ok(items[3].isSelected, 'fourth item is selected'); + assert.ok(items[4].isSelected, 'fifth item is selected'); + }); -QUnit.test('Checking arguments of the \'onSelectionChanged\' event when select/deselect all rows', function(assert) { + QUnit.test('Select All when several rows are selected', function(assert) { // arrange - const selectionChangedArgs = []; - const $testElement = $('#treeList'); - const items = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } - ]; - - this.options.dataSource = items; - this.options.expandedRowKeys = [1]; - this.options.onSelectionChanged = function(e) { - selectionChangedArgs.push(e); - }; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.selectAll(); - - // assert - assert.strictEqual(selectionChangedArgs.length, 1, 'count call \'onSelectionChanged\' event'); - assert.deepEqual(selectionChangedArgs[0].selectedRowKeys, [1], 'selected row keys'); - assert.deepEqual(selectionChangedArgs[0].selectedRowsData, [items[0]], 'selected rows data'); - assert.deepEqual(selectionChangedArgs[0].currentSelectedRowKeys, [1], 'current selected row keys'); - assert.deepEqual(selectionChangedArgs[0].currentDeselectedRowKeys, [], 'current deselected row keys'); - - // act - this.deselectAll(); - - // assert - assert.strictEqual(selectionChangedArgs.length, 2, 'count call \'onSelectionChanged\' event'); - assert.deepEqual(selectionChangedArgs[1].selectedRowKeys, [], 'selected row keys'); - assert.deepEqual(selectionChangedArgs[1].selectedRowsData, [], 'selected rows data'); - assert.deepEqual(selectionChangedArgs[1].currentSelectedRowKeys, [], 'current selected row keys'); - assert.deepEqual(selectionChangedArgs[1].currentDeselectedRowKeys, [1], 'current deselected row keys'); -}); + let items; + const $testElement = $('#treeList'); + + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, field1: 'test5', field2: 5, field3: new Date(2001, 0, 5) } + ]; + this.options.expandedRowKeys = [1]; + this.options.selectedRowKeys = [2, 3]; + this.setupTreeList(); + this.rowsView.render($testElement); + + // act + this.selectAll(); + + // assert + items = this.dataController.items(); + assert.deepEqual(this.option('selectedRowKeys'), [1, 5], 'selected row keys'); + assert.ok(items[0].isSelected, 'first item is selected'); + assert.ok(items[1].isSelected, 'second item is selected'); + assert.ok(items[2].isSelected, 'third item is selected'); + assert.ok(items[3].isSelected, 'fourth item is selected'); + assert.ok(items[4].isSelected, 'fifth item is selected'); + }); -QUnit.test('getSelectedRowKeys with default parameter', function(assert) { + QUnit.test('Deselect All', function(assert) { // arrange - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, parentId: 4, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } - ]; - this.options.expandedRowKeys = [1]; - this.options.selectedRowKeys = [2, 4]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act, assert - assert.deepEqual(this.getSelectedRowKeys(), [2, 4], 'actual selection'); // deprecated in 18.1 -}); + let items; + const $testElement = $('#treeList'); + + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } + ]; + this.options.expandedRowKeys = [1]; + this.options.selectedRowKeys = [2, 3, 4]; + this.setupTreeList(); + this.rowsView.render($testElement); + + // act + this.deselectAll(); -QUnit.test('getSelectedRowKeys with \'leavesOnly\' parameter', function(assert) { + // assert + items = this.dataController.items(); + assert.deepEqual(this.option('selectedRowKeys'), [], 'selected row keys'); + assert.notOk(items[0].isSelected, 'first item isn\'t selected'); + assert.notOk(items[1].isSelected, 'second item isn\'t selected'); + assert.notOk(items[2].isSelected, 'third item isn\'t selected'); + assert.notOk(items[3].isSelected, 'fourth item isn\'t selected'); + }); + + QUnit.test('Checking arguments of the \'onSelectionChanged\' event when select row', function(assert) { // arrange - sinon.spy(errors, 'log'); - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, parentId: 4, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } - ]; - this.options.expandedRowKeys = [1]; - this.options.selectedRowKeys = [2, 4]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act, assert - assert.deepEqual(this.getSelectedRowKeys(true), [2, 5], 'only leaves selected'); // deprecated in 18.1 - assert.equal(errors.log.lastCall.args[0], 'W0002', 'Warning is raised'); - - assert.deepEqual(this.getSelectedRowKeys('leavesOnly'), [2, 5], 'only leaves selected'); - assert.equal(errors.log.callCount, 1, 'Warning is raised one time'); - - errors.log.restore(); -}); + const selectionChangedArgs = []; + const $testElement = $('#treeList'); + const items = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } + ]; + + this.options.dataSource = items; + this.options.expandedRowKeys = [1]; + this.options.selectedRowKeys = [2]; + this.options.onSelectionChanged = function(e) { + selectionChangedArgs.push(e); + }; + this.setupTreeList(); + this.rowsView.render($testElement); -QUnit.test('getSelectedRowKeys with \'all\' parameter', function(assert) { + assert.deepEqual(this.option('selectedRowKeys'), [2], 'selected row keys'); + + // act + this.selectRows(1); + + // assert + assert.strictEqual(selectionChangedArgs.length, 1, 'count call \'onSelectionChanged\' event'); + assert.deepEqual(selectionChangedArgs[0].selectedRowKeys, [1], 'selected row keys'); + assert.deepEqual(selectionChangedArgs[0].selectedRowsData, [items[0]], 'selected rows data'); + assert.deepEqual(selectionChangedArgs[0].currentSelectedRowKeys, [1], 'current selected row keys'); + assert.deepEqual(selectionChangedArgs[0].currentDeselectedRowKeys, [], 'current deselected row keys'); + }); + + QUnit.test('Checking arguments of the \'onSelectionChanged\' event when deselect row', function(assert) { // arrange - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, parentId: 4, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } - ]; - this.options.expandedRowKeys = [1, 4]; - this.options.selectedRowKeys = [2, 3, 4]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act, assert - assert.deepEqual(this.getSelectedRowKeys('all'), [1, 2, 3, 4, 5], 'all selected items'); -}); + const selectionChangedArgs = []; + const $testElement = $('#treeList'); + const items = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } + ]; -QUnit.test('getSelectedRowKeys with \'excludeRecursive\' parameter', function(assert) { + this.options.dataSource = items; + this.options.expandedRowKeys = [1]; + this.options.selectedRowKeys = [1]; + this.options.onSelectionChanged = function(e) { + selectionChangedArgs.push(e); + }; + this.setupTreeList(); + this.rowsView.render($testElement); + + assert.deepEqual(this.option('selectedRowKeys'), [1], 'selected row keys'); + + // act + this.deselectRows(2); + + // assert + assert.strictEqual(selectionChangedArgs.length, 1, 'count call \'onSelectionChanged\' event'); + assert.deepEqual(selectionChangedArgs[0].selectedRowKeys, [3, 4], 'selected row keys'); + assert.deepEqual(selectionChangedArgs[0].selectedRowsData, [items[2], items[3]], 'selected rows data'); + assert.deepEqual(selectionChangedArgs[0].currentSelectedRowKeys, [], 'current selected row keys'); + assert.deepEqual(selectionChangedArgs[0].currentDeselectedRowKeys, [2], 'current deselected row keys'); + }); + + QUnit.test('Checking arguments of the \'onSelectionChanged\' event when select/deselect all rows', function(assert) { // arrange - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, parentId: 4, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) }, - { id: 6, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 7, parentId: 6, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - ]; - this.options.expandedRowKeys = [1, 4]; - this.options.selectedRowKeys = [2, 5, 7]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act, assert - assert.deepEqual(this.getSelectedRowKeys('excludeRecursive'), [2, 4, 6], 'all selected items'); -}); + const selectionChangedArgs = []; + const $testElement = $('#treeList'); + const items = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } + ]; + + this.options.dataSource = items; + this.options.expandedRowKeys = [1]; + this.options.onSelectionChanged = function(e) { + selectionChangedArgs.push(e); + }; + this.setupTreeList(); + this.rowsView.render($testElement); + + // act + this.selectAll(); + + // assert + assert.strictEqual(selectionChangedArgs.length, 1, 'count call \'onSelectionChanged\' event'); + assert.deepEqual(selectionChangedArgs[0].selectedRowKeys, [1], 'selected row keys'); + assert.deepEqual(selectionChangedArgs[0].selectedRowsData, [items[0]], 'selected rows data'); + assert.deepEqual(selectionChangedArgs[0].currentSelectedRowKeys, [1], 'current selected row keys'); + assert.deepEqual(selectionChangedArgs[0].currentDeselectedRowKeys, [], 'current deselected row keys'); -QUnit.test('getSelectedRowsData with mode parameter calls getSelectedRowKeys', function(assert) { + // act + this.deselectAll(); + + // assert + assert.strictEqual(selectionChangedArgs.length, 2, 'count call \'onSelectionChanged\' event'); + assert.deepEqual(selectionChangedArgs[1].selectedRowKeys, [], 'selected row keys'); + assert.deepEqual(selectionChangedArgs[1].selectedRowsData, [], 'selected rows data'); + assert.deepEqual(selectionChangedArgs[1].currentSelectedRowKeys, [], 'current selected row keys'); + assert.deepEqual(selectionChangedArgs[1].currentDeselectedRowKeys, [1], 'current deselected row keys'); + }); + + QUnit.test('getSelectedRowKeys with default parameter', function(assert) { // arrange - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) } - ]; - this.options.expandedRowKeys = [1]; - this.options.selectedRowKeys = [3]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.selectionController.getSelectedRowKeys = sinon.spy(); - this.getSelectedRowsData('all'); - - // assert - assert.equal(this.selectionController.getSelectedRowKeys.callCount, 1, 'getSelectedRowKeys is called'); - assert.equal(this.selectionController.getSelectedRowKeys.args[0], 'all', 'getSelectedRowKeys is called with a mode parameter'); -}); + const $testElement = $('#treeList'); + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, parentId: 4, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } + ]; + this.options.expandedRowKeys = [1]; + this.options.selectedRowKeys = [2, 4]; + this.setupTreeList(); + this.rowsView.render($testElement); -QUnit.test('getSelectedRowsData with mode parameter when key has no data', function(assert) { - // arrange, act - const clock = sinon.useFakeTimers(); - const $testElement = $('#treeList'); - const data = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) } - ]; - - this.options.dataSource = { - load: function() { - const d = $.Deferred(); - - setTimeout(function() { - d.resolve(data); - }, 100); - - return d.promise(); - } - }; - this.options.expandedRowKeys = [1]; - this.options.selectedRowKeys = [1]; - this.setupTreeList(); - this.rowsView.render($testElement); + // act, assert + assert.deepEqual(this.getSelectedRowKeys(), [2, 4], 'actual selection'); // deprecated in 18.1 + }); - // assert - assert.deepEqual(this.getSelectedRowsData('leavesOnly'), [], 'empty data'); + QUnit.test('getSelectedRowKeys with \'leavesOnly\' parameter', function(assert) { + // arrange + sinon.spy(errors, 'log'); + const $testElement = $('#treeList'); - // act - clock.tick(100); + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, parentId: 4, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } + ]; + this.options.expandedRowKeys = [1]; + this.options.selectedRowKeys = [2, 4]; + this.setupTreeList(); + this.rowsView.render($testElement); - // assert - assert.equal(this.getSelectedRowsData('leavesOnly').length, 2, '2 nodes are returned'); - assert.deepEqual(this.getSelectedRowsData('leavesOnly')[0], data[1], 'first child'); - assert.deepEqual(this.getSelectedRowsData('leavesOnly')[1], data[2], 'second child'); + // act, assert + assert.deepEqual(this.getSelectedRowKeys(true), [2, 5], 'only leaves selected'); // deprecated in 18.1 + assert.equal(errors.log.lastCall.args[0], 'W0002', 'Warning is raised'); - clock.restore(); -}); + assert.deepEqual(this.getSelectedRowKeys('leavesOnly'), [2, 5], 'only leaves selected'); + assert.equal(errors.log.callCount, 1, 'Warning is raised one time'); + errors.log.restore(); + }); -QUnit.test('Selection state of rows should be updated on loadDescendants', function(assert) { + QUnit.test('getSelectedRowKeys with \'all\' parameter', function(assert) { // arrange - const clock = sinon.useFakeTimers(); - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } - ]; - this.options.remoteOperations = true; - this.options.loadingTimeout = 0; - this.options.selectedRowKeys = [1]; - this.setupTreeList(); - clock.tick(); - - this.rowsView.render($testElement); - - // assert - assert.deepEqual(this.getSelectedRowKeys('leavesOnly'), [], 'leaves'); - - // act - this.loadDescendants(); - clock.tick(); - - // assert - assert.deepEqual(this.getSelectedRowKeys('leavesOnly'), [2, 3, 4], 'leaves'); - clock.restore(); -}); + const $testElement = $('#treeList'); + + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, parentId: 4, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } + ]; + this.options.expandedRowKeys = [1, 4]; + this.options.selectedRowKeys = [2, 3, 4]; + this.setupTreeList(); + this.rowsView.render($testElement); + + // act, assert + assert.deepEqual(this.getSelectedRowKeys('all'), [1, 2, 3, 4, 5], 'all selected items'); + }); -QUnit.test('Checkbox of the parent node should be in an indeterminate state when deselecting child node', function(assert) { + QUnit.test('getSelectedRowKeys with \'excludeRecursive\' parameter', function(assert) { // arrange - let items; - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, parentId: 4, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } - ]; - this.options.expandedRowKeys = [1]; - this.options.selectedRowKeys = [1]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.deselectRows(2); - - // assert - items = this.dataController.items(); - assert.strictEqual(items[0].isSelected, undefined, 'selection state of the first item is indeterminate'); - assert.ok($testElement.find('.dx-checkbox').first().hasClass('dx-checkbox-indeterminate'), 'Checkbox of the first row in an indeterminate state'); -}); + const $testElement = $('#treeList'); -QUnit.test('Update selection after expanding node when \'remoteOperations\' is true', function(assert) { + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, parentId: 4, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) }, + { id: 6, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 7, parentId: 6, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + ]; + this.options.expandedRowKeys = [1, 4]; + this.options.selectedRowKeys = [2, 5, 7]; + this.setupTreeList(); + this.rowsView.render($testElement); + + // act, assert + assert.deepEqual(this.getSelectedRowKeys('excludeRecursive'), [2, 4, 6], 'all selected items'); + }); + + QUnit.test('getSelectedRowsData with mode parameter calls getSelectedRowKeys', function(assert) { // arrange - let items; - const $testElement = $('#treeList'); - - this.options.remoteOperations = true; - this.options.selectedRowKeys = [1]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // assert - items = this.dataController.items(); - assert.strictEqual(items.length, 1, 'count item'); - assert.ok(items[0].isSelected, 'first item is selected'); - - // act - this.expandRow(1); - - // assert - items = this.dataController.items(); - assert.deepEqual(this.option('selectedRowKeys'), [1], 'selected row keys'); - assert.strictEqual(items.length, 2, 'count item'); - assert.ok(items[0].isSelected, 'first item is selected'); - assert.ok(items[1].isSelected, 'second item is selected'); -}); + const $testElement = $('#treeList'); + + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) } + ]; + this.options.expandedRowKeys = [1]; + this.options.selectedRowKeys = [3]; + this.setupTreeList(); + this.rowsView.render($testElement); + + // act + this.selectionController.getSelectedRowKeys = sinon.spy(); + this.getSelectedRowsData('all'); + + // assert + assert.equal(this.selectionController.getSelectedRowKeys.callCount, 1, 'getSelectedRowKeys is called'); + assert.equal(this.selectionController.getSelectedRowKeys.args[0], 'all', 'getSelectedRowKeys is called with a mode parameter'); + }); + + + QUnit.test('getSelectedRowsData with mode parameter when key has no data', function(assert) { + // arrange, act + const clock = sinon.useFakeTimers(); + const $testElement = $('#treeList'); + const data = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) } + ]; + + this.options.dataSource = { + load: function() { + const d = $.Deferred(); + + setTimeout(function() { + d.resolve(data); + }, 100); + + return d.promise(); + } + }; + this.options.expandedRowKeys = [1]; + this.options.selectedRowKeys = [1]; + this.setupTreeList(); + this.rowsView.render($testElement); + + // assert + assert.deepEqual(this.getSelectedRowsData('leavesOnly'), [], 'empty data'); + + // act + clock.tick(100); + + // assert + assert.equal(this.getSelectedRowsData('leavesOnly').length, 2, '2 nodes are returned'); + assert.deepEqual(this.getSelectedRowsData('leavesOnly')[0], data[1], 'first child'); + assert.deepEqual(this.getSelectedRowsData('leavesOnly')[1], data[2], 'second child'); + + clock.restore(); + }); -QUnit.test('Changing recursive option at runtime - Deselecting row when all rows are selected', function(assert) { + + QUnit.test('Selection state of rows should be updated on loadDescendants', function(assert) { // arrange - const $testElement = $('#treeList'); - - this.options.selection.recursive = false; - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2001, 0, 2) }, - { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 2, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } - ]; - this.options.expandedRowKeys = [1, 2]; - this.setupTreeList(); - this.rowsView.render($testElement); - - this.selectAll(); - - // act - this.options.selection.recursive = true; - this.selectionController.optionChanged({ name: 'selection' }); - this.deselectRows(3); - - // assert - assert.deepEqual(this.option('selectedRowKeys'), [4], 'selectedRowKeys'); -}); + const clock = sinon.useFakeTimers(); + const $testElement = $('#treeList'); -QUnit.test('Deselecting child node when all nodes are selected', function(assert) { + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } + ]; + this.options.remoteOperations = true; + this.options.loadingTimeout = 0; + this.options.selectedRowKeys = [1]; + this.setupTreeList(); + clock.tick(); + + this.rowsView.render($testElement); + + // assert + assert.deepEqual(this.getSelectedRowKeys('leavesOnly'), [], 'leaves'); + + // act + this.loadDescendants(); + clock.tick(); + + // assert + assert.deepEqual(this.getSelectedRowKeys('leavesOnly'), [2, 3, 4], 'leaves'); + clock.restore(); + }); + + QUnit.test('Checkbox of the parent node should be in an indeterminate state when deselecting child node', function(assert) { // arrange - let items; - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 3, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, parentId: 3, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) }, - { id: 6, parentId: 3, field1: 'test6', field2: 6, field3: new Date(2002, 1, 6) } - ]; - - this.options.expandedRowKeys = [3]; - this.options.selectedRowKeys = [1, 2, 3]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.deselectRows(4); - - // assert - items = this.dataController.items(); - assert.deepEqual(this.option('selectedRowKeys'), [1, 2, 5, 6], 'selected row keys'); - assert.ok(items[0].isSelected, 'first item is selected'); - assert.ok(items[1].isSelected, 'second item is selected'); - assert.notOk(items[2].isSelected, 'third item isn\'t selected'); - assert.notOk(items[3].isSelected, 'fourth item isn\'t selected'); - assert.ok(items[4].isSelected, 'fifth item is selected'); - assert.ok(items[5].isSelected, 'sixth item is selected'); -}); + let items; + const $testElement = $('#treeList'); + + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 1, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, parentId: 4, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } + ]; + this.options.expandedRowKeys = [1]; + this.options.selectedRowKeys = [1]; + this.setupTreeList(); + this.rowsView.render($testElement); + + // act + this.deselectRows(2); + + // assert + items = this.dataController.items(); + assert.strictEqual(items[0].isSelected, undefined, 'selection state of the first item is indeterminate'); + assert.ok($testElement.find('.dx-checkbox').first().hasClass('dx-checkbox-indeterminate'), 'Checkbox of the first row in an indeterminate state'); + }); -// T550090 -QUnit.test('Select all when end nodes are selected', function(assert) { + QUnit.test('Update selection after expanding node when \'remoteOperations\' is true', function(assert) { // arrange - const $testElement = $('#treeList'); + let items; + const $testElement = $('#treeList'); - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2002, 1, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 3, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } - ]; + this.options.remoteOperations = true; + this.options.selectedRowKeys = [1]; + this.setupTreeList(); + this.rowsView.render($testElement); - this.options.selectedRowKeys = [2, 3, 4]; - this.setupTreeList(); - this.rowsView.render($testElement); + // assert + items = this.dataController.items(); + assert.strictEqual(items.length, 1, 'count item'); + assert.ok(items[0].isSelected, 'first item is selected'); - // act - this.selectAll(); + // act + this.expandRow(1); - // assert - assert.deepEqual(this.option('selectedRowKeys'), [2, 3, 4, 5], 'selected row keys'); -}); + // assert + items = this.dataController.items(); + assert.deepEqual(this.option('selectedRowKeys'), [1], 'selected row keys'); + assert.strictEqual(items.length, 2, 'count item'); + assert.ok(items[0].isSelected, 'first item is selected'); + assert.ok(items[1].isSelected, 'second item is selected'); + }); -// T550090 -QUnit.test('Deselect all after deselecting -> selecting a nested node', function(assert) { + QUnit.test('Changing recursive option at runtime - Deselecting row when all rows are selected', function(assert) { // arrange - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2002, 1, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 3, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } - ]; + const $testElement = $('#treeList'); - this.options.selectedRowKeys = [1, 5]; - this.setupTreeList(); - this.rowsView.render($testElement); + this.options.selection.recursive = false; + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2001, 0, 2) }, + { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 2, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } + ]; + this.options.expandedRowKeys = [1, 2]; + this.setupTreeList(); + this.rowsView.render($testElement); - this.deselectRows(2); - this.selectRows(2); + this.selectAll(); - // act - this.deselectAll(); + // act + this.options.selection.recursive = true; + this.selectionController.optionChanged({ name: 'selection' }); + this.deselectRows(3); - // assert - assert.deepEqual(this.option('selectedRowKeys'), [], 'selected row keys'); -}); + // assert + assert.deepEqual(this.option('selectedRowKeys'), [4], 'selectedRowKeys'); + }); -// T557278 -QUnit.test('SelectRows - onSelectionChanged event should be fired before resolving the Deferred object', function(assert) { + QUnit.test('Deselecting child node when all nodes are selected', function(assert) { // arrange - const $testElement = $('#treeList'); - const done = assert.async(); - let onSelectionChangedFired; - - this.options.dataSource = { - load: function() { - const d = $.Deferred(); - - setTimeout(function() { - d.resolve([ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2002, 1, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 3, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } - ]); - }, 30); - - return d.promise(); - } - }; - this.options.selectedRowKeys = [2, 3]; - this.options.onSelectionChanged = function() { - onSelectionChangedFired = true; - }; - this.setupTreeList(); - this.rowsView.render($testElement); + let items; + const $testElement = $('#treeList'); + + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 3, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, parentId: 3, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) }, + { id: 6, parentId: 3, field1: 'test6', field2: 6, field3: new Date(2002, 1, 6) } + ]; - // act - this.selectRows(1, true).done(function() { - assert.ok(onSelectionChangedFired, 'onSelectionChanged event fired'); - done(); + this.options.expandedRowKeys = [3]; + this.options.selectedRowKeys = [1, 2, 3]; + this.setupTreeList(); + this.rowsView.render($testElement); + + // act + this.deselectRows(4); + + // assert + items = this.dataController.items(); + assert.deepEqual(this.option('selectedRowKeys'), [1, 2, 5, 6], 'selected row keys'); + assert.ok(items[0].isSelected, 'first item is selected'); + assert.ok(items[1].isSelected, 'second item is selected'); + assert.notOk(items[2].isSelected, 'third item isn\'t selected'); + assert.notOk(items[3].isSelected, 'fourth item isn\'t selected'); + assert.ok(items[4].isSelected, 'fifth item is selected'); + assert.ok(items[5].isSelected, 'sixth item is selected'); }); -}); -QUnit.test('Selecting a node and its child node', function(assert) { + // T550090 + QUnit.test('Select all when end nodes are selected', function(assert) { // arrange - let items; - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2002, 1, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) } - ]; - this.options.expandedRowKeys = [1]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.selectRows([1, 2]); - - // assert - items = this.dataController.items(); - assert.deepEqual(this.option('selectedRowKeys'), [1, 2], 'selected row keys'); - assert.ok(items[0].isSelected, 'first item is selected'); - assert.ok(items[1].isSelected, 'second item is selected'); - assert.ok(items[2].isSelected, 'third item is selected'); -}); + const $testElement = $('#treeList'); + + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2002, 1, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 3, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } + ]; + + this.options.selectedRowKeys = [2, 3, 4]; + this.setupTreeList(); + this.rowsView.render($testElement); + + // act + this.selectAll(); + + // assert + assert.deepEqual(this.option('selectedRowKeys'), [2, 3, 4, 5], 'selected row keys'); + }); -// T560463 -QUnit.test('Select all after filtering data', function(assert) { + // T550090 + QUnit.test('Deselect all after deselecting -> selecting a nested node', function(assert) { // arrange - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, field1: 'test5', field2: 5, field3: new Date(2001, 0, 5) } - ]; - this.options.searchPanel = { - text: 'test2' - }; - this.setupTreeList(); - this.rowsView.render($testElement); + const $testElement = $('#treeList'); - // act - this.selectAll(); + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2002, 1, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 3, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } + ]; - // assert - assert.deepEqual(this.option('selectedRowKeys'), [1, 4, 5], 'selected row keys'); -}); + this.options.selectedRowKeys = [1, 5]; + this.setupTreeList(); + this.rowsView.render($testElement); + + this.deselectRows(2); + this.selectRows(2); + + // act + this.deselectAll(); -// T558153 -QUnit.test('Selection state should be updated correctly after options are changed', function(assert) { + // assert + assert.deepEqual(this.option('selectedRowKeys'), [], 'selected row keys'); + }); + + // T557278 + QUnit.test('SelectRows - onSelectionChanged event should be fired before resolving the Deferred object', function(assert) { // arrange - let items; - const $testElement = $('#treeList'); - const clock = sinon.useFakeTimers(); + const $testElement = $('#treeList'); + const done = assert.async(); + let onSelectionChangedFired; + + this.options.dataSource = { + load: function() { + const d = $.Deferred(); + + setTimeout(function() { + d.resolve([ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2002, 1, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 3, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) } + ]); + }, 30); + + return d.promise(); + } + }; + this.options.selectedRowKeys = [2, 3]; + this.options.onSelectionChanged = function() { + onSelectionChangedFired = true; + }; + this.setupTreeList(); + this.rowsView.render($testElement); - try { - this.options.loadingTimeout = 30; + // act + this.selectRows(1, true).done(function() { + assert.ok(onSelectionChangedFired, 'onSelectionChanged event fired'); + done(); + }); + }); + + QUnit.test('Selecting a node and its child node', function(assert) { + // arrange + let items; + const $testElement = $('#treeList'); + + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2002, 1, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) } + ]; + this.options.expandedRowKeys = [1]; this.setupTreeList(); this.rowsView.render($testElement); - this.options.selectedRowKeys = [2, 4]; + // act + this.selectRows([1, 2]); + + // assert + items = this.dataController.items(); + assert.deepEqual(this.option('selectedRowKeys'), [1, 2], 'selected row keys'); + assert.ok(items[0].isSelected, 'first item is selected'); + assert.ok(items[1].isSelected, 'second item is selected'); + assert.ok(items[2].isSelected, 'third item is selected'); + }); + + // T560463 + QUnit.test('Select all after filtering data', function(assert) { + // arrange + const $testElement = $('#treeList'); + this.options.dataSource = [ { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 3, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } + { id: 3, parentId: 1, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, field1: 'test5', field2: 5, field3: new Date(2001, 0, 5) } ]; + this.options.searchPanel = { + text: 'test2' + }; + this.setupTreeList(); + this.rowsView.render($testElement); // act - this.selectionController.optionChanged({ name: 'selectedRowKeys', value: this.options.selectedRowKeys }); - this.dataController.optionChanged({ name: 'dataSource' }); - clock.tick(30); + this.selectAll(); // assert - items = this.dataController.items(); - assert.strictEqual(items.length, 2, 'count row'); - assert.ok(items[0].isSelected, 'first row is selected'); - assert.ok(items[1].isSelected, 'second row is selected'); - } finally { - clock.restore(); - } -}); + assert.deepEqual(this.option('selectedRowKeys'), [1, 4, 5], 'selected row keys'); + }); -QUnit.test('Check selectedRowKeys after deselecting nested node', function(assert) { + // T558153 + QUnit.test('Selection state should be updated correctly after options are changed', function(assert) { // arrange - const $testElement = $('#treeList'); - - this.options.dataSource = [ - { id: 1, field1: 'test1', field2: 1, field3: new Date(2002, 1, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, - { id: 4, parentId: 3, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, - { id: 5, parentId: 2, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) }, - { id: 6, parentId: 1, field1: 'test6', field2: 6, field3: new Date(2002, 1, 6) } - ]; - - this.options.selectedRowKeys = [2]; - this.setupTreeList(); - this.rowsView.render($testElement); - - // act - this.deselectRows(4); - - // assert - assert.deepEqual(this.option('selectedRowKeys'), [5], 'selected row keys'); -}); + let items; + const $testElement = $('#treeList'); + const clock = sinon.useFakeTimers(); + + try { + this.options.loadingTimeout = 30; + this.setupTreeList(); + this.rowsView.render($testElement); + + this.options.selectedRowKeys = [2, 4]; + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 3, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) } + ]; + + // act + this.selectionController.optionChanged({ name: 'selectedRowKeys', value: this.options.selectedRowKeys }); + this.dataController.optionChanged({ name: 'dataSource' }); + clock.tick(30); + + // assert + items = this.dataController.items(); + assert.strictEqual(items.length, 2, 'count row'); + assert.ok(items[0].isSelected, 'first row is selected'); + assert.ok(items[1].isSelected, 'second row is selected'); + } finally { + clock.restore(); + } + }); -QUnit.test('focusedItemIndex should be reset to -1 after change page index (T742193)', function(assert) { + QUnit.test('Check selectedRowKeys after deselecting nested node', function(assert) { // arrange - const $testElement = $('#treeList'); - const array = [ - { id: 1, field1: 'test1', field2: 1 }, - { id: 2, field1: 'test2', field2: 2 }, - { id: 3, field1: 'test3', field2: 3 }, - { id: 4, field1: 'test4', field2: 4 } - ]; - - this.options.dataSource = { - store: { - type: 'array', - data: array, - key: 'id' - }, - pageSize: 2, - paginate: true - }; + const $testElement = $('#treeList'); - this.setupTreeList(); - this.rowsView.render($testElement); + this.options.dataSource = [ + { id: 1, field1: 'test1', field2: 1, field3: new Date(2002, 1, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) }, + { id: 4, parentId: 3, field1: 'test4', field2: 4, field3: new Date(2002, 1, 4) }, + { id: 5, parentId: 2, field1: 'test5', field2: 5, field3: new Date(2002, 1, 5) }, + { id: 6, parentId: 1, field1: 'test6', field2: 6, field3: new Date(2002, 1, 6) } + ]; - // act - this.selectionController.changeItemSelection(1, { shift: true }); - // assert - assert.deepEqual(this.selectionController.getSelectedRowsData(), [{ id: 2, field1: 'test2', field2: 2 }]); - assert.equal(this.selectionController._selection._focusedItemIndex, 1, '_focusedItemIndex corrected'); + this.options.selectedRowKeys = [2]; + this.setupTreeList(); + this.rowsView.render($testElement); + + // act + this.deselectRows(4); + + // assert + assert.deepEqual(this.option('selectedRowKeys'), [5], 'selected row keys'); + }); + + QUnit.test('focusedItemIndex should be reset to -1 after change page index (T742193)', function(assert) { + // arrange + const $testElement = $('#treeList'); + const array = [ + { id: 1, field1: 'test1', field2: 1 }, + { id: 2, field1: 'test2', field2: 2 }, + { id: 3, field1: 'test3', field2: 3 }, + { id: 4, field1: 'test4', field2: 4 } + ]; - // act - this.dataController.pageIndex(1); - // assert - assert.equal(this.selectionController._selection._focusedItemIndex, -1, '_focusedItemIndex corrected'); + this.options.dataSource = { + store: { + type: 'array', + data: array, + key: 'id' + }, + pageSize: 2, + paginate: true + }; + + this.setupTreeList(); + this.rowsView.render($testElement); + + // act + this.selectionController.changeItemSelection(1, { shift: true }); + // assert + assert.deepEqual(this.selectionController.getSelectedRowsData(), [{ id: 2, field1: 'test2', field2: 2 }]); + assert.equal(this.selectionController._selection._focusedItemIndex, 1, '_focusedItemIndex corrected'); + + // act + this.dataController.pageIndex(1); + // assert + assert.equal(this.selectionController._selection._focusedItemIndex, -1, '_focusedItemIndex corrected'); + }); }); + diff --git a/testing/tests/DevExpress.ui.widgets.treeList/stateStoring.tests.js b/testing/tests/DevExpress.ui.widgets.treeList/stateStoring.tests.js index 249eeaf61eb7..aaaf6c47a013 100644 --- a/testing/tests/DevExpress.ui.widgets.treeList/stateStoring.tests.js +++ b/testing/tests/DevExpress.ui.widgets.treeList/stateStoring.tests.js @@ -47,184 +47,186 @@ QUnit.module('State Storing', { afterEach: function() { this.clock.restore(); } -}); +}, () => { -QUnit.test('Apply state storing', function(assert) { + QUnit.test('Apply state storing', function(assert) { // arrange - let rows; - - // act - this.setupDataGridModules({ - filterRow: { visible: true }, - stateStoring: { - enabled: true, - type: 'custom', - customLoad: function() { - return { - columns: [ - { dataField: 'name' }, - { dataField: 'age', filterValue: 14, selectedFilterOperation: '>' } - ], - expandedRowKeys: [1, 2], - selectedRowKeys: [3], - searchPanel: { - text: 'Name' - } - }; + let rows; + + // act + this.setupDataGridModules({ + filterRow: { visible: true }, + stateStoring: { + enabled: true, + type: 'custom', + customLoad: function() { + return { + columns: [ + { dataField: 'name' }, + { dataField: 'age', filterValue: 14, selectedFilterOperation: '>' } + ], + expandedRowKeys: [1, 2], + selectedRowKeys: [3], + searchPanel: { + text: 'Name' + } + }; + } } - } + }); + + rows = this.getVisibleRows(); + assert.strictEqual(rows.length, 4, 'row count'); + assert.strictEqual(rows[0].key, 1, 'key of the first row'); + assert.strictEqual(rows[1].key, 2, 'key of the second row'); + assert.strictEqual(rows[2].key, 3, 'key of the third row'); + assert.ok(rows[2].isSelected, 'third row is selected'); + assert.strictEqual(rows[3].key, 4, 'key of the fourth row'); }); - rows = this.getVisibleRows(); - assert.strictEqual(rows.length, 4, 'row count'); - assert.strictEqual(rows[0].key, 1, 'key of the first row'); - assert.strictEqual(rows[1].key, 2, 'key of the second row'); - assert.strictEqual(rows[2].key, 3, 'key of the third row'); - assert.ok(rows[2].isSelected, 'third row is selected'); - assert.strictEqual(rows[3].key, 4, 'key of the fourth row'); -}); - -QUnit.test('Save user state', function(assert) { + QUnit.test('Save user state', function(assert) { // arrange - let state = {}; - - // act - this.setupDataGridModules({ - expandedRowKeys: [1], - stateStoring: { - enabled: true, - type: 'custom', - customLoad: function() { - return state; - }, - customSave: function(arg) { - state = arg; - }, - savingTimeout: 0 - } + let state = {}; + + // act + this.setupDataGridModules({ + expandedRowKeys: [1], + stateStoring: { + enabled: true, + type: 'custom', + customLoad: function() { + return state; + }, + customSave: function(arg) { + state = arg; + }, + savingTimeout: 0 + } + }); + + // assert + assert.deepEqual(state, { + columns: [ + { dataField: 'name', dataType: 'string', visible: true, visibleIndex: 0 }, + { dataField: 'age', dataType: 'number', visible: true, visibleIndex: 1 } + ], + filterPanel: {}, + filterValue: null, + expandedRowKeys: [1], + pageIndex: 0, + pageSize: 20, + searchText: '' + }, 'state'); }); - // assert - assert.deepEqual(state, { - columns: [ - { dataField: 'name', dataType: 'string', visible: true, visibleIndex: 0 }, - { dataField: 'age', dataType: 'number', visible: true, visibleIndex: 1 } - ], - filterPanel: {}, - filterValue: null, - expandedRowKeys: [1], - pageIndex: 0, - pageSize: 20, - searchText: '' - }, 'state'); -}); - -QUnit.test('The expandRowKeys state should not persist when autoExpandAll is enabled', function(assert) { + QUnit.test('The expandRowKeys state should not persist when autoExpandAll is enabled', function(assert) { // arrange - let state = {}; - - // act - this.setupDataGridModules({ - autoExpandAll: true, - stateStoring: { - enabled: true, - type: 'custom', - customLoad: function() { - return state; - }, - customSave: function(arg) { - state = arg; - }, - savingTimeout: 0 - } - }); + let state = {}; + + // act + this.setupDataGridModules({ + autoExpandAll: true, + stateStoring: { + enabled: true, + type: 'custom', + customLoad: function() { + return state; + }, + customSave: function(arg) { + state = arg; + }, + savingTimeout: 0 + } + }); - // assert - assert.notOk(Object.prototype.hasOwnProperty.call(state, 'expandedRowKeys'), 'state doesn\'t have expandedRowKeys'); -}); + // assert + assert.notOk(Object.prototype.hasOwnProperty.call(state, 'expandedRowKeys'), 'state doesn\'t have expandedRowKeys'); + }); -// T811724, T824333 -QUnit.test('customSave should be fired after expand', function(assert) { + // T811724, T824333 + QUnit.test('customSave should be fired after expand', function(assert) { // arrange - let state = { - columns: [ - { visibleIndex: 0, dataField: 'name', dataType: 'string', visible: true }, - { visibleIndex: 1, dataField: 'age', dataType: 'number', visible: true } - ], - filterPanel: {}, - filterValue: null, - expandedRowKeys: [1], - pageIndex: 0, - pageSize: 20, - searchText: '' - }; - let customSaveCallCount = 0; - - // act - this.setupDataGridModules({ - expandedRowKeys: [], - stateStoring: { - enabled: true, - type: 'custom', - customLoad: function() { - return state; - }, - customSave: function(arg) { - customSaveCallCount++; - state = arg; - }, - savingTimeout: 0 - } - }); + let state = { + columns: [ + { visibleIndex: 0, dataField: 'name', dataType: 'string', visible: true }, + { visibleIndex: 1, dataField: 'age', dataType: 'number', visible: true } + ], + filterPanel: {}, + filterValue: null, + expandedRowKeys: [1], + pageIndex: 0, + pageSize: 20, + searchText: '' + }; + let customSaveCallCount = 0; + + // act + this.setupDataGridModules({ + expandedRowKeys: [], + stateStoring: { + enabled: true, + type: 'custom', + customLoad: function() { + return state; + }, + customSave: function(arg) { + customSaveCallCount++; + state = arg; + }, + savingTimeout: 0 + } + }); - this.clock.tick(); - assert.strictEqual(customSaveCallCount, 0, 'customSave is not called'); + this.clock.tick(); + assert.strictEqual(customSaveCallCount, 0, 'customSave is not called'); - // act - this.expandRow(2); - this.clock.tick(); + // act + this.expandRow(2); + this.clock.tick(); - // assert - assert.strictEqual(customSaveCallCount, 1, 'customSave is called once after expandRow'); - assert.deepEqual(state.expandedRowKeys, [1, 2], 'expandedRowKeys in state is correct'); -}); + // assert + assert.strictEqual(customSaveCallCount, 1, 'customSave is called once after expandRow'); + assert.deepEqual(state.expandedRowKeys, [1, 2], 'expandedRowKeys in state is correct'); + }); -// T851561 -QUnit.test('The expandedRowKeys should be updated in the state storing when expanding/collapsing nodes', function(assert) { + // T851561 + QUnit.test('The expandedRowKeys should be updated in the state storing when expanding/collapsing nodes', function(assert) { // arrange - let state = {}; - - this.setupDataGridModules({ - stateStoring: { - enabled: true, - type: 'custom', - customLoad: function() { - return state; - }, - customSave: function(arg) { - state = arg; - }, - savingTimeout: 0 - } + let state = {}; + + this.setupDataGridModules({ + stateStoring: { + enabled: true, + type: 'custom', + customLoad: function() { + return state; + }, + customSave: function(arg) { + state = arg; + }, + savingTimeout: 0 + } + }); + + // act + this.expandRow(1); + this.clock.tick(); + + // assert + let expandedRowKeys = this.option('expandedRowKeys'); + assert.deepEqual(expandedRowKeys, [1], 'expandedRowKeys'); + assert.deepEqual(state.expandedRowKeys, [1], 'expandedRowKeys has been updated in the state storage'); + assert.notStrictEqual(state.expandedRowKeys, this.option('expandedRowKeys'), 'expandedRowKeys has a different instance in the state storage'); + + // act + this.collapseRow(1); + this.clock.tick(); + + // assert + expandedRowKeys = this.option('expandedRowKeys'); + assert.deepEqual(expandedRowKeys, [], 'expandedRowKeys'); + assert.deepEqual(state.expandedRowKeys, [], 'expandedRowKeys has been updated in the state storage'); + assert.notStrictEqual(state.expandedRowKeys, this.option('expandedRowKeys'), 'expandedRowKeys has a different instance in the state storage'); }); - - // act - this.expandRow(1); - this.clock.tick(); - - // assert - let expandedRowKeys = this.option('expandedRowKeys'); - assert.deepEqual(expandedRowKeys, [1], 'expandedRowKeys'); - assert.deepEqual(state.expandedRowKeys, [1], 'expandedRowKeys has been updated in the state storage'); - assert.notStrictEqual(state.expandedRowKeys, this.option('expandedRowKeys'), 'expandedRowKeys has a different instance in the state storage'); - - // act - this.collapseRow(1); - this.clock.tick(); - - // assert - expandedRowKeys = this.option('expandedRowKeys'); - assert.deepEqual(expandedRowKeys, [], 'expandedRowKeys'); - assert.deepEqual(state.expandedRowKeys, [], 'expandedRowKeys has been updated in the state storage'); - assert.notStrictEqual(state.expandedRowKeys, this.option('expandedRowKeys'), 'expandedRowKeys has a different instance in the state storage'); }); + diff --git a/testing/tests/DevExpress.ui.widgets.treeList/treeList.markup.tests.js b/testing/tests/DevExpress.ui.widgets.treeList/treeList.markup.tests.js index a8397502030b..87e21d89a001 100644 --- a/testing/tests/DevExpress.ui.widgets.treeList/treeList.markup.tests.js +++ b/testing/tests/DevExpress.ui.widgets.treeList/treeList.markup.tests.js @@ -17,65 +17,67 @@ QUnit.module('TreeList markup', { afterEach: function() { this.clock.restore(); } -}); - -QUnit.test('markup init', function(assert) { - const $element = $('#treeList').dxTreeList(); - const $container = $element.children(); - const $headersView = $container.children('.dx-treelist-headers'); - const $rowsView = $container.children('.dx-treelist-rowsview'); +}, () => { - assert.ok($element.hasClass('dx-treelist'), 'dx-treelist'); - assert.ok($container.hasClass('dx-treelist-container'), 'dx-treelist'); - assert.equal($headersView.length, 1, 'headers view'); - assert.equal($headersView.find('td').length, 0, 'headers view has no cell'); - assert.equal($rowsView.length, 1, 'rows view'); - assert.ok($rowsView.hasClass('dx-empty'), 'rows view is empty'); - assert.equal($rowsView.find('td').length, 0, 'rows view has no cell'); -}); + QUnit.test('markup init', function(assert) { + const $element = $('#treeList').dxTreeList(); + const $container = $element.children(); + const $headersView = $container.children('.dx-treelist-headers'); + const $rowsView = $container.children('.dx-treelist-rowsview'); -QUnit.test('markup with dataSource', function(assert) { - const $element = $('#treeList').dxTreeList({ - dataSource: [ - { id: 1, parentId: 0, name: 'Alex' }, - { id: 2, parentId: 1, name: 'Bob' }, - { id: 3, parentId: 2, name: 'Tom' } - ], - expandedRowKeys: [1], - keyExpr: 'id', - parentIdExpr: 'parentId' + assert.ok($element.hasClass('dx-treelist'), 'dx-treelist'); + assert.ok($container.hasClass('dx-treelist-container'), 'dx-treelist'); + assert.equal($headersView.length, 1, 'headers view'); + assert.equal($headersView.find('td').length, 0, 'headers view has no cell'); + assert.equal($rowsView.length, 1, 'rows view'); + assert.ok($rowsView.hasClass('dx-empty'), 'rows view is empty'); + assert.equal($rowsView.find('td').length, 0, 'rows view has no cell'); }); - this.clock.tick(30); + QUnit.test('markup with dataSource', function(assert) { + const $element = $('#treeList').dxTreeList({ + dataSource: [ + { id: 1, parentId: 0, name: 'Alex' }, + { id: 2, parentId: 1, name: 'Bob' }, + { id: 3, parentId: 2, name: 'Tom' } + ], + expandedRowKeys: [1], + keyExpr: 'id', + parentIdExpr: 'parentId' + }); - const $container = $element.children(); - const $headersView = $container.children('.dx-treelist-headers'); - const $rowsView = $container.children('.dx-treelist-rowsview'); + this.clock.tick(30); - assert.ok($element.hasClass('dx-treelist'), 'dx-widget'); - assert.ok($container.hasClass('dx-treelist-container'), 'dx-treelist'); + const $container = $element.children(); + const $headersView = $container.children('.dx-treelist-headers'); + const $rowsView = $container.children('.dx-treelist-rowsview'); - assert.equal($headersView.length, 1, 'headers view'); - assert.equal($headersView.find('td').length, 3, 'headers view has 3 cells'); - assert.equal($headersView.find('td').eq(0).text(), 'Id', 'first column title'); - assert.equal($headersView.find('td').eq(1).text(), 'Parent Id', 'second column title'); - assert.equal($headersView.find('td').eq(2).text(), 'Name', 'third column title'); + assert.ok($element.hasClass('dx-treelist'), 'dx-widget'); + assert.ok($container.hasClass('dx-treelist-container'), 'dx-treelist'); - assert.equal($rowsView.length, 1, 'rows view'); - assert.notOk($rowsView.hasClass('dx-empty'), 'rows view is not empty'); - assert.equal($rowsView.find('table').length, 1, 'one table'); - assert.equal($rowsView.find('.dx-data-row').length, 2, 'data row count'); - assert.equal($rowsView.find('.dx-data-row td').length, 6, 'rows view has 6 data cells'); - assert.equal($rowsView.find('td').length, 9, 'rows view has 15 cells (6 data cells + 3 free space cells)'); + assert.equal($headersView.length, 1, 'headers view'); + assert.equal($headersView.find('td').length, 3, 'headers view has 3 cells'); + assert.equal($headersView.find('td').eq(0).text(), 'Id', 'first column title'); + assert.equal($headersView.find('td').eq(1).text(), 'Parent Id', 'second column title'); + assert.equal($headersView.find('td').eq(2).text(), 'Name', 'third column title'); - const $dataCells = $rowsView.find('.dx-data-row td'); - assert.equal($dataCells.find('.dx-treelist-expanded').length, 1, 'first row is expanded'); - assert.equal($dataCells.eq(0).text(), '1', 'value of the first cell of the first row'); - assert.equal($dataCells.eq(1).text(), '0', 'value of the second cell of the first row'); - assert.equal($dataCells.eq(2).text(), 'Alex', 'value of the third cell of the first row'); + assert.equal($rowsView.length, 1, 'rows view'); + assert.notOk($rowsView.hasClass('dx-empty'), 'rows view is not empty'); + assert.equal($rowsView.find('table').length, 1, 'one table'); + assert.equal($rowsView.find('.dx-data-row').length, 2, 'data row count'); + assert.equal($rowsView.find('.dx-data-row td').length, 6, 'rows view has 6 data cells'); + assert.equal($rowsView.find('td').length, 9, 'rows view has 15 cells (6 data cells + 3 free space cells)'); - assert.equal($dataCells.eq(3).find('.dx-treelist-collapsed').length, 1, 'first row is collapsed'); - assert.equal($dataCells.eq(3).text(), '2', 'value of the first cell of the first row'); - assert.equal($dataCells.eq(4).text(), '1', 'value of the second cell of the first row'); - assert.equal($dataCells.eq(5).text(), 'Bob', 'value of the third cell of the first row'); + const $dataCells = $rowsView.find('.dx-data-row td'); + assert.equal($dataCells.find('.dx-treelist-expanded').length, 1, 'first row is expanded'); + assert.equal($dataCells.eq(0).text(), '1', 'value of the first cell of the first row'); + assert.equal($dataCells.eq(1).text(), '0', 'value of the second cell of the first row'); + assert.equal($dataCells.eq(2).text(), 'Alex', 'value of the third cell of the first row'); + + assert.equal($dataCells.eq(3).find('.dx-treelist-collapsed').length, 1, 'first row is collapsed'); + assert.equal($dataCells.eq(3).text(), '2', 'value of the first cell of the first row'); + assert.equal($dataCells.eq(4).text(), '1', 'value of the second cell of the first row'); + assert.equal($dataCells.eq(5).text(), 'Bob', 'value of the third cell of the first row'); + }); }); + diff --git a/testing/tests/DevExpress.ui.widgets.treeList/treeList.tests.js b/testing/tests/DevExpress.ui.widgets.treeList/treeList.tests.js index 907774075aa5..0581b675d955 100644 --- a/testing/tests/DevExpress.ui.widgets.treeList/treeList.tests.js +++ b/testing/tests/DevExpress.ui.widgets.treeList/treeList.tests.js @@ -35,16 +35,11 @@ const defaultModuleConfig = { } }; -QUnit.module('Initialization', defaultModuleConfig); - -const treeListWrapper = new TreeListWrapper('#container'); - const createTreeList = function(options) { - let treeList; const treeListElement = $('#treeList').dxTreeList(options); QUnit.assert.ok(treeListElement); - treeList = treeListElement.dxTreeList('instance'); + const treeList = treeListElement.dxTreeList('instance'); return treeList; }; @@ -60,955 +55,992 @@ const generateData = function(count) { return result; }; -QUnit.test('Empty options', function(assert) { - const treeList = createTreeList({}); - const $treeListElement = $(treeList.$element()); - const $noDataElement = $treeListElement.find('.dx-treelist-nodata'); +const treeListWrapper = new TreeListWrapper('#container'); - assert.ok(treeList); - assert.ok($treeListElement.hasClass('dx-treelist'), 'widget class on the root element'); - assert.ok($noDataElement.length, 'widget have a \'no data\' element'); - assert.ok($noDataElement.is(':visible'), '\'No data\' element is visible'); - assert.ok($treeListElement.children().hasClass('dx-treelist-container'), 'container class on the child'); -}); +QUnit.module('Initialization', defaultModuleConfig, () => { + QUnit.test('Empty options', function(assert) { + const treeList = createTreeList({}); + const $treeListElement = $(treeList.$element()); + const $noDataElement = $treeListElement.find('.dx-treelist-nodata'); -QUnit.test('Sorting should be applied on header cell click', function(assert) { - const treeList = createTreeList({ - columns: ['name', 'age'], - dataSource: [ - { id: 1, parentId: 0, name: 'Name 3', age: 19 }, - { id: 2, parentId: 0, name: 'Name 1', age: 19 }, - { id: 3, parentId: 0, name: 'Name 2', age: 18 } - ] + assert.ok(treeList); + assert.ok($treeListElement.hasClass('dx-treelist'), 'widget class on the root element'); + assert.ok($noDataElement.length, 'widget have a \'no data\' element'); + assert.ok($noDataElement.is(':visible'), '\'No data\' element is visible'); + assert.ok($treeListElement.children().hasClass('dx-treelist-container'), 'container class on the child'); }); - this.clock.tick(); - - // act - const $headerCell = $(treeList.$element().find('.dx-header-row td').first()); - - $($headerCell).trigger('dxclick'); - this.clock.tick(); - - // assert - const $dataRows = $(treeList.$element().find('.dx-data-row')); - assert.equal($dataRows.eq(0).children().eq(0).text(), 'Name 1', 'row 0 is sorted'); - assert.equal($dataRows.eq(1).children().eq(0).text(), 'Name 2', 'row 1 is sorted'); - assert.equal($dataRows.eq(2).children().eq(0).text(), 'Name 3', 'row 2 is sorted'); - assert.equal(treeList.$element().find('.dx-sort-up').length, 1, 'one sort up indicator'); - assert.equal(treeList.$element().find('.dx-header-row td').first().find('.dx-sort-up').length, 1, 'sort indicator is rendered in first cell'); -}); + QUnit.test('Sorting should be applied on header cell click', function(assert) { + const treeList = createTreeList({ + columns: ['name', 'age'], + dataSource: [ + { id: 1, parentId: 0, name: 'Name 3', age: 19 }, + { id: 2, parentId: 0, name: 'Name 1', age: 19 }, + { id: 3, parentId: 0, name: 'Name 2', age: 18 } + ] + }); -QUnit.test('Fixed column should be rendered in separate table', function(assert) { - // act - const treeList = createTreeList({ - columns: [{ dataField: 'name', fixed: true }, 'age'], - dataSource: [ - { id: 1, parentId: 0, name: 'Name 1', age: 19 } - ] - }); + this.clock.tick(); - this.clock.tick(); + // act + const $headerCell = $(treeList.$element().find('.dx-header-row td').first()); - // assert - const $rowElement = $(treeList.getRowElement(0)); - assert.equal($rowElement.length, 2, 'two row elements for one row'); - assert.notEqual($rowElement.eq(0).closest('table').get(0), $rowElement.eq(1).closest('table').get(0), 'row elements are in different tables'); -}); + $($headerCell).trigger('dxclick'); + this.clock.tick(); -QUnit.test('Resize columns', function(assert) { - // arrange - const treeList = createTreeList({ - width: 400, - allowColumnResizing: true, - loadingTimeout: undefined, - dataSource: [{ id: 1, firstName: 'Dmitriy', lastName: 'Semenov', room: 101, birthDay: '1992/08/06' }], - columns: [{ dataField: 'firstName', width: 100 }, { dataField: 'lastName', width: 100 }, { dataField: 'room', width: 100 }, { dataField: 'birthDay', width: 100 }] + // assert + const $dataRows = $(treeList.$element().find('.dx-data-row')); + assert.equal($dataRows.eq(0).children().eq(0).text(), 'Name 1', 'row 0 is sorted'); + assert.equal($dataRows.eq(1).children().eq(0).text(), 'Name 2', 'row 1 is sorted'); + assert.equal($dataRows.eq(2).children().eq(0).text(), 'Name 3', 'row 2 is sorted'); + assert.equal(treeList.$element().find('.dx-sort-up').length, 1, 'one sort up indicator'); + assert.equal(treeList.$element().find('.dx-header-row td').first().find('.dx-sort-up').length, 1, 'sort indicator is rendered in first cell'); }); - let headersCols; - let rowsCols; - let resizeController; + QUnit.test('Fixed column should be rendered in separate table', function(assert) { // act - resizeController = treeList.getController('columnsResizer'); - resizeController._isResizing = true; - resizeController._targetPoint = { columnIndex: 1 }; - resizeController._setupResizingInfo(-9800); - resizeController._moveSeparator({ - event: { - data: resizeController, - type: 'mousemove', - pageX: -9750, - preventDefault: noop - } - }); + const treeList = createTreeList({ + columns: [{ dataField: 'name', fixed: true }, 'age'], + dataSource: [ + { id: 1, parentId: 0, name: 'Name 1', age: 19 } + ] + }); - // assert - headersCols = $('.dx-treelist-headers col'); - rowsCols = $('.dx-treelist-rowsview col'); - assert.equal($(headersCols[1]).css('width'), '150px', 'width of two column - headers view'); - assert.equal($(headersCols[2]).css('width'), '50px', 'width of three column - headers view'); - assert.equal($(rowsCols[1]).css('width'), '150px', 'width of two column - rows view'); - assert.equal($(rowsCols[2]).css('width'), '50px', 'width of three column - rows view'); -}); + this.clock.tick(); -QUnit.test('Reordering column', function(assert) { - // arrange - let $cellElement; - let $iconContainer; - const treeList = createTreeList({ - allowColumnReordering: true, - loadingTimeout: undefined, - dataSource: [{ id: 1, firstName: '1', lastName: '2', room: '3', birthDay: '4' }], - columns: ['firstName', 'lastName', 'room', 'birthDay'] + // assert + const $rowElement = $(treeList.getRowElement(0)); + assert.equal($rowElement.length, 2, 'two row elements for one row'); + assert.notEqual($rowElement.eq(0).closest('table').get(0), $rowElement.eq(1).closest('table').get(0), 'row elements are in different tables'); }); - let columnController; - // act - columnController = treeList.getController('columns'); - columnController.moveColumn(0, 3); - - // assert - $cellElement = $('#treeList').find('.dx-treelist-rowsview').find('.dx-data-row > td').first(); - $iconContainer = $('#treeList').find('.dx-treelist-rowsview').find('.dx-treelist-icon-container'); - assert.equal($iconContainer.length, 1, 'count expand icon'); - assert.equal($cellElement.children('.dx-treelist-icon-container').length, 1, 'first cell have expand icon'); - assert.equal($cellElement.text(), '2', 'first cell value'); -}); + QUnit.test('Resize columns', function(assert) { + // arrange + const treeList = createTreeList({ + width: 400, + allowColumnResizing: true, + loadingTimeout: undefined, + dataSource: [{ id: 1, firstName: 'Dmitriy', lastName: 'Semenov', room: 101, birthDay: '1992/08/06' }], + columns: [{ dataField: 'firstName', width: 100 }, { dataField: 'lastName', width: 100 }, { dataField: 'room', width: 100 }, { dataField: 'birthDay', width: 100 }] + }); + let headersCols; + let rowsCols; + let resizeController; -QUnit.test('Columns hiding - columnHidingEnabled is true', function(assert) { - // arrange, act - let $cellElement; - const treeList = createTreeList({ - width: 200, - loadingTimeout: undefined, - columnHidingEnabled: true, - dataSource: [{ id: 1, firstName: 'Blablablablablablablablablabla', lastName: 'Psy' }], - columns: ['firstName', 'lastName'] + // act + resizeController = treeList.getController('columnsResizer'); + resizeController._isResizing = true; + resizeController._targetPoint = { columnIndex: 1 }; + resizeController._setupResizingInfo(-9800); + resizeController._moveSeparator({ + event: { + data: resizeController, + type: 'mousemove', + pageX: -9750, + preventDefault: noop + } + }); + + // assert + headersCols = $('.dx-treelist-headers col'); + rowsCols = $('.dx-treelist-rowsview col'); + assert.equal($(headersCols[1]).css('width'), '150px', 'width of two column - headers view'); + assert.equal($(headersCols[2]).css('width'), '50px', 'width of three column - headers view'); + assert.equal($(rowsCols[1]).css('width'), '150px', 'width of two column - rows view'); + assert.equal($(rowsCols[2]).css('width'), '50px', 'width of three column - rows view'); }); - // assert - $cellElement = $(treeList.$element().find('.dx-header-row > td')); - assert.equal($cellElement.length, 3, 'count cell'); - assert.equal($cellElement.eq(0).text(), 'First Name', 'caption of the first cell'); - assert.notOk($cellElement.eq(0).hasClass('dx-treelist-hidden-column'), 'first cell is visible'); - assert.ok($cellElement.eq(1).hasClass('dx-treelist-hidden-column'), 'second cell is hidden'); - assert.notOk($cellElement.eq(2).hasClass('dx-command-adaptive-hidden'), 'adaptive cell is visible'); + QUnit.test('Reordering column', function(assert) { + // arrange + let $cellElement; + let $iconContainer; + const treeList = createTreeList({ + allowColumnReordering: true, + loadingTimeout: undefined, + dataSource: [{ id: 1, firstName: '1', lastName: '2', room: '3', birthDay: '4' }], + columns: ['firstName', 'lastName', 'room', 'birthDay'] + }); + let columnController; - this.clock.tick(300); + // act + columnController = treeList.getController('columns'); + columnController.moveColumn(0, 3); - // act - treeList.option('width', 800); - - // assert - $cellElement = $(treeList.$element().find('.dx-header-row > td')); - assert.equal($cellElement.length, 3, 'count cell'); - assert.equal($cellElement.eq(0).text(), 'First Name', 'caption of the first cell'); - assert.notOk($cellElement.eq(0).hasClass('dx-treelist-hidden-column'), 'first cell is visible'); - assert.equal($cellElement.eq(1).text(), 'Last Name', 'caption of the second cell'); - assert.notOk($cellElement.eq(1).hasClass('dx-treelist-hidden-column'), 'second cell is visible'); - assert.ok($cellElement.eq(2).hasClass('dx-command-adaptive-hidden'), 'adaptive cell is hidden'); -}); + // assert + $cellElement = $('#treeList').find('.dx-treelist-rowsview').find('.dx-data-row > td').first(); + $iconContainer = $('#treeList').find('.dx-treelist-rowsview').find('.dx-treelist-icon-container'); + assert.equal($iconContainer.length, 1, 'count expand icon'); + assert.equal($cellElement.children('.dx-treelist-icon-container').length, 1, 'first cell have expand icon'); + assert.equal($cellElement.text(), '2', 'first cell value'); + }); -QUnit.test('Height rows view', function(assert) { + QUnit.test('Columns hiding - columnHidingEnabled is true', function(assert) { // arrange, act - const treeList = createTreeList({ - height: 200, - showColumnHeaders: false, - loadingTimeout: undefined, - columnHidingEnabled: true, - dataSource: [ - { id: 1, name: 'Name 1', age: 10 }, - { id: 2, name: 'Name 2', age: 11 }, - { id: 3, name: 'Name 3', age: 12 }, - { id: 4, name: 'Name 4', age: 13 }, - { id: 5, name: 'Name 5', age: 14 }, - { id: 6, name: 'Name 6', age: 15 }, - { id: 7, name: 'Name 7', age: 16 } - ] + let $cellElement; + const treeList = createTreeList({ + width: 200, + loadingTimeout: undefined, + columnHidingEnabled: true, + dataSource: [{ id: 1, firstName: 'Blablablablablablablablablabla', lastName: 'Psy' }], + columns: ['firstName', 'lastName'] + }); + + // assert + $cellElement = $(treeList.$element().find('.dx-header-row > td')); + assert.equal($cellElement.length, 3, 'count cell'); + assert.equal($cellElement.eq(0).text(), 'First Name', 'caption of the first cell'); + assert.notOk($cellElement.eq(0).hasClass('dx-treelist-hidden-column'), 'first cell is visible'); + assert.ok($cellElement.eq(1).hasClass('dx-treelist-hidden-column'), 'second cell is hidden'); + assert.notOk($cellElement.eq(2).hasClass('dx-command-adaptive-hidden'), 'adaptive cell is visible'); + + this.clock.tick(300); + + // act + treeList.option('width', 800); + + // assert + $cellElement = $(treeList.$element().find('.dx-header-row > td')); + assert.equal($cellElement.length, 3, 'count cell'); + assert.equal($cellElement.eq(0).text(), 'First Name', 'caption of the first cell'); + assert.notOk($cellElement.eq(0).hasClass('dx-treelist-hidden-column'), 'first cell is visible'); + assert.equal($cellElement.eq(1).text(), 'Last Name', 'caption of the second cell'); + assert.notOk($cellElement.eq(1).hasClass('dx-treelist-hidden-column'), 'second cell is visible'); + assert.ok($cellElement.eq(2).hasClass('dx-command-adaptive-hidden'), 'adaptive cell is hidden'); }); - // assert - assert.equal(treeList.$element().find('.dx-treelist-rowsview').outerHeight(), 200, 'height rows view'); -}); + QUnit.test('Height rows view', function(assert) { + // arrange, act + const treeList = createTreeList({ + height: 200, + showColumnHeaders: false, + loadingTimeout: undefined, + columnHidingEnabled: true, + dataSource: [ + { id: 1, name: 'Name 1', age: 10 }, + { id: 2, name: 'Name 2', age: 11 }, + { id: 3, name: 'Name 3', age: 12 }, + { id: 4, name: 'Name 4', age: 13 }, + { id: 5, name: 'Name 5', age: 14 }, + { id: 6, name: 'Name 6', age: 15 }, + { id: 7, name: 'Name 7', age: 16 } + ] + }); -QUnit.test('Virtual scrolling enabled by default and should render two virtual rows', function(assert) { - const treeList = createTreeList({ - height: 50, - paging: { pageSize: 2, pageIndex: 1 }, - columns: ['name', 'age'], - dataSource: [ - { id: 1, parentId: 0, name: 'Name 1', age: 19 }, - { id: 2, parentId: 0, name: 'Name 2', age: 19 }, - { id: 3, parentId: 0, name: 'Name 3', age: 18 }, - { id: 4, parentId: 0, name: 'Name 4', age: 18 }, - { id: 5, parentId: 0, name: 'Name 5', age: 18 }, - { id: 6, parentId: 0, name: 'Name 6', age: 18 }, - { id: 7, parentId: 0, name: 'Name 7', age: 18 }, - { id: 8, parentId: 0, name: 'Name 8', age: 18 } - ] + // assert + assert.equal(treeList.$element().find('.dx-treelist-rowsview').outerHeight(), 200, 'height rows view'); }); - // act - this.clock.tick(); - - // assert - assert.equal(treeList.option('scrolling.mode'), 'virtual', 'scrolling mode is virtual'); - const $rowsViewTables = $(treeList.$element().find('.dx-treelist-rowsview table')); - assert.equal($rowsViewTables.length, 1, 'one table are rendered'); - assert.equal($rowsViewTables.eq(0).find('.dx-data-row').length, 4, 'data rows in table'); - assert.equal($rowsViewTables.eq(0).find('.dx-virtual-row').length, 2, 'two virtual rows in table'); - assert.equal($rowsViewTables.eq(0).find('.dx-freespace-row').length, 1, 'one freespace row in table'); -}); + QUnit.test('Virtual scrolling enabled by default and should render two virtual rows', function(assert) { + const treeList = createTreeList({ + height: 50, + paging: { pageSize: 2, pageIndex: 1 }, + columns: ['name', 'age'], + dataSource: [ + { id: 1, parentId: 0, name: 'Name 1', age: 19 }, + { id: 2, parentId: 0, name: 'Name 2', age: 19 }, + { id: 3, parentId: 0, name: 'Name 3', age: 18 }, + { id: 4, parentId: 0, name: 'Name 4', age: 18 }, + { id: 5, parentId: 0, name: 'Name 5', age: 18 }, + { id: 6, parentId: 0, name: 'Name 6', age: 18 }, + { id: 7, parentId: 0, name: 'Name 7', age: 18 }, + { id: 8, parentId: 0, name: 'Name 8', age: 18 } + ] + }); + // act + this.clock.tick(); -QUnit.testInActiveWindow('Ctrl + left/right keys should collapse/expand row', function(assert) { - if(devices.real().deviceType !== 'desktop') { - assert.ok(true, 'keyboard navigation is disabled for not desktop devices'); - return; - } - const treeList = createTreeList({ - columns: ['name', 'age'], - dataSource: [ - { id: 1, parentId: 0, name: 'Name 1', age: 19 }, - { id: 2, parentId: 0, name: 'Name 2', age: 19 }, - { id: 3, parentId: 2, name: 'Name 3', age: 18 } - ] + // assert + assert.equal(treeList.option('scrolling.mode'), 'virtual', 'scrolling mode is virtual'); + const $rowsViewTables = $(treeList.$element().find('.dx-treelist-rowsview table')); + assert.equal($rowsViewTables.length, 1, 'one table are rendered'); + assert.equal($rowsViewTables.eq(0).find('.dx-data-row').length, 4, 'data rows in table'); + assert.equal($rowsViewTables.eq(0).find('.dx-virtual-row').length, 2, 'two virtual rows in table'); + assert.equal($rowsViewTables.eq(0).find('.dx-freespace-row').length, 1, 'one freespace row in table'); }); - const navigationController = treeList.getController('keyboardNavigation'); - this.clock.tick(); - treeList.focus($(treeList.getCellElement(1, 0))); - this.clock.tick(); + QUnit.testInActiveWindow('Ctrl + left/right keys should collapse/expand row', function(assert) { + if(devices.real().deviceType !== 'desktop') { + assert.ok(true, 'keyboard navigation is disabled for not desktop devices'); + return; + } + const treeList = createTreeList({ + columns: ['name', 'age'], + dataSource: [ + { id: 1, parentId: 0, name: 'Name 1', age: 19 }, + { id: 2, parentId: 0, name: 'Name 2', age: 19 }, + { id: 3, parentId: 2, name: 'Name 3', age: 18 } + ] + }); + const navigationController = treeList.getController('keyboardNavigation'); - // act - navigationController._keyDownHandler({ keyName: 'rightArrow', key: 'ArrowRight', ctrl: true, originalEvent: $.Event('keydown', { target: treeList.getCellElement(1, 0), ctrlKey: true }) }); - this.clock.tick(); + this.clock.tick(); - // assert - assert.ok(treeList.isRowExpanded(2), 'second row is expanded'); + treeList.focus($(treeList.getCellElement(1, 0))); + this.clock.tick(); - // act - navigationController._keyDownHandler({ keyName: 'leftArrow', key: 'ArrowLeft', ctrl: true, originalEvent: $.Event('keydown', { target: treeList.getCellElement(1, 0), ctrlKey: true }) }); - this.clock.tick(); + // act + navigationController._keyDownHandler({ keyName: 'rightArrow', key: 'ArrowRight', ctrl: true, originalEvent: $.Event('keydown', { target: treeList.getCellElement(1, 0), ctrlKey: true }) }); + this.clock.tick(); - // assert - assert.notOk(treeList.isRowExpanded(2), 'second row is collapsed'); -}); + // assert + assert.ok(treeList.isRowExpanded(2), 'second row is expanded'); -QUnit.test('Filter Row', function(assert) { - const treeList = createTreeList({ - filterRow: { - visible: true - }, - columns: ['name', { dataField: 'age', filterValue: 19 }], - dataSource: [ - { id: 1, parentId: 0, name: 'Name 3', age: 19 }, - { id: 2, parentId: 0, name: 'Name 1', age: 19 }, - { id: 3, parentId: 0, name: 'Name 2', age: 18 } - ] + // act + navigationController._keyDownHandler({ keyName: 'leftArrow', key: 'ArrowLeft', ctrl: true, originalEvent: $.Event('keydown', { target: treeList.getCellElement(1, 0), ctrlKey: true }) }); + this.clock.tick(); + + // assert + assert.notOk(treeList.isRowExpanded(2), 'second row is collapsed'); }); - // act - this.clock.tick(); + QUnit.test('Filter Row', function(assert) { + const treeList = createTreeList({ + filterRow: { + visible: true + }, + columns: ['name', { dataField: 'age', filterValue: 19 }], + dataSource: [ + { id: 1, parentId: 0, name: 'Name 3', age: 19 }, + { id: 2, parentId: 0, name: 'Name 1', age: 19 }, + { id: 3, parentId: 0, name: 'Name 2', age: 18 } + ] + }); - // assert - assert.equal(treeList.$element().find('.dx-data-row').length, 2, 'two filtered rows are rendered'); - assert.equal(treeList.$element().find('.dx-treelist-filter-row').length, 1, 'filter row is rendered'); -}); + // act + this.clock.tick(); -// T516918 -QUnit.test('Filter menu items should have icons', function(assert) { - // arrange - let $filterMenuElement; - let $menuItemElements; - const treeList = createTreeList({ - filterRow: { - visible: true - }, - columns: ['name', { dataField: 'age', filterValue: 19 }], - dataSource: [ - { id: 1, parentId: 0, name: 'Name 3', age: 19 }, - { id: 2, parentId: 0, name: 'Name 1', age: 19 }, - { id: 3, parentId: 0, name: 'Name 2', age: 18 } - ] + // assert + assert.equal(treeList.$element().find('.dx-data-row').length, 2, 'two filtered rows are rendered'); + assert.equal(treeList.$element().find('.dx-treelist-filter-row').length, 1, 'filter row is rendered'); }); - this.clock.tick(); + // T516918 + QUnit.test('Filter menu items should have icons', function(assert) { + // arrange + let $filterMenuElement; + let $menuItemElements; + const treeList = createTreeList({ + filterRow: { + visible: true + }, + columns: ['name', { dataField: 'age', filterValue: 19 }], + dataSource: [ + { id: 1, parentId: 0, name: 'Name 3', age: 19 }, + { id: 2, parentId: 0, name: 'Name 1', age: 19 }, + { id: 3, parentId: 0, name: 'Name 2', age: 18 } + ] + }); - // act - $filterMenuElement = $(treeList.$element().find('.dx-treelist-filter-row').find('.dx-menu').first().find('.dx-menu-item')); - $($filterMenuElement).trigger('dxclick'); // show menu + this.clock.tick(); - // assert - $menuItemElements = $('.dx-overlay-wrapper').find('.dx-menu-item'); - assert.ok($menuItemElements.length > 0, 'has filter menu items'); - assert.equal($menuItemElements.first().find('.dx-icon').css('fontFamily'), 'DXIcons', 'first item has icon'); -}); + // act + $filterMenuElement = $(treeList.$element().find('.dx-treelist-filter-row').find('.dx-menu').first().find('.dx-menu-item')); + $($filterMenuElement).trigger('dxclick'); // show menu -QUnit.test('Header Filter', function(assert) { - const treeList = createTreeList({ - headerFilter: { - visible: true - }, - columns: ['name', { dataField: 'age', filterValues: [19] }], - dataSource: [ - { id: 1, parentId: 0, name: 'Name 3', age: 19 }, - { id: 2, parentId: 0, name: 'Name 1', age: 19 }, - { id: 3, parentId: 0, name: 'Name 2', age: 18 } - ] + // assert + $menuItemElements = $('.dx-overlay-wrapper').find('.dx-menu-item'); + assert.ok($menuItemElements.length > 0, 'has filter menu items'); + assert.equal($menuItemElements.first().find('.dx-icon').css('fontFamily'), 'DXIcons', 'first item has icon'); }); - // act - this.clock.tick(); + QUnit.test('Header Filter', function(assert) { + const treeList = createTreeList({ + headerFilter: { + visible: true + }, + columns: ['name', { dataField: 'age', filterValues: [19] }], + dataSource: [ + { id: 1, parentId: 0, name: 'Name 3', age: 19 }, + { id: 2, parentId: 0, name: 'Name 1', age: 19 }, + { id: 3, parentId: 0, name: 'Name 2', age: 18 } + ] + }); - // assert - assert.equal(treeList.$element().find('.dx-data-row').length, 2, 'two filtered rows are rendered'); - assert.equal(treeList.$element().find('.dx-header-filter').length, 2, 'two header filter icons area rendered'); -}); + // act + this.clock.tick(); -QUnit.test('Expanding of all items should work correctly after clearing filter', function(assert) { - const treeList = createTreeList({ - headerFilter: { - visible: true - }, - autoExpandAll: true, - columns: ['name', { dataField: 'age', filterValues: [19], allowFiltering: true }, 'gender'], - dataSource: [ - { id: 1, parentId: 0, name: 'Name 3', age: 19, gender: 'male' }, - { id: 2, parentId: 1, name: 'Name 1', age: 19, gender: 'female' }, - { id: 3, parentId: 1, name: 'Name 2', age: 18, gender: 'male' }, - { id: 4, parentId: 2, name: 'Name 4', age: 19, gender: 'male' }, - { id: 5, parentId: 2, name: 'Name 5', age: 20, gender: 'female' }, - { id: 6, parentId: 3, name: 'Name 6', age: 18, gender: 'male' } - ] + // assert + assert.equal(treeList.$element().find('.dx-data-row').length, 2, 'two filtered rows are rendered'); + assert.equal(treeList.$element().find('.dx-header-filter').length, 2, 'two header filter icons area rendered'); }); - this.clock.tick(); - assert.equal(treeList.$element().find('.dx-data-row').length, 3, 'filtered rows are rendered'); - treeList.filter('gender', '=', 'male'); - this.clock.tick(); - assert.equal(treeList.$element().find('.dx-data-row').length, 3, 'filtered rows are rendered'); + QUnit.test('Expanding of all items should work correctly after clearing filter', function(assert) { + const treeList = createTreeList({ + headerFilter: { + visible: true + }, + autoExpandAll: true, + columns: ['name', { dataField: 'age', filterValues: [19], allowFiltering: true }, 'gender'], + dataSource: [ + { id: 1, parentId: 0, name: 'Name 3', age: 19, gender: 'male' }, + { id: 2, parentId: 1, name: 'Name 1', age: 19, gender: 'female' }, + { id: 3, parentId: 1, name: 'Name 2', age: 18, gender: 'male' }, + { id: 4, parentId: 2, name: 'Name 4', age: 19, gender: 'male' }, + { id: 5, parentId: 2, name: 'Name 5', age: 20, gender: 'female' }, + { id: 6, parentId: 3, name: 'Name 6', age: 18, gender: 'male' } + ] + }); - // act - treeList.clearFilter(); - this.clock.tick(); + this.clock.tick(); + assert.equal(treeList.$element().find('.dx-data-row').length, 3, 'filtered rows are rendered'); + treeList.filter('gender', '=', 'male'); + this.clock.tick(); + assert.equal(treeList.$element().find('.dx-data-row').length, 3, 'filtered rows are rendered'); - // assert - assert.equal(treeList.$element().find('.dx-data-row').length, 6, 'six filtered rows are rendered'); -}); + // act + treeList.clearFilter(); + this.clock.tick(); -QUnit.test('Items should be collapsed after clearing filter, autoExpandAll = false', function(assert) { - const treeList = createTreeList({ - headerFilter: { - visible: true - }, - autoExpandAll: false, - columns: ['name', { dataField: 'age', filterValues: [19], allowFiltering: true }], - dataSource: [ - { id: 1, parentId: 0, name: 'Name 3', age: 19 }, - { id: 2, parentId: 1, name: 'Name 1', age: 19 }, - { id: 3, parentId: 2, name: 'Name 2', age: 18 }, - { id: 4, parentId: 0, name: 'Name 4', age: 19 }, - { id: 5, parentId: 4, name: 'Name 5', age: 20 }, - { id: 6, parentId: 5, name: 'Name 6', age: 18 } - ] + // assert + assert.equal(treeList.$element().find('.dx-data-row').length, 6, 'six filtered rows are rendered'); }); - this.clock.tick(); - assert.equal(treeList.$element().find('.dx-data-row').length, 3, 'filtered rows are rendered'); + QUnit.test('Items should be collapsed after clearing filter, autoExpandAll = false', function(assert) { + const treeList = createTreeList({ + headerFilter: { + visible: true + }, + autoExpandAll: false, + columns: ['name', { dataField: 'age', filterValues: [19], allowFiltering: true }], + dataSource: [ + { id: 1, parentId: 0, name: 'Name 3', age: 19 }, + { id: 2, parentId: 1, name: 'Name 1', age: 19 }, + { id: 3, parentId: 2, name: 'Name 2', age: 18 }, + { id: 4, parentId: 0, name: 'Name 4', age: 19 }, + { id: 5, parentId: 4, name: 'Name 5', age: 20 }, + { id: 6, parentId: 5, name: 'Name 6', age: 18 } + ] + }); - // act - treeList.clearFilter(); - this.clock.tick(); + this.clock.tick(); + assert.equal(treeList.$element().find('.dx-data-row').length, 3, 'filtered rows are rendered'); - // assert - assert.equal(treeList.$element().find('.dx-data-row').length, 2, 'two rows are rendered'); -}); + // act + treeList.clearFilter(); + this.clock.tick(); -QUnit.test('Search Panel', function(assert) { - const treeList = createTreeList({ - columns: ['name', 'age'], - searchPanel: { - visible: true, - text: 'Name 1' - }, - dataSource: [ - { id: 1, parentId: 0, name: 'Name 3', age: 19 }, - { id: 2, parentId: 0, name: 'Name 1', age: 19 }, - { id: 3, parentId: 0, name: 'Name 2', age: 18 } - ] + // assert + assert.equal(treeList.$element().find('.dx-data-row').length, 2, 'two rows are rendered'); }); - // act - this.clock.tick(); + QUnit.test('Search Panel', function(assert) { + const treeList = createTreeList({ + columns: ['name', 'age'], + searchPanel: { + visible: true, + text: 'Name 1' + }, + dataSource: [ + { id: 1, parentId: 0, name: 'Name 3', age: 19 }, + { id: 2, parentId: 0, name: 'Name 1', age: 19 }, + { id: 3, parentId: 0, name: 'Name 2', age: 18 } + ] + }); + // act + this.clock.tick(); - // assert - assert.equal(treeList.$element().find('.dx-data-row').length, 1, 'one filtered row is rendered'); - assert.equal(treeList.$element().find('.dx-toolbar .dx-searchbox').length, 1, 'searchPanel is rendered'); - assert.equal(treeList.$element().find('.dx-toolbar .dx-searchbox').dxTextBox('instance').option('value'), 'Name 1', 'searchPanel text is applied'); -}); -QUnit.test('Selectable treeList should have right default options', function(assert) { - const treeList = createTreeList({ - columns: ['name', 'age'], - selection: { mode: 'multiple' }, - dataSource: [ - { id: 1, parentId: 0, name: 'Name 3', age: 19 }, - { id: 2, parentId: 0, name: 'Name 1', age: 19 }, - { id: 3, parentId: 0, name: 'Name 2', age: 18 } - ] + // assert + assert.equal(treeList.$element().find('.dx-data-row').length, 1, 'one filtered row is rendered'); + assert.equal(treeList.$element().find('.dx-toolbar .dx-searchbox').length, 1, 'searchPanel is rendered'); + assert.equal(treeList.$element().find('.dx-toolbar .dx-searchbox').dxTextBox('instance').option('value'), 'Name 1', 'searchPanel text is applied'); }); - // act - this.clock.tick(); + QUnit.test('Selectable treeList should have right default options', function(assert) { + const treeList = createTreeList({ + columns: ['name', 'age'], + selection: { mode: 'multiple' }, + dataSource: [ + { id: 1, parentId: 0, name: 'Name 3', age: 19 }, + { id: 2, parentId: 0, name: 'Name 1', age: 19 }, + { id: 3, parentId: 0, name: 'Name 2', age: 18 } + ] + }); - // assert - assert.equal(treeList.option('selection.showCheckBoxesMode'), 'always', 'showCheckBoxesMode is always'); -}); + // act + this.clock.tick(); -QUnit.test('Click on selectCheckBox shouldn\'t render editor, editing & selection', function(assert) { - createTreeList({ - columns: ['name', 'age'], - selection: { mode: 'multiple' }, - editing: { - mode: 'batch', - allowUpdating: true - }, - dataSource: [ - { id: 1, parentId: 0, name: 'Name 3', age: 19 } - ] + // assert + assert.equal(treeList.option('selection.showCheckBoxesMode'), 'always', 'showCheckBoxesMode is always'); }); - // act - this.clock.tick(); - const $selectCheckbox = $('#treeList').find('.dx-treelist-cell-expandable').eq(0).find('.dx-select-checkbox').eq(0); - $($selectCheckbox).trigger('dxclick'); - this.clock.tick(); + QUnit.test('Click on selectCheckBox shouldn\'t render editor, editing & selection', function(assert) { + createTreeList({ + columns: ['name', 'age'], + selection: { mode: 'multiple' }, + editing: { + mode: 'batch', + allowUpdating: true + }, + dataSource: [ + { id: 1, parentId: 0, name: 'Name 3', age: 19 } + ] + }); - // assert - assert.notOk($('#treeList').find('.dx-texteditor').length, 'Editing textEditor wasn\'t rendered'); -}); + // act + this.clock.tick(); + const $selectCheckbox = $('#treeList').find('.dx-treelist-cell-expandable').eq(0).find('.dx-select-checkbox').eq(0); + $($selectCheckbox).trigger('dxclick'); + this.clock.tick(); -// T857405 -QUnit.test('Assign new values using the promise parameter in the onInitNewRow', function(assert) { - // arrange - let visibleRows; - - const rowData = { room: 42 }; - - const treeList = createTreeList({ - editing: { - allowAdding: true, - mode: 'row' - }, - dataSource: [], - columns: ['room'], - onInitNewRow: function(e) { - e.promise = $.Deferred(); - setTimeout(() => { - e.data = rowData; - e.promise.resolve(); - }, 500); - } + // assert + assert.notOk($('#treeList').find('.dx-texteditor').length, 'Editing textEditor wasn\'t rendered'); }); - // act - treeList.addRow(); + // T857405 + QUnit.test('Assign new values using the promise parameter in the onInitNewRow', function(assert) { + // arrange + let visibleRows; - visibleRows = treeList.getVisibleRows(); + const rowData = { room: 42 }; - // assert - assert.equal(visibleRows.length, 0); + const treeList = createTreeList({ + editing: { + allowAdding: true, + mode: 'row' + }, + dataSource: [], + columns: ['room'], + onInitNewRow: function(e) { + e.promise = $.Deferred(); + setTimeout(() => { + e.data = rowData; + e.promise.resolve(); + }, 500); + } + }); - // act - this.clock.tick(500); + // act + treeList.addRow(); - treeList.saveEditData(); - this.clock.tick(); + visibleRows = treeList.getVisibleRows(); - visibleRows = treeList.getVisibleRows(); + // assert + assert.equal(visibleRows.length, 0); - // assert - assert.equal(visibleRows.length, 1, 'row was added'); - assert.deepEqual(visibleRows[0].data, rowData, 'row data'); -}); + // act + this.clock.tick(500); -// T742147 -QUnit.test('Selection checkbox should be rendered if first column is lookup', function(assert) { - const treeList = createTreeList({ - columns: [{ - dataField: 'nameId', - lookup: { - dataSource: [{ id: 1, name: 'Name 1' }], - valueExpr: 'id', - displayExpr: 'name' - } - }, 'age'], - selection: { - mode: 'multiple' - }, - dataSource: [ - { id: 1, parentId: 0, nameId: 1, age: 19 } - ] + treeList.saveEditData(); + this.clock.tick(); + + visibleRows = treeList.getVisibleRows(); + + // assert + assert.equal(visibleRows.length, 1, 'row was added'); + assert.deepEqual(visibleRows[0].data, rowData, 'row data'); }); - // act - this.clock.tick(); + // T742147 + QUnit.test('Selection checkbox should be rendered if first column is lookup', function(assert) { + const treeList = createTreeList({ + columns: [{ + dataField: 'nameId', + lookup: { + dataSource: [{ id: 1, name: 'Name 1' }], + valueExpr: 'id', + displayExpr: 'name' + } + }, 'age'], + selection: { + mode: 'multiple' + }, + dataSource: [ + { id: 1, parentId: 0, nameId: 1, age: 19 } + ] + }); - // assert - const $firstDataCell = $(treeList.getCellElement(0, 0)); - assert.equal($firstDataCell.find('.dx-select-checkbox.dx-checkbox').length, 1, 'first cell contains select checkbox'); - assert.equal($firstDataCell.find('.dx-treelist-text-content').text(), 'Name 1', 'first cell text'); -}); + // act + this.clock.tick(); -QUnit.test('Filter row should not contains selection checkboxes', function(assert) { - createTreeList({ - columns: ['name', 'age'], - selection: { mode: 'multiple' }, - filterRow: { - visible: true - }, - dataSource: [ - { id: 1, parentId: 0, name: 'Name 3', age: 19 } - ] + // assert + const $firstDataCell = $(treeList.getCellElement(0, 0)); + assert.equal($firstDataCell.find('.dx-select-checkbox.dx-checkbox').length, 1, 'first cell contains select checkbox'); + assert.equal($firstDataCell.find('.dx-treelist-text-content').text(), 'Name 1', 'first cell text'); }); - // act - this.clock.tick(); + QUnit.test('Filter row should not contains selection checkboxes', function(assert) { + createTreeList({ + columns: ['name', 'age'], + selection: { mode: 'multiple' }, + filterRow: { + visible: true + }, + dataSource: [ + { id: 1, parentId: 0, name: 'Name 3', age: 19 } + ] + }); - // assert - assert.equal($('#treeList').find('.dx-treelist-filter-row').length, 1, 'filter row is rendered'); - assert.equal($('#treeList').find('.dx-checkbox').length, 2, 'selection chebkboxes are rendered'); - assert.equal($('#treeList').find('.dx-treelist-filter-row .dx-checkbox').length, 0, 'no selection chebkboxes in filter row'); -}); + // act + this.clock.tick(); -QUnit.test('Aria accessibility', function(assert) { - // arrange, act - let $dataRows; - let $headerTable; - let $dataTable; - let $treeList; - const treeList = createTreeList({ - dataSource: [ - { id: 1, parentId: 0, name: 'Name 1', age: 19 }, - { id: 2, parentId: 1, name: 'Name 2', age: 19 }, - { id: 3, parentId: 2, name: 'Name 3', age: 18 }, - { id: 4, parentId: 0, name: 'Name 4', age: 18 } - ], - expandedRowKeys: [1] + // assert + assert.equal($('#treeList').find('.dx-treelist-filter-row').length, 1, 'filter row is rendered'); + assert.equal($('#treeList').find('.dx-checkbox').length, 2, 'selection chebkboxes are rendered'); + assert.equal($('#treeList').find('.dx-treelist-filter-row .dx-checkbox').length, 0, 'no selection chebkboxes in filter row'); }); - this.clock.tick(); + QUnit.test('Aria accessibility', function(assert) { + // arrange, act + let $dataRows; + let $headerTable; + let $dataTable; + let $treeList; + const treeList = createTreeList({ + dataSource: [ + { id: 1, parentId: 0, name: 'Name 1', age: 19 }, + { id: 2, parentId: 1, name: 'Name 2', age: 19 }, + { id: 3, parentId: 2, name: 'Name 3', age: 18 }, + { id: 4, parentId: 0, name: 'Name 4', age: 18 } + ], + expandedRowKeys: [1] + }); - // assert - $treeList = $(treeList.$element()); + this.clock.tick(); - assert.equal($treeList.find('.dx-gridbase-container').attr('role'), 'treegrid', 'treeList base container - value of \'role\' attribute'); + // assert + $treeList = $(treeList.$element()); - $headerTable = $treeList.find('.dx-treelist-headers table').first(); - assert.equal($headerTable.attr('role'), 'presentation', 'header table - value of \'role\' attribute'); + assert.equal($treeList.find('.dx-gridbase-container').attr('role'), 'treegrid', 'treeList base container - value of \'role\' attribute'); - $dataTable = $treeList.find('.dx-treelist-rowsview table').first(); - assert.equal($dataTable.attr('role'), 'presentation', 'data table - value of \'role\' attribute'); + $headerTable = $treeList.find('.dx-treelist-headers table').first(); + assert.equal($headerTable.attr('role'), 'presentation', 'header table - value of \'role\' attribute'); - $dataRows = $dataTable.find('.dx-data-row'); - assert.equal($dataRows.eq(0).attr('aria-expanded'), 'true', 'first data row - value of \'aria-expanded\' attribute'); - assert.equal($dataRows.eq(0).attr('aria-level'), '0', 'first data row - value of \'aria-level\' attribute'); - assert.equal($dataRows.eq(1).attr('aria-expanded'), 'false', 'second data row - value of \'aria-expanded\' attribute'); - assert.equal($dataRows.eq(1).attr('aria-level'), '1', 'second data row - value of \'aria-level\' attribute'); - assert.equal($dataRows.eq(2).attr('aria-expanded'), undefined, 'third data row hasn\'t the \'aria-expanded\' attribute'); - assert.equal($dataRows.eq(2).attr('aria-level'), '0', 'third data row - value of \'aria-level\' attribute'); -}); + $dataTable = $treeList.find('.dx-treelist-rowsview table').first(); + assert.equal($dataTable.attr('role'), 'presentation', 'data table - value of \'role\' attribute'); -QUnit.test('Command buttons should contains aria-label accessibility attribute if rendered as icons (T755185)', function(assert) { - // arrange - const columnsWrapper = treeListWrapper.columns; - const clock = sinon.useFakeTimers(); - const treeList = createTreeList({ - dataSource: [ - { id: 0, parentId: -1, c0: 'c0' }, - { id: 1, parentId: 0, c0: 'c1' } - ], - columns: [ - { - type: 'buttons', - buttons: ['add', 'edit', 'delete', 'save', 'cancel'] - }, - 'id' - ], - editing: { - allowUpdating: true, - allowDeleting: true, - useIcons: true - } + $dataRows = $dataTable.find('.dx-data-row'); + assert.equal($dataRows.eq(0).attr('aria-expanded'), 'true', 'first data row - value of \'aria-expanded\' attribute'); + assert.equal($dataRows.eq(0).attr('aria-level'), '0', 'first data row - value of \'aria-level\' attribute'); + assert.equal($dataRows.eq(1).attr('aria-expanded'), 'false', 'second data row - value of \'aria-expanded\' attribute'); + assert.equal($dataRows.eq(1).attr('aria-level'), '1', 'second data row - value of \'aria-level\' attribute'); + assert.equal($dataRows.eq(2).attr('aria-expanded'), undefined, 'third data row hasn\'t the \'aria-expanded\' attribute'); + assert.equal($dataRows.eq(2).attr('aria-level'), '0', 'third data row - value of \'aria-level\' attribute'); }); - clock.tick(); + QUnit.test('Command buttons should contains aria-label accessibility attribute if rendered as icons (T755185)', function(assert) { + // arrange + const columnsWrapper = treeListWrapper.columns; + const clock = sinon.useFakeTimers(); + const treeList = createTreeList({ + dataSource: [ + { id: 0, parentId: -1, c0: 'c0' }, + { id: 1, parentId: 0, c0: 'c1' } + ], + columns: [ + { + type: 'buttons', + buttons: ['add', 'edit', 'delete', 'save', 'cancel'] + }, + 'id' + ], + editing: { + allowUpdating: true, + allowDeleting: true, + useIcons: true + } + }); - // assert - columnsWrapper.getCommandButtons().each((_, button) => { - const ariaLabel = $(button).attr('aria-label'); - assert.ok(ariaLabel && ariaLabel.length, `aria-label '${ariaLabel}'`); - }); + clock.tick(); - // act - treeList.editRow(0); - // assert - columnsWrapper.getCommandButtons().each((_, button) => { - const ariaLabel = $(button).attr('aria-label'); - assert.ok(ariaLabel && ariaLabel.length, `aria-label '${ariaLabel}'`); - }); + // assert + columnsWrapper.getCommandButtons().each((_, button) => { + const ariaLabel = $(button).attr('aria-label'); + assert.ok(ariaLabel && ariaLabel.length, `aria-label '${ariaLabel}'`); + }); - clock.restore(); -}); + // act + treeList.editRow(0); + // assert + columnsWrapper.getCommandButtons().each((_, button) => { + const ariaLabel = $(button).attr('aria-label'); + assert.ok(ariaLabel && ariaLabel.length, `aria-label '${ariaLabel}'`); + }); -// T632028 -QUnit.test('Display context menu', function(assert) { - // arrange, act - const contextMenuItems = [{ text: 'test' }]; - const treeList = createTreeList({ - dataSource: [ - { id: 1 } - ], - onContextMenuPreparing: function($event) { - $event.items = contextMenuItems; - } + clock.restore(); }); - this.clock.tick(); + // T632028 + QUnit.test('Display context menu', function(assert) { + // arrange, act + const contextMenuItems = [{ text: 'test' }]; + const treeList = createTreeList({ + dataSource: [ + { id: 1 } + ], + onContextMenuPreparing: function($event) { + $event.items = contextMenuItems; + } + }); - const $cellElement = $(treeList.getCellElement(0, 0)); - $cellElement.trigger('contextmenu'); - const contextMenuInstance = treeList.getView('contextMenuView').element().dxContextMenu('instance'); + this.clock.tick(); - // assert - assert.ok(contextMenuInstance); - assert.deepEqual(contextMenuInstance.option('items'), contextMenuItems); -}); + const $cellElement = $(treeList.getCellElement(0, 0)); + $cellElement.trigger('contextmenu'); + const contextMenuInstance = treeList.getView('contextMenuView').element().dxContextMenu('instance'); -QUnit.test('filterSyncEnabled is working in TreeList', function(assert) { - // act - const treeList = createTreeList({ - filterSyncEnabled: true, - columns: [{ dataField: 'field', allowHeaderFiltering: true, filterValues: [2] }] + // assert + assert.ok(contextMenuInstance); + assert.deepEqual(contextMenuInstance.option('items'), contextMenuItems); }); + QUnit.test('filterSyncEnabled is working in TreeList', function(assert) { // act - treeList.columnOption('field', { filterValues: [2, 1] }); - - // assert - assert.deepEqual(treeList.option('filterValue'), ['field', 'anyof', [2, 1]]); -}); + const treeList = createTreeList({ + filterSyncEnabled: true, + columns: [{ dataField: 'field', allowHeaderFiltering: true, filterValues: [2] }] + }); -QUnit.test('filterBulider is working in TreeList', function(assert) { - // arrange - const handlerInit = sinon.spy(); + // act + treeList.columnOption('field', { filterValues: [2, 1] }); - // act - const treeList = createTreeList({ - filterBuilder: { - onInitialized: handlerInit - }, - columns: [{ dataField: 'field' }] + // assert + assert.deepEqual(treeList.option('filterValue'), ['field', 'anyof', [2, 1]]); }); - // assert - assert.equal(handlerInit.called, 0); + QUnit.test('filterBulider is working in TreeList', function(assert) { + // arrange + const handlerInit = sinon.spy(); + + // act + const treeList = createTreeList({ + filterBuilder: { + onInitialized: handlerInit + }, + columns: [{ dataField: 'field' }] + }); - // act - treeList.option('filterBuilderPopup.visible', true); + // assert + assert.equal(handlerInit.called, 0); - // assert - assert.equal(handlerInit.called, 1); -}); + // act + treeList.option('filterBuilderPopup.visible', true); + + // assert + assert.equal(handlerInit.called, 1); + }); -// T812031 -QUnit.test('Change filterPanel.visible to false', function(assert) { + // T812031 + QUnit.test('Change filterPanel.visible to false', function(assert) { // arrange // act - const treeList = createTreeList({ - dataSource: [], - filterPanel: { - visible: true - }, - columns: [{ dataField: 'field' }] - }); + const treeList = createTreeList({ + dataSource: [], + filterPanel: { + visible: true + }, + columns: [{ dataField: 'field' }] + }); - this.clock.tick(); + this.clock.tick(); - // assert - assert.ok(treeList.$element().find('.dx-treelist-filter-panel').is(':visible'), 'filter panel is visible'); + // assert + assert.ok(treeList.$element().find('.dx-treelist-filter-panel').is(':visible'), 'filter panel is visible'); - // act - treeList.option('filterPanel.visible', false); + // act + treeList.option('filterPanel.visible', false); - // assert - assert.notOk(treeList.$element().find('.dx-treelist-filter-panel').is(':visible'), 'filter panel is hidden'); -}); + // assert + assert.notOk(treeList.$element().find('.dx-treelist-filter-panel').is(':visible'), 'filter panel is hidden'); + }); -QUnit.test('TreeList with paging', function(assert) { + QUnit.test('TreeList with paging', function(assert) { // arrange, act - let $treeListElement; - const treeList = createTreeList({ - autoExpandAll: true, - dataSource: generateData(5), - paging: { - pageSize: 5 - }, - pager: { - visible: true, - showPageSizeSelector: true, - allowedPageSizes: [2, 5, 8] - } - }); + let $treeListElement; + const treeList = createTreeList({ + autoExpandAll: true, + dataSource: generateData(5), + paging: { + pageSize: 5 + }, + pager: { + visible: true, + showPageSizeSelector: true, + allowedPageSizes: [2, 5, 8] + } + }); - this.clock.tick(); + this.clock.tick(); - // assert - $treeListElement = $(treeList.$element()); - assert.strictEqual($treeListElement.find('.dx-treelist-pager').length, 1, 'has pager'); - assert.strictEqual($treeListElement.find('.dx-page').length, 2, 'number of containers for page'); - assert.ok($treeListElement.find('.dx-page').first().hasClass('dx-selection'), 'current page - first'); - assert.strictEqual($treeListElement.find('.dx-page-size').length, 3, 'number of containers for page sizes'); + // assert + $treeListElement = $(treeList.$element()); + assert.strictEqual($treeListElement.find('.dx-treelist-pager').length, 1, 'has pager'); + assert.strictEqual($treeListElement.find('.dx-page').length, 2, 'number of containers for page'); + assert.ok($treeListElement.find('.dx-page').first().hasClass('dx-selection'), 'current page - first'); + assert.strictEqual($treeListElement.find('.dx-page-size').length, 3, 'number of containers for page sizes'); + }); }); -QUnit.module('Option Changed', defaultModuleConfig); +QUnit.module('Option Changed', defaultModuleConfig, () => { -QUnit.test('Change dataSource, selectedRowKeys and scrolling options together', function(assert) { + QUnit.test('Change dataSource, selectedRowKeys and scrolling options together', function(assert) { // arrange - const treeList = createTreeList({}); - this.clock.tick(30); + const treeList = createTreeList({}); + this.clock.tick(30); - // act - treeList.option({ - dataSource: [{ id: 1 }], - selectedRowKeys: [1], - scrolling: { mode: 'virtual' } - }); - this.clock.tick(30); + // act + treeList.option({ + dataSource: [{ id: 1 }], + selectedRowKeys: [1], + scrolling: { mode: 'virtual' } + }); + this.clock.tick(30); - // assert - assert.strictEqual(treeList.getVisibleRows().length, 1, 'row count'); -}); + // assert + assert.strictEqual(treeList.getVisibleRows().length, 1, 'row count'); + }); -// T575440 -QUnit.test('Change options and call selectRows', function(assert) { + // T575440 + QUnit.test('Change options and call selectRows', function(assert) { // arrange - const createOptions = function() { - return { - dataSource: [{ - id: 1, - text: 'Brazil' - }, { - id: 2, - text: 'Spain' - }, { - id: 3, - text: 'USA' - }], - selectedRowKeys: [1, 2, 3], - selection: { - mode: 'multiple', - recursive: true - }, - scrolling: { - mode: 'virtual' - } + const createOptions = function() { + return { + dataSource: [{ + id: 1, + text: 'Brazil' + }, { + id: 2, + text: 'Spain' + }, { + id: 3, + text: 'USA' + }], + selectedRowKeys: [1, 2, 3], + selection: { + mode: 'multiple', + recursive: true + }, + scrolling: { + mode: 'virtual' + } + }; }; - }; - - const treeList = createTreeList(createOptions()); - this.clock.tick(30); - // act - treeList.option(createOptions()); - treeList.selectRows([1, 2, 3]); - this.clock.tick(30); + const treeList = createTreeList(createOptions()); + this.clock.tick(30); - // assert - assert.strictEqual(treeList.getSelectedRowsData().length, 3, 'selected rows'); -}); + // act + treeList.option(createOptions()); + treeList.selectRows([1, 2, 3]); + this.clock.tick(30); -// T576806 -QUnit.test('Pages should be correctly loaded after change dataSource and selectedRowKeys options', function(assert) { - const treeList = createTreeList({ - height: 1500, - autoExpandAll: true + // assert + assert.strictEqual(treeList.getSelectedRowsData().length, 3, 'selected rows'); }); - this.clock.tick(300); + // T576806 + QUnit.test('Pages should be correctly loaded after change dataSource and selectedRowKeys options', function(assert) { + const treeList = createTreeList({ + height: 1500, + autoExpandAll: true + }); - // act - treeList.option({ - dataSource: generateData(20), - selectedRowKeys: [1] - }); - this.clock.tick(0); + this.clock.tick(300); - // assert - assert.strictEqual(treeList.getVisibleRows().length, 40, 'row count'); -}); + // act + treeList.option({ + dataSource: generateData(20), + selectedRowKeys: [1] + }); + this.clock.tick(0); -// T591390 -QUnit.test('Change expandedRowKeys', function(assert) { - // arrange - const treeList = createTreeList({ - dataSource: [ - { id: 1, parentId: 0, name: 'Name 1', age: 16 }, - { id: 2, parentId: 1, name: 'Name 2', age: 17 }, - { id: 3, parentId: 2, name: 'Name 3', age: 18 } - ] + // assert + assert.strictEqual(treeList.getVisibleRows().length, 40, 'row count'); }); - this.clock.tick(30); - // assert - assert.strictEqual(treeList.getVisibleRows().length, 1, 'row count'); + // T591390 + QUnit.test('Change expandedRowKeys', function(assert) { + // arrange + const treeList = createTreeList({ + dataSource: [ + { id: 1, parentId: 0, name: 'Name 1', age: 16 }, + { id: 2, parentId: 1, name: 'Name 2', age: 17 }, + { id: 3, parentId: 2, name: 'Name 3', age: 18 } + ] + }); + this.clock.tick(30); - // act - treeList.option('expandedRowKeys', [1, 2]); - this.clock.tick(30); + // assert + assert.strictEqual(treeList.getVisibleRows().length, 1, 'row count'); - // assert - assert.strictEqual(treeList.getVisibleRows().length, 3, 'row count'); -}); + // act + treeList.option('expandedRowKeys', [1, 2]); + this.clock.tick(30); -QUnit.test('TreeList with columnAutoWidth should be rendered', function(assert) { - // act - const treeList = createTreeList({ - columnAutoWidth: true, - columns: ['name', 'age'], - dataSource: [ - { id: 1, parentId: 0, name: 'Name 1', age: 19 } - ] + // assert + assert.strictEqual(treeList.getVisibleRows().length, 3, 'row count'); }); - this.clock.tick(); + QUnit.test('TreeList with columnAutoWidth should be rendered', function(assert) { + // act + const treeList = createTreeList({ + columnAutoWidth: true, + columns: ['name', 'age'], + dataSource: [ + { id: 1, parentId: 0, name: 'Name 1', age: 19 } + ] + }); + + this.clock.tick(); - // assert - assert.equal(treeList.$element().find('.dx-treelist-headers .dx-header-row').length, 1, 'header row is rendered'); - assert.equal(treeList.$element().find('.dx-treelist-rowsview .dx-data-row').length, 1, 'data row is rendered'); -}); + // assert + assert.equal(treeList.$element().find('.dx-treelist-headers .dx-header-row').length, 1, 'header row is rendered'); + assert.equal(treeList.$element().find('.dx-treelist-rowsview .dx-data-row').length, 1, 'data row is rendered'); + }); -QUnit.test('Virtual columns', function(assert) { + QUnit.test('Virtual columns', function(assert) { // arrange, act - const columns = []; + const columns = []; - for(let i = 1; i <= 20; i++) { - columns.push('field' + i); - } - - const treeList = createTreeList({ - width: 200, - columnWidth: 50, - dataSource: [{}], - columns: columns, - scrolling: { - columnRenderingMode: 'virtual' + for(let i = 1; i <= 20; i++) { + columns.push('field' + i); } - }); - this.clock.tick(0); + const treeList = createTreeList({ + width: 200, + columnWidth: 50, + dataSource: [{}], + columns: columns, + scrolling: { + columnRenderingMode: 'virtual' + } + }); - // assert - assert.equal(treeList.getVisibleColumns().length, 6, 'visible column count'); -}); + this.clock.tick(0); -QUnit.test('Call getSelectedRowKeys with \'leavesOnly\' parameter and wrong selectedKeys after dataSource change', function(assert) { - const treeList = createTreeList({ - dataSource: [ - { id: 1, field1: 'test1' }, - { id: 2, parentId: 1, field1: 'test2' }, - { id: 3, field1: 'test3' } - ], - selection: { - mode: 'multiple', - recursive: true - }, - selectedRowKeys: [1, 3], + // assert + assert.equal(treeList.getVisibleColumns().length, 6, 'visible column count'); }); - this.clock.tick(30); - // act - treeList.option({ - dataSource: [ - { id: 1, field1: 'test1' }, - { id: 2, parentId: 1, field1: 'test2' } - ] - }); + QUnit.test('Call getSelectedRowKeys with \'leavesOnly\' parameter and wrong selectedKeys after dataSource change', function(assert) { + const treeList = createTreeList({ + dataSource: [ + { id: 1, field1: 'test1' }, + { id: 2, parentId: 1, field1: 'test2' }, + { id: 3, field1: 'test3' } + ], + selection: { + mode: 'multiple', + recursive: true + }, + selectedRowKeys: [1, 3], + }); + this.clock.tick(30); - // assert - assert.deepEqual(treeList.getSelectedRowKeys('leavesOnly'), [], 'dataSource is not loaded yet'); + // act + treeList.option({ + dataSource: [ + { id: 1, field1: 'test1' }, + { id: 2, parentId: 1, field1: 'test2' } + ] + }); - this.clock.tick(30); - assert.deepEqual(treeList.getSelectedRowKeys('leavesOnly'), [2], 'dataSource is reloaded'); -}); + // assert + assert.deepEqual(treeList.getSelectedRowKeys('leavesOnly'), [], 'dataSource is not loaded yet'); -// T664886 -QUnit.test('Highlight searchText in expandable column', function(assert) { - const treeList = createTreeList({ - dataSource: [ - { id: 1, parentId: 0, name: 'Name 1', age: 16 }, - { id: 2, parentId: 1, name: 'Name 2', age: 17 }, - { id: 3, parentId: 2, name: 'Name', age: 18 } - ], - searchPanel: { - text: '3' - } + this.clock.tick(30); + assert.deepEqual(treeList.getSelectedRowKeys('leavesOnly'), [2], 'dataSource is reloaded'); }); - const searchTextSelector = '.dx-treelist-search-text'; - this.clock.tick(30); + // T664886 + QUnit.test('Highlight searchText in expandable column', function(assert) { + const treeList = createTreeList({ + dataSource: [ + { id: 1, parentId: 0, name: 'Name 1', age: 16 }, + { id: 2, parentId: 1, name: 'Name 2', age: 17 }, + { id: 3, parentId: 2, name: 'Name', age: 18 } + ], + searchPanel: { + text: '3' + } + }); + const searchTextSelector = '.dx-treelist-search-text'; - assert.equal(treeList.$element().find(searchTextSelector).length, 1); -}); + this.clock.tick(30); -// T835655 -QUnit.test('Change searchPanel.text', function(assert) { - const treeList = createTreeList({ - dataSource: [ - { id: 1, parentId: 0, name: 'Name 1', age: 16 }, - { id: 2, parentId: 1, name: 'Name 2', age: 17 }, - { id: 3, parentId: 2, name: 'Name', age: 18 } - ], - searchPanel: { - visible: true, - text: '3' - } + assert.equal(treeList.$element().find(searchTextSelector).length, 1); }); - const searchPanelSelector = '.dx-treelist-search-panel'; - let $searchInput; - this.clock.tick(30); + // T835655 + QUnit.test('Change searchPanel.text', function(assert) { + const treeList = createTreeList({ + dataSource: [ + { id: 1, parentId: 0, name: 'Name 1', age: 16 }, + { id: 2, parentId: 1, name: 'Name 2', age: 17 }, + { id: 3, parentId: 2, name: 'Name', age: 18 } + ], + searchPanel: { + visible: true, + text: '3' + } + }); + const searchPanelSelector = '.dx-treelist-search-panel'; + let $searchInput; + + this.clock.tick(30); - // act, assert - $searchInput = treeList.$element().find(searchPanelSelector).find('input'); + // act, assert + $searchInput = treeList.$element().find(searchPanelSelector).find('input'); - assert.equal($searchInput.val(), '3', 'search text'); + assert.equal($searchInput.val(), '3', 'search text'); - // act - treeList.option('searchPanel.text', 'new text'); + // act + treeList.option('searchPanel.text', 'new text'); - $searchInput = treeList.$element().find(searchPanelSelector).find('input'); + $searchInput = treeList.$element().find(searchPanelSelector).find('input'); - // assert - assert.equal($searchInput.val(), 'new text', 'search text'); -}); + // assert + assert.equal($searchInput.val(), 'new text', 'search text'); + }); -// T846709 -['standard', 'virual'].forEach((rowRenderingMode) => { - QUnit.test(`Modified expand state should be displayed correctly when repaintChangesOnly is true and scrolling.rowRenderingMode is ${rowRenderingMode}`, function(assert) { + // T846709 + ['standard', 'virual'].forEach((rowRenderingMode) => { + QUnit.test(`Modified expand state should be displayed correctly when repaintChangesOnly is true and scrolling.rowRenderingMode is ${rowRenderingMode}`, function(assert) { // arrange + const data = generateData(2); + const treeList = createTreeList({ + dataSource: data, + autoExpandAll: true, + repaintChangesOnly: true, + scrolling: { + rowRenderingMode: rowRenderingMode + }, + columns: ['id'] + }); + + this.clock.tick(30); + + // act + data[1].parentId = data[3].id; + treeList.option('dataSource', [...data]); + this.clock.tick(30); + + // assert + const $rowElements = $(treeList.element()).find('.dx-treelist-rowsview').find('.dx-data-row'); + assert.strictEqual($rowElements.length, 3, 'node count'); + assert.strictEqual($rowElements.eq(0).find('.dx-treelist-empty-space').length, 1, 'first node - first level'); + assert.strictEqual($rowElements.eq(0).children().first().text(), '1', 'first node - first cell text'); + assert.strictEqual($rowElements.eq(1).find('.dx-treelist-empty-space').length, 1, 'second node - first level'); + assert.strictEqual($rowElements.eq(1).children().first().text(), '3', 'second node - first cell text'); + assert.strictEqual($rowElements.eq(2).find('.dx-treelist-empty-space').length, 2, 'third node - second level'); + assert.strictEqual($rowElements.eq(2).children().first().text(), '4', 'third node - first cell text'); + assert.strictEqual($rowElements.eq(2).children().first().find('.dx-treelist-collapsed').length, 1, 'third node has an expand icon'); + }); + }); + + QUnit.test('The select checkbox should be displayed after changing expand state when repaintChangesOnly is true', function(assert) { + // arrange const data = generateData(2); const treeList = createTreeList({ dataSource: data, autoExpandAll: true, repaintChangesOnly: true, - scrolling: { - rowRenderingMode: rowRenderingMode + selection: { + mode: 'multiple' }, columns: ['id'] }); @@ -1029,873 +1061,845 @@ QUnit.test('Change searchPanel.text', function(assert) { assert.strictEqual($rowElements.eq(1).children().first().text(), '3', 'second node - first cell text'); assert.strictEqual($rowElements.eq(2).find('.dx-treelist-empty-space').length, 2, 'third node - second level'); assert.strictEqual($rowElements.eq(2).children().first().text(), '4', 'third node - first cell text'); - assert.strictEqual($rowElements.eq(2).children().first().find('.dx-treelist-collapsed').length, 1, 'third node has an expand icon'); + + const $expandIcon = $rowElements.eq(2).children().first().find('.dx-treelist-collapsed'); + assert.strictEqual($expandIcon.length, 1, 'third node has an expand icon'); + assert.ok($expandIcon.next().hasClass('dx-select-checkbox'), 'third node has a select checkbox'); }); -}); -QUnit.test('The select checkbox should be displayed after changing expand state when repaintChangesOnly is true', function(assert) { + // T861052 + QUnit.test('The child node position should be updated after changing dataSource when rowRenderingMode is "virtual" and repaintChangesOnly is true', function(assert) { // arrange - const data = generateData(2); - const treeList = createTreeList({ - dataSource: data, - autoExpandAll: true, - repaintChangesOnly: true, - selection: { - mode: 'multiple' - }, - columns: ['id'] - }); + const array = [ + { id: 1, parentId: 0, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, + { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, + { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) } + ]; + const treeList = createTreeList({ + dataSource: array, + autoExpandAll: true, + loadingTimeout: undefined, + keyExpr: 'id', + parentIdExpr: 'parentId', + rootValue: 0, + columns: ['field1', 'field2', 'field3'], + repaintChangesOnly: true, + scrolling: { + mode: 'virtual', + rowRenderingMode: 'virtual' + } + }); - this.clock.tick(30); + // assert + let rows = treeList.getVisibleRows(); + assert.strictEqual(rows[0].level, 0, 'level of the first node'); + assert.strictEqual(rows[1].level, 1, 'level of the second node'); + assert.strictEqual(rows[2].level, 2, 'level of the third node'); - // act - data[1].parentId = data[3].id; - treeList.option('dataSource', [...data]); - this.clock.tick(30); - - // assert - const $rowElements = $(treeList.element()).find('.dx-treelist-rowsview').find('.dx-data-row'); - assert.strictEqual($rowElements.length, 3, 'node count'); - assert.strictEqual($rowElements.eq(0).find('.dx-treelist-empty-space').length, 1, 'first node - first level'); - assert.strictEqual($rowElements.eq(0).children().first().text(), '1', 'first node - first cell text'); - assert.strictEqual($rowElements.eq(1).find('.dx-treelist-empty-space').length, 1, 'second node - first level'); - assert.strictEqual($rowElements.eq(1).children().first().text(), '3', 'second node - first cell text'); - assert.strictEqual($rowElements.eq(2).find('.dx-treelist-empty-space').length, 2, 'third node - second level'); - assert.strictEqual($rowElements.eq(2).children().first().text(), '4', 'third node - first cell text'); - - const $expandIcon = $rowElements.eq(2).children().first().find('.dx-treelist-collapsed'); - assert.strictEqual($expandIcon.length, 1, 'third node has an expand icon'); - assert.ok($expandIcon.next().hasClass('dx-select-checkbox'), 'third node has a select checkbox'); -}); + // act + array[2].parentId = 1; + treeList.option('dataSource', array); -// T861052 -QUnit.test('The child node position should be updated after changing dataSource when rowRenderingMode is "virtual" and repaintChangesOnly is true', function(assert) { - // arrange - const array = [ - { id: 1, parentId: 0, field1: 'test1', field2: 1, field3: new Date(2001, 0, 1) }, - { id: 2, parentId: 1, field1: 'test2', field2: 2, field3: new Date(2002, 1, 2) }, - { id: 3, parentId: 2, field1: 'test3', field2: 3, field3: new Date(2002, 1, 3) } - ]; - const treeList = createTreeList({ - dataSource: array, - autoExpandAll: true, - loadingTimeout: undefined, - keyExpr: 'id', - parentIdExpr: 'parentId', - rootValue: 0, - columns: ['field1', 'field2', 'field3'], - repaintChangesOnly: true, - scrolling: { - mode: 'virtual', - rowRenderingMode: 'virtual' - } + // assert + rows = treeList.getVisibleRows(); + assert.strictEqual(rows[0].level, 0, 'level of the first node'); + assert.strictEqual(rows[1].level, 1, 'level of the second node'); + assert.strictEqual(rows[2].level, 1, 'level of the third node'); }); - - // assert - let rows = treeList.getVisibleRows(); - assert.strictEqual(rows[0].level, 0, 'level of the first node'); - assert.strictEqual(rows[1].level, 1, 'level of the second node'); - assert.strictEqual(rows[2].level, 2, 'level of the third node'); - - // act - array[2].parentId = 1; - treeList.option('dataSource', array); - - // assert - rows = treeList.getVisibleRows(); - assert.strictEqual(rows[0].level, 0, 'level of the first node'); - assert.strictEqual(rows[1].level, 1, 'level of the second node'); - assert.strictEqual(rows[2].level, 1, 'level of the third node'); }); +QUnit.module('Expand/Collapse rows', () => { -QUnit.module('Expand/Collapse rows'); - -// T627926 -QUnit.test('Nodes should not be shifted after expanding node on last page', function(assert) { + // T627926 + QUnit.test('Nodes should not be shifted after expanding node on last page', function(assert) { // arrange - const clock = sinon.useFakeTimers(); - let topVisibleRowData; - const treeList = createTreeList({ - height: 120, - loadingTimeout: undefined, - paging: { - enabled: true, - pageSize: 2 - }, - scrolling: { - mode: 'virtual' - }, - expandedRowKeys: [1], - dataSource: [ - { name: 'Category1', id: 1 }, - { name: 'SubCategory1', id: 2, parentId: 1 }, - { name: 'SubCategory2', id: 3, parentId: 1 }, - { name: 'Category2', id: 4 }, - { name: 'Category3', id: 5 }, - { name: 'Category4', id: 6 }, - { name: 'Category7', id: 7 }, - { name: 'Category5', id: 8 }, - { name: 'SubCategory3', id: 9, parentId: 8 }, - { name: 'SubCategory5', id: 12, parentId: 9 }, - { name: 'SubCategory4', id: 10, parentId: 8 }, - { name: 'Category6', id: 11 } - ] - }); - const scrollable = treeList.getScrollable(); + const clock = sinon.useFakeTimers(); + let topVisibleRowData; + const treeList = createTreeList({ + height: 120, + loadingTimeout: undefined, + paging: { + enabled: true, + pageSize: 2 + }, + scrolling: { + mode: 'virtual' + }, + expandedRowKeys: [1], + dataSource: [ + { name: 'Category1', id: 1 }, + { name: 'SubCategory1', id: 2, parentId: 1 }, + { name: 'SubCategory2', id: 3, parentId: 1 }, + { name: 'Category2', id: 4 }, + { name: 'Category3', id: 5 }, + { name: 'Category4', id: 6 }, + { name: 'Category7', id: 7 }, + { name: 'Category5', id: 8 }, + { name: 'SubCategory3', id: 9, parentId: 8 }, + { name: 'SubCategory5', id: 12, parentId: 9 }, + { name: 'SubCategory4', id: 10, parentId: 8 }, + { name: 'Category6', id: 11 } + ] + }); + const scrollable = treeList.getScrollable(); - try { - scrollable.scrollTo({ y: 300 }); // scroll to the last page - devices.real().deviceType !== 'desktop' && $(scrollable._container()).trigger('scroll'); - clock.tick(); + try { + scrollable.scrollTo({ y: 300 }); // scroll to the last page + devices.real().deviceType !== 'desktop' && $(scrollable._container()).trigger('scroll'); + clock.tick(); - topVisibleRowData = treeList.getTopVisibleRowData(); + topVisibleRowData = treeList.getTopVisibleRowData(); - // assert - assert.strictEqual(treeList.pageIndex(), 4, 'page index'); - assert.strictEqual(treeList.pageCount(), 5, 'page count'); + // assert + assert.strictEqual(treeList.pageIndex(), 4, 'page index'); + assert.strictEqual(treeList.pageCount(), 5, 'page count'); - // act - treeList.expandRow(8); - treeList.expandRow(9); + // act + treeList.expandRow(8); + treeList.expandRow(9); - // assert - assert.strictEqual(treeList.pageIndex(), 3, 'page index'); - assert.strictEqual(treeList.pageCount(), 6, 'page count'); - assert.deepEqual(treeList.getTopVisibleRowData(), topVisibleRowData, 'top visible row data has not changed'); - } finally { - clock.restore(); - } -}); + // assert + assert.strictEqual(treeList.pageIndex(), 3, 'page index'); + assert.strictEqual(treeList.pageCount(), 6, 'page count'); + assert.deepEqual(treeList.getTopVisibleRowData(), topVisibleRowData, 'top visible row data has not changed'); + } finally { + clock.restore(); + } + }); -// T648005 -QUnit.test('Scrollbar position must be kept after expanding node when the treelist container has max-height', function(assert) { + // T648005 + QUnit.test('Scrollbar position must be kept after expanding node when the treelist container has max-height', function(assert) { // arrange - $('#treeList').css('max-height', 400); - - const done = assert.async(); - const treeList = createTreeList({ - loadingTimeout: undefined, - scrolling: { - mode: 'virtual', - useNative: false - }, - dataSource: generateData(100) - }); + $('#treeList').css('max-height', 400); + + const done = assert.async(); + const treeList = createTreeList({ + loadingTimeout: undefined, + scrolling: { + mode: 'virtual', + useNative: false + }, + dataSource: generateData(100) + }); - treeList.getScrollable().scrollTo({ y: 1000 }); + treeList.getScrollable().scrollTo({ y: 1000 }); - setTimeout(function() { + setTimeout(function() { // act - treeList.expandRow(69); + treeList.expandRow(69); - setTimeout(function() { + setTimeout(function() { // assert - assert.ok($(treeList.element()).find('.dx-treelist-rowsview .dx-scrollbar-vertical > .dx-scrollable-scroll').position().top > 0, 'scrollbar position top'); - done(); - }, 310); + assert.ok($(treeList.element()).find('.dx-treelist-rowsview .dx-scrollbar-vertical > .dx-scrollable-scroll').position().top > 0, 'scrollbar position top'); + done(); + }, 310); + }); }); -}); -// T692068 -QUnit.test('Expand row if repaintChangesOnly is true', function(assert) { + // T692068 + QUnit.test('Expand row if repaintChangesOnly is true', function(assert) { // arrange - const treeList = createTreeList({ - height: 120, - loadingTimeout: undefined, - repaintChangesOnly: true, - dataSource: [ - { id: 1, name: 'node_1' }, - { id: 2, name: 'node_1_1', parentId: 1 }, - { id: 3, name: 'node_1_2', parentId: 1 } - ] - }); + const treeList = createTreeList({ + height: 120, + loadingTimeout: undefined, + repaintChangesOnly: true, + dataSource: [ + { id: 1, name: 'node_1' }, + { id: 2, name: 'node_1_1', parentId: 1 }, + { id: 3, name: 'node_1_2', parentId: 1 } + ] + }); - // act - treeList.expandRow(1); + // act + treeList.expandRow(1); - // assert - assert.strictEqual(treeList.getVisibleRows()[0].isExpanded, true, 'first row has corrent isExpanded state'); - assert.strictEqual($(treeList.getRowElement(0)).find('.dx-treelist-expanded').length, 1, 'first row has expanded icon'); -}); + // assert + assert.strictEqual(treeList.getVisibleRows()[0].isExpanded, true, 'first row has corrent isExpanded state'); + assert.strictEqual($(treeList.getRowElement(0)).find('.dx-treelist-expanded').length, 1, 'first row has expanded icon'); + }); -// T742885 -QUnit.test('Expand node after filtering when it has many children and they are selected', function(assert) { + // T742885 + QUnit.test('Expand node after filtering when it has many children and they are selected', function(assert) { // arrange - const clock = sinon.useFakeTimers(); - - try { - const treeList = createTreeList({ - loadingTimeout: 30, - height: 200, - dataSource: { - store: { - type: 'array', - data: [{ - field1: 'test1', - items: [{ - field1: 'test2' - }, { - field1: 'test2' - }, { - field1: 'test2' - }, { - field1: 'test2' - }, { - field1: 'test2' - }, { - field1: 'test2' - }, { - field1: 'test2' - }, { - field1: 'test2' + const clock = sinon.useFakeTimers(); + + try { + const treeList = createTreeList({ + loadingTimeout: 30, + height: 200, + dataSource: { + store: { + type: 'array', + data: [{ + field1: 'test1', + items: [{ + field1: 'test2' + }, { + field1: 'test2' + }, { + field1: 'test2' + }, { + field1: 'test2' + }, { + field1: 'test2' + }, { + field1: 'test2' + }, { + field1: 'test2' + }, { + field1: 'test2' + }] }] - }] + }, + pageSize: 2 }, - pageSize: 2 - }, - scrolling: { - mode: 'virtual' - }, - selection: { - mode: 'multiple' - }, - itemsExpr: 'items', - dataStructure: 'tree', - columns: [{ dataField: 'field1', dataType: 'string', filterValues: ['test2'] }], - onContentReady: function(e) { - e.component.selectRows([2, 3, 4, 5, 6, 7, 8, 9]); - } - }); + scrolling: { + mode: 'virtual' + }, + selection: { + mode: 'multiple' + }, + itemsExpr: 'items', + dataStructure: 'tree', + columns: [{ dataField: 'field1', dataType: 'string', filterValues: ['test2'] }], + onContentReady: function(e) { + e.component.selectRows([2, 3, 4, 5, 6, 7, 8, 9]); + } + }); - clock.tick(500); + clock.tick(500); - // act - treeList.collapseRow(1); - clock.tick(100); + // act + treeList.collapseRow(1); + clock.tick(100); - // assert - const items = treeList.getVisibleRows(); - assert.strictEqual(items.length, 1, 'row count'); - assert.notOk(treeList.isRowExpanded(1), 'first node is collapsed'); - } finally { - clock.restore(); - } + // assert + const items = treeList.getVisibleRows(); + assert.strictEqual(items.length, 1, 'row count'); + assert.notOk(treeList.isRowExpanded(1), 'first node is collapsed'); + } finally { + clock.restore(); + } + }); }); -QUnit.module('Focused Row', defaultModuleConfig); +QUnit.module('Focused Row', defaultModuleConfig, () => { -QUnit.test('TreeList with focusedRowEnabled and focusedRowIndex 0', function(assert) { + QUnit.test('TreeList with focusedRowEnabled and focusedRowIndex 0', function(assert) { // arrange, act - const treeList = createTreeList({ - dataSource: generateData(5), - focusedRowEnabled: true, - focusedRowIndex: 0 - }); + const treeList = createTreeList({ + dataSource: generateData(5), + focusedRowEnabled: true, + focusedRowIndex: 0 + }); - this.clock.tick(); + this.clock.tick(); - // assert - assert.ok($(treeList.getRowElement(0)).hasClass('dx-row-focused'), 'first row is focused'); -}); + // assert + assert.ok($(treeList.getRowElement(0)).hasClass('dx-row-focused'), 'first row is focused'); + }); -QUnit.test('TreeList with focusedRowKey', function(assert) { + QUnit.test('TreeList with focusedRowKey', function(assert) { // arrange, act - const treeList = createTreeList({ - height: 100, - keyExpr: 'id', - dataSource: generateData(10), - paging: { - pageSize: 4 - }, - focusedRowEnabled: true, - focusedRowKey: 12 - }); + const treeList = createTreeList({ + height: 100, + keyExpr: 'id', + dataSource: generateData(10), + paging: { + pageSize: 4 + }, + focusedRowEnabled: true, + focusedRowKey: 12 + }); - this.clock.tick(); + this.clock.tick(); - // assert - assert.equal(treeList.pageIndex(), 1, 'page is changed'); - assert.deepEqual(treeList.option('expandedRowKeys'), [11], 'focus parent is expanded'); - assert.ok($(treeList.getRowElement(treeList.getRowIndexByKey(12))).hasClass('dx-row-focused'), 'focused row is visible'); -}); + // assert + assert.equal(treeList.pageIndex(), 1, 'page is changed'); + assert.deepEqual(treeList.option('expandedRowKeys'), [11], 'focus parent is expanded'); + assert.ok($(treeList.getRowElement(treeList.getRowIndexByKey(12))).hasClass('dx-row-focused'), 'focused row is visible'); + }); -QUnit.test('TreeList with remoteOperations and focusedRowKey', function(assert) { + QUnit.test('TreeList with remoteOperations and focusedRowKey', function(assert) { // arrange, act - const treeList = createTreeList({ - height: 100, - keyExpr: 'id', - dataSource: generateData(10), - remoteOperations: true, - paging: { - pageSize: 4 - }, - focusedRowEnabled: true, - focusedRowKey: 12 - }); + const treeList = createTreeList({ + height: 100, + keyExpr: 'id', + dataSource: generateData(10), + remoteOperations: true, + paging: { + pageSize: 4 + }, + focusedRowEnabled: true, + focusedRowKey: 12 + }); - this.clock.tick(); + this.clock.tick(); - // assert - assert.equal(treeList.pageIndex(), 1, 'page is changed'); - assert.deepEqual(treeList.option('expandedRowKeys'), [11], 'focus parent is expanded'); - assert.ok($(treeList.getRowElement(treeList.getRowIndexByKey(12))).hasClass('dx-row-focused'), 'focused row is visible'); -}); + // assert + assert.equal(treeList.pageIndex(), 1, 'page is changed'); + assert.deepEqual(treeList.option('expandedRowKeys'), [11], 'focus parent is expanded'); + assert.ok($(treeList.getRowElement(treeList.getRowIndexByKey(12))).hasClass('dx-row-focused'), 'focused row is visible'); + }); -QUnit.test('TreeList with remoteOperations(filtering, sorting, grouping) and focusedRowKey should not generate repeated node', function(assert) { + QUnit.test('TreeList with remoteOperations(filtering, sorting, grouping) and focusedRowKey should not generate repeated node', function(assert) { // arrange, act - let childrenNodes; - const treeList = createTreeList({ - dataSource: [ - { 'Task_ID': 1, 'Task_Parent_ID': 0 }, - { 'Task_ID': 3, 'Task_Parent_ID': 1 }, - { 'Task_ID': 4, 'Task_Parent_ID': 2 }, - { 'Task_ID': 5, 'Task_Parent_ID': 3 } - ], - keyExpr: 'Task_ID', - parentIdExpr: 'Task_Parent_ID', - remoteOperations: { - filtering: true, - sorting: true, - grouping: true - }, - focusedRowEnabled: true, - focusedRowKey: 5 - }); + let childrenNodes; + const treeList = createTreeList({ + dataSource: [ + { 'Task_ID': 1, 'Task_Parent_ID': 0 }, + { 'Task_ID': 3, 'Task_Parent_ID': 1 }, + { 'Task_ID': 4, 'Task_Parent_ID': 2 }, + { 'Task_ID': 5, 'Task_Parent_ID': 3 } + ], + keyExpr: 'Task_ID', + parentIdExpr: 'Task_Parent_ID', + remoteOperations: { + filtering: true, + sorting: true, + grouping: true + }, + focusedRowEnabled: true, + focusedRowKey: 5 + }); - this.clock.tick(); + this.clock.tick(); - // arrange - childrenNodes = treeList.getNodeByKey(1).children; + // arrange + childrenNodes = treeList.getNodeByKey(1).children; - // assert - assert.equal(childrenNodes.length, 1, 'children nodes count'); - assert.equal(childrenNodes[0].key, 3, 'children node key'); -}); + // assert + assert.equal(childrenNodes.length, 1, 'children nodes count'); + assert.equal(childrenNodes[0].key, 3, 'children node key'); + }); -QUnit.testInActiveWindow('TreeList should focus the corresponding group row if group collapsed and inner data row was focused', function(assert) { + QUnit.testInActiveWindow('TreeList should focus the corresponding group row if group collapsed and inner data row was focused', function(assert) { // arrange - const treeList = createTreeList({ - keyExpr: 'id', - dataSource: generateData(10), - focusedRowEnabled: true, - expandedRowKeys: [3], - focusedRowKey: 4 - }); + const treeList = createTreeList({ + keyExpr: 'id', + dataSource: generateData(10), + focusedRowEnabled: true, + expandedRowKeys: [3], + focusedRowKey: 4 + }); - this.clock.tick(); + this.clock.tick(); - // act - treeList.collapseRow(3); + // act + treeList.collapseRow(3); - this.clock.tick(); + this.clock.tick(); - // assert - assert.equal(treeList.isRowExpanded(3), false, 'parent node collapsed'); - assert.equal(treeList.option('focusedRowKey'), 3, 'parent node focused'); -}); + // assert + assert.equal(treeList.isRowExpanded(3), false, 'parent node collapsed'); + assert.equal(treeList.option('focusedRowKey'), 3, 'parent node focused'); + }); -QUnit.test('TreeList should focus only one focused row (T827201)', function(assert) { + QUnit.test('TreeList should focus only one focused row (T827201)', function(assert) { // arrange - const rowsViewWrapper = treeListWrapper.rowsView; - const treeList = createTreeList({ - keyExpr: 'id', - dataSource: generateData(10), - focusedRowEnabled: true, - focusedRowKey: 3 - }); + const rowsViewWrapper = treeListWrapper.rowsView; + const treeList = createTreeList({ + keyExpr: 'id', + dataSource: generateData(10), + focusedRowEnabled: true, + focusedRowKey: 3 + }); - this.clock.tick(); + this.clock.tick(); - // assert - const rowIndex = treeList.getRowIndexByKey(3); - assert.ok(rowsViewWrapper.getDataRow(rowIndex).isFocusedRow(), 'Row 3 is a focused row'); + // assert + const rowIndex = treeList.getRowIndexByKey(3); + assert.ok(rowsViewWrapper.getDataRow(rowIndex).isFocusedRow(), 'Row 3 is a focused row'); - // act - $(treeList.getCellElement(4, 1)).trigger(CLICK_EVENT); - this.clock.tick(); + // act + $(treeList.getCellElement(4, 1)).trigger(CLICK_EVENT); + this.clock.tick(); - // assert - assert.notOk(rowsViewWrapper.getDataRow(rowIndex).isFocusedRow(), 'Row 3 is not a focused row'); - assert.ok(rowsViewWrapper.getDataRow(4).isFocusedRow(), 'Row 4 is a focused row'); -}); + // assert + assert.notOk(rowsViewWrapper.getDataRow(rowIndex).isFocusedRow(), 'Row 3 is not a focused row'); + assert.ok(rowsViewWrapper.getDataRow(4).isFocusedRow(), 'Row 4 is a focused row'); + }); -QUnit.test('TreeList navigateTo', function(assert) { + QUnit.test('TreeList navigateTo', function(assert) { // arrange, act - const treeList = createTreeList({ - dataSource: generateData(10), - paging: { - pageSize: 4 - } - }); + const treeList = createTreeList({ + dataSource: generateData(10), + paging: { + pageSize: 4 + } + }); - this.clock.tick(); + this.clock.tick(); - treeList.navigateToRow(12); - this.clock.tick(); + treeList.navigateToRow(12); + this.clock.tick(); - // assert - assert.deepEqual(treeList.option('expandedRowKeys'), [11], 'parent node is expanded'); - assert.equal(treeList.pageIndex(), 1, 'page is changed'); - assert.ok(treeList.getRowIndexByKey(12) >= 0, 'key is visible'); -}); + // assert + assert.deepEqual(treeList.option('expandedRowKeys'), [11], 'parent node is expanded'); + assert.equal(treeList.pageIndex(), 1, 'page is changed'); + assert.ok(treeList.getRowIndexByKey(12) >= 0, 'key is visible'); + }); -QUnit.test('TreeList navigateTo to the same page with expand', function(assert) { + QUnit.test('TreeList navigateTo to the same page with expand', function(assert) { // arrange, act - const treeList = createTreeList({ - dataSource: generateData(10), - paging: { - pageSize: 4 - } - }); + const treeList = createTreeList({ + dataSource: generateData(10), + paging: { + pageSize: 4 + } + }); - this.clock.tick(); + this.clock.tick(); - treeList.navigateToRow(2); - this.clock.tick(); + treeList.navigateToRow(2); + this.clock.tick(); - // assert - assert.deepEqual(treeList.option('expandedRowKeys'), [1], 'parent node is expanded'); - assert.equal(treeList.pageIndex(), 0, 'page is not changed'); - assert.ok(treeList.getRowIndexByKey(2) >= 0, 'key is visible'); -}); + // assert + assert.deepEqual(treeList.option('expandedRowKeys'), [1], 'parent node is expanded'); + assert.equal(treeList.pageIndex(), 0, 'page is not changed'); + assert.ok(treeList.getRowIndexByKey(2) >= 0, 'key is visible'); + }); -// T697860 -QUnit.test('dataSource change with columns should force one loading only', function(assert) { - const loadingSpy = sinon.spy(); + // T697860 + QUnit.test('dataSource change with columns should force one loading only', function(assert) { + const loadingSpy = sinon.spy(); - const options = { - dataSource: new DataSource({ - load: function() { - const d = $.Deferred(); + const options = { + dataSource: new DataSource({ + load: function() { + const d = $.Deferred(); - setTimeout(function() { - d.resolve([{ id: 1 }, { id: 2 }, { id: 3 }]); - }); + setTimeout(function() { + d.resolve([{ id: 1 }, { id: 2 }, { id: 3 }]); + }); - return d; - } - }), - paging: { - pageSize: 2 - }, - columns: ['id'] - }; + return d; + } + }), + paging: { + pageSize: 2 + }, + columns: ['id'] + }; - const treeList = createTreeList(options); + const treeList = createTreeList(options); - this.clock.tick(0); + this.clock.tick(0); - options.dataSource.store().on('loading', loadingSpy); + options.dataSource.store().on('loading', loadingSpy); - // act - treeList.option(options); - this.clock.tick(0); + // act + treeList.option(options); + this.clock.tick(0); - // assert - assert.equal(loadingSpy.callCount, 1, 'loading called once'); - assert.equal(treeList.getVisibleRows().length, 3, 'visible row count'); -}); + // assert + assert.equal(loadingSpy.callCount, 1, 'loading called once'); + assert.equal(treeList.getVisibleRows().length, 3, 'visible row count'); + }); -QUnit.test('Should not generate exception when selection mode is multiple and focusedRowKey is set for the nested node (T735585)', function(assert) { - const options = { - dataSource: [ - { id: 0, parentId: -1, c0: 'C0_0', c1: 'c1_0' }, - { id: 1, parentId: 0, c0: 'C0_0', c1: 'c1_0' } - ], - keyExpr: 'id', - parentIdExpr: 'parentId', - selection: { mode: 'single' }, - focusedRowEnabled: true, - focusedRowKey: 1, - expandedRowKeys: [1], - onFocusedRowChanged: e => { - if(e.row && e.row.data) { - e.component.selectRows([e.row.key], true); + QUnit.test('Should not generate exception when selection mode is multiple and focusedRowKey is set for the nested node (T735585)', function(assert) { + const options = { + dataSource: [ + { id: 0, parentId: -1, c0: 'C0_0', c1: 'c1_0' }, + { id: 1, parentId: 0, c0: 'C0_0', c1: 'c1_0' } + ], + keyExpr: 'id', + parentIdExpr: 'parentId', + selection: { mode: 'single' }, + focusedRowEnabled: true, + focusedRowKey: 1, + expandedRowKeys: [1], + onFocusedRowChanged: e => { + if(e.row && e.row.data) { + e.component.selectRows([e.row.key], true); + } } - } - }; + }; - try { + try { // act - createTreeList(options); - this.clock.tick(); + createTreeList(options); + this.clock.tick(); - // arrange - options.selection.mode = 'multiple'; + // arrange + options.selection.mode = 'multiple'; - // act - createTreeList(options); - this.clock.tick(); - } catch(e) { + // act + createTreeList(options); + this.clock.tick(); + } catch(e) { // assert - assert.ok(false, e.message); - } + assert.ok(false, e.message); + } - // assert - assert.ok(true, 'No exceptions'); + // assert + assert.ok(true, 'No exceptions'); + }); }); -QUnit.module('Scroll', defaultModuleConfig); +QUnit.module('Scroll', defaultModuleConfig, () => { -// T757537 -QUnit.test('TreeList should not hang when scrolling', function(assert) { + // T757537 + QUnit.test('TreeList should not hang when scrolling', function(assert) { // arrange - let scrollable; - const contentReadySpy = sinon.spy(); - const treeList = createTreeList({ - dataSource: [ - { id: 1, parentId: 0 }, - { id: 2, parentId: 0 }, - { id: 3, parentId: 0 }, - { id: 4, parentId: 0 }, - { id: 5, parentId: 0 }, - { id: 6, parentId: 0 }, - { id: 7, parentId: 0 }, - { id: 8, parentId: 0 }, - { id: 9, parentId: 0 }, - { id: 10, parentId: 0 }, - { id: 11, parentId: 0 }, - { id: 12, parentId: 0 }, - { id: 13, parentId: 0 }, - { id: 14, parentId: 0 }, - { id: 15, parentId: 0 } - ], - paging: { - pageSize: 5 - }, - height: 200, - columnAutoWidth: true, - scrolling: { - useNative: false - }, - onContentReady: contentReadySpy - }); - const done = assert.async(); + let scrollable; + const contentReadySpy = sinon.spy(); + const treeList = createTreeList({ + dataSource: [ + { id: 1, parentId: 0 }, + { id: 2, parentId: 0 }, + { id: 3, parentId: 0 }, + { id: 4, parentId: 0 }, + { id: 5, parentId: 0 }, + { id: 6, parentId: 0 }, + { id: 7, parentId: 0 }, + { id: 8, parentId: 0 }, + { id: 9, parentId: 0 }, + { id: 10, parentId: 0 }, + { id: 11, parentId: 0 }, + { id: 12, parentId: 0 }, + { id: 13, parentId: 0 }, + { id: 14, parentId: 0 }, + { id: 15, parentId: 0 } + ], + paging: { + pageSize: 5 + }, + height: 200, + columnAutoWidth: true, + scrolling: { + useNative: false + }, + onContentReady: contentReadySpy + }); + const done = assert.async(); - this.clock.tick(100); - this.clock.restore(); - scrollable = treeList.getScrollable(); - contentReadySpy.reset(); + this.clock.tick(100); + this.clock.restore(); + scrollable = treeList.getScrollable(); + contentReadySpy.reset(); - // act - scrollable.scrollTo({ y: 200 }); - scrollable.scrollTo({ y: 500 }); + // act + scrollable.scrollTo({ y: 200 }); + scrollable.scrollTo({ y: 500 }); - setTimeout(function() { + setTimeout(function() { // assert - assert.strictEqual(treeList.pageIndex(), 2, 'page index'); - assert.strictEqual(contentReadySpy.callCount, 3, 'onContentReady'); - done(); - }, 1000); -}); + assert.strictEqual(treeList.pageIndex(), 2, 'page index'); + assert.strictEqual(contentReadySpy.callCount, 3, 'onContentReady'); + done(); + }, 1000); + }); -// T806141 -QUnit.test('TreeList should correctly load data when filtering is remote and sorting is applied', function(assert) { + // T806141 + QUnit.test('TreeList should correctly load data when filtering is remote and sorting is applied', function(assert) { // arrange - const loadSpy = sinon.spy(); - const data = [{ id: 0, parentId: '', hasItems: true }, { id: 1, parentId: 0, hasItems: false }]; - const treeList = createTreeList({ - dataSource: { - load: function(options) { - loadSpy(options); - if(options.filter && options.filter[2] !== '') { - return $.Deferred().resolve([data[1]]); + const loadSpy = sinon.spy(); + const data = [{ id: 0, parentId: '', hasItems: true }, { id: 1, parentId: 0, hasItems: false }]; + const treeList = createTreeList({ + dataSource: { + load: function(options) { + loadSpy(options); + if(options.filter && options.filter[2] !== '') { + return $.Deferred().resolve([data[1]]); + } + return $.Deferred().resolve([data[0]]); } - return $.Deferred().resolve([data[0]]); - } - }, - remoteOperations: { - filtering: true - }, - keyExpr: 'id', - parentIdExpr: 'parentId', - hasItemsExpr: 'hasItems', - rootValue: '', - showBorders: true, - columns: [ - { dataField: 'id', sortOrder: 'asc' } - ] - }); - - this.clock.tick(100); + }, + remoteOperations: { + filtering: true + }, + keyExpr: 'id', + parentIdExpr: 'parentId', + hasItemsExpr: 'hasItems', + rootValue: '', + showBorders: true, + columns: [ + { dataField: 'id', sortOrder: 'asc' } + ] + }); - // act - $('#treeList').find('.dx-treelist-collapsed').trigger('dxclick'); - this.clock.tick(100); - this.clock.restore(); + this.clock.tick(100); - // assert - assert.equal(loadSpy.callCount, 2, 'load call count'); + // act + $('#treeList').find('.dx-treelist-collapsed').trigger('dxclick'); + this.clock.tick(100); + this.clock.restore(); - assert.deepEqual(loadSpy.args[0][0].filter, ['parentId', '=', ''], 'first load arguments'); - assert.deepEqual(loadSpy.args[1][0].filter, ['parentId', '=', 0], 'second load arguments'); + // assert + assert.equal(loadSpy.callCount, 2, 'load call count'); - assert.equal($(treeList.getCellElement(0, 0)).text(), '0', 'first row first cell'); - assert.equal($(treeList.getCellElement(1, 0)).text(), '1', 'second row first cell'); + assert.deepEqual(loadSpy.args[0][0].filter, ['parentId', '=', ''], 'first load arguments'); + assert.deepEqual(loadSpy.args[1][0].filter, ['parentId', '=', 0], 'second load arguments'); - loadSpy.reset(); -}); + assert.equal($(treeList.getCellElement(0, 0)).text(), '0', 'first row first cell'); + assert.equal($(treeList.getCellElement(1, 0)).text(), '1', 'second row first cell'); -// T806547 -QUnit.test('TreeList should correctly switch dx-row-alt class for fixed column after expand if repaintChangesOnly = true', function(assert) { - // arrange - let $row; - const treeList = createTreeList({ - rowAlternationEnabled: true, - autoExpandAll: false, - repaintChangesOnly: true, - columns: [{ - dataField: 'id', - fixed: true - }, 'field'], - dataSource: [{ - id: 1, - parentId: 0, - field: 'data' - }, { - id: 2, - parentId: 1, - field: 'data' - }, { - id: 3, - parentId: 0, - field: 'data' - }] + loadSpy.reset(); }); - this.clock.tick(100); - - // act - treeList.expandRow(1); - this.clock.tick(); - $row = $(treeList.getRowElement(2)); - - // assert - assert.notOk($row.eq(0).hasClass('dx-row-alt'), 'unfixed table row element'); - assert.notOk($row.eq(1).hasClass('dx-row-alt'), 'fixed table row element'); -}); - -QUnit.test('TreeList should reshape data after update dataSource if reshapeOnPush set true (T815367)', function(assert) { + // T806547 + QUnit.test('TreeList should correctly switch dx-row-alt class for fixed column after expand if repaintChangesOnly = true', function(assert) { // arrange - let $row; - const treeList = createTreeList({ - dataSource: { - store: [{ - ID: 1, - Head_ID: 0, - Name: 'John' - }], - reshapeOnPush: true - }, - keyExpr: 'ID', - parentIdExpr: 'Head_ID', - columns: ['Name'], - expandedRowKeys: [1] - }); - this.clock.tick(); - - // act - treeList.getDataSource().store().push([{ - type: 'insert', - data: { ID: 2, Head_ID: 1, Name: 'Alex' } - }]); - this.clock.tick(); + let $row; + const treeList = createTreeList({ + rowAlternationEnabled: true, + autoExpandAll: false, + repaintChangesOnly: true, + columns: [{ + dataField: 'id', + fixed: true + }, 'field'], + dataSource: [{ + id: 1, + parentId: 0, + field: 'data' + }, { + id: 2, + parentId: 1, + field: 'data' + }, { + id: 3, + parentId: 0, + field: 'data' + }] + }); - // arrange - $row = $(treeList.getRowElement(1)); + this.clock.tick(100); - // assert - assert.ok($row && $row.text() === 'Alex', 'pushed item displays'); -}); + // act + treeList.expandRow(1); + this.clock.tick(); + $row = $(treeList.getRowElement(2)); -QUnit.test('TreeList should not reshape data after expand row (T815367)', function(assert) { - // arrange - const onNodesInitializedSpy = sinon.spy(); - const treeList = createTreeList({ - dataSource: { - store: [ - { ID: 1, Head_ID: 0, Name: 'John' }, - { ID: 2, Head_ID: 1, Name: 'Alex' } - ], - reshapeOnPush: true - }, - keyExpr: 'ID', - parentIdExpr: 'Head_ID', - columns: ['Name'], - expandedRowKeys: [], - onNodesInitialized: onNodesInitializedSpy + // assert + assert.notOk($row.eq(0).hasClass('dx-row-alt'), 'unfixed table row element'); + assert.notOk($row.eq(1).hasClass('dx-row-alt'), 'fixed table row element'); }); - this.clock.tick(); - - // act - treeList.expandRow(1); - this.clock.tick(); - // assert - assert.equal(onNodesInitializedSpy.callCount, 1, 'data did not reshape'); -}); - -QUnit.test('TreeList should not occur an exception on an attempt to remove the non-existing key from the store (T827142)', function(assert) { + QUnit.test('TreeList should reshape data after update dataSource if reshapeOnPush set true (T815367)', function(assert) { // arrange - const store = new ArrayStore({ - data: [ - { id: 1, parentId: 0, age: 19 }, - { id: 2, parentId: 1, age: 16 } - ], - key: 'id', - reshapeOnPush: true - }); + let $row; + const treeList = createTreeList({ + dataSource: { + store: [{ + ID: 1, + Head_ID: 0, + Name: 'John' + }], + reshapeOnPush: true + }, + keyExpr: 'ID', + parentIdExpr: 'Head_ID', + columns: ['Name'], + expandedRowKeys: [1] + }); + this.clock.tick(); - createTreeList({ - dataSource: { - store: store - }, - keyExpr: 'id', - parentIdExpr: 'parentId', - }); - this.clock.tick(); + // act + treeList.getDataSource().store().push([{ + type: 'insert', + data: { ID: 2, Head_ID: 1, Name: 'Alex' } + }]); + this.clock.tick(); - // act - store.push([{ type: 'remove', key: 100 }]); - this.clock.tick(); + // arrange + $row = $(treeList.getRowElement(1)); - // assert - assert.ok(true, 'exception does not occur'); -}); + // assert + assert.ok($row && $row.text() === 'Alex', 'pushed item displays'); + }); -QUnit.test('TreeList should filter data with unreachable items (T816921)', function(assert) { + QUnit.test('TreeList should not reshape data after expand row (T815367)', function(assert) { // arrange - const treeList = createTreeList({ - dataSource: [ - { ID: 1, Head_ID: 0, Name: 'John' }, - { ID: 2, Head_ID: 1, Name: 'Alex' }, - { ID: 3, Head_ID: 100, Name: 'Alex' } - ], - keyExpr: 'ID', - parentIdExpr: 'Head_ID', - loadingTimeout: undefined, - searchPanel: { - visible: true, + const onNodesInitializedSpy = sinon.spy(); + const treeList = createTreeList({ + dataSource: { + store: [ + { ID: 1, Head_ID: 0, Name: 'John' }, + { ID: 2, Head_ID: 1, Name: 'Alex' } + ], + reshapeOnPush: true + }, + keyExpr: 'ID', + parentIdExpr: 'Head_ID', + columns: ['Name'], + expandedRowKeys: [], + onNodesInitialized: onNodesInitializedSpy + }); + this.clock.tick(); - // act - text: 'Alex' - } + // act + treeList.expandRow(1); + this.clock.tick(); + + // assert + assert.equal(onNodesInitializedSpy.callCount, 1, 'data did not reshape'); }); - // assert - assert.equal(treeList.getVisibleRows().length, 2, 'filtered row count'); -}); + QUnit.test('TreeList should not occur an exception on an attempt to remove the non-existing key from the store (T827142)', function(assert) { + // arrange + const store = new ArrayStore({ + data: [ + { id: 1, parentId: 0, age: 19 }, + { id: 2, parentId: 1, age: 16 } + ], + key: 'id', + reshapeOnPush: true + }); + createTreeList({ + dataSource: { + store: store + }, + keyExpr: 'id', + parentIdExpr: 'parentId', + }); + this.clock.tick(); -QUnit.module('Row dragging', defaultModuleConfig); + // act + store.push([{ type: 'remove', key: 100 }]); + this.clock.tick(); -// T831020 -QUnit.test('The draggable row should have correct markup when defaultOptions is specified', function(assert) { - // arrange - TreeList.defaultOptions({ - options: { - filterRow: { - visible: true - }, - groupPanel: { - visible: true - }, - filterPanel: { - visible: true - } - } + // assert + assert.ok(true, 'exception does not occur'); }); - try { + QUnit.test('TreeList should filter data with unreachable items (T816921)', function(assert) { + // arrange const treeList = createTreeList({ dataSource: [ { ID: 1, Head_ID: 0, Name: 'John' }, - { ID: 2, Head_ID: 0, Name: 'Alex' } + { ID: 2, Head_ID: 1, Name: 'Alex' }, + { ID: 3, Head_ID: 100, Name: 'Alex' } ], keyExpr: 'ID', parentIdExpr: 'Head_ID', - rowDragging: { - allowReordering: true + loadingTimeout: undefined, + searchPanel: { + visible: true, + + // act + text: 'Alex' } }); - this.clock.tick(); - - // act - pointerMock(treeList.getCellElement(0, 0)).start().down().move(100, 100); - // assert - const $draggableRow = $('body').children('.dx-sortable-dragging'); - assert.strictEqual($draggableRow.length, 1, 'has draggable row'); + assert.equal(treeList.getVisibleRows().length, 2, 'filtered row count'); + }); +}); + +QUnit.module('Row dragging', defaultModuleConfig, () => { - const $visibleView = $draggableRow.find('.dx-gridbase-container').children(':visible'); - assert.strictEqual($visibleView.length, 1, 'markup of the draggable row is correct'); - assert.ok($visibleView.hasClass('dx-treelist-rowsview'), 'rowsview is visible'); - } finally { + // T831020 + QUnit.test('The draggable row should have correct markup when defaultOptions is specified', function(assert) { + // arrange TreeList.defaultOptions({ options: { filterRow: { - visible: false + visible: true }, groupPanel: { - visible: false + visible: true }, filterPanel: { - visible: false + visible: true } } }); - } + + try { + const treeList = createTreeList({ + dataSource: [ + { ID: 1, Head_ID: 0, Name: 'John' }, + { ID: 2, Head_ID: 0, Name: 'Alex' } + ], + keyExpr: 'ID', + parentIdExpr: 'Head_ID', + rowDragging: { + allowReordering: true + } + }); + + this.clock.tick(); + + // act + pointerMock(treeList.getCellElement(0, 0)).start().down().move(100, 100); + + // assert + const $draggableRow = $('body').children('.dx-sortable-dragging'); + assert.strictEqual($draggableRow.length, 1, 'has draggable row'); + + const $visibleView = $draggableRow.find('.dx-gridbase-container').children(':visible'); + assert.strictEqual($visibleView.length, 1, 'markup of the draggable row is correct'); + assert.ok($visibleView.hasClass('dx-treelist-rowsview'), 'rowsview is visible'); + } finally { + TreeList.defaultOptions({ + options: { + filterRow: { + visible: false + }, + groupPanel: { + visible: false + }, + filterPanel: { + visible: false + } + } + }); + } + }); }); -QUnit.module('Selection', defaultModuleConfig); +QUnit.module('Selection', defaultModuleConfig, () => { -// T861403 -[true, false].forEach(recursive => { - QUnit.test(`Select and deselect all rows if filter is applied, filterMode is matchOnly and recursive=${recursive}`, function(assert) { + // T861403 + [true, false].forEach(recursive => { + QUnit.test(`Select and deselect all rows if filter is applied, filterMode is matchOnly and recursive=${recursive}`, function(assert) { // arrange - const selectedRowKeys = recursive ? [1, 2] : [2]; - const treeList = createTreeList({ - dataSource: [{ - id: 1, - parent_id: 0, - data: 'some' - }, { - id: 2, - parent_id: 1, - data: 'some2' - }], - columns: ['id', 'data'], - filterValue: ['data', '=', 'some2'], - keyExpr: 'id', - parentIdExpr: 'parent_id', - filterMode: 'matchOnly', - selection: { - recursive, - mode: 'multiple' - } - }); + const selectedRowKeys = recursive ? [1, 2] : [2]; + const treeList = createTreeList({ + dataSource: [{ + id: 1, + parent_id: 0, + data: 'some' + }, { + id: 2, + parent_id: 1, + data: 'some2' + }], + columns: ['id', 'data'], + filterValue: ['data', '=', 'some2'], + keyExpr: 'id', + parentIdExpr: 'parent_id', + filterMode: 'matchOnly', + selection: { + recursive, + mode: 'multiple' + } + }); - this.clock.tick(); + this.clock.tick(); - // act - const $selectCheckBoxes = $('.dx-select-checkbox'); - $selectCheckBoxes.eq(0).trigger('dxclick'); + // act + const $selectCheckBoxes = $('.dx-select-checkbox'); + $selectCheckBoxes.eq(0).trigger('dxclick'); - // assert - assert.equal($selectCheckBoxes.eq(0).attr('aria-checked'), 'true', 'selectAll checkbox is checked'); - assert.equal($selectCheckBoxes.eq(1).attr('aria-checked'), 'true', 'first row\'s checkbox is checked'); - assert.deepEqual(treeList.getSelectedRowKeys(), selectedRowKeys, 'selected row keys'); - assert.deepEqual(treeList.option('selectedRowKeys'), selectedRowKeys, 'selected row keys'); + // assert + assert.equal($selectCheckBoxes.eq(0).attr('aria-checked'), 'true', 'selectAll checkbox is checked'); + assert.equal($selectCheckBoxes.eq(1).attr('aria-checked'), 'true', 'first row\'s checkbox is checked'); + assert.deepEqual(treeList.getSelectedRowKeys(), selectedRowKeys, 'selected row keys'); + assert.deepEqual(treeList.option('selectedRowKeys'), selectedRowKeys, 'selected row keys'); - // act - $selectCheckBoxes.eq(0).trigger('dxclick'); + // act + $selectCheckBoxes.eq(0).trigger('dxclick'); - // assert - assert.equal($selectCheckBoxes.eq(0).attr('aria-checked'), 'false', 'selectAll checkbox is not checked'); - assert.equal($selectCheckBoxes.eq(1).attr('aria-checked'), 'false', 'first row\'s checkbox is not checked'); - assert.deepEqual(treeList.getSelectedRowKeys(), [], 'selected row keys'); - assert.deepEqual(treeList.option('selectedRowKeys'), [], 'selected row keys'); + // assert + assert.equal($selectCheckBoxes.eq(0).attr('aria-checked'), 'false', 'selectAll checkbox is not checked'); + assert.equal($selectCheckBoxes.eq(1).attr('aria-checked'), 'false', 'first row\'s checkbox is not checked'); + assert.deepEqual(treeList.getSelectedRowKeys(), [], 'selected row keys'); + assert.deepEqual(treeList.option('selectedRowKeys'), [], 'selected row keys'); + }); }); }); + diff --git a/testing/tests/DevExpress.ui.widgets/diagramParts/options.tests.js b/testing/tests/DevExpress.ui.widgets/diagramParts/options.tests.js index c9ee15df927a..f87b595ca33d 100644 --- a/testing/tests/DevExpress.ui.widgets/diagramParts/options.tests.js +++ b/testing/tests/DevExpress.ui.widgets/diagramParts/options.tests.js @@ -212,6 +212,70 @@ QUnit.module('Options', moduleConfig, () => { this.instance._diagramInstance.commandManager.getCommand(DiagramCommand.ToggleSimpleView).execute(true); assert.equal(this.instance.option('simpleView'), true); }); + + test('should change dataSource options', function(assert) { + assert.equal(this.instance._diagramInstance.documentDataSource, undefined); + this.instance.option('nodes.dataSource', [ + { + id: '1', + text: 'text1' + }, + { + id: '2', + text: 'text2' + } + ]); + assert.notEqual(this.instance._diagramInstance.documentDataSource, undefined); + assert.equal(this.instance._diagramInstance.documentDataSource.nodes.length, 2); + assert.equal(this.instance._diagramInstance.documentDataSource.edges.length, 0); + + this.instance.option('edges.dataSource', [ + { + id: '3', + from: '1', + to: '2' + } + ]); + assert.notEqual(this.instance._diagramInstance.documentDataSource, undefined); + assert.equal(this.instance._diagramInstance.documentDataSource.nodes.length, 2); + assert.equal(this.instance._diagramInstance.documentDataSource.edges.length, 1); + }); + + test('should change data expression options', function(assert) { + assert.equal(this.instance._diagramInstance.documentDataSource, undefined); + this.instance.option('nodes.dataSource', [ + { + id: '1', + text: 'text1' + } + ]); + assert.notEqual(this.instance._diagramInstance.documentDataSource, undefined); + + assert.notEqual(this.instance._diagramInstance.documentDataSource.nodeDataImporter.getKey, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.nodeDataImporter.setKey, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.nodeDataImporter.getType, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.nodeDataImporter.setType, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.nodeDataImporter.getText, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.nodeDataImporter.setText, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.nodeDataImporter.getChildren, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.nodeDataImporter.setChildren, undefined); + assert.equal(this.instance._diagramInstance.documentDataSource.nodeDataImporter.getContainerKey, undefined); + assert.equal(this.instance._diagramInstance.documentDataSource.nodeDataImporter.setContainerKey, undefined); + + assert.notEqual(this.instance._diagramInstance.documentDataSource.edgeDataImporter.getKey, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.edgeDataImporter.setKey, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.edgeDataImporter.getFrom, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.edgeDataImporter.setFrom, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.edgeDataImporter.getTo, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.edgeDataImporter.setTo, undefined); + + this.instance.option('nodes.containerKeyExpr', 'containerKey'); + assert.equal(this.instance._diagramInstance.documentDataSource.nodeDataImporter.getChildren, undefined); + assert.equal(this.instance._diagramInstance.documentDataSource.nodeDataImporter.setChildren, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.nodeDataImporter.getContainerKey, undefined); + assert.notEqual(this.instance._diagramInstance.documentDataSource.nodeDataImporter.setContainerKey, undefined); + }); + test('should return correct autoLayout parameters based on the nodes.autoLayout option', function(assert) { assert.equal(this.instance.option('nodes.autoLayout'), 'auto'); assert.deepEqual(this.instance._getDataBindingLayoutParameters(), { type: DataLayoutType.Sugiyama }); @@ -237,4 +301,29 @@ QUnit.module('Options', moduleConfig, () => { this.instance.option('nodes.autoLayout', { type: 'tree' }); assert.deepEqual(this.instance._getDataBindingLayoutParameters(), { type: DataLayoutType.Tree }); }); + + test('should change customShapes option', function(assert) { + const descriptions = this.instance._diagramInstance.shapeDescriptionManager.descriptions; + assert.equal(Object.keys(descriptions).length, 43); + + this.instance.option('customShapes', [ + { + type: 'type1', + title: 'type1' + }, + { + type: 'type2', + title: 'type2' + } + ]); + assert.equal(Object.keys(descriptions).length, 45); + + this.instance.option('customShapes', [ + { + type: 'type3', + title: 'type3' + } + ]); + assert.equal(Object.keys(descriptions).length, 44); + }); }); diff --git a/testing/tests/DevExpress.ui.widgets/drawer.scenarios.tests.js b/testing/tests/DevExpress.ui.widgets/drawer.scenarios.tests.js index 3b97b1e459ba..a119b53c0324 100644 --- a/testing/tests/DevExpress.ui.widgets/drawer.scenarios.tests.js +++ b/testing/tests/DevExpress.ui.widgets/drawer.scenarios.tests.js @@ -14,21 +14,23 @@ QUnit.testStart(() => { // $("#qunit-tests").prepend(drawerTesters.markup); }); -// TODO: templateSize, minSize, maxSize, shading, scrolling, rtlEnabled, animationEnabled, onRendered, _viewPortChangeHandler, target, template overflow and/or view overflow +// TODO: templateSize, maxSize, scrolling, rtlEnabled, animationEnabled, onRendered, _viewPortChangeHandler, target, template overflow and/or view overflow const configs = []; ['shrink', 'push', 'overlap'].forEach(openedStateMode => { ['left', 'top', 'right'].forEach(position => { ['slide', 'expand'].forEach(revealMode => { [true, false].forEach(shading => { - configs.push({ openedStateMode, position, revealMode, shading }); + [undefined, 25].forEach(minSize => { + configs.push({ openedStateMode, position, revealMode, shading, minSize }); + }); }); }); }); }); configs.forEach(config => { - QUnit.module(`Scenarios (${config.openedStateMode}, ${config.position}, ${config.revealMode}, shading: ${config.shading})`, { + QUnit.module(`Scenarios (${config.openedStateMode}, ${config.position}, ${config.revealMode}, shading: ${config.shading}, minSize: ${config.minSize})`, { beforeEach() { this.clock = sinon.useFakeTimers(); clearStack(); @@ -40,6 +42,10 @@ configs.forEach(config => { } }, () => { + function configIs(openedStateMode, position, revealMode) { + return config.openedStateMode === openedStateMode && (config.position === position || !position) && (config.revealMode === revealMode || !revealMode); + } + function testOrSkip(name, skip, callback) { if(skip()) { QUnit.skip(name + ' - NOT SUPPORTED', function() {}); @@ -59,18 +65,10 @@ configs.forEach(config => { } function getFullDrawerOptions(targetOptions) { - const defaultOptions = { - revealMode: config.revealMode, - openedStateMode: config.openedStateMode, - position: config.position, - rtlEnabled: false, - shading: config.shading, - animationEnabled: false - }; - return extend(defaultOptions, targetOptions); + return extend({ rtlEnabled: false, animationEnabled: false }, config, targetOptions); } - testOrSkip('opened: false', () => (config.openedStateMode === 'push' && config.position === 'top'), function(assert) { + testOrSkip('opened: false', () => configIs('push', 'top') || configIs('overlap', 'right', 'expand') && config.minSize, function(assert) { const drawerElement = document.getElementById(drawerTesters.drawerElementId); const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ opened: false, @@ -82,10 +80,10 @@ configs.forEach(config => { drawerTesters[config.position].checkHidden(assert, drawer, drawerElement); }); - testOrSkip('opened: false -> opened: true', () => config.openedStateMode === 'push' && config.position === 'top', function(assert) { + testOrSkip('opened: false -> opened: true', () => configIs('push', 'top') || configIs('overlap', 'right'), function(assert) { const drawerElement = document.getElementById(drawerTesters.drawerElementId); const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ - opened: true, + opened: false, template: drawerTesters[config.position].template() })); @@ -96,7 +94,7 @@ configs.forEach(config => { drawerTesters[config.position].checkOpened(assert, drawer, drawerElement); }); - testOrSkip('opened: false, visible: false -> visible: true', () => config.openedStateMode === 'shrink' || (config.openedStateMode === 'push' && config.position === 'top'), function(assert) { + testOrSkip('opened: false, visible: false -> visible: true', () => configIs('shrink') || configIs('push') || configIs('overlap') && config.minSize, function(assert) { const drawerElement = document.getElementById(drawerTesters.drawerElementId); const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ opened: false, @@ -111,7 +109,7 @@ configs.forEach(config => { drawerTesters[config.position].checkHidden(assert, drawer, drawerElement); }); - testOrSkip('opened: false, visible: false -> visible: true -> opened: true', () => config.openedStateMode === 'overlap' || (config.openedStateMode === 'push' && config.position === 'top'), function(assert) { + testOrSkip('opened: false, visible: false -> visible: true -> opened: true', () => configIs('overlap') || configIs('push', 'top'), function(assert) { const drawerElement = document.getElementById(drawerTesters.drawerElementId); const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ opened: false, @@ -128,7 +126,7 @@ configs.forEach(config => { drawerTesters[config.position].checkOpened(assert, drawer, drawerElement); }); - testOrSkip('opened: true', () => config.openedStateMode === 'push' && config.position === 'top', function(assert) { + testOrSkip('opened: true', () => configIs('push', 'top'), function(assert) { const drawerElement = document.getElementById(drawerTesters.drawerElementId); const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ opened: true, @@ -155,7 +153,7 @@ configs.forEach(config => { assert.strictEqual(window.getComputedStyle(drawerElement).display, 'none', 'drawerElement.display'); }); - testOrSkip('opened: true -> visible: false -> visible: true', () => config.openedStateMode === 'push' && config.position === 'top', function(assert) { + testOrSkip('opened: true -> visible: false -> visible: true', () => configIs('push', 'top'), function(assert) { const drawerElement = document.getElementById(drawerTesters.drawerElementId); const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ opened: true, @@ -171,7 +169,7 @@ configs.forEach(config => { drawerTesters[config.position].checkOpened(assert, drawer, drawerElement); }); - testOrSkip(`opened: true, shading: ${config.shading} -> shading: ${!config.shading}`, () => config.openedStateMode === 'push' && config.position === 'top', function(assert) { + testOrSkip(`opened: true, shading: ${config.shading} -> shading: ${!config.shading}`, () => configIs('push', 'top'), function(assert) { const drawerElement = document.getElementById(drawerTesters.drawerElementId); const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ opened: true, @@ -186,7 +184,7 @@ configs.forEach(config => { drawerTesters[config.position].checkOpened(assert, drawer, drawerElement); }); - testOrSkip('opened: true -> repaint', () => config.openedStateMode === 'push' && config.position === 'top', function(assert) { + testOrSkip('opened: true -> repaint', () => configIs('push', 'top'), function(assert) { const drawerElement = document.getElementById(drawerTesters.drawerElementId); const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ opened: true, @@ -200,7 +198,7 @@ configs.forEach(config => { drawerTesters[config.position].checkOpened(assert, drawer, drawerElement); }); - testOrSkip('opened: true (template + onRendered)', () => config.openedStateMode === 'push' && config.position === 'top' || (config.openedStateMode === 'overlap' && config.position === 'right' && config.revealMode === 'expand'), function(assert) { + testOrSkip('opened: true (template + onRendered)', () => configIs('push', 'top') || configIs('overlap', 'right', 'expand'), function(assert) { const drawerElement = document.getElementById(drawerTesters.drawerElementId); const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ width: 200, @@ -226,7 +224,7 @@ configs.forEach(config => { drawerTesters[config.position].checkOpened(assert, drawer, drawerElement); }); - testOverlap('opened: true (T813710: template + rendered + _viewPortChangeHandler)', () => config.position === 'right', function(assert) { + testOverlap('opened: true (T813710: template + rendered + _viewPortChangeHandler)', () => configIs(undefined, 'right'), function(assert) { const drawerElement = document.getElementById(drawerTesters.drawerElementId); const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ opened: true, @@ -252,7 +250,7 @@ configs.forEach(config => { drawerTesters[config.position].checkOpened(assert, drawer, drawerElement); }); - testOrSkip('opened: true, visible: false -> visible: true', () => config.openedStateMode === 'overlap' || config.openedStateMode === 'push', function(assert) { + testOrSkip('opened: true, visible: false -> visible: true', () => configIs('overlap') || configIs('push'), function(assert) { const drawerElement = document.getElementById(drawerTesters.drawerElementId); const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ opened: true, @@ -267,7 +265,7 @@ configs.forEach(config => { drawerTesters[config.position].checkOpened(assert, drawer, drawerElement); }); - testOrSkip('opened: true, visible: false -> repaint', () => config.openedStateMode === 'shrink' || config.openedStateMode === 'overlap' || config.openedStateMode === 'push', function(assert) { + testOrSkip('opened: true, visible: false -> repaint -> visible: true', () => configIs('shrink') || configIs('overlap') || configIs('push'), function(assert) { const drawerElement = document.getElementById(drawerTesters.drawerElementId); const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ opened: true, @@ -279,9 +277,27 @@ configs.forEach(config => { drawer.repaint(); this.clock.tick(100); + drawer.option('visible', true); + this.clock.tick(100); + drawerTesters[config.position].checkOpened(assert, drawer, drawerElement); }); + }); +}); + +QUnit.module('Specific scenarios', { + beforeEach() { + this.clock = sinon.useFakeTimers(); + clearStack(); + }, + afterEach() { + this.clock.restore(); + this.clock = undefined; + clearStack(); + } +}, () => { + ['shrink', 'push', 'overlap'].forEach(openedStateMode => { QUnit.test('opened: false -> opened: true, shader has more priority z-index than overlay inside view content', function(assert) { const prevBaseZIndex = dxOverlay.baseZIndex(); @@ -289,16 +305,24 @@ configs.forEach(config => { dxOverlay.baseZIndex(3000); const drawerElement = document.getElementById(drawerTesters.drawerElementId); - const drawer = new dxDrawer(drawerElement, getFullDrawerOptions({ + const drawer = new dxDrawer(drawerElement, { + rtlEnabled: false, + animationEnabled: false, opened: false, - template: drawerTesters[config.position].template - })); + position: 'left', + revealMode: 'slide', + shading: true, + template: drawerTesters.left.template, + openedStateMode + }); const env = { drawer, drawerElement, templateElement: drawerElement.querySelector('#template'), - viewElement: drawerElement.querySelector('#view') + viewElement: drawerElement.querySelector('#view'), + shading: drawer.option('shading'), + minSize: drawer.option('minSize') || 0 }; new dxLoadPanel(document.getElementById('loadPanel'), { @@ -311,15 +335,15 @@ configs.forEach(config => { drawer.option('opened', true); this.clock.tick(100); - if(config.openedStateMode === 'overlap') { + if(openedStateMode === 'overlap') { assert.strictEqual($('.dx-loadpanel-wrapper').css('zIndex'), '3002', 'loadPanelWrapper.zIndex'); assert.strictEqual($('.dx-loadpanel-content').css('zIndex'), '3002', 'loadPanelContent.zIndex'); + drawerTesters.checkShader(assert, env, { shader: env.shading ? '3500' : 'auto', panel: '3501' }); } else { assert.strictEqual($('.dx-loadpanel-wrapper').css('zIndex'), '3001', 'loadPanelWrapper.zIndex'); assert.strictEqual($('.dx-loadpanel-content').css('zIndex'), '3001', 'loadPanelContent.zIndex'); + drawerTesters.checkShader(assert, env, { shader: env.shading ? '3500' : 'auto', panel: 'auto' }); } - - drawerTesters.checkShader(assert, env, { shader: '3500', panel: '3501' }); } finally { dxOverlay.baseZIndex(prevBaseZIndex); } diff --git a/testing/tests/DevExpress.ui.widgets/fileManagerParts/arrayProvider.tests.js b/testing/tests/DevExpress.ui.widgets/fileManagerParts/arrayProvider.tests.js index 164a40d992c8..290024e0fb20 100644 --- a/testing/tests/DevExpress.ui.widgets/fileManagerParts/arrayProvider.tests.js +++ b/testing/tests/DevExpress.ui.widgets/fileManagerParts/arrayProvider.tests.js @@ -83,9 +83,9 @@ QUnit.module('Array File Provider', moduleConfig, () => { assert.equal(items.length, 2); assert.equal(items[0].name, 'F1'); - assert.ok(items[0].hasSubDirs); + assert.ok(items[0].hasSubDirectories); assert.equal(items[1].name, 'F2'); - assert.notOk(items[1].hasSubDirs); + assert.notOk(items[1].hasSubDirectories); }) .then(() => this.provider.getItems(dir1)) .done(items => { @@ -93,11 +93,11 @@ QUnit.module('Array File Provider', moduleConfig, () => { assert.equal(items.length, 3); assert.equal(items[0].name, 'F1.1'); - assert.notOk(items[0].hasSubDirs); + assert.notOk(items[0].hasSubDirectories); assert.equal(items[1].name, 'F1.2'); - assert.notOk(items[1].hasSubDirs); + assert.notOk(items[1].hasSubDirectories); assert.equal(items[2].name, 'F1.3'); - assert.ok(items[2].hasSubDirs); + assert.ok(items[2].hasSubDirectories); }) .then(() => this.provider.getItems(dir2)) .done(items => { @@ -185,11 +185,31 @@ QUnit.module('Array File Provider', moduleConfig, () => { done(); assert.equal(items.length, 1); - assert.ok(items[0].hasSubDirs); + assert.ok(items[0].hasSubDirectories); assert.strictEqual(subItems.length, subItemsCount + 1, 'sub item count has increased'); }); }); + test('move directory via arguments without data items', function(assert) { + const destDir = new FileSystemItem('F2', true); + const destDirData = this.options.data[1]; + + const dir = new FileSystemItem('F1', true); + + const srcItemCount = this.options.data.length; + const destItemCount = destDirData.items && destDirData.items.length || 0; + + const done = assert.async(1); + + const deferreds = this.provider.moveItems([ dir ], destDir); + deferreds[0].done(() => { + done(); + + assert.strictEqual(this.options.data.length, srcItemCount - 1, 'directory removed from source'); + assert.strictEqual(destDirData.items.length, destItemCount + 1, 'destination directory sub items increased'); + }); + }); + test('copy directory', function(assert) { const dir = new FileSystemItem('F2', true); @@ -225,7 +245,7 @@ QUnit.module('Array File Provider', moduleConfig, () => { items = result; assert.equal(items.length, 2, 'source dir preserved'); - assert.ok(items[0].hasSubDirs, 'source dir items preserved'); + assert.ok(items[0].hasSubDirectories, 'source dir items preserved'); return this.provider.getItems(dir); }) @@ -240,7 +260,7 @@ QUnit.module('Array File Provider', moduleConfig, () => { const root = new FileSystemItem(); const dir = new FileSystemItem('F1', true); - assert.strictEqual(root.hasSubDirs, undefined, 'root hasSubDirs property is undefined'); + assert.strictEqual(root.hasSubDirectories, undefined, 'root hasSubDirectories property is undefined'); let items = null; let itemCount = -1; @@ -269,11 +289,31 @@ QUnit.module('Array File Provider', moduleConfig, () => { .then(result => { done(); - assert.strictEqual(root.hasSubDirs, undefined, 'root hasSubDirs property is undefined'); + assert.strictEqual(root.hasSubDirectories, undefined, 'root hasSubDirectories property is undefined'); assert.strictEqual(result.length, itemCount + 1, 'sub item count has increased'); }); }); + test('copy directory via arguments without data items', function(assert) { + const destDir = new FileSystemItem('F2', true); + const destDirData = this.options.data[1]; + + const dir = new FileSystemItem('F1', true); + + const srcItemCount = this.options.data.length; + const destItemCount = destDirData.items && destDirData.items.length || 0; + + const done = assert.async(1); + + const deferreds = this.provider.copyItems([ dir ], destDir); + deferreds[0].done(() => { + done(); + + assert.strictEqual(this.options.data.length, srcItemCount, 'source directory not changed'); + assert.strictEqual(destDirData.items.length, destItemCount + 1, 'destination directory sub items increased'); + }); + }); + test('throw error when try moving folder with incorrect parameters', function(assert) { let items = null; let subFolders = null; @@ -426,6 +466,22 @@ QUnit.module('Array File Provider', moduleConfig, () => { }); }); + test('create directory via arguments without data items', function(assert) { + const dir = new FileSystemItem('F2', true); + const dirData = this.options.data[1]; + + const itemCount = dirData.items && dirData.items.length || 0; + + const done = assert.async(1); + + this.provider.createDirectory(dir, 'new F.2.2 test dir') + .done(() => { + done(); + + assert.strictEqual(dirData.items.length, itemCount + 1, 'directory created'); + }); + }); + test('throw error on creating new directory in unexisting directory', function(assert) { const done = assert.async(2); @@ -468,6 +524,22 @@ QUnit.module('Array File Provider', moduleConfig, () => { }); }); + test('rename directory via arguments without data items', function(assert) { + const dir = new FileSystemItem('F2', true); + const dirData = this.options.data[1]; + + assert.strictEqual(dirData.name, 'F2', 'directory name correct'); + + const done = assert.async(1); + + this.provider.renameItem(dir, 'new F2 test name') + .done(() => { + done(); + + assert.strictEqual(dirData.name, 'new F2 test name', 'directory renamed'); + }); + }); + test('delete directory', function(assert) { let fileItems = null; @@ -519,6 +591,21 @@ QUnit.module('Array File Provider', moduleConfig, () => { }); }); + test('delete directory via arguments without data items', function(assert) { + const dir = new FileSystemItem('F1', true); + + const srcItemCount = this.options.data.length; + + const done = assert.async(1); + + const deferreds = this.provider.deleteItems([ dir ]); + deferreds[0].done(() => { + done(); + + assert.strictEqual(this.options.data.length, srcItemCount - 1, 'directory removed from source'); + }); + }); + test('upload file', function(assert) { const done = assert.async(4); @@ -558,6 +645,31 @@ QUnit.module('Array File Provider', moduleConfig, () => { }); }); + test('upload file via arguments without data items', function(assert) { + const done = assert.async(2); + + const dir = new FileSystemItem('F2', true); + const dirData = this.options.data[1]; + + const itemCount = dirData.items && dirData.items.length || 0; + + const file = createUploaderFiles(1)[0]; + let uploadInfo = createUploadInfo(file); + + this.provider.uploadFileChunk(file, uploadInfo, dir) + .then(() => { + done(); + + uploadInfo = createUploadInfo(file, 1, uploadInfo.customData); + return this.provider.uploadFileChunk(file, uploadInfo, dir); + }) + .then(() => { + done(); + + assert.strictEqual(dirData.items.length, itemCount + 1, 'file uploaded'); + }); + }); + test('download single file', function(assert) { const content = 'Test content 1'; const done = assert.async(2); @@ -609,4 +721,16 @@ QUnit.module('Array File Provider', moduleConfig, () => { }); }); + test('download single file via arguments without data items', function(assert) { + const done = assert.async(1); + + const file = new FileSystemItem('F1/F1.2/File1.2.txt'); + + fileSaver._onTestSaveAs = (fileName) => { + done(); + assert.strictEqual(fileName, 'File1.2.txt', 'file downloaded'); + }; + + this.provider.downloadItems([ file ]); + }); }); diff --git a/testing/tests/DevExpress.ui.widgets/fileManagerParts/customProvider.tests.js b/testing/tests/DevExpress.ui.widgets/fileManagerParts/customProvider.tests.js index c0c494b2ecb0..24084abe19d4 100644 --- a/testing/tests/DevExpress.ui.widgets/fileManagerParts/customProvider.tests.js +++ b/testing/tests/DevExpress.ui.widgets/fileManagerParts/customProvider.tests.js @@ -186,4 +186,21 @@ QUnit.module('Custom file provider', moduleConfig, () => { }); }); + test('native promise can be used as the funciton result', function(assert) { + const done = assert.async(); + + this.options.getItems = sinon.spy(dir => new Promise(resolve => resolve(itemData))); + this.provider = new CustomFileSystemProvider(this.options); + + const filesDir = new FileSystemItem('Root/Files', true); + + this.provider.getItems(filesDir) + .done(items => { + assert.deepEqual(items, fileSystemItems, 'items acquired'); + assert.strictEqual(this.options.getItems.callCount, 1, 'getItems called once'); + assert.deepEqual(this.options.getItems.args[0][0], filesDir, 'getItems arguments are valid'); + done(); + }); + }); + }); diff --git a/testing/tests/DevExpress.ui.widgets/fileManagerParts/editing.tests.js b/testing/tests/DevExpress.ui.widgets/fileManagerParts/editing.tests.js index 344843f9401b..3ede65f1b687 100644 --- a/testing/tests/DevExpress.ui.widgets/fileManagerParts/editing.tests.js +++ b/testing/tests/DevExpress.ui.widgets/fileManagerParts/editing.tests.js @@ -241,6 +241,9 @@ QUnit.module('Editing operations', moduleConfig, () => { this.wrapper.getToolbarButton('Delete').trigger('dxclick'); this.clock.tick(400); + this.wrapper.getDialogButton('Delete').trigger('dxclick'); + this.clock.tick(400); + $folderNodes = this.wrapper.getFolderNodes(); assert.equal($folderNodes.length, initialCount - 1, 'folders count decreased'); assert.strictEqual($folderNodes.eq(1).find('span').text().indexOf('Folder 1'), -1, 'first folder is not target folder'); @@ -261,6 +264,9 @@ QUnit.module('Editing operations', moduleConfig, () => { this.wrapper.getToolbarButton('Delete').trigger('dxclick'); this.clock.tick(400); + this.wrapper.getDialogButton('Delete').trigger('dxclick'); + this.clock.tick(400); + const $rows = this.wrapper.getRowsInDetailsView(); assert.equal($rows.length, initialCount - 1, 'files count decreased'); assert.strictEqual($rows.eq(0).text().indexOf('File 1.txt'), -1, 'first folder is not target folder'); @@ -287,6 +293,9 @@ QUnit.module('Editing operations', moduleConfig, () => { this.wrapper.getToolbarButton('Delete').trigger('dxclick'); this.clock.tick(400); + this.wrapper.getDialogButton('Delete').trigger('dxclick'); + this.clock.tick(400); + $rows = this.wrapper.getRowsInDetailsView(); assert.equal($rows.length, initialCount - 1, 'files count decreased'); assert.strictEqual($rows.eq(0).text().indexOf('File 1-1.txt'), -1, 'first folder is not target folder'); diff --git a/testing/tests/DevExpress.ui.widgets/fileManagerParts/navigation.tests.js b/testing/tests/DevExpress.ui.widgets/fileManagerParts/navigation.tests.js index 831ec81769b4..7047e6da9e69 100644 --- a/testing/tests/DevExpress.ui.widgets/fileManagerParts/navigation.tests.js +++ b/testing/tests/DevExpress.ui.widgets/fileManagerParts/navigation.tests.js @@ -616,4 +616,47 @@ QUnit.module('Navigation operations', moduleConfig, () => { assert.strictEqual(optionChangedSpy.args[2][0].name, 'currentPath', 'currentPath option changed'); assert.strictEqual(optionChangedSpy.args[2][0].value, 'Folder 1/Folder 1.1', 'currentPath option updated'); }); + + test('Details view - navigation by double click doesn\'t focus parent folder', function(assert) { + this.fileManager.option({ + itemView: { + mode: 'details' + } + }); + this.clock.tick(400); + + let $cell = this.wrapper.getRowNameCellInDetailsView(3); + $cell.trigger('dxdblclick'); + this.clock.tick(400); + + assert.strictEqual(this.wrapper.getDetailsItemName(0), '..', 'parent folder shown'); + assert.notOk(this.wrapper.isDetailsRowSelected(0), 'item not selected'); + assert.notOk(this.wrapper.isDetailsRowFocused(0), 'item not focused'); + + $cell = this.wrapper.getRowNameCellInDetailsView(1); + $cell.trigger('dxdblclick'); + this.clock.tick(400); + + assert.notOk(this.wrapper.isDetailsRowSelected(3), 'item not selected'); + assert.ok(this.wrapper.isDetailsRowFocused(3), 'item focused'); + }); + + test('Thumbnails view - navigation by double click doesn\'t focus parent folder', function(assert) { + const itemName = 'Folder 3'; + + let $item = this.wrapper.findThumbnailsItem(itemName); + $item.trigger('dxdblclick'); + this.clock.tick(400); + + assert.strictEqual(this.wrapper.getThumbnailsItemName(0), '..', 'parent folder shown'); + assert.notOk(this.wrapper.isThumbnailsItemSelected('..'), 'item not selected'); + assert.notOk(this.wrapper.isThumbnailsItemFocused('..'), 'item not focused'); + + $item = this.wrapper.findThumbnailsItem('..'); + $item.trigger('dxdblclick'); + this.clock.tick(400); + + assert.notOk(this.wrapper.isThumbnailsItemSelected(itemName), 'item not selected'); + assert.ok(this.wrapper.isThumbnailsItemFocused(itemName), 'item focused'); + }); }); diff --git a/testing/tests/DevExpress.ui.widgets/fileManagerParts/selection.tests.js b/testing/tests/DevExpress.ui.widgets/fileManagerParts/selection.tests.js index a32b3fbd030f..5dc717a01a6f 100644 --- a/testing/tests/DevExpress.ui.widgets/fileManagerParts/selection.tests.js +++ b/testing/tests/DevExpress.ui.widgets/fileManagerParts/selection.tests.js @@ -3,6 +3,7 @@ const { test } = QUnit; import 'ui/file_manager'; import fx from 'animation/fx'; import { FileManagerWrapper, createTestFileSystem } from '../../../helpers/fileManagerHelpers.js'; +import { triggerCellClick } from '../../../helpers/fileManager/events.js'; const moduleConfig = { @@ -112,4 +113,72 @@ QUnit.module('Selection', moduleConfig, () => { assert.deepEqual(selectionSpy.args[lastIndex - 1][0].currentDeselectedItemKeys, [ itemPath1 ], 'one item became deselected'); }); + test('Details view - single selection can be cleared', function(assert) { + const itemPath = 'Folder 2'; + + const selectionSpy = sinon.spy(); + const focusSpy = sinon.spy(); + + createFileManager(this, { + selectionMode: 'single', + onFocusedItemChanged: focusSpy, + onSelectionChanged: selectionSpy + }); + + const $cell = this.wrapper.getRowNameCellInDetailsView(2); + triggerCellClick($cell); + this.clock.tick(400); + + assert.ok(this.wrapper.isDetailsRowFocused(2), 'item selected'); + assert.strictEqual(selectionSpy.callCount, 1, 'selection event raised'); + assert.strictEqual(selectionSpy.args[0][0].selectedItems[0].path, itemPath, 'correct item in selection'); + assert.strictEqual(focusSpy.callCount, 1, 'focused event raised'); + assert.strictEqual(focusSpy.args[0][0].item.path, itemPath, 'focused item is correct'); + + this.wrapper.getToolbarButton('Clear').trigger('dxclick'); + this.clock.tick(400); + + assert.notOk(this.wrapper.isDetailsRowFocused(2), 'item not selected'); + assert.strictEqual(selectionSpy.callCount, 2, 'selection event raised'); + assert.deepEqual(selectionSpy.args[1][0].currentDeselectedItemKeys, [ itemPath ], 'correct item in selection'); + assert.strictEqual(focusSpy.callCount, 2, 'focused event raised'); + assert.notOk(focusSpy.args[1][0].item, 'focused item is correct'); + }); + + test('Thumbnails view - single selection can be cleared', function(assert) { + const itemPath = 'Folder 2'; + + const selectionSpy = sinon.spy(); + const focusSpy = sinon.spy(); + + createFileManager(this, { + selectionMode: 'single', + itemView: { + mode: 'thumbnails' + }, + onFocusedItemChanged: focusSpy, + onSelectionChanged: selectionSpy + }); + + const $item = this.wrapper.findThumbnailsItem(itemPath); + triggerCellClick($item); + this.clock.tick(400); + + assert.ok(this.wrapper.isThumbnailsItemSelected(itemPath), 'item selected'); + assert.ok(this.wrapper.isThumbnailsItemFocused(itemPath), 'item focused'); + assert.strictEqual(selectionSpy.callCount, 1, 'selection event raised'); + assert.strictEqual(selectionSpy.args[0][0].selectedItems[0].path, itemPath, 'correct item in selection'); + assert.strictEqual(focusSpy.callCount, 1, 'focused event raised'); + assert.strictEqual(focusSpy.args[0][0].item.path, itemPath, 'focused item is correct'); + + this.wrapper.getToolbarButton('Clear').trigger('dxclick'); + this.clock.tick(400); + + assert.notOk(this.wrapper.isThumbnailsItemSelected(itemPath), 'item not selected'); + assert.ok(this.wrapper.isThumbnailsItemFocused(itemPath), 'item focused'); + assert.strictEqual(selectionSpy.callCount, 2, 'selection event raised'); + assert.deepEqual(selectionSpy.args[1][0].currentDeselectedItemKeys, [ itemPath ], 'correct item in selection'); + assert.strictEqual(focusSpy.callCount, 1, 'focused event not raised'); + }); + }); diff --git a/testing/tests/DevExpress.ui.widgets/fileManagerParts/toolbar.tests.js b/testing/tests/DevExpress.ui.widgets/fileManagerParts/toolbar.tests.js index 50460d4e2784..e35be0696ee6 100644 --- a/testing/tests/DevExpress.ui.widgets/fileManagerParts/toolbar.tests.js +++ b/testing/tests/DevExpress.ui.widgets/fileManagerParts/toolbar.tests.js @@ -548,4 +548,93 @@ QUnit.module('Toolbar', moduleConfig, () => { assert.strictEqual($(spy.args[1][0].element).get(0), this.$element.get(0), 'element is correct'); }); + test('file toolbar should support case when only custom elements are specified', function(assert) { + createFileManager(false); + this.clock.tick(400); + + const buttonClick = sinon.spy(); + const fileManager = this.wrapper.getInstance(); + fileManager.option({ + onToolbarItemClick: buttonClick, + toolbar: { + fileSelectionItems: [ + { + widget: 'dxButton', + options: { + text: 'Properties', + icon: 'preferences' + } + }, + { + widget: 'dxButton', + options: { + text: 'Options', + secretKey: 42 + } + } + ] + } + }); + this.clock.tick(400); + + const $item = this.wrapper.findDetailsItem('File 1.txt'); + $item.trigger('dxclick'); + this.clock.tick(400); + + const $toolbar = this.wrapper.getToolbar(); + assert.ok($toolbar.hasClass(Consts.FILE_TOOLBAR_CLASS), 'file toolbar displayed'); + + const $elements = this.wrapper.getToolbarElements(); + assert.equal($elements.length, 2, 'file toolbar has two elements'); + + assert.strictEqual($elements.eq(0).text(), 'Properties', 'properties button is rendered'); + assert.strictEqual($elements.eq(1).text(), 'Options', 'options button is rendered'); + + $elements.eq(0).trigger('dxclick'); + + assert.strictEqual(buttonClick.callCount, 1, 'event raised'); + assert.strictEqual(buttonClick.args[0][0].itemData.widget, 'dxButton', 'is correct widget'); + assert.strictEqual(buttonClick.args[0][0].itemData.options.text, 'Properties', 'has correct text'); + assert.strictEqual(buttonClick.args[0][0].itemData.options.icon, 'preferences', 'has correct icon'); + + $elements.eq(1).trigger('dxclick'); + + assert.strictEqual(buttonClick.callCount, 2, 'event raised'); + assert.strictEqual(buttonClick.args[1][0].itemData.widget, 'dxButton', 'is correct widget'); + assert.strictEqual(buttonClick.args[1][0].itemData.options.text, 'Options', 'has correct text'); + assert.strictEqual(buttonClick.args[1][0].itemData.options.secretKey, 42, 'has custom option'); + }); + + test('display only general toolbar if file toolbar have only custom items and none visible', function(assert) { + createFileManager(false); + this.clock.tick(400); + + const fileManager = this.wrapper.getInstance(); + fileManager.option({ + toolbar: { + fileSelectionItems: [ + { + widget: 'dxButton', + options: { + text: 'Properties' + }, + visible: false + } + ] + } + }); + this.clock.tick(400); + + const $toolbar = this.wrapper.getToolbar(); + assert.ok($toolbar.hasClass(Consts.GENERAL_TOOLBAR_CLASS), 'general toolbar displayed'); + assert.ok(!$toolbar.hasClass(Consts.FILE_TOOLBAR_CLASS), 'file toolbar hidden'); + + const $item = this.wrapper.findDetailsItem('File 1.txt'); + $item.trigger('dxclick'); + this.clock.tick(400); + + assert.ok($toolbar.hasClass(Consts.GENERAL_TOOLBAR_CLASS), 'general toolbar displayed'); + assert.ok(!$toolbar.hasClass(Consts.FILE_TOOLBAR_CLASS), 'file toolbar hidden'); + }); + }); diff --git a/testing/tests/DevExpress.ui.widgets/gantt.tests.js b/testing/tests/DevExpress.ui.widgets/gantt.tests.js index 70dff9a9843c..2cd5187f7eb6 100644 --- a/testing/tests/DevExpress.ui.widgets/gantt.tests.js +++ b/testing/tests/DevExpress.ui.widgets/gantt.tests.js @@ -294,15 +294,14 @@ QUnit.module('Options', moduleConfig, () => { this.clock.tick(); let coreEditingSettings = getGanttViewCore(this.instance).settings.editing; assert.equal(coreEditingSettings.enabled, false, 'editing is prohibited by default'); - assert.equal(coreEditingSettings.allowTaskAdding, true, 'task adding allowed by default'); - assert.equal(coreEditingSettings.allowTaskDeleting, true, 'task deleting allowed by default'); - assert.equal(coreEditingSettings.allowTaskUpdating, true, 'task updating allowed by default'); - assert.equal(coreEditingSettings.allowDependencyAdding, true, 'dependency adding allowed by default'); - assert.equal(coreEditingSettings.allowDependencyDeleting, true, 'dependency deleting allowed by default'); - assert.equal(coreEditingSettings.allowDependencyUpdating, true, 'dependency updating allowed by default'); - assert.equal(coreEditingSettings.allowResourceAdding, true, 'resource adding allowed by default'); - assert.equal(coreEditingSettings.allowResourceDeleting, true, 'resource deleting allowed by default'); - assert.equal(coreEditingSettings.allowResourceUpdating, true, 'resource updating allowed by default'); + assert.equal(coreEditingSettings.allowTaskInsert, true, 'task adding allowed by default'); + assert.equal(coreEditingSettings.allowTaskDelete, true, 'task deleting allowed by default'); + assert.equal(coreEditingSettings.allowTaskUpdate, true, 'task updating allowed by default'); + assert.equal(coreEditingSettings.allowDependencyInsert, true, 'dependency adding allowed by default'); + assert.equal(coreEditingSettings.allowDependencyDelete, true, 'dependency deleting allowed by default'); + assert.equal(coreEditingSettings.allowResourceInsert, true, 'resource adding allowed by default'); + assert.equal(coreEditingSettings.allowResourceDelete, true, 'resource deleting allowed by default'); + assert.equal(coreEditingSettings.allowResourceUpdate, true, 'resource updating allowed by default'); this.instance.option('editing', { enabled: true, allowTaskAdding: false, @@ -310,22 +309,20 @@ QUnit.module('Options', moduleConfig, () => { allowTaskUpdating: false, allowDependencyAdding: false, allowDependencyDeleting: false, - allowDependencyUpdating: false, allowResourceAdding: false, allowResourceDeleting: false, allowResourceUpdating: false, }); coreEditingSettings = getGanttViewCore(this.instance).settings.editing; assert.equal(coreEditingSettings.enabled, true, 'editing allowed'); - assert.equal(coreEditingSettings.allowTaskAdding, false, 'task adding is prohibited'); - assert.equal(coreEditingSettings.allowTaskDeleting, false, 'task deleting is prohibited'); - assert.equal(coreEditingSettings.allowTaskUpdating, false, 'task updating is prohibited'); - assert.equal(coreEditingSettings.allowDependencyAdding, false, 'dependency adding is prohibited'); - assert.equal(coreEditingSettings.allowDependencyDeleting, false, 'dependency deleting is prohibited'); - assert.equal(coreEditingSettings.allowDependencyUpdating, false, 'dependency updating is prohibited'); - assert.equal(coreEditingSettings.allowResourceAdding, false, 'resource adding is prohibited'); - assert.equal(coreEditingSettings.allowResourceDeleting, false, 'resource deleting is prohibited'); - assert.equal(coreEditingSettings.allowResourceUpdating, false, 'resource updating is prohibited'); + assert.equal(coreEditingSettings.allowTaskInsert, false, 'task adding is prohibited'); + assert.equal(coreEditingSettings.allowTaskDelete, false, 'task deleting is prohibited'); + assert.equal(coreEditingSettings.allowTaskUpdate, false, 'task updating is prohibited'); + assert.equal(coreEditingSettings.allowDependencyInsert, false, 'dependency adding is prohibited'); + assert.equal(coreEditingSettings.allowDependencyDelete, false, 'dependency deleting is prohibited'); + assert.equal(coreEditingSettings.allowResourceInsert, false, 'resource adding is prohibited'); + assert.equal(coreEditingSettings.allowResourceDelete, false, 'resource deleting is prohibited'); + assert.equal(coreEditingSettings.allowResourceUpdate, false, 'resource updating is prohibited'); this.instance.option('editing.enabled', false); coreEditingSettings = getGanttViewCore(this.instance).settings.editing; assert.equal(coreEditingSettings.enabled, false, 'editing is prohibited'); @@ -619,7 +616,7 @@ QUnit.module('Toolbar', moduleConfig, () => { 'redo', 'separator', { - formatName: 'zoomIn', + name: 'zoomIn', options: { text: 'test' } diff --git a/testing/tests/DevExpress.ui.widgets/resizable.tests.js b/testing/tests/DevExpress.ui.widgets/resizable.tests.js index 21649668a232..64775c260ccc 100644 --- a/testing/tests/DevExpress.ui.widgets/resizable.tests.js +++ b/testing/tests/DevExpress.ui.widgets/resizable.tests.js @@ -741,6 +741,32 @@ QUnit.module('actions', () => { pointer.dragStart(); }); + QUnit.test('onResizeStart - subscription by "on" method', function(assert) { + assert.expect(3); + + const resizeStartHandler = function(e) { + assert.ok(true, 'onResizeStart action fired'); + + assert.equal(e.width, $resizable.outerWidth(), 'width passed as event argument'); + assert.equal(e.height, $resizable.outerHeight(), 'height passed as event argument'); + }; + + const resize = () => { + const $handle = $resizable.find('.' + RESIZABLE_HANDLE_CORNER_CLASS + '-bottom-right'); + const pointer = pointerMock($handle).start(); + pointer.dragStart(); + }; + + const $resizable = $('#resizable').dxResizable(); + const instance = $resizable.dxResizable('instance'); + + instance.on('resizeStart', resizeStartHandler); + resize(); + + instance.off('resizeStart', resizeStartHandler); + resize(); + }); + QUnit.test('onResize action should be fired during resize', function(assert) { assert.expect(3); @@ -758,6 +784,32 @@ QUnit.module('actions', () => { pointer.dragStart().drag(10, 0); }); + QUnit.test('onResize action - subscription by "on" method', function(assert) { + assert.expect(3); + + const resizeHandler = function(e) { + assert.ok(true, 'onResize action fired'); + + assert.equal(e.width, $resizable.outerWidth(), 'width passed as event argument'); + assert.equal(e.height, $resizable.outerHeight(), 'height passed as event argument'); + }; + + const resize = () => { + const $handle = $resizable.find('.' + RESIZABLE_HANDLE_CORNER_CLASS + '-bottom-right'); + const pointer = pointerMock($handle).start(); + pointer.dragStart().drag(10, 0); + }; + + const $resizable = $('#resizable').dxResizable(); + const instance = $resizable.dxResizable('instance'); + + instance.on('resize', resizeHandler); + resize(); + + instance.off('resize', resizeHandler); + resize(); + }); + QUnit.test('dxresize event should be fired during resize', function(assert) { const $resizable = $('#resizable').dxResizable(); const $handle = $resizable.find('.' + RESIZABLE_HANDLE_CORNER_CLASS + '-bottom-right'); const pointer = pointerMock($handle).start(); @@ -813,5 +865,29 @@ QUnit.module('actions', () => { pointer.dragStart().drag(10, 0).dragEnd(); }); + + QUnit.test('onResizeEnd action - subscription by "on" method', function(assert) { + const resizeEndHandler = function(e) { + assert.ok(true, 'onResizeEnd action fired'); + + assert.equal(e.width, $resizable.outerWidth(), 'width passed as event argument'); + assert.equal(e.height, $resizable.outerHeight(), 'height passed as event argument'); + }; + + const resize = () => { + const $handle = $resizable.find('.' + RESIZABLE_HANDLE_CORNER_CLASS + '-bottom-right'); + const pointer = pointerMock($handle).start(); + pointer.dragStart().drag(10, 0).dragEnd(); + }; + + const $resizable = $('#resizable').dxResizable(); + const instance = $resizable.dxResizable('instance'); + + instance.on('resizeEnd', resizeEndHandler); + resize(); + + instance.off('resizeEnd', resizeEndHandler); + resize(); + }); }); diff --git a/testing/tests/DevExpress.ui.widgets/scrollableParts/scrollable.scrollingByThumb.tests.js b/testing/tests/DevExpress.ui.widgets/scrollableParts/scrollable.scrollingByThumb.tests.js index dfc9bfc5d2b2..c23698ec0880 100644 --- a/testing/tests/DevExpress.ui.widgets/scrollableParts/scrollable.scrollingByThumb.tests.js +++ b/testing/tests/DevExpress.ui.widgets/scrollableParts/scrollable.scrollingByThumb.tests.js @@ -226,6 +226,27 @@ QUnit.test('thumb is visible after update when content became more then containe assert.equal(scrollbar.option('visible'), true, 'thumb is visible after update'); }); +QUnit.test('showScrollbar: onHover, useNative: false, direction: vertical -> scaleRatio should be recalculated on mouseenter before scrollbar has been shown', function(assert) { + const $scrollable = $('#scrollable').height(100); + $scrollable.wrapInner('
').children().height(200); + + const scrollable = $scrollable.dxScrollable({ + showScrollbar: 'onHover', + useNative: false, + direction: 'vertical' + }).dxScrollable('instance'); + + const scrollbar = Scrollbar.getInstance($scrollable.find('.' + SCROLLABLE_SCROLLBAR_CLASS)); + scrollable._strategy._scrollers['vertical']._scaleRatio = 0.5; + + const $container = $scrollable.find(`.${SCROLLABLE_CONTAINER_CLASS}`); + assert.equal(scrollbar.option('visible'), false, 'thumb is hidden'); + $container.trigger('mouseenter'); + + assert.equal(scrollable._strategy._scrollers['vertical']._scaleRatio, 1, 'scaleRatio recalculated'); + assert.equal(scrollbar.option('visible'), true, 'thumb is visible after mouseenter'); +}); + QUnit.test('thumb hide after scroll when showScrollbar = onScroll', function(assert) { const $scrollable = $('#scrollable').dxScrollable({ showScrollbar: 'onScroll', diff --git a/testing/tests/DevExpress.ui.widgets/sortable.tests.js b/testing/tests/DevExpress.ui.widgets/sortable.tests.js index 9b87b4407fc9..c17b0fc0664b 100644 --- a/testing/tests/DevExpress.ui.widgets/sortable.tests.js +++ b/testing/tests/DevExpress.ui.widgets/sortable.tests.js @@ -57,18 +57,33 @@ QUnit.testStart(function() {
item10
-
-
-
item0
-
item1
-
item2
-
item3
-
item4
-
item5
-
item6
-
item7
-
item8
-
item9
+
+
+
item0
+
item1
+
item2
+
item3
+
item4
+
item5
+
item6
+
item7
+
item8
+
item9
+
+
+
+
+
item0
+
item1
+
item2
+
item3
+
item4
+
item5
+
item6
+
item7
+
item8
+
item9
+
`; @@ -125,7 +140,7 @@ QUnit.module('rendering', moduleConfig, () => { }); QUnit.test('Drag template - check args', function(assert) { - // arrange + // arrange let items; const dragTemplate = sinon.spy(() => { return $('
'); @@ -150,7 +165,7 @@ QUnit.module('rendering', moduleConfig, () => { // T826089 QUnit.test('Asynchronous drag template (React)', function(assert) { - // arrange + // arrange let $dragContainer; this.createSortable({ @@ -173,7 +188,7 @@ QUnit.module('rendering', moduleConfig, () => { }); QUnit.test('Default drag template', function(assert) { - // arrange + // arrange this.createSortable({ }); @@ -196,7 +211,7 @@ QUnit.module('rendering', moduleConfig, () => { }); QUnit.test('While dragging cursor should be \'grabbing/pointer\'', function(assert) { - // arrange + // arrange this.createSortable({}); // act @@ -208,7 +223,7 @@ QUnit.module('rendering', moduleConfig, () => { }); QUnit.test('The selector is specific enough to override the style applied to handle element', function(assert) { - // arrange + // arrange $('
Drag
').addClass('default').appendTo(this.$element); this.createSortable({ @@ -234,7 +249,7 @@ QUnit.module('rendering', moduleConfig, () => { QUnit.module('allowReordering', moduleConfig, () => { QUnit.test('allowReordering = false when dropFeedbackMode is \'push\'', function(assert) { - // arrange + // arrange const onDragChangeSpy = sinon.spy(); const onReorderSpy = sinon.spy(); @@ -263,7 +278,7 @@ QUnit.module('allowReordering', moduleConfig, () => { }); QUnit.test('allowReordering = false when dropFeedbackMode is \'indicate\'', function(assert) { - // arrange + // arrange const onDragChangeSpy = sinon.spy(); const onReorderSpy = sinon.spy(); @@ -292,7 +307,7 @@ QUnit.module('allowReordering', moduleConfig, () => { }); QUnit.test('allowReordering = false when allowDropInsideItem is true', function(assert) { - // arrange + // arrange const onDragChangeSpy = sinon.spy(); this.createSortable({ @@ -318,7 +333,7 @@ QUnit.module('allowReordering', moduleConfig, () => { }); QUnit.test('Move to root if allowReordering is false and allowDropInsideItem is true', function(assert) { - // arrange + // arrange const onDragChangeSpy = sinon.spy(); this.createSortable({ @@ -349,7 +364,7 @@ QUnit.module('allowReordering', moduleConfig, () => { }); QUnit.test('option changing', function(assert) { - // arrange + // arrange const sortable = this.createSortable({ filter: '.draggable', moveItemOnDrop: true @@ -370,7 +385,7 @@ QUnit.module('allowReordering', moduleConfig, () => { }); QUnit.test('Move to top if allowReordering is false', function(assert) { - // arrange + // arrange const onDragChangeSpy = sinon.spy(); this.createSortable({ @@ -391,7 +406,7 @@ QUnit.module('allowReordering', moduleConfig, () => { QUnit.module('placeholder and source', moduleConfig, () => { QUnit.test('Source item if filter is not defined', function(assert) { - // arrange + // arrange this.createSortable({ dropFeedbackMode: 'push' }); @@ -413,7 +428,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Source item if content template is defined', function(assert) { - // arrange + // arrange this.$element = $('#itemsWithContentTemplate'); this.createSortable({ @@ -437,7 +452,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Source item', function(assert) { - // arrange + // arrange this.createSortable({ dropFeedbackMode: 'push', filter: '.draggable' @@ -460,7 +475,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Initial placeholder if dropFeedbackMode is indicate', function(assert) { - // arrange + // arrange let items; let $placeholder; let $dragItemElement; @@ -498,7 +513,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Initial placeholder if allowDropInsideItem is true', function(assert) { - // arrange + // arrange let items; let $placeholder; let $dragItemElement; @@ -538,7 +553,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Initial placeholder if dropFeedbackMode is indicate and itemOrientation is horiontal', function(assert) { - // arrange + // arrange let items; let $placeholder; let $dragItemElement; @@ -567,7 +582,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Source classes toggling', function(assert) { - // arrange + // arrange this.createSortable({ dropFeedbackMode: 'push' }); @@ -593,7 +608,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Move items during dragging', function(assert) { - // arrange + // arrange let items; let $item; let $dragItemElement; @@ -625,7 +640,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Move items during dragging if content tempalte is defined', function(assert) { - // arrange + // arrange let items; let $item; let $dragItemElement; @@ -658,7 +673,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Drop when dropFeedbackMode is push', function(assert) { - // arrange + // arrange let items; let $dragItemElement; @@ -682,7 +697,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Drop when dropFeedbackMode is indicate', function(assert) { - // arrange + // arrange let items; let $dragItemElement; @@ -706,7 +721,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Remove placeholder after the drop end', function(assert) { - // arrange + // arrange this.createSortable({ filter: '.draggable', dropFeedbackMode: 'indicate' @@ -729,7 +744,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('The source item should be correct after drag and drop items', function(assert) { - // arrange + // arrange let items; let $item; @@ -759,7 +774,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Dragging an item to the last position when there is ignored (not draggable) item', function(assert) { - // arrange + // arrange let pointer; let items; @@ -792,7 +807,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Dragging an item to the last position when there is ignored (not draggable) item and dropFeedbackMode option is \'indicate\'', function(assert) { - // arrange + // arrange let pointer; let items; @@ -827,7 +842,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Dragging an item when the next item is hidden (dropFeedbackMode is "indicate")', function(assert) { - // arrange + // arrange const $items = $('#items').children(); const onDragChangeSpy = sinon.spy(); @@ -856,7 +871,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Dragging an item when the prev item is hidden (dropFeedbackMode is "indicate")', function(assert) { - // arrange + // arrange const $items = $('#items').children(); const onDragChangeSpy = sinon.spy(); @@ -885,7 +900,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('Dragging an item to the last position when the last item is hidden (dropFeedbackMode is "indicate")', function(assert) { - // arrange + // arrange const $items = $('#items').children(); const onDragChangeSpy = sinon.spy(); @@ -907,7 +922,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { }); QUnit.test('The placeholder should not be displayed for the last visible item when it is dragged (dropFeedbackMode is "indicate")', function(assert) { - // arrange + // arrange let $items = $('#items').children(); $items.eq(1).hide(); @@ -935,7 +950,7 @@ QUnit.module('placeholder and source', moduleConfig, () => { QUnit.module('Events', crossComponentModuleConfig, () => { QUnit.test('onDragChange - check args when dragging an item down', function(assert) { - // arrange + // arrange let items; let args; const onDragChange = sinon.spy(); @@ -962,7 +977,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onDragChange - check args when dragging an item up', function(assert) { - // arrange + // arrange let items; let args; const onDragChange = sinon.spy(); @@ -985,7 +1000,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onDragChange - check args when dragging to last position', function(assert) { - // arrange + // arrange let items; let args; const onDragChange = sinon.spy(); @@ -1008,7 +1023,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('\'onDragChange\' option changing', function(assert) { - // arrange + // arrange let args; let items; const onDragChange = sinon.spy(); @@ -1036,7 +1051,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('\'onDragChange\' event - not drag item when eventArgs.cancel is true', function(assert) { - // arrange + // arrange let items; let $dragItemElement; @@ -1061,7 +1076,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onDragEnd - check args when dragging an item down', function(assert) { - // arrange + // arrange let items; let args; const onDragEnd = sinon.spy(); @@ -1089,7 +1104,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onDragEnd - check args when dragging an item up', function(assert) { - // arrange + // arrange let items; let args; const onDragEnd = sinon.spy(); @@ -1113,7 +1128,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onDragEnd - check args when dragging to last position', function(assert) { - // arrange + // arrange let items; let args; const onDragEnd = sinon.spy(); @@ -1136,7 +1151,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onDragEnd with eventArgs.cancel is true - the draggable element should not change position', function(assert) { - // arrange + // arrange let items; this.createSortable({ @@ -1161,7 +1176,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onDragEnd - check args when dropping onto itself (dropFeedbackMode is \'indicate\')', function(assert) { - // arrange + // arrange let items; let args; const onDragEnd = sinon.spy(); @@ -1190,7 +1205,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('The draggable element should not change position without moveItemOnDrop', function(assert) { - // arrange + // arrange let items; this.createSortable({ @@ -1211,7 +1226,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onDragEnd - check args when dragging inside item', function(assert) { - // arrange + // arrange let items; let args; const onDragEnd = sinon.spy(); @@ -1236,7 +1251,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onPlaceholderPrepared - check args when dragging', function(assert) { - // arrange + // arrange let items; let args; const onPlaceholderPrepared = sinon.spy(); @@ -1266,7 +1281,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('\'onPlaceholderPrepared\' option changing', function(assert) { - // arrange + // arrange let args; let items; const onPlaceholderPrepared = sinon.spy(); @@ -1298,7 +1313,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onAdd - check args', function(assert) { - // arrange + // arrange const onAddSpy = sinon.spy(); const sortable1 = this.createSortable({ @@ -1332,7 +1347,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onAdd - not add item when eventArgs.cancel is true', function(assert) { - // arrange + // arrange const onAddSpy = sinon.spy((e) => { e.cancel = true; }); const sortable1 = this.createSortable({ @@ -1356,7 +1371,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onAdd - not add item without moveItemOnDrop', function(assert) { - // arrange + // arrange const onAddSpy = sinon.spy(); const sortable1 = this.createSortable({ @@ -1379,7 +1394,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onRemove - check args', function(assert) { - // arrange + // arrange const onRemoveSpy = sinon.spy(); const sortable1 = this.createSortable({ @@ -1413,7 +1428,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onRemove - not add item when eventArgs.cancel is true', function(assert) { - // arrange + // arrange const onRemoveSpy = sinon.spy((e) => { e.cancel = true; }); const sortable1 = this.createSortable({ @@ -1439,7 +1454,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onRemove - not add item without moveItemOnDrop', function(assert) { - // arrange + // arrange const onRemoveSpy = sinon.spy(); const sortable1 = this.createSortable({ @@ -1464,7 +1479,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onReorder - check args', function(assert) { - // arrange + // arrange const onReorderSpy = sinon.spy(); const sortable = this.createSortable({ @@ -1489,7 +1504,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onDragMove, onDragEnd, onDragChange, onReorder - check itemData arg', function(assert) { - // arrange + // arrange const itemData = { test: true }; const options = { filter: '.draggable', @@ -1516,7 +1531,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { }); QUnit.test('onAdd, onRemove - check itemData arg', function(assert) { - // arrange + // arrange const itemData = { test: true }; const onAddSpy = sinon.spy(); const onRemoveSpy = sinon.spy(); @@ -1547,7 +1562,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { // T835349 QUnit.test('The onAdd event should be fired when there is horizontal scrolling', function(assert) { - // arrange + // arrange const onAddSpy = sinon.spy(); const sortable1 = this.createSortable({ @@ -1574,7 +1589,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { // T835349 QUnit.test('The onAdd event should be fired when there is vertical scrolling', function(assert) { - // arrange + // arrange const onAddSpy = sinon.spy(); $('#scroll').css('top', '300px'); @@ -1606,7 +1621,7 @@ QUnit.module('Events', crossComponentModuleConfig, () => { QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => { QUnit.test('Dragging item to another the sortable widget', function(assert) { - // arrange + // arrange let items1; let items2; @@ -1643,7 +1658,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Dragging item to another the sortable widget when allowReordering is false', function(assert) { - // arrange + // arrange let items1; let items2; @@ -1682,7 +1697,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Dragging item to another the sortable widget if dragTemplate contains scrollable', function(assert) { - // arrange + // arrange let items1; let items2; @@ -1725,7 +1740,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Dragging item to another the sortable widget without free space', function(assert) { - // arrange + // arrange $('#items2').css('height', ''); $('#items2').children().last().css('margin', 1); @@ -1758,7 +1773,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Dragging an item to another the sortable widget without free space when its last element is hidden', function(assert) { - // arrange + // arrange $('#items2').css('height', ''); $('#items2').children().last().hide(); $('#items2').children().eq(-2).css('margin', 1); @@ -1793,7 +1808,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => QUnit.test('Dragging item with dropFeedbackMode push to another the sortable widget with dropFeedbackMode indicate', function(assert) { - // arrange + // arrange let items1; let items2; @@ -1824,7 +1839,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Dragging item to another the sortable widget when group as object', function(assert) { - // arrange + // arrange let items1; let items2; const group = {}; @@ -1861,7 +1876,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Dragging item should not work when another the sortable widget does not have a group', function(assert) { - // arrange + // arrange let items1; let items2; @@ -1885,7 +1900,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Dropping item to another the sortable widget', function(assert) { - // arrange + // arrange let items1; let items2; @@ -1913,7 +1928,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Dragging item to another the sortable widget with dropFeedbackMode indicate', function(assert) { - // arrange + // arrange let items1; let items2; @@ -1941,7 +1956,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Dropping item to another the sortable widget with dropFeedbackMode indicate', function(assert) { - // arrange + // arrange let items1; let items2; @@ -1971,7 +1986,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Dragging items between sortable widgets', function(assert) { - // arrange + // arrange let items1; let items2; @@ -2011,7 +2026,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Animation should be stopped for target sortable items after leave', function(assert) { - // arrange + // arrange let items2; const sortable1 = this.createSortable({ @@ -2049,7 +2064,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('items should not be moved after leave and enter', function(assert) { - // arrange + // arrange const sortable1 = this.createSortable({ group: 'shared', dropFeedbackMode: 'push' @@ -2072,7 +2087,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Items should be moved after leave sortable if group is defined', function(assert) { - // arrange + // arrange const sortable = this.createSortable({ group: 'shared', dropFeedbackMode: 'push' @@ -2090,7 +2105,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Items should not be moved after leave sortable if group is not defined', function(assert) { - // arrange + // arrange const sortable = this.createSortable({ dropFeedbackMode: 'push' }, $('#items')); @@ -2107,7 +2122,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Update item points when dragging an item to another the sortable widget if dropFeedbackMode is push', function(assert) { - // arrange + // arrange const sortable1 = this.createSortable({ dropFeedbackMode: 'push', filter: '.draggable', @@ -2137,7 +2152,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Drag and drop item from draggable to sortable', function(assert) { - // arrange + // arrange let items1; let items2; @@ -2166,7 +2181,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Drag and drop item from sortable to draggable should not move item', function(assert) { - // arrange + // arrange let items1; let items2; @@ -2191,7 +2206,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Drag and drop item from sortable to draggable with drop handler', function(assert) { - // arrange + // arrange let items1; let items2; @@ -2224,7 +2239,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Drag and drop item to empty sortable', function(assert) { - // arrange + // arrange let items1; let items2; @@ -2252,7 +2267,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Placeholder should not visible on drag item to empty sortable', function(assert) { - // arrange + // arrange const sortable1 = this.createSortable({ filter: '.draggable', dropFeedbackMode: 'indicate', @@ -2275,7 +2290,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => }); QUnit.test('Dragging an item to another sortable and back when it is alone in the collection', function(assert) { - // arrange + // arrange let items1; let items2; @@ -2323,7 +2338,7 @@ QUnit.module('Cross-Component Drag and Drop', crossComponentModuleConfig, () => // T846161 QUnit.test('The onRemove event should be fired when dragging the item from sortable to draggable', function(assert) { - // arrange + // arrange const onRemoveSpy = sinon.spy(); const onDragEndSpy = sinon.spy(); @@ -2390,7 +2405,7 @@ function getModuleConfigForTestsWithScroll(elementSelector, scrollSelector) { QUnit.module('With scroll', getModuleConfigForTestsWithScroll('#itemsWithScroll', '#scroll'), () => { QUnit.test('Placeholder position should be updated during autoscroll', function(assert) { - // arrange + // arrange let pointer; let items; let previousPlaceholderOffsetTop; @@ -2424,7 +2439,7 @@ QUnit.module('With scroll', getModuleConfigForTestsWithScroll('#itemsWithScroll' }); QUnit.test('Placeholder should not be visible outside bottom of scroll container', function(assert) { - // arrange + // arrange let pointer; let items; @@ -2448,8 +2463,45 @@ QUnit.module('With scroll', getModuleConfigForTestsWithScroll('#itemsWithScroll' assert.notOk($(PLACEHOLDER_SELECTOR).is(':visible'), 'placeholder is not visible'); }); + QUnit.test('Placeholder should be visible after page scrolling (T871213)', function(assert) { + // arrange + this.createSortable({ + filter: '.draggable', + dropFeedbackMode: 'indicate' + }); + + try { + const scrollPosition = 1000; + $('#qunit-fixture').removeClass('qunit-fixture-visible'); + $('#qunit-fixture').css('top', 0); + $('body').css('height', 10000); + $('#scroll').css('top', scrollPosition); + window.scrollTo(0, scrollPosition); + + const $items = this.$element.children(); + + // act + const pointer = pointerMock($items.eq(0)).start().down(0, scrollPosition).move(0, 250); + + // assert + assert.strictEqual(window.pageYOffset, scrollPosition); + assert.ok($(PLACEHOLDER_SELECTOR).is(':visible'), 'placeholder is visisble'); + + // act + pointer.move(0, 50); + + // assert + assert.notOk($(PLACEHOLDER_SELECTOR).is(':visible'), 'placeholder is not visible'); + } finally { + $('#qunit-fixture').css('top', ''); + $('body').css('height', ''); + $('#scroll').css('top', 0); + window.scrollTo(0, 0); + } + }); + QUnit.test('Placeholder should not be visible outside bottom of scroll container if overflow on sortable', function(assert) { - // arrange + // arrange let pointer; let items; @@ -2480,7 +2532,7 @@ QUnit.module('With scroll', getModuleConfigForTestsWithScroll('#itemsWithScroll' }); QUnit.test('Placeholder should not be visible outside top of scroll container', function(assert) { - // arrange + // arrange let pointer; let items; @@ -2512,7 +2564,7 @@ QUnit.module('With both scrolls', getModuleConfigForTestsWithScroll('#itemsWithB // T830034 QUnit.test('Placeholder width and offset should be correct if horizontal scroll exists and items have left margin', function(assert) { - // arrange + // arrange let items; const maxScroll = this.$scroll.prop('scrollWidth') - this.$scroll.width(); @@ -2576,7 +2628,7 @@ QUnit.module('With both scrolls', getModuleConfigForTestsWithScroll('#itemsWithB // T830034 QUnit.test('Placeholder width and offset should be correct if horizontal scroll exists and items have right margin', function(assert) { - // arrange + // arrange let items; const maxScroll = this.$scroll.prop('scrollWidth') - this.$scroll.width(); @@ -2642,7 +2694,7 @@ QUnit.module('With both scrolls', getModuleConfigForTestsWithScroll('#itemsWithB // T830034 QUnit.test('Placeholder width and offset should be correct if horizontal scroll exists and items have right and left margins', function(assert) { - // arrange + // arrange let items; const maxScroll = this.$scroll.prop('scrollWidth') - this.$scroll.width(); @@ -2707,4 +2759,164 @@ QUnit.module('With both scrolls', getModuleConfigForTestsWithScroll('#itemsWithB $('.draggable').css('margin-right', '0px'); $('.draggable').css('margin-left', '0px'); }); + + QUnit.test('onReorder should not be called if item was dropped under the sortable', function(assert) { + // arrange + $('#bothScrolls').height(300); + const onReorder = sinon.spy(); + + this.createSortable({ + filter: '.draggable', + onReorder + }); + + // act + pointerMock(this.$element.children().eq(0)).start().down().move(0, 450).up(); + + // assert + assert.equal(onReorder.callCount, 0, 'onReorder call count'); + }); +}); + +QUnit.module('Dragging between sortables with scroll', { + beforeEach: function() { + this.clock = sinon.useFakeTimers(); + + $('#qunit-fixture').addClass('qunit-fixture-visible'); + + this.$elements = [$('#itemsWithBothScrolls'), $('#itemsWithBothScrolls2')]; + this.$scrolls = [$('#bothScrolls'), $('#bothScrolls2')]; + this.instances = []; + + this.createOneSortable = (options, $element) => { + const instance = $element.dxSortable(options).dxSortable('instance'); + + this.instances.push(instance); + + return instance; + }; + + this.createSortable = (options) => { + this.$elements.forEach(($element) => { + this.createOneSortable(options, $element); + }); + }; + }, + afterEach: function() { + this.clock.restore(); + this.clock.reset(); + + $('#qunit-fixture').removeClass('qunit-fixture-visible'); + this.instances.forEach((instance) => { + instance.dispose(); + }); + } +}, () => { + function dragBetweenSortableTest(that, assert, scroll, dragPos) { + // arrange + const onAdd = sinon.spy(); + const onRemove = sinon.spy(); + + that.createSortable({ + filter: '.draggable', + group: 'shared', + moveItemOnDrop: true, + onAdd, + onRemove + }); + + // act + that.$elements[1].scrollLeft(scroll.left); + that.$elements[1].scrollTop(scroll.top); + + pointerMock(that.$elements[0].children().eq(0)).start().down().move(dragPos.x, dragPos.y).move(1, 1).up(); + + // assert + assert.equal(that.$elements[0].children().length, 10, 'children count'); + assert.equal(that.$elements[1].children().length, 10, 'children count'); + assert.equal(onRemove.callCount, 0, 'onRemove call count'); + assert.equal(onAdd.callCount, 0, 'onAdd call count'); + } + + QUnit.test('Dragging between two sortables with scroll', function(assert) { + // arrange + const onAdd = sinon.spy(); + const onRemove = sinon.spy(); + + this.createSortable({ + filter: '.draggable', + group: 'shared', + moveItemOnDrop: true, + onAdd, + onRemove + }); + + // act + pointerMock(this.$elements[0].children().eq(0)).start().down().move(650, 650).move(1, 1).up(); + + // assert + assert.equal(this.$elements[0].children().length, 9, 'children count'); + assert.equal(this.$elements[1].children().length, 11, 'children count'); + assert.equal(onRemove.callCount, 1, 'onRemove call count'); + assert.equal(onAdd.callCount, 1, 'onAdd call count'); + }); + + // T872379 + [true, false].forEach((needBothScrolls) => { + QUnit.test('onAdd and onRemove events should not trigger after dragging under sortable' + (needBothScrolls ? '(both scrolls)' : '(vertical scroll)'), function(assert) { + if(!needBothScrolls) { + $('.draggable').width(280); + this.$elements[0].width(280); + this.$elements[1].width(280); + } + + dragBetweenSortableTest(this, assert, { + left: 0, + top: 0 + }, { + x: 650, + y: 800 + }); + }); + + QUnit.test('onAdd and onRemove events should not trigger after dragging above the sortable' + (needBothScrolls ? '(both scrolls)' : '(vertical scroll)'), function(assert) { + if(!needBothScrolls) { + $('.draggable').width(280); + this.$elements[0].width(280); + this.$elements[1].width(280); + } + + dragBetweenSortableTest(this, assert, { + left: 0, + top: 0 + }, { + x: 800, + y: 650 + }); + }); + + QUnit.test('onAdd and onRemove events should not trigger after dragging on the right of the sortable' + (needBothScrolls ? '(both scrolls)' : '(horizontal scroll)'), function(assert) { + !needBothScrolls && $('.draggable').height(25); + + dragBetweenSortableTest(this, assert, { + left: 500, + top: 500 + }, { + x: 650, + y: 400 + }); + }); + + QUnit.test('onAdd and onRemove events should not trigger after dragging on the left of the sortable' + (needBothScrolls ? '(both scrolls)' : '(horizontal scroll)'), function(assert) { + !needBothScrolls && $('.draggable').height(25); + + dragBetweenSortableTest(this, assert, { + left: 500, + top: 500 + }, { + x: 400, + y: 650 + }); + }); + }); }); diff --git a/testing/tests/DevExpress.ui.widgets/tabs.tests.js b/testing/tests/DevExpress.ui.widgets/tabs.tests.js index 87cf3c935867..0de9e682d2f9 100644 --- a/testing/tests/DevExpress.ui.widgets/tabs.tests.js +++ b/testing/tests/DevExpress.ui.widgets/tabs.tests.js @@ -15,7 +15,7 @@ QUnit.testStart(function() { display: table-cell; padding: 35px; } - + .bigtab.dx-tabs-expanded .dx-tab { width: 1000px; } @@ -407,7 +407,6 @@ QUnit.module('horizontal scrolling', () => { this.clock.restore(); }); - QUnit.test('left nav button should be rendered if showNavButtons=true and possible to scroll left', function(assert) { const $element = $('#scrollableTabs').dxTabs({ items: [{ text: 'item 1' }, { text: 'item 1' }, { text: 'item 1' }, { text: 'item 1' }], @@ -852,3 +851,217 @@ QUnit.module('Live Update', { assert.equal($element.find(`.${TABS_SCROLLABLE_CLASS}`).length, 0, 'scrolling is disabled'); }); }); + +class TestAsyncTabsWrapper { + constructor(options) { + const { itemsCount } = options; + const tabsOptions = extend({ + items: this._generateItems(itemsCount), + templatesRenderAsynchronously: true, + integrationOptions: { + templates: { + 'testItem': { + render(args) { + setTimeout(() => { + $(args.container).append($('
').text(args.model.text)); + args.onRendered(); + }); + } + } + } + } + }, options); + + if(options.itemTemplate !== null) { + tabsOptions.itemTemplate = 'testItem'; + } else { + delete tabsOptions.itemTemplate; + } + + this._$tabs = $('#tabs').dxTabs(tabsOptions); + this._tabs = this._$tabs.dxTabs('instance'); + } + + _generateItems(count = 5) { + return [...new Array(count)].map((value, index) => ({ text: `item ${index}` })); + } + + _checkTabsContent($tabs) { + const items = this._tabs.option('items'); + $tabs.toArray().forEach((tabElement, index) => + QUnit.assert.equal($(tabElement).text(), items[index].text, `text of the ${index} tab`) + ); + } + + set width(value) { + this._tabs.option('width', value); + } + + setItemsByCount(count) { + this._tabs.option('items', this._generateItems(count)); + } + + checkTabsWithoutScrollable() { + const $tabs = this._$tabs.find('> .dx-tabs-wrapper > .dx-tab'); + QUnit.assert.equal($tabs.length, this._tabs.option('items').length, 'tabs are rendered'); + this._checkTabsContent($tabs); + } + + checkTabsWithScrollable() { + const $tabs = this._$tabs.find('> .dx-tabs-scrollable .dx-scrollable-content > .dx-tabs-wrapper > .dx-tab'); + QUnit.assert.equal($tabs.length, this._tabs.option('items').length, 'tabs are rendered in the Scrollable'); + this._checkTabsContent($tabs); + } + + checkNavigationButtons(expected) { + const visibilityStateText = expected ? 'shown' : 'hidden'; + QUnit.assert.equal(this._$tabs.find('> .dx-tabs-nav-button-left').length, Number(expected), `the left nav button element is ${visibilityStateText}`); + QUnit.assert.equal(this._$tabs.find('> .dx-tabs-nav-button-right').length, Number(expected), `the right nav button element is ${visibilityStateText}`); + } +} + +QUnit.module('Async templates', { + beforeEach() { + this.clock = sinon.useFakeTimers(); + }, + afterEach() { + this.clock.restore(); + } +}, () => { + QUnit.test('render tabs', function() { + const testWrapper = new TestAsyncTabsWrapper({ width: 220 }); + this.clock.tick(); + testWrapper.checkTabsWithoutScrollable(); + testWrapper.checkNavigationButtons(false); + }); + + QUnit.test('render tabs. use default and custom templates', function() { + const testWrapper = new TestAsyncTabsWrapper({ + width: 150, + items: [{ text: 'item 1' }, { text: 'item 2' }, { text: 'item 3', template: 'item' }], + itemTemplate: null + }); + + this.clock.tick(); + testWrapper.checkTabsWithoutScrollable(); + testWrapper.checkNavigationButtons(false); + }); + + QUnit.test('render tabs with scrollable. use default and custom templates', function() { + const testWrapper = new TestAsyncTabsWrapper({ + width: 100, + items: [{ text: 'item 1' }, { text: 'item 2' }, { text: 'item 3', template: 'item' }], + showNavButtons: false, + itemTemplate: null + }); + + this.clock.tick(); + testWrapper.checkTabsWithScrollable(); + testWrapper.checkNavigationButtons(false); + }); + + QUnit.test('render tabs with scrollable and navigation buttons. use default and custom templates', function() { + const testWrapper = new TestAsyncTabsWrapper({ + width: 100, + items: [{ text: 'item 1' }, { text: 'item 2' }, { text: 'item 3', template: 'item' }], + showNavButtons: true, + itemTemplate: null + }); + + this.clock.tick(); + testWrapper.checkTabsWithScrollable(); + testWrapper.checkNavigationButtons(true); + }); + + QUnit.test('render tabs with scrollable', function() { + const testWrapper = new TestAsyncTabsWrapper({ width: 150, showNavButtons: false }); + this.clock.tick(); + testWrapper.checkTabsWithScrollable(); + testWrapper.checkNavigationButtons(false); + }); + + QUnit.test('render tabs with scrollable and navigation buttons', function() { + const testWrapper = new TestAsyncTabsWrapper({ width: 150, showNavButtons: true }); + this.clock.tick(); + testWrapper.checkTabsWithScrollable(); + testWrapper.checkNavigationButtons(true); + }); + + QUnit.test('Add scrollable when width is changed from large to small', function() { + const testWrapper = new TestAsyncTabsWrapper({ width: 220, showNavButtons: false }); + this.clock.tick(); + testWrapper.width = 150; + testWrapper.checkTabsWithScrollable(); + testWrapper.checkNavigationButtons(false); + }); + + QUnit.test('Add scrollable and navigation buttons when width is changed from large to small', function() { + const testWrapper = new TestAsyncTabsWrapper({ width: 220, showNavButtons: true }); + this.clock.tick(); + testWrapper.width = 150; + testWrapper.checkTabsWithScrollable(); + testWrapper.checkNavigationButtons(true); + }); + + QUnit.test('Remove scrollable when width is changed from small to large', function() { + const testWrapper = new TestAsyncTabsWrapper({ width: 150, showNavButtons: false }); + this.clock.tick(); + testWrapper.width = 220; + testWrapper.checkTabsWithoutScrollable(); + testWrapper.checkNavigationButtons(false); + }); + + QUnit.test('Remove scrollable and navigation buttons when width is changed from small to large', function() { + const testWrapper = new TestAsyncTabsWrapper({ width: 150, showNavButtons: true }); + this.clock.tick(); + testWrapper.width = 220; + testWrapper.checkTabsWithoutScrollable(); + testWrapper.checkNavigationButtons(false); + }); + + [false, true].forEach(repaintChangesOnly => { + QUnit.test(`Add scrollable when items are changed from 5 to 10, repaintChangesOnly: ${repaintChangesOnly}`, function() { + const testWrapper = new TestAsyncTabsWrapper({ width: 220, showNavButtons: false, repaintChangesOnly }); + + this.clock.tick(); + testWrapper.setItemsByCount(10); + this.clock.tick(); + + testWrapper.checkTabsWithScrollable(); + testWrapper.checkNavigationButtons(false); + }); + + QUnit.test(`Add scrollable and navigation buttons when items are changed from 5 to 10, repaintChangesOnly: ${repaintChangesOnly}`, function() { + const testWrapper = new TestAsyncTabsWrapper({ width: 220, showNavButtons: true, repaintChangesOnly }); + + this.clock.tick(); + testWrapper.setItemsByCount(10); + this.clock.tick(); + + testWrapper.checkTabsWithScrollable(); + testWrapper.checkNavigationButtons(true); + }); + + QUnit.test(`Remove scrollable when items are changed from 10 to 5, repaintChangesOnly: ${repaintChangesOnly}`, function() { + const testWrapper = new TestAsyncTabsWrapper({ width: 220, showNavButtons: false, repaintChangesOnly, itemsCount: 10 }); + + this.clock.tick(); + testWrapper.setItemsByCount(5); + this.clock.tick(); + + testWrapper.checkTabsWithoutScrollable(); + testWrapper.checkNavigationButtons(false); + }); + + QUnit.test(`Remove scrollable and navigation buttons when items are changed from 10 to 5, repaintChangesOnly: ${repaintChangesOnly}`, function() { + const testWrapper = new TestAsyncTabsWrapper({ width: 220, showNavButtons: true, repaintChangesOnly, itemsCount: 10 }); + + this.clock.tick(); + testWrapper.setItemsByCount(5); + this.clock.tick(); + + testWrapper.checkTabsWithoutScrollable(); + testWrapper.checkNavigationButtons(false); + }); + }); +}); diff --git a/testing/tests/DevExpress.ui.widgets/treeViewParts/scrolling.tests.js b/testing/tests/DevExpress.ui.widgets/treeViewParts/scrolling.tests.js index fc69443c3dc5..156d1a4df5f3 100644 --- a/testing/tests/DevExpress.ui.widgets/treeViewParts/scrolling.tests.js +++ b/testing/tests/DevExpress.ui.widgets/treeViewParts/scrolling.tests.js @@ -1,20 +1,20 @@ import TreeViewTestWrapper from '../../../helpers/TreeViewTestHelper.js'; -import devices from 'core/devices'; +import browser from 'core/utils/browser'; import $ from 'jquery'; QUnit.module('scrollToItem', () => { - if(devices.real().ios) { + if(browser.msie) { return; } - function createWrapper(config, dataSource) { + function createWrapper(config, items) { const wrapper = new TreeViewTestWrapper({ displayExpr: 'id', scrollDirection: config.scrollDirection, height: 150, width: 150, - animationEnabled: false, // +400ms per test - dataSource: dataSource, + animationEnabled: false, + items: items, rtlEnabled: config.rtlEnabled, onContentReady: config.onContentReady }); @@ -143,7 +143,7 @@ QUnit.module('scrollToItem', () => { }); wrapper.instance._scrollableContainer.scrollTo({ left: 0, top: 0 }); - const itemData = wrapper.instance.option('dataSource')[0].items[0].items[0]; + const itemData = wrapper.instance.option('items')[0].items[0].items[0]; wrapper.instance.scrollToItem(itemData).done(() => { wrapper.checkNodeIsInVisibleArea(itemData.id); done(); diff --git a/testing/tests/DevExpress.viz.charts/chart.integration.tests.js b/testing/tests/DevExpress.viz.charts/chart.integration.tests.js index d228b7f4ed8d..fbbf39de4d2a 100644 --- a/testing/tests/DevExpress.viz.charts/chart.integration.tests.js +++ b/testing/tests/DevExpress.viz.charts/chart.integration.tests.js @@ -1905,6 +1905,31 @@ QUnit.test('auto switching point markers visibility', function(assert) { assert.ok(chart.getAllSeries()[0].getVisiblePoints()[0].graphic); }); +// T857880 +QUnit.test('Point is visible when placed in visualRande', function(assert) { + const chart = moduleSetup.createChart.call(this, { + dataSource: [{ + country: 'USA', + hydro: 13.7 + }, { + country: 'China', + oil: 13.7 + }], + commonSeriesSettings: { + argumentField: 'country' + }, + series: [ + { valueField: 'hydro', type: 'bar' }, + { valueField: 'oil', type: 'line' } + ], + valueAxis: { + visualRange: [0, 12] + } + }); + + assert.ok(chart.getAllSeries()[1].getVisiblePoints()[0].graphic); +}); + QUnit.test('auto switching point markers visibility is disabled for non-line/area series', function(assert) { const chart = this.createChart({ series: [{ type: 'bar' }] @@ -2724,10 +2749,9 @@ QUnit.test('Checking border hover when pie chart palette changed. B237181', func point.select(); } }); - let hoverState; chart.option({ palette: 'Soft Pastel' }); - hoverState = chart.getAllSeries()[0].getPoints()[0].getOptions().styles.hover; + const hoverState = chart.getAllSeries()[0].getPoints()[0].getOptions().styles.hover; assert.equal(hoverState.stroke, '#60a69f', 'Hover color is color of series'); assert.equal(hoverState['stroke-width'], 0, 'Hover width was 0'); }); @@ -3642,7 +3666,7 @@ QUnit.test('Value axis. Set customPosition and offset options', function(assert) const chart = this.createChart({}); chart.option('valueAxis[0].customPosition', 380); - assert.roughEqual(chart.getValueAxis('axis0')._axisPosition, 450, 5); + assert.roughEqual(chart.getValueAxis('axis0')._axisPosition, 450, 8); chart.option('valueAxis[2].customPosition', 1100); assert.roughEqual(chart.getValueAxis('axis2')._axisPosition, 990, 5); @@ -3693,13 +3717,13 @@ QUnit.test('Zoom and pan', function(assert) { customPosition: 400 }); - assert.roughEqual(valAxis1._axisPosition, 340, 5); + assert.roughEqual(valAxis1._axisPosition, 340, 8); $root.trigger(new $.Event('dxdragstart', { pageX: 500, pageY: 250 })); $root.trigger(new $.Event('dxdrag', { offset: { x: -400, y: 0 } })); $root.trigger(new $.Event('dxdragend', {})); - assert.roughEqual(valAxis1._axisPosition, 125, 5); + assert.equal(valAxis1._axisPosition, chart.getValueAxis('axis0')._axisPosition); assert.roughEqual(valAxis1._axisShift, 37, 5); }); @@ -3754,5 +3778,5 @@ QUnit.test('Argument axis. Set customPositionAxis option', function(assert) { assert.roughEqual(initAxisPosition - emptyAxisPosition, 0, 8); assert.roughEqual(initAxisPosition - otherAxisPosition, 95, 8); - assert.roughEqual(defaultAxisPosition - initAxisPosition, 135, 8); + assert.roughEqual(defaultAxisPosition - initAxisPosition, 135, 10); }); diff --git a/testing/tests/DevExpress.viz.charts/chartAxisDrawing.tests.js b/testing/tests/DevExpress.viz.charts/chartAxisDrawing.tests.js index 5949277e3b17..dfad3df40749 100644 --- a/testing/tests/DevExpress.viz.charts/chartAxisDrawing.tests.js +++ b/testing/tests/DevExpress.viz.charts/chartAxisDrawing.tests.js @@ -88,7 +88,8 @@ function createAxisStubs() { hideTitle: sinon.spy(), drawScaleBreaks: sinon.spy(), hideOuterElements: sinon.spy(), - prepareAnimation: sinon.spy() + prepareAnimation: sinon.spy(), + estimateTickInterval: sinon.stub().returns(false) }; axisFakes @@ -2042,6 +2043,39 @@ QUnit.test('Draw scale breaks', function(assert) { assert.ok(argAxisStub.drawScaleBreaks.called, 'draw scaleBreaks for argument axis'); }); +QUnit.test('Redraw vertical axes, if tickInterval is changed', function(assert) { + const argAxis = createAxisStubs(); + const valAxis = createAxisStubs(); + + argAxis.estimateMargins.returns({ left: 10, top: 8, right: 11, bottom: 20 }); + argAxis.getMargins.returns({ left: 10, top: 7, right: 20, bottom: 13 }); + valAxis.getMargins.returns({ left: 18, top: 15, right: 10, bottom: 9 }); + + valAxis.estimateTickInterval.returns(true); + + this.setupAxes([argAxis, valAxis]); + + new dxChart(this.container, { + dataSource: [{ arg: 1, val: 10 }], + legend: { visible: false } + }); + + assert.deepEqual(this.axisStub.getCall(1).returnValue.createTicks_test_arg, { + left: 18, + right: 20, + top: 15, + bottom: 13, + originalLeft: 0, + originalRight: 0, + originalTop: 0, + originalBottom: 0, + width: 800, + height: 600 + }, 'createTicks valAxis canvas'); + assert.equal(this.axisStub.getCall(1).returnValue.draw.callCount, 2); + assert.equal(this.axisStub.getCall(1).returnValue.draw.lastCall.args[0], false, 'draw valAxis'); +}); + QUnit.module('Axes synchronization', environment); QUnit.test('synchronizeMultiAxes true - only value axes are synchronized', function(assert) { diff --git a/testing/tests/DevExpress.viz.charts/pieChart.tests.js b/testing/tests/DevExpress.viz.charts/pieChart.tests.js index b221a6e9811e..460eace9a987 100644 --- a/testing/tests/DevExpress.viz.charts/pieChart.tests.js +++ b/testing/tests/DevExpress.viz.charts/pieChart.tests.js @@ -2257,6 +2257,18 @@ const overlappingEnvironment = $.extend({}, environment, { assert.deepEqual(series.adjustLabels.getCall(1).args, [true], 'Move from center (T586419)'); }); + QUnit.test('Last adjust labels before resolve overlapping with moving from center (T877200)', function(assert) { + const pie = this.createPieChartWithLabels([{ x: 5, y: 10, width: 10, height: 10, pointPosition: { y: 1, angle: 1 } }, + { x: 5, y: 10, width: 10, height: 10, pointPosition: { y: 2, angle: 2 } }]); + const series = pie.getAllSeries()[0]; + const points = series.getVisiblePoints(); + + // assert + assert.equal(series.adjustLabels.callCount, 5); + assert.ok(series.adjustLabels.lastCall.calledBefore(points[1].getLabels()[0].shift.lastCall)); + assert.deepEqual(series.adjustLabels.getCall(1).args, [true]); + }); + QUnit.test('Do not Adjust labels after resolve overlapping in columns position', function(assert) { const pie = this.createPieChartWithLabels([{ x: 5, y: 10, width: 10, height: 10, pointPosition: { y: 1, angle: 1 } }, { x: 5, y: 10, width: 10, height: 10, pointPosition: { y: 2, angle: 2 } }], 'columns'); diff --git a/testing/tests/DevExpress.viz.charts/zoomAndPan.tests.js b/testing/tests/DevExpress.viz.charts/zoomAndPan.tests.js index 829380d5a06c..3a1b89715704 100644 --- a/testing/tests/DevExpress.viz.charts/zoomAndPan.tests.js +++ b/testing/tests/DevExpress.viz.charts/zoomAndPan.tests.js @@ -3244,3 +3244,258 @@ QUnit.test('Do not prevent and stop if no actions allowed', function(assert) { assert.equal(stopPropagation.callCount, 0); assert.equal(this.trackerStopHandling.callCount, 0); }); + +QUnit.module('Axes custom positioning', environment); + +QUnit.test('Argument axis panning (value axis has custom position)', function(assert) { + const chart = this.createChart({ + argumentAxis: { + visualRange: { + startValue: 3, + endValue: 7 + } + }, + valueAxis: { + customPosition: 5 + }, + zoomAndPan: { + argumentAxis: 'pan' + } + }); + + const valueAxis = chart.getValueAxis(); + const initAxisPosition = valueAxis.getAxisPosition(); + + // act + this.pointer.start({ x: 100, y: 250 }).dragStart().drag(100, 50).dragEnd(); + const panAxisPosition = valueAxis.getAxisPosition(); + + assert.roughEqual(initAxisPosition, 400, 2.01, 'custom position applied'); + assert.ok(initAxisPosition < panAxisPosition, 'value axis moved'); +}); + +QUnit.test('Argument axis panning (value axis shifted by offset)', function(assert) { + const chart = this.createChart({ + argumentAxis: { + visualRange: { + startValue: 3, + endValue: 7 + } + }, + valueAxis: { + offset: 100 + }, + zoomAndPan: { + argumentAxis: 'pan' + } + }); + + const valueAxis = chart.getValueAxis(); + const initAxisPosition = valueAxis.getAxisPosition(); + + // act + this.pointer.start({ x: 100, y: 250 }).dragStart().drag(200, 50).dragEnd(); + const panAxisPosition = valueAxis.getAxisPosition(); + + assert.roughEqual(panAxisPosition, 100, 2.01, 'offset applied'); + assert.equal(initAxisPosition, panAxisPosition, 'value axis is static'); +}); + +QUnit.test('Argument axis panning - value axis adjust to predefined position', function(assert) { + const chart = this.createChart({ + argumentAxis: { + visualRange: { + startValue: 3, + endValue: 7 + } + }, + valueAxis: { + customPosition: 6, + offset: 50 + }, + zoomAndPan: { + argumentAxis: 'pan' + } + }); + + const valueAxis = chart.getValueAxis(); + const initAxisPosition = valueAxis.getAxisPosition(); + + // act + this.pointer.start({ x: 100, y: 250 }).dragStart().drag(300, 10).dragEnd(); + const axisPositionRight = valueAxis.getAxisPosition(); + + this.pointer.start({ x: 50, y: 250 }).dragStart().drag(100, 10).dragEnd(); + const staticPositionAfterDrag = valueAxis.getAxisPosition(); + + assert.ok(initAxisPosition < axisPositionRight, 'value axis moved'); + assert.equal(axisPositionRight, 800, 'value axis has predefined position'); + assert.equal(axisPositionRight, staticPositionAfterDrag, 'value axis not moved'); +}); + +QUnit.test('Value axis panning (argument axis has custom position)', function(assert) { + const chart = this.createChart({ + valueAxis: { + visualRange: { + startValue: 1, + endValue: 4 + } + }, + argumentAxis: { + customPosition: 2.5 + }, + zoomAndPan: { + valueAxis: 'pan' + } + }); + + const argumentAxis = chart.getArgumentAxis(); + const initAxisPosition = argumentAxis.getAxisPosition(); + + // act + this.pointer.start({ x: 100, y: 100 }).dragStart().drag(10, 100).dragEnd(); + const panAxisPosition = argumentAxis.getAxisPosition(); + + assert.roughEqual(initAxisPosition, 300, 2.01, 'custom position applied'); + assert.ok(initAxisPosition < panAxisPosition, 'argument axis moved'); +}); + +QUnit.test('Value axis panning (argument axis shifted by offset)', function(assert) { + const chart = this.createChart({ + valueAxis: { + visualRange: { + startValue: 1, + endValue: 4 + } + }, + argumentAxis: { + offset: -200 + }, + zoomAndPan: { + valueAxis: 'pan' + } + }); + + const argumentAxis = chart.getArgumentAxis(); + const initAxisPosition = argumentAxis.getAxisPosition(); + + // act + this.pointer.start({ x: 100, y: 100 }).dragStart().drag(50, 100).dragEnd(); + const panAxisPosition = argumentAxis.getAxisPosition(); + + assert.roughEqual(panAxisPosition, 400, 2.01, 'offset applied'); + assert.equal(initAxisPosition, panAxisPosition, 'argument axis is static'); +}); + +QUnit.test('Value axis panning - argument axis adjust to predefined position', function(assert) { + const chart = this.createChart({ + valueAxis: { + visualRange: { + startValue: 1, + endValue: 4 + } + }, + argumentAxis: { + customPosition: 2, + offset: 50 + }, + zoomAndPan: { + valueAxis: 'pan' + } + }); + + const argumentAxis = chart.getArgumentAxis(); + const initAxisPosition = argumentAxis.getAxisPosition(); + + // act + this.pointer.start({ x: 100, y: 100 }).dragStart().drag(10, 300).dragEnd(); + const axisPositionBottom = argumentAxis.getAxisPosition(); + + this.pointer.start({ x: 100, y: 100 }).dragStart().drag(10, 100).dragEnd(); + const staticPositionAfterDrag = argumentAxis.getAxisPosition(); + + assert.ok(initAxisPosition < axisPositionBottom, 'argument axis moved'); + assert.equal(axisPositionBottom, 600, 'argument axis has predefined position'); + assert.equal(axisPositionBottom, staticPositionAfterDrag, 'argument axis not moved'); +}); + +QUnit.test('Axes zooming - wheel', function(assert) { + const chart = this.createChart({ + argumentAxis: { + customPosition: 2.5, + visualRange: { + startValue: 3, + endValue: 7 + } + }, + valueAxis: { + customPosition: 5, + visualRange: { + startValue: 1, + endValue: 4 + } + }, + zoomAndPan: { + argumentAxis: 'both', + valueAxis: 'both', + allowMouseWheel: true + } + }); + + const argumentAxis = chart.getArgumentAxis(); + const valueAxis = chart.getValueAxis(); + + // act + this.pointer.start({ x: 150, y: 100 }).wheel(10); + + assert.roughEqual(argumentAxis.getAxisPosition(), 320, 2.01, 'argument axis moved - zoom in'); + assert.roughEqual(valueAxis.getAxisPosition(), 425, 2.01, 'value axis moved - zoom in'); + + this.pointer.start({ x: 200, y: 200 }).wheel(-10).wheel(-10).wheel(-10); + + assert.roughEqual(argumentAxis.getAxisPosition(), 287, 2.01, 'argument axis moved - zoom out'); + assert.roughEqual(valueAxis.getAxisPosition(), 364, 2.01, 'value axis moved - zoom out'); +}); + +QUnit.test('Axes zooming - pinch', function(assert) { + const chart = this.createChart({ + argumentAxis: { + customPosition: 2.5, + visualRange: { + startValue: 3, + endValue: 7 + } + }, + valueAxis: { + customPosition: 5, + visualRange: { + startValue: 1, + endValue: 4 + } + }, + zoomAndPan: { + argumentAxis: 'both', + valueAxis: 'both', + allowMouseWheel: true + } + }); + + const argumentAxis = chart.getArgumentAxis(); + const valueAxis = chart.getValueAxis(); + + // act + const $root = $(chart._renderer.root.element); + $root.trigger($.Event('dxpointerdown', { pointerType: 'touch', pointers: [{ pointerId: 1, pageX: 20, pageY: 20 }, { pointerId: 2, pageX: 50, pageY: 50 }] })); + $root.trigger($.Event('dxpointermove', { pointerType: 'touch', pointers: [{ pointerId: 1, pageX: 10, pageY: 10 }, { pointerId: 2, pageX: 60, pageY: 60 }] })); + $root.trigger($.Event('dxpointerup', { pointerType: 'touch', pointers: [] })); + + assert.roughEqual(argumentAxis.getAxisPosition(), 477, 2.01, 'argument axis moved - zoom in'); + assert.roughEqual(valueAxis.getAxisPosition(), 643, 2.01, 'value axis moved - zoom in'); + + $root.trigger($.Event('dxpointerdown', { pointerType: 'touch', pointers: [{ pointerId: 1, pageX: 10, pageY: 10 }, { pointerId: 2, pageX: 60, pageY: 60 }] })); + $root.trigger($.Event('dxpointermove', { pointerType: 'touch', pointers: [{ pointerId: 1, pageX: 20, pageY: 20 }, { pointerId: 2, pageX: 50, pageY: 50 }] })); + $root.trigger($.Event('dxpointerup', { pointerType: 'touch', pointers: [] })); + + assert.roughEqual(argumentAxis.getAxisPosition(), 300, 2.01, 'argument axis moved - zoom out'); + assert.roughEqual(valueAxis.getAxisPosition(), 400, 2.01, 'value axis moved - zoom out'); +}); diff --git a/testing/tests/DevExpress.viz.core/baseAxis.tests.js b/testing/tests/DevExpress.viz.core/baseAxis.tests.js index e3d90d6dd85c..50270ca82444 100644 --- a/testing/tests/DevExpress.viz.core/baseAxis.tests.js +++ b/testing/tests/DevExpress.viz.core/baseAxis.tests.js @@ -83,11 +83,10 @@ QUnit.test('Create axis', function(assert) { const constantLinesGroup = { above: this.renderer.g(), under: this.renderer.g() }; const axesContainerGroup = renderer.g(); const gridGroup = renderer.g(); - let axis; renderer.g.reset(); - axis = new Axis({ + const axis = new Axis({ renderer: renderer, stripsGroup: stripsGroup, labelAxesGroup: labelAxesGroup, @@ -321,6 +320,36 @@ QUnit.test('Get options after resetTypes - axis type and data types are in lower }, 'Options should be correct'); }); +QUnit.test('Check tickInterval with new canvas', function(assert) { + this.updateOptions({}); + this.generatedTickInterval = 2; + this.axis.createTicks(this.canvas); + const translator = translator2DModule.Translator2D.lastCall.returnValue; + const canvas = { + top: 200, + bottom: 200, + left: 200, + right: 200, + width: 400, + height: 400 + }; + + assert.ok(!this.axis.estimateTickInterval(canvas), 'tickInterval is not change'); + assert.equal(translator.updateCanvas.lastCall.args[0], canvas, 'translator is updated'); + + this.generatedTickInterval = 3; + const newCanvas = { + top: 100, + bottom: 100, + left: 100, + right: 100, + width: 400, + height: 400 + }; + assert.ok(this.axis.estimateTickInterval(newCanvas), 'tickInterval is change'); + assert.equal(translator.updateCanvas.lastCall.args[0], newCanvas, 'translator is updated'); +}); + QUnit.test('GetMarginOptions when they are not set', function(assert) { this.updateOptions({}); @@ -491,6 +520,28 @@ QUnit.test('categoriesSortingMethod returns \'categories\' option when \'categor assert.deepEqual(sort, ['1', '2']); }); +// T857880 +QUnit.test('getCanvasRange', function(assert) { + const translator = translator2DModule.Translator2D.lastCall.returnValue; + + translator.stub('translate').onCall(0).returns('translateResult_1'); + translator.stub('translate').onCall(1).returns('translateResult_2'); + translator.stub('from').onCall(0).returns('startValue'); + translator.stub('from').onCall(1).returns('endValue'); + + const canvasRange = this.axis.getCanvasRange(); + + assert.strictEqual(translator.translate.callCount, 2); + assert.strictEqual(translator.translate.getCall(0).args[0], 'canvas_position_start'); + assert.strictEqual(translator.translate.getCall(1).args[0], 'canvas_position_end'); + + assert.strictEqual(translator.from.callCount, 2); + assert.strictEqual(translator.from.getCall(0).args[0], 'translateResult_1'); + assert.strictEqual(translator.from.getCall(1).args[0], 'translateResult_2'); + + assert.deepEqual(canvasRange, { startValue: 'startValue', endValue: 'endValue' }); +}); + QUnit.module('Labels Settings', { beforeEach: function() { environment.beforeEach.call(this); @@ -688,7 +739,7 @@ QUnit.test('Validate, argumentType - numeric, max and min is wrong specified', f assert.deepEqual(this.axis.getOptions().max, undefined); }); -QUnit.test('Validate, argumentType - numeric, max and min is wrong specified', function(assert) { +QUnit.test('Validate, argumentType - wrong, max and min is wrong specified', function(assert) { this.updateOptions({ argumentType: 'wrongType', max: 'll', min: 'kk' }); this.axis.validate(); @@ -813,7 +864,6 @@ QUnit.test('getViewport return object with field \'action\' if need', function(a }); QUnit.test('hold min/max for single point series', function(assert) { - let businessRange; this.updateOptions({ tick: { visible: true @@ -822,7 +872,7 @@ QUnit.test('hold min/max for single point series', function(assert) { this.axis.setBusinessRange({ min: 4, max: 4 }); this.generatedTicks = [3.2, 3.4, 3.6, 3.8, 4.0, 4.2, 4.4, 4.6, 4.8]; this.axis.createTicks(this.canvas); - businessRange = this.axis.getTranslator().getBusinessRange(); + const businessRange = this.axis.getTranslator().getBusinessRange(); assert.equal(businessRange.min, 4, 'min'); assert.equal(businessRange.max, 4, 'max'); diff --git a/testing/tests/DevExpress.viz.core/tooltip.tests.js b/testing/tests/DevExpress.viz.core/tooltip.tests.js index e1ba426f5d01..e5e9667bf7c5 100644 --- a/testing/tests/DevExpress.viz.core/tooltip.tests.js +++ b/testing/tests/DevExpress.viz.core/tooltip.tests.js @@ -60,34 +60,29 @@ rendererModule.Renderer = function(parameters) { QUnit.module('Main functionality', { beforeEach: function() { this.options = getInitialOptions(); - this.patchFontOptions = sinon.stub(vizUtils, 'patchFontOptions', function(arg) { - const obj = {}; - for(const key in arg) { - obj[key + 'Patched'] = arg[key]; - } - return obj; - }); + + this._oldPatchFontOptions = vizUtils.patchFontOptions; + this.patchFontOptions = sinon.spy(function() { return this._oldPatchFontOptions.apply(null, arguments); }.bind(this)); + vizUtils.patchFontOptions = this.patchFontOptions; }, afterEach: function() { - this.patchFontOptions.restore(); + vizUtils.patchFontOptions = this._oldPatchFontOptions; } }); QUnit.test('Create tooltip', function(assert) { const et = { event: 'trigger' }; - let tooltip; - let wrapper; let div; // act - tooltip = new Tooltip({ eventTrigger: et, cssClass: 'tooltip-class', pathModified: 'pathModified-option' }); + const tooltip = new Tooltip({ eventTrigger: et, cssClass: 'tooltip-class', pathModified: 'pathModified-option' }); // assert assert.equal(tooltip._eventTrigger, et, 'eventTrigger'); assert.deepEqual(tooltip._renderer.ctorArgs, [{ pathModified: 'pathModified-option', container: tooltip._wrapper[0] }], 'renderer with rendererOptions'); assert.ok(tooltip._wrapper, 'wrapper'); - wrapper = tooltip._wrapper.get(0); + const wrapper = tooltip._wrapper.get(0); assert.equal(wrapper.nodeName, 'DIV'); assert.equal(wrapper.className, 'tooltip-class'); assert.equal(wrapper.style.position, 'absolute'); @@ -131,10 +126,9 @@ QUnit.test('Create tooltip', function(assert) { QUnit.test('Set options. All options', function(assert) { const et = { event: 'trigger' }; const tooltip = new Tooltip({ eventTrigger: et }); - let result; // act - result = tooltip.setOptions(this.options); + const result = tooltip.setOptions(this.options); // assert assert.equal(tooltip, result); @@ -143,7 +137,9 @@ QUnit.test('Set options. All options', function(assert) { assert.equal(this.patchFontOptions.callCount, 1, 'font'); assert.deepEqual(this.patchFontOptions.firstCall.args, [this.options.font]); assert.equal(tooltip._textFontStyles, this.patchFontOptions.firstCall.returnValue); // reference - assert.equal(tooltip._textFontStyles.color, this.options.font.color); // additional value + assert.notEqual(tooltip._textFontStyles.color, this.options.font.color); // additional value + assert.equal(tooltip._textFontStyles.color, tooltip._textFontStyles.fill); // T879069 + assert.notOk(tooltip._textFontStyles.color.opacity); // T879069 assert.equal(tooltip._customizeTooltip, this.options.customizeTooltip, 'customizeTooltip'); }); @@ -151,10 +147,9 @@ QUnit.test('Set options. All options', function(assert) { QUnit.test('Set options. Cloud border options', function(assert) { const et = { event: 'trigger' }; const tooltip = new Tooltip({ eventTrigger: et }); - let result; this.options.border.visible = false; // act - result = tooltip.setOptions(this.options); + const result = tooltip.setOptions(this.options); // assert assert.equal(tooltip, result); @@ -164,11 +159,10 @@ QUnit.test('Set options. Cloud border options', function(assert) { QUnit.test('Set options. ZIndex', function(assert) { const et = { event: 'trigger' }; const tooltip = new Tooltip({ eventTrigger: et }); - let result; this.options.zIndex = 1000; // act sinon.spy(tooltip._wrapper, 'css'); - result = tooltip.setOptions(this.options); + const result = tooltip.setOptions(this.options); // assert assert.equal(tooltip, result); @@ -179,10 +173,9 @@ QUnit.test('Set options. ZIndex', function(assert) { QUnit.test('Set options. Container is incorrect', function(assert) { const et = { event: 'trigger' }; const tooltip = new Tooltip({ eventTrigger: et }); - let result; this.options.container = '.some-wrong-class-name'; // act - result = tooltip.setOptions(this.options); + const result = tooltip.setOptions(this.options); // assert assert.equal(tooltip, result); @@ -192,10 +185,9 @@ QUnit.test('Set options. Container is incorrect', function(assert) { QUnit.test('Set options. Container is correct', function(assert) { const et = { event: 'trigger' }; const tooltip = new Tooltip({ eventTrigger: et }); - let result; this.options.container = '.some-correct-class-name'; // act - result = tooltip.setOptions(this.options); + const result = tooltip.setOptions(this.options); // assert assert.equal(tooltip, result); @@ -300,10 +292,9 @@ QUnit.test('Body has horizontal scroll', function(assert) { QUnit.test('Set options. customizeTooltip', function(assert) { const et = { event: 'trigger' }; const tooltip = new Tooltip({ eventTrigger: et }); - let result; this.options.customizeTooltip = {}; // act - result = tooltip.setOptions(this.options); + const result = tooltip.setOptions(this.options); // assert assert.equal(tooltip, result); @@ -350,12 +341,11 @@ QUnit.test('Set options. Two times', function(assert) { } }; const tooltip = new Tooltip({ eventTrigger: et }); - let result; tooltip.setOptions(this.options); // act - result = tooltip.setOptions(options2); + const result = tooltip.setOptions(options2); // assert assert.equal(tooltip, result); @@ -389,7 +379,6 @@ QUnit.test('Set renderer options / rtl enabled', function(assert) { QUnit.test('Render, enabled', function(assert) { const et = { event: 'trigger' }; const tooltip = new Tooltip({ eventTrigger: et }); - let result; tooltip.setOptions(this.options); @@ -398,7 +387,7 @@ QUnit.test('Render, enabled', function(assert) { tooltip._textGroupHtml.css = sinon.spy(); // act - result = tooltip.render(); + const result = tooltip.render(); // assert assert.equal(tooltip, result); @@ -408,12 +397,19 @@ QUnit.test('Render, enabled', function(assert) { // for svg text ↓ assert.equal(tooltip._text.css.callCount, 1, 'text styles'); - assert.deepEqual(tooltip._text.css.firstCall.args, [tooltip._textFontStyles]); + assert.deepEqual(tooltip._text.css.firstCall.args[0], tooltip._textFontStyles); // for svg text ↑ // for html text ↓ assert.equal(tooltip._textGroupHtml.css.callCount, 1, 'textGroupHtml styles'); - assert.deepEqual(tooltip._textGroupHtml.css.firstCall.args, [tooltip._textFontStyles]); + assert.deepEqual(tooltip._textGroupHtml.css.firstCall.args[0], { + color: 'rgba(147,147,147,0.7)', + fill: 'rgba(147,147,147,0.7)', + fontFamily: '\'Segoe UI\', \'Helvetica Neue\', \'Trebuchet MS\', Verdana', + fontSize: 14, + fontWeight: 400, + opacity: null + }); // for html text ↑ }); @@ -423,13 +419,12 @@ QUnit.test('Update', function(assert) { const tooltip = new Tooltip({ eventTrigger: et }); const setOptions = tooltip.setOptions; const render = tooltip.render; - let result; tooltip.setOptions = sinon.spy(function() { return setOptions.apply(tooltip, arguments); }); tooltip.render = sinon.spy(function() { return render.apply(tooltip, arguments); }); // act - result = tooltip.update(options); + const result = tooltip.update(options); // assert assert.equal(tooltip, result); @@ -620,7 +615,7 @@ QUnit.test('Show preparations. W/o customize, w/ text', function(assert) { assert.deepEqual(this.tooltip._state, { color: '#ffffff', borderColor: '#252525', - textColor: '#939393', + textColor: 'rgba(147,147,147,0.7)', formatObject: formatObject, text: 'some-text' }, 'state'); @@ -672,7 +667,7 @@ QUnit.test('Show preparations. W/o customize, w/ text from \'description\' filed assert.deepEqual(this.tooltip._state, { color: '#ffffff', borderColor: '#252525', - textColor: '#939393', + textColor: 'rgba(147,147,147,0.7)', text: 'some-text', formatObject: formatObject }, 'state'); @@ -1010,7 +1005,7 @@ QUnit.test('Show preparations. Certain container', function(assert) { assert.deepEqual(this.tooltip._state, { color: '#ffffff', borderColor: '#252525', - textColor: '#939393', + textColor: 'rgba(147,147,147,0.7)', text: 'some-text', formatObject }, 'state'); @@ -1044,7 +1039,7 @@ QUnit.test('Show. W/o params', function(assert) { assert.deepEqual(this.tooltip._state, { color: '#ffffff', borderColor: '#252525', - textColor: '#939393', + textColor: 'rgba(147,147,147,0.7)', text: 'some-text', formatObject }, 'state'); @@ -1054,7 +1049,7 @@ QUnit.test('Show. W/o params', function(assert) { assert.equal(cloud._stored_settings.stroke, '#252525'); assert.equal(this.tooltip._text.css.callCount, 1, 'text styles'); - assert.deepEqual(this.tooltip._text.css.firstCall.args, [{ fill: '#939393' }]); + assert.deepEqual(this.tooltip._text.css.firstCall.args, [{ fill: 'rgba(147,147,147,0.7)' }]); assert.equal(this.tooltip._text.attr.callCount, 1, 'text attrs'); assert.deepEqual(this.tooltip._text.attr.firstCall.args, [{ text: 'some-text', 'class': 'tooltip_class' }]); @@ -1096,7 +1091,7 @@ QUnit.test('Show. W/o params. Html', function(assert) { assert.deepEqual(this.tooltip._state, { color: '#ffffff', borderColor: '#252525', - textColor: '#939393', + textColor: 'rgba(147,147,147,0.7)', isRendered: true, html: 'some-html', formatObject @@ -1107,7 +1102,7 @@ QUnit.test('Show. W/o params. Html', function(assert) { assert.equal(cloud._stored_settings.stroke, '#252525'); assert.equal(this.tooltip._textGroupHtml.css.callCount, 3, 'textGroupHtml styles'); - assert.deepEqual(this.tooltip._textGroupHtml.css.firstCall.args, [{ color: '#939393', width: 3000 }]); + assert.deepEqual(this.tooltip._textGroupHtml.css.firstCall.args, [{ color: 'rgba(147,147,147,0.7)', width: 3000 }]); assert.ok(this.tooltip._textHtml.html.calledOnce, 'textHtml html'); assert.deepEqual(this.tooltip._textHtml.html.firstCall.args, ['some-html'], 'textHtml html'); @@ -1160,7 +1155,7 @@ QUnit.test('Show. W/o params. Template', function(assert) { assert.deepEqual(this.tooltip._state, { color: '#ffffff', borderColor: '#252525', - textColor: '#939393', + textColor: 'rgba(147,147,147,0.7)', isRendered: true, html: 'custom html', text: 'some-text', @@ -1172,7 +1167,7 @@ QUnit.test('Show. W/o params. Template', function(assert) { assert.equal(cloud._stored_settings.stroke, '#252525'); assert.equal(this.tooltip._textGroupHtml.css.callCount, 3, 'textGroupHtml styles'); - assert.deepEqual(this.tooltip._textGroupHtml.css.firstCall.args, [{ color: '#939393', width: 3000 }]); + assert.deepEqual(this.tooltip._textGroupHtml.css.firstCall.args, [{ color: 'rgba(147,147,147,0.7)', width: 3000 }]); assert.ok(this.tooltip._textHtml.html.calledOnce, 'textHtml html'); assert.deepEqual(this.tooltip._textHtml.html(), 'custom html', 'textHtml html'); @@ -1282,7 +1277,7 @@ QUnit.test('Show. W/o params. Do not call template if skipTemplate in formatObje assert.deepEqual(this.tooltip._state, { color: '#ffffff', borderColor: '#252525', - textColor: '#939393', + textColor: 'rgba(147,147,147,0.7)', text: 'some-text', formatObject }, 'state'); @@ -1292,7 +1287,7 @@ QUnit.test('Show. W/o params. Do not call template if skipTemplate in formatObje assert.equal(cloud._stored_settings.stroke, '#252525'); assert.equal(this.tooltip._text.css.callCount, 1, 'text styles'); - assert.deepEqual(this.tooltip._text.css.firstCall.args, [{ fill: '#939393' }]); + assert.deepEqual(this.tooltip._text.css.firstCall.args, [{ fill: 'rgba(147,147,147,0.7)' }]); assert.equal(this.tooltip._text.attr.callCount, 1, 'text attrs'); assert.deepEqual(this.tooltip._text.attr.firstCall.args, [{ text: 'some-text', 'class': 'tooltip_class' }]); @@ -1374,7 +1369,7 @@ QUnit.test('Show. W/ params', function(assert) { assert.deepEqual(this.tooltip._state, { color: '#ffffff', borderColor: '#252525', - textColor: '#939393', + textColor: 'rgba(147,147,147,0.7)', text: 'some-text', formatObject }, 'state'); @@ -1760,8 +1755,6 @@ QUnit.test('RB corner of page', function(assert) { }); QUnit.test('Orientation is not changed', function(assert) { - let wrapper; - this.options.customizeTooltip = null; this.tooltip.update(this.options); @@ -1773,7 +1766,7 @@ QUnit.test('Orientation is not changed', function(assert) { this.tooltip.move(500, 400, 10); // assert - wrapper = $('.test-title-class'); + const wrapper = $('.test-title-class'); assert.equal(this.tooltip._textGroupHtml.css.callCount, 0, 'textGroupHtml move'); assert.equal(this.renderer.path.callCount, 1); assert.deepEqual(this.getCloudPoints(), [480, 330, 520, 330, 520, 350, 530, 360, 520, 370, 520, 390, 480, 390]); @@ -1874,8 +1867,6 @@ QUnit.test('Show after move w/o orientation changing', function(assert) { // T277991, T447623 QUnit.test('Position when page\'s body has relative position and margins and page is scrolled. T277991, T447623', function(assert) { - let wrapper; - this.options.customizeTooltip = null; this.tooltip.update(this.options); @@ -1896,7 +1887,7 @@ QUnit.test('Position when page\'s body has relative position and margins and pag this.tooltip.move(500, 400, 30); // assert - wrapper = $('.test-title-class'); + const wrapper = $('.test-title-class'); assert.strictEqual(wrapper.css('left'), '340px'); assert.strictEqual(wrapper.css('top'), '181px'); }); diff --git a/testing/tests/DevExpress.viz.core/xyAxes.tests.js b/testing/tests/DevExpress.viz.core/xyAxes.tests.js index 4e9b00d0afea..4af9f2015e1d 100644 --- a/testing/tests/DevExpress.viz.core/xyAxes.tests.js +++ b/testing/tests/DevExpress.viz.core/xyAxes.tests.js @@ -128,7 +128,7 @@ const environment = { return axis; }, createSimpleAxis: function(options) { - options = $.extend(true, this.options, options); + options = $.extend(true, {}, this.options, options); let axis; this.range.categories = options.categories; @@ -4918,3 +4918,148 @@ QUnit.test('Update discrete translator business range after adjust axis', functi assert.strictEqual(minVisible, '120'); assert.strictEqual(maxVisible, '180'); }); + +QUnit.module('Custom positioning', { + beforeEach: function() { + environment.beforeEach.call(this); + this.generatedTicks = [10, 50, 90]; + this.options.type = 'continuous'; + this.options.visible = true; + this.options.label = { + visible: true, + indentFromAxis: 5 + }; + + translator2DModule.Translator2D.restore(); + sinon.spy(translator2DModule, 'Translator2D'); + }, + afterEach: function() { + environment.afterEach.call(this); + }, + createAxis: environment.createAxis, + createSimpleAxis: environment.createSimpleAxis, + drawOrthogonalAxes(horizontalAxisOptions, verticalAxisOptions) { + const horizontalAxis = this.createSimpleAxis($.extend(true, { isArgumentAxis: false, argumentType: 'numeric' }, horizontalAxisOptions)); + const verticalAxis = this.createSimpleAxis($.extend(true, { isArgumentAxis: false, isHorizontal: false, valueType: 'numeric' }, verticalAxisOptions)); + + horizontalAxis.getCustomPositionAxis = () => { return verticalAxis; }; + verticalAxis.getCustomPositionAxis = () => { return horizontalAxis; }; + + horizontalAxis.draw(this.canvas); + verticalAxis.draw(this.canvas); + horizontalAxis.draw(this.canvas); + + return { horizontalAxis, verticalAxis }; + } +}); + +QUnit.test('Set predefined axis position by \'position\' option', function(assert) { + const { horizontalAxis, verticalAxis } = this.drawOrthogonalAxes({ + position: 'top' + }, { + position: 'right' + }); + + assert.equal(horizontalAxis.getAxisPosition(), 10); + assert.equal(verticalAxis.getAxisPosition(), 910); + assert.notOk(horizontalAxis.customPositionIsAvailable()); + assert.notOk(verticalAxis.customPositionIsAvailable()); + assert.notOk(horizontalAxis.hasCustomPosition()); + assert.notOk(verticalAxis.hasCustomPosition()); + assert.equal(horizontalAxis.getResolvedPositionOption(), 'top'); + assert.equal(verticalAxis.getResolvedPositionOption(), 'right'); + + assert.equal(horizontalAxis.getLabelsPosition(), 5); + assert.equal(verticalAxis.getLabelsPosition(), 915); +}); + +QUnit.test('Set custom position', function(assert) { + const { horizontalAxis, verticalAxis } = this.drawOrthogonalAxes({ + customPosition: 50 + }, { + customPosition: 75 + }); + + assert.equal(horizontalAxis.getAxisPosition(), 305); + assert.equal(verticalAxis.getAxisPosition(), 688); + assert.ok(horizontalAxis.customPositionIsAvailable()); + assert.ok(verticalAxis.customPositionIsAvailable()); + assert.ok(horizontalAxis.hasCustomPosition()); + assert.ok(verticalAxis.hasCustomPosition()); + assert.equal(horizontalAxis.getResolvedPositionOption(), 50); + assert.equal(verticalAxis.getResolvedPositionOption(), 75); + assert.notOk(horizontalAxis.customPositionIsBoundary()); + assert.notOk(verticalAxis.customPositionIsBoundary()); + + assert.equal(horizontalAxis.getLabelsPosition(), 310); + assert.equal(verticalAxis.getLabelsPosition(), 683); + + assert.deepEqual(horizontalAxis._axisElement.attr.getCall(0).args[0], { points: [20, 305, 910, 305] }); + assert.deepEqual(verticalAxis._axisElement.attr.getCall(0).args[0], { points: [688, 600, 688, 10] }); + + assert.deepEqual(horizontalAxis._majorTicks[0].label.attr.getCall(5).args[0], { x: 109, y: 305 }); + assert.deepEqual(verticalAxis._majorTicks[0].label.attr.getCall(1).args[0], { x: 688, y: 541 }); +}); + +QUnit.test('Shift axis by offset option', function(assert) { + const { horizontalAxis, verticalAxis } = this.drawOrthogonalAxes({ + customPosition: 50, + offset: -150 + }, { + offset: 200 + }); + + assert.equal(horizontalAxis.getAxisPosition(), 155); + assert.equal(verticalAxis.getAxisPosition(), 220); + assert.ok(horizontalAxis.customPositionIsAvailable()); + assert.ok(verticalAxis.customPositionIsAvailable()); + assert.equal(horizontalAxis.getResolvedPositionOption(), 50); + assert.equal(verticalAxis.getResolvedPositionOption(), 'left'); + assert.notOk(horizontalAxis.customPositionIsBoundary()); + assert.notOk(verticalAxis.customPositionIsBoundary()); + + assert.equal(horizontalAxis.getLabelsPosition(), 160); + assert.equal(verticalAxis.getLabelsPosition(), 215); + + assert.deepEqual(horizontalAxis._axisElement.attr.getCall(0).args[0], { points: [20, 155, 910, 155] }); + assert.deepEqual(verticalAxis._axisElement.attr.getCall(0).args[0], { points: [220, 600, 220, 10] }); + + assert.deepEqual(horizontalAxis._majorTicks[0].label.attr.getCall(5).args[0], { x: 109, y: 155 }); + assert.deepEqual(verticalAxis._majorTicks[0].label.attr.getCall(1).args[0], { x: 220, y: 541 }); +}); + +QUnit.test('Set predefined axis position by \'customPosition\' option', function(assert) { + const { horizontalAxis, verticalAxis } = this.drawOrthogonalAxes({ + customPosition: 0 + }, { + customPosition: 200 + }); + + assert.equal(horizontalAxis.getAxisPosition(), 600); + assert.equal(verticalAxis.getAxisPosition(), 910); + assert.ok(horizontalAxis.customPositionIsBoundary()); + assert.ok(verticalAxis.customPositionIsBoundary()); + assert.equal(horizontalAxis.getResolvedBoundaryPosition(), 'bottom'); + assert.equal(verticalAxis.getResolvedBoundaryPosition(), 'right'); + assert.ok(horizontalAxis.customPositionEqualsToPredefined()); + assert.notOk(verticalAxis.customPositionEqualsToPredefined()); + + assert.deepEqual(horizontalAxis._axisElement.attr.getCall(0).args[0], { points: [20, 600, 910, 600] }); + assert.deepEqual(verticalAxis._axisElement.attr.getCall(0).args[0], { points: [910, 600, 910, 10] }); + + assert.deepEqual(horizontalAxis._majorTicks[0].label.attr.getCall(5).args[0], { x: 109, y: 600 }); + assert.deepEqual(verticalAxis._majorTicks[0].label.attr.getCall(1).args[0], { x: 910, y: 541 }); +}); + +QUnit.test('Validate \'customPosition\' option', function(assert) { + const { horizontalAxis } = this.drawOrthogonalAxes({ + customPosition: 'abcd' + }, {}); + + assert.equal(horizontalAxis.getAxisPosition(), 600); + assert.ok(horizontalAxis.customPositionIsAvailable()); + assert.notOk(horizontalAxis.hasCustomPosition()); + assert.ok(horizontalAxis.customPositionIsBoundary()); + assert.equal(horizontalAxis.getResolvedBoundaryPosition(), 'bottom'); + assert.ok(horizontalAxis.customPositionEqualsToPredefined()); +}); diff --git a/testing/tests/DevExpress.viz.rangeSelector/common.part3.tests.js b/testing/tests/DevExpress.viz.rangeSelector/common.part3.tests.js index 6753d4b50870..6196925f4a67 100644 --- a/testing/tests/DevExpress.viz.rangeSelector/common.part3.tests.js +++ b/testing/tests/DevExpress.viz.rangeSelector/common.part3.tests.js @@ -911,6 +911,41 @@ QUnit.test('Calculate tickInterval based on screen delta (datetime. minRange is assert.deepEqual(options.customTicks, [new Date(2015, 0, 15), new Date(2015, 1, 1)]); }); +QUnit.test('Scale starts from sunday by default (minRange is week)', function(assert) { + this.$container.width(1000); + this.createWidget({ + scale: { + type: 'semidiscrete', + minRange: 'week', + + startValue: new Date(2020, 2, 25), + endValue: new Date(2020, 3, 15) + } + }); + + const options = this.axis.updateOptions.lastCall.args[0]; + assert.strictEqual(options.tickInterval, 'week'); + assert.deepEqual(options.customTicks, [new Date(2020, 2, 22), new Date(2020, 2, 29), new Date(2020, 3, 5), new Date(2020, 3, 12)]); +}); + +QUnit.test('Scale starts from first day of defined scale.workWeek (minRange is week)', function(assert) { + this.$container.width(1000); + this.createWidget({ + scale: { + type: 'semidiscrete', + minRange: 'week', + workWeek: [2, 3, 4, 5, 6], + + startValue: new Date(2020, 2, 25), + endValue: new Date(2020, 3, 15) + } + }); + + const options = this.axis.updateOptions.lastCall.args[0]; + assert.strictEqual(options.tickInterval, 'week'); + assert.deepEqual(options.customTicks, [new Date(2020, 2, 24), new Date(2020, 2, 31), new Date(2020, 3, 7), new Date(2020, 3, 14)]); +}); + QUnit.test('If not set - scale label format depends on tickInterval', function(assert) { this.createWidget({ dataSource: [{ x: new Date(2016, 0, 1) }, { x: new Date(2016, 1, 1) }, { x: new Date(2016, 2, 1) }, { x: new Date(2016, 4, 10) }], diff --git a/testing/tests/DevExpress.viz.sparklines/bullet.tests.js b/testing/tests/DevExpress.viz.sparklines/bullet.tests.js index 5c5eb238663a..2b972da018d4 100644 --- a/testing/tests/DevExpress.viz.sparklines/bullet.tests.js +++ b/testing/tests/DevExpress.viz.sparklines/bullet.tests.js @@ -121,6 +121,28 @@ QUnit.test('Create canvas when container size is defined', function(assert) { QUnit.module('Range', environment); +QUnit.test('startScaleValue > endScaleValue. Arg translator must be inverted', function(assert) { + this.createBullet({ + startScaleValue: 10, + endScaleValue: 1 + }); + + const argTranslator = translator2DModule.Translator2D.getCall(0).returnValue; + + assert.strictEqual(argTranslator.update.lastCall.args[0].invert, true, 'Arg translator inverted'); +}); + +QUnit.test('startScaleValue > endScaleValue. Arg translator must not be inverted', function(assert) { + this.createBullet({ + startScaleValue: 1, + endScaleValue: 10 + }); + + const argTranslator = translator2DModule.Translator2D.getCall(0).returnValue; + + assert.strictEqual(argTranslator.update.lastCall.args[0].invert, undefined, 'Arg translator inverted'); +}); + QUnit.test('Create range when all value options are defined', function(assert) { this.createBullet({ value: 10, target: 20, startScaleValue: 0, endScaleValue: 30 }); @@ -490,7 +512,7 @@ QUnit.test('Groups structure when target < 0 and startScaleValue is not defined, assert.equal(renderer.stub('path').getCall(2).returnValue.stub('append').lastCall.args[0], renderer.root); }); -QUnit.test('Groups structure when target < 0 and startScaleValue is not defined, value < 0. target < value', function(assert) { +QUnit.test('Groups structure when target < 0 and startScaleValue is not defined, value < 0. target > value', function(assert) { this.createBullet({ target: -10, endScaleValue: 20, value: -15 }); const renderer = this.renderer; @@ -1067,73 +1089,6 @@ QUnit.test('Change size if size = 0,10 - B239871', function(assert) { assert.ok(redrawFunctionCalled, 'Redraw function was not called'); }); -// QUnit.test('Rendering in container with container size 0, 0', function (assert) { -// var container = $('#containerForResize'), -// sparkCont = $('
').appendTo(container), -// options = { -// value: 10, -// startScaleValue: 0, -// endScaleValue: 10, -// target: 8 -// }, -// renderCalled = 0; -// -// dxBullet.prototype._drawWidgetElements = function () { -// renderCalled++; -// }; -// -// sparkCont.dxBullet(options); -// sparkCont.width(200); -// sparkCont.height(400); -// sparkCont.dxBullet('instance').render(); -// -// assert.equal(renderCalled, 2, 'Render called 2 time'); -// }); -// -// QUnit.test('Rendering in container with size 0, 0', function (assert) { -// var container = $('#containerForResize'), -// sparkCont = $('
').appendTo(container), -// options = { -// value: 10, -// startScaleValue: 0, -// endScaleValue: 10, -// target: 8, -// size: { -// width: 0, -// height: 0 -// } -// }, -// renderCalled = 0; -// -// dxBullet.prototype._drawWidgetElements = function () { -// renderCalled++; -// }; -// -// sparkCont.dxBullet(options); -// sparkCont.dxBullet('instance').option('size', { width: 200, height: 30 }); -// -// assert.equal(renderCalled, 1, 'Render called 1 time'); -// }); -// -// QUnit.test('Rendering in container with big margin', function (assert) { -// var container = $('#containerForResize'), -// sparkCont = $('
').appendTo(container), -// options = { -// }, -// renderCalled = 0; -// -// dxBullet.prototype._drawWidgetElements = function () { -// renderCalled++; -// }; -// -// sparkCont.dxBullet(options); -// sparkCont.width(200); -// sparkCont.height(400); -// sparkCont.dxBullet('instance').render(); -// -// assert.equal(renderCalled, 1, 'Render called 2 time'); -// }); - QUnit.module('drawn', { beforeEach: function() { environment.beforeEach.call(this); @@ -1161,3 +1116,29 @@ QUnit.test('drawn is called after resize', function(assert) { assert.strictEqual(BaseWidget.prototype._drawn.calledTwice, true); }); + +QUnit.module('RTL support', environment); + +QUnit.test('rtlEnabled = true. EndScaleValue > startScaleValue', function(assert) { + this.createBullet({ + startScaleValue: 1, + endScaleValue: 10, + rtlEnabled: true + }); + + const argTranslator = translator2DModule.Translator2D.getCall(0).returnValue; + + assert.strictEqual(argTranslator.update.lastCall.args[0].invert, true, 'Arg translator is inverted'); +}); + +QUnit.test('rtlEnabled = true. EndScaleValue < startScaleValue', function(assert) { + this.createBullet({ + startScaleValue: 10, + endScaleValue: 1, + rtlEnabled: true + }); + + const argTranslator = translator2DModule.Translator2D.getCall(0).returnValue; + + assert.strictEqual(argTranslator.update.lastCall.args[0].invert, false, 'Arg translator is inverted'); +}); diff --git a/testing/tests/DevExpress.viz.treeMap/treeMap.base.tests.js b/testing/tests/DevExpress.viz.treeMap/treeMap.base.tests.js index 9db755eb544b..0e9710e4452e 100644 --- a/testing/tests/DevExpress.viz.treeMap/treeMap.base.tests.js +++ b/testing/tests/DevExpress.viz.treeMap/treeMap.base.tests.js @@ -1100,7 +1100,7 @@ QUnit.test('simple labels creation', function(assert) { assert.strictEqual(text.css.lastCall.args[0].fill, 'someColor'); assert.strictEqual(text.css.lastCall.args[0]['font-size'], 'someSize'); assert.deepEqual(text.attr.firstCall.args[0], { - filter: 'shadowFilter.id', stroke: '#000000', 'stroke-width': 1, 'stroke-opacity': 0.3 + filter: 'shadowFilter.id' }); }); }); diff --git a/testing/tests/Renovation/button.markup.tests.js b/testing/tests/Renovation/button.markup.tests.js index 86bbf555dc50..0c2e240c721c 100644 --- a/testing/tests/Renovation/button.markup.tests.js +++ b/testing/tests/Renovation/button.markup.tests.js @@ -2,7 +2,7 @@ import $ from 'jquery'; import { isRenderer } from 'core/utils/type'; import config from 'core/config'; -import 'renovation/dist/button.j'; +import 'renovation/button.j'; import 'common.css!'; QUnit.testStart(function() { @@ -102,8 +102,7 @@ QUnit.module('Button markup', () => { assert.ok(element.hasClass(BUTTON_BACK_CLASS), 'button has correct type class after change type'); }); - // TODO - QUnit.skip('class is not removed after change type', function(assert) { + QUnit.test('class is not removed after change type', function(assert) { const $element = $('#button').Button({}); $element.addClass('test'); @@ -165,15 +164,13 @@ QUnit.module('Button markup', () => { assert.ok($button.find('.' + BUTTON_CONTENT_CLASS).hasClass(TEMPLATE_WRAPPER_CLASS), 'template has content class'); }); - // TODO - QUnit.skip('Button with anonymous template', function(assert) { + QUnit.test('Button with anonymous template', function(assert) { const $button = $('#buttonWithAnonymousTemplate').Button(); assert.equal($.trim($button.text()), 'test', 'anonymous template rendered'); }); - // TODO - QUnit.skip('anonymous content template rendering', function(assert) { + QUnit.test('anonymous content template rendering', function(assert) { const $contentElement = $('#buttonWithAnonymousTemplate #content'); const $button = $('#buttonWithAnonymousTemplate').Button(); diff --git a/testing/tests/Renovation/button.tests.js b/testing/tests/Renovation/button.tests.js index 1cac79f6297e..99cf1527e272 100644 --- a/testing/tests/Renovation/button.tests.js +++ b/testing/tests/Renovation/button.tests.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import 'renovation/dist/button.j'; +import 'renovation/button.j'; import { isRenderer } from 'core/utils/type'; import config from 'core/config'; import ValidationEngine from 'ui/validation_engine'; @@ -60,7 +60,7 @@ QUnit.test('should render button with default template', function(assert) { $element.Button({ text: 'test', icon: 'check' }); const $contentElements = $element.find('.dx-button-content').children(); - assert.strictEqual($element.Button('instance').option('template'), '', 'default template value'); + assert.strictEqual($element.Button('instance').option('template'), undefined, 'default template value'); assert.ok($contentElements.eq(0).hasClass('dx-icon'), 'render icon'); assert.ok($contentElements.eq(1).hasClass('dx-button-text'), 'render test'); }); @@ -347,22 +347,23 @@ QUnit.module('contentReady', {}, () => { }); QUnit.module('inkRipple', {}, () => { - // NOTE: deprecated behavior - QUnit.skip('inkRipple should be removed when widget is removed', function(assert) { - $('#inkButton').Button({ + QUnit.test('inkRipple should be removed when widget is removed', function(assert) { + const $element = $('#inkButton'); + + $element.Button({ useInkRipple: true, - onClick(e) { - const $element = $(e.component.$element()); - $element.triggerHandler({ type: 'dxremove' }); - $element.trigger('dxinactive'); - assert.ok(true, 'no exceptions'); - } }); - $('#inkButton').trigger('dxclick'); + $element.Button('instance').option('onClick', (e) => { + const $element = $(e.component.$element()); + $element.triggerHandler({ type: 'dxremove' }); + $element.trigger('dxinactive'); + assert.ok(true, 'no exceptions'); + }); + + $element.trigger('dxclick'); }); - // NOTE: deprecated behavior - QUnit.skip('widget should works correctly when the useInkRipple option is changed at runtime', function(assert) { + QUnit.test('widget should works correctly when the useInkRipple option is changed at runtime', function(assert) { const clock = sinon.useFakeTimers(); const $inkButton = $('#inkButton').Button({ text: 'test', @@ -414,8 +415,7 @@ QUnit.module('widget sizing render', {}, () => { const instance = $element.Button('instance'); assert.strictEqual(instance.option('width'), undefined); - // TODO - // assert.strictEqual($element.outerWidth(), 300, 'outer width of the element must be equal to custom width'); + assert.strictEqual($element.outerWidth(), 300, 'outer width of the element must be equal to custom width'); }); QUnit.test('change width', function(assert) { diff --git a/testing/tests/Renovation/widget.tests.js b/testing/tests/Renovation/widget.tests.js index 0213e01bfce3..51809b81d163 100644 --- a/testing/tests/Renovation/widget.tests.js +++ b/testing/tests/Renovation/widget.tests.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import 'renovation/dist/widget.j'; +import 'renovation/widget.j'; QUnit.testStart(function() { $('#qunit-fixture').html(` @@ -117,3 +117,11 @@ QUnit.test('should not recreate container element after rerender', function(asse assert.strictEqual(widget.$element().get(0), container); }); + +QUnit.module('Preact Wrapper', config); + +QUnit.test('should create in separate element', function(assert) { + $('
').Widget({}); + + assert.ok(true, 'no exceptions'); +}); diff --git a/themebuilder/package.json b/themebuilder/package.json index 4b63ec973d21..d45b99dfe1bb 100644 --- a/themebuilder/package.json +++ b/themebuilder/package.json @@ -23,10 +23,10 @@ }, "devDependencies": { "chai": "^4.1.2", + "dart-sass": "^1.25.0", "express": "^4.16.4", "less": "3.9.0", "mocha": "^5.2.0", - "mock-require": "^3.0.2", - "node-sass": "^4.9.3" + "mock-require": "^3.0.2" } } diff --git a/themebuilder/tests/less-template-loader-spec.js b/themebuilder/tests/less-template-loader-spec.js index 2782107bd04f..16076607cea2 100644 --- a/themebuilder/tests/less-template-loader-spec.js +++ b/themebuilder/tests/less-template-loader-spec.js @@ -24,7 +24,7 @@ const emptyHeader = () => { return ''; }; const scssCompiler = { render: (scss) => { return new Promise((resolve, reject) => { - require('node-sass').render({ + require('dart-sass').render({ data: scss }, (error, result) => { if(error) { @@ -303,20 +303,24 @@ describe('LessTemplateLoader', () => { }).then(data => { assert.equal(data.css, `div { - color: #fff; } + color: #fff; +} .dx-theme-accent-as-text-color { - color: #fff; } + color: #fff; +} .dx-theme-generic-typography { - color: #0f0; } - .dx-theme-generic-typography .dx-theme-accent-as-text-color { - color: #fff; } + color: #0f0; +} +.dx-theme-generic-typography .dx-theme-accent-as-text-color { + color: #fff; +} #devexpress-metadata-compiler { base-bg: #fff; - base-text-color: #0f0; } -`); + base-text-color: #0f0; +}`); }); }); diff --git a/ts/dx.all.d.ts b/ts/dx.all.d.ts index 905a181c2621..2d405547538a 100644 --- a/ts/dx.all.d.ts +++ b/ts/dx.all.d.ts @@ -1702,6 +1702,8 @@ declare module DevExpress.fileManagement { dataItem: any; /** @name FileSystemItem.dateModified */ dateModified: Date; + /** @name FileSystemItem.hasSubDirectories */ + hasSubDirectories: boolean; /** @name FileSystemItem.isDirectory */ isDirectory: boolean; /** @name FileSystemItem.key */ @@ -3239,7 +3241,7 @@ declare module DevExpress.ui { /** @name dxDiagram.Options.mainToolbar */ mainToolbar?: { commands?: Array | Array<'separator' | 'exportSvg' | 'exportPng' | 'exportJpg' | 'undo' | 'redo' | 'cut' | 'copy' | 'paste' | 'selectAll' | 'delete' | 'fontName' | 'fontSize' | 'bold' | 'italic' | 'underline' | 'fontColor' | 'lineColor' | 'fillColor' | 'textAlignLeft' | 'textAlignCenter' | 'textAlignRight' | 'lock' | 'unlock' | 'sendToBack' | 'bringToFront' | 'insertShapeImage' | 'editShapeImage' | 'deleteShapeImage' | 'connectorLineType' | 'connectorLineStart' | 'connectorLineEnd' | 'layoutTreeTopToBottom' | 'layoutTreeBottomToTop' | 'layoutTreeLeftToRight' | 'layoutTreeRightToLeft' | 'layoutLayeredTopToBottom' | 'layoutLayeredBottomToTop' | 'layoutLayeredLeftToRight' | 'layoutLayeredRightToLeft' | 'fullScreen' | 'zoomLevel' | 'showGrid' | 'snapToGrid' | 'gridSize' | 'units' | 'pageSize' | 'pageOrientation' | 'pageColor'>, visible?: boolean }; /** @name dxDiagram.Options.nodes */ - nodes?: { autoLayout?: 'auto' | 'off' | 'tree' | 'layered' | { orientation?: 'auto' | 'vertical' | 'horizontal', type?: 'auto' | 'off' | 'tree' | 'layered' }, childrenExpr?: string | ((data: any) => any), containerKeyExpr?: string | ((data: any) => any), dataSource?: Array | DevExpress.data.DataSource | DevExpress.data.DataSourceOptions, heightExpr?: string | ((data: any) => any), imageUrlExpr?: string | ((data: any) => any), itemsExpr?: string | ((data: any) => any), keyExpr?: string | ((data: any) => any), leftExpr?: string | ((data: any) => any), lockedExpr?: string | ((data: any) => any), parentKeyExpr?: string | ((data: any) => any), styleExpr?: string | ((data: any) => any), textExpr?: string | ((data: any) => any), textStyleExpr?: string | ((data: any) => any), topExpr?: string | ((data: any) => any), typeExpr?: string | ((data: any) => any), widthExpr?: string | ((data: any) => any), zIndexExpr?: string | ((data: any) => any) }; + nodes?: { autoLayout?: 'auto' | 'off' | 'tree' | 'layered' | { orientation?: 'auto' | 'vertical' | 'horizontal', type?: 'auto' | 'off' | 'tree' | 'layered' }, containerChildrenExpr?: string | ((data: any) => any), containerKeyExpr?: string | ((data: any) => any), dataSource?: Array | DevExpress.data.DataSource | DevExpress.data.DataSourceOptions, heightExpr?: string | ((data: any) => any), imageUrlExpr?: string | ((data: any) => any), itemsExpr?: string | ((data: any) => any), keyExpr?: string | ((data: any) => any), leftExpr?: string | ((data: any) => any), lockedExpr?: string | ((data: any) => any), parentKeyExpr?: string | ((data: any) => any), styleExpr?: string | ((data: any) => any), textExpr?: string | ((data: any) => any), textStyleExpr?: string | ((data: any) => any), topExpr?: string | ((data: any) => any), typeExpr?: string | ((data: any) => any), widthExpr?: string | ((data: any) => any), zIndexExpr?: string | ((data: any) => any) }; /** @name dxDiagram.Options.onCustomCommand */ onCustomCommand?: ((e: { component?: dxDiagram, element?: DevExpress.core.dxElement, name?: string }) => any); /** @name dxDiagram.Options.onItemClick */ @@ -3606,7 +3608,7 @@ declare module DevExpress.ui { /** @name dxFileManager.Options.currentPathKeys */ currentPathKeys?: Array; /** @name dxFileManager.Options.customizeDetailColumns */ - customizeDetailColumns?: ((columns: Array) => Array); + customizeDetailColumns?: ((columns: Array) => Array); /** @name dxFileManager.Options.customizeThumbnail */ customizeThumbnail?: ((fileSystemItem: DevExpress.fileManagement.FileSystemItem) => string); /** @name dxFileManager.Options.fileSystemProvider */ @@ -4108,7 +4110,7 @@ declare module DevExpress.ui { /** @name dxGantt.Options.dependencies */ dependencies?: { dataSource?: Array | DevExpress.data.DataSource | DevExpress.data.DataSourceOptions, keyExpr?: string | Function, predecessorIdExpr?: string | Function, successorIdExpr?: string | Function, typeExpr?: string | Function }; /** @name dxGantt.Options.editing */ - editing?: { allowDependencyAdding?: boolean, allowDependencyDeleting?: boolean, allowDependencyUpdating?: boolean, allowResourceAdding?: boolean, allowResourceDeleting?: boolean, allowResourceUpdating?: boolean, allowTaskAdding?: boolean, allowTaskDeleting?: boolean, allowTaskUpdating?: boolean, enabled?: boolean }; + editing?: { allowDependencyAdding?: boolean, allowDependencyDeleting?: boolean, allowResourceAdding?: boolean, allowResourceDeleting?: boolean, allowResourceUpdating?: boolean, allowTaskAdding?: boolean, allowTaskDeleting?: boolean, allowTaskUpdating?: boolean, enabled?: boolean }; /** @name dxGantt.Options.onSelectionChanged */ onSelectionChanged?: ((e: { component?: dxGantt, element?: DevExpress.core.dxElement, model?: any, selectedRowKey?: any }) => any); /** @name dxGantt.Options.resourceAssignments */ @@ -4134,7 +4136,7 @@ declare module DevExpress.ui { /** @name dxGantt.Options.toolbar */ toolbar?: dxGanttToolbar; /** @name dxGantt.Options.validation */ - validation?: { autoUpdateParentTasks?: boolean, enableDependencyValidation?: boolean }; + validation?: { autoUpdateParentTasks?: boolean, validateDependencies?: boolean }; } /** @name dxGantt */ export class dxGantt extends Widget { @@ -4159,10 +4161,10 @@ declare module DevExpress.ui { } /** @name dxGanttToolbarItem */ export interface dxGanttToolbarItem extends dxToolbarItem { - /** @name dxGanttToolbarItem.formatName */ - formatName?: 'separator' | 'undo' | 'redo' | 'zoomIn' | 'zoomOut' | string; /** @name dxGanttToolbarItem.location */ location?: 'after' | 'before' | 'center'; + /** @name dxGanttToolbarItem.name */ + name?: 'separator' | 'undo' | 'redo' | 'zoomIn' | 'zoomOut' | string; } /** @name dxHtmlEditor.Options */ export interface dxHtmlEditorOptions extends EditorOptions { @@ -4265,14 +4267,14 @@ declare module DevExpress.ui { /** @name dxHtmlEditorToolbar.container */ container?: string | Element | JQuery; /** @name dxHtmlEditorToolbar.items */ - items?: Array; + items?: Array; /** @name dxHtmlEditorToolbar.multiline */ multiline?: boolean; } /** @name dxHtmlEditorToolbarItem */ export interface dxHtmlEditorToolbarItem extends dxToolbarItem { /** @name dxHtmlEditorToolbarItem.formatName */ - formatName?: 'background' | 'bold' | 'color' | 'italic' | 'link' | 'image' | 'strike' | 'subscript' | 'superscript' | 'underline' | 'blockquote' | 'header' | 'increaseIndent' | 'decreaseIndent' | 'orderedList' | 'bulletList' | 'alignLeft' | 'alignCenter' | 'alignRight' | 'alignJustify' | 'codeBlock' | 'variable' | 'separator' | 'undo' | 'redo' | 'clear' | string; + formatName?: 'background' | 'bold' | 'color' | 'font' | 'italic' | 'link' | 'image' | 'size' | 'strike' | 'subscript' | 'superscript' | 'underline' | 'blockquote' | 'header' | 'increaseIndent' | 'decreaseIndent' | 'orderedList' | 'bulletList' | 'alignLeft' | 'alignCenter' | 'alignRight' | 'alignJustify' | 'codeBlock' | 'variable' | 'separator' | 'undo' | 'redo' | 'clear' | string; /** @name dxHtmlEditorToolbarItem.formatValues */ formatValues?: Array; /** @name dxHtmlEditorToolbarItem.location */ diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 5783ee56f6bf..000000000000 --- a/tslint.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "extends": [ - "tslint:latest", - "tslint-react", - "tslint-eslint-rules", - "tslint-config-airbnb" - ], - "linterOptions": { - "exclude": [ - "artifacts", - "**/*.js" - ] - }, - "rules": { - "filenames/match-regex": [2, "^[a-z-\\.]+$"], - "align": [true, "members", "statements"], - "interface-over-type-literal": false, - "interface-name": false, - "import-spacing": true, - "semicolon": true, - "max-line-length": [true, 100], - "object-literal-sort-keys": true, - "ordered-imports": false, - "jsx-no-lambda": false, - "jsx-boolean-value": false, - "jsx-no-multiline-js": false, - "member-access": [true, "no-public"], - "variable-name": [true, "allow-pascal-case", "allow-leading-underscore", "require-const-for-all-caps"], - "no-empty": [true, "allow-empty-functions"], - "no-implicit-dependencies": false, - "object-shorthand-properties-first": false, - "prefer-array-literal": [true, { "allow-type-parameters": true }], - "array-type": false, - "member-ordering": [ - true, { - "order": [ - "public-static-field", - "public-instance-field", - "public-constructor", - "private-static-field", - "private-instance-field", - "private-constructor", - "public-instance-method", - "protected-instance-method", - "private-instance-method" - ], - "alphabetize": true - }], - "jsx-wrap-multiline": false, - "no-this-assignment": [true, {"allow-destructuring": true}], - "ter-indent": [true, 4, { "SwitchCase": 1 }], - "import-name": [false], - "no-unused-expression": [true, "allow-fast-null-checks"], - "no-object-literal-type-assertion": [false], - "no-submodule-imports": false - }, - "jsRules": { - "object-literal-sort-keys": false - } -}