vis-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.gitmodules +3 -0
  4. data/.project +11 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +202 -0
  7. data/README.md +29 -0
  8. data/Rakefile +1 -0
  9. data/lib/vis/rails/engine.rb +6 -0
  10. data/lib/vis/rails/version.rb +5 -0
  11. data/lib/vis/rails.rb +7 -0
  12. data/vendor/assets/javascripts/vis.js +1 -0
  13. data/vendor/assets/stylesheets/vis.css +3 -0
  14. data/vendor/assets/vis/DataSet.js +936 -0
  15. data/vendor/assets/vis/DataView.js +281 -0
  16. data/vendor/assets/vis/EventBus.js +89 -0
  17. data/vendor/assets/vis/events.js +116 -0
  18. data/vendor/assets/vis/graph/ClusterMixin.js +1019 -0
  19. data/vendor/assets/vis/graph/Edge.js +620 -0
  20. data/vendor/assets/vis/graph/Graph.js +2111 -0
  21. data/vendor/assets/vis/graph/Groups.js +80 -0
  22. data/vendor/assets/vis/graph/Images.js +41 -0
  23. data/vendor/assets/vis/graph/NavigationMixin.js +245 -0
  24. data/vendor/assets/vis/graph/Node.js +978 -0
  25. data/vendor/assets/vis/graph/Popup.js +105 -0
  26. data/vendor/assets/vis/graph/SectorsMixin.js +547 -0
  27. data/vendor/assets/vis/graph/SelectionMixin.js +515 -0
  28. data/vendor/assets/vis/graph/dotparser.js +829 -0
  29. data/vendor/assets/vis/graph/img/downarrow.png +0 -0
  30. data/vendor/assets/vis/graph/img/leftarrow.png +0 -0
  31. data/vendor/assets/vis/graph/img/minus.png +0 -0
  32. data/vendor/assets/vis/graph/img/plus.png +0 -0
  33. data/vendor/assets/vis/graph/img/rightarrow.png +0 -0
  34. data/vendor/assets/vis/graph/img/uparrow.png +0 -0
  35. data/vendor/assets/vis/graph/img/zoomExtends.png +0 -0
  36. data/vendor/assets/vis/graph/shapes.js +225 -0
  37. data/vendor/assets/vis/module/exports.js +68 -0
  38. data/vendor/assets/vis/module/header.js +24 -0
  39. data/vendor/assets/vis/module/imports.js +32 -0
  40. data/vendor/assets/vis/shim.js +252 -0
  41. data/vendor/assets/vis/timeline/Controller.js +172 -0
  42. data/vendor/assets/vis/timeline/Range.js +553 -0
  43. data/vendor/assets/vis/timeline/Stack.js +192 -0
  44. data/vendor/assets/vis/timeline/TimeStep.js +449 -0
  45. data/vendor/assets/vis/timeline/Timeline.js +476 -0
  46. data/vendor/assets/vis/timeline/component/Component.js +148 -0
  47. data/vendor/assets/vis/timeline/component/ContentPanel.js +113 -0
  48. data/vendor/assets/vis/timeline/component/CurrentTime.js +101 -0
  49. data/vendor/assets/vis/timeline/component/CustomTime.js +255 -0
  50. data/vendor/assets/vis/timeline/component/Group.js +129 -0
  51. data/vendor/assets/vis/timeline/component/GroupSet.js +546 -0
  52. data/vendor/assets/vis/timeline/component/ItemSet.js +612 -0
  53. data/vendor/assets/vis/timeline/component/Panel.js +112 -0
  54. data/vendor/assets/vis/timeline/component/RootPanel.js +215 -0
  55. data/vendor/assets/vis/timeline/component/TimeAxis.js +522 -0
  56. data/vendor/assets/vis/timeline/component/css/currenttime.css +5 -0
  57. data/vendor/assets/vis/timeline/component/css/customtime.css +6 -0
  58. data/vendor/assets/vis/timeline/component/css/groupset.css +59 -0
  59. data/vendor/assets/vis/timeline/component/css/item.css +93 -0
  60. data/vendor/assets/vis/timeline/component/css/itemset.css +17 -0
  61. data/vendor/assets/vis/timeline/component/css/panel.css +14 -0
  62. data/vendor/assets/vis/timeline/component/css/timeaxis.css +41 -0
  63. data/vendor/assets/vis/timeline/component/css/timeline.css +2 -0
  64. data/vendor/assets/vis/timeline/component/item/Item.js +81 -0
  65. data/vendor/assets/vis/timeline/component/item/ItemBox.js +302 -0
  66. data/vendor/assets/vis/timeline/component/item/ItemPoint.js +237 -0
  67. data/vendor/assets/vis/timeline/component/item/ItemRange.js +251 -0
  68. data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +91 -0
  69. data/vendor/assets/vis/util.js +673 -0
  70. data/vis-rails.gemspec +47 -0
  71. metadata +142 -0
@@ -0,0 +1,612 @@
1
+ /**
2
+ * An ItemSet holds a set of items and ranges which can be displayed in a
3
+ * range. The width is determined by the parent of the ItemSet, and the height
4
+ * is determined by the size of the items.
5
+ * @param {Component} parent
6
+ * @param {Component[]} [depends] Components on which this components depends
7
+ * (except for the parent)
8
+ * @param {Object} [options] See ItemSet.setOptions for the available
9
+ * options.
10
+ * @constructor ItemSet
11
+ * @extends Panel
12
+ */
13
+ // TODO: improve performance by replacing all Array.forEach with a for loop
14
+ function ItemSet(parent, depends, options) {
15
+ this.id = util.randomUUID();
16
+ this.parent = parent;
17
+ this.depends = depends;
18
+
19
+ // one options object is shared by this itemset and all its items
20
+ this.options = options || {};
21
+ this.defaultOptions = {
22
+ type: 'box',
23
+ align: 'center',
24
+ orientation: 'bottom',
25
+ margin: {
26
+ axis: 20,
27
+ item: 10
28
+ },
29
+ padding: 5
30
+ };
31
+
32
+ this.dom = {};
33
+
34
+ var me = this;
35
+ this.itemsData = null; // DataSet
36
+ this.range = null; // Range or Object {start: number, end: number}
37
+
38
+ this.listeners = {
39
+ 'add': function (event, params, senderId) {
40
+ if (senderId != me.id) {
41
+ me._onAdd(params.items);
42
+ }
43
+ },
44
+ 'update': function (event, params, senderId) {
45
+ if (senderId != me.id) {
46
+ me._onUpdate(params.items);
47
+ }
48
+ },
49
+ 'remove': function (event, params, senderId) {
50
+ if (senderId != me.id) {
51
+ me._onRemove(params.items);
52
+ }
53
+ }
54
+ };
55
+
56
+ this.items = {}; // object with an Item for every data item
57
+ this.selection = []; // list with the ids of all selected nodes
58
+ this.queue = {}; // queue with id/actions: 'add', 'update', 'delete'
59
+ this.stack = new Stack(this, Object.create(this.options));
60
+ this.conversion = null;
61
+
62
+ // TODO: ItemSet should also attach event listeners for rangechange and rangechanged, like timeaxis
63
+ }
64
+
65
+ ItemSet.prototype = new Panel();
66
+
67
+ // available item types will be registered here
68
+ ItemSet.types = {
69
+ box: ItemBox,
70
+ range: ItemRange,
71
+ rangeoverflow: ItemRangeOverflow,
72
+ point: ItemPoint
73
+ };
74
+
75
+ /**
76
+ * Set options for the ItemSet. Existing options will be extended/overwritten.
77
+ * @param {Object} [options] The following options are available:
78
+ * {String | function} [className]
79
+ * class name for the itemset
80
+ * {String} [type]
81
+ * Default type for the items. Choose from 'box'
82
+ * (default), 'point', or 'range'. The default
83
+ * Style can be overwritten by individual items.
84
+ * {String} align
85
+ * Alignment for the items, only applicable for
86
+ * ItemBox. Choose 'center' (default), 'left', or
87
+ * 'right'.
88
+ * {String} orientation
89
+ * Orientation of the item set. Choose 'top' or
90
+ * 'bottom' (default).
91
+ * {Number} margin.axis
92
+ * Margin between the axis and the items in pixels.
93
+ * Default is 20.
94
+ * {Number} margin.item
95
+ * Margin between items in pixels. Default is 10.
96
+ * {Number} padding
97
+ * Padding of the contents of an item in pixels.
98
+ * Must correspond with the items css. Default is 5.
99
+ */
100
+ ItemSet.prototype.setOptions = Component.prototype.setOptions;
101
+
102
+ /**
103
+ * Set range (start and end).
104
+ * @param {Range | Object} range A Range or an object containing start and end.
105
+ */
106
+ ItemSet.prototype.setRange = function setRange(range) {
107
+ if (!(range instanceof Range) && (!range || !range.start || !range.end)) {
108
+ throw new TypeError('Range must be an instance of Range, ' +
109
+ 'or an object containing start and end.');
110
+ }
111
+ this.range = range;
112
+ };
113
+
114
+ /**
115
+ * Set selected items by their id. Replaces the current selection
116
+ * Unknown id's are silently ignored.
117
+ * @param {Array} [ids] An array with zero or more id's of the items to be
118
+ * selected. If ids is an empty array, all items will be
119
+ * unselected.
120
+ */
121
+ ItemSet.prototype.setSelection = function setSelection(ids) {
122
+ var i, ii, id, item, selection;
123
+
124
+ if (ids) {
125
+ if (!Array.isArray(ids)) {
126
+ throw new TypeError('Array expected');
127
+ }
128
+
129
+ // unselect currently selected items
130
+ for (i = 0, ii = this.selection.length; i < ii; i++) {
131
+ id = this.selection[i];
132
+ item = this.items[id];
133
+ if (item) item.unselect();
134
+ }
135
+
136
+ // select items
137
+ this.selection = [];
138
+ for (i = 0, ii = ids.length; i < ii; i++) {
139
+ id = ids[i];
140
+ item = this.items[id];
141
+ if (item) {
142
+ this.selection.push(id);
143
+ item.select();
144
+ }
145
+ }
146
+
147
+ // trigger a select event
148
+ selection = this.selection.concat([]);
149
+ events.trigger(this, 'select', {
150
+ ids: selection
151
+ });
152
+
153
+ if (this.controller) {
154
+ this.requestRepaint();
155
+ }
156
+ }
157
+ };
158
+
159
+ /**
160
+ * Get the selected items by their id
161
+ * @return {Array} ids The ids of the selected items
162
+ */
163
+ ItemSet.prototype.getSelection = function getSelection() {
164
+ return this.selection.concat([]);
165
+ };
166
+
167
+ /**
168
+ * Deselect a selected item
169
+ * @param {String | Number} id
170
+ * @private
171
+ */
172
+ ItemSet.prototype._deselect = function _deselect(id) {
173
+ var selection = this.selection;
174
+ for (var i = 0, ii = selection.length; i < ii; i++) {
175
+ if (selection[i] == id) { // non-strict comparison!
176
+ selection.splice(i, 1);
177
+ break;
178
+ }
179
+ }
180
+ };
181
+
182
+ /**
183
+ * Repaint the component
184
+ * @return {Boolean} changed
185
+ */
186
+ ItemSet.prototype.repaint = function repaint() {
187
+ var changed = 0,
188
+ update = util.updateProperty,
189
+ asSize = util.option.asSize,
190
+ options = this.options,
191
+ orientation = this.getOption('orientation'),
192
+ defaultOptions = this.defaultOptions,
193
+ frame = this.frame;
194
+
195
+ if (!frame) {
196
+ frame = document.createElement('div');
197
+ frame.className = 'itemset';
198
+
199
+ var className = options.className;
200
+ if (className) {
201
+ util.addClassName(frame, util.option.asString(className));
202
+ }
203
+
204
+ // create background panel
205
+ var background = document.createElement('div');
206
+ background.className = 'background';
207
+ frame.appendChild(background);
208
+ this.dom.background = background;
209
+
210
+ // create foreground panel
211
+ var foreground = document.createElement('div');
212
+ foreground.className = 'foreground';
213
+ frame.appendChild(foreground);
214
+ this.dom.foreground = foreground;
215
+
216
+ // create axis panel
217
+ var axis = document.createElement('div');
218
+ axis.className = 'itemset-axis';
219
+ //frame.appendChild(axis);
220
+ this.dom.axis = axis;
221
+
222
+ this.frame = frame;
223
+ changed += 1;
224
+ }
225
+
226
+ if (!this.parent) {
227
+ throw new Error('Cannot repaint itemset: no parent attached');
228
+ }
229
+ var parentContainer = this.parent.getContainer();
230
+ if (!parentContainer) {
231
+ throw new Error('Cannot repaint itemset: parent has no container element');
232
+ }
233
+ if (!frame.parentNode) {
234
+ parentContainer.appendChild(frame);
235
+ changed += 1;
236
+ }
237
+ if (!this.dom.axis.parentNode) {
238
+ parentContainer.appendChild(this.dom.axis);
239
+ changed += 1;
240
+ }
241
+
242
+ // reposition frame
243
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
244
+ changed += update(frame.style, 'top', asSize(options.top, '0px'));
245
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
246
+ changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
247
+
248
+ // reposition axis
249
+ changed += update(this.dom.axis.style, 'left', asSize(options.left, '0px'));
250
+ changed += update(this.dom.axis.style, 'width', asSize(options.width, '100%'));
251
+ if (orientation == 'bottom') {
252
+ changed += update(this.dom.axis.style, 'top', (this.height + this.top) + 'px');
253
+ }
254
+ else { // orientation == 'top'
255
+ changed += update(this.dom.axis.style, 'top', this.top + 'px');
256
+ }
257
+
258
+ this._updateConversion();
259
+
260
+ var me = this,
261
+ queue = this.queue,
262
+ itemsData = this.itemsData,
263
+ items = this.items,
264
+ dataOptions = {
265
+ // TODO: cleanup
266
+ // fields: [(itemsData && itemsData.fieldId || 'id'), 'start', 'end', 'content', 'type', 'className']
267
+ };
268
+
269
+ // show/hide added/changed/removed items
270
+ for (var id in queue) {
271
+ if (queue.hasOwnProperty(id)) {
272
+ var entry = queue[id],
273
+ item = items[id],
274
+ action = entry.action;
275
+
276
+ //noinspection FallthroughInSwitchStatementJS
277
+ switch (action) {
278
+ case 'add':
279
+ case 'update':
280
+ var itemData = itemsData && itemsData.get(id, dataOptions);
281
+
282
+ if (itemData) {
283
+ var type = itemData.type ||
284
+ (itemData.start && itemData.end && 'range') ||
285
+ options.type ||
286
+ 'box';
287
+ var constructor = ItemSet.types[type];
288
+
289
+ // TODO: how to handle items with invalid data? hide them and give a warning? or throw an error?
290
+ if (item) {
291
+ // update item
292
+ if (!constructor || !(item instanceof constructor)) {
293
+ // item type has changed, hide and delete the item
294
+ changed += item.hide();
295
+ item = null;
296
+ }
297
+ else {
298
+ item.data = itemData; // TODO: create a method item.setData ?
299
+ changed++;
300
+ }
301
+ }
302
+
303
+ if (!item) {
304
+ // create item
305
+ if (constructor) {
306
+ item = new constructor(me, itemData, options, defaultOptions);
307
+ item.id = entry.id; // we take entry.id, as id itself is stringified
308
+ changed++;
309
+ }
310
+ else {
311
+ throw new TypeError('Unknown item type "' + type + '"');
312
+ }
313
+ }
314
+
315
+ // force a repaint (not only a reposition)
316
+ item.repaint();
317
+
318
+ items[id] = item;
319
+ }
320
+
321
+ // update queue
322
+ delete queue[id];
323
+ break;
324
+
325
+ case 'remove':
326
+ if (item) {
327
+ // remove the item from the set selected items
328
+ if (item.selected) {
329
+ me._deselect(id);
330
+ }
331
+
332
+ // remove DOM of the item
333
+ changed += item.hide();
334
+ }
335
+
336
+ // update lists
337
+ delete items[id];
338
+ delete queue[id];
339
+ break;
340
+
341
+ default:
342
+ console.log('Error: unknown action "' + action + '"');
343
+ }
344
+ }
345
+ }
346
+
347
+ // reposition all items. Show items only when in the visible area
348
+ util.forEach(this.items, function (item) {
349
+ if (item.visible) {
350
+ changed += item.show();
351
+ item.reposition();
352
+ }
353
+ else {
354
+ changed += item.hide();
355
+ }
356
+ });
357
+
358
+ return (changed > 0);
359
+ };
360
+
361
+ /**
362
+ * Get the foreground container element
363
+ * @return {HTMLElement} foreground
364
+ */
365
+ ItemSet.prototype.getForeground = function getForeground() {
366
+ return this.dom.foreground;
367
+ };
368
+
369
+ /**
370
+ * Get the background container element
371
+ * @return {HTMLElement} background
372
+ */
373
+ ItemSet.prototype.getBackground = function getBackground() {
374
+ return this.dom.background;
375
+ };
376
+
377
+ /**
378
+ * Get the axis container element
379
+ * @return {HTMLElement} axis
380
+ */
381
+ ItemSet.prototype.getAxis = function getAxis() {
382
+ return this.dom.axis;
383
+ };
384
+
385
+ /**
386
+ * Reflow the component
387
+ * @return {Boolean} resized
388
+ */
389
+ ItemSet.prototype.reflow = function reflow () {
390
+ var changed = 0,
391
+ options = this.options,
392
+ marginAxis = options.margin && options.margin.axis || this.defaultOptions.margin.axis,
393
+ marginItem = options.margin && options.margin.item || this.defaultOptions.margin.item,
394
+ update = util.updateProperty,
395
+ asNumber = util.option.asNumber,
396
+ asSize = util.option.asSize,
397
+ frame = this.frame;
398
+
399
+ if (frame) {
400
+ this._updateConversion();
401
+
402
+ util.forEach(this.items, function (item) {
403
+ changed += item.reflow();
404
+ });
405
+
406
+ // TODO: stack.update should be triggered via an event, in stack itself
407
+ // TODO: only update the stack when there are changed items
408
+ this.stack.update();
409
+
410
+ var maxHeight = asNumber(options.maxHeight);
411
+ var fixedHeight = (asSize(options.height) != null);
412
+ var height;
413
+ if (fixedHeight) {
414
+ height = frame.offsetHeight;
415
+ }
416
+ else {
417
+ // height is not specified, determine the height from the height and positioned items
418
+ var visibleItems = this.stack.ordered; // TODO: not so nice way to get the filtered items
419
+ if (visibleItems.length) {
420
+ var min = visibleItems[0].top;
421
+ var max = visibleItems[0].top + visibleItems[0].height;
422
+ util.forEach(visibleItems, function (item) {
423
+ min = Math.min(min, item.top);
424
+ max = Math.max(max, (item.top + item.height));
425
+ });
426
+ height = (max - min) + marginAxis + marginItem;
427
+ }
428
+ else {
429
+ height = marginAxis + marginItem;
430
+ }
431
+ }
432
+ if (maxHeight != null) {
433
+ height = Math.min(height, maxHeight);
434
+ }
435
+ changed += update(this, 'height', height);
436
+
437
+ // calculate height from items
438
+ changed += update(this, 'top', frame.offsetTop);
439
+ changed += update(this, 'left', frame.offsetLeft);
440
+ changed += update(this, 'width', frame.offsetWidth);
441
+ }
442
+ else {
443
+ changed += 1;
444
+ }
445
+
446
+ return (changed > 0);
447
+ };
448
+
449
+ /**
450
+ * Hide this component from the DOM
451
+ * @return {Boolean} changed
452
+ */
453
+ ItemSet.prototype.hide = function hide() {
454
+ var changed = false;
455
+
456
+ // remove the DOM
457
+ if (this.frame && this.frame.parentNode) {
458
+ this.frame.parentNode.removeChild(this.frame);
459
+ changed = true;
460
+ }
461
+ if (this.dom.axis && this.dom.axis.parentNode) {
462
+ this.dom.axis.parentNode.removeChild(this.dom.axis);
463
+ changed = true;
464
+ }
465
+
466
+ return changed;
467
+ };
468
+
469
+ /**
470
+ * Set items
471
+ * @param {vis.DataSet | null} items
472
+ */
473
+ ItemSet.prototype.setItems = function setItems(items) {
474
+ var me = this,
475
+ ids,
476
+ oldItemsData = this.itemsData;
477
+
478
+ // replace the dataset
479
+ if (!items) {
480
+ this.itemsData = null;
481
+ }
482
+ else if (items instanceof DataSet || items instanceof DataView) {
483
+ this.itemsData = items;
484
+ }
485
+ else {
486
+ throw new TypeError('Data must be an instance of DataSet');
487
+ }
488
+
489
+ if (oldItemsData) {
490
+ // unsubscribe from old dataset
491
+ util.forEach(this.listeners, function (callback, event) {
492
+ oldItemsData.unsubscribe(event, callback);
493
+ });
494
+
495
+ // remove all drawn items
496
+ ids = oldItemsData.getIds();
497
+ this._onRemove(ids);
498
+ }
499
+
500
+ if (this.itemsData) {
501
+ // subscribe to new dataset
502
+ var id = this.id;
503
+ util.forEach(this.listeners, function (callback, event) {
504
+ me.itemsData.subscribe(event, callback, id);
505
+ });
506
+
507
+ // draw all new items
508
+ ids = this.itemsData.getIds();
509
+ this._onAdd(ids);
510
+ }
511
+ };
512
+
513
+ /**
514
+ * Get the current items items
515
+ * @returns {vis.DataSet | null}
516
+ */
517
+ ItemSet.prototype.getItems = function getItems() {
518
+ return this.itemsData;
519
+ };
520
+
521
+ /**
522
+ * Handle updated items
523
+ * @param {Number[]} ids
524
+ * @private
525
+ */
526
+ ItemSet.prototype._onUpdate = function _onUpdate(ids) {
527
+ this._toQueue('update', ids);
528
+ };
529
+
530
+ /**
531
+ * Handle changed items
532
+ * @param {Number[]} ids
533
+ * @private
534
+ */
535
+ ItemSet.prototype._onAdd = function _onAdd(ids) {
536
+ this._toQueue('add', ids);
537
+ };
538
+
539
+ /**
540
+ * Handle removed items
541
+ * @param {Number[]} ids
542
+ * @private
543
+ */
544
+ ItemSet.prototype._onRemove = function _onRemove(ids) {
545
+ this._toQueue('remove', ids);
546
+ };
547
+
548
+ /**
549
+ * Put items in the queue to be added/updated/remove
550
+ * @param {String} action can be 'add', 'update', 'remove'
551
+ * @param {Number[]} ids
552
+ */
553
+ ItemSet.prototype._toQueue = function _toQueue(action, ids) {
554
+ var queue = this.queue;
555
+ ids.forEach(function (id) {
556
+ queue[id] = {
557
+ id: id,
558
+ action: action
559
+ };
560
+ });
561
+
562
+ if (this.controller) {
563
+ //this.requestReflow();
564
+ this.requestRepaint();
565
+ }
566
+ };
567
+
568
+ /**
569
+ * Calculate the scale and offset to convert a position on screen to the
570
+ * corresponding date and vice versa.
571
+ * After the method _updateConversion is executed once, the methods toTime
572
+ * and toScreen can be used.
573
+ * @private
574
+ */
575
+ ItemSet.prototype._updateConversion = function _updateConversion() {
576
+ var range = this.range;
577
+ if (!range) {
578
+ throw new Error('No range configured');
579
+ }
580
+
581
+ if (range.conversion) {
582
+ this.conversion = range.conversion(this.width);
583
+ }
584
+ else {
585
+ this.conversion = Range.conversion(range.start, range.end, this.width);
586
+ }
587
+ };
588
+
589
+ /**
590
+ * Convert a position on screen (pixels) to a datetime
591
+ * Before this method can be used, the method _updateConversion must be
592
+ * executed once.
593
+ * @param {int} x Position on the screen in pixels
594
+ * @return {Date} time The datetime the corresponds with given position x
595
+ */
596
+ ItemSet.prototype.toTime = function toTime(x) {
597
+ var conversion = this.conversion;
598
+ return new Date(x / conversion.scale + conversion.offset);
599
+ };
600
+
601
+ /**
602
+ * Convert a datetime (Date object) into a position on the screen
603
+ * Before this method can be used, the method _updateConversion must be
604
+ * executed once.
605
+ * @param {Date} time A date
606
+ * @return {int} x The position on the screen in pixels which corresponds
607
+ * with the given date.
608
+ */
609
+ ItemSet.prototype.toScreen = function toScreen(time) {
610
+ var conversion = this.conversion;
611
+ return (time.valueOf() - conversion.offset) * conversion.scale;
612
+ };
@@ -0,0 +1,112 @@
1
+ /**
2
+ * A panel can contain components
3
+ * @param {Component} [parent]
4
+ * @param {Component[]} [depends] Components on which this components depends
5
+ * (except for the parent)
6
+ * @param {Object} [options] Available parameters:
7
+ * {String | Number | function} [left]
8
+ * {String | Number | function} [top]
9
+ * {String | Number | function} [width]
10
+ * {String | Number | function} [height]
11
+ * {String | function} [className]
12
+ * @constructor Panel
13
+ * @extends Component
14
+ */
15
+ function Panel(parent, depends, options) {
16
+ this.id = util.randomUUID();
17
+ this.parent = parent;
18
+ this.depends = depends;
19
+
20
+ this.options = options || {};
21
+ }
22
+
23
+ Panel.prototype = new Component();
24
+
25
+ /**
26
+ * Set options. Will extend the current options.
27
+ * @param {Object} [options] Available parameters:
28
+ * {String | function} [className]
29
+ * {String | Number | function} [left]
30
+ * {String | Number | function} [top]
31
+ * {String | Number | function} [width]
32
+ * {String | Number | function} [height]
33
+ */
34
+ Panel.prototype.setOptions = Component.prototype.setOptions;
35
+
36
+ /**
37
+ * Get the container element of the panel, which can be used by a child to
38
+ * add its own widgets.
39
+ * @returns {HTMLElement} container
40
+ */
41
+ Panel.prototype.getContainer = function () {
42
+ return this.frame;
43
+ };
44
+
45
+ /**
46
+ * Repaint the component
47
+ * @return {Boolean} changed
48
+ */
49
+ Panel.prototype.repaint = function () {
50
+ var changed = 0,
51
+ update = util.updateProperty,
52
+ asSize = util.option.asSize,
53
+ options = this.options,
54
+ frame = this.frame;
55
+ if (!frame) {
56
+ frame = document.createElement('div');
57
+ frame.className = 'panel';
58
+
59
+ var className = options.className;
60
+ if (className) {
61
+ if (typeof className == 'function') {
62
+ util.addClassName(frame, String(className()));
63
+ }
64
+ else {
65
+ util.addClassName(frame, String(className));
66
+ }
67
+ }
68
+
69
+ this.frame = frame;
70
+ changed += 1;
71
+ }
72
+ if (!frame.parentNode) {
73
+ if (!this.parent) {
74
+ throw new Error('Cannot repaint panel: no parent attached');
75
+ }
76
+ var parentContainer = this.parent.getContainer();
77
+ if (!parentContainer) {
78
+ throw new Error('Cannot repaint panel: parent has no container element');
79
+ }
80
+ parentContainer.appendChild(frame);
81
+ changed += 1;
82
+ }
83
+
84
+ changed += update(frame.style, 'top', asSize(options.top, '0px'));
85
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
86
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
87
+ changed += update(frame.style, 'height', asSize(options.height, '100%'));
88
+
89
+ return (changed > 0);
90
+ };
91
+
92
+ /**
93
+ * Reflow the component
94
+ * @return {Boolean} resized
95
+ */
96
+ Panel.prototype.reflow = function () {
97
+ var changed = 0,
98
+ update = util.updateProperty,
99
+ frame = this.frame;
100
+
101
+ if (frame) {
102
+ changed += update(this, 'top', frame.offsetTop);
103
+ changed += update(this, 'left', frame.offsetLeft);
104
+ changed += update(this, 'width', frame.offsetWidth);
105
+ changed += update(this, 'height', frame.offsetHeight);
106
+ }
107
+ else {
108
+ changed += 1;
109
+ }
110
+
111
+ return (changed > 0);
112
+ };