From c97eaeac54cdca8109bb18563402b36f5afbc331 Mon Sep 17 00:00:00 2001 From: Albert Backenhof Date: Thu, 20 Jun 2019 14:31:50 +0200 Subject: [PATCH] Build with webpack and force style with eslint -Also removed tests and some cleanup. Issue: QLIK-95906 --- .babelrc | 10 + .editorconfig | 10 + .eslintrc.js | 62 + {src => assets}/preview.png | Bin gulpfile.js | 136 +- package.json | 70 +- src/calendar-settings.js | 562 +++--- src/lib/daterangepicker.js | 2971 ++++++++++++++-------------- src/lib/moment.min.js | 1 + src/qlik-date-picker.js | 442 +++-- stylelint.config.js | 35 + test/aw.config.js | 4 - test/index.html | 11 - test/requirejs-config.js | 21 - test/requirejs-main.js | 10 - test/stubs/qlik.stub.js | 24 - test/unit/qlik-date-picker.spec.js | 89 - webpack.config.js | 71 + 18 files changed, 2284 insertions(+), 2245 deletions(-) create mode 100644 .babelrc create mode 100644 .editorconfig create mode 100644 .eslintrc.js rename {src => assets}/preview.png (100%) create mode 100644 stylelint.config.js delete mode 100644 test/aw.config.js delete mode 100644 test/index.html delete mode 100644 test/requirejs-config.js delete mode 100644 test/requirejs-main.js delete mode 100644 test/stubs/qlik.stub.js delete mode 100644 test/unit/qlik-date-picker.spec.js create mode 100644 webpack.config.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..6d9553f --- /dev/null +++ b/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + ["@babel/preset-env", { + "targets": { + "ie": "11" + }, + "useBuiltIns": "entry" + }] + ] +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0f09989 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..b2696f4 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,62 @@ +module.exports = { + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + modules: true + }, + sourceType: "module" + }, + parser: "babel-eslint", + env: { + browser: true, + es6: true, + node: true + }, + globals: { + angular: false, + define: false, + describe: false, + document: false, + expect: false, + it: false, + require: false + }, + rules: { + "indent": ["error", 2, { "SwitchCase": 1 }], + "object-curly-spacing": ["error", "always"], + "max-len": ["warn", { "code": 120, "ignoreComments": true, "ignoreTrailingComments": false }], + "no-console": ["warn"], + "no-mixed-operators": ["warn", { + "groups": [ + ["==", "!=", "===", "!==", ">", ">=", "<", "<="], + ["&&", "||"], + ["in", "instanceof"] + ], + "allowSamePrecedence": true + }], + "no-multi-spaces": ["error"], + "no-cond-assign": ["warn"], + "no-fallthrough": ["warn"], + "no-undef": ["warn"], + "no-unused-vars": ["warn", { "args": "none" }], + "no-use-before-define": ["warn", { "functions": false, "classes": false, "variables": false }], + "no-useless-escape": ["warn"], + "no-useless-return": ["warn"], + "no-underscore-dangle": ["warn", { "allow": ["_id"] }], + "no-redeclare": ["warn"], + "no-restricted-syntax": ["warn"], + "operator-linebreak": ["warn", "before"], + "prefer-promise-reject-errors": ["warn"], + "padded-blocks": ["warn", { "blocks": "never", "switches": "never", "classes": "never" }], + "semi": ["error", "always"], + "valid-typeof": ["warn"], + "no-eval": ["error"], + "no-implied-eval": ["error"], + "no-debugger": ["warn"], + "no-mixed-spaces-and-tabs": ["warn"], + }, + extends: [ + "eslint:recommended" + ] +} diff --git a/src/preview.png b/assets/preview.png similarity index 100% rename from src/preview.png rename to assets/preview.png diff --git a/gulpfile.js b/gulpfile.js index a52a431..ef96259 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,105 +1,89 @@ /* global require process Buffer */ var gulp = require('gulp'); -var cssnano = require('gulp-cssnano'); var gutil = require('gulp-util'); -var uglify = require('gulp-uglify'); -var saveLicense = require('uglify-save-license'); var pkg = require('./package.json'); var zip = require('gulp-zip'); +var del = require('del'); +var webpackConfig = require('./webpack.config'); +var webpack = require('webpack'); var DIST = './dist', - SRC = './src', - NAME = pkg.name, - VERSION = process.env.VERSION || 'local-dev'; + NAME = pkg.name, + VERSION = process.env.VERSION || 'local-dev'; gulp.task('qext', function () { - var qext = { - name: 'Date picker', - type: 'visualization', - description: pkg.description + '\nVersion: ' + VERSION, - version: VERSION, - icon: 'calendar', - preview: 'preview.png', - keywords: 'qlik-sense, visualization', - author: pkg.author, - homepage: pkg.homepage, - license: pkg.license, - repository: pkg.repository, - dependencies: { - 'qlik-sense': '>=5.5.x' - } - }; - if (pkg.contributors) { - qext.contributors = pkg.contributors; - } - var src = require('stream').Readable({ - objectMode: true - }); - src._read = function () { - this.push(new gutil.File({ - cwd: '', - base: '', - path: NAME + '.qext', - contents: Buffer.from(JSON.stringify(qext, null, 4)) - })); - this.push(null); - }; - return src.pipe(gulp.dest(DIST)); + var qext = { + name: 'Date picker', + type: 'visualization', + description: pkg.description + '\nVersion: ' + VERSION, + version: VERSION, + icon: 'calendar', + preview: 'preview.png', + keywords: 'qlik-sense, visualization', + author: pkg.author, + homepage: pkg.homepage, + license: pkg.license, + repository: pkg.repository, + dependencies: { + 'qlik-sense': '>=5.5.x' + } + }; + if (pkg.contributors) { + qext.contributors = pkg.contributors; + } + var src = require('stream').Readable({ + objectMode: true + }); + src._read = function () { + this.push(new gutil.File({ + cwd: '', + base: '', + path: NAME + '.qext', + contents: Buffer.from(JSON.stringify(qext, null, 4)) + })); + this.push(null); + }; + return src.pipe(gulp.dest(DIST)); }); -gulp.task('clean', function (ready) { - var del = require('del'); - del.sync([DIST]); - ready(); +gulp.task('clean', function () { + return del([DIST], { force: true }); }); -gulp.task('less', function () { - var less = require('gulp-less'); - var LessPluginAutoPrefix = require('less-plugin-autoprefix'); - var autoprefix = new LessPluginAutoPrefix({ - browsers: ['last 2 versions'] - }); - return gulp.src(SRC + '/**/*.less') - .pipe(less({ - plugins: [autoprefix] - })) - .pipe(cssnano()) - .pipe(gulp.dest(DIST)); +gulp.task('zip-build', function () { + return gulp.src(DIST + '/**/*') + .pipe(zip(`${NAME}_${VERSION}.zip`)) + .pipe(gulp.dest(DIST)); }); gulp.task('add-assets', function () { - return gulp.src([ - SRC + '/**/*.png', - SRC + '/**/*.txt' - ]) - .pipe(gulp.dest(DIST)); + return gulp.src('./assets/**/*').pipe(gulp.dest(DIST)); }); -gulp.task('add-src', function () { - return gulp.src(SRC + '/**/*.js') - .pipe(uglify({ - mangle: false, - output: { - comments: saveLicense - } - })) - .pipe(gulp.dest(DIST)); -}); +gulp.task('webpack-build', done => { + webpack(webpackConfig, (error, statistics) => { + const compilationErrors = statistics && statistics.compilation.errors; + const hasCompilationErrors = !statistics || (compilationErrors && compilationErrors.length > 0); -gulp.task('zip-build', function () { - return gulp.src(DIST + '/**/*') - .pipe(zip(`${NAME}_${VERSION}.zip`)) - .pipe(gulp.dest(DIST)); + console.log(statistics && statistics.toString({ chunks: false, colors: true })); // eslint-disable-line no-console + + if (error || hasCompilationErrors) { + console.log('Build has errors or eslint errors, fail it'); // eslint-disable-line no-console + process.exit(1); + } + + done(); + }); }); gulp.task('build', - gulp.series('clean', 'qext', 'less', 'add-assets', 'add-src') + gulp.series('clean', 'webpack-build', 'qext', 'add-assets') ); gulp.task('zip', - gulp.series('build', 'zip-build') + gulp.series('build', 'zip-build') ); gulp.task('default', - gulp.series('build') + gulp.series('build') ); diff --git a/package.json b/package.json index d53c707..790e413 100644 --- a/package.json +++ b/package.json @@ -1,38 +1,36 @@ { - "name": "qlik-date-picker", - "version": "0.0.1", - "description": "A calendar object that allows a user to make selections in a date field.", - "private": true, - "homepage": "http://branch.qlik.com/#/project/5697a878dcc497f80ed514bf", - "repository": "https://github.com/qlik-oss/SenseDateRangePicker/tree/qlik-date-picker", - "author": "Nodier Torres", - "license": "MIT", - "scripts": { - "build": "gulp build", - "build:zip": "gulp zip", - "test:unit": "aw chrome -c ./test/aw.config.js" - }, - "devDependencies": { - "after-work.js": "4.3.2", - "angular": "1.7.2", - "babel": "^6.23.0", - "babel-cli": "^6.26.0", - "babel-core": "^6.26.3", - "babel-plugin-istanbul": "^4.1.6", - "del": "^2.0.2", - "gulp": "4.0.0", - "gulp-cssnano": "^2.1.0", - "gulp-dest": "^0.2.3", - "gulp-ignore": "^2.0.1", - "gulp-less": "^3.0.3", - "gulp-replace": "^0.5.4", - "gulp-uglify": "^3.0.1", - "gulp-util": "^3.0.7", - "gulp-zip": "^3.0.2", - "jquery": "^3.3.1", - "less-plugin-autoprefix": "^1.5.1", - "require-css": "^0.1.10", - "requirejs": "^2.3.6", - "uglify-save-license": "^0.4.1" - } + "name": "qlik-date-picker", + "version": "0.0.1", + "description": "A calendar object that allows a user to make selections in a date field.", + "private": true, + "homepage": "http://branch.qlik.com/#/project/5697a878dcc497f80ed514bf", + "repository": "https://github.com/qlik-oss/SenseDateRangePicker/tree/qlik-date-picker", + "author": "Nodier Torres", + "license": "MIT", + "scripts": { + "build": "gulp build", + "build:zip": "gulp zip", + "eslint": "eslint src" + }, + "devDependencies": { + "@babel/core": "7.1.2", + "@babel/polyfill": "7.0.0", + "@babel/preset-env": "7.1.0", + "babel-eslint": "10.0.1", + "babel-loader": "8.0.4", + "css-loader": "1.0.1", + "del": "2.0.2", + "eslint": "5.7.0", + "eslint-loader": "2.1.1", + "gulp": "4.0.0", + "gulp-util": "^3.0.7", + "gulp-zip": "3.0.2", + "jquery": "^3.3.1", + "less": "3.8.1", + "less-loader": "4.1.0", + "style-loader": "0.23.1", + "stylelint": "8.4.0", + "stylelint-webpack-plugin": "0.10.5", + "webpack": "4.20.2" + } } diff --git a/src/calendar-settings.js b/src/calendar-settings.js index 6cebae7..93b8049 100644 --- a/src/calendar-settings.js +++ b/src/calendar-settings.js @@ -1,295 +1,295 @@ define(["qlik"], function (qlik) { - var fieldList, fieldListPromise; + var fieldList, fieldListPromise; - function getPromise() { - if (!fieldListPromise) { - fieldListPromise = qlik.currApp().createGenericObject({ - qFieldListDef: { - qType: 'variable' - } - }).then(function (reply) { - fieldList = reply.layout.qFieldList.qItems.filter(function (item) { - return item.qTags && item.qTags.indexOf('$date') > -1; - }).map(function (item) { - return { - value: item.qName, - label: item.qName - }; - }); - return fieldList; - }); + function getPromise() { + if (!fieldListPromise) { + fieldListPromise = qlik.currApp().createGenericObject({ + qFieldListDef: { + qType: 'variable' } - return fieldListPromise; + }).then(function (reply) { + fieldList = reply.layout.qFieldList.qItems.filter(function (item) { + return item.qTags && item.qTags.indexOf('$date') > -1; + }).map(function (item) { + return { + value: item.qName, + label: item.qName + }; + }); + return fieldList; + }); } + return fieldListPromise; + } - var dimension = { + var dimension = { + type: "items", + label: "Field", + ref: "qListObjectDef", + min: 1, + max: 1, + items: { + field: { + type: "string", + ref: "qListObjectDef.qDef.qFieldDefs.0", + label: "Date field", + component: 'dropdown', + options: function () { + if (fieldList) { + return fieldList; + } + return getPromise(); + }, + show: function (data) { + return !data.advanced; + }, + change: function (data) { + var field = data.qListObjectDef.qDef.qFieldDefs[0]; + data.props.minDate = { qStringExpression: '=Min( {1} [' + field + '])' }; + data.props.maxDate = { qStringExpression: '=Max( {1} [' + field + '])' }; + data.props.startDate = { qStringExpression: '=Min([' + field + '])' }; + data.props.endDate = { qStringExpression: '=Max([' + field + '])' }; + } + }, + fieldAdvanced: { + type: "string", + ref: "qListObjectDef.qDef.qFieldDefs.0", + label: "Date field", + expression: "always", + expressionType: "dimension", + show: function (data) { + return data.advanced; + } + }, + SingleDateSwitch: { + type: "boolean", + component: "switch", + label: "Single date / interval", + ref: "props.isSingleDate", + options: [{ + value: true, + label: "Single date" + }, { + value: false, + label: "Date interval" + }], + defaultValue: false + }, + advanced: { + type: "boolean", + component: "switch", + label: "Advanced setup", + ref: "advanced", + options: [{ + value: true, + translation: "properties.on" + }, { + value: false, + translation: "properties.off" + }], + defaultValue: false, + show: function (data) { + return data.qListObjectDef.qDef.qFieldDefs.length > 0 + && data.qListObjectDef.qDef.qFieldDefs[0].length > 0; + } + }, + minDate: { + ref: "props.minDate", + label: "Min date", + type: "string", + expression: "optional", + show: function (data) { + return data.advanced; + } + }, + maxDate: { + ref: "props.maxDate", + label: "Max date", + type: "string", + expression: "optional", + show: function (data) { + return data.advanced; + } + }, + startDate: { + ref: "props.startDate", + label: "Start date", + type: "string", + expression: "optional", + show: function (data) { + return data.advanced; + } + }, + endDate: { + ref: "props.endDate", + label: "End date", + type: "string", + expression: "optional", + show: function (data) { + return data.advanced; + } + } + } + }; + var CalendarSettings = { + component: "expandable-items", + label: "Calendar Settings", + items: { + ranges: { type: "items", - label: "Field", - ref: "qListObjectDef", - min: 1, - max: 1, + label: "Predefined ranges", items: { - field: { - type: "string", - ref: "qListObjectDef.qDef.qFieldDefs.0", - label: "Date field", - component: 'dropdown', - options: function () { - if (fieldList) { - return fieldList; - } - return getPromise(); - }, - show: function (data) { - return !data.advanced; - }, - change: function (data) { - var field = data.qListObjectDef.qDef.qFieldDefs[0]; - data.props.minDate = { qStringExpression: '=Min( {1} [' + field + '])' }; - data.props.maxDate = { qStringExpression: '=Max( {1} [' + field + '])' }; - data.props.startDate = { qStringExpression: '=Min([' + field + '])' }; - data.props.endDate = { qStringExpression: '=Max([' + field + '])' }; - } - }, - fieldAdvanced: { - type: "string", - ref: "qListObjectDef.qDef.qFieldDefs.0", - label: "Date field", - expression: "always", - expressionType: "dimension", - show: function (data) { - return data.advanced; - } - }, - SingleDateSwitch: { - type: "boolean", - component: "switch", - label: "Single date / interval", - ref: "props.isSingleDate", - options: [{ - value: true, - label: "Single date" - }, { - value: false, - label: "Date interval" - }], - defaultValue: false - }, - advanced: { - type: "boolean", - component: "switch", - label: "Advanced setup", - ref: "advanced", - options: [{ - value: true, - translation: "properties.on" - }, { - value: false, - translation: "properties.off" - }], - defaultValue: false, - show: function (data) { - return data.qListObjectDef.qDef.qFieldDefs.length > 0 && - data.qListObjectDef.qDef.qFieldDefs[0].length > 0; - } - }, - minDate: { - ref: "props.minDate", - label: "Min date", - type: "string", - expression: "optional", - show: function (data) { - return data.advanced; - } - }, - maxDate: { - ref: "props.maxDate", - label: "Max date", - type: "string", - expression: "optional", - show: function (data) { - return data.advanced; - } - }, - startDate: { - ref: "props.startDate", - label: "Start date", - type: "string", - expression: "optional", - show: function (data) { - return data.advanced; - } - }, - endDate: { - ref: "props.endDate", - label: "End date", - type: "string", - expression: "optional", - show: function (data) { - return data.advanced; - } + CustomRangesSwitch: { + type: "boolean", + component: "switch", + label: "Show predefined ranges", + ref: "props.CustomRangesEnabled", + options: [{ + value: true, + translation: "properties.on" + }, { + value: false, + translation: "properties.off" + }], + defaultValue: true + }, + CustomRange: { + type: "string", + ref: "props.customRangeLabel", + label: "Custom Range", + defaultValue: "Range", + expression: "optional", + show: function (data) { + return data.props.CustomRangesEnabled; } - } - }; - var CalendarSettings = { - component: "expandable-items", - label: "Calendar Settings", - items: { - ranges: { - type: "items", - label: "Predefined ranges", - items: { - CustomRangesSwitch: { - type: "boolean", - component: "switch", - label: "Show predefined ranges", - ref: "props.CustomRangesEnabled", - options: [{ - value: true, - translation: "properties.on" - }, { - value: false, - translation: "properties.off" - }], - defaultValue: true - }, - CustomRange: { - type: "string", - ref: "props.customRangeLabel", - label: "Custom Range", - defaultValue: "Range", - expression: "optional", - show: function (data) { - return data.props.CustomRangesEnabled; - } - }, - Today: { - type: "string", - ref: "props.today", - label: "Today", - defaultValue: "Today", - expression: "optional", - show: function (data) { - return data.props.CustomRangesEnabled; - } - }, - Yesterday: { - type: "string", - ref: "props.yesterday", - label: "Yesterday", - defaultValue: "Yesterday", - expression: "optional", - show: function (data) { - return data.props.CustomRangesEnabled; - } - }, - LastDays: { - type: "string", - ref: "props.lastXDays", - label: "Last $ days", - defaultValue: "Last $ days", - expression: "optional", - show: function (data) { - return data.props.CustomRangesEnabled; - } - }, - ThisMonth: { - type: "string", - ref: "props.thisMonth", - label: "This Month", - defaultValue: "This Month", - expression: "optional", - show: function (data) { - return data.props.CustomRangesEnabled; - } - }, - LastMonth: { - type: "string", - ref: "props.lastMonth", - label: "Last Month", - defaultValue: "Last Month", - expression: "optional", - show: function (data) { - return data.props.CustomRangesEnabled; - } - } - } - }, - header1: { - type: "items", - label: "Language and labels", - items: { - Language: { - type: "string", - ref: "props.locale", - label: "Locale", - defaultValue: "en", - expression: "optional" - }, - Format: { - type: "string", - ref: "props.format", - label: "Format", - defaultValue: "YYYY-MM-DD", - expression: "optional" - }, - Separator: { - type: "string", - ref: "props.separator", - label: "Separator", - defaultValue: " - ", - expression: "optional" - }, - defaultText: { - type: "string", - ref: "props.defaultText", - label: "Default Text", - expression: "optional", - defaultValue: "Select date range" - } - } + }, + Today: { + type: "string", + ref: "props.today", + label: "Today", + defaultValue: "Today", + expression: "optional", + show: function (data) { + return data.props.CustomRangesEnabled; } + }, + Yesterday: { + type: "string", + ref: "props.yesterday", + label: "Yesterday", + defaultValue: "Yesterday", + expression: "optional", + show: function (data) { + return data.props.CustomRangesEnabled; + } + }, + LastDays: { + type: "string", + ref: "props.lastXDays", + label: "Last $ days", + defaultValue: "Last $ days", + expression: "optional", + show: function (data) { + return data.props.CustomRangesEnabled; + } + }, + ThisMonth: { + type: "string", + ref: "props.thisMonth", + label: "This Month", + defaultValue: "This Month", + expression: "optional", + show: function (data) { + return data.props.CustomRangesEnabled; + } + }, + LastMonth: { + type: "string", + ref: "props.lastMonth", + label: "Last Month", + defaultValue: "Last Month", + expression: "optional", + show: function (data) { + return data.props.CustomRangesEnabled; + } + } } - }; - var about = { - label: "About", - component: "items", - items: { - header: { - label: 'Date picker', - style: 'header', - component: 'text' - }, - paragraph1: { - label: 'A calendar object that allows a user to make selections in a date field.', - component: 'text' - }, - paragraph2: { - label: 'Date picker is based upon an extension created by Nodier Torres.', - component: 'text' - } - } - }; - var appearance = { - uses: "settings", - items: { - general: { - items: { - details: { - show: false - } - } - }, - } - }; - return { + }, + header1: { type: "items", - component: "accordion", + label: "Language and labels", items: { - dimension: dimension, - settings: appearance, - CalSettings: CalendarSettings, - about: about + Language: { + type: "string", + ref: "props.locale", + label: "Locale", + defaultValue: "en", + expression: "optional" + }, + Format: { + type: "string", + ref: "props.format", + label: "Format", + defaultValue: "YYYY-MM-DD", + expression: "optional" + }, + Separator: { + type: "string", + ref: "props.separator", + label: "Separator", + defaultValue: " - ", + expression: "optional" + }, + defaultText: { + type: "string", + ref: "props.defaultText", + label: "Default Text", + expression: "optional", + defaultValue: "Select date range" + } } + } + } + }; + var about = { + label: "About", + component: "items", + items: { + header: { + label: 'Date picker', + style: 'header', + component: 'text' + }, + paragraph1: { + label: 'A calendar object that allows a user to make selections in a date field.', + component: 'text' + }, + paragraph2: { + label: 'Date picker is based upon an extension created by Nodier Torres.', + component: 'text' + } + } + }; + var appearance = { + uses: "settings", + items: { + general: { + items: { + details: { + show: false + } + } + }, + } + }; + return { + type: "items", + component: "accordion", + items: { + dimension: dimension, + settings: appearance, + CalSettings: CalendarSettings, + about: about } -}) \ No newline at end of file + }; +}); \ No newline at end of file diff --git a/src/lib/daterangepicker.js b/src/lib/daterangepicker.js index eaabf88..3e7ea33 100644 --- a/src/lib/daterangepicker.js +++ b/src/lib/daterangepicker.js @@ -12,1499 +12,1506 @@ *** */ -(function(root, factory) { - - if (typeof define === 'function' && define.amd ) { - define(['./moment.min', 'jquery', 'exports'], function(momentjs, $, exports) { - root.qlikdaterangepicker = factory(root, exports, momentjs, $); - }); - - } else if (typeof exports !== 'undefined') { - var momentjs = require('moment'); - var jQuery = (typeof window != 'undefined') ? window.jQuery : undefined; //isomorphic issue +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define(['./moment.min', 'jquery', 'exports'], function (momentjs, $, exports) { + root.qlikdaterangepicker = factory(root, exports, momentjs, $); + }); + } else if (typeof exports !== 'undefined') { + var momentjs = require('moment'); + var jQuery = (typeof window != 'undefined') ? window.jQuery : undefined; //isomorphic issue + if (!jQuery) { + try { + jQuery = require('jquery'); + if (!jQuery.fn) { + jQuery.fn = {}; //isomorphic issue + } + } catch (err) { if (!jQuery) { - try { - jQuery = require('jquery'); - if (!jQuery.fn) jQuery.fn = {}; //isomorphic issue - } catch (err) { - if (!jQuery) throw new Error('jQuery dependency not found'); - } + throw new Error('jQuery dependency not found'); } - - factory(root, exports, momentjs, jQuery); - + } + } + + factory(root, exports, momentjs, jQuery); + // Finally, as a browser global. - } else { - root.qlikdaterangepicker = factory(root, {}, root.moment || moment, (root.jQuery || root.Zepto || root.ender || root.$)); + } else { + root.qlikdaterangepicker = factory( + root, {}, root.moment || momentjs, (root.jQuery || root.Zepto || root.ender || root.$)); + } +} (this || {}, function (root, qlikdaterangepicker, moment, $) { // 'this' doesn't exist on a server + var DateRangePicker = function (element, options, cb) { + //default settings for options + this.parentEl = 'body'; + this.element = $(element); + this.startDate = moment().startOf('day'); + this.endDate = moment().endOf('day'); + this.minDate = false; + this.maxDate = false; + this.dateLimit = false; + this.autoApply = false; + this.singleDatePicker = false; + this.showDropdowns = false; + this.showWeekNumbers = false; + this.timePicker = false; + this.timePicker24Hour = false; + this.timePickerIncrement = 1; + this.timePickerSeconds = false; + this.linkedCalendars = true; + this.autoUpdateInput = true; + this.ranges = {}; + + this.opens = 'right'; + if (this.element.hasClass('pull-right')) + this.opens = 'left'; + + this.drops = 'down'; + if (this.element.hasClass('dropup')) + this.drops = 'up'; + + this.buttonClasses = 'btn btn-sm'; + this.applyClass = 'btn-success'; + this.cancelClass = 'btn-default'; + + this.locale = { + format: 'MM/DD/YYYY', + separator: ' - ', + applyLabel: 'Apply', + cancelLabel: 'Cancel', + weekLabel: 'W', + customRangeLabel: 'Custom Range', + daysOfWeek: moment.weekdaysMin(), + monthNames: moment.monthsShort(), + firstDay: moment.localeData().firstDayOfWeek() + }; + + this.callback = function () { }; + + //some state information + this.isShowing = false; + this.leftCalendar = {}; + this.rightCalendar = {}; + + this.preventSelections = false; + //custom options from user + if (typeof options !== 'object' || options === null) + options = {}; + + //allow setting options with data attributes + //data-api options will be overwritten with custom javascript options + options = $.extend(this.element.data(), options); + + //html template for the picker UI + if (typeof options.template !== 'string') + options.template = ` + `; + + this.parentEl = (options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl); + + this.container = $(options.template).appendTo(this.parentEl); + + // + // handle all the possible options overriding defaults + // + if (typeof options.locale === 'object') { + if (typeof options.locale.format === 'string') + this.locale.format = options.locale.format; + + if (typeof options.locale.separator === 'string') + this.locale.separator = options.locale.separator; + + if (typeof options.locale.daysOfWeek === 'object') + this.locale.daysOfWeek = options.locale.daysOfWeek.slice(); + + if (typeof options.locale.monthNames === 'object') + this.locale.monthNames = options.locale.monthNames.slice(); + + if (typeof options.locale.firstDay === 'number') + this.locale.firstDay = options.locale.firstDay; + + if (typeof options.locale.applyLabel === 'string') + this.locale.applyLabel = options.locale.applyLabel; + + if (typeof options.locale.cancelLabel === 'string') + this.locale.cancelLabel = options.locale.cancelLabel; + + if (typeof options.locale.weekLabel === 'string') + this.locale.weekLabel = options.locale.weekLabel; + + if (typeof options.locale.customRangeLabel === 'string') + this.locale.customRangeLabel = options.locale.customRangeLabel; } - - }(this || {}, function(root, qlikdaterangepicker, moment, $) { // 'this' doesn't exist on a server - - var DateRangePicker = function(element, options, cb) { - - //default settings for options - this.parentEl = 'body'; - this.element = $(element); - this.startDate = moment().startOf('day'); - this.endDate = moment().endOf('day'); - this.minDate = false; - this.maxDate = false; - this.dateLimit = false; - this.autoApply = false; - this.singleDatePicker = false; - this.showDropdowns = false; - this.showWeekNumbers = false; - this.timePicker = false; - this.timePicker24Hour = false; - this.timePickerIncrement = 1; - this.timePickerSeconds = false; - this.linkedCalendars = true; - this.autoUpdateInput = true; - this.ranges = {}; - - this.opens = 'right'; - if (this.element.hasClass('pull-right')) - this.opens = 'left'; - - this.drops = 'down'; - if (this.element.hasClass('dropup')) - this.drops = 'up'; - - this.buttonClasses = 'btn btn-sm'; - this.applyClass = 'btn-success'; - this.cancelClass = 'btn-default'; - - this.locale = { - format: 'MM/DD/YYYY', - separator: ' - ', - applyLabel: 'Apply', - cancelLabel: 'Cancel', - weekLabel: 'W', - customRangeLabel: 'Custom Range', - daysOfWeek: moment.weekdaysMin(), - monthNames: moment.monthsShort(), - firstDay: moment.localeData().firstDayOfWeek() - }; - - this.callback = function() { }; - - //some state information - this.isShowing = false; - this.leftCalendar = {}; - this.rightCalendar = {}; - - this.preventSelections = false; - //custom options from user - if (typeof options !== 'object' || options === null) - options = {}; - - //allow setting options with data attributes - //data-api options will be overwritten with custom javascript options - options = $.extend(this.element.data(), options); - - //html template for the picker UI - if (typeof options.template !== 'string') - options.template = ''; - - this.parentEl = (options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl); - - this.container = $(options.template).appendTo(this.parentEl); - - // - // handle all the possible options overriding defaults - // - - if (typeof options.locale === 'object') { - - if (typeof options.locale.format === 'string') - this.locale.format = options.locale.format; - - if (typeof options.locale.separator === 'string') - this.locale.separator = options.locale.separator; - - if (typeof options.locale.daysOfWeek === 'object') - this.locale.daysOfWeek = options.locale.daysOfWeek.slice(); - - if (typeof options.locale.monthNames === 'object') - this.locale.monthNames = options.locale.monthNames.slice(); - - if (typeof options.locale.firstDay === 'number') - this.locale.firstDay = options.locale.firstDay; - - if (typeof options.locale.applyLabel === 'string') - this.locale.applyLabel = options.locale.applyLabel; - - if (typeof options.locale.cancelLabel === 'string') - this.locale.cancelLabel = options.locale.cancelLabel; - - if (typeof options.locale.weekLabel === 'string') - this.locale.weekLabel = options.locale.weekLabel; - - if (typeof options.locale.customRangeLabel === 'string') - this.locale.customRangeLabel = options.locale.customRangeLabel; - - } - - if (typeof options.startDate === 'string') - this.startDate = moment(options.startDate, this.locale.format); - - if (typeof options.endDate === 'string') - this.endDate = moment(options.endDate, this.locale.format); - - if (typeof options.minDate === 'string') - this.minDate = moment(options.minDate, this.locale.format); - - if (typeof options.maxDate === 'string') - this.maxDate = moment(options.maxDate, this.locale.format); - - if (typeof options.startDate === 'object') - this.startDate = moment(options.startDate); - - if (typeof options.endDate === 'object') - this.endDate = moment(options.endDate); - - if (typeof options.minDate === 'object') - this.minDate = moment(options.minDate); - - if (typeof options.maxDate === 'object') - this.maxDate = moment(options.maxDate); - - // sanity check for bad options - if (this.minDate && this.startDate.isBefore(this.minDate)) - this.startDate = this.minDate.clone(); - - // sanity check for bad options - if (this.maxDate && this.endDate.isAfter(this.maxDate)) - this.endDate = this.maxDate.clone(); - - if (typeof options.applyClass === 'string') - this.applyClass = options.applyClass; - - if (typeof options.cancelClass === 'string') - this.cancelClass = options.cancelClass; - - if (typeof options.dateLimit === 'object') - this.dateLimit = options.dateLimit; - - if (typeof options.opens === 'string') - this.opens = options.opens; - - if (typeof options.drops === 'string') - this.drops = options.drops; - - if (typeof options.showWeekNumbers === 'boolean') - this.showWeekNumbers = options.showWeekNumbers; - - if (typeof options.buttonClasses === 'string') - this.buttonClasses = options.buttonClasses; - - if (typeof options.buttonClasses === 'object') - this.buttonClasses = options.buttonClasses.join(' '); - - if (typeof options.showDropdowns === 'boolean') - this.showDropdowns = options.showDropdowns; - - if (typeof options.singleDatePicker === 'boolean') { - this.singleDatePicker = options.singleDatePicker; - if (this.singleDatePicker) - this.endDate = this.startDate.clone(); - } - - if (typeof options.timePicker === 'boolean') - this.timePicker = options.timePicker; - - if (typeof options.timePickerSeconds === 'boolean') - this.timePickerSeconds = options.timePickerSeconds; - - if (typeof options.timePickerIncrement === 'number') - this.timePickerIncrement = options.timePickerIncrement; - - if (typeof options.timePicker24Hour === 'boolean') - this.timePicker24Hour = options.timePicker24Hour; - - if (typeof options.autoApply === 'boolean') - this.autoApply = options.autoApply; - - if (typeof options.autoUpdateInput === 'boolean') - this.autoUpdateInput = options.autoUpdateInput; - - if (typeof options.linkedCalendars === 'boolean') - this.linkedCalendars = options.linkedCalendars; - - if (typeof options.isInvalidDate === 'function') - this.isInvalidDate = options.isInvalidDate; - - if (typeof options.getClass === 'function') - this.getClass = options.getClass; - - if (typeof options.preventSelections === 'boolean') - this.preventSelections = options.preventSelections; - - // update day names order to firstDay - if (this.locale.firstDay != 0) { - var iterator = this.locale.firstDay; - while (iterator > 0) { - this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift()); - iterator--; - } - } - - var start, end, range; - - //if no start/end dates set, check if an input element contains initial values - if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') { - if ($(this.element).is('input[type=text]')) { - var val = $(this.element).val(), - split = val.split(this.locale.separator); - - start = end = null; - - if (split.length == 2) { - start = moment(split[0], this.locale.format); - end = moment(split[1], this.locale.format); - } else if (this.singleDatePicker && val !== "") { - start = moment(val, this.locale.format); - end = moment(val, this.locale.format); - } - if (start !== null && end !== null) { - this.setStartDate(start); - this.setEndDate(end); - } - } - } - - if (typeof options.ranges === 'object') { - for (range in options.ranges) { - - if (typeof options.ranges[range][0] === 'string') - start = moment(options.ranges[range][0], this.locale.format); - else - start = moment(options.ranges[range][0]); - - if (typeof options.ranges[range][1] === 'string') - end = moment(options.ranges[range][1], this.locale.format); - else - end = moment(options.ranges[range][1]); - - // If the start or end date exceed those allowed by the minDate or dateLimit - // options, shorten the range to the allowable period. - if (this.minDate && start.isBefore(this.minDate)) - start = this.minDate.clone(); - - var maxDate = this.maxDate; - if (this.dateLimit && start.clone().add(this.dateLimit).isAfter(maxDate)) - maxDate = start.clone().add(this.dateLimit); - if (maxDate && end.isAfter(maxDate)) - end = maxDate.clone(); - - // If the end of the range is before the minimum or the start of the range is - // after the maximum, disable this range option . - var disabled = (this.minDate && end.isBefore(this.minDate)) || (maxDate && start.isAfter(maxDate)); - - var elem = document.createElement('textarea'); - elem.innerHTML = range; - var rangeHtml = elem.value; - - this.ranges[rangeHtml] = [start, end, disabled]; - } - - var list = '
Select:
'; - this.container.find('.ranges').prepend(list); - } - - if (typeof cb === 'function') { - this.callback = cb; + + if (typeof options.startDate === 'string') + this.startDate = moment(options.startDate, this.locale.format); + + if (typeof options.endDate === 'string') + this.endDate = moment(options.endDate, this.locale.format); + + if (typeof options.minDate === 'string') + this.minDate = moment(options.minDate, this.locale.format); + + if (typeof options.maxDate === 'string') + this.maxDate = moment(options.maxDate, this.locale.format); + + if (typeof options.startDate === 'object') + this.startDate = moment(options.startDate); + + if (typeof options.endDate === 'object') + this.endDate = moment(options.endDate); + + if (typeof options.minDate === 'object') + this.minDate = moment(options.minDate); + + if (typeof options.maxDate === 'object') + this.maxDate = moment(options.maxDate); + + // sanity check for bad options + if (this.minDate && this.startDate.isBefore(this.minDate)) + this.startDate = this.minDate.clone(); + + // sanity check for bad options + if (this.maxDate && this.endDate.isAfter(this.maxDate)) + this.endDate = this.maxDate.clone(); + + if (typeof options.applyClass === 'string') + this.applyClass = options.applyClass; + + if (typeof options.cancelClass === 'string') + this.cancelClass = options.cancelClass; + + if (typeof options.dateLimit === 'object') + this.dateLimit = options.dateLimit; + + if (typeof options.opens === 'string') + this.opens = options.opens; + + if (typeof options.drops === 'string') + this.drops = options.drops; + + if (typeof options.showWeekNumbers === 'boolean') + this.showWeekNumbers = options.showWeekNumbers; + + if (typeof options.buttonClasses === 'string') + this.buttonClasses = options.buttonClasses; + + if (typeof options.buttonClasses === 'object') + this.buttonClasses = options.buttonClasses.join(' '); + + if (typeof options.showDropdowns === 'boolean') + this.showDropdowns = options.showDropdowns; + + if (typeof options.singleDatePicker === 'boolean') { + this.singleDatePicker = options.singleDatePicker; + if (this.singleDatePicker) + this.endDate = this.startDate.clone(); + } + + if (typeof options.timePicker === 'boolean') + this.timePicker = options.timePicker; + + if (typeof options.timePickerSeconds === 'boolean') + this.timePickerSeconds = options.timePickerSeconds; + + if (typeof options.timePickerIncrement === 'number') + this.timePickerIncrement = options.timePickerIncrement; + + if (typeof options.timePicker24Hour === 'boolean') + this.timePicker24Hour = options.timePicker24Hour; + + if (typeof options.autoApply === 'boolean') + this.autoApply = options.autoApply; + + if (typeof options.autoUpdateInput === 'boolean') + this.autoUpdateInput = options.autoUpdateInput; + + if (typeof options.linkedCalendars === 'boolean') + this.linkedCalendars = options.linkedCalendars; + + if (typeof options.isInvalidDate === 'function') + this.isInvalidDate = options.isInvalidDate; + + if (typeof options.getClass === 'function') + this.getClass = options.getClass; + + if (typeof options.preventSelections === 'boolean') + this.preventSelections = options.preventSelections; + + // update day names order to firstDay + if (this.locale.firstDay != 0) { + var iterator = this.locale.firstDay; + while (iterator > 0) { + this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift()); + iterator--; + } + } + + var start, end, range; + + //if no start/end dates set, check if an input element contains initial values + if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') { + if ($(this.element).is('input[type=text]')) { + var val = $(this.element).val(), + split = val.split(this.locale.separator); + + start = end = null; + + if (split.length == 2) { + start = moment(split[0], this.locale.format); + end = moment(split[1], this.locale.format); + } else if (this.singleDatePicker && val !== "") { + start = moment(val, this.locale.format); + end = moment(val, this.locale.format); + } + if (start !== null && end !== null) { + this.setStartDate(start); + this.setEndDate(end); + } + } + } + + if (typeof options.ranges === 'object') { + for (range in options.ranges) { + if (typeof options.ranges[range][0] === 'string') + start = moment(options.ranges[range][0], this.locale.format); + else + start = moment(options.ranges[range][0]); + + if (typeof options.ranges[range][1] === 'string') + end = moment(options.ranges[range][1], this.locale.format); + else + end = moment(options.ranges[range][1]); + + // If the start or end date exceed those allowed by the minDate or dateLimit + // options, shorten the range to the allowable period. + if (this.minDate && start.isBefore(this.minDate)) + start = this.minDate.clone(); + + var maxDate = this.maxDate; + if (this.dateLimit && start.clone().add(this.dateLimit).isAfter(maxDate)) + maxDate = start.clone().add(this.dateLimit); + if (maxDate && end.isAfter(maxDate)) + end = maxDate.clone(); + + // If the end of the range is before the minimum or the start of the range is + // after the maximum, disable this range option . + var disabled = (this.minDate && end.isBefore(this.minDate)) || (maxDate && start.isAfter(maxDate)); + + var elem = document.createElement('textarea'); + elem.innerHTML = range; + var rangeHtml = elem.value; + + this.ranges[rangeHtml] = [start, end, disabled]; + } + + var list = '
Select:
'; + this.container.find('.ranges').prepend(list); + } + + if (typeof cb === 'function') { + this.callback = cb; + } + + if (!this.timePicker) { + this.startDate = this.startDate.startOf('day'); + this.endDate = this.endDate.endOf('day'); + this.container.find('.calendar-time').hide(); + } + + //can't be used together for now + if (this.timePicker && this.autoApply) + this.autoApply = false; + + if (this.autoApply && typeof options.ranges !== 'object') { + this.container.find('.ranges').hide(); + } else if (this.autoApply) { + this.container.find('.applyBtn, .cancelBtn').addClass('hide'); + } + + if (this.singleDatePicker) { + this.container.addClass('single'); + this.container.find('.calendar.dpleft').addClass('single'); + this.container.find('.calendar.dpleft').show(); + this.container.find('.calendar.dpright').hide(); + this.container.find('.qlik-daterangepicker_input input, .qlik-daterangepicker_input i').hide(); + if (!this.timePicker) { + this.container.find('.ranges').hide(); + } + } + + if (typeof options.ranges === 'undefined' && !this.singleDatePicker) { + this.container.addClass('show-calendar'); + } + + this.container.addClass('opens' + this.opens); + + //swap the position of the predefined ranges if opens right + if (typeof options.ranges !== 'undefined' && this.opens == 'right') { + var ranges = this.container.find('.ranges'); + var html = ranges.clone(); + ranges.remove(); + this.container.find('.calendar.dpleft').parent().prepend(html); + } + + //apply CSS classes and labels to buttons + this.container.find('.applyBtn, .cancelBtn').addClass(this.buttonClasses); + if (this.applyClass.length) + this.container.find('.applyBtn').addClass(this.applyClass); + if (this.cancelClass.length) + this.container.find('.cancelBtn').addClass(this.cancelClass); + this.container.find('.applyBtn').html(this.locale.applyLabel); + this.container.find('.cancelBtn').html(this.locale.cancelLabel); + + // + // event listeners + // + + this.container.find('.calendar') + .on('click.qlik-daterangepicker', '.prev.available', $.proxy(this.clickPrev, this)) + .on('click.qlik-daterangepicker', '.next.available', $.proxy(this.clickNext, this)) + .on('click.qlik-daterangepicker', 'td.available', $.proxy(this.clickDate, this)) + .on('mouseenter.qlik-daterangepicker', 'td.available', $.proxy(this.hoverDate, this)) + .on('mouseleave.qlik-daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this)) + .on('change.qlik-daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged, this)) + .on('change.qlik-daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this)) + .on( + 'change.qlik-daterangepicker', + 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', + $.proxy(this.timeChanged, this)) + .on('click.qlik-daterangepicker', '.qlik-daterangepicker_input input', $.proxy(this.showCalendars, this)) + //.on('keyup.qlik-daterangepicker', '.qlik-daterangepicker_input input', $.proxy(this.formInputsChanged, this)) + .on('change.qlik-daterangepicker', '.qlik-daterangepicker_input input', $.proxy(this.formInputsChanged, this)); + + this.container.find('.ranges') + .on('click.qlik-daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this)) + .on('click.qlik-daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this)) + .on('click.qlik-daterangepicker', 'li', $.proxy(this.clickRange, this)) + .on('mouseenter.qlik-daterangepicker', 'li', $.proxy(this.hoverRange, this)) + .on('mouseleave.qlik-daterangepicker', 'li', $.proxy(this.updateFormInputs, this)); + + if (this.element.is('input')) { + this.element.on({ + 'click.qlik-daterangepicker': $.proxy(this.show, this), + 'focus.qlik-daterangepicker': $.proxy(this.show, this), + 'keyup.qlik-daterangepicker': $.proxy(this.elementChanged, this), + 'keydown.qlik-daterangepicker': $.proxy(this.keydown, this) + }); + } else { + this.element.on('click.qlik-daterangepicker', $.proxy(this.toggle, this)); + } + + // + // if attached to a text input, set the initial value + // + + if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) { + this.element.val( + this.startDate.format(this.locale.format) + + this.locale.separator + + this.endDate.format(this.locale.format)); + this.element.trigger('change'); + } else if (this.element.is('input') && this.autoUpdateInput) { + this.element.val(this.startDate.format(this.locale.format)); + this.element.trigger('change'); + } + }; + + DateRangePicker.prototype = { + constructor: DateRangePicker, + + setStartDate: function (startDate) { + if (typeof startDate === 'string') + this.startDate = moment(startDate, this.locale.format); + + if (typeof startDate === 'object') + this.startDate = moment(startDate); + + if (!this.timePicker) + this.startDate = this.startDate.startOf('day'); + + if (this.timePicker && this.timePickerIncrement) + this.startDate.minute( + Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); + + if (this.minDate && this.startDate.isBefore(this.minDate)) + this.startDate = this.minDate; + + if (this.maxDate && this.startDate.isAfter(this.maxDate)) + this.startDate = this.maxDate; + + if (!this.isShowing) + this.updateElement(); + + this.container.addClass('in-selection'); + this.updateMonthsInView(); + }, + + setEndDate: function (endDate) { + if (typeof endDate === 'string') + this.endDate = moment(endDate, this.locale.format); + + if (typeof endDate === 'object') + this.endDate = moment(endDate); + + if (!this.timePicker) + this.endDate = this.endDate.endOf('day'); + + if (this.timePicker && this.timePickerIncrement) + this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); + + if (this.endDate.isBefore(this.startDate)) + this.endDate = this.startDate.clone(); + + if (this.maxDate.endOf('day') && this.endDate.isAfter(this.maxDate)) + this.endDate = this.maxDate; + + if (this.dateLimit && this.startDate.clone().add(this.dateLimit).isBefore(this.endDate)) + this.endDate = this.startDate.clone().add(this.dateLimit); + + if (!this.isShowing) + this.updateElement(); + + this.updateMonthsInView(); + }, + + isInvalidDate: function () { + return false; + }, + + updateView: function () { + if (this.timePicker) { + this.renderTimePicker('left'); + this.renderTimePicker('right'); + if (!this.endDate) { + this.container.find('.dpright .calendar-time select').attr('disabled', 'disabled').addClass('disabled'); + } else { + this.container.find('.dpright .calendar-time select').removeAttr('disabled').removeClass('disabled'); + } + } + this.container.find('.calendar.active').removeClass('active'); + + if (this.endDate) { + this.container.find('input[name=qlik-daterangepicker_start]').closest('.calendar').addClass('active'); + } else { + this.container.find('input[name=qlik-daterangepicker_end]').closest('.calendar').addClass('active'); + } + this.updateMonthsInView(); + this.updateCalendars(); + this.updateFormInputs(); + }, + + + updateMonthsInView: function () { + if (this.endDate) { + //if both dates are visible already, do nothing + if (!this.singleDatePicker && this.leftCalendar.month && this.rightCalendar.month + && (this.startDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') + || this.startDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM')) + && (this.endDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') + || this.endDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM')) + ) { + return; + } + + this.leftCalendar.month = this.startDate.clone().date(2); + if (!this.linkedCalendars + && (this.endDate.month() != this.startDate.month() + || this.endDate.year() != this.startDate.year())) { + this.rightCalendar.month = this.endDate.clone().date(2); + } else { + this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month'); + } + } else { + if (this.leftCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM') + && this.rightCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM')) { + this.leftCalendar.month = this.startDate.clone().date(2); + this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month'); + } + } + }, + + updateCalendars: function () { + if (this.timePicker) { + var hour, minute, second, ampm; + if (this.endDate) { + hour = parseInt(this.container.find('.dpleft .hourselect').val(), 10); + minute = parseInt(this.container.find('.dpleft .minuteselect').val(), 10); + second = this.timePickerSeconds ? parseInt(this.container.find('.dpleft .secondselect').val(), 10) : 0; + if (!this.timePicker24Hour) { + ampm = this.container.find('.dpleft .ampmselect').val(); + if (ampm === 'PM' && hour < 12) + hour += 12; + if (ampm === 'AM' && hour === 12) + hour = 0; } - - if (!this.timePicker) { - this.startDate = this.startDate.startOf('day'); - this.endDate = this.endDate.endOf('day'); - this.container.find('.calendar-time').hide(); + } else { + hour = parseInt(this.container.find('.dpright .hourselect').val(), 10); + minute = parseInt(this.container.find('.dpright .minuteselect').val(), 10); + second = this.timePickerSeconds ? parseInt(this.container.find('.dpright .secondselect').val(), 10) : 0; + if (!this.timePicker24Hour) { + ampm = this.container.find('.dpright .ampmselect').val(); + if (ampm === 'PM' && hour < 12) + hour += 12; + if (ampm === 'AM' && hour === 12) + hour = 0; } - - //can't be used together for now - if (this.timePicker && this.autoApply) - this.autoApply = false; - - if (this.autoApply && typeof options.ranges !== 'object') { - this.container.find('.ranges').hide(); - } else if (this.autoApply) { - this.container.find('.applyBtn, .cancelBtn').addClass('hide'); + } + this.leftCalendar.month.hour(hour).minute(minute).second(second); + this.rightCalendar.month.hour(hour).minute(minute).second(second); + } + + this.renderCalendar('left'); + this.renderCalendar('right'); + + // looks like we don't do anything with active ranges + this.showCalendars(); + }, + + renderCalendar: function (side) { + // + // Build the matrix of dates that will populate the calendar + // + var calendar = side == 'left' ? this.leftCalendar : this.rightCalendar; + var month = calendar.month.month(); + var year = calendar.month.year(); + var hour = calendar.month.hour(); + var minute = calendar.month.minute(); + var second = calendar.month.second(); + var daysInMonth = moment([year, month]).daysInMonth(); + var firstDay = moment([year, month, 1]); + var lastDay = moment([year, month, daysInMonth]); + var lastMonth = moment(firstDay).subtract(1, 'month').month(); + var lastYear = moment(firstDay).subtract(1, 'month').year(); + var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth(); + var dayOfWeek = firstDay.day(); + //initialize a 6 rows x 7 columns array for the calendar + calendar = []; + calendar.firstDay = firstDay; + calendar.lastDay = lastDay; + + for (let i = 0; i < 6; i++) { + calendar[i] = []; + } + + //populate the calendar with date objects + var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1; + if (startDay > daysInLastMonth) + startDay -= 7; + + var curDate; + if (dayOfWeek == this.locale.firstDay) { + startDay = 1; + curDate = moment([year, month, startDay, 12, minute, second]); + } else { + curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]); + } + + for (let i = 0, col = 0, row = 0; i < 42; i++ , col++ , curDate = moment(curDate).add(24, 'hour')) { + if (i > 0 && col % 7 === 0) { + col = 0; + row++; + } + calendar[row][col] = curDate.clone().hour(hour).minute(minute).second(second); + curDate.hour(12); + + if (this.minDate + && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') + && calendar[row][col].isBefore(this.minDate)) { + calendar[row][col] = this.minDate.clone(); + } + + if (this.maxDate + && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') + && calendar[row][col].isAfter(this.maxDate)) { + calendar[row][col] = this.maxDate.clone(); + } + } + + //make the calendar object available to hoverDate/clickDate + if (side == 'left') { + this.leftCalendar.calendar = calendar; + } else { + this.rightCalendar.calendar = calendar; + } + + // + // Display the calendar + // + var minDate = side == 'left' ? this.minDate : this.startDate; + var maxDate = this.maxDate; + + var html = ''; + html += ''; + html += ''; + + // add empty cell for week number + if (this.showWeekNumbers) + html += ''; + + if ((!this.linkedCalendars || side == 'left')) { + html += '`; + } else { + html += ''; + } + + var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY"); + + if (this.showDropdowns) { + var currentMonth = calendar[1][1].month(); + var currentYear = calendar[1][1].year(); + var maxYear = (maxDate && maxDate.year()) || (currentYear + 5); + var minYear = (minDate && minDate.year()) || (currentYear - 50); + var inMinYear = currentYear == minYear; + var inMaxYear = currentYear == maxYear; + + var monthHtml = '"; + + var yearHtml = ''; + + dateHtml = monthHtml + yearHtml; + } + + html += ''; + if ((!this.linkedCalendars || side == 'right' || this.singleDatePicker)) { + html += '`; + } else { + html += ''; + } + + html += ''; + html += ''; + + // add week number label + if (this.showWeekNumbers) + html += ''; + + $.each(this.locale.daysOfWeek, function (index, dayOfWeek) { + html += ''; + }); + + html += ''; + html += ''; + html += ''; + + //adjust maxDate to reflect the dateLimit setting in order to + //grey out end dates beyond the dateLimit + if (this.endDate == null && this.dateLimit) { + var maxLimit = this.startDate.clone().add(this.dateLimit).endOf('day'); + if (!maxDate || maxLimit.isBefore(maxDate)) { + maxDate = maxLimit; + } + } + + for (var row = 0; row < 6; row++) { + html += ''; + + // add week number + if (this.showWeekNumbers) + html += ''; + + for (var col = 0; col < 7; col++) { + var classes = []; + //grey out the dates in other months displayed at beginning and end of this calendar + if (calendar[row][col].month() != calendar[1][1].month()) { + classes.push('empty'); + } else { + //highlight today's date + if (calendar[row][col].isSame(new Date(), "day")) + classes.push('today'); + + //highlight weekends + if (calendar[row][col].isoWeekday() > 5) + classes.push('weekend'); + + //don't allow selection of dates before the minimum date + if (this.minDate && calendar[row][col].isBefore(this.minDate, 'day')) + classes.push('off', 'disabled'); + + //don't allow selection of dates after the maximum date + if (maxDate && calendar[row][col].isAfter(maxDate, 'day')) + classes.push('off', 'disabled'); + + //don't allow selection of date if a custom function decides it's invalid + if (this.isInvalidDate(calendar[row][col])) + classes.push('off', 'disabled'); + + if (this.getClass) + classes.push(this.getClass(calendar[row][col])); + + //highlight the currently selected start date + if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD')) + classes.push('active', 'start-date'); + + //highlight the currently selected end date + if (this.endDate != null + && calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD')) + classes.push('active', 'end-date'); } - - if (typeof options.ranges === 'undefined' && !this.singleDatePicker) { - this.container.addClass('show-calendar'); + var cname = '', disabled = false; + for (var i = 0; i < classes.length; i++) { + cname += classes[i] + ' '; + if (['disabled', 'nodata', 'empty'].indexOf(classes[i]) > -1) + disabled = true; } - - this.container.addClass('opens' + this.opens); - - //swap the position of the predefined ranges if opens right - if (typeof options.ranges !== 'undefined' && this.opens == 'right') { - var ranges = this.container.find('.ranges'); - var html = ranges.clone(); - ranges.remove(); - this.container.find('.calendar.dpleft').parent().prepend(html); + if (!disabled) + cname += 'available'; + + html += ''; + } + html += ''; + } + + html += ''; + html += '
' + dateHtml + '
' + this.locale.weekLabel + '' + dayOfWeek + '
' + calendar[row][0].week() + '' + + calendar[row][col].date() + '
'; + + this.container.find('.calendar.dp' + side + ' .calendar-table').html(html); + }, + + renderTimePicker: function (side) { + var html, selected, minDate, maxDate = this.maxDate; + + if (this.dateLimit + && (!this.maxDate || this.startDate.clone().add(this.dateLimit).isAfter(this.maxDate))) + maxDate = this.startDate.clone().add(this.dateLimit); + + if (side == 'left') { + selected = this.startDate.clone(); + minDate = this.minDate; + } else if (side == 'right') { + selected = this.endDate ? this.endDate.clone() : this.startDate.clone(); + minDate = this.startDate; + } + + // + // hours + // + + html = ' '; + + // + // minutes + // + + html += ': '; + + // + // seconds + // + + if (this.timePickerSeconds) { + html += ': '; + } + + // + // AM/PM + // + + if (!this.timePicker24Hour) { + html += ''; + } + + this.container.find('.calendar.dp' + side + ' .calendar-time div').html(html); + }, + + updateFormInputs: function () { + //ignore mouse movements while an above-calendar text input has focus + if (this.container.find('input[name=qlik-daterangepicker_start]').is(":focus") + || this.container.find('input[name=qlik-daterangepicker_end]').is(":focus")) { + return; + } + + this.container.find('input[name=qlik-daterangepicker_start]') + .val(this.startDate.format(this.locale.format)); + + if (this.endDate && this.endDate._isValid) { //eslint-disable-line + this.container.find('input[name=qlik-daterangepicker_end]') + .val(this.endDate.format(this.locale.format)); + } else { + this.container.find('input[name=qlik-daterangepicker_end]').val(""); + } + + if (this.singleDatePicker + || (this.endDate + && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) { + this.container.find('button.applyBtn').removeAttr('disabled'); + } else { + this.container.find('button.applyBtn').attr('disabled', 'disabled'); + } + }, + + move: function () { + var parentOffset = { top: 0, left: 0 }, + containerTop; + var parentRightEdge = $(window).width(); + if (!this.parentEl.is('body')) { + parentOffset = { + top: this.parentEl.offset().top - this.parentEl.scrollTop(), + left: this.parentEl.offset().left - this.parentEl.scrollLeft() + }; + parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left; + } + + if (this.drops == 'up') + containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top; + else + containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top; + this.container[this.drops == 'up' ? 'addClass' : 'removeClass']('dropup'); + + if (this.opens == 'left') { + this.container.css({ + top: containerTop, + right: parentRightEdge - this.element.offset().left - this.element.outerWidth(), + left: 'auto' + }); + if (this.container.offset().left < 0) { + this.container.css({ + right: 'auto', + left: 9 + }); + } + } else if (this.opens == 'center') { + this.container.css({ + top: containerTop, + left: this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2 + - this.container.outerWidth() / 2, + right: 'auto' + }); + if (this.container.offset().left < 0) { + this.container.css({ + right: 'auto', + left: 9 + }); + } + } else { + this.container.css({ + top: containerTop, + left: this.element.offset().left - parentOffset.left, + right: 'auto' + }); + if (this.container.offset().left + this.container.outerWidth() > $(window).width()) { + this.container.css({ + left: 'auto', + right: 0 + }); + } + } + }, + + show: function (e) { + if (this.isShowing) return; + // Create a click proxy that is private to this instance of datepicker, for unbinding + this._outsideClickProxy = $.proxy(function (e) { this.outsideClick(e); }, this); //eslint-disable-line + + // Bind global datepicker mousedown for hiding and + $(document) + .on('mousedown.qlik-daterangepicker', this._outsideClickProxy) //eslint-disable-line + // also support mobile devices + .on('touchend.qlik-daterangepicker', this._outsideClickProxy) //eslint-disable-line + // also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them + .on('click.qlik-daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy) //eslint-disable-line + // and also close when focus changes to outside the picker (eg. tabbing between controls) + .on('focusin.qlik-daterangepicker', this._outsideClickProxy); //eslint-disable-line + + // Reposition the picker if the window is resized while it's open + $(window).on('resize.qlik-daterangepicker', $.proxy(function (e) { this.move(e); }, this)); + + this.oldStartDate = this.startDate.clone(); + this.oldEndDate = this.endDate.clone(); + + this.updateView(); + this.container.show(); + this.move(); + this.element.trigger('show.qlik-daterangepicker', this); + this.isShowing = true; + }, + + hide: function (e) { + if (!this.isShowing) return; + + //incomplete date selection, revert to last values + if (!this.endDate) { + this.startDate = this.oldStartDate.clone(); + this.endDate = this.oldEndDate.clone(); + } + + //if a new date range was selected, invoke the user callback function + //or if applyclicked + if (this.applyClicked || (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))) + this.callback(this.startDate, this.endDate, this.chosenLabel); + + //if picker is attached to a text input, update it + this.updateElement(); + + $(document).off('.qlik-daterangepicker'); + $(window).off('.qlik-daterangepicker'); + this.container.removeClass('in-selection'); + this.applyClicked = false; + this.container.hide(); + this.element.trigger('hide.qlik-daterangepicker', this); + this.isShowing = false; + }, + + toggle: function (e) { + if (this.isShowing) { + this.hide(); + } else { + this.show(); + } + }, + + outsideClick: function (e) { + var target = $(e.target); + // if the page is clicked anywhere except within the daterangerpicker/button + // itself then call this.hide() + if ( + // ie modal dialog fix + e.type == "focusin" + || target.closest(this.element).length + || target.closest(this.container).length + || target.closest('.calendar-table').length) { + return; + } + this.hide(); + }, + + showCalendars: function () { + this.container.addClass('show-calendar'); + this.move(); + this.element.trigger('showCalendar.qlik-daterangepicker', this); + }, + + hideCalendars: function () { + this.container.removeClass('show-calendar'); + this.element.trigger('hideCalendar.qlik-daterangepicker', this); + }, + + hoverRange: function (e) { + //ignore mouse movements while an above-calendar text input has focus + if (this.container.find('input[name=qlik-daterangepicker_start]').is(":focus") + || this.container.find('input[name=qlik-daterangepicker_end]').is(":focus")) { + return; + } + + var label = e.target.innerHTML; + if (label == this.locale.customRangeLabel) { + this.updateView(); + } else { + var dates = this.ranges[label]; + this.container.find('input[name=qlik-daterangepicker_start]').val(dates[0].format(this.locale.format)); + + if (this.endDate) { + this.container.find('input[name=qlik-daterangepicker_end]').val(dates[1].format(this.locale.format)); + } + } + }, + + clickRange: function (e) { + if (this.preventSelections) return; + + var label = e.target.innerHTML; + this.chosenLabel = label; + if (label == this.locale.customRangeLabel) { + this.showCalendars(); + } else { + var dates = this.ranges[label]; + this.startDate = dates[0]; + this.endDate = dates[1]; + + if (!this.timePicker) { + this.startDate.startOf('day'); + this.endDate.endOf('day'); + } + + this.hideCalendars(); + this.clickApply(); + } + }, + + clickPrev: function (e) { + if (this.preventSelections) return; + var cal = $(e.target).parents('.calendar'); + if (cal.hasClass('dpleft')) { + this.leftCalendar.month.subtract(1, 'month'); + if (this.linkedCalendars) + this.rightCalendar.month.subtract(1, 'month'); + } else { + this.rightCalendar.month.subtract(1, 'month'); + } + this.updateCalendars(); + }, + + clickNext: function (e) { + if (this.preventSelections) return; + + var cal = $(e.target).parents('.calendar'); + if (cal.hasClass('dpleft')) { + this.leftCalendar.month.add(1, 'month'); + } else { + this.rightCalendar.month.add(1, 'month'); + if (this.linkedCalendars) + this.leftCalendar.month.add(1, 'month'); + } + this.updateCalendars(); + }, + + hoverDate: function (e) { + //ignore mouse movements while an above-calendar text input has focus + if (this.container.find('input[name=qlik-daterangepicker_start]').is(":focus") + || this.container.find('input[name=qlik-daterangepicker_end]').is(":focus")) { + return; + } + + //ignore dates that can't be selected + if (!$(e.target).hasClass('available')) return; + + //have the text inputs above calendars reflect the date being hovered over + var title = $(e.target).attr('data-title'); + var row = title.substr(1, 1); + var col = title.substr(3, 1); + var cal = $(e.target).parents('.calendar'); + var date = cal.hasClass('dpleft') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col]; + + if (this.endDate) { + this.container.find('input[name=qlik-daterangepicker_start]').val(date.format(this.locale.format)); + } else { + this.container.find('input[name=qlik-daterangepicker_end]').val(date.format(this.locale.format)); + } + + //highlight the dates between the start date and the date being hovered as a potential end date + var leftCalendar = this.leftCalendar; + var rightCalendar = this.rightCalendar; + var startDate = this.startDate; + if (!this.endDate) { + this.container.find('.calendar td').each(function (index, el) { + //skip week numbers, only look at dates + if ($(el).hasClass('week')) return; + //skip empty cells + if ($(el).hasClass('empty')) return; + + var title = $(el).attr('data-title'); + var row = title.substr(1, 1); + var col = title.substr(3, 1); + var cal = $(el).parents('.calendar'); + var dt = cal.hasClass('dpleft') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col]; + + if (dt.isAfter(startDate) && dt.isBefore(date)) { + $(el).addClass('in-range'); } else { - this.element.on('click.qlik-daterangepicker', $.proxy(this.toggle, this)); + $(el).removeClass('in-range'); } - - // - // if attached to a text input, set the initial value - // - - if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) { - this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format)); - this.element.trigger('change'); - } else if (this.element.is('input') && this.autoUpdateInput) { - this.element.val(this.startDate.format(this.locale.format)); - this.element.trigger('change'); + }); + } + }, + + clickDate: function (e) { + if (this.preventSelections || !$(e.target).hasClass('available')) { + return; + } + + var title = $(e.target).attr('data-title'); + var row = title.substr(1, 1); + var col = title.substr(3, 1); + var cal = $(e.target).parents('.calendar'); + var date = cal.hasClass('dpleft') + ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col]; + + // + // this function needs to do a few things: + // * alternate between selecting a start and end date for the range, + // * if the time picker is enabled, apply the hour/minute/second from the select boxes to the clicked date + // * if autoapply is enabled, and an end date was chosen, apply the selection + // * if single date picker mode, and time picker isn't enabled, apply the selection immediately + // + + if (this.endDate || date.isBefore(this.startDate)) { + if (this.timePicker) { + let hour = parseInt(this.container.find('.dpleft .hourselect').val(), 10); + if (!this.timePicker24Hour) { + let ampm = cal.find('.ampmselect').val(); + if (ampm === 'PM' && hour < 12) + hour += 12; + if (ampm === 'AM' && hour === 12) + hour = 0; } - - }; - - DateRangePicker.prototype = { - - constructor: DateRangePicker, - - setStartDate: function(startDate) { - if (typeof startDate === 'string') - this.startDate = moment(startDate, this.locale.format); - - if (typeof startDate === 'object') - this.startDate = moment(startDate); - - if (!this.timePicker) - this.startDate = this.startDate.startOf('day'); - - if (this.timePicker && this.timePickerIncrement) - this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); - - if (this.minDate && this.startDate.isBefore(this.minDate)) - this.startDate = this.minDate; - - if (this.maxDate && this.startDate.isAfter(this.maxDate)) - this.startDate = this.maxDate; - - if (!this.isShowing) - this.updateElement(); - - this.container.addClass('in-selection'); - this.updateMonthsInView(); - }, - - setEndDate: function(endDate) { - if (typeof endDate === 'string') - this.endDate = moment(endDate, this.locale.format); - - if (typeof endDate === 'object') - this.endDate = moment(endDate); - - if (!this.timePicker) - this.endDate = this.endDate.endOf('day'); - - if (this.timePicker && this.timePickerIncrement) - this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); - - if (this.endDate.isBefore(this.startDate)) - this.endDate = this.startDate.clone(); - - if (this.maxDate.endOf('day') && this.endDate.isAfter(this.maxDate)) - this.endDate = this.maxDate; - - if (this.dateLimit && this.startDate.clone().add(this.dateLimit).isBefore(this.endDate)) - this.endDate = this.startDate.clone().add(this.dateLimit); - - if (!this.isShowing) - this.updateElement(); - - this.updateMonthsInView(); - }, - - isInvalidDate: function() { - return false; - }, - - updateView: function() { - if (this.timePicker) { - this.renderTimePicker('left'); - this.renderTimePicker('right'); - if (!this.endDate) { - this.container.find('.dpright .calendar-time select').attr('disabled', 'disabled').addClass('disabled'); - } else { - this.container.find('.dpright .calendar-time select').removeAttr('disabled').removeClass('disabled'); - } - } - this.container.find('.calendar.active').removeClass('active'); - - if (this.endDate) { - this.container.find('input[name=qlik-daterangepicker_start]').closest('.calendar').addClass('active'); - } else { - this.container.find('input[name=qlik-daterangepicker_end]').closest('.calendar').addClass('active'); - } - this.updateMonthsInView(); - this.updateCalendars(); - this.updateFormInputs(); - }, - - - updateMonthsInView: function() { - if (this.endDate) { - - //if both dates are visible already, do nothing - if (!this.singleDatePicker && this.leftCalendar.month && this.rightCalendar.month && - (this.startDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.startDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM')) - && - (this.endDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.endDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM')) - ) { - return; - } - - this.leftCalendar.month = this.startDate.clone().date(2); - if (!this.linkedCalendars && (this.endDate.month() != this.startDate.month() || this.endDate.year() != this.startDate.year())) { - this.rightCalendar.month = this.endDate.clone().date(2); - } else { - this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month'); - } - - } else { - if (this.leftCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM') && this.rightCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM')) { - this.leftCalendar.month = this.startDate.clone().date(2); - this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month'); - } - } - }, - - updateCalendars: function() { - - if (this.timePicker) { - var hour, minute, second; - if (this.endDate) { - hour = parseInt(this.container.find('.dpleft .hourselect').val(), 10); - minute = parseInt(this.container.find('.dpleft .minuteselect').val(), 10); - second = this.timePickerSeconds ? parseInt(this.container.find('.dpleft .secondselect').val(), 10) : 0; - if (!this.timePicker24Hour) { - var ampm = this.container.find('.dpleft .ampmselect').val(); - if (ampm === 'PM' && hour < 12) - hour += 12; - if (ampm === 'AM' && hour === 12) - hour = 0; - } - } else { - hour = parseInt(this.container.find('.dpright .hourselect').val(), 10); - minute = parseInt(this.container.find('.dpright .minuteselect').val(), 10); - second = this.timePickerSeconds ? parseInt(this.container.find('.dpright .secondselect').val(), 10) : 0; - if (!this.timePicker24Hour) { - var ampm = this.container.find('.dpright .ampmselect').val(); - if (ampm === 'PM' && hour < 12) - hour += 12; - if (ampm === 'AM' && hour === 12) - hour = 0; - } - } - this.leftCalendar.month.hour(hour).minute(minute).second(second); - this.rightCalendar.month.hour(hour).minute(minute).second(second); - } - - this.renderCalendar('left'); - this.renderCalendar('right'); - - // looks like we don't do anything with active ranges - this.showCalendars(); - }, - - renderCalendar: function(side) { - // - // Build the matrix of dates that will populate the calendar - // - var calendar = side == 'left' ? this.leftCalendar : this.rightCalendar; - var month = calendar.month.month(); - var year = calendar.month.year(); - var hour = calendar.month.hour(); - var minute = calendar.month.minute(); - var second = calendar.month.second(); - var daysInMonth = moment([year, month]).daysInMonth(); - var firstDay = moment([year, month, 1]); - var lastDay = moment([year, month, daysInMonth]); - var lastMonth = moment(firstDay).subtract(1, 'month').month(); - var lastYear = moment(firstDay).subtract(1, 'month').year(); - var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth(); - var dayOfWeek = firstDay.day(); - //initialize a 6 rows x 7 columns array for the calendar - var calendar = []; - calendar.firstDay = firstDay; - calendar.lastDay = lastDay; - - for (var i = 0; i < 6; i++) { - calendar[i] = []; - } - - //populate the calendar with date objects - var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1; - if (startDay > daysInLastMonth) - startDay -= 7; - - var curDate - if (dayOfWeek == this.locale.firstDay){ - startDay = 1; - curDate = moment([year, month, startDay, 12, minute, second]); - } else { - curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]); - } - - var col, row; - for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) { - if (i > 0 && col % 7 === 0) { - col = 0; - row++; - } - calendar[row][col] = curDate.clone().hour(hour).minute(minute).second(second); - curDate.hour(12); - - if (this.minDate && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.minDate)) { - calendar[row][col] = this.minDate.clone(); - } - - if (this.maxDate && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.maxDate)) { - calendar[row][col] = this.maxDate.clone(); - } - - } - - //make the calendar object available to hoverDate/clickDate - if (side == 'left') { - this.leftCalendar.calendar = calendar; - } else { - this.rightCalendar.calendar = calendar; - } - - // - // Display the calendar - // - var minDate = side == 'left' ? this.minDate : this.startDate; - var maxDate = this.maxDate; - var selected = side == 'left' ? this.startDate : this.endDate; - - var html = ''; - html += ''; - html += ''; - - // add empty cell for week number - if (this.showWeekNumbers) - html += ''; - - if ( (!this.linkedCalendars || side == 'left')) { - html += ''; - } else { - html += ''; - } - - var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY"); - - if (this.showDropdowns) { - var currentMonth = calendar[1][1].month(); - var currentYear = calendar[1][1].year(); - var maxYear = (maxDate && maxDate.year()) || (currentYear + 5); - var minYear = (minDate && minDate.year()) || (currentYear - 50); - var inMinYear = currentYear == minYear; - var inMaxYear = currentYear == maxYear; - - var monthHtml = '"; - - var yearHtml = ''; - - dateHtml = monthHtml + yearHtml; - } - - html += ''; - if ((!this.linkedCalendars || side == 'right' || this.singleDatePicker)) { - html += ''; - } else { - html += ''; - } - - html += ''; - html += ''; - - // add week number label - if (this.showWeekNumbers) - html += ''; - - $.each(this.locale.daysOfWeek, function(index, dayOfWeek) { - html += ''; - }); - - html += ''; - html += ''; - html += ''; - - //adjust maxDate to reflect the dateLimit setting in order to - //grey out end dates beyond the dateLimit - if (this.endDate == null && this.dateLimit) { - var maxLimit = this.startDate.clone().add(this.dateLimit).endOf('day'); - if (!maxDate || maxLimit.isBefore(maxDate)) { - maxDate = maxLimit; - } - } - - for (var row = 0; row < 6; row++) { - html += ''; - - // add week number - if (this.showWeekNumbers) - html += ''; - - for (var col = 0; col < 7; col++) { - - var classes = []; - //grey out the dates in other months displayed at beginning and end of this calendar - if (calendar[row][col].month() != calendar[1][1].month()){ - classes.push('empty'); - }else{ - //highlight today's date - if (calendar[row][col].isSame(new Date(), "day")) - classes.push('today'); - - //highlight weekends - if (calendar[row][col].isoWeekday() > 5) - classes.push('weekend'); - - //don't allow selection of dates before the minimum date - if (this.minDate && calendar[row][col].isBefore(this.minDate, 'day')) - classes.push('off', 'disabled'); - - //don't allow selection of dates after the maximum date - if (maxDate && calendar[row][col].isAfter(maxDate, 'day')) - classes.push('off', 'disabled'); - - //don't allow selection of date if a custom function decides it's invalid - if (this.isInvalidDate(calendar[row][col])) - classes.push('off', 'disabled'); - - if(this.getClass) - classes.push(this.getClass(calendar[row][col])); - - //highlight the currently selected start date - if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD')) - classes.push('active', 'start-date'); - - //highlight the currently selected end date - if (this.endDate != null && calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD')) - classes.push('active', 'end-date'); - - //highlight dates in-between the selected dates - //if (this.endDate != null && calendar[row][col] > this.startDate && calendar[row][col] < this.endDate) - // classes.push('in-range'); - } - var cname = '', disabled = false; - for (var i = 0; i < classes.length; i++) { - cname += classes[i] + ' '; - if ( ['disabled','nodata','empty'].indexOf(classes[i]) > -1) - disabled = true; - } - if (!disabled) - cname += 'available'; - - html += ''; - - } - html += ''; - } - - html += ''; - html += '
' + dateHtml + '
' + this.locale.weekLabel + '' + dayOfWeek + '
' + calendar[row][0].week() + '' + calendar[row][col].date() + '
'; - - this.container.find('.calendar.dp' + side + ' .calendar-table').html(html); - - }, - - renderTimePicker: function(side) { - - var html, selected, minDate, maxDate = this.maxDate; - - if (this.dateLimit && (!this.maxDate || this.startDate.clone().add(this.dateLimit).isAfter(this.maxDate))) - maxDate = this.startDate.clone().add(this.dateLimit); - - if (side == 'left') { - selected = this.startDate.clone(); - minDate = this.minDate; - } else if (side == 'right') { - selected = this.endDate ? this.endDate.clone() : this.startDate.clone(); - minDate = this.startDate; - } - - // - // hours - // - - html = ' '; - - // - // minutes - // - - html += ': '; - - // - // seconds - // - - if (this.timePickerSeconds) { - html += ': '; - } - - // - // AM/PM - // - - if (!this.timePicker24Hour) { - html += ''; - } - - this.container.find('.calendar.dp' + side + ' .calendar-time div').html(html); - - }, - - updateFormInputs: function() { - //ignore mouse movements while an above-calendar text input has focus - if (this.container.find('input[name=qlik-daterangepicker_start]').is(":focus") || this.container.find('input[name=qlik-daterangepicker_end]').is(":focus")) - return; - - this.container.find('input[name=qlik-daterangepicker_start]').val(this.startDate.format(this.locale.format)); - - if (this.endDate?this.endDate._isValid:false){ - this.container.find('input[name=qlik-daterangepicker_end]').val(this.endDate.format(this.locale.format)); - } - else{ - this.container.find('input[name=qlik-daterangepicker_end]').val("") - } - - - if (this.singleDatePicker || (this.endDate && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) { - this.container.find('button.applyBtn').removeAttr('disabled'); - } else { - this.container.find('button.applyBtn').attr('disabled', 'disabled'); - } - - }, - - move: function() { - var parentOffset = { top: 0, left: 0 }, - containerTop; - var parentRightEdge = $(window).width(); - if (!this.parentEl.is('body')) { - parentOffset = { - top: this.parentEl.offset().top - this.parentEl.scrollTop(), - left: this.parentEl.offset().left - this.parentEl.scrollLeft() - }; - parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left; - } - - if (this.drops == 'up') - containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top; - else - containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top; - this.container[this.drops == 'up' ? 'addClass' : 'removeClass']('dropup'); - - if (this.opens == 'left') { - this.container.css({ - top: containerTop, - right: parentRightEdge - this.element.offset().left - this.element.outerWidth(), - left: 'auto' - }); - if (this.container.offset().left < 0) { - this.container.css({ - right: 'auto', - left: 9 - }); - } - } else if (this.opens == 'center') { - this.container.css({ - top: containerTop, - left: this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2 - - this.container.outerWidth() / 2, - right: 'auto' - }); - if (this.container.offset().left < 0) { - this.container.css({ - right: 'auto', - left: 9 - }); - } - } else { - this.container.css({ - top: containerTop, - left: this.element.offset().left - parentOffset.left, - right: 'auto' - }); - if (this.container.offset().left + this.container.outerWidth() > $(window).width()) { - this.container.css({ - left: 'auto', - right: 0 - }); - } - } - }, - - show: function(e) { - if (this.isShowing) return; - // Create a click proxy that is private to this instance of datepicker, for unbinding - this._outsideClickProxy = $.proxy(function(e) { this.outsideClick(e); }, this); - - // Bind global datepicker mousedown for hiding and - $(document) - .on('mousedown.qlik-daterangepicker', this._outsideClickProxy) - // also support mobile devices - .on('touchend.qlik-daterangepicker', this._outsideClickProxy) - // also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them - .on('click.qlik-daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy) - // and also close when focus changes to outside the picker (eg. tabbing between controls) - .on('focusin.qlik-daterangepicker', this._outsideClickProxy); - - // Reposition the picker if the window is resized while it's open - $(window).on('resize.qlik-daterangepicker', $.proxy(function(e) { this.move(e); }, this)); - - this.oldStartDate = this.startDate.clone(); - this.oldEndDate = this.endDate.clone(); - - this.updateView(); - this.container.show(); - this.move(); - this.element.trigger('show.qlik-daterangepicker', this); - this.isShowing = true; - }, - - hide: function(e) { - if (!this.isShowing) return; - - //incomplete date selection, revert to last values - if (!this.endDate) { - this.startDate = this.oldStartDate.clone(); - this.endDate = this.oldEndDate.clone(); - } - - //if a new date range was selected, invoke the user callback function - //or if applyclicked - if (this.applyClicked || (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))) - this.callback(this.startDate, this.endDate, this.chosenLabel); - - //if picker is attached to a text input, update it - this.updateElement(); - - $(document).off('.qlik-daterangepicker'); - $(window).off('.qlik-daterangepicker'); - this.container.removeClass('in-selection'); - this.applyClicked = false; - this.container.hide(); - this.element.trigger('hide.qlik-daterangepicker', this); - this.isShowing = false; - }, - - toggle: function(e) { - if (this.isShowing) { - this.hide(); - } else { - this.show(); - } - }, - - outsideClick: function(e) { - var target = $(e.target); - // if the page is clicked anywhere except within the daterangerpicker/button - // itself then call this.hide() - if ( - // ie modal dialog fix - e.type == "focusin" || - target.closest(this.element).length || - target.closest(this.container).length || - target.closest('.calendar-table').length - ) return; - this.hide(); - }, - - showCalendars: function() { - this.container.addClass('show-calendar'); - this.move(); - this.element.trigger('showCalendar.qlik-daterangepicker', this); - }, - - hideCalendars: function() { - this.container.removeClass('show-calendar'); - this.element.trigger('hideCalendar.qlik-daterangepicker', this); - }, - - hoverRange: function(e) { - //ignore mouse movements while an above-calendar text input has focus - if (this.container.find('input[name=qlik-daterangepicker_start]').is(":focus") || this.container.find('input[name=qlik-daterangepicker_end]').is(":focus")) - return; - - var label = e.target.innerHTML; - if (label == this.locale.customRangeLabel) { - this.updateView(); - } else { - var dates = this.ranges[label]; - this.container.find('input[name=qlik-daterangepicker_start]').val(dates[0].format(this.locale.format)); - - if (this.endDate) { - this.container.find('input[name=qlik-daterangepicker_end]').val(dates[1].format(this.locale.format)); - } - } - - }, - - clickRange: function(e) { - if (this.preventSelections) return; - - var label = e.target.innerHTML; - this.chosenLabel = label; - if (label == this.locale.customRangeLabel) { - this.showCalendars(); - } else { - var dates = this.ranges[label]; - this.startDate = dates[0]; - this.endDate = dates[1]; - - if (!this.timePicker) { - this.startDate.startOf('day'); - this.endDate.endOf('day'); - } - - this.hideCalendars(); - this.clickApply(); - } - }, - - clickPrev: function(e) { - if (this.preventSelections) return; - var cal = $(e.target).parents('.calendar'); - if (cal.hasClass('dpleft')) { - this.leftCalendar.month.subtract(1, 'month'); - if (this.linkedCalendars) - this.rightCalendar.month.subtract(1, 'month'); - } else { - this.rightCalendar.month.subtract(1, 'month'); - } - this.updateCalendars(); - }, - - clickNext: function(e) { - if (this.preventSelections) return; - - var cal = $(e.target).parents('.calendar'); - if (cal.hasClass('dpleft')) { - this.leftCalendar.month.add(1, 'month'); - } else { - this.rightCalendar.month.add(1, 'month'); - if (this.linkedCalendars) - this.leftCalendar.month.add(1, 'month'); - } - this.updateCalendars(); - }, - - hoverDate: function(e) { - //ignore mouse movements while an above-calendar text input has focus - if (this.container.find('input[name=qlik-daterangepicker_start]').is(":focus") || this.container.find('input[name=qlik-daterangepicker_end]').is(":focus")) - return; - - //ignore dates that can't be selected - if (!$(e.target).hasClass('available')) return; - - //have the text inputs above calendars reflect the date being hovered over - var title = $(e.target).attr('data-title'); - var row = title.substr(1, 1); - var col = title.substr(3, 1); - var cal = $(e.target).parents('.calendar'); - var date = cal.hasClass('dpleft') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col]; - - if (this.endDate) { - this.container.find('input[name=qlik-daterangepicker_start]').val(date.format(this.locale.format)); - } else { - this.container.find('input[name=qlik-daterangepicker_end]').val(date.format(this.locale.format)); - } - - //highlight the dates between the start date and the date being hovered as a potential end date - var leftCalendar = this.leftCalendar; - var rightCalendar = this.rightCalendar; - var startDate = this.startDate; - if (!this.endDate) { - this.container.find('.calendar td').each(function(index, el) { - - //skip week numbers, only look at dates - if ($(el).hasClass('week')) return; - //skip empty cells - if ($(el).hasClass('empty')) return; - - var title = $(el).attr('data-title'); - var row = title.substr(1, 1); - var col = title.substr(3, 1); - var cal = $(el).parents('.calendar'); - var dt = cal.hasClass('dpleft') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col]; - - if (dt.isAfter(startDate) && dt.isBefore(date)) { - $(el).addClass('in-range'); - } else { - $(el).removeClass('in-range'); - } - - }); - } - - }, - - clickDate: function(e) { - if (this.preventSelections) return; - if (!$(e.target).hasClass('available')) return; - - var title = $(e.target).attr('data-title'); - var row = title.substr(1, 1); - var col = title.substr(3, 1); - var cal = $(e.target).parents('.calendar'); - var date = cal.hasClass('dpleft') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col]; - - // - // this function needs to do a few things: - // * alternate between selecting a start and end date for the range, - // * if the time picker is enabled, apply the hour/minute/second from the select boxes to the clicked date - // * if autoapply is enabled, and an end date was chosen, apply the selection - // * if single date picker mode, and time picker isn't enabled, apply the selection immediately - // - - if (this.endDate || date.isBefore(this.startDate)) { - if (this.timePicker) { - var hour = parseInt(this.container.find('.dpleft .hourselect').val(), 10); - if (!this.timePicker24Hour) { - var ampm = cal.find('.ampmselect').val(); - if (ampm === 'PM' && hour < 12) - hour += 12; - if (ampm === 'AM' && hour === 12) - hour = 0; - } - var minute = parseInt(this.container.find('.dpleft .minuteselect').val(), 10); - var second = this.timePickerSeconds ? parseInt(this.container.find('.dpleft .secondselect').val(), 10) : 0; - date = date.clone().hour(hour).minute(minute).second(second); - } - this.endDate = null; - this.setStartDate(date.clone()); - } else { - if (this.timePicker) { - var hour = parseInt(this.container.find('.dpright .hourselect').val(), 10); - if (!this.timePicker24Hour) { - var ampm = this.container.find('.dpright .ampmselect').val(); - if (ampm === 'PM' && hour < 12) - hour += 12; - if (ampm === 'AM' && hour === 12) - hour = 0; - } - var minute = parseInt(this.container.find('.dpright .minuteselect').val(), 10); - var second = this.timePickerSeconds ? parseInt(this.container.find('.dpright .secondselect').val(), 10) : 0; - date = date.clone().hour(hour).minute(minute).second(second); - } - this.setEndDate(date.clone()); - if (this.autoApply) - this.clickApply(); - } - - if (this.singleDatePicker) { - this.setEndDate(this.startDate); - if (!this.timePicker) - this.clickApply(); - } - - this.updateView(); - - }, - - clickApply: function(e) { - if (this.preventSelections) return; - // set applyClicked to true so we know if - // startdate and enddate are selected or being set to default if nothing selected - // or we hide the date picker when clicking outside to cancel - this.applyClicked = true; - this.hide(); - this.element.trigger('apply.qlik-daterangepicker', this); - }, - - clickCancel: function(e) { - if (this.preventSelections) return; - this.startDate = this.oldStartDate; - this.endDate = this.oldEndDate; - this.hide(); - this.element.trigger('cancel.qlik-daterangepicker', this); - }, - - monthOrYearChanged: function(e) { - var isLeft = $(e.target).closest('.calendar').hasClass('dpleft'), - leftOrRight = isLeft ? 'left' : 'right', - cal = this.container.find('.calendar.'+leftOrRight); - - // Month must be Number for new moment versions - var month = parseInt(cal.find('.monthselect').val(), 10); - var year = cal.find('.yearselect').val(); - - if (!isLeft) { - if (year < this.startDate.year() || (year == this.startDate.year() && month < this.startDate.month())) { - month = this.startDate.month(); - year = this.startDate.year(); - } - } - - if (this.minDate) { - if (year < this.minDate.year() || (year == this.minDate.year() && month < this.minDate.month())) { - month = this.minDate.month(); - year = this.minDate.year(); - } - } - - if (this.maxDate) { - if (year > this.maxDate.year() || (year == this.maxDate.year() && month > this.maxDate.month())) { - month = this.maxDate.month(); - year = this.maxDate.year(); - } - } - - if (isLeft) { - this.leftCalendar.month.month(month).year(year); - if (this.linkedCalendars) - this.rightCalendar.month = this.leftCalendar.month.clone().add(1, 'month'); - } else { - this.rightCalendar.month.month(month).year(year); - if (this.linkedCalendars) - this.leftCalendar.month = this.rightCalendar.month.clone().subtract(1, 'month'); - } - this.updateCalendars(); - }, - - timeChanged: function(e) { - - var cal = $(e.target).closest('.calendar'), - isLeft = cal.hasClass('dpleft'); - - var hour = parseInt(cal.find('.hourselect').val(), 10); - var minute = parseInt(cal.find('.minuteselect').val(), 10); - var second = this.timePickerSeconds ? parseInt(cal.find('.secondselect').val(), 10) : 0; - - if (!this.timePicker24Hour) { - var ampm = cal.find('.ampmselect').val(); - if (ampm === 'PM' && hour < 12) - hour += 12; - if (ampm === 'AM' && hour === 12) - hour = 0; - } - - if (isLeft) { - var start = this.startDate.clone(); - start.hour(hour); - start.minute(minute); - start.second(second); - this.setStartDate(start); - if (this.singleDatePicker) { - this.endDate = this.startDate.clone(); - } else if (this.endDate && this.endDate.format('YYYY-MM-DD') == start.format('YYYY-MM-DD') && this.endDate.isBefore(start)) { - this.setEndDate(start.clone()); - } - } else if (this.endDate) { - var end = this.endDate.clone(); - end.hour(hour); - end.minute(minute); - end.second(second); - this.setEndDate(end); - } - - //update the calendars so all clickable dates reflect the new time component - this.updateCalendars(); - - //update the form inputs above the calendars with the new time - this.updateFormInputs(); - - //re-render the time pickers because changing one selection can affect what's enabled in another - this.renderTimePicker('left'); - this.renderTimePicker('right'); - }, - - formInputsChanged: function(e) { - var isRight = $(e.target).closest('.calendar').hasClass('dpright'); - var start = moment(this.container.find('input[name=qlik-daterangepicker_start]').val(), this.locale.format); - var end = moment(this.container.find('input[name=qlik-daterangepicker_end]').val(), this.locale.format); - - if (start.isValid() && end.isValid()) { - - if (isRight && end.isBefore(start)) - start = end.clone(); - - this.setStartDate(start); - this.setEndDate(end); - - if (isRight) { - this.container.find('input[name=qlik-daterangepicker_start]').val(this.startDate.format(this.locale.format)); - } else { - this.container.find('input[name=qlik-daterangepicker_end]').val(this.endDate.format(this.locale.format)); - } - - } - - this.updateCalendars(); - if (this.timePicker) { - this.renderTimePicker('left'); - this.renderTimePicker('right'); - } - }, - - elementChanged: function() { - if (!this.element.is('input')) return; - if (!this.element.val().length) return; - if (this.element.val().length < this.locale.format.length) return; - - var dateString = this.element.val().split(this.locale.separator), - start = null, - end = null; - - if (dateString.length === 2) { - start = moment(dateString[0], this.locale.format); - end = moment(dateString[1], this.locale.format); - } - - if (this.singleDatePicker || start === null || end === null) { - start = moment(this.element.val(), this.locale.format); - end = start; - } - - if (!start.isValid() || !end.isValid()) return; - - this.setStartDate(start); - this.setEndDate(end); - this.updateView(); - }, - - keydown: function(e) { - //hide on tab or enter - if ((e.keyCode === 9) || (e.keyCode === 13)) { - this.hide(); - } - }, - - updateElement: function() { - if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) { - this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format)); - this.element.trigger('change'); - } else if (this.element.is('input') && this.autoUpdateInput) { - this.element.val(this.startDate.format(this.locale.format)); - this.element.trigger('change'); - } - }, - - remove: function() { - this.container.remove(); - this.element.off('.qlik-daterangepicker'); - this.element.removeData(); + let minute = parseInt(this.container.find('.dpleft .minuteselect').val(), 10); + let second = this.timePickerSeconds ? parseInt(this.container.find('.dpleft .secondselect').val(), 10) : 0; + date = date.clone().hour(hour).minute(minute).second(second); + } + this.endDate = null; + this.setStartDate(date.clone()); + } else { + if (this.timePicker) { + let hour = parseInt(this.container.find('.dpright .hourselect').val(), 10); + if (!this.timePicker24Hour) { + let ampm = this.container.find('.dpright .ampmselect').val(); + if (ampm === 'PM' && hour < 12) + hour += 12; + if (ampm === 'AM' && hour === 12) + hour = 0; } - - }; - - $.fn.qlikdaterangepicker = function(options, callback) { - this.each(function() { - var el = $(this); - if (el.data('qlik-daterangepicker')) - el.data('qlik-daterangepicker').remove(); - el.data('qlik-daterangepicker', new DateRangePicker(el, options, callback)); - }); - return this; - }; - - return DateRangePicker; - - })); - \ No newline at end of file + let minute = parseInt(this.container.find('.dpright .minuteselect').val(), 10); + let second = this.timePickerSeconds ? parseInt(this.container.find('.dpright .secondselect').val(), 10) : 0; + date = date.clone().hour(hour).minute(minute).second(second); + } + this.setEndDate(date.clone()); + if (this.autoApply) + this.clickApply(); + } + + if (this.singleDatePicker) { + this.setEndDate(this.startDate); + if (!this.timePicker) + this.clickApply(); + } + + this.updateView(); + }, + + clickApply: function (e) { + if (this.preventSelections) return; + // set applyClicked to true so we know if + // startdate and enddate are selected or being set to default if nothing selected + // or we hide the date picker when clicking outside to cancel + this.applyClicked = true; + this.hide(); + this.element.trigger('apply.qlik-daterangepicker', this); + }, + + clickCancel: function (e) { + if (this.preventSelections) return; + this.startDate = this.oldStartDate; + this.endDate = this.oldEndDate; + this.hide(); + this.element.trigger('cancel.qlik-daterangepicker', this); + }, + + monthOrYearChanged: function (e) { + var isLeft = $(e.target).closest('.calendar').hasClass('dpleft'), + leftOrRight = isLeft ? 'left' : 'right', + cal = this.container.find('.calendar.' + leftOrRight); + + // Month must be Number for new moment versions + var month = parseInt(cal.find('.monthselect').val(), 10); + var year = cal.find('.yearselect').val(); + + if (!isLeft) { + if (year < this.startDate.year() || (year == this.startDate.year() && month < this.startDate.month())) { + month = this.startDate.month(); + year = this.startDate.year(); + } + } + + if (this.minDate) { + if (year < this.minDate.year() || (year == this.minDate.year() && month < this.minDate.month())) { + month = this.minDate.month(); + year = this.minDate.year(); + } + } + + if (this.maxDate) { + if (year > this.maxDate.year() || (year == this.maxDate.year() && month > this.maxDate.month())) { + month = this.maxDate.month(); + year = this.maxDate.year(); + } + } + + if (isLeft) { + this.leftCalendar.month.month(month).year(year); + if (this.linkedCalendars) + this.rightCalendar.month = this.leftCalendar.month.clone().add(1, 'month'); + } else { + this.rightCalendar.month.month(month).year(year); + if (this.linkedCalendars) + this.leftCalendar.month = this.rightCalendar.month.clone().subtract(1, 'month'); + } + this.updateCalendars(); + }, + + timeChanged: function (e) { + var cal = $(e.target).closest('.calendar'), + isLeft = cal.hasClass('dpleft'); + + var hour = parseInt(cal.find('.hourselect').val(), 10); + var minute = parseInt(cal.find('.minuteselect').val(), 10); + var second = this.timePickerSeconds ? parseInt(cal.find('.secondselect').val(), 10) : 0; + + if (!this.timePicker24Hour) { + var ampm = cal.find('.ampmselect').val(); + if (ampm === 'PM' && hour < 12) + hour += 12; + if (ampm === 'AM' && hour === 12) + hour = 0; + } + + if (isLeft) { + var start = this.startDate.clone(); + start.hour(hour); + start.minute(minute); + start.second(second); + this.setStartDate(start); + if (this.singleDatePicker) { + this.endDate = this.startDate.clone(); + } else if (this.endDate + && this.endDate.format('YYYY-MM-DD') == start.format('YYYY-MM-DD') + && this.endDate.isBefore(start)) { + this.setEndDate(start.clone()); + } + } else if (this.endDate) { + var end = this.endDate.clone(); + end.hour(hour); + end.minute(minute); + end.second(second); + this.setEndDate(end); + } + + //update the calendars so all clickable dates reflect the new time component + this.updateCalendars(); + + //update the form inputs above the calendars with the new time + this.updateFormInputs(); + + //re-render the time pickers because changing one selection can affect what's enabled in another + this.renderTimePicker('left'); + this.renderTimePicker('right'); + }, + + formInputsChanged: function (e) { + var isRight = $(e.target).closest('.calendar').hasClass('dpright'); + var start = moment(this.container.find('input[name=qlik-daterangepicker_start]').val(), this.locale.format); + var end = moment(this.container.find('input[name=qlik-daterangepicker_end]').val(), this.locale.format); + + if (start.isValid() && end.isValid()) { + if (isRight && end.isBefore(start)) + start = end.clone(); + + this.setStartDate(start); + this.setEndDate(end); + + if (isRight) { + this.container.find('input[name=qlik-daterangepicker_start]').val(this.startDate.format(this.locale.format)); + } else { + this.container.find('input[name=qlik-daterangepicker_end]').val(this.endDate.format(this.locale.format)); + } + } + + this.updateCalendars(); + if (this.timePicker) { + this.renderTimePicker('left'); + this.renderTimePicker('right'); + } + }, + + elementChanged: function () { + if (!this.element.is('input')) return; + if (!this.element.val().length) return; + if (this.element.val().length < this.locale.format.length) return; + + var dateString = this.element.val().split(this.locale.separator), + start = null, + end = null; + + if (dateString.length === 2) { + start = moment(dateString[0], this.locale.format); + end = moment(dateString[1], this.locale.format); + } + + if (this.singleDatePicker || start === null || end === null) { + start = moment(this.element.val(), this.locale.format); + end = start; + } + + if (!start.isValid() || !end.isValid()) return; + + this.setStartDate(start); + this.setEndDate(end); + this.updateView(); + }, + + keydown: function (e) { + //hide on tab or enter + if ((e.keyCode === 9) || (e.keyCode === 13)) { + this.hide(); + } + }, + + updateElement: function () { + if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) { + this.element.val(this.startDate.format(this.locale.format) + + this.locale.separator + + this.endDate.format(this.locale.format)); + this.element.trigger('change'); + } else if (this.element.is('input') && this.autoUpdateInput) { + this.element.val(this.startDate.format(this.locale.format)); + this.element.trigger('change'); + } + }, + + remove: function () { + this.container.remove(); + this.element.off('.qlik-daterangepicker'); + this.element.removeData(); + } + }; + + $.fn.qlikdaterangepicker = function (options, callback) { + this.each(function () { + var el = $(this); + if (el.data('qlik-daterangepicker')) + el.data('qlik-daterangepicker').remove(); + el.data('qlik-daterangepicker', new DateRangePicker(el, options, callback)); + }); + return this; + }; + + return DateRangePicker; +})); diff --git a/src/lib/moment.min.js b/src/lib/moment.min.js index 90eb91b..6dc0389 100644 --- a/src/lib/moment.min.js +++ b/src/lib/moment.min.js @@ -1,3 +1,4 @@ +/* eslint-disable */ !function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return Md.apply(null,arguments)}function b(a){Md=a}function c(a){return"[object Array]"===Object.prototype.toString.call(a)}function d(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function e(a,b){var c,d=[];for(c=0;c0)for(c in Od)d=Od[c],e=b[d],"undefined"!=typeof e&&(a[d]=e);return a}function n(b){m(this,b),this._d=new Date(null!=b._d?b._d.getTime():NaN),Pd===!1&&(Pd=!0,a.updateOffset(this),Pd=!1)}function o(a){return a instanceof n||null!=a&&null!=a._isAMomentObject}function p(a){return 0>a?Math.ceil(a):Math.floor(a)}function q(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=p(b)),c}function r(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;e>d;d++)(c&&a[d]!==b[d]||!c&&q(a[d])!==q(b[d]))&&g++;return g+f}function s(){}function t(a){return a?a.toLowerCase().replace("_","-"):a}function u(a){for(var b,c,d,e,f=0;f0;){if(d=v(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&r(e,c,!0)>=b-1)break;b--}f++}return null}function v(a){var b=null;if(!Qd[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=Nd._abbr,require("./locale/"+a),w(b)}catch(c){}return Qd[a]}function w(a,b){var c;return a&&(c="undefined"==typeof b?y(a):x(a,b),c&&(Nd=c)),Nd._abbr}function x(a,b){return null!==b?(b.abbr=a,Qd[a]=Qd[a]||new s,Qd[a].set(b),w(a),Qd[a]):(delete Qd[a],null)}function y(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return Nd;if(!c(a)){if(b=v(a))return b;a=[a]}return u(a)}function z(a,b){var c=a.toLowerCase();Rd[c]=Rd[c+"s"]=Rd[b]=a}function A(a){return"string"==typeof a?Rd[a]||Rd[a.toLowerCase()]:void 0}function B(a){var b,c,d={};for(c in a)f(a,c)&&(b=A(c),b&&(d[b]=a[c]));return d}function C(b,c){return function(d){return null!=d?(E(this,b,d),a.updateOffset(this,c),this):D(this,b)}}function D(a,b){return a._d["get"+(a._isUTC?"UTC":"")+b]()}function E(a,b,c){return a._d["set"+(a._isUTC?"UTC":"")+b](c)}function F(a,b){var c;if("object"==typeof a)for(c in a)this.set(c,a[c]);else if(a=A(a),"function"==typeof this[a])return this[a](b);return this}function G(a,b,c){var d=""+Math.abs(a),e=b-d.length,f=a>=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d}function H(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(Vd[a]=e),b&&(Vd[b[0]]=function(){return G(e.apply(this,arguments),b[1],b[2])}),c&&(Vd[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function I(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function J(a){var b,c,d=a.match(Sd);for(b=0,c=d.length;c>b;b++)Vd[d[b]]?d[b]=Vd[d[b]]:d[b]=I(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function K(a,b){return a.isValid()?(b=L(b,a.localeData()),Ud[b]=Ud[b]||J(b),Ud[b](a)):a.localeData().invalidDate()}function L(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Td.lastIndex=0;d>=0&&Td.test(a);)a=a.replace(Td,c),Td.lastIndex=0,d-=1;return a}function M(a){return"function"==typeof a&&"[object Function]"===Object.prototype.toString.call(a)}function N(a,b,c){ie[a]=M(b)?b:function(a){return a&&c?c:b}}function O(a,b){return f(ie,a)?ie[a](b._strict,b._locale):new RegExp(P(a))}function P(a){return a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}).replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function Q(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),"number"==typeof b&&(d=function(a,c){c[b]=q(a)}),c=0;cd;d++){if(e=h([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}}function X(a,b){var c;return"string"==typeof b&&(b=a.localeData().monthsParse(b),"number"!=typeof b)?a:(c=Math.min(a.date(),T(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a)}function Y(b){return null!=b?(X(this,b),a.updateOffset(this,!0),this):D(this,"Month")}function Z(){return T(this.year(),this.month())}function $(a){var b,c=a._a;return c&&-2===j(a).overflow&&(b=c[le]<0||c[le]>11?le:c[me]<1||c[me]>T(c[ke],c[le])?me:c[ne]<0||c[ne]>24||24===c[ne]&&(0!==c[oe]||0!==c[pe]||0!==c[qe])?ne:c[oe]<0||c[oe]>59?oe:c[pe]<0||c[pe]>59?pe:c[qe]<0||c[qe]>999?qe:-1,j(a)._overflowDayOfYear&&(ke>b||b>me)&&(b=me),j(a).overflow=b),a}function _(b){a.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+b)}function aa(a,b){var c=!0;return g(function(){return c&&(_(a+"\n"+(new Error).stack),c=!1),b.apply(this,arguments)},b)}function ba(a,b){te[a]||(_(b),te[a]=!0)}function ca(a){var b,c,d=a._i,e=ue.exec(d);if(e){for(j(a).iso=!0,b=0,c=ve.length;c>b;b++)if(ve[b][1].exec(d)){a._f=ve[b][0];break}for(b=0,c=we.length;c>b;b++)if(we[b][1].exec(d)){a._f+=(e[6]||" ")+we[b][0];break}d.match(fe)&&(a._f+="Z"),va(a)}else a._isValid=!1}function da(b){var c=xe.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(ca(b),void(b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b))))}function ea(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function fa(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function ga(a){return ha(a)?366:365}function ha(a){return a%4===0&&a%100!==0||a%400===0}function ia(){return ha(this.year())}function ja(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=Da(a).add(f,"d"),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function ka(a){return ja(a,this._week.dow,this._week.doy).week}function la(){return this._week.dow}function ma(){return this._week.doy}function na(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function oa(a){var b=ja(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")}function pa(a,b,c,d,e){var f,g=6+e-d,h=fa(a,0,1+g),i=h.getUTCDay();return e>i&&(i+=7),c=null!=c?1*c:e,f=1+g+7*(b-1)-i+c,{year:f>0?a:a-1,dayOfYear:f>0?f:ga(a-1)+f}}function qa(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function ra(a,b,c){return null!=a?a:null!=b?b:c}function sa(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function ta(a){var b,c,d,e,f=[];if(!a._d){for(d=sa(a),a._w&&null==a._a[me]&&null==a._a[le]&&ua(a),a._dayOfYear&&(e=ra(a._a[ke],d[ke]),a._dayOfYear>ga(e)&&(j(a)._overflowDayOfYear=!0),c=fa(e,0,a._dayOfYear),a._a[le]=c.getUTCMonth(),a._a[me]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=f[b]=d[b];for(;7>b;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b];24===a._a[ne]&&0===a._a[oe]&&0===a._a[pe]&&0===a._a[qe]&&(a._nextDay=!0,a._a[ne]=0),a._d=(a._useUTC?fa:ea).apply(null,f),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[ne]=24)}}function ua(a){var b,c,d,e,f,g,h;b=a._w,null!=b.GG||null!=b.W||null!=b.E?(f=1,g=4,c=ra(b.GG,a._a[ke],ja(Da(),1,4).year),d=ra(b.W,1),e=ra(b.E,1)):(f=a._locale._week.dow,g=a._locale._week.doy,c=ra(b.gg,a._a[ke],ja(Da(),f,g).year),d=ra(b.w,1),null!=b.d?(e=b.d,f>e&&++d):e=null!=b.e?b.e+f:f),h=pa(c,d,e,g,f),a._a[ke]=h.year,a._dayOfYear=h.dayOfYear}function va(b){if(b._f===a.ISO_8601)return void ca(b);b._a=[],j(b).empty=!0;var c,d,e,f,g,h=""+b._i,i=h.length,k=0;for(e=L(b._f,b._locale).match(Sd)||[],c=0;c0&&j(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),k+=d.length),Vd[f]?(d?j(b).empty=!1:j(b).unusedTokens.push(f),S(f,d,b)):b._strict&&!d&&j(b).unusedTokens.push(f);j(b).charsLeftOver=i-k,h.length>0&&j(b).unusedInput.push(h),j(b).bigHour===!0&&b._a[ne]<=12&&b._a[ne]>0&&(j(b).bigHour=void 0),b._a[ne]=wa(b._locale,b._a[ne],b._meridiem),ta(b),$(b)}function wa(a,b,c){var d;return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&12>b&&(b+=12),d||12!==b||(b=0),b):b}function xa(a){var b,c,d,e,f;if(0===a._f.length)return j(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;ef)&&(d=f,c=b));g(a,c||b)}function ya(a){if(!a._d){var b=B(a._i);a._a=[b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],ta(a)}}function za(a){var b=new n($(Aa(a)));return b._nextDay&&(b.add(1,"d"),b._nextDay=void 0),b}function Aa(a){var b=a._i,e=a._f;return a._locale=a._locale||y(a._l),null===b||void 0===e&&""===b?l({nullInput:!0}):("string"==typeof b&&(a._i=b=a._locale.preparse(b)),o(b)?new n($(b)):(c(e)?xa(a):e?va(a):d(b)?a._d=b:Ba(a),a))}function Ba(b){var f=b._i;void 0===f?b._d=new Date:d(f)?b._d=new Date(+f):"string"==typeof f?da(b):c(f)?(b._a=e(f.slice(0),function(a){return parseInt(a,10)}),ta(b)):"object"==typeof f?ya(b):"number"==typeof f?b._d=new Date(f):a.createFromInputFallback(b)}function Ca(a,b,c,d,e){var f={};return"boolean"==typeof c&&(d=c,c=void 0),f._isAMomentObject=!0,f._useUTC=f._isUTC=e,f._l=c,f._i=a,f._f=b,f._strict=d,za(f)}function Da(a,b,c,d){return Ca(a,b,c,d,!1)}function Ea(a,b){var d,e;if(1===b.length&&c(b[0])&&(b=b[0]),!b.length)return Da();for(d=b[0],e=1;ea&&(a=-a,c="-"),c+G(~~(a/60),2)+b+G(~~a%60,2)})}function Ka(a){var b=(a||"").match(fe)||[],c=b[b.length-1]||[],d=(c+"").match(Ce)||["-",0,0],e=+(60*d[1])+q(d[2]);return"+"===d[0]?e:-e}function La(b,c){var e,f;return c._isUTC?(e=c.clone(),f=(o(b)||d(b)?+b:+Da(b))-+e,e._d.setTime(+e._d+f),a.updateOffset(e,!1),e):Da(b).local()}function Ma(a){return 15*-Math.round(a._d.getTimezoneOffset()/15)}function Na(b,c){var d,e=this._offset||0;return null!=b?("string"==typeof b&&(b=Ka(b)),Math.abs(b)<16&&(b=60*b),!this._isUTC&&c&&(d=Ma(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?bb(this,Ya(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?e:Ma(this)}function Oa(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Pa(a){return this.utcOffset(0,a)}function Qa(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Ma(this),"m")),this}function Ra(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(Ka(this._i)),this}function Sa(a){return a=a?Da(a).utcOffset():0,(this.utcOffset()-a)%60===0}function Ta(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Ua(){if("undefined"!=typeof this._isDSTShifted)return this._isDSTShifted;var a={};if(m(a,this),a=Aa(a),a._a){var b=a._isUTC?h(a._a):Da(a._a);this._isDSTShifted=this.isValid()&&r(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Va(){return!this._isUTC}function Wa(){return this._isUTC}function Xa(){return this._isUTC&&0===this._offset}function Ya(a,b){var c,d,e,g=a,h=null;return Ia(a)?g={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(g={},b?g[b]=a:g.milliseconds=a):(h=De.exec(a))?(c="-"===h[1]?-1:1,g={y:0,d:q(h[me])*c,h:q(h[ne])*c,m:q(h[oe])*c,s:q(h[pe])*c,ms:q(h[qe])*c}):(h=Ee.exec(a))?(c="-"===h[1]?-1:1,g={y:Za(h[2],c),M:Za(h[3],c),d:Za(h[4],c),h:Za(h[5],c),m:Za(h[6],c),s:Za(h[7],c),w:Za(h[8],c)}):null==g?g={}:"object"==typeof g&&("from"in g||"to"in g)&&(e=_a(Da(g.from),Da(g.to)),g={},g.ms=e.milliseconds,g.M=e.months),d=new Ha(g),Ia(a)&&f(a,"_locale")&&(d._locale=a._locale),d}function Za(a,b){var c=a&&parseFloat(a.replace(",","."));return(isNaN(c)?0:c)*b}function $a(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function _a(a,b){var c;return b=La(b,a),a.isBefore(b)?c=$a(a,b):(c=$a(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c}function ab(a,b){return function(c,d){var e,f;return null===d||isNaN(+d)||(ba(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period)."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Ya(c,d),bb(this,e,a),this}}function bb(b,c,d,e){var f=c._milliseconds,g=c._days,h=c._months;e=null==e?!0:e,f&&b._d.setTime(+b._d+f*d),g&&E(b,"Date",D(b,"Date")+g*d),h&&X(b,D(b,"Month")+h*d),e&&a.updateOffset(b,g||h)}function cb(a,b){var c=a||Da(),d=La(c,this).startOf("day"),e=this.diff(d,"days",!0),f=-6>e?"sameElse":-1>e?"lastWeek":0>e?"lastDay":1>e?"sameDay":2>e?"nextDay":7>e?"nextWeek":"sameElse";return this.format(b&&b[f]||this.localeData().calendar(f,this,Da(c)))}function db(){return new n(this)}function eb(a,b){var c;return b=A("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=o(a)?a:Da(a),+this>+a):(c=o(a)?+a:+Da(a),c<+this.clone().startOf(b))}function fb(a,b){var c;return b=A("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=o(a)?a:Da(a),+a>+this):(c=o(a)?+a:+Da(a),+this.clone().endOf(b)b-f?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)}function kb(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function lb(){var a=this.clone().utc();return 0b;b++)if(this._weekdaysParse[b]||(c=Da([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b}function Pb(a){var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Kb(a,this.localeData()),this.add(a-b,"d")):b}function Qb(a){var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function Rb(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)}function Sb(a,b){H(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})}function Tb(a,b){return b._meridiemParse}function Ub(a){return"p"===(a+"").toLowerCase().charAt(0)}function Vb(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Wb(a,b){b[qe]=q(1e3*("0."+a))}function Xb(){return this._isUTC?"UTC":""}function Yb(){return this._isUTC?"Coordinated Universal Time":""}function Zb(a){return Da(1e3*a)}function $b(){return Da.apply(null,arguments).parseZone()}function _b(a,b,c){var d=this._calendar[a];return"function"==typeof d?d.call(b,c):d}function ac(a){var b=this._longDateFormat[a],c=this._longDateFormat[a.toUpperCase()];return b||!c?b:(this._longDateFormat[a]=c.replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a])}function bc(){return this._invalidDate}function cc(a){return this._ordinal.replace("%d",a)}function dc(a){return a}function ec(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)}function fc(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)}function gc(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function hc(a,b,c,d){var e=y(),f=h().set(d,b);return e[c](f,a)}function ic(a,b,c,d,e){if("number"==typeof a&&(b=a,a=void 0),a=a||"",null!=b)return hc(a,b,c,e);var f,g=[];for(f=0;d>f;f++)g[f]=hc(a,f,c,e);return g}function jc(a,b){return ic(a,b,"months",12,"month")}function kc(a,b){return ic(a,b,"monthsShort",12,"month")}function lc(a,b){return ic(a,b,"weekdays",7,"day")}function mc(a,b){return ic(a,b,"weekdaysShort",7,"day")}function nc(a,b){return ic(a,b,"weekdaysMin",7,"day")}function oc(){var a=this._data;return this._milliseconds=_e(this._milliseconds),this._days=_e(this._days),this._months=_e(this._months),a.milliseconds=_e(a.milliseconds),a.seconds=_e(a.seconds),a.minutes=_e(a.minutes),a.hours=_e(a.hours),a.months=_e(a.months),a.years=_e(a.years),this}function pc(a,b,c,d){var e=Ya(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()}function qc(a,b){return pc(this,a,b,1)}function rc(a,b){return pc(this,a,b,-1)}function sc(a){return 0>a?Math.floor(a):Math.ceil(a)}function tc(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data;return f>=0&&g>=0&&h>=0||0>=f&&0>=g&&0>=h||(f+=864e5*sc(vc(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=p(f/1e3),i.seconds=a%60,b=p(a/60),i.minutes=b%60,c=p(b/60),i.hours=c%24,g+=p(c/24),e=p(uc(g)),h+=e,g-=sc(vc(e)),d=p(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function uc(a){return 4800*a/146097}function vc(a){return 146097*a/4800}function wc(a){var b,c,d=this._milliseconds;if(a=A(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+uc(b),"month"===a?c:c/12;switch(b=this._days+Math.round(vc(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3;case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}}function xc(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*q(this._months/12)}function yc(a){return function(){return this.as(a)}}function zc(a){return a=A(a),this[a+"s"]()}function Ac(a){return function(){return this._data[a]}}function Bc(){return p(this.days()/7)}function Cc(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function Dc(a,b,c){var d=Ya(a).abs(),e=qf(d.as("s")),f=qf(d.as("m")),g=qf(d.as("h")),h=qf(d.as("d")),i=qf(d.as("M")),j=qf(d.as("y")),k=e0,k[4]=c,Cc.apply(null,k)}function Ec(a,b){return void 0===rf[a]?!1:void 0===b?rf[a]:(rf[a]=b,!0)}function Fc(a){var b=this.localeData(),c=Dc(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function Gc(){var a,b,c,d=sf(this._milliseconds)/1e3,e=sf(this._days),f=sf(this._months);a=p(d/60),b=p(a/60),d%=60,a%=60,c=p(f/12),f%=12;var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(0>m?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"} //! moment.js locale configuration //! locale : belarusian (be) diff --git a/src/qlik-date-picker.js b/src/qlik-date-picker.js index 6fa0100..0811c08 100644 --- a/src/qlik-date-picker.js +++ b/src/qlik-date-picker.js @@ -5,216 +5,236 @@ * Copyrights licensed under the terms of the MIT license. * Original source */ -define(["qlik", "jquery", "./lib/moment.min", "./calendar-settings", "css!./lib/daterangepicker.css", "./lib/daterangepicker" -], - function (qlik, $, moment, CalendarSettings) { - 'use strict'; - function createDate(num) { - return moment((num - 25569) * 86400 * 1000).utc().format("YYYYMMDD").toString(); +define([ + "qlik", + "jquery", + "./lib/moment.min", + "./calendar-settings", + "./lib/daterangepicker.less", + "./lib/daterangepicker" +], function (qlik, $, moment, CalendarSettings) { + 'use strict'; + + function createDate(num) { + return moment((num - 25569) * 86400 * 1000).utc().format("YYYYMMDD").toString(); + } + + function createMoment(str, format) { + if (isNaN(str)) { + return moment.utc(str, format); + } + return moment.utc(createDate(str), 'YYYYMMDD'); + } + + function createRanges(props) { + var ranges = {}; + ranges[props.today] = [ + moment().startOf('day'), + moment().startOf('day')]; + ranges[props.yesterday] = [ + moment().subtract(1, 'days').startOf('day'), + moment().subtract(1, 'days').startOf('day')]; + ranges[props.lastXDays.replace("$", "7")] = [ + moment().subtract(6, 'days').startOf('day'), + moment().startOf('day')]; + ranges[props.lastXDays.replace("$", "30")] = [ + moment().subtract(29, 'days').startOf('day'), + moment().startOf('day')]; + ranges[props.thisMonth] = [ + moment().startOf('month').startOf('day'), + moment().endOf('month').startOf('day')]; + ranges[props.lastMonth] = [ + moment().subtract(1, 'month').startOf('month').startOf('day'), + moment().subtract(1, 'month').endOf('month').startOf('day')]; + return ranges; + } + function createDateStates(pages, sortAscending) { + var dateStates = {}; + pages.forEach(function (page) { + page.qMatrix.forEach(function (row) { + var d = createDate(row[0].qNum); + dateStates[d] = row[0].qState; + //based on order numerically + if (row[0].qState === 'S') { + if (sortAscending) { + dateStates.rangeStart = dateStates.rangeStart || row[0].qNum; + dateStates.rangeEnd = row[0].qNum; + } else { + dateStates.rangeEnd = dateStates.rangeEnd || row[0].qNum; + dateStates.rangeStart = row[0].qNum; + } } - function createMoment(str, format) { - if (isNaN(str)) { - return moment.utc(str, format); - } - return moment.utc(createDate(str), 'YYYYMMDD'); + }); + }); + return dateStates; + } + function createHtml(dateStates, DateFormat, props) { + var html = `
+
+  `; + if (dateStates.rangeStart) { + html += createMoment(dateStates.rangeStart).format(DateFormat); + if (dateStates.rangeEnd && (dateStates.rangeEnd !== dateStates.rangeStart)) { + html += props.separator + createMoment(dateStates.rangeEnd).format(DateFormat); + } + } else { + html += props.defaultText; + } + html += `
`; + return html; + } + return { + methods: { //for testability + createDate: createDate, + createMoment: createMoment, + createRanges: createRanges, + createDateStates: createDateStates, + createHtml: createHtml + }, + initialProperties: { + version: 1.0, + qListObjectDef: { + qDef: { + autoSort: false, + qSortCriterias: [ + { qSortByNumeric: -1 }, + { qSortByState: 1 }, + ], + }, + qShowAlternatives: true, + qFrequencyMode: "V", + qInitialDataFetch: [{ + qWidth: 1, + qHeight: 10000 + }] + }, + advanced: false + }, + // Prevent conversion from and to this object + exportProperties: null, + importProperties: null, + definition: CalendarSettings, + support: { + snapshot: false, + export: false, + exportData: false + }, + paint: function ($element, layout) { + var self = this; + var interactionState = this.$scope.options.interactionState; + var noSelections = this.options.noSelections === true; + + // old sort order was ascending, check to see if the object was created before the change + // to calcuate the range start and end dates in the createDateStates + var sortAscending = layout + && layout.qListObject + && layout.qListObject.qSortCriterias + && layout.qListObject.qSortCriterias.qSortByNumeric == "1"; + + function canInteract() { + return interactionState === 1; + } + this.dateStates = createDateStates(layout.qListObject.qDataPages, sortAscending); + if (!self.app) { + self.app = qlik.currApp(this); + } + + //console.log('dateStates', this.dateStates); + var qlikDateFormat = layout.qListObject.qDimensionInfo.qNumFormat.qFmt + || self.app.model.layout.qLocaleInfo.qDateFmt; + var outDateFormat = layout.props.format || qlikDateFormat; + moment.locale(layout.props.locale); + var minDate = createMoment(layout.props.minDate, qlikDateFormat); + var maxDate = createMoment(layout.props.maxDate, qlikDateFormat); + var startDate = createMoment(layout.props.startDate, qlikDateFormat); + var endDate = createMoment(layout.props.endDate, qlikDateFormat); + + $('#dropDown_' + layout.qInfo.qId).remove(); + + $element.html(createHtml(this.dateStates, outDateFormat, layout.props)); + + var config = { + singleDatePicker: layout.props.isSingleDate, + preventSelections: noSelections, + "locale": { + "format": outDateFormat, + "separator": layout.props.separator + }, + "parentEl": "#grid", + "autoUpdateInput": false, + "autoApply": true, + "opens": $element.offset().left < 500 ? "right" : "left", + "id": layout.qInfo.qId, + getClass: function (date) { + var d = date.format('YYYYMMDD'); + if (self.dateStates[d]) { + return 'state' + self.dateStates[d]; + } + return 'nodata'; } - function createRanges(props) { - var ranges = {}; - ranges[props.today] = [moment().startOf('day'), moment().startOf('day')]; - ranges[props.yesterday] = [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').startOf('day')]; - ranges[props.lastXDays.replace("$", "7")] = [moment().subtract(6, 'days').startOf('day'), moment().startOf('day')]; - ranges[props.lastXDays.replace("$", "30")] = [moment().subtract(29, 'days').startOf('day'), moment().startOf('day')]; - ranges[props.thisMonth] = [moment().startOf('month').startOf('day'), moment().endOf('month').startOf('day')]; - ranges[props.lastMonth] = [moment().subtract(1, 'month').startOf('month').startOf('day'), moment().subtract(1, 'month').endOf('month').startOf('day')]; - return ranges; - } - function createDateStates(pages, sortAscending) { - var dateStates = {}; - pages.forEach(function (page) { - page.qMatrix.forEach(function (row) { - var d = createDate(row[0].qNum); - dateStates[d] = row[0].qState; - //based on order numerically - if (row[0].qState === 'S') { - if(sortAscending) { - dateStates.rangeStart = dateStates.rangeStart || row[0].qNum; - dateStates.rangeEnd = row[0].qNum; - } else { - dateStates.rangeEnd = dateStates.rangeEnd || row[0].qNum; - dateStates.rangeStart = row[0].qNum; - } - } - }); - }); - return dateStates; - } - function createHtml(dateStates, DateFormat, props) { - var html = '
' - html += '
'; - html += '  '; - if (dateStates.rangeStart) { - html += createMoment(dateStates.rangeStart).format(DateFormat); - if (dateStates.rangeEnd && (dateStates.rangeEnd !== dateStates.rangeStart)) { - html += props.separator + createMoment(dateStates.rangeEnd).format(DateFormat); - } - } else { - html += props.defaultText; - } - html += ' '; - html += '
'; - html += '
'; - return html; - } - return { - methods: { //for testability - createDate: createDate, - createMoment: createMoment, - createRanges: createRanges, - createDateStates: createDateStates, - createHtml:createHtml - }, - initialProperties: { - version: 1.0, - qListObjectDef: { - qDef: { - autoSort: false, - qSortCriterias: [ - {qSortByNumeric: -1}, - {qSortByState: 1}, - ], - }, - qShowAlternatives: true, - qFrequencyMode: "V", - qInitialDataFetch: [{ - qWidth: 1, - qHeight: 10000 - }] - }, - advanced: false - }, - // Prevent conversion from and to this object - exportProperties: null, - importProperties: null, - definition: CalendarSettings, - support: { - snapshot: false, - export: false, - exportData: false - }, - paint: function ($element, layout) { - var self = this; - var interactionState = this._interactionState; - var noSelections = this.options.noSelections === true; - - // old sort order was ascending, check to see if the object was created before the change - // to calcuate the range start and end dates in the createDateStates - var sortAscending = layout && layout.qListObject && layout.qListObject.qSortCriterias && - layout.qListObject.qSortCriterias.qSortByNumeric == "1"; - - function canInteract() { - return interactionState === 1; - } - this.dateStates = createDateStates(layout.qListObject.qDataPages, sortAscending); - if (!self.app) { - self.app = qlik.currApp(this); - } - - //console.log('dateStates', this.dateStates); - var qlikDateFormat = layout.qListObject.qDimensionInfo.qNumFormat.qFmt - || self.app.model.layout.qLocaleInfo.qDateFmt; - var outDateFormat = layout.props.format || qlikDateFormat; - moment.locale(layout.props.locale); - var minDate = createMoment(layout.props.minDate, qlikDateFormat); - var maxDate = createMoment(layout.props.maxDate, qlikDateFormat); - var startDate = createMoment(layout.props.startDate, qlikDateFormat); - var endDate = createMoment(layout.props.endDate, qlikDateFormat); - - $('#dropDown_' + layout.qInfo.qId).remove(); - - $element.html(createHtml(this.dateStates, outDateFormat, layout.props)); - - var config = { - singleDatePicker: layout.props.isSingleDate, - preventSelections: noSelections, - "locale": { - "format": outDateFormat, - "separator": layout.props.separator - }, - "parentEl": "#grid", - "autoUpdateInput": false, - "autoApply": true, - "opens": $element.offset().left < 500 ? "right" : "left", - "id": layout.qInfo.qId, - getClass: function (date) { - var d = date.format('YYYYMMDD'); - if (self.dateStates[d]) { - return 'state' + self.dateStates[d]; - } - return 'nodata'; - } - }; - - if (minDate.isValid()) { - config.minDate = minDate; - } - - if (maxDate.isValid()) { - config.maxDate = maxDate; - } - - if (startDate.isValid()) { - config.startDate = startDate; - } else { - config.startDate = config.minDate; - } - - if (endDate.isValid()) { - config.endDate = endDate; - } else { - config.endDate = config.maxDate; - } - - if (layout.props.CustomRangesEnabled) { - config.locale.customRangeLabel = layout.props.customRangeLabel; - config.ranges = createRanges(layout.props); - } - - if (canInteract()) { - $element.find('.show-range').qlikdaterangepicker(config, function (pickStart, pickEnd, label) { - if (!noSelections && pickStart.isValid() && pickEnd.isValid()) { - var dateBinarySearch = function(seachDate, lowIndex, highIndex) { - if (lowIndex === highIndex) { - return lowIndex; - } - - var middleIndex = lowIndex + Math.ceil((highIndex - lowIndex) / 2); - var middleDate = moment.utc( - layout.qListObject.qDataPages[0].qMatrix[middleIndex][0].qText, - qlikDateFormat); - - // The matrix stores the dates from latest to earliest, so if the - // sought date is after the middle date, pick the lower index span - if (seachDate.isAfter(middleDate)) { - return dateBinarySearch(seachDate, lowIndex, middleIndex - 1); - } - return dateBinarySearch(seachDate, middleIndex, highIndex); - }; - - var lastIndex = layout.qListObject.qDataPages[0].qMatrix.length - 1; - // Elements are stored in reverse order, so pick out index of end first - var lowIndex = dateBinarySearch(pickEnd.startOf('day'), 0, lastIndex); - // Index of start is guaranteed to be >= index of end - var highIndex = dateBinarySearch(pickStart, lowIndex, lastIndex); - - var qElemNumbers = layout.qListObject.qDataPages[0].qMatrix - .slice(lowIndex, highIndex + 1).map(function (fieldValue) { - return fieldValue[0].qElemNumber; - }); - - self.backendApi.selectValues(0, qElemNumbers, false); - } - }); - } - } - }; - }); \ No newline at end of file + }; + + if (minDate.isValid()) { + config.minDate = minDate; + } + + if (maxDate.isValid()) { + config.maxDate = maxDate; + } + + if (startDate.isValid()) { + config.startDate = startDate; + } else { + config.startDate = config.minDate; + } + + if (endDate.isValid()) { + config.endDate = endDate; + } else { + config.endDate = config.maxDate; + } + + if (layout.props.CustomRangesEnabled) { + config.locale.customRangeLabel = layout.props.customRangeLabel; + config.ranges = createRanges(layout.props); + } + + if (canInteract()) { + $element.find('.show-range').qlikdaterangepicker(config, function (pickStart, pickEnd, label) { + if (!noSelections && pickStart.isValid() && pickEnd.isValid()) { + var dateBinarySearch = function (seachDate, lowIndex, highIndex) { + if (lowIndex === highIndex) { + return lowIndex; + } + + var middleIndex = lowIndex + Math.ceil((highIndex - lowIndex) / 2); + var middleDate = moment.utc( + layout.qListObject.qDataPages[0].qMatrix[middleIndex][0].qText, + qlikDateFormat); + + // The matrix stores the dates from latest to earliest, so if the + // sought date is after the middle date, pick the lower index span + if (seachDate.isAfter(middleDate)) { + return dateBinarySearch(seachDate, lowIndex, middleIndex - 1); + } + return dateBinarySearch(seachDate, middleIndex, highIndex); + }; + + var lastIndex = layout.qListObject.qDataPages[0].qMatrix.length - 1; + // Elements are stored in reverse order, so pick out index of end first + var lowIndex = dateBinarySearch(pickEnd.startOf('day'), 0, lastIndex); + // Index of start is guaranteed to be >= index of end + var highIndex = dateBinarySearch(pickStart, lowIndex, lastIndex); + + var qElemNumbers = layout.qListObject.qDataPages[0].qMatrix + .slice(lowIndex, highIndex + 1).map(function (fieldValue) { + return fieldValue[0].qElemNumber; + }); + + self.backendApi.selectValues(0, qElemNumbers, false); + } + }); + } + } + }; +}); \ No newline at end of file diff --git a/stylelint.config.js b/stylelint.config.js new file mode 100644 index 0000000..2cdb4a0 --- /dev/null +++ b/stylelint.config.js @@ -0,0 +1,35 @@ +"use strict"; + +module.exports = { + rules: { + "at-rule-no-unknown": true, + "block-no-empty": true, + "color-no-invalid-hex": true, + "comment-no-empty": true, + "declaration-block-no-duplicate-properties": [ + true, + { + ignore: ["consecutive-duplicates-with-different-values"] + } + ], + "declaration-block-no-shorthand-property-overrides": true, + "font-family-no-duplicate-names": true, + "font-family-no-missing-generic-family-keyword": true, + "function-calc-no-unspaced-operator": true, + "function-linear-gradient-no-nonstandard-direction": true, + "keyframe-declaration-no-important": true, + "media-feature-name-no-unknown": true, + "no-descending-specificity": true, + "no-duplicate-at-import-rules": true, + "no-duplicate-selectors": true, + "no-empty-source": true, + "no-extra-semicolons": true, + "no-invalid-double-slash-comments": true, + "property-no-unknown": true, + "selector-pseudo-class-no-unknown": true, + "selector-pseudo-element-no-unknown": true, + "selector-type-no-unknown": true, + "string-no-newline": true, + "unit-no-unknown": true + } +}; diff --git a/test/aw.config.js b/test/aw.config.js deleted file mode 100644 index 3332d7f..0000000 --- a/test/aw.config.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - url: 'http://localhost:9676/test/index.html', - glob: ['test/unit/**/*.spec.js'] -}; \ No newline at end of file diff --git a/test/index.html b/test/index.html deleted file mode 100644 index 6d30684..0000000 --- a/test/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - Test date-picker - - - - - - - \ No newline at end of file diff --git a/test/requirejs-config.js b/test/requirejs-config.js deleted file mode 100644 index 921c238..0000000 --- a/test/requirejs-config.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -requirejs.config({ - baseUrl: './', - paths: { - angular: './../node_modules/angular/angular', - chai: './../node_modules/chai/chai', - jquery: './../node_modules/jquery/dist/jquery', - qlik: './stubs/qlik.stub', - css: './../node_modules/require-css/css' - }, - shim: { - angular: { - deps: ['jquery'], - exports: 'angular' - }, - qlik: { - deps: ['angular'] - } - } -}); \ No newline at end of file diff --git a/test/requirejs-main.js b/test/requirejs-main.js deleted file mode 100644 index 6bdf15e..0000000 --- a/test/requirejs-main.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -(function init() { // eslint-disable-line func-names - function run() { - requirejs(window.awFiles, function () { - mocha.run(); - }); - } - requirejs(['./requirejs-config'], run); -})(); \ No newline at end of file diff --git a/test/stubs/qlik.stub.js b/test/stubs/qlik.stub.js deleted file mode 100644 index 2c53e9f..0000000 --- a/test/stubs/qlik.stub.js +++ /dev/null @@ -1,24 +0,0 @@ -define([], function () { - 'use strict'; - - return { - currApp: function () { - return { - getAppObjectList: () => { - - }, - getStoryList: () => { - - }, - getList: () => {} - }; - }, - getGlobal: () => { - return { - getAppList: () => { - return Promise.resolve(); - } - }; - } - }; - }); \ No newline at end of file diff --git a/test/unit/qlik-date-picker.spec.js b/test/unit/qlik-date-picker.spec.js deleted file mode 100644 index 9de587c..0000000 --- a/test/unit/qlik-date-picker.spec.js +++ /dev/null @@ -1,89 +0,0 @@ -'use strict'; - -define(['chai', - '../../../src/calendar-settings', - '../../../src/qlik-date-picker'], - function (chai, calendarSettings, dateRangePicker) { - const expect = chai.expect; - - describe('properties', function () { - it('should have items', function () { - expect(calendarSettings).to.have.a.property('items'); - }); - }); - describe('dateRangePicker', function () { - it('should have a paint method', function () { - expect(dateRangePicker).to.have.a.property('paint'); - }); - }); - describe('getFieldName', function () { - it('should strip leading =', function () { - var result = dateRangePicker.methods.getFieldName("=XXX"); - expect(result).to.equal('XXX'); - }); - }); - describe('createDate', function () { - it('should convert a day number to a date string', function () { - var result = dateRangePicker.methods.createDate(41276); - expect(result).to.equal('20130102'); - }); - }); - describe('createMoment', function () { - it('should create a Moment object from a day number', function () { - var result = dateRangePicker.methods.createMoment(41276); - expect(result.format).to.be.a('function'); - }); - }); - describe('createRanges', function () { - it('should create a ranges object', function () { - var result = dateRangePicker.methods.createRanges({ - today: 'Idag', - yesterday: 'Igår', - lastXDays: 'Senaste $ dagarna', - thisMonth: 'Denna månad', - lastMonth: 'Föregående månad' - }); - expect(result).to.have.a.property('Idag'); - }); - }); - describe('createDateStates', function () { - it('should map date states', function () { - var testData = [{ - "qMatrix": [[{ "qText": "1/2/2013", "qNum": 41276, "qElemNumber": 132, "qState": "A" }], - [{ "qText": "1/3/2013", "qNum": 41277, "qElemNumber": 499, "qState": "A" }], - [{ "qText": "1/4/2013", "qNum": 41278, "qElemNumber": 501, "qState": "A" }], - [{ "qText": "1/5/2013", "qNum": 41279, "qElemNumber": 502, "qState": "A" }], - [{ "qText": "1/6/2013", "qNum": 41280, "qElemNumber": 500, "qState": "A" }], - [{ "qText": "1/7/2013", "qNum": 41281, "qElemNumber": 130, "qState": "A" }], - [{ "qText": "1/8/2013", "qNum": 41282, "qElemNumber": 142, "qState": "A" }], - [{ "qText": "1/9/2013", "qNum": 41283, "qElemNumber": 335, "qState": "A" }], - [{ "qText": "1/10/2013", "qNum": 41284, "qElemNumber": 433, "qState": "A" }], - [{ "qText": "1/11/2013", "qNum": 41285, "qElemNumber": 414, "qState": "A" }], - [{ "qText": "1/14/2013", "qNum": 41288, "qElemNumber": 406, "qState": "S", "qFrequency": "211" }], - [{ "qText": "1/15/2013", "qNum": 41289, "qElemNumber": 394, "qState": "S", "qFrequency": "265" }], - [{ "qText": "1/16/2013", "qNum": 41290, "qElemNumber": 386, "qState": "S", "qFrequency": "177" }], - [{ "qText": "1/17/2013", "qNum": 41291, "qElemNumber": 348, "qState": "S", "qFrequency": "350" }], - [{ "qText": "1/18/2013", "qNum": 41292, "qElemNumber": 108, "qState": "S", "qFrequency": "327" }], - [{ "qText": "1/21/2013", "qNum": 41295, "qElemNumber": 99, "qState": "A" }], - [{ "qText": "1/22/2013", "qNum": 41296, "qElemNumber": 365, "qState": "A" }], - [{ "qText": "1/23/2013", "qNum": 41297, "qElemNumber": 34, "qState": "A" }], - [{ "qText": "1/24/2013", "qNum": 41298, "qElemNumber": 426, "qState": "A" }]], - "qTails": [], - "qArea": { "qLeft": 0, "qTop": 0, "qWidth": 1, "qHeight": 505 } - }]; - var result = dateRangePicker.methods.createDateStates(testData); - expect(result).to.have.a.property('rangeStart'); - expect(result.rangeStart).to.be.equals(41288); - expect(result['20130114']).to.be.equal('S'); - }); - }); - describe('createHtml', function () { - it('should use defaultText if no range is available', function () { - var defaultText = 'DEFAULT TEXT'; - var result = dateRangePicker.methods.createHtml({}, 'YYYY-MM-DD', - { separator: '-', defaultText: defaultText }); - expect(result).to.include(defaultText); - }); - }); - - }); \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..5eac811 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,71 @@ +const StyleLintPlugin = require('stylelint-webpack-plugin'); +const packageJSON = require('./package.json'); +const path = require('path'); + +const DIST = path.resolve("./dist"); +const MODE = process.env.NODE_ENV || 'development'; + +console.log('Webpack mode:', MODE); // eslint-disable-line no-console + +const config = { + devtool: 'source-map', + entry: [ + './src/qlik-date-picker.js' + ], + mode: MODE, + output: { + filename: `${packageJSON.name}.js`, + libraryTarget: 'amd', + path: DIST + }, + externals: { + jquery: { + amd: 'jquery', + commonjs: 'jquery', + commonjs2: 'jquery', + root: '_' + }, + qlik: { + amd: 'qlik', + commonjs: 'qlik', + commonjs2: 'qlik', + root: '_' + } + }, + module: { + rules: [ + { + enforce: "pre", + test: /\.js$/, + exclude: /(node_modules|Library)/, + loader: "eslint-loader", + options: { + failOnError: true + } + }, + { + test: /.js$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env'] + } + } + }, + { + test: /\.html$/, + loader: "html-loader" + }, + { + test: /.(less|css)$/, + use: ['style-loader', 'css-loader', 'less-loader'], + } + ] + }, + plugins: [ + new StyleLintPlugin() + ] +}; + +module.exports = config;