From 0a64249ce65b1322a2b67ccd055d02c8272109dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Schn=C3=BCriger?= Date: Tue, 2 May 2017 14:07:46 +0200 Subject: [PATCH] Implementation of a week scale feature (#3009) * Add Gitter badge (#2179) * do not generate source-maps in distribution version * add 'dist' folder for deployment * added Badges * added codeclimate badge * added @Tooa to the support team * added badges from isitmaintained.com (#2517) * do not ignore dist and test folders in master * generated dist files for v4.18.0 * generated dist files for v4.18.1 * Cheap fix for bug #2795 * Update to PR #2826 to use newline format * changed to v4.18.1-SNAPSHOT * chore(docs): general improvements (#2652) * removed NOTICE file * updated license date range to include 2017 * chore(docs): updated support team members * chore: updated dependencies and devDependencies (#2649) * Fixes instanceof Object statements for objects from other windows and iFrames. (#2631) * Replaces instanceof Object checks with typeof to prevent cross tab issues. * Adds missing space. * chore: removed google-analytics from all examples (#2670) * chore(docs): Add note that PRs should be submitted against the `develop` branch (#2623) Related to: https://github.com/almende/vis/pull/2618 Related to: https://github.com/almende/vis/pull/2620 * feat(timeline): Change setCustomTimeTitle title parameter to be a string or a function (#2611) * change setCustomTimeTitle title parameter, Now could be an string or a function * Fixed indent and spacing * feat(timeline): refactor tooltip to only use one dom-element (#2662) * feat(network): Allow for image nodes to have a selected or broken image (#2601) * feat(tests): run mocha tests in travis ci (#2687) * Added showX(YZ)Axis options to Graph3d (#2686) * Added showX(YZ)Axis to Graph3d * Added show_Axis options to docs and playground example * Resolved merge conflict * Added show_Axis options to docs and playground example * fix(build): use babel version compatible with webpack@1.14 (#2693) fixes #2685 * feat(docs): use babel preset2015 for custom builds (#2678) * add link to a mentioned example (#2709) * chore(lint): added support for eslint (#2695) * Trivial typo fix in how_to_help doc. (#2714) * fix(timeline): #2598 Flickering onUpdateTimeTooltip (#2702) * Fix redraw order * Fix error when option is not defined * Allow template labels * Add .travis.yml file * Add experiment travis code * Fix react example * Add animation to onUpdateTooltip * fix(timeline): #778 Tooltip does not work with background items in timeline (#2703) * Fix redraw order * Fix error when option is not defined * Allow template labels * Add .travis.yml file * Add experiment travis code * Fix react example * Make items z-index default to 1 * Add initial tests for Timeline PointItem (#2716) * fix(timeline): #2679 TypeError: Cannot read property 'hasOwnProperty' of null (#2735) * Fix redraw order * Fix error when option is not defined * Allow template labels * Add .travis.yml file * Add experiment travis code * Fix react example * Fix bug in item editable * feat(timeline): #2647 Dynamic rolling mode option (#2705) * Fix redraw order * Fix error when option is not defined * Allow template labels * Add .travis.yml file * Add experiment travis code * Fix react example * Add toggleRollingMode option * Update docs with toggleRollingMode option * fixes timestep next issue (#2732) * feat(timeline): added new locales for french and espanol (#2723) * Fix eslint problem on Travis. (#2744) * fix: Range.js "event" is undeclared (#2749) * fix(timeline): #2720 Problems with option editable (#2743) Clean up and centralise edit status for Timeline Items. * feat(network): Improve the performance of the network layout engine (#2729) * Improve the performance of the network layout engine Short-cut the execution of a number of methods in LayoutEngine to make them handle highly-connected graphs better. * Demonstrations of layouts of large networks * Added support to supply an end to bar charts to have them scale (#2760) * Added support to supply an X2 to bar charts to have them scale * Fixed graph2d stacking issue. It no longer takes into account hidden items * Changed x2 to end per recommendation and added this to the docs * Initial tests for timeline ItemSet. (#2750) Somewhat more complicated setup, associated with the need for a real window. * [Timeline] Modify redraw logic to treat scroll as needing restack. (#2774) This addresses #1982 and #1417. It possibly reduces performance, but correctness seems better. * fix(timeline): #2672 Item events original event (#2704) * Fix redraw order * Fix error when option is not defined * Allow template labels * Add .travis.yml file * Add experiment travis code * Fix react example * Fix events returned from mouse events * Fix example * Rename censor to stringifyObject in example * [timeline] Update examples to use ISOString format. (#2791) Resolves #2790 * [timeline] Update serialization example to use ISOString dates. (#2789) Resolves #2696 * added github templates for issues and pull-requests (#2787) fixes #2418 * feat(timeline): Add item data as argument to the template function (#2799) * Fix regression introduced in #2743. (#2796) * Fix for issue #2536 (#2803) * Fix for issue #2536 * Adjusted documentation for fix. * Adjustments due to review * Grrrrr whitespace * Fixed Travis issue * Cheap fix for bug #2795 * Update to PR #2826 to use newline format * Update to gitignore to reflect changes on remote * clean dist folder * Local gitignore update * Just a first example file for the week scale feature * Allowing sourcemap creation * Initial (non-functional) commit to ensure we insert code at the right places (check TODOs) * Functional, not bug-free version which works with locale aware week numbers. * Locale-aware implementation and simplified major labels to a full year * Trying to make the major labels show the correct start date * Working implementation of week numbers using localization. * removing development leftovers * Updated package.json * Reintagrate package.json from accidental deletion * Updates for package.json * Fixing package.json * Integrate the week numbers feature in the documentation. * Reverting local changes to .gitignore * Reverting local changes * Extending examples to cover the case when 1st day of week and 1st day of month align; Fixing display bug so that week numbers are not repeated in minorLabels * Putting the examples into a loop --- docs/timeline/index.html | 13 ++- examples/timeline/styling/weekStyling.html | 111 +++++++++++++++++++++ lib/timeline/TimeStep.js | 59 ++++++++++- lib/timeline/component/TimeAxis.js | 6 +- lib/timeline/optionsTimeline.js | 6 +- 5 files changed, 187 insertions(+), 8 deletions(-) create mode 100644 examples/timeline/styling/weekStyling.html diff --git a/docs/timeline/index.html b/docs/timeline/index.html index 81e40f97..374b9f97 100644 --- a/docs/timeline/index.html +++ b/docs/timeline/index.html @@ -612,6 +612,7 @@ function (option, path) { hour: 'HH:mm', weekday: 'ddd D', day: 'D', + week: 'w', month: 'MMM', year: 'YYYY' }, @@ -622,6 +623,7 @@ function (option, path) { hour: 'ddd D MMMM', weekday: 'MMMM YYYY', day: 'MMMM YYYY', + week: 'MMMM YYYY', month: 'YYYY', year: '' } @@ -1044,7 +1046,7 @@ function (option, path) { function When moving items on the Timeline, they will be snapped to nice dates like full hours or days, depending on the current scale. The snap function can be replaced with a custom function, or can be set to null to disable snapping. The signature of the snap function is:
function snap(date: Date, scale: string, step: number) : Date or number
- The parameter scale can be can be 'millisecond', 'second', 'minute', 'hour', 'weekday, 'day, 'month, or 'year'. The parameter step is a number like 1, 2, 4, 5. + The parameter scale can be can be 'millisecond', 'second', 'minute', 'hour', 'weekday, 'week', 'day, 'month, or 'year'. The parameter step is a number like 1, 2, 4, 5. @@ -1088,10 +1090,11 @@ function (option, path) { timeAxis.scale String none - Set a fixed scale for the time axis of the Timeline. Choose from 'millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'month', 'year'. Example usage: + Set a fixed scale for the time axis of the Timeline. Choose from 'millisecond', 'second', 'minute', 'hour', 'weekday', 'week', 'day', 'month', 'year'. Example usage:
var options = {
   timeAxis: {scale: 'minute', step: 5}
 }
+

Note: The 'week' scale only works properly when locales are enabled.

@@ -2023,6 +2026,9 @@ var options = { Daysvis-day1, vis-day2, ..., vis-day31 + + Weekvis-week1, vis-week2, ..., vis-week53 + Monthsvis-january, vis-february, vis-march, vis-april, vis-may, vis-june, vis-july, vis-august, vis-september, vis-october, vis-november, vis-december @@ -2030,6 +2036,9 @@ var options = { Yearsvis-year2014, vis-year2015, ... +

+ Note: the 'week' scale is not included in the automatic zoom levels as its scale is not a direct logical successor of 'days' nor a logical predecessor of 'months' +

Examples:

diff --git a/examples/timeline/styling/weekStyling.html b/examples/timeline/styling/weekStyling.html new file mode 100644 index 00000000..9a3a919e --- /dev/null +++ b/examples/timeline/styling/weekStyling.html @@ -0,0 +1,111 @@ + + + + Timeline | Grid styling + + + + + + + + + + +

+ Week numbers are calculated based on locales. For this to properly work, the timeline must be loaded with a version + of moment.js including locales.

+

To set a locale for the timeline, specify the option + {locale: STRING}.

+

To set the scale to use week numbers, use for example {scale: 'week', step: 1}.

+

The following timeline is initialized with the 'de' locale and items are added with locally localized moment.js + objects. The timeline locale can be switched to another locale at runtime. If you choose the 'en' locale, the week + numbers will be calculated according to the US week calendar numbering scheme.

+ +

+ + +

+ +
+ + + + \ No newline at end of file diff --git a/lib/timeline/TimeStep.js b/lib/timeline/TimeStep.js index e3f6ff7b..530e1726 100644 --- a/lib/timeline/TimeStep.js +++ b/lib/timeline/TimeStep.js @@ -69,6 +69,7 @@ TimeStep.FORMAT = { hour: 'HH:mm', weekday: 'ddd D', day: 'D', + week: 'w', month: 'MMM', year: 'YYYY' }, @@ -79,6 +80,7 @@ TimeStep.FORMAT = { hour: 'ddd D MMMM', weekday: 'MMMM YYYY', day: 'MMMM YYYY', + week: 'MMMM YYYY', month: 'YYYY', year: '' } @@ -101,7 +103,7 @@ TimeStep.prototype.setMoment = function (moment) { /** * Set custom formatting for the minor an major labels of the TimeStep. * Both `minorLabels` and `majorLabels` are an Object with properties: - * 'millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'month', 'year'. + * 'millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'week', 'month', 'year'. * @param {{minorLabels: Object, majorLabels: Object}} format */ TimeStep.prototype.setFormat = function (format) { @@ -153,6 +155,7 @@ TimeStep.prototype.roundToMinor = function() { this.current.year(this.step * Math.floor(this.current.year() / this.step)); this.current.month(0); case 'month': this.current.date(1); + case 'week': this.current.weekday(0); case 'day': // intentional fall through case 'weekday': this.current.hours(0); case 'hour': this.current.minutes(0); @@ -170,6 +173,7 @@ TimeStep.prototype.roundToMinor = function() { case 'hour': this.current.subtract(this.current.hours() % this.step, 'hours'); break; case 'weekday': // intentional fall through case 'day': this.current.subtract((this.current.date() - 1) % this.step, 'day'); break; + case 'week': this.current.subtract(this.current.week() % this.step, 'week'); break; case 'month': this.current.subtract(this.current.month() % this.step, 'month'); break; case 'year': this.current.subtract(this.current.year() % this.step, 'year'); break; default: break; @@ -210,6 +214,21 @@ TimeStep.prototype.next = function() { break; case 'weekday': // intentional fall through case 'day': this.current.add(this.step, 'day'); break; + case 'week': + if (this.current.weekday() !== 0){ // we had a month break not correlating with a week's start before + this.current.weekday(0); // switch back to week cycles + this.current.add(this.step, 'week'); + } else { // first day of the week + var nextWeek = this.current.clone(); + nextWeek.add(1, 'week'); + if(nextWeek.isSame(this.current, 'month')){ // is the first day of the next week in the same month? + this.current.add(this.step, 'week'); // the default case + } else { // inject a step at each first day of the month + this.current.add(this.step, 'week'); + this.current.date(1); + } + } + break; case 'month': this.current.add(this.step, 'month'); break; case 'year': this.current.add(this.step, 'year'); break; default: break; @@ -224,6 +243,7 @@ TimeStep.prototype.next = function() { case 'hour': if(this.current.hours() > 0 && this.current.hours() < this.step) this.current.hours(0); break; case 'weekday': // intentional fall through case 'day': if(this.current.date() < this.step+1) this.current.date(1); break; + case 'week': if(this.current.week() < this.step) this.current.week(1); break; // week numbering starts at 1, not 0 case 'month': if(this.current.month() < this.step) this.current.month(0); break; case 'year': break; // nothing to do for year default: break; @@ -260,7 +280,7 @@ TimeStep.prototype.getCurrent = function() { * @param {{scale: string, step: number}} params * An object containing two properties: * - A string 'scale'. Choose from 'millisecond', 'second', - * 'minute', 'hour', 'weekday', 'day', 'month', 'year'. + * 'minute', 'hour', 'weekday', 'day', 'week', 'month', 'year'. * - A number 'step'. A step size, by default 1. * Choose for example 1, 2, 5, or 10. */ @@ -338,7 +358,7 @@ TimeStep.prototype.setMinimumStep = function(minimumStep) { * Static function * @param {Date} date the date to be snapped. * @param {string} scale Current scale, can be 'millisecond', 'second', - * 'minute', 'hour', 'weekday, 'day', 'month', 'year'. + * 'minute', 'hour', 'weekday, 'day', 'week', 'month', 'year'. * @param {number} step Current step (1, 2, 4, 5, ... * @return {Date} snappedDate */ @@ -370,6 +390,20 @@ TimeStep.snap = function(date, scale, step) { clone.seconds(0); clone.milliseconds(0); } + else if (scale == 'week') { + if (clone.weekday() > 2) { // doing it the momentjs locale aware way + clone.weekday(0); + clone.add(1, 'week'); + } + else { + clone.weekday(0); + } + + clone.hours(0); + clone.minutes(0); + clone.seconds(0); + clone.milliseconds(0); + } else if (scale == 'day') { //noinspection FallthroughInSwitchStatementJS switch (step) { @@ -452,6 +486,7 @@ TimeStep.prototype.isMajor = function() { switch (this.scale) { case 'year': case 'month': + case 'week': case 'weekday': case 'day': case 'hour': @@ -465,6 +500,7 @@ TimeStep.prototype.isMajor = function() { } else if (this.switchedMonth == true) { switch (this.scale) { + case 'week': case 'weekday': case 'day': case 'hour': @@ -501,6 +537,8 @@ TimeStep.prototype.isMajor = function() { case 'weekday': // intentional fall through case 'day': return (date.date() == 1); + case 'week': + return (date.date() == 1); case 'month': return (date.month() == 0); case 'year': @@ -530,7 +568,15 @@ TimeStep.prototype.getLabelMinor = function(date) { } var format = this.format.minorLabels[this.scale]; - return (format && format.length > 0) ? this.moment(date).format(format) : ''; + // noinspection FallThroughInSwitchStatementJS + switch (this.scale) { + case 'week': + if(this.isMajor() && date.weekday() !== 0){ + return ""; + } + default: + return (format && format.length > 0) ? this.moment(date).format(format) : ''; + } }; /** @@ -624,6 +670,11 @@ TimeStep.prototype.getClassName = function() { classNames.push(this.step <= 2 ? 'vis-' + current.format('dddd').toLowerCase() : ''); classNames.push(even(current.date() - 1)); break; + case 'week': + classNames.push('vis-week' + current.format('w')); + classNames.push(currentWeek(current)); + classNames.push(even(current.week())); + break; case 'month': classNames.push('vis-' + current.format('MMMM').toLowerCase()); classNames.push(currentMonth(current)); diff --git a/lib/timeline/component/TimeAxis.js b/lib/timeline/component/TimeAxis.js index f575af4b..2d1b1d18 100644 --- a/lib/timeline/component/TimeAxis.js +++ b/lib/timeline/component/TimeAxis.js @@ -227,6 +227,7 @@ TimeAxis.prototype._repaintLabels = function () { var x; var xNext; var isMajor, nextIsMajor; + var showMinorGrid; var width = 0, prevWidth; var line; var labelMinor; @@ -255,7 +256,10 @@ TimeAxis.prototype._repaintLabels = function () { prevWidth = width; width = xNext - x; - var showMinorGrid = (width >= prevWidth * 0.4); // prevent displaying of the 31th of the month on a scale of 5 days + switch (step.scale) { + case 'week': showMinorGrid = true; break; + default: showMinorGrid = (width >= prevWidth * 0.4); break; // prevent displaying of the 31th of the month on a scale of 5 days + } if (this.options.showMinorLabels && showMinorGrid) { var label = this._repaintMinorText(x, labelMinor, orientation, className); diff --git a/lib/timeline/optionsTimeline.js b/lib/timeline/optionsTimeline.js index 41ac8d0b..fb6f3c3d 100644 --- a/lib/timeline/optionsTimeline.js +++ b/lib/timeline/optionsTimeline.js @@ -54,6 +54,7 @@ let allOptions = { hour: {string,'undefined': 'undefined'}, weekday: {string,'undefined': 'undefined'}, day: {string,'undefined': 'undefined'}, + week: {string,'undefined': 'undefined'}, month: {string,'undefined': 'undefined'}, year: {string,'undefined': 'undefined'}, __type__: {object, 'function': 'function'} @@ -65,6 +66,7 @@ let allOptions = { hour: {string,'undefined': 'undefined'}, weekday: {string,'undefined': 'undefined'}, day: {string,'undefined': 'undefined'}, + week: {string,'undefined': 'undefined'}, month: {string,'undefined': 'undefined'}, year: {string,'undefined': 'undefined'}, __type__: {object, 'function': 'function'} @@ -181,6 +183,7 @@ let configureOptions = { hour: 'HH:mm', weekday: 'ddd D', day: 'D', + week: 'w', month: 'MMM', year: 'YYYY' }, @@ -191,6 +194,7 @@ let configureOptions = { hour: 'ddd D MMMM', weekday: 'MMMM YYYY', day: 'MMMM YYYY', + week: 'MMMM YYYY', month: 'YYYY', year: '' } @@ -236,7 +240,7 @@ let configureOptions = { start: '', //template: {'function': 'function'}, //timeAxis: { - // scale: ['millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'month', 'year'], + // scale: ['millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'week', 'month', 'year'], // step: [1, 1, 10, 1] //}, tooltip: {