wavesjs / waves-ui

A library to display and edit audio data and timeseries data in the browser.
http://wavesjs.github.io/waves-ui/
BSD 3-Clause "New" or "Revised" License
122 stars 16 forks source link

Test coverage failing on Master ? #36

Open justinlevi opened 6 years ago

justinlevi commented 6 years ago

Hey guys, I'm noticing a bunch of failing tests on master. Is this known? I was hoping to try and get a test to fail for the odd segment drag behavior I'm seeing but I'm a total newb when it comes to writing JS tests using browserify, testling, babel-tape-runner, etc.

Also, I really want to be working off the develop branch and the tests don't seem to be running at all??

I'm seeing the following differences in the package.json scripts

MASTER BRANCH

"test": "browserify -t [ babelify --blacklist regenerator ] tests/*/*.js |  testling -u --no-show",
    "coverage": "browserify -t [ babelify --blacklist regenerator ] -t coverify tests/*/*.js | testling -u --no-show | coverify --json -o coverage.json && node ./bin/scripts --cover-report -i coverage.json"

DEVELOP BRANCH

"test": "babel-tape-runner tests/**.test.js",

The coverage command seems to have been killed in the develop branch. The test command in develop doesn't print anything.

Also, all the tests in the develop branch still include from the es6 directory which doesn't exist.

import AnnotatedMarkerLayer from '../es6/helpers/annotated-marker-layer';


$ MASTER BRANCH yarn coverage output below

⚡ yarn coverage
yarn run v1.2.1
$ browserify -t [ babelify --blacklist regenerator ] -t coverify tests/*/*.js | testling -u --no-show | coverify --json -o coverage.json && node ./bin/scripts --cover-report -i coverage.json
http://localhost:54242/__testling?show=false

TAP version 13
# Edit Breakpoint Behavior
ok 1 should be equal
ok 2 should be equal
# MarkerBehavior
ok 3 should be equal
# SegmentBehavior
ok 4 should be equal
ok 5 should be equal
ok 6 should be equal
ok 7 should be equal
ok 8 should be equal
ok 9 should be equal
ok 10 should be equal
ok 11 should be equal
# TimeContextBehavior should edit shape accordingly
ok 12 should be equal
ok 13 should be equal
ok 14 should be equal
ok 15 should be equal
ok 16 should be equal
ok 17 should be equal
ok 18 should be equal
ok 19 should be equal
ok 20 should be equal
ok 21 should be equal
ok 22 should be equal
ok 23 should be equal
ok 24 should be equal
ok 25 should be equal
ok 26 should be equal
# TimeContextBehavior should edit shape accordingly v2
ok 27 should be equal
ok 28 should be equal
ok 29 should be equal
ok 30 should be equal
ok 31 should be equal
ok 32 should be equal
ok 33 should be equal
ok 34 should be equal
ok 35 should be equal
ok 36 should be equal
ok 37 should be equal
ok 38 should be equal
# TimeContextBehavior should stretch behavior correctly
ok 39 should be equal
ok 40 should be equal
ok 41 should be equal
ok 42 should be equal
ok 43 should be equal
ok 44 should be equal
ok 45 should be truthy
ok 46 should be equal
ok 47 should be equal
ok 48 should be equal
ok 49 should be truthy
# TraceBehavior
ok 50 should be equal
ok 51 should be equal
ok 52 should be equal
ok 53 should be equal
ok 54 should be equal
ok 55 should be equal
ok 56 should be equal
ok 57 should be equal
ok 58 should be equal
ok 59 should be equal
ok 60 should be equal
ok 61 should be equal
# LayerTimeContext get default values
ok 62 Default layerTimeContext startis 0 second
ok 63 Default layerTimeContext start is 10 seconds
ok 64 Default layerTimeContext offset 0 seconds
ok 65 Default stretchRatio is 1
# Layer with default params
ok 66 should be equal
ok 67 should be equal
# TimelineTimeContext get default values
ok 68 Initial offset is 0
ok 69 Initial pixelsPerSecond is 0
ok 70 Initial zoom is 1
ok 71 Initial visibleWidth is 1000 pixels
ok 72 Initial visibleDuration is 10 seconds
ok 73 Initial maintainVisibleDuration is false
# TimelineTimeContext set values
ok 74 visibleDuration is 1 second
ok 75 zoom is unchanged
ok 76 pps is unchanged
ok 77 0.5 seconds
# Timeline get default window view values over tracks
ok 78 Initial offset is 0
ok 79 Initial zoom is 1
ok 80 Initial pixelsPerSecond is 100
ok 81 Initial visibleWidth is 1000
ok 82 Initial visibleDuration is 10 seconds
# Timeline set window view values over tracks
ok 83 Offset is set to 10 seconds
ok 84 Zoom is set to 1
ok 85 pixelsPerSecond is set to 200
ok 86 visibleWidth is set to 2000
# Create a track from the timeline
ok 87 Timeline has one track
ok 88 The timeline track is the one just added
ok 89 should be equal
ok 90 should be equal
# Add a track from the timeline
ok 91 Can't add a track already added to the timeline
ok 92 Timeline has two tracks
ok 93 The timeline second track is the one just added
# Add a layer to a track from the timeline, and remove it
ok 94 should be equivalent
ok 95 The entire timeline has one, and just one layer
ok 96 The track we created contains one layer
ok 97 The track layer is the right one
ok 98 The entire timeline has no more layers
ok 99 The track we created doesn't contain any layer
ok 100 We can retrieve a track with a specific id
ok 101 We can get all the layers that belong to a certain group
ok 102 We can get all groupedLayers
ok 103 Can't add a track with a trackId already added
# TrackCollection methods
ok 104 should be equal
ok 105 should be equivalent
ok 106 update event is emitted
ok 107 update:containers event is emitted
ok 108 update:layers event is emitted
ok 109 render event is emitted
# Track - instanciation
ok 110 Default height is 100
ok 111 should be equal
ok 112 should be equal
ok 113 should be equal
ok 114 should be equal
ok 115 should be equal
ok 116 should be equal
ok 117 should be equal
ok 118 should be equal
ok 119 should be equal
ok 120 should be equal
ok 121 should be equal
ok 122 should be equal
ok 123 should be equal
ok 124 should be equal
ok 125 should be equal
ok 126 should be equal
ok 127 should be equal
ok 128 should be equal
ok 129 should be equal
ok 130 When set to 200, height is 200px
# Track - add/remove Layer
ok 131 should be equal
ok 132 should be equal
ok 133 should be equal
ok 134 should be equal
ok 135 should be equal
ok 136 should be equal
ok 137 should be equal
ok 138 should be equal
ok 139 should be equal
ok 140 should be equal
ok 141 should be equal
ok 142 should be equal
ok 143 should be equal
ok 144 should be equal
ok 145 should be equal
ok 146 should be equal
ok 147 should be equal
ok 148 should be equal
ok 149 should be equal
ok 150 should be equal
ok 151 should be equal
ok 152 should be equal
ok 153 should be equal
ok 154 should be equal
ok 155 should be equal
ok 156 should be equal
ok 157 should be equal
ok 158 should be equal
ok 159 should be equal
ok 160 should be equal
ok 161 should be equal
ok 162 should be equivalent
# Track - update container
ok 163 should be equal
ok 164 should be equal
ok 165 should be equal
# KeyBoard keydown
not ok 166 should be equal
  ---
    operator: equal
    expected: 'A'
    actual:   '\x00'
    at: Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:54242/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:54242/__testling?show=false:13499:32)
          at Keyboard.<anonymous> (http://localhost:54242/__testling?show=false:22104:53)
          at Keyboard.EventEmitter.emit (http://localhost:54242/__testling?show=false:9095:20)
          at HTMLDocument.onKeyDown (http://localhost:54242/__testling?show=false:18852:58)
          at Test.<anonymous> (http://localhost:54242/__testling?show=false:22131:53)
          at Test.bound [as _cb] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.run (http://localhost:54242/__testling?show=false:13518:10)
  ...
ok 167 should be equal
ok 168 should be equal
ok 169 should be equal
ok 170 should be equal
ok 171 should be equal
not ok 172 should be equal
  ---
    operator: equal
    expected: 'A'
    actual:   '\x00'
    at: Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:54242/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:54242/__testling?show=false:13499:32)
          at Keyboard.<anonymous> (http://localhost:54242/__testling?show=false:22104:53)
          at Keyboard.EventEmitter.emit (http://localhost:54242/__testling?show=false:9095:20)
          at HTMLDocument.onKeyUp (http://localhost:54242/__testling?show=false:18860:58)
          at Test.<anonymous> (http://localhost:54242/__testling?show=false:22133:53)
          at Test.bound [as _cb] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.run (http://localhost:54242/__testling?show=false:13518:10)
  ...
ok 173 should be equal
ok 174 should be equal
ok 175 should be equal
ok 176 should be equal
ok 177 should be equal
# Surface, default instance attributes
not ok 178 should be equal
  ---
    operator: equal
    expected: null
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:54242/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.<anonymous> (http://localhost:54242/__testling?show=false:22161:51)
          at Test.bound [as _cb] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.run (http://localhost:54242/__testling?show=false:13518:10)
          at Test.bound [as run] (http://localhost:54242/__testling?show=false:13499:32)
          at next (http://localhost:54242/__testling?show=false:13311:15)
          at Item.run (http://localhost:54242/__testling?show=false:10218:14)
  ...
not ok 179 should be equal
  ---
    operator: equal
    expected: null
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:54242/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.<anonymous> (http://localhost:54242/__testling?show=false:22163:51)
          at Test.bound [as _cb] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.run (http://localhost:54242/__testling?show=false:13518:10)
          at Test.bound [as run] (http://localhost:54242/__testling?show=false:13499:32)
          at next (http://localhost:54242/__testling?show=false:13311:15)
          at Item.run (http://localhost:54242/__testling?show=false:10218:14)
  ...
# Surface, trigger events
ok 180 should be equal
not ok 181 should be equal
  ---
    operator: equal
    expected: true
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:54242/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:54242/__testling?show=false:13499:32)
          at Surface.<anonymous> (http://localhost:54242/__testling?show=false:22192:53)
          at Surface.g (http://localhost:54242/__testling?show=false:9165:16)
          at Surface.EventEmitter.emit (http://localhost:54242/__testling?show=false:9073:17)
          at HTMLBodyElement.onMouseDown (http://localhost:54242/__testling?show=false:19088:58)
          at Test.<anonymous> (http://localhost:54242/__testling?show=false:22244:49)
          at Test.bound [as _cb] (http://localhost:54242/__testling?show=false:13499:32)
  ...
ok 182 should be equal
ok 183 should be equal
ok 184 should be equal
not ok 185 should be equal
  ---
    operator: equal
    expected: true
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:54242/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:54242/__testling?show=false:13499:32)
          at Surface.<anonymous> (http://localhost:54242/__testling?show=false:22211:55)
          at Surface.g (http://localhost:54242/__testling?show=false:9165:16)
          at Surface.EventEmitter.emit (http://localhost:54242/__testling?show=false:9073:17)
          at onMouseMove (http://localhost:54242/__testling?show=false:19102:58)
          at Surface.<anonymous> (http://localhost:54242/__testling?show=false:22240:51)
          at Surface.g (http://localhost:54242/__testling?show=false:9165:16)
  ...
ok 186 should be equal
not ok 187 should be equal
  ---
    operator: equal
    expected: false
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:54242/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:54242/__testling?show=false:13499:32)
          at Surface.<anonymous> (http://localhost:54242/__testling?show=false:22226:57)
          at Surface.g (http://localhost:54242/__testling?show=false:9165:16)
          at Surface.EventEmitter.emit (http://localhost:54242/__testling?show=false:9073:17)
          at onMouseUp (http://localhost:54242/__testling?show=false:19123:58)
          at Surface.<anonymous> (http://localhost:54242/__testling?show=false:22236:53)
          at Surface.g (http://localhost:54242/__testling?show=false:9165:16)
  ...
not ok 188 should be equal
  ---
    operator: equal
    expected: null
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:54242/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:54242/__testling?show=false:13499:32)
          at Surface.<anonymous> (http://localhost:54242/__testling?show=false:22228:57)
          at Surface.g (http://localhost:54242/__testling?show=false:9165:16)
          at Surface.EventEmitter.emit (http://localhost:54242/__testling?show=false:9073:17)
          at onMouseUp (http://localhost:54242/__testling?show=false:19123:58)
          at Surface.<anonymous> (http://localhost:54242/__testling?show=false:22236:53)
          at Surface.g (http://localhost:54242/__testling?show=false:9165:16)
  ...
not ok 189 should be equal
  ---
    operator: equal
    expected: null
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:54242/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:54242/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:54242/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:54242/__testling?show=false:13499:32)
          at Surface.<anonymous> (http://localhost:54242/__testling?show=false:22230:57)
          at Surface.g (http://localhost:54242/__testling?show=false:9165:16)
          at Surface.EventEmitter.emit (http://localhost:54242/__testling?show=false:9073:17)
          at onMouseUp (http://localhost:54242/__testling?show=false:19123:58)
          at Surface.<anonymous> (http://localhost:54242/__testling?show=false:22236:53)
          at Surface.g (http://localhost:54242/__testling?show=false:9165:16)
  ...
ok 190 should be equal
ok 191 should be equal
# Annotated Marker
ok 192 should contain the right text
ok 193 should contain the right text
# Dot
ok 194 Dot is well positioned 1
ok 195 Dot is well positioned 2
# Marker
ok 196 should be equal
ok 197 should be equal
# Segment instanciation
ok 198 should be equal
ok 199 should be equal
ok 200 should be equal
ok 201 should be equal
# Segment navigation zoom and move
ok 202 should be equal
ok 203 should be equal
ok 204 should be equal
# TraceDots
ok 205 should be equal
ok 206 should be equal
# OrthogonalData
ok 207 Correctly tranforms cols to rows
ok 208 Correctly tranforms rows to cols

1..208
# tests 208
# pass  199
# fail  9

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/core/layer.js

1      import events from 'events';
2      import ns from './namespace';
3      import scales from '../utils/scales';
4      import Segment from '../shapes/segment';
5      import TimeContextBehavior from '../behaviors/time-context-behavior';
6      
7      // time context bahevior
8      let timeContextBehavior = null;
9      let timeContextBehaviorCtor = TimeContextBehavior;
10     
11     /**
12      * The layer class is the main visualization class. It is mainly defines by its
13      * related `LayerTimeContext` which determines its position in the overall
14      * timeline (through the `start`, `duration`, `offset` and `stretchRatio`
15      * attributes) and by it's registered Shape which defines how to display the
16      * data associated to the layer. Each created layer must be inserted into a
17      * `Track` instance in order to be displayed.
18      *
19      * _Note: in the context of the layer, an __item__ is the SVG element
20      * returned by a `Shape` instance and associated with a particular __datum__._
21      *
22      * ### Layer DOM structure
23      * ```
24      * <g class="layer" transform="translate(${start}, 0)">
25      *   <svg class="bounding-box" width="${duration}">
26      *     <g class="offset" transform="translate(${offset, 0})">
27      *       <!-- background -->
28      *       <rect class="background"></rect>
29      *       <!-- shapes and common shapes are inserted here -->
30      *     </g>
31      *     <g class="interactions"><!-- for feedback --></g>
32      *   </svg>
33      * </g>
34      * ```
35      */
36     export default class Layer extends events.EventEmitter {
37       /**
38        * @param {String} dataType - Defines how the layer should look at the data.
39        *    Can be 'entity' or 'collection'.
40        * @param {(Array|Object)} data - The data associated to the layer.
41        * @param {Object} options - Configures the layer.
42        * @param {Number} [options.height=100] - Defines the height of the layer.
43        * @param {Number} [options.top=0] - Defines the top position of the layer.
44        * @param {Number} [options.opacity=1] - Defines the opacity of the layer.
45        * @param {Number} [options.yDomain=[0,1]] - Defines boundaries of the data
46        *    values in y axis (for exemple to display an audio buffer, this attribute
47        *    should be set to [-1, 1].
48        * @param {String} [options.className=null] - An optionnal class to add to each
49        *    created shape.
50        * @param {String} [options.className='selected'] - The class to add to a shape
51        *    when selected.
52        * @param {Number} [options.contextHandlerWidth=2] - The width of the handlers
53        *    displayed to edit the layer.
54        * @param {Number} [options.hittable=false] - Defines if the layer can be interacted
55        *    with. Basically, the layer is not returned by `BaseState.getHitLayers` when
56        *    set to false (a common use case is a layer that contains a cursor)
57        */
58       constructor(dataType, data, options = {}) {
59         super();
60     
61         const defaults = {
62           height: 100,
63           top: 0,
64           opacity: 1,
65           yDomain: [0, 1],
66           className: null,
67           selectedClassName: 'selected',
68           contextHandlerWidth: 2,
69           hittable: true, // when false the layer is not returned by `BaseState.getHitLayers`
70           id: '', // used ?
71           overflow: 'hidden', // usefull ?
72         };
73     
74         /**
75          * Parameters of the layers, `defaults` overrided with options.
76          * @type {Object}
77          */
78         this.params = Object.assign({}, defaults, options);
79         /**
80          * Defines how the layer should look at the data (`'entity'` or `'collection'`).
81          * @type {String}
82          */
83         this.dataType = dataType; // 'entity' || 'collection';
84         /** @type {LayerTimeContext} */
85         this.timeContext = null;
86         /** @type {Element} */
87         this.$el = null;
88         /** @type {Element} */
89         this.$background = null;
90         /** @type {Element} */
91         this.$boundingBox = null;
92         /** @type {Element} */
93         this.$offset = null;
94         /** @type {Element} */
95         this.$interactions = null;
96         /**
97          * A Segment instanciated to interact with the Layer itself.
98          * @type {Segment}
99          */
100        this.contextShape = null;
101    
102        this._shapeConfiguration = null;       // { ctor, accessors, options }
103        this._commonShapeConfiguration = null; // { ctor, accessors, options }
104        this._$itemShapeMap = new Map();
105        this._$itemDataMap = new Map();
106        this._$itemCommonShapeMap = new Map();
107    
108        this._isContextEditable = false;
109        this._behavior = null;
110    
111        this.data = data;
112    
113        this._valueToPixel = scales.linear()
114          .domain(this.params.yDomain)
115          .range([0, this.params.height]);
116    
117        // initialize timeContext layout
118        this._renderContainer();
119        // creates the timeContextBehavior for all layers
120        if (timeContextBehavior === null) {
121          timeContextBehavior = new timeContextBehaviorCtor();
122        }
123      }
124    
125      /**
126       * Destroy the layer, clear all references.
127       */
128      destroy() {
129        this.timeContext = null;
130        this.data = null;
131        this.params = null;
132        this._behavior = null;
133    
134        this._$itemShapeMap.clear();
135        this._$itemDataMap.clear();
136        this._$itemCommonShapeMap.clear();
137    
138        this.removeAllListeners();
139      }
140    
141      /**
142       * Allows to override default the `TimeContextBehavior` used to edit the layer.
143       *
144       * @param {Object} ctor
145       */
146      static configureTimeContextBehavior(ctor) {
147        timeContextBehaviorCtor = ctor;
148      }
149    
150      /**
151       * Returns `LayerTimeContext`'s `start` time domain value.
152       *
153       * @type {Number}
154       */
155      get start() {
156        return this.timeContext.start;
157      }
158    
159      /**
160       * Sets `LayerTimeContext`'s `start` time domain value.
161       *
162       * @type {Number}
163       */
164      set start(value) {
165        this.timeContext.start = value;
166      }
167    
168      /**
169       * Returns `LayerTimeContext`'s `offset` time domain value.
170       *
171       * @type {Number}
172       */
173      get offset() {
174        return this.timeContext.offset;
175      }
176    
177      /**
178       * Sets `LayerTimeContext`'s `offset` time domain value.
179       *
180       * @type {Number}
181       */
182      set offset(value) {
183        this.timeContext.offset = value;
184      }
185    
186      /**
187       * Returns `LayerTimeContext`'s `duration` time domain value.
188       *
189       * @type {Number}
190       */
191      get duration() {
192        return this.timeContext.duration;
193      }
194    
195      /**
196       * Sets `LayerTimeContext`'s `duration` time domain value.
197       *
198       * @type {Number}
199       */
200      set duration(value) {
201        this.timeContext.duration = value;
202      }
203    
204      /**
205       * Returns `LayerTimeContext`'s `stretchRatio` time domain value.
206       *
207       * @type {Number}
208       */
209      get stretchRatio() {
210        return this.timeContext.stretchRatio;
211      }
212    
213      /**
214       * Sets `LayerTimeContext`'s `stretchRatio` time domain value.
215       *
216       * @type {Number}
217       */
218      set stretchRatio(value) {
219        this.timeContext.stretchRatio = value;
220      }
221    
222      /**
223       * Set the domain boundaries of the data for the y axis.
224       *
225       * @type {Array}
226       */
227      set yDomain(domain) {
228        this.params.yDomain = domain;
229        this._valueToPixel.domain(domain);
230      }
231    
232      /**
233       * Returns the domain boundaries of the data for the y axis.
234       *
235       * @type {Array}
236       */
237      get yDomain() {
238        return this.params.yDomain;
239      }
240    
241      /**
242       * Sets the opacity of the whole layer.
243       *
244       * @type {Number}
245       */
246      set opacity(value) {
247        this.params.opacity = value;
248      }
249    
250      /**
251       * Returns the opacity of the whole layer.
252       *
253       * @type {Number}
254       */
255      get opacity() {
256        return this.params.opacity;
257      }
258    
259      /**
260       * Returns the transfert function used to display the data in the x axis.
261       *
262       * @type {Number}
263       */
264      get timeToPixel() {
265        return this.timeContext.timeToPixel;
266      }
267    
268      /**
269       * Returns the transfert function used to display the data in the y axis.
270       *
271       * @type {Number}
272       */
273      get valueToPixel() {
274        return this._valueToPixel;
275      }
276    
277      /**
278       * Returns an array containing all the displayed items.
279       *
280       * @type {Array<Element>}
281       */
282      get items() {
283        return Array.from(this._$itemDataMap.keys());
284      }
285    
286      /**
287       * Returns the data associated to the layer.
288       *
289       * @type {Object[]}
290       */
291      get data() { return this._data; }
292    
293      /**
294       * Sets the data associated with the layer.
295       *
296       * @type {Object|Object[]}
297       */
298      set data(data) {
299        switch (this.dataType) {
300          case 'entity':
301            if (this._data) {  // if data already exists, reuse the reference
302              this._data[0] = data;
303            } else {
304              this._data = [data];
305            }
306            break;
307          case 'collection':
308            this._data = data;
309            break;
310        }
311      }
312    
313      // --------------------------------------
314      // Initialization
315      // --------------------------------------
316    
317      /**
318       * Renders the DOM in memory on layer creation to be able to use it before
319       * the layer is actually inserted in the DOM.
320       */
321      _renderContainer() {
322        // wrapper group for `start, top and context flip matrix
323        this.$el = document.createElementNS(ns, 'g');
324        this.$el.classList.add('layer');
325        if (this.params.className !== null) {
326          this.$el.classList.add(this.params.className);
327        }
328        // clip the context with a `svg` element
329        this.$boundingBox = document.createElementNS(ns, 'svg');
330        this.$boundingBox.classList.add('bounding-box');
331        this.$boundingBox.style.overflow = this.params.overflow;
332        // group to apply offset
333        this.$offset = document.createElementNS(ns, 'g');
334        this.$offset.classList.add('offset', 'items');
335        // layer background
336        this.$background = document.createElementNS(ns, 'rect');
337        this.$background.setAttributeNS(null, 'height', '100%');
338        this.$background.setAttributeNS(null, 'width', '100%');
339        this.$background.classList.add('background');
340        this.$background.style.fillOpacity = 0;
341        this.$background.style.pointerEvents = 'none';
342        // context interactions
343        this.$interactions = document.createElementNS(ns, 'g');
344        this.$interactions.classList.add('interactions');
345        this.$interactions.style.display = 'none';
346        // @NOTE: works but king of ugly... should be cleaned
347        this.contextShape = new Segment();
348        this.contextShape.install({
349          opacity: () => 0.1,
350          color  : () => '#787878',
351          width  : () => this.timeContext.duration,
352          height : () => this._renderingContext.valueToPixel.domain()[1],
353          y      : () => this._renderingContext.valueToPixel.domain()[0]
354        });
355    
356        this.$interactions.appendChild(this.contextShape.render());
357        // create the DOM tree
358        this.$el.appendChild(this.$boundingBox);
359        this.$boundingBox.appendChild(this.$offset);
360        this.$offset.appendChild(this.$background);
361        this.$boundingBox.appendChild(this.$interactions);
362      }
363    
364      // --------------------------------------
365      // Component Configuration
366      // --------------------------------------
367    
368      /**
369       * Sets the context of the layer, thus defining its `start`, `duration`,
370       * `offset` and `stretchRatio`.
371       *
372       * @param {TimeContext} timeContext - The timeContext in which the layer is displayed.
373       */
374      setTimeContext(timeContext) {
375        this.timeContext = timeContext;
376        // create a mixin to pass to the shapes
377        this._renderingContext = {};
378        this._updateRenderingContext();
379      }
380    
381      /**
382       * Register a shape and its configuration to use in order to render the data.
383       *
384       * @param {BaseShape} ctor - The constructor of the shape to be used.
385       * @param {Object} [accessors={}] - Defines how the shape should adapt to a particular data struture.
386       * @param {Object} [options={}] - Global configuration for the shapes, is specific to each `Shape`.
387       */
388      configureShape(ctor, accessors = {}, options = {}) {
389        this._shapeConfiguration = { ctor, accessors, options };
390      }
391    
392      /**
393       * Optionnaly register a shape to be used accros the entire collection.
394       *
395       * @param {BaseShape} ctor - The constructor of the shape to be used.
396       * @param {Object} [accessors={}] - Defines how the shape should adapt to a particular data struture.
397       * @param {Object} [options={}] - Global configuration for the shapes, is specific to each `Shape`.
398       */
399      configureCommonShape(ctor, accessors = {}, options = {}) {
400        this._commonShapeConfiguration = { ctor, accessors, options };
401      }
402    
403      /**
404       * Register the behavior to use when interacting with a shape.
405       *
406       * @param {BaseBehavior} behavior
407       */
408      setBehavior(behavior) {
409        behavior.initialize(this);
410        this._behavior = behavior;
411      }
412    
413      /**
414       * Updates the values stored int the `_renderingContext` passed  to shapes
415       * for rendering and updating.
416       */
417      _updateRenderingContext() {
418        this._renderingContext.timeToPixel = this.timeContext.timeToPixel;
419        this._renderingContext.valueToPixel = this._valueToPixel;
420    
421        this._renderingContext.height = this.params.height;
422        this._renderingContext.width  = this.timeContext.timeToPixel(this.timeContext.duration);
423        // for foreign object issue in chrome
424        this._renderingContext.offsetX = this.timeContext.timeToPixel(this.timeContext.offset);
425        this._renderingContext.startX = this.timeContext.parent.timeToPixel(this.timeContext.start);
426    
427        // @todo replace with `minX` and `maxX` representing the visible pixels in which
428        // the shapes should be rendered, could allow to not update the DOM of shapes
429        // who are not in this area.
430        this._renderingContext.trackOffsetX = this.timeContext.parent.timeToPixel(this.timeContext.parent.offset);
431        this._renderingContext.visibleWidth = this.timeContext.parent.visibleWidth;
432      }
433    
434      // --------------------------------------
435      // Behavior Accessors
436      // --------------------------------------
437    
438      /**
439       * Returns the items marked as selected.
440       *
441       * @type {Array<Element>}
442       */
443      get selectedItems() {
444        return this._behavior ? this._behavior.selectedItems : [];
445      }
446    
447      /**
448       * Mark item(s) as selected.
449       *
450       * @param {Element|Element[]} $items
451       */
452      select(...$items) {
453        if (!this._behavior) { return; }
454        if (!$items.length) { $items = this._$itemDataMap.keys(); }
455        if (Array.isArray($items[0])) { $items = $items[0]; }
456    
457        for (let $item of $items) {
458          const datum = this._$itemDataMap.get($item);
459          this._behavior.select($item, datum);
460          this._toFront($item);
461        }
462      }
463    
464      /**
465       * Removes item(s) from selected items.
466       *
467       * @param {Element|Element[]} $items
468       */
469      unselect(...$items) {
470        if (!this._behavior) { return; }
471        if (!$items.length) { $items = this._$itemDataMap.keys(); }
472        if (Array.isArray($items[0])) { $items = $items[0]; }
473    
474        for (let $item of $items) {
475          const datum = this._$itemDataMap.get($item);
476          this._behavior.unselect($item, datum);
477        }
478      }
479    
480      /**
481       * Toggle item(s) selection state according to their current state.
482       *
483       * @param {Element|Element[]} $items
484       */
485      toggleSelection(...$items) {
486        if (!this._behavior) { return; }
487        if (!$items.length) { $items = this._$itemDataMap.keys(); }
488        if (Array.isArray($items[0])) { $items = $items[0]; }
489    
490        for (let $item of $items) {
491          const datum = this._$itemDataMap.get($item);
492          this._behavior.toggleSelection($item, datum);
493        }
494      }
495    
496      /**
497       * Edit item(s) according to the `edit` defined in the registered `Behavior`.
498       *
499       * @param {Element|Element[]} $items - The item(s) to edit.
500       * @param {Number} dx - The modification to apply in the x axis (in pixels).
501       * @param {Number} dy - The modification to apply in the y axis (in pixels).
502       * @param {Element} $target - The target of the interaction (for example, left
503       *    handler DOM element in a segment).
504       */
505      edit($items, dx, dy, $target) {
506        if (!this._behavior) { return; }
507        $items = !Array.isArray($items) ? [$items] : $items;
508    
509        for (let $item of $items) {
510          const shape = this._$itemShapeMap.get($item);
511          const datum = this._$itemDataMap.get($item);
512    
513          this._behavior.edit(this._renderingContext, shape, datum, dx, dy, $target);
514          this.emit('edit', shape, datum);
515        }
516      }
517    
518      /**
519       * Defines if the `Layer`, and thus the `LayerTimeContext` is editable or not.
520       *
521       * @params {Boolean} [bool=true]
522       */
523      setContextEditable(bool = true) {
524        const display = bool ? 'block' : 'none';
525        this.$interactions.style.display = display;
526        this._isContextEditable = bool;
527      }
528    
529      /**
530       * Edit the layer and thus its related `LayerTimeContext` attributes.
531       *
532       * @param {Number} dx - The modification to apply in the x axis (in pixels).
533       * @param {Number} dy - The modification to apply in the y axis (in pixels).
534       * @param {Element} $target - The target of the event of the interaction.
535       */
536      editContext(dx, dy, $target) {
537        timeContextBehavior.edit(this, dx, dy, $target);
538      }
539    
540      /**
541       * Stretch the layer and thus its related `LayerTimeContext` attributes.
542       *
543       * @param {Number} dx - The modification to apply in the x axis (in pixels).
544       * @param {Number} dy - The modification to apply in the y axis (in pixels).
545       * @param {Element} $target - The target of the event of the interaction.
546       */
547      stretchContext(dx, dy, $target) {
548        timeContextBehavior.stretch(this, dx, dy, $target);
549      }
550    
551      // --------------------------------------
552      // Helpers
553      // --------------------------------------
554    
555      /**
556       * Returns an item from a DOM element related to the shape, null otherwise.
557       *
558       * @param {Element} $el - the element to be tested
559       * @return {Element|null}
560       */
561      getItemFromDOMElement($el) {
562        let $item;
563    
564        do {
565          if ($el.classList && $el.classList.contains('item')) {
566            $item = $el;
567            break;
568          }
569    
570          $el = $el.parentNode;
571        } while ($el !== null);
572    
573        return this.hasItem($item) ? $item : null;
574      }
575    
576      /**
577       * Returns the datum associated to a specific item.
578       *
579       * @param {Element} $item
580       * @return {Object|Array|null}
581       */
582      getDatumFromItem($item) {
583        const datum = this._$itemDataMap.get($item);
584        return datum ? datum : null;
585      }
586    
587      /**
588       * Returns the datum associated to a specific item from any DOM element
589       * composing the shape. Basically a shortcut for `getItemFromDOMElement` and
590       * `getDatumFromItem` methods.
591       *
592       * @param {Element} $el
593       * @return {Object|Array|null}
594       */
595      getDatumFromDOMElement($el) {
596        var $item = this.getItemFromDOMElement($el);
597        if ($item === null) { return null; }
598        return this.getDatumFromItem($item);
599      }
600    
601      /**
602       * Tests if the given DOM element is an item of the layer.
603       *
604       * @param {Element} $item - The item to be tested.
605       * @return {Boolean}
606       */
607      hasItem($item) {
608        return this._$itemDataMap.has($item);
609      }
610    
611      /**
612       * Defines if a given element belongs to the layer. Is more general than
613       * `hasItem`, can mostly used to check interactions elements.
614       *
615       * @param {Element} $el - The DOM element to be tested.
616       * @return {bool}
617       */
618      hasElement($el) {
619        do {
620          if ($el === this.$el) {
621            return true;
622          }
623    
624          $el = $el.parentNode;
625        } while ($el !== null);
626    
627        return false;
628      }
629    
630      /**
631       * Retrieve all the items in a given area as defined in the registered `Shape~inArea` method.
632       *
633       * @param {Object} area - The area in which to find the elements
634       * @param {Number} area.top
635       * @param {Number} area.left
636       * @param {Number} area.width
637       * @param {Number} area.height
638       * @return {Array} - list of the items presents in the area
639       */
640      getItemsInArea(area) {
641        const start    = this.timeContext.parent.timeToPixel(this.timeContext.start);
642        const duration = this.timeContext.timeToPixel(this.timeContext.duration);
643        const offset   = this.timeContext.timeToPixel(this.timeContext.offset);
644        const top      = this.params.top;
645        // be aware af context's translations - constrain in working view
646        let x1 = Math.max(area.left, start);
647        let x2 = Math.min(area.left + area.width, start + duration);
648        x1 -= (start + offset);
649        x2 -= (start + offset);
650        // keep consistent with context y coordinates system
651        let y1 = this.params.height - (area.top + area.height);
652        let y2 = this.params.height - area.top;
653    
654        y1 += this.params.top;
655        y2 += this.params.top;
656    
657        const $filteredItems = [];
658    
659        for (let [$item, datum] of this._$itemDataMap.entries()) {
660          const shape = this._$itemShapeMap.get($item);
661          const inArea = shape.inArea(this._renderingContext, datum, x1, y1, x2, y2);
662    
663          if (inArea) { $filteredItems.push($item); }
664        }
665    
666        return $filteredItems;
667      }
668    
669      // --------------------------------------
670      // Rendering / Display methods
671      // --------------------------------------
672    
673      /**
674       * Moves an item to the end of the layer to display it front of its
675       * siblings (svg z-index...).
676       *
677       * @param {Element} $item - The item to be moved.
678       */
679      _toFront($item) {
680        this.$offset.appendChild($item);
681      }
682    
683      /**
684       * Create the DOM structure of the shapes according to the given data. Inspired
685       * from the `enter` and `exit` d3.js paradigm, this method should be called
686       * each time a datum is added or removed from the data. While the DOM is
687       * created the `update` method must be called in order to update the shapes
688       * attributes and thus place them where they should.
689       */
690      render() {
691        // render `commonShape` only once
692        if (
693          this._commonShapeConfiguration !== null &&
694          this._$itemCommonShapeMap.size === 0
695        ) {
696          const { ctor, accessors, options } = this._commonShapeConfiguration;
697          const $group = document.createElementNS(ns, 'g');
698          const shape = new ctor(options);
699    
700          shape.install(accessors);
701          $group.appendChild(shape.render());
702          $group.classList.add('item', 'common', shape.getClassName());
703    
704          this._$itemCommonShapeMap.set($group, shape);
705          this.$offset.appendChild($group);
706        }
707    
708        // append elements all at once
709        const fragment = document.createDocumentFragment();
710        const values = this._$itemDataMap.values(); // iterator
711    
712        // enter
713        this.data.forEach((datum) => {
714          for (let value of values) { if (value === datum) { return; } }
715    
716          const { ctor, accessors, options } = this._shapeConfiguration;
717          const shape = new ctor(options);
718          shape.install(accessors);
719    
720          const $el = shape.render(this._renderingContext);
721          $el.classList.add('item', shape.getClassName());
722    
723          this._$itemShapeMap.set($el, shape);
724          this._$itemDataMap.set($el, datum);
725    
726          fragment.appendChild($el);
727        });
728    
729        this.$offset.appendChild(fragment);
730    
731        // remove
732        for (let [$item, datum] of this._$itemDataMap.entries()) {
733          if (this.data.indexOf(datum) !== -1) { continue; }
734    
735          const shape = this._$itemShapeMap.get($item);
736    
737          this.$offset.removeChild($item);
738          shape.destroy();
739          // a removed item cannot be selected
740          if (this._behavior) {
741            this._behavior.unselect($item, datum);
742          }
743    
744          this._$itemDataMap.delete($item);
745          this._$itemShapeMap.delete($item);
746        }
747      }
748    
749      /**
750       * Updates the container of the layer and the attributes of the existing shapes.
751       */
752      update() {
753        this.updateContainer();
754        this.updateShapes();
755      }
756    
757      /**
758       * Updates the container of the layer.
759       */
760      updateContainer() {
761        this._updateRenderingContext();
762    
763        const timeContext = this.timeContext;
764        const width  = timeContext.timeToPixel(timeContext.duration);
765        // x is relative to timeline's timeContext
766        const x      = timeContext.parent.timeToPixel(timeContext.start);
767        const offset = timeContext.timeToPixel(timeContext.offset);
768        const top    = this.params.top;
769        const height = this.params.height;
770        // matrix to invert the coordinate system
771        const translateMatrix = `matrix(1, 0, 0, -1, ${x}, ${top + height})`;
772    
773        this.$el.setAttributeNS(null, 'transform', translateMatrix);
774    
775        this.$boundingBox.setAttributeNS(null, 'width', width);
776        this.$boundingBox.setAttributeNS(null, 'height', height);
777        this.$boundingBox.style.opacity = this.params.opacity;
778    
779        this.$offset.setAttributeNS(null, 'transform', `translate(${offset}, 0)`);
780        // maintain context shape
781        this.contextShape.update(this._renderingContext, this.timeContext, 0);
782      }
783    
784      /**
785       * Updates the attributes of all the `Shape` instances rendered into the layer.
786       *
787       * @todo - allow to filter which shape(s) should be updated.
788       */
789      updateShapes() {
790        this._updateRenderingContext();
791        // update common shapes
792        this._$itemCommonShapeMap.forEach((shape, $item) => {
793          shape.update(this._renderingContext, this.data);
794        });
795    
796        for (let [$item, datum] of this._$itemDataMap.entries()) {
797          const shape = this._$itemShapeMap.get($item);
798          shape.update(this._renderingContext, datum);
799        }
800      }
801    }
802    

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/utils/scales.js

1      /**
2       * Lightweight scales mimicing the `d3.js` functionnal API.
3       */
4      export default {
5        /**
6         * A linear scale interpolating values between a `domain` and a `range`.
7         * @return {Function}
8         */
9        linear() {
10         let _domain = [0, 1];
11         let _range = [0, 1];
12     
13         let _slope = 1;
14         let _intercept = 0;
15     
16         function _updateCoefs() {
17           _slope = (_range[1] - _range[0]) / (_domain[1] - _domain[0]);
18           _intercept = _range[0] - (_slope * _domain[0]);
19         }
20     
21         function scale (value) {
22           return (_slope * value) + _intercept;
23         }
24     
25         scale.invert = function(value) {
26           return (value - _intercept) / _slope;
27         };
28     
29         scale.domain = function(arr = null) {
30           if (arr === null) { return _domain; }
31     
32           _domain = arr;
33           _updateCoefs();
34     
35           return scale;
36         };
37     
38         scale.range = function(arr = null) {
39           if (arr === null) { return _range; }
40     
41           _range = arr;
42           _updateCoefs();
43     
44           return scale;
45         };
46     
47         return scale;
48       }
49     };
50     

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/shapes/segment.js

1      import BaseShape from './base-shape';
2      
3      
4      /**
5       * A shape to display a segment.
6       *
7       * [example usage](./examples/layer-segment.html)
8       */
9      export default class Segment extends BaseShape {
10       getClassName() { return 'segment'; }
11     
12       _getAccessorList() {
13         return { x: 0, y: 0, width: 0, height: 1, color: '#000000', opacity: 1 };
14       }
15     
16       _getDefaults() {
17         return {
18           displayHandlers: true,
19           handlerWidth: 2,
20           handlerOpacity: 0.8,
21           opacity: 0.6
22         };
23       }
24     
25       render(renderingContext) {
26         if (this.$el) { return this.$el; }
27     
28         this.$el = document.createElementNS(this.ns, 'g');
29     
30         this.$segment = document.createElementNS(this.ns, 'rect');
31         this.$segment.classList.add('segment');
32         this.$segment.style.opacity = this.params.opacity;
33         this.$segment.setAttributeNS(null, 'shape-rendering', 'crispEdges');
34     
35         this.$el.appendChild(this.$segment);
36     
37         if (this.params.displayHandlers) {
38           this.$leftHandler = document.createElementNS(this.ns, 'rect');
39           this.$leftHandler.classList.add('left', 'handler');
40           this.$leftHandler.setAttributeNS(null, 'width', this.params.handlerWidth);
41           this.$leftHandler.setAttributeNS(null, 'shape-rendering', 'crispEdges');
42           this.$leftHandler.style.opacity = this.params.handlerOpacity;
43           this.$leftHandler.style.cursor = 'ew-resize';
44     
45           this.$rightHandler = document.createElementNS(this.ns, 'rect');
46           this.$rightHandler.classList.add('right', 'handler');
47           this.$rightHandler.setAttributeNS(null, 'width', this.params.handlerWidth);
48           this.$rightHandler.setAttributeNS(null, 'shape-rendering', 'crispEdges');
49           this.$rightHandler.style.opacity = this.params.handlerOpacity;
50           this.$rightHandler.style.cursor = 'ew-resize';
51     
52           this.$el.appendChild(this.$leftHandler);
53           this.$el.appendChild(this.$rightHandler);
54         }
55     
56         return this.$el;
57       }
58     
59       update(renderingContext, datum) {
60         const x = renderingContext.timeToPixel(this.x(datum));
61         const y = renderingContext.valueToPixel(this.y(datum));
62     
63         const width = renderingContext.timeToPixel(this.width(datum));
64         const height = renderingContext.valueToPixel(this.height(datum));
65         const color = this.color(datum);
66         const opacity = this.opacity(datum);
67     
68         this.$el.setAttributeNS(null, 'transform', `translate(${x}, ${y})`);
69         this.$el.style.opacity = opacity;
70     
71         this.$segment.setAttributeNS(null, 'width', Math.max(width, 0));
72         this.$segment.setAttributeNS(null, 'height', height);
73         this.$segment.style.fill = color;
74     
75         if (this.params.displayHandlers) {
76           // display handlers
77           this.$leftHandler.setAttributeNS(null, 'height', height);
78           this.$leftHandler.setAttributeNS(null, 'transform', 'translate(0, 0)');
79           this.$leftHandler.style.fill = color;
80     
81           const rightHandlerTranslate = `translate(${width - this.params.handlerWidth}, 0)`;
82           this.$rightHandler.setAttributeNS(null, 'height', height);
83           this.$rightHandler.setAttributeNS(null, 'transform', rightHandlerTranslate);
84           this.$rightHandler.style.fill = color;
85         }
86       }
87     
88       inArea(renderingContext, datum, x1, y1, x2, y2) {
89         const shapeX1 = renderingContext.timeToPixel(this.x(datum));
90         const shapeX2 = renderingContext.timeToPixel(this.x(datum) + this.width(datum));
91         const shapeY1 = renderingContext.valueToPixel(this.y(datum));
92         const shapeY2 = renderingContext.valueToPixel(this.y(datum) + this.height(datum));
93     
94         // http://jsfiddle.net/uthyZ/ - check overlaping area
95         const xOverlap = Math.max(0, Math.min(x2, shapeX2) - Math.max(x1, shapeX1));
96         const yOverlap = Math.max(0, Math.min(y2, shapeY2) - Math.max(y1, shapeY1));
97         const area = xOverlap * yOverlap;
98     
99         return area > 0;
100      }
101    }
102    

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/shapes/base-shape.js

1      import ns from '../core/namespace';
2      
3      
4      /**
5       * Is an abstract class or interface to be overriden in order to define new
6       * shapes. Shapes define the way a given datum should be rendered, they are
7       * the smallest unit of rendering into a timeline.
8       *
9       * All the life cycle of `Shape` instances is handled into the `Layer` instance
10      * they are attach to. As a consequence, they should be mainly considered as
11      * private objects. The only place they should be interacted with is in `Behavior`
12      * definitions, to test which element of the shape is the target of the
13      * interaction and define the interaction according to that test.
14      *
15      * Depending of its implementation a `Shape` can be used along with `entity` or
16      * `collection` data type. Some shapes are then created to use data considered
17      * as a single entity (Waveform, TracePath, Line), while others are defined to
18      * be used with data seen as a collection, each shape rendering a single entry
19      * of the collection. The shapes working with entity type data should therefore
20      * be used in an `entity` configured `Layer`. Note that if they are registered
21      * as "commonShape" in a `collection` type `Layer`, they will behave the exact
22      * same way. These kind of shapes are noted: "entity shape".
23      *
24      * ### Available `collection` shapes:
25      * - Marker / Annotated Marker
26      * - Segment / Annotated Segment
27      * - Dot
28      * - TraceDots
29      *
30      * ### Available `entity` shapes:
31      * - Line
32      * - Tick (for axis)
33      * - Waveform
34      * - TracePath
35      */
36     export default class BaseShape {
37       /**
38        * @param {Object} options - override default configuration
39        */
40       constructor(options = {}) {
41         /** @type {Element} - Svg element to be returned by the `render` method. */
42         this.$el = null;
43         /** @type {String} - Svg namespace. */
44         this.ns = ns;
45         /** @type {Object} - Object containing the global parameters of the shape */
46         this.params = Object.assign({}, this._getDefaults(), options);
47         // create accessors methods and set default accessor functions
48         const accessors = this._getAccessorList();
49         this._createAccessors(accessors);
50         this._setDefaultAccessors(accessors);
51       }
52     
53       /**
54        * Destroy the shape and clean references. Interface method called from the `layer`.
55        */
56       destroy() {
57         // this.group = null;
58         this.$el = null;
59       }
60     
61       /**
62        * Interface method to override when extending this base class. The method
63        * is called by the `Layer~render` method. Returns the name of the shape,
64        * used as a class in the element group (defaults to `'shape'`).
65        *
66        * @return {String}
67        */
68       getClassName() { return 'shape'; }
69     
70       /**
71        * @todo not implemented
72        * allow to install defs in the track svg element. Should be called when
73        * adding the `Layer` to the `Track`.
74        */
75       // setSvgDefinition(defs) {}
76     
77       /**
78        * Returns the defaults for global configuration of the shape.
79        * @protected
80        * @return {Object}
81        */
82       _getDefaults() {
83         return {};
84       }
85     
86       /**
87        * Returns an object where keys are the accessors methods names to create
88        * and values are the default values for each given accessor.
89        *
90        * @protected
91        * @todo rename ?
92        * @return {Object}
93        */
94       _getAccessorList() { return {}; }
95     
96     
97       /**
98        * Interface method called by Layer when creating a shape. Install the
99        * given accessors on the shape, overriding the default accessors.
100       *
101       * @param {Object<String, function>} accessors
102       */
103      install(accessors) {
104        for (let key in accessors) { this[key] = accessors[key]; }
105      }
106    
107      /**
108       * Generic method to create accessors. Adds getters en setters to the
109       * prototype if not already present.
110       */
111      _createAccessors(accessors) {
112        this._accessors = {};
113        // add it to the prototype
114        const proto = Object.getPrototypeOf(this);
115        // create a getter / setter for each accessors
116        // setter : `this.x = callback`
117        // getter : `this.x(datum)`
118        Object.keys(accessors).forEach((name) => {
119          if (proto.hasOwnProperty(name)) { return; }
120    
121          Object.defineProperty(proto, name, {
122            get: function() { return this._accessors[name]; },
123            set: function(func) {
124              this._accessors[name] = func;
125            }
126          });
127        });
128      }
129    
130      /**
131       * Create a function to be used as a default accessor for each accesors
132       */
133      _setDefaultAccessors(accessors) {
134        Object.keys(accessors).forEach((name) => {
135          const defaultValue = accessors[name];
136          let accessor = function(d, v = null) {
137            if (v === null) { return d[name] || defaultValue; }
138            d[name] = v;
139          };
140          // set accessor as the default one
141          this[name] = accessor;
142        });
143      }
144    
145      /**
146       * Interface method called by `Layer~render`. Creates the DOM structure of
147       * the shape.
148       *
149       * @param {Object} renderingContext - the renderingContext of the layer
150       *    which owns this shape.
151       * @return {Element} - the DOM element to insert in the item's group.
152       */
153      render(renderingContext) {}
154    
155      /**
156       * Interface method called by `Layer~update`. Updates the DOM structure of the shape.
157       *
158       * @param {Object} renderingContext - The `renderingContext` of the layer
159       *    which owns this shape.
160       * @param {Object|Array} datum - The datum associated to the shape.
161       */
162      update(renderingContext, datum) {}
163    
164      /**
165       * Interface method to override called by `Layer~getItemsInArea`. Defines if
166       * the shape is considered to be the given area. Arguments are passed in pixel domain.
167       *
168       * @param {Object} renderingContext - the renderingContext of the layer which
169       *    owns this shape.
170       * @param {Object|Array} datum - The datum associated to the shape.
171       * @param {Number} x1 - The x component of the top-left corner of the area to test.
172       * @param {Number} y1 - The y component of the top-left corner of the area to test.
173       * @param {Number} x2 - The x component of the bottom-right corner of the area to test.
174       * @param {Number} y2 - The y component of the bottom-right corner of the area to test.
175       * @return {Boolean} - Returns `true` if the is considered to be in the given area, `false` otherwise.
176       */
177      inArea(renderingContext, datum, x1, y1, x2, y2) {}
178    }
179    

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/shapes/dot.js

1      import BaseShape from './base-shape';
2      
3      
4      /**
5       * A shape to display a dot.
6       *
7       * [example usage](./examples/layer-breakpoint.html)
8       */
9      export default class Dot extends BaseShape {
10       getClassName() { return 'dot'; }
11     
12       // @TODO rename : confusion between accessors and meta-accessors
13       _getAccessorList() {
14         return { cx: 0, cy: 0, r: 3, color: '#000000' };
15       }
16     
17       render() {
18         if (this.$el) { return this.$el; }
19     
20         this.$el = document.createElementNS(this.ns, 'circle');
21     
22         return this.$el;
23       }
24     
25       update(renderingContext, datum) {
26         const cx = renderingContext.timeToPixel(this.cx(datum));
27         const cy = renderingContext.valueToPixel(this.cy(datum));
28         const r  = this.r(datum);
29         const color = this.color(datum);
30     
31         this.$el.setAttributeNS(null, 'transform', `translate(${cx}, ${cy})`);
32         this.$el.setAttributeNS(null, 'r', r);
33         this.$el.style.fill = color;
34       }
35     
36       // x1, x2, y1, y2 => in pixel domain
37       inArea(renderingContext, datum, x1, y1, x2, y2) {
38         const cx = renderingContext.timeToPixel(this.cx(datum));
39         const cy = renderingContext.valueToPixel(this.cy(datum));
40     
41         if ((cx > x1 && cx < x2) && (cy > y1 && cy < y2)) {
42           return true;
43         }
44     
45         return false;
46       }
47     }
48     

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/core/layer-time-context.js

1      import scales from '../utils/scales';
2      
3      
4      /**
5       * A `LayerTimeContext` instance represents a time segment into a `TimelineTimeContext`.
6       * It must be attached to a `TimelineTimeContext` (the one of the timeline it
7       * belongs to). It relies on its parent's `timeToPixel` (time to pixel transfert
8       * function) to create the time to pixel representation of the Layer (the view) it is attached to.
9       *
10      * The `layerTimeContext` has four important attributes:
11      * - `start` represent the time at which temporal data must be represented
12      *   in the timeline (for instance the begining of a soundfile in a DAW).
13      * - `offset` represents offset time of the data in the context of a Layer.
14      *   (@TODO give a use case example here "crop ?", and/or explain that it's not a common use case).
15      * - `duration` is the duration of the view on the data.
16      * - `stretchRatio` is the stretch applyed to the temporal data contained in
17      *   the view (this value can be seen as a local zoom on the data, or as a stretch
18      *   on the time components of the data). When applyed, the stretch ratio maintain
19      *   the start position of the view in the timeline.
20      *
21      * ```
22      * + timeline -----------------------------------------------------------------
23      * 0         5         10          15          20        25          30 seconds
24      * +---+*****************+------------------------------------------+*******+--
25      *     |*** soundfile ***|Layer (view on the sound file)            |*******|
26      *     +*****************+------------------------------------------+*******+
27      *
28      *     <---- offset ----><--------------- duration ----------------->
29      * <-------- start ----->
30      *
31      * The parts of the sound file represented with '*' are hidden from the view
32      * ```
33      *
34      * [example usage](./examples/time-contexts.html)
35      */
36     export default class LayerTimeContext {
37       /**
38        * @param {TimelineTimeContext} parent - The `TimelineTimeContext` instance of the timeline.
39        */
40       constructor(parent) {
41         if (!parent) { throw new Error('LayerTimeContext must have a parent'); }
42     
43         /**
44          * The `TimelineTimeContext` instance of the timeline.
45          *
46          * @type {TimelineTimeContext}
47          */
48         this.parent = parent;
49     
50         this._timeToPixel = null;
51         this._start = 0;
52         this._duration = parent.visibleDuration;
53         this._offset = 0;
54         this._stretchRatio = 1;
55         // register into the timeline's TimeContext
56         this.parent._children.push(this);
57       }
58     
59       /**
60        * Creates a clone of the current time context.
61        *
62        * @return {LayerTimeContext}
63        */
64       clone() {
65         const ctx = new this();
66     
67         ctx.parent = this.parent;
68         ctx.start = this.start;
69         ctx.duration = this.duration;
70         ctx.offset = this.offset;
71         ctx.stretchRatio = this.stretchRatio; // creates the local scale if needed
72     
73         return ctx;
74       }
75     
76       /**
77        * Returns the start position of the time context (in seconds).
78        *
79        * @type {Number}
80        */
81       get start() {
82         return this._start;
83       }
84     
85       /**
86        * Sets the start position of the time context (in seconds).
87        *
88        * @type {Number}
89        */
90       set start(value) {
91         this._start = value;
92       }
93     
94       /**
95        * Returns the duration of the time context (in seconds).
96        *
97        * @type {Number}
98        */
99       get duration() {
100        return this._duration;
101      }
102    
103      /**
104       * Sets the duration of the time context (in seconds).
105       *
106       * @type {Number}
107       */
108      set duration(value) {
109        this._duration = value;
110      }
111    
112      /**
113       * Returns the offset of the time context (in seconds).
114       *
115       * @type {Number}
116       */
117      get offset() {
118        return this._offset;
119      }
120    
121      /**
122       * Sets the offset of the time context (in seconds).
123       *
124       * @type {Number}
125       */
126      set offset(value) {
127        this._offset = value;
128      }
129    
130      /**
131       * Returns the stretch ratio of the time context.
132       *
133       * @type {Number}
134       */
135      get stretchRatio() {
136        return this._stretchRatio;
137      }
138    
139      /**
140       * Sets the stretch ratio of the time context.
141       *
142       * @type {Number}
143       */
144      set stretchRatio(value) {
145        // remove local scale if ratio = 1
146        if (value ===  1) {
147          this._timeToPixel = null;
148          return;
149        }
150        // reuse previsously created local scale if exists
151        const timeToPixel = this._timeToPixel ?
152          this._timeToPixel : scales.linear().domain([0, 1]);
153    
154        timeToPixel.range([0, this.parent.computedPixelsPerSecond * value]);
155    
156        this._timeToPixel = timeToPixel;
157        this._stretchRatio = value;
158      }
159    
160      /**
161       * Returns the time to pixel transfert function of the time context. If
162       * the `stretchRatio` attribute is equal to 1, this function is the global
163       * one from the `TimelineTimeContext` instance.
164       *
165       * @type {Function}
166       */
167      get timeToPixel() {
168        if (!this._timeToPixel) {
169          return this.parent.timeToPixel;
170        }
171    
172        return this._timeToPixel;
173      }
174    
175      /**
176       * Helper function to convert pixel to time.
177       *
178       * @param {Number} px
179       * @return {Number}
180       */
181      pixelToTime(px) {
182        if (!this._timeToPixel) {
183          return this.parent.timeToPixel.invert(px);
184        }
185    
186        return this._timeToPixel.invert(px);
187      }
188    }
189    

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/behaviors/base-behavior.js

1      /**
2       * Is an abstract class or interface to be overriden in order to define the way
3       * a given shape should behave when selected or edited by a user. Instances of
4       * `BaseBehavior` are internally used by `Layer` instances to modify the data
5       * according to a user interaction and a given shape. A single instance of
6       * `Behavior` is created in one given shape.
7       *
8       * By default, the only method to override to define a new behavior for a
9       * shape is the `edit` method. However, if needed in special cases, all the
10      * selection handling can be overriden too.
11      *
12      * The flow is the following:
13      * `Event`  - (forwarded to) -> `Layer` - (command) -> `Behavior` - (modify) -> `data` - (upates) -> `Shape`
14      *
15      * The behavior responsability is then to modify the data according to the
16      * user interactions, while shapes are always a view of the current state of the
17      * data.
18      */
19     export default class BaseBehavior {
20       constructor() {
21         this._selectedItems = new Set(); // no duplicate in Set
22         this._selectedClass = null;
23         this._layer = null;
24       }
25     
26       initialize(layer) {
27         this._layer = layer;
28         this._selectedClass = layer.params.selectedClassName;
29       }
30     
31       /**
32        * Destroy the references to the selected items.
33        *
34        * @type {String}
35        * @todo - rename to `clearSelection` (removing the class) ?
36        */
37       destroy() {
38         this._selectedItems.clear();
39       }
40     
41       /**
42        * The class to add to the shapes when selected.
43        *
44        * @type {String}
45        */
46       set selectedClass(value) {
47         this._selectedClass = value;
48       }
49     
50       /**
51        * The class to add to the shapes when selected.
52        *
53        * @type {String}
54        */
55       get selectedClass() {
56         return this._selectedClass;
57       }
58     
59       /**
60        * An array containing all the selected items of the layer.
61        *
62        * @type {Array}
63        */
64       get selectedItems() {
65         return [...this._selectedItems];
66       }
67     
68       /**
69        * @param {Element} $item - The item to select.
70        * @param {Object} datum - Not used in this implementation. Could be
71        *    used to mark the data as selected.
72        * @todo - Pass the shape object to get the accessors ?
73        */
74       select($item, datum) {
75         $item.classList.add(this.selectedClass);
76         this._selectedItems.add($item);
77       }
78     
79       /**
80        * @param {Element} $item - The item to unselect.
81        * @param {Object} datum - Not used in this implementation. Could be
82        *    used to mark the data as selected.
83        * @todo - Pass the shape object to get the accessors ?
84        */
85       unselect($item, datum) {
86         $item.classList.remove(this.selectedClass);
87         this._selectedItems.delete($item);
88       }
89     
90       /**
91        * @param {Element} $item - The item to toggle selection.
92        * @param {Object} datum - Not used in this implementation. Could be
93        *    used to mark the data as selected.
94        * @todo - Pass the shape object to get the accessors ?
95        */
96       toggleSelection($item, datum) {
97         const method = this._selectedItems.has($item) ? 'unselect' : 'select';
98         this[method]($item);
99       }
100    
101      /**
102       * Interface method to override in order to define its particular behavior when
103       * interacted with.
104       *
105       * @param {Object} renderingContext - The layer rendering context.
106       * @param {BaseShape} shape - The shape object to be edited.
107       * @param {Object|Array} datum - The related datum to modify.
108       * @param {Number} dx - The value of the interaction in the x axis (in pixels).
109       * @param {Number} dy - The value of the interaction in the y axis (in pixels).
110       * @param {Element} $target - The target DOM element of the interaction.
111       */
112      edit(renderingContext, shape, datum, dx, dy, $target) {
113        // must be implemented in children
114      }
115    }
116    

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/core/timeline.js

1      import events from 'events';
2      
3      import Keyboard from '../interactions/keyboard';
4      import LayerTimeContext from './layer-time-context';
5      import Surface from '../interactions/surface';
6      import TimelineTimeContext from './timeline-time-context';
7      import Track from './track';
8      import TrackCollection from './track-collection';
9      
10     
11     /**
12      * Is the main entry point to create a temporal visualization.
13      *
14      * A `timeline` instance mainly provides the context for any visualization of
15      * temporal data and maintains the hierarchy of `Track`, `Layer` and `Shape`
16      * over the entiere visualisation.
17      *
18      * Its main responsabilites are:
19      * - maintaining the temporal consistency accross the visualisation through
20      *   its `timeContext` property (instance of `TimelineTimeContext`).
21      * - handling interactions to its current state (acting here as a simple
22      *   state machine).
23      *
24      * @TODO insert figure
25      *
26      * It also contains a reference to all the register track allowing to `render`
27      * or `update` all the layer from a single entry point.
28      *
29      * ## Example Usage
30      *
31      * ```js
32      * const visibleWidth = 500; // default width in pixels for all created `Track`
33      * const duration = 10; // the visible area represents 10 seconds
34      * const pixelsPerSeconds = visibleWidth / duration;
35      * const timeline = new ui.core.Timeline(pixelsPerSecond, width);
36      * ```
37      */
38     export default class Timeline extends events.EventEmitter {
39       /**
40        * @param {Number} [pixelsPerSecond=100] - the default scaling between time and pixels.
41        * @param {Number} [visibleWidth=1000] - the default visible area for all registered tracks.
42        */
43       constructor(pixelsPerSecond = 100, visibleWidth = 1000, {
44         registerKeyboard = true
45       } = {}) {
46     
47         super();
48     
49         this._tracks = new TrackCollection(this);
50         this._state = null;
51     
52         // default interactions
53         this._surfaceCtor = Surface;
54     
55         if (registerKeyboard) {
56           this.createInteraction(Keyboard, document);
57         }
58     
59         // stores
60         this._trackById = {};
61         this._groupedLayers = {};
62     
63         /** @type {TimelineTimeContext} - master time context for the visualization. */
64         this.timeContext = new TimelineTimeContext(pixelsPerSecond, visibleWidth);
65       }
66     
67       /**
68        * Returns `TimelineTimeContext`'s `offset` time domain value.
69        *
70        * @type {Number} [offset=0]
71        */
72       get offset() {
73         return this.timeContext.offset;
74       }
75     
76       /**
77        * Updates `TimelineTimeContext`'s `offset` time domain value.
78        *
79        * @type {Number} [offset=0]
80        */
81       set offset(value) {
82         this.timeContext.offset = value;
83       }
84     
85       /**
86        * Returns the `TimelineTimeContext`'s `zoom` value.
87        *
88        * @type {Number} [offset=0]
89        */
90       get zoom() {
91         return this.timeContext.zoom;
92       }
93     
94       /**
95        * Updates the `TimelineTimeContext`'s `zoom` value.
96        *
97        * @type {Number} [offset=0]
98        */
99       set zoom(value) {
100        this.timeContext.zoom = value;
101      }
102    
103      /**
104       * Returns the `TimelineTimeContext`'s `pixelsPerSecond` ratio.
105       *
106       * @type {Number} [offset=0]
107       */
108      get pixelsPerSecond() {
109        return this.timeContext.pixelsPerSecond;
110      }
111    
112      /**
113       * Updates the `TimelineTimeContext`'s `pixelsPerSecond` ratio.
114       *
115       * @type {Number} [offset=0]
116       */
117      set pixelsPerSecond(value) {
118        this.timeContext.pixelsPerSecond = value;
119      }
120    
121      /**
122       * Returns the `TimelineTimeContext`'s `visibleWidth` pixel domain value.
123       *
124       * @type {Number} [offset=0]
125       */
126      get visibleWidth() {
127        return this.timeContext.visibleWidth;
128      }
129    
130      /**
131       * Updates the `TimelineTimeContext`'s `visibleWidth` pixel domain value.
132       *
133       * @type {Number} [offset=0]
134       */
135      set visibleWidth(value) {
136        this.timeContext.visibleWidth = value;
137      }
138    
139      /**
140       * Returns `TimelineTimeContext`'s `timeToPixel` transfert function.
141       *
142       * @type {Function}
143       */
144      get timeToPixel() {
145        return this.timeContext.timeToPixel;
146      }
147    
148      /**
149       * Returns `TimelineTimeContext`'s `visibleDuration` helper value.
150       *
151       * @type {Number}
152       */
153      get visibleDuration() {
154        return this.timeContext.visibleDuration;
155      }
156    
157      /**
158       * Updates the `TimelineTimeContext`'s `maintainVisibleDuration` value.
159       * Defines if the duration of the visible area should be maintain when
160       * the `visibleWidth` attribute is updated.
161       *
162       * @type {Boolean}
163       */
164      set maintainVisibleDuration(bool) {
165        this.timeContext.maintainVisibleDuration = bool;
166      }
167    
168      /**
169       * Returns `TimelineTimeContext`'s `maintainVisibleDuration` current value.
170       *
171       * @type {Boolean}
172       */
173      get maintainVisibleDuration() {
174        return this.timeContext.maintainVisibleDuration;
175      }
176    
177      /**
178       * Object maintaining arrays of `Layer` instances ordered by their `groupId`.
179       * Is used internally by the `TrackCollection` instance.
180       *
181       * @type {Object}
182       */
183      get groupedLayers() {
184        return this._groupedLayers;
185      }
186    
187      /**
188       * Overrides the default `Surface` that is instanciated on each `Track`
189       * instance. This methos should be called before adding any `Track` instance
190       * to the current `timeline`.
191       *
192       * @param {EventSource} ctor - The constructor to use in order to catch mouse
193       *    events on each `Track` instances.
194       */
195      configureSurface(ctor) {
196        this._surfaceCtor = ctor;
197      }
198    
199      /**
200       * Factory method to add interaction modules the timeline should listen to.
201       * By default, the timeline instanciate a global `Keyboard` instance and a
202       * `Surface` instance on each container.
203       * Should be used to install new interactions implementing the `EventSource` interface.
204       *
205       * @param {EventSource} ctor - The contructor of the interaction module to instanciate.
206       * @param {Element} $el - The DOM element which will be binded to the `EventSource` module.
207       * @param {Object} [options={}] - Options to be applied to the `ctor`.
208       */
209      createInteraction(ctor, $el, options = {}) {
210        const interaction = new ctor($el, options);
211        interaction.on('event', (e) => this._handleEvent(e));
212      }
213    
214      /**
215       * Returns a list of the layers situated under the position of a `WaveEvent`.
216       *
217       * @param {WavesEvent} e - An event triggered by a `WaveEvent`
218       * @return {Array} - Matched layers
219       */
220      getHitLayers(e) {
221        const clientX = e.originalEvent.clientX;
222        const clientY = e.originalEvent.clientY;
223        let layers = [];
224    
225        this.layers.forEach((layer) => {
226          if (!layer.params.hittable) { return; }
227          const br = layer.$el.getBoundingClientRect();
228    
229          if (
230            clientX > br.left && clientX < br.right &&
231            clientY > br.top && clientY < br.bottom
232          ) {
233            layers.push(layer);
234          }
235        });
236    
237        return layers;
238      }
239    
240      /**
241       * The callback that is used to listen to interactions modules.
242       *
243       * @param {WaveEvent} e - An event generated by an interaction modules (`EventSource`).
244       */
245      _handleEvent(e) {
246        const hitLayers = (e.source === 'surface') ?
247          this.getHitLayers(e) : null;
248        // emit event as a middleware
249        this.emit('event', e, hitLayers);
250        // propagate to the state
251        if (!this._state) { return; }
252        this._state.handleEvent(e, hitLayers);
253      }
254    
255      /**
256       * Updates the state of the timeline.
257       *
258       * @type {BaseState}
259       */
260      set state(state) {
261        if (this._state) { this._state.exit(); }
262        this._state = state;
263        if (this._state) { this._state.enter(); }
264      }
265    
266      /**
267       * Returns the current state of the timeline.
268       *
269       * @type {BaseState}
270       */
271      get state() {
272        return this._state;
273      }
274    
275      /**
276       * Returns the `TrackCollection` instance.
277       *
278       * @type {TrackCollection}
279       */
280      get tracks() {
281        return this._tracks;
282      }
283    
284      /**
285       * Returns the list of all registered layers.
286       *
287       * @type {Array}
288       */
289      get layers() {
290        return this._tracks.layers;
291      }
292    
293      /**
294       * Adds a new track to the timeline.
295       *
296       * @param {Track} track - The new track to be registered in the timeline.
297       * @param {String} [trackId=null] - Optionnal unique id to associate with
298       *    the track, this id only exists in timeline's context and should be used
299       *    in conjonction with `addLayer` method.
300       */
301      add(track, trackId = null) {
302        if (this.tracks.indexOf(track) !== -1) {
303          throw new Error('track already added to the timeline');
304        }
305    
306        this._registerTrackId(track, trackId);
307        track.configure(this.timeContext);
308    
309        this.tracks.push(track);
310        this.createInteraction(this._surfaceCtor, track.$el);
311      }
312    
313      /**
314       * Removes a track from the timeline.
315       *
316       * @param {Track} track - the track to remove from the timeline.
317       * @todo not implemented.
318       */
319      remove(track) {
320        // should destroy interaction too, avoid ghost eventListeners
321      }
322    
323      /**
324       * Helper to create a new `Track` instance. The `track` is added,
325       * rendered and updated before being returned.
326       *
327       * @param {Element} $el - The DOM element where the track should be inserted.
328       * @param {Number} trackHeight - The height of the newly created track.
329       * @param {String} [trackId=null] - Optionnal unique id to associate with
330       *    the track, this id only exists in timeline's context and should be used in
331       *    conjonction with `addLayer` method.
332       * @return {Track}
333       */
334      createTrack($el, trackHeight = 100, trackId = null) {
335        const track = new Track($el, trackHeight);
336        // Add track to the timeline
337        this.add(track, trackId);
338        track.render();
339        track.update();
340    
341        return track;
342      }
343    
344      /**
345       * If track id is defined, associate a track with a unique id.
346       */
347      _registerTrackId(track, trackId) {
348        if (trackId !== null) {
349          if (this._trackById[trackId] !== undefined) {
350            throw new Error(`trackId: "${trackId}" is already used`);
351          }
352    
353          this._trackById[trackId] = track;
354        }
355      }
356    
357      /**
358       * Helper to add a `Layer` instance into a given `Track`. Is designed to be
359       * used in conjonction with the `Timeline~getLayersByGroup` method. The
360       * layer is internally rendered and updated.
361       *
362       * @param {Layer} layer - The `Layer` instance to add into the visualization.
363       * @param {(Track|String)} trackOrTrackId - The `Track` instance (or its `id`
364       *    as defined in the `createTrack` method) where the `Layer` instance should be inserted.
365       * @param {String} [groupId='default'] - An optionnal group id in which the
366       *    `Layer` should be inserted.
367       * @param {Boolean} [isAxis] - Set to `true` if the added `layer` is an
368       *    instance of `AxisLayer` (these layers shares the `TimlineTimeContext` instance
369       *    of the timeline).
370       */
371      addLayer(layer, trackOrTrackId, groupId = 'default', isAxis = false) {
372        let track = trackOrTrackId;
373    
374        if (typeof trackOrTrackId === 'string') {
375          track = this.getTrackById(trackOrTrackId);
376        }
377    
378        // creates the `LayerTimeContext` if not present
379        if (!layer.timeContext) {
380          const timeContext = isAxis ?
381            this.timeContext : new LayerTimeContext(this.timeContext);
382    
383          layer.setTimeContext(timeContext);
384        }
385    
386        // we should have a Track instance at this point
387        track.add(layer);
388    
389        if (!this._groupedLayers[groupId]) {
390          this._groupedLayers[groupId] = [];
391        }
392    
393        this._groupedLayers[groupId].push(layer);
394    
395        layer.render();
396        layer.update();
397      }
398    
399      /**
400       * Removes a layer from its track. The layer is detatched from the DOM but
401       * can still be reused later.
402       *
403       * @param {Layer} layer - The layer to remove.
404       */
405      removeLayer(layer) {
406        this.tracks.forEach(function(track) {
407          const index = track.layers.indexOf(layer);
408          if (index !== -1) { track.remove(layer); }
409        });
410    
411        // clean references in helpers
412        for (let groupId in this._groupedLayers) {
413          const group = this._groupedLayers[groupId];
414          const index = group.indexOf(layer);
415    
416          if (index !== -1) { group.splice(layer, 1); }
417    
418          if (!group.length) {
419            delete this._groupedLayers[groupId];
420          }
421        }
422      }
423    
424      /**
425       * Returns a `Track` instance from it's given id.
426       *
427       * @param {String} trackId
428       * @return {Track}
429       */
430      getTrackById(trackId) {
431        return this._trackById[trackId];
432      }
433    
434      /**
435       * Returns the track containing a given DOM Element, returns null if no match found.
436       *
437       * @param {Element} $el - The DOM Element to be tested.
438       * @return {Track}
439       */
440      getTrackFromDOMElement($el) {
441        let $svg = null;
442        let track = null;
443        // find the closest `.track` element
444        do {
445          if ($el.classList.contains('track')) {
446            $svg = $el;
447          }
448          $el = $el.parentNode;
449        } while ($svg === null);
450        // find the related `Track`
451        this.tracks.forEach(function(_track) {
452          if (_track.$svg === $svg) { track = _track; }
453        });
454    
455        return track;
456      }
457    
458      /**
459       * Returns an array of layers from their given group id.
460       *
461       * @param {String} groupId - The id of the group as defined in `addLayer`.
462       * @return {(Array|undefined)}
463       */
464      getLayersByGroup(groupId) {
465        return this._groupedLayers[groupId];
466      }
467    
468      /**
469       * Iterates through the added tracks.
470       */
471      *[Symbol.iterator]() {
472        yield* this.tracks[Symbol.iterator]();
473      }
474    }
475    

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/interactions/surface.js

1      import EventSource from './event-source';
2      import WaveEvent from './wave-event';
3      
4      
5      /**
6       * Normalizes mouse user interactions with the timeline upon the DOM
7       * container element of `Track` instances. As soon as a `track` is added to a
8       * `timeline`, its attached `Surface` instance will emit the mouse events.
9       */
10     export default class Surface extends EventSource {
11       /**
12        * @param {DOMElement} el - The DOM element to listen.
13        * @todo - Add some padding to the surface.
14        */
15       constructor($el) {
16         super($el);
17     
18         /**
19          * The name of the event source.
20          * @type {String}
21          */
22         this.sourceName = 'surface';
23         this._mouseDownEvent = null;
24         this._lastEvent = null;
25       }
26     
27       /**
28        * Factory method for `Event` class
29        */
30       _createEvent(type, e) {
31         const event = new WaveEvent(this.sourceName, type, e);
32     
33         const pos = this._getRelativePosition(e);
34         event.x = pos.x;
35         event.y = pos.y;
36     
37         return event;
38       }
39     
40       /**
41        * Returns the x, y coordinates coordinates relative to the surface element.
42        *
43        * @param {Event} e - Raw event from listener.
44        * @return {Object}
45        * @todo - handle padding.
46        */
47       _getRelativePosition(e) {
48         // @TODO: should be able to ignore padding
49         let x = 0;
50         let y = 0;
51         const clientRect = this.$el.getBoundingClientRect();
52         const scrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft;
53         const scrollTop  = document.body.scrollTop + document.documentElement.scrollTop;
54     
55         // Adapted from http://www.quirksmode.org/js/events_properties.html#position
56         if (e.pageX || e.pageY) {
57           x = e.pageX;
58           y = e.pageY;
59         } else if (e.clientX || e.clientY) {
60           // Normalize to pageX, pageY
61           x = e.clientX + scrollLeft;
62           y = e.clientY + scrollTop;
63         }
64     
65         // clientRect refers to the client, not to the page
66         x = x - (clientRect.left + scrollLeft);
67         y = y - (clientRect.top  + scrollTop );
68     
69         return { x, y };
70       }
71     
72       _defineArea(e, mouseDownEvent, lastEvent) {
73         if (!mouseDownEvent || !lastEvent) { return; }
74         e.dx = e.x - lastEvent.x;
75         e.dy = e.y - lastEvent.y;
76     
77         const left = mouseDownEvent.x < e.x ? mouseDownEvent.x : e.x;
78         const top  = mouseDownEvent.y < e.y ? mouseDownEvent.y : e.y;
79         const width  = Math.abs(Math.round(e.x - mouseDownEvent.x));
80         const height = Math.abs(Math.round(e.y - mouseDownEvent.y));
81     
82         e.area = { left, top, width, height };
83       }
84     
85       /**
86        * Keep this private to avoid double event binding. Main logic of the surface
87        * is here. Should be extended with needed events (mouseenter, mouseleave,
88        * wheel ...).
89        */
90       _bindEvents() {
91         const onMouseDown = (e) => {
92           // By removing the previous selection we prevent bypassing the mousemove events coming from SVG in Firefox.
93           window.getSelection().removeAllRanges();
94           const event = this._createEvent('mousedown', e);
95     
96     
97           this._mouseDownEvent = event;
98           this._lastEvent = event;
99           // Register mousemove and mouseup listeners on window
100          window.addEventListener('mousemove', onMouseMove, false);
101          window.addEventListener('mouseup', onMouseUp, false);
102    
103          this.emit('event', event);
104        };
105    
106        const onMouseMove = (e) => {
107          let event = this._createEvent('mousemove', e);
108          this._defineArea(event, this._mouseDownEvent, this._lastEvent);
109          // Update `lastEvent` for next call
110          this._lastEvent = event;
111    
112          this.emit('event', event);
113        };
114    
115        const onMouseUp = (e) => {
116          let event = this._createEvent('mouseup', e);
117          this._defineArea(event, this._mouseDownEvent, this._lastEvent);
118    
119    
120          this._mouseDownEvent = null;
121          this._lastEvent = null;
122          // Remove mousemove and mouseup listeners on window
123          window.removeEventListener('mousemove', onMouseMove);
124          window.removeEventListener('mouseup', onMouseUp);
125    
126          this.emit('event', event);
127        };
128    
129        const onClick = (e) => {
130          let event = this._createEvent('click', e);
131          this.emit('event', event);
132        };
133    
134        const onDblClick = (e) => {
135          let event = this._createEvent('dblclick', e);
136          this.emit('event', event);
137        };
138    
139        const onMouseOver = (e) => {
140          let event = this._createEvent('mouseover', e);
141          this.emit('event', event);
142        };
143    
144        const onMouseOut = (e) => {
145          let event = this._createEvent('mouseout', e);
146          this.emit('event', event);
147        };
148    
149        // Bind callbacks
150        this.$el.addEventListener('mousedown', onMouseDown, false);
151        this.$el.addEventListener('click', onClick, false);
152        this.$el.addEventListener('dblclick', onDblClick, false);
153        this.$el.addEventListener('mouseover', onMouseOver, false);
154        this.$el.addEventListener('mouseout', onMouseOut, false);
155      }
156    }
157    

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/core/timeline-time-context.js

1      import scales from '../utils/scales';
2      
3      
4      /**
5       * Defines and maintains global aspects of the visualization concerning the
6       * relations between time and pixels.
7       *
8       * The `TimelineTimeContext` instance (unique across a visualization) keeps the
9       * main reference on how many pixels should be used to represent one second
10      * though its `timeToPixel` method. The attributes `zoom`, `offset` (i.e. from
11      * origin) and `visibleWidth` allow for navigating in time and for maintaining
12      * view consistency upon the DOM structure (`<svg>` and `<g>` tags) created by
13      * the registered tracks.
14      *
15      * It also maintain an array of all references to `LayerTimeContext` instances
16      * to propagate to `layers`, changes made on the time to pixel representation.
17      *
18      * [example usage](./examples/time-contexts.html)
19      */
20     export default class TimelineTimeContext {
21       /**
22        * @param {Number} pixelsPerSecond - The number of pixels that should be
23        *    used to display one second.
24        * @param {Number} visibleWidth - The default with of the visible area
25        *    displayed in `tracks` (in pixels).
26        */
27       constructor(pixelsPerSecond, visibleWidth) {
28         this._children = [];
29     
30         this._timeToPixel = null;
31         this._offset = 0;
32         this._zoom = 1;
33         this._computedPixelsPerSecond = pixelsPerSecond;
34         // params
35         this._visibleWidth = visibleWidth;
36         this._maintainVisibleDuration = false;
37     
38         // create the timeToPixel scale
39         const scale = scales.linear()
40           .domain([0, 1])
41           .range([0, pixelsPerSecond]);
42     
43         this._timeToPixel = scale;
44     
45         this._originalPixelsPerSecond = this._computedPixelsPerSecond;
46       }
47     
48       /**
49        * Returns the number of pixels per seconds ignoring the current zoom value.
50        *
51        * @type {Number}
52        */
53       get pixelsPerSecond() {
54         return this._originalPixelsPerSecond;
55       }
56     
57       /**
58        * Updates all the caracteristics of this object according to the new
59        * given value of pixels per seconds. Propagates the changes to the
60        * `LayerTimeContext` children.
61        *
62        * @type {Number}
63        */
64       set pixelsPerSecond(value) {
65         this._computedPixelsPerSecond = value * this.zoom;
66         this._originalPixelsPerSecond = value;
67         this._updateTimeToPixelRange();
68     
69         // force children scale update
70         this._children.forEach(function(child) {
71           if (child.stretchRatio === 1) { return; }
72           child.stretchRatio = child.stretchRatio;
73         });
74       }
75     
76       /**
77        * Returns the number of pixels per seconds including the current zoom value.
78        *
79        * @type {Number}
80        */
81       get computedPixelsPerSecond() {
82         return this._computedPixelsPerSecond;
83       }
84     
85       /**
86        * Returns the current offset applied to the registered `Track` instances
87        * from origin (in seconds).
88        *
89        * @type {Number}
90        */
91       get offset() {
92         return this._offset;
93       }
94     
95       /**
96        * Sets the offset to apply to the registered `Track` instances from origin
97        * (in seconds).
98        *
99        * @type {Number}
100       */
101      set offset(value) {
102        this._offset = value;
103      }
104    
105      /**
106       * Returns the current zoom level applied to the whole visualization.
107       *
108       * @type {Number}
109       */
110      get zoom() {
111        return this._zoom;
112      }
113    
114      /**
115       * Sets the zoom ratio for the whole visualization.
116       *
117       * @type {Number}
118       */
119      set zoom(value) {
120        // Compute change to propagate to children who have their own timeToPixel
121        const ratioChange = value / this._zoom;
122        this._zoom = value;
123        this._computedPixelsPerSecond = this._originalPixelsPerSecond * value;
124        this._updateTimeToPixelRange();
125    
126        this._children.forEach(function(child) {
127          if (child.stretchRatio === 1) { return; }
128          child.stretchRatio = child.stretchRatio * ratioChange;
129        });
130      }
131    
132      /**
133       * Returns the visible width of the `Track` instances.
134       *
135       * @type {Number}
136       */
137      get visibleWidth() {
138        return this._visibleWidth;
139      }
140    
141      /**
142       * Sets the visible width of the `Track` instances.
143       *
144       * @type {Number}
145       */
146      set visibleWidth(value) {
147        const widthRatio = value / this.visibleWidth;
148        this._visibleWidth = value;
149    
150        if (this.maintainVisibleDuration) {
151          this.pixelsPerSecond = this._computedPixelsPerSecond * widthRatio;
152        }
153      }
154    
155      /**
156       * Returns the duration displayed by `Track` instances.
157       *
158       * @type {Number}
159       */
160      get visibleDuration() {
161        return this.visibleWidth / this._computedPixelsPerSecond;
162      }
163    
164      /**
165       * Returns if the duration displayed by tracks should be maintained when
166       * their width is updated.
167       *
168       * @type {Number}
169       */
170      get maintainVisibleDuration() {
171        return this._maintainVisibleDuration;
172      }
173    
174      /**
175       * Defines if the duration displayed by tracks should be maintained when
176       * their width is updated.
177       *
178       * @type {Boolean}
179       */
180      set maintainVisibleDuration(bool) {
181        this._maintainVisibleDuration = bool;
182      }
183    
184      /**
185       * Returns the time to pixel trasfert function.
186       *
187       * @type {Function}
188       */
189      get timeToPixel() {
190        return this._timeToPixel;
191      }
192    
193      _updateTimeToPixelRange() {
194        this.timeToPixel.range([0, this._computedPixelsPerSecond]);
195      }
196    }
197    

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/core/track.js

1      import ns from './namespace';
2      
3      
4      /**
5       * Acts as a placeholder to organize the vertical layout of the visualization
6       * and the horizontal alignement to an abscissa that correspond to a common
7       * time reference. It basically offer a view on the overall timeline.
8       *
9       * Tracks are inserted into a given DOM element, allowing to create DAW like
10      * representations. Each `Track` instance can host multiple `Layer` instances.
11      * A track must be added to a timeline before being updated.
12      *
13      * ### A timeline with 3 tracks:
14      *
15      * ```
16      * 0                 6                               16
17      * +- - - - - - - - -+-------------------------------+- - - - - - -
18      * |                 |x track 1 xxxxxxxxxxxxxxxxxxxxx|
19      * +- - - - - - - - -+-------------------------------+- - - - - - -
20      * |                 |x track 2 xxxxxxxxxxxxxxxxxxxxx|
21      * +- - - - - - - - -+-------------------------------+- - - - - - -
22      * |                 |x track 3 xxxxxxxxxxxxxxxxxxxxx|
23      * +- - - - - - - - ---------------------------------+- - - - - - -
24      * +----------------->
25      * timeline.timeContext.timeToPixel(timeline.timeContext.offset)
26      *
27      *                   <------------------------------->
28      *                   timeline's tracks defaults to 1000px
29      *                   with a default pixelsPerSecond of 100px/s.
30      *                   and a default `stretchRatio = 1`
31      *                   track1 shows 10 seconds of the timeline
32      * ```
33      *
34      * ### Track DOM structure
35      *
36      * ```html
37      * <svg width="${visibleWidth}">
38      *   <!-- background -->
39      *   <rect><rect>
40      *   <!-- main view -->
41      *   <g class="offset" transform="translate(${offset}, 0)">
42      *     <g class="layout">
43      *       <!-- layers -->
44      *     </g>
45      *   </g>
46      *   <g class="interactions"><!-- for feedback --></g>
47      * </svg>
48      * ```
49      */
50     export default class Track {
51       /**
52        * @param {DOMElement} $el
53        * @param {Number} [height = 100]
54        */
55       constructor($el, height = 100) {
56         this._height = height;
57     
58         /**
59          * The DOM element in which the track is created.
60          * @type {Element}
61          */
62         this.$el = $el;
63         /**
64          * A placeholder to add shapes for interactions feedback.
65          * @type {Element}
66          */
67         this.$interactions = null;
68         /** @type {Element} */
69         this.$layout = null;
70         /** @type {Element} */
71         this.$offset = null;
72         /** @type {Element} */
73         this.$svg = null;
74         /** @type {Element} */
75         this.$background = null;
76     
77         /**
78          * An array of all the layers belonging to the track.
79          * @type {Array<Layer>}
80          */
81         this.layers = [];
82         /**
83          * The context used to maintain the DOM structure of the track.
84          * @type {TimelineTimeContext}
85          */
86         this.renderingContext = null;
87     
88         this._createContainer();
89       }
90     
91       /**
92        * Returns the height of the track.
93        *
94        * @type {Number}
95        */
96       get height() {
97         return this._height;
98       }
99     
100      /**
101       * Sets the height of the track.
102       *
103       * @todo propagate to layers, keeping ratio? could be handy for vertical
104       *    resize. This is why a set/get is implemented here.
105       * @type {Number}
106       */
107      set height(value) {
108        this._height = value;
109      }
110    
111      /**
112       * This method is called when the track is added to the timeline. The
113       * track cannot be updated without being added to a timeline.
114       *
115       * @private
116       * @param {TimelineTimeContext} renderingContext
117       */
118      configure(renderingContext) {
119        this.renderingContext = renderingContext;
120      }
121    
122      /**
123       * Destroy the track. The layers from this track can still be reused elsewhere.
124       */
125      destroy() {
126        // Detach everything from the DOM
127        this.$el.removeChild(this.$svg);
128        this.layers.forEach((layer) => this.$layout.removeChild(layer.$el));
129        // clean references
130        this.$el = null;
131        this.renderingContext = null;
132        this.layers.length = 0;
133      }
134    
135      /**
136       * Creates the DOM structure of the track.
137       */
138      _createContainer() {
139        const $svg = document.createElementNS(ns, 'svg');
140        $svg.setAttributeNS(null, 'shape-rendering', 'optimizeSpeed');
141        $svg.setAttributeNS(null, 'height', this.height);
142        $svg.setAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
143        $svg.classList.add('track');
144    
145        const $background = document.createElementNS(ns, 'rect');
146        $background.setAttributeNS(null, 'height', '100%');
147        $background.setAttributeNS(null, 'width', '100%');
148        $background.style.fillOpacity = 0;
149        // $background.style.pointerEvents = 'none';
150    
151        const $defs = document.createElementNS(ns, 'defs');
152    
153        const $offsetGroup = document.createElementNS(ns, 'g');
154        $offsetGroup.classList.add('offset');
155    
156        const $layoutGroup = document.createElementNS(ns, 'g');
157        $layoutGroup.classList.add('layout');
158    
159        const $interactionsGroup = document.createElementNS(ns, 'g');
160        $interactionsGroup.classList.add('interactions');
161    
162        $offsetGroup.appendChild($layoutGroup);
163        $svg.appendChild($defs);
164        $svg.appendChild($background);
165        $svg.appendChild($offsetGroup);
166        $svg.appendChild($interactionsGroup);
167        this.$el.appendChild($svg);
168        // removes additionnal height added who knows why...
169        this.$el.style.fontSize = 0;
170        // fixes one of the (many ?) weird canvas rendering bugs in Chrome
171        this.$el.style.transform = 'translateZ(0)';
172    
173        this.$layout = $layoutGroup;
174        this.$offset = $offsetGroup;
175        this.$interactions = $interactionsGroup;
176        this.$svg = $svg;
177        this.$background = $background;
178      }
179    
180      /**
181       * Adds a layer to the track.
182       *
183       * @param {Layer} layer - the layer to add to the track.
184       */
185      add(layer) {
186        this.layers.push(layer);
187        // Create a default renderingContext for the layer if missing
188        this.$layout.appendChild(layer.$el);
189      }
190    
191      /**
192       * Removes a layer from the track. The layer can be reused elsewhere.
193       *
194       * @param {Layer} layer - the layer to remove from the track.
195       */
196      remove(layer) {
197        this.layers.splice(this.layers.indexOf(layer), 1);
198        // Removes layer from its container
199        this.$layout.removeChild(layer.$el);
200      }
201    
202      /**
203       * Tests if a given element belongs to the track.
204       *
205       * @param {Element} $el
206       * @return {bool}
207       */
208      hasElement($el) {
209        do {
210          if ($el === this.$el) {
211            return true;
212          }
213    
214          $el = $el.parentNode;
215        } while ($el !== null);
216    
217        return false;
218      }
219    
220      /**
221       * Render all the layers added to the track.
222       */
223      render() {
224        for (let layer of this) { layer.render(); }
225      }
226    
227      /**
228       * Updates the track DOM structure and updates the layers.
229       *
230       * @param {Array<Layer>} [layers=null] - if not null, a subset of the layers to update.
231       */
232      update(layers = null) {
233        this.updateContainer();
234        this.updateLayers(layers);
235      }
236    
237      /**
238       * Updates the track DOM structure.
239       */
240      updateContainer() {
241        const $svg = this.$svg;
242        const $offset = this.$offset;
243        // Should be in some update layout
244        const renderingContext = this.renderingContext;
245        const height = this.height;
246        const width = Math.round(renderingContext.visibleWidth);
247        const offsetX = Math.round(renderingContext.timeToPixel(renderingContext.offset));
248        const translate = `translate(${offsetX}, 0)`;
249    
250        $svg.setAttributeNS(null, 'height', height);
251        $svg.setAttributeNS(null, 'width', width);
252        $svg.setAttributeNS(null, 'viewbox', `0 0 ${width} ${height}`);
253    
254        $offset.setAttributeNS(null, 'transform', translate);
255      }
256    
257      /**
258       * Updates the layers.
259       *
260       * @param {Array<Layer>} [layers=null] - if not null, a subset of the layers to update.
261       */
262      updateLayers(layers = null) {
263        layers = (layers === null) ? this.layers : layers;
264    
265        layers.forEach((layer) => {
266          if (this.layers.indexOf(layer) === -1) { return; }
267          layer.update();
268        });
269      }
270    
271      /**
272       * Iterates through the added layers.
273       */
274      *[Symbol.iterator]() {
275        yield* this.layers[Symbol.iterator]();
276      }
277    }
278    

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/core/track-collection.js

1      import Layer from './layer';
2      
3      
4      /**
5       * Collection hosting all the `Track` instances registered into the timeline.
6       * It provides shorcuts to trigger `render` / `update` methods on tracks or
7       * layers. Extend built-in Array
8       */
9      export default class TrackCollection extends Array {
10       constructor(timeline) {
11         super();
12     
13         this._timeline = timeline;
14       }
15     
16       // @note - should be in the timeline ?
17       // @todo - allow to pass an array of layers
18       _getLayersOrGroups(layerOrGroup = null) {
19         let layers = null;
20     
21         if (typeof layerOrGroup === 'string') {
22           layers = this._timeline.groupedLayers[layerOrGroup];
23         } else if (layerOrGroup instanceof Layer) {
24           layers = [layerOrGroup];
25         } else {
26           layers = this.layers;
27         }
28     
29         return layers;
30       }
31     
32       // @NOTE keep this ?
33       // could prepare some vertical resizing ability
34       // this should be able to modify the layers yScale to be really usefull
35     
36       /**
37        * @type {Number} - Updates the height of all tracks at once.
38        * @todo - Propagate to layers, not usefull for now.
39        */
40       set height(value) {
41         this.forEach((track) => track.height = value);
42       }
43     
44       get height() {
45         return track.height;
46       }
47     
48       /**
49        * An array of all registered layers.
50        *
51        * @type {Array<Layer>}
52        */
53       get layers() {
54         let layers = [];
55         this.forEach((track) => layers = layers.concat(track.layers));
56     
57         return layers;
58       }
59     
60       /**
61        * Render all tracks and layers. When done, the timeline triggers a `render` event.
62        */
63       render() {
64         this.forEach((track) => track.render());
65         this._timeline.emit('render');
66       }
67     
68       /**
69        * Updates all tracks and layers. When done, the timeline triggers a
70        * `update` event.
71        *
72        * @param {Layer|String} layerOrGroup - Filter the layers to update by
73        *    passing the `Layer` instance to update or a `groupId`
74        */
75       update(layerOrGroup) {
76         const layers = this._getLayersOrGroups(layerOrGroup);
77         this.forEach((track) => track.update(layers));
78         this._timeline.emit('update', layers);
79       }
80     
81       /**
82        * Updates all `Track` containers, layers are not updated with this method.
83        * When done, the timeline triggers a `update:containers` event.
84        */
85       updateContainer(/* trackOrTrackIds */) {
86         this.forEach((track) => track.updateContainer());
87         this._timeline.emit('update:containers');
88       }
89     
90       /**
91        * Updates all layers. When done, the timeline triggers a `update:layers` event.
92        *
93        * @param {Layer|String} layerOrGroup - Filter the layers to update by
94        *    passing the `Layer` instance to update or a `groupId`
95        */
96       updateLayers(layerOrGroup) {
97         const layers = this._getLayersOrGroups(layerOrGroup);
98         this.forEach((track) => track.updateLayers(layers));
99         this._timeline.emit('update:layers', layers);
100      }
101    }
102    

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/shapes/marker.js

1      import BaseShape from './base-shape';
2      
3      
4      /**
5       * A shape to display a marker.
6       *
7       * [example usage](./examples/layer-marker.html)
8       */
9      export default class Marker extends BaseShape {
10       getClassName() { return 'marker'; }
11     
12       _getAccessorList() {
13         return { x: 0, color: '#ff0000' };
14       }
15     
16       _getDefaults() {
17         return {
18           handlerWidth: 7,
19           handlerHeight: 10,
20           displayHandlers: true,
21           opacity: 1,
22           color: 'red',
23         };
24       }
25     
26       render(renderingContext) {
27         if (this.$el) { return this.$el; }
28     
29         const height = renderingContext.height;
30     
31         this.$el = document.createElementNS(this.ns, 'g');
32         this.$line = document.createElementNS(this.ns, 'line');
33     
34         // draw line
35         this.$line.setAttributeNS(null, 'x', 0);
36         this.$line.setAttributeNS(null, 'y1', 0);
37         this.$line.setAttributeNS(null, 'y2', height);
38         this.$line.setAttributeNS(null, 'shape-rendering', 'crispEdges');
39     
40         this.$el.appendChild(this.$line);
41     
42         if (this.params.displayHandlers) {
43           this.$handler = document.createElementNS(this.ns, 'rect');
44     
45           this.$handler.setAttributeNS(null, 'x', -((this.params.handlerWidth) / 2 ));
46           this.$handler.setAttributeNS(null, 'y', renderingContext.height - this.params.handlerHeight);
47           this.$handler.setAttributeNS(null, 'width', this.params.handlerWidth);
48           this.$handler.setAttributeNS(null, 'height', this.params.handlerHeight);
49           this.$handler.setAttributeNS(null, 'shape-rendering', 'crispEdges');
50     
51           this.$el.appendChild(this.$handler);
52         }
53     
54         this.$el.style.opacity = this.params.opacity;
55     
56         return this.$el;
57       }
58     
59       update(renderingContext, datum) {
60         const x = renderingContext.timeToPixel(this.x(datum)) - 0.5;
61         const color = this.color(datum);
62     
63         this.$el.setAttributeNS(null, 'transform', `translate(${x}, 0)`);
64         this.$line.style.stroke = color;
65     
66         if (this.params.displayHandlers) {
67           this.$handler.style.fill = color;
68         }
69       }
70     
71       inArea(renderingContext, datum, x1, y1, x2, y2) {
72         // handlers only are selectable
73         const x = renderingContext.timeToPixel(this.x(datum));
74         const shapeX1 = x - (this.params.handlerWidth - 1) / 2;
75         const shapeX2 = shapeX1 + this.params.handlerWidth;
76         const shapeY1 = renderingContext.height - this.params.handlerHeight;
77         const shapeY2 = renderingContext.height;
78     
79         const xOverlap = Math.max(0, Math.min(x2, shapeX2) - Math.max(x1, shapeX1));
80         const yOverlap = Math.max(0, Math.min(y2, shapeY2) - Math.max(y1, shapeY1));
81         const area = xOverlap * yOverlap;
82     
83         return area > 0;
84       }
85     }
86     

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/behaviors/segment-behavior.js

1      import BaseBehavior from './base-behavior';
2      
3      
4      /**
5       * Defines the default behavior for a segment.
6       *
7       * [example usage](./examples/layer-marker.html)
8       */
9      export default class SegmentBehavior extends BaseBehavior {
10       edit(renderingContext, shape, datum, dx, dy, target) {
11         const classList = target.classList;
12         let action = 'move';
13     
14         if (classList.contains('handler') && classList.contains('left')) {
15           action = 'resizeLeft';
16         } else if (classList.contains('handler') && classList.contains('right')) {
17           action = 'resizeRight';
18         }
19     
20         this[`_${action}`](renderingContext, shape, datum, dx, dy, target);
21       }
22     
23       _move(renderingContext, shape, datum, dx, dy, target) {
24         const layerHeight = renderingContext.height;
25         // current values
26         const x = renderingContext.timeToPixel(shape.x(datum));
27         const y = renderingContext.valueToPixel(shape.y(datum));
28         const height = renderingContext.valueToPixel(shape.height(datum));
29         // target values
30         let targetX = Math.max(x + dx, 0);
31         let targetY = y - dy;
32     
33         // lock in layer's y axis
34         if (targetY < 0) {
35           targetY = 0;
36         } else if (targetY + height > layerHeight) {
37           targetY = layerHeight - height;
38         }
39     
40         shape.x(datum, renderingContext.timeToPixel.invert(targetX));
41         shape.y(datum, renderingContext.valueToPixel.invert(targetY));
42       }
43     
44       _resizeLeft(renderingContext, shape, datum, dx, dy, target) {
45         // current values
46         const x     = renderingContext.timeToPixel(shape.x(datum));
47         const width = renderingContext.timeToPixel(shape.width(datum));
48         // target values
49         let maxTargetX  = x + width;
50         let targetX     = x + dx < maxTargetX ? Math.max(x + dx, 0) : x;
51         let targetWidth = targetX !== 0 ? Math.max(width - dx, 1) : width;
52     
53         shape.x(datum, renderingContext.timeToPixel.invert(targetX));
54         shape.width(datum, renderingContext.timeToPixel.invert(targetWidth));
55       }
56     
57       _resizeRight(renderingContext, shape, datum, dx, dy, target) {
58         // current values
59         const width = renderingContext.timeToPixel(shape.width(datum));
60         // target values
61         let targetWidth = Math.max(width + dx, 1);
62     
63         shape.width(datum, renderingContext.timeToPixel.invert(targetWidth));
64       }
65     }
66     

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/shapes/trace-dots.js

1      import BaseShape from './base-shape';
2      
3      
4      /**
5       * A shape to display dots in a trace visualization (mean / range).
6       *
7       * [example usage](./examples/layer-trace.html)
8       */
9      export default class TraceDots extends BaseShape {
10       getClassName() { return 'trace-dots'; }
11     
12       _getAccessorList() {
13         return { x: 0, mean: 0, range: 0 };
14       }
15     
16       _getDefaults() {
17         return {
18           meanRadius: 3,
19           rangeRadius: 3,
20           meanColor: '#232323',
21           rangeColor: 'steelblue'
22         };
23       }
24     
25       render(renderingContext) {
26         if (this.$el) { return this.$el; }
27         // container
28         this.$el = document.createElementNS(this.ns, 'g');
29         // draw mean dot
30         this.$mean = document.createElementNS(this.ns, 'circle');
31         this.$mean.setAttributeNS(null, 'r', this.params.meanRadius);
32         this.$mean.setAttributeNS(null, 'stroke', this.params.meanColor);
33         this.$mean.setAttributeNS(null, 'fill', 'transparent');
34         this.$mean.classList.add('mean');
35         // range dots (0 => top, 1 => bottom)
36         this.$max = document.createElementNS(this.ns, 'circle');
37         this.$max.setAttributeNS(null, 'r', this.params.meanRadius);
38         this.$max.setAttributeNS(null, 'stroke', this.params.rangeColor);
39         this.$max.setAttributeNS(null, 'fill', 'transparent');
40         this.$max.classList.add('max');
41     
42         this.$min = document.createElementNS(this.ns, 'circle');
43         this.$min.setAttributeNS(null, 'r', this.params.meanRadius);
44         this.$min.setAttributeNS(null, 'stroke', this.params.rangeColor);
45         this.$min.setAttributeNS(null, 'fill', 'transparent');
46         this.$min.classList.add('min');
47     
48         this.$el.appendChild(this.$mean);
49         this.$el.appendChild(this.$max);
50         this.$el.appendChild(this.$min);
51     
52         return this.$el;
53       }
54     
55       // @TODO use accessors
56       update(renderingContext, datum) {
57         const mean = this.mean(datum);
58         const range = this.range(datum);
59         const x = this.x(datum);
60         // y positions
61         const meanPos = `${renderingContext.valueToPixel(mean)}`;
62         this.$mean.setAttributeNS(null, 'transform', `translate(0, ${meanPos})`);
63     
64         const halfRange = range / 2;
65         const max = renderingContext.valueToPixel(mean + halfRange);
66         const min = renderingContext.valueToPixel(mean - halfRange);
67         const xPos = renderingContext.timeToPixel(x);
68     
69         this.$max.setAttributeNS(null, 'transform', `translate(0, ${max})`);
70         this.$min.setAttributeNS(null, 'transform', `translate(0, ${min})`);
71         this.$el.setAttributeNS(null, 'transform', `translate(${xPos}, 0)`);
72       }
73     
74       inArea(renderingContext, datum, x1, y1, x2, y2) {
75         const x = renderingContext.timeToPixel(this.x(datum));
76         const mean = renderingContext.valueToPixel(this.mean(datum));
77         const range = renderingContext.valueToPixel(this.range(datum));
78         const min = mean - (range / 2);
79         const max = mean + (range / 2);
80     
81         if (x > x1 && x < x2 && (min > y1 || max < y2)) {
82           return true;
83         }
84     
85         return false;
86       }
87     }
88     

> coverage of: /Users/justinwinter/Sites/local/docroot/waves/waves-ui/src/utils/orthogonal-data.js

1      /**
2       * OrthogonalData transforms an object of arrays `{foo: [1, 2], bar: [3, 4]}`
3       * to or from an array of objects `[{foo: 1, bar: 3}, {foo: 2, bar: 4}]`
4       */
5      export default class OrthogonalData {
6        constructor() {
7          this._cols = null; // Object of arrays
8          this._rows = null; // Array of objects
9        }
10     
11       /**
12        * Check the consistency of the data.
13        */
14       _checkConsistency() {
15         let size = null;
16     
17         for (let key in this._cols) {
18           const col = this._cols[key];
19           const colLength = col.length;
20     
21           if (size !== null && size !== colLength) {
22             throw new Error(`${this.prototype.constructor.name}: inconsistent data`);
23           } else if (size === null) {
24             size = colLength;
25           }
26         }
27       }
28     
29       /**
30        * Updates array of objects from object of arrays.
31        */
32       updateFromCols() {
33         let keys = Object.keys(this._cols);
34     
35         keys.forEach((key, i) => {
36           const col = this._cols[key];
37     
38           col.forEach((value, index) => {
39             if (this._rows[index] === undefined) this._rows[index] = {};
40             this._rows[index][key] = value;
41           });
42         });
43     
44         this._checkConsistency();
45       }
46     
47       /**
48        * Updates object of arrays from array of objects.
49        */
50       updateFromRows() {
51         this._rows.forEach((obj, index) => {
52           for (let key in obj) {
53             if (index === 0) this._cols[key] = [];
54             this._cols[key].push(obj[key]);
55           }
56         });
57     
58         this._checkConsistency();
59       }
60     
61       /**
62        * Sets an object of arrays.
63        *
64        * @type {Object<String, Array>}
65        */
66       set cols(obj) {
67         this._cols = obj;
68         this._rows = [];
69     
70         this.updateFromCols();
71       }
72     
73       /**
74        * Returns an object of arrays.
75        *
76        * @type {Object<String, Array>}
77        */
78       get cols() {
79         return this._cols;
80       }
81     
82       /**
83        * Sets an array of objects.
84        *
85        * @type {Array<Object>}
86        */
87       set rows(arr) {
88         this._rows = arr;
89         this._cols = {};
90     
91         this.updateFromRows();
92       }
93     
94       /**
95        * Returns an array of objects.
96        *
97        * @type {Array<Object>}
98        */
99       get rows() {
100        return this._rows;
101      }
102    }
103    
✨  Done in 58.93s.
justinlevi commented 6 years ago

$ yarn test output below. Note, I have to visit the url provided by the command line in order to get this output. Is this how it is supposed to work?

$ browserify -t [ babelify --blacklist regenerator ] tests/*/*.js |  testling -u --no-show
http://localhost:62205/__testling?show=false

TAP version 13
# Edit Breakpoint Behavior
ok 1 should be equal
ok 2 should be equal
# MarkerBehavior
ok 3 should be equal
# SegmentBehavior
ok 4 should be equal
ok 5 should be equal
ok 6 should be equal
ok 7 should be equal
ok 8 should be equal
ok 9 should be equal
ok 10 should be equal
ok 11 should be equal
# TimeContextBehavior should edit shape accordingly
ok 12 should be equal
ok 13 should be equal
ok 14 should be equal
ok 15 should be equal
ok 16 should be equal
ok 17 should be equal
ok 18 should be equal
ok 19 should be equal
ok 20 should be equal
ok 21 should be equal
ok 22 should be equal
ok 23 should be equal
ok 24 should be equal
ok 25 should be equal
ok 26 should be equal
# TimeContextBehavior should edit shape accordingly v2
ok 27 should be equal
ok 28 should be equal
ok 29 should be equal
ok 30 should be equal
ok 31 should be equal
ok 32 should be equal
ok 33 should be equal
ok 34 should be equal
ok 35 should be equal
ok 36 should be equal
ok 37 should be equal
ok 38 should be equal
# TimeContextBehavior should stretch behavior correctly
ok 39 should be equal
ok 40 should be equal
ok 41 should be equal
ok 42 should be equal
ok 43 should be equal
ok 44 should be equal
ok 45 should be truthy
ok 46 should be equal
ok 47 should be equal
ok 48 should be equal
ok 49 should be truthy
# TraceBehavior
ok 50 should be equal
ok 51 should be equal
ok 52 should be equal
ok 53 should be equal
ok 54 should be equal
ok 55 should be equal
ok 56 should be equal
ok 57 should be equal
ok 58 should be equal
ok 59 should be equal
ok 60 should be equal
ok 61 should be equal
# LayerTimeContext get default values
ok 62 Default layerTimeContext startis 0 second
ok 63 Default layerTimeContext start is 10 seconds
ok 64 Default layerTimeContext offset 0 seconds
ok 65 Default stretchRatio is 1
# Layer with default params
ok 66 should be equal
ok 67 should be equal
# TimelineTimeContext get default values
ok 68 Initial offset is 0
ok 69 Initial pixelsPerSecond is 0
ok 70 Initial zoom is 1
ok 71 Initial visibleWidth is 1000 pixels
ok 72 Initial visibleDuration is 10 seconds
ok 73 Initial maintainVisibleDuration is false
# TimelineTimeContext set values
ok 74 visibleDuration is 1 second
ok 75 zoom is unchanged
ok 76 pps is unchanged
ok 77 0.5 seconds
# Timeline get default window view values over tracks
ok 78 Initial offset is 0
ok 79 Initial zoom is 1
ok 80 Initial pixelsPerSecond is 100
ok 81 Initial visibleWidth is 1000
ok 82 Initial visibleDuration is 10 seconds
# Timeline set window view values over tracks
ok 83 Offset is set to 10 seconds
ok 84 Zoom is set to 1
ok 85 pixelsPerSecond is set to 200
ok 86 visibleWidth is set to 2000
# Create a track from the timeline
ok 87 Timeline has one track
ok 88 The timeline track is the one just added
ok 89 should be equal
ok 90 should be equal
# Add a track from the timeline
ok 91 Can't add a track already added to the timeline
ok 92 Timeline has two tracks
ok 93 The timeline second track is the one just added
# Add a layer to a track from the timeline, and remove it
ok 94 should be equivalent
ok 95 The entire timeline has one, and just one layer
ok 96 The track we created contains one layer
ok 97 The track layer is the right one
ok 98 The entire timeline has no more layers
ok 99 The track we created doesn't contain any layer
ok 100 We can retrieve a track with a specific id
ok 101 We can get all the layers that belong to a certain group
ok 102 We can get all groupedLayers
ok 103 Can't add a track with a trackId already added
# TrackCollection methods
ok 104 should be equal
ok 105 should be equivalent
ok 106 update event is emitted
ok 107 update:containers event is emitted
ok 108 update:layers event is emitted
ok 109 render event is emitted
# Track - instanciation
ok 110 Default height is 100
ok 111 should be equal
ok 112 should be equal
ok 113 should be equal
ok 114 should be equal
ok 115 should be equal
ok 116 should be equal
ok 117 should be equal
ok 118 should be equal
ok 119 should be equal
ok 120 should be equal
ok 121 should be equal
ok 122 should be equal
ok 123 should be equal
ok 124 should be equal
ok 125 should be equal
ok 126 should be equal
ok 127 should be equal
ok 128 should be equal
ok 129 should be equal
ok 130 When set to 200, height is 200px
# Track - add/remove Layer
ok 131 should be equal
ok 132 should be equal
ok 133 should be equal
ok 134 should be equal
ok 135 should be equal
ok 136 should be equal
ok 137 should be equal
ok 138 should be equal
ok 139 should be equal
ok 140 should be equal
ok 141 should be equal
ok 142 should be equal
ok 143 should be equal
ok 144 should be equal
ok 145 should be equal
ok 146 should be equal
ok 147 should be equal
ok 148 should be equal
ok 149 should be equal
ok 150 should be equal
ok 151 should be equal
ok 152 should be equal
ok 153 should be equal
ok 154 should be equal
ok 155 should be equal
ok 156 should be equal
ok 157 should be equal
ok 158 should be equal
ok 159 should be equal
ok 160 should be equal
ok 161 should be equal
ok 162 should be equivalent
# Track - update container
ok 163 should be equal
ok 164 should be equal
ok 165 should be equal
# KeyBoard keydown
not ok 166 should be equal
  ---
    operator: equal
    expected: 'A'
    actual:   '\x00'
    at: Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:62205/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:62205/__testling?show=false:13499:32)
          at Keyboard.<anonymous> (http://localhost:62205/__testling?show=false:19966:12)
          at Keyboard.EventEmitter.emit (http://localhost:62205/__testling?show=false:9095:20)
          at HTMLDocument.onKeyDown (http://localhost:62205/__testling?show=false:17824:15)
          at Test.<anonymous> (http://localhost:62205/__testling?show=false:19983:12)
          at Test.bound [as _cb] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.run (http://localhost:62205/__testling?show=false:13518:10)
  ...
ok 167 should be equal
ok 168 should be equal
ok 169 should be equal
ok 170 should be equal
ok 171 should be equal
not ok 172 should be equal
  ---
    operator: equal
    expected: 'A'
    actual:   '\x00'
    at: Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:62205/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:62205/__testling?show=false:13499:32)
          at Keyboard.<anonymous> (http://localhost:62205/__testling?show=false:19966:12)
          at Keyboard.EventEmitter.emit (http://localhost:62205/__testling?show=false:9095:20)
          at HTMLDocument.onKeyUp (http://localhost:62205/__testling?show=false:17829:15)
          at Test.<anonymous> (http://localhost:62205/__testling?show=false:19984:12)
          at Test.bound [as _cb] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.run (http://localhost:62205/__testling?show=false:13518:10)
  ...
ok 173 should be equal
ok 174 should be equal
ok 175 should be equal
ok 176 should be equal
ok 177 should be equal
# Surface, default instance attributes
not ok 178 should be equal
  ---
    operator: equal
    expected: null
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:62205/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.<anonymous> (http://localhost:62205/__testling?show=false:20002:10)
          at Test.bound [as _cb] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.run (http://localhost:62205/__testling?show=false:13518:10)
          at Test.bound [as run] (http://localhost:62205/__testling?show=false:13499:32)
          at next (http://localhost:62205/__testling?show=false:13311:15)
          at Item.run (http://localhost:62205/__testling?show=false:10218:14)
  ...
not ok 179 should be equal
  ---
    operator: equal
    expected: null
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:62205/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.<anonymous> (http://localhost:62205/__testling?show=false:20003:10)
          at Test.bound [as _cb] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.run (http://localhost:62205/__testling?show=false:13518:10)
          at Test.bound [as run] (http://localhost:62205/__testling?show=false:13499:32)
          at next (http://localhost:62205/__testling?show=false:13311:15)
          at Item.run (http://localhost:62205/__testling?show=false:10218:14)
  ...
# Surface, trigger events
ok 180 should be equal
not ok 181 should be equal
  ---
    operator: equal
    expected: true
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:62205/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:62205/__testling?show=false:13499:32)
          at Surface.<anonymous> (http://localhost:62205/__testling?show=false:20023:12)
          at Surface.g (http://localhost:62205/__testling?show=false:9165:16)
          at Surface.EventEmitter.emit (http://localhost:62205/__testling?show=false:9073:17)
          at HTMLBodyElement.onMouseDown (http://localhost:62205/__testling?show=false:17983:15)
          at Test.<anonymous> (http://localhost:62205/__testling?show=false:20058:8)
          at Test.bound [as _cb] (http://localhost:62205/__testling?show=false:13499:32)
  ...
ok 182 should be equal
ok 183 should be equal
ok 184 should be equal
not ok 185 should be equal
  ---
    operator: equal
    expected: true
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:62205/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:62205/__testling?show=false:13499:32)
          at Surface.<anonymous> (http://localhost:62205/__testling?show=false:20037:14)
          at Surface.g (http://localhost:62205/__testling?show=false:9165:16)
          at Surface.EventEmitter.emit (http://localhost:62205/__testling?show=false:9073:17)
          at onMouseMove (http://localhost:62205/__testling?show=false:17992:15)
          at Surface.<anonymous> (http://localhost:62205/__testling?show=false:20056:10)
          at Surface.g (http://localhost:62205/__testling?show=false:9165:16)
  ...
ok 186 should be equal
not ok 187 should be equal
  ---
    operator: equal
    expected: false
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:62205/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:62205/__testling?show=false:13499:32)
          at Surface.<anonymous> (http://localhost:62205/__testling?show=false:20049:16)
          at Surface.g (http://localhost:62205/__testling?show=false:9165:16)
          at Surface.EventEmitter.emit (http://localhost:62205/__testling?show=false:9073:17)
          at onMouseUp (http://localhost:62205/__testling?show=false:18005:15)
          at Surface.<anonymous> (http://localhost:62205/__testling?show=false:20054:12)
          at Surface.g (http://localhost:62205/__testling?show=false:9165:16)
  ...
not ok 188 should be equal
  ---
    operator: equal
    expected: null
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:62205/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:62205/__testling?show=false:13499:32)
          at Surface.<anonymous> (http://localhost:62205/__testling?show=false:20050:16)
          at Surface.g (http://localhost:62205/__testling?show=false:9165:16)
          at Surface.EventEmitter.emit (http://localhost:62205/__testling?show=false:9073:17)
          at onMouseUp (http://localhost:62205/__testling?show=false:18005:15)
          at Surface.<anonymous> (http://localhost:62205/__testling?show=false:20054:12)
          at Surface.g (http://localhost:62205/__testling?show=false:9165:16)
  ...
not ok 189 should be equal
  ---
    operator: equal
    expected: null
    actual:   undefined
    at: Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13651:17)
    stack: |-
      Error: should be equal
          at Test.assert [as _assert] (http://localhost:62205/__testling?show=false:13647:54)
          at Test.bound [as _assert] (http://localhost:62205/__testling?show=false:13499:32)
          at Test.equal.Test.equals.Test.isEqual.Test.is.Test.strictEqual.Test.strictEquals (http://localhost:62205/__testling?show=false:13782:10)
          at Test.bound [as equal] (http://localhost:62205/__testling?show=false:13499:32)
          at Surface.<anonymous> (http://localhost:62205/__testling?show=false:20051:16)
          at Surface.g (http://localhost:62205/__testling?show=false:9165:16)
          at Surface.EventEmitter.emit (http://localhost:62205/__testling?show=false:9073:17)
          at onMouseUp (http://localhost:62205/__testling?show=false:18005:15)
          at Surface.<anonymous> (http://localhost:62205/__testling?show=false:20054:12)
          at Surface.g (http://localhost:62205/__testling?show=false:9165:16)
  ...
ok 190 should be equal
ok 191 should be equal
# Annotated Marker
ok 192 should contain the right text
ok 193 should contain the right text
# Dot
ok 194 Dot is well positioned 1
ok 195 Dot is well positioned 2
# Marker
ok 196 should be equal
ok 197 should be equal
# Segment instanciation
ok 198 should be equal
ok 199 should be equal
ok 200 should be equal
ok 201 should be equal
# Segment navigation zoom and move
ok 202 should be equal
ok 203 should be equal
ok 204 should be equal
# TraceDots
ok 205 should be equal
ok 206 should be equal
# OrthogonalData
ok 207 Correctly tranforms cols to rows
ok 208 Correctly tranforms rows to cols

1..208
# tests 208
# pass  199
# fail  9

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
b-ma commented 6 years ago

Actually the coverage is an old thing (there wasn't even babel at that time) that we didn't maintain since a long time. I will probably remove that in the next release.

As for the test themselves, I guess we didn't take time to maintain them as we should have... so some of them are probably outdated.

justinlevi commented 6 years ago

Bummer :(

This library is fantastic. Not sure people realize the wealth of information here.

I’d love to work on getting test coverage setup for more modern tools.

I really appreciate the feedback. I’ll continue to help wherever I can. On Tue, Nov 14, 2017 at 1:32 PM Benjamin Matuszewski < notifications@github.com> wrote:

Actually the coverage is an old thing (there wasn't even babel at that time) that we didn't maintain since a long time. I will probably remove that in the next release.

As for the test themselves, I guess we didn't take time to maintain them as we should have... so some of them are probably outdated.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/wavesjs/waves-ui/issues/36#issuecomment-344353463, or mute the thread https://github.com/notifications/unsubscribe-auth/ABEPJQYP4_ROyGJijZWSiaQyFPzIK-iDks5s2dyhgaJpZM4QdsBl .