Project

General

Profile

« Previous | Next » 

Revision 26624

utility libraries moved to modular-ui module

View differences:

modules/dnet-modular-ui/trunk/src/main/resources/eu/dnetlib/web/resources/css/timeline.css
1
div.timeline-frame {
2
	border: 1px solid #BEBEBE;
3
	overflow: hidden;
4
}
5

  
6
div.timeline-axis {
7
	border-color: #BEBEBE;
8
	border-width: 1px;
9
	border-top-style: solid;
10
}
11

  
12
div.timeline-axis-grid {
13
	border-left-style: solid;
14
	border-width: 1px;
15
}
16

  
17
div.timeline-axis-grid-minor {
18
	border-color: #e5e5e5;
19
}
20

  
21
div.timeline-axis-grid-major {
22
	border-color: #bfbfbf;
23
}
24

  
25
div.timeline-axis-text {
26
	color: #4D4D4D;
27
	padding: 3px;
28
	white-space: nowrap;
29
}
30

  
31
div.timeline-axis-text-minor {
32
	
33
}
34

  
35
div.timeline-axis-text-major {
36
	
37
}
38

  
39
div.timeline-event {
40
	color: #1A1A1A;
41
	border-color: #97B0F8;
42
	background-color: #D5DDF6;
43
	display: inline-block;
44
}
45

  
46
div.timeline-event-selected {
47
	border-color: #FFC200;
48
	background-color: #FFF785;
49
	z-index: 999;
50
}
51

  
52
div.timeline-event-cluster { /* TODO: use another color or pattern? */
53
	background: #97B0F8 url('img/cluster_bg.png');
54
	color: white;
55
}
56

  
57
div.timeline-event-cluster div.timeline-event-dot {
58
	border-color: #D5DDF6;
59
}
60

  
61
div.timeline-event-box {
62
	text-align: center;
63
	border-style: solid;
64
	border-width: 1px;
65
	border-radius: 5px;
66
	-moz-border-radius: 5px; /* For Firefox 3.6 and older */
67
}
68

  
69
div.timeline-event-dot {
70
	border-style: solid;
71
	border-width: 5px;
72
	border-radius: 5px;
73
	-moz-border-radius: 5px; /* For Firefox 3.6 and older */
74
}
75

  
76
div.timeline-event-range {
77
	border-style: solid;
78
	border-width: 1px;
79
	border-radius: 2px;
80
	-moz-border-radius: 2px; /* For Firefox 3.6 and older */
81
}
82

  
83
div.timeline-event-range-drag-left {
84
	cursor: w-resize;
85
	z-index: 1000;
86
}
87

  
88
div.timeline-event-range-drag-right {
89
	cursor: e-resize;
90
	z-index: 1000;
91
}
92

  
93
div.timeline-event-line {
94
	border-left-width: 1px;
95
	border-left-style: solid;
96
}
97

  
98
div.timeline-event-content {
99
	margin: 5px;
100
	white-space: nowrap;
101
	overflow: hidden;
102
}
103

  
104
div.timeline-groups-axis {
105
	border-color: #BEBEBE;
106
	border-width: 1px;
107
}
108

  
109
div.timeline-groups-axis-onleft {
110
	border-style: none solid none none;
111
}
112

  
113
div.timeline-groups-axis-onright {
114
	border-style: none none none solid;
115
}
116

  
117
div.timeline-groups-text {
118
	color: #4D4D4D;
119
	padding-left: 10px;
120
	padding-right: 10px;
121
}
122

  
123
div.timeline-currenttime {
124
	background-color: #aabbcc;
125
	width: 2px;
126
}
127

  
128
div.timeline-customtime {
129
	background-color: #6E94FF;
130
	width: 2px;
131
	cursor: move;
132
}
133

  
134
div.timeline-navigation {
135
	font-family: arial;
136
	font-size: 20px;
137
	font-weight: bold;
138
	color: gray;
139
	border: 1px solid #BEBEBE;
140
	background-color: #F5F5F5;
141
	border-radius: 2px;
142
	-moz-border-radius: 2px; /* For Firefox 3.6 and older */
143
}
144

  
145
div.timeline-navigation-new,div.timeline-navigation-delete,div.timeline-navigation-zoom-in,div.timeline-navigation-zoom-out,div.timeline-navigation-move-left,div.timeline-navigation-move-right
146
	{
147
	cursor: pointer;
148
	padding: 10px 10px;
149
	float: left;
150
	text-decoration: none;
151
	border-color: #BEBEBE;
152
	/* border is used for the separator between new and navigation buttons */
153
	width: 16px;
154
	height: 16px;
155
}
156

  
157
div.timeline-navigation-new {
158
	background: url('img/16/new.png') no-repeat center;
159
}
160

  
161
div.timeline-navigation-new-line {
162
	border-right: solid 1px;
163
}
164

  
165
div.timeline-navigation-delete {
166
	padding: 0px;
167
	padding-left: 5px;
168
	background: url('img/16/delete.png') no-repeat center;
169
}
170

  
171
div.timeline-navigation-zoom-in {
172
	background: url('img/16/zoomin.png') no-repeat center;
173
}
174

  
175
div.timeline-navigation-zoom-out {
176
	background: url('img/16/zoomout.png') no-repeat center;
177
}
178

  
179
div.timeline-navigation-move-left {
180
	background: url('img/16/moveleft.png') no-repeat center;
181
}
182

  
183
div.timeline-navigation-move-right {
184
	background: url('img/16/moveright.png') no-repeat center;
185
}
186

  
187
div.success {
188
	background-color: #339933;
189
	border-color: #002200;
190
	color: white;
191
}
192

  
193
div.success:hover {
194
	background-color: #99dd99;
195
}
196

  
197
div.failure {
198
	background-color: #dd3333;
199
	border-color: #aa1111;
200
	color: white;
201
}
202

  
203
div.failure:hover {
204
	background-color: #ff9999;
205
}
modules/dnet-modular-ui/trunk/src/main/resources/eu/dnetlib/web/resources/js/timeline.js
1
/**
2
 * @file timeline.js
3
 *
4
 * @brief
5
 * The Timeline is an interactive visualization chart to visualize events in
6
 * time, having a start and end date.
7
 * You can freely move and zoom in the timeline by dragging
8
 * and scrolling in the Timeline. Items are optionally dragable. The time
9
 * scale on the axis is adjusted automatically, and supports scales ranging
10
 * from milliseconds to years.
11
 *
12
 * Timeline is part of the CHAP Links library.
13
 *
14
 * Timeline is tested on Firefox 3.6, Safari 5.0, Chrome 6.0, Opera 10.6, and
15
 * Internet Explorer 6+.
16
 *
17
 * @license
18
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
19
 * use this file except in compliance with the License. You may obtain a copy
20
 * of the License at
21
 *
22
 * http://www.apache.org/licenses/LICENSE-2.0
23
 *
24
 * Unless required by applicable law or agreed to in writing, software
25
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
26
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
27
 * License for the specific language governing permissions and limitations under
28
 * the License.
29
 *
30
 * Copyright (c) 2011-2013 Almende B.V.
31
 *
32
 * @author     Jos de Jong, <jos@almende.org>
33
 * @date    2013-08-20
34
 * @version 2.5.0
35
 */
36

  
37
/*
38
 * i18n mods by github user iktuz (https://gist.github.com/iktuz/3749287/)
39
 * added to v2.4.1 with da_DK language by @bjarkebech
40
 */
41

  
42
/*
43
 * TODO
44
 *
45
 * Add zooming with pinching on Android
46
 * 
47
 * Bug: when an item contains a javascript onclick or a link, this does not work
48
 *      when the item is not selected (when the item is being selected,
49
 *      it is redrawn, which cancels any onclick or link action)
50
 * Bug: when an item contains an image without size, or a css max-width, it is not sized correctly
51
 * Bug: neglect items when they have no valid start/end, instead of throwing an error
52
 * Bug: Pinching on ipad does not work very well, sometimes the page will zoom when pinching vertically
53
 * Bug: cannot set max width for an item, like div.timeline-event-content {white-space: normal; max-width: 100px;}
54
 * Bug on IE in Quirks mode. When you have groups, and delete an item, the groups become invisible
55
 */
56

  
57
/**
58
 * Declare a unique namespace for CHAP's Common Hybrid Visualisation Library,
59
 * "links"
60
 */
61
if (typeof links === 'undefined') {
62
    links = {};
63
    // important: do not use var, as "var links = {};" will overwrite 
64
    //            the existing links variable value with undefined in IE8, IE7.  
65
}
66

  
67

  
68
/**
69
 * Ensure the variable google exists
70
 */
71
if (typeof google === 'undefined') {
72
    google = undefined;
73
    // important: do not use var, as "var google = undefined;" will overwrite 
74
    //            the existing google variable value with undefined in IE8, IE7.
75
}
76

  
77

  
78

  
79
// Internet Explorer 8 and older does not support Array.indexOf,
80
// so we define it here in that case
81
// http://soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer/
82
if(!Array.prototype.indexOf) {
83
    Array.prototype.indexOf = function(obj){
84
        for(var i = 0; i < this.length; i++){
85
            if(this[i] == obj){
86
                return i;
87
            }
88
        }
89
        return -1;
90
    }
91
}
92

  
93
// Internet Explorer 8 and older does not support Array.forEach,
94
// so we define it here in that case
95
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach
96
if (!Array.prototype.forEach) {
97
    Array.prototype.forEach = function(fn, scope) {
98
        for(var i = 0, len = this.length; i < len; ++i) {
99
            fn.call(scope || this, this[i], i, this);
100
        }
101
    }
102
}
103

  
104

  
105
/**
106
 * @constructor links.Timeline
107
 * The timeline is a visualization chart to visualize events in time.
108
 *
109
 * The timeline is developed in javascript as a Google Visualization Chart.
110
 *
111
 * @param {Element} container   The DOM element in which the Timeline will
112
 *                                  be created. Normally a div element.
113
 */
114
links.Timeline = function(container) {
115
    if (!container) {
116
        // this call was probably only for inheritance, no constructor-code is required
117
        return;
118
    }
119

  
120
    // create variables and set default values
121
    this.dom = {};
122
    this.conversion = {};
123
    this.eventParams = {}; // stores parameters for mouse events
124
    this.groups = [];
125
    this.groupIndexes = {};
126
    this.items = [];
127
    this.renderQueue = {
128
        show: [],   // Items made visible but not yet added to DOM
129
        hide: [],   // Items currently visible but not yet removed from DOM
130
        update: []  // Items with changed data but not yet adjusted DOM
131
    };
132
    this.renderedItems = [];  // Items currently rendered in the DOM
133
    this.clusterGenerator = new links.Timeline.ClusterGenerator(this);
134
    this.currentClusters = [];
135
    this.selection = undefined; // stores index and item which is currently selected
136

  
137
    this.listeners = {}; // event listener callbacks
138

  
139
    // Initialize sizes. 
140
    // Needed for IE (which gives an error when you try to set an undefined
141
    // value in a style)
142
    this.size = {
143
        'actualHeight': 0,
144
        'axis': {
145
            'characterMajorHeight': 0,
146
            'characterMajorWidth': 0,
147
            'characterMinorHeight': 0,
148
            'characterMinorWidth': 0,
149
            'height': 0,
150
            'labelMajorTop': 0,
151
            'labelMinorTop': 0,
152
            'line': 0,
153
            'lineMajorWidth': 0,
154
            'lineMinorHeight': 0,
155
            'lineMinorTop': 0,
156
            'lineMinorWidth': 0,
157
            'top': 0
158
        },
159
        'contentHeight': 0,
160
        'contentLeft': 0,
161
        'contentWidth': 0,
162
        'frameHeight': 0,
163
        'frameWidth': 0,
164
        'groupsLeft': 0,
165
        'groupsWidth': 0,
166
        'items': {
167
            'top': 0
168
        }
169
    };
170

  
171
    this.dom.container = container;
172

  
173
    this.options = {
174
        'width': "100%",
175
        'height': "auto",
176
        'minHeight': 0,        // minimal height in pixels
177
        'autoHeight': true,
178

  
179
        'eventMargin': 10,     // minimal margin between events
180
        'eventMarginAxis': 20, // minimal margin between events and the axis
181
        'dragAreaWidth': 10,   // pixels
182

  
183
        'min': undefined,
184
        'max': undefined,
185
        'zoomMin': 10,     // milliseconds
186
        'zoomMax': 1000 * 60 * 60 * 24 * 365 * 10000, // milliseconds
187

  
188
        'moveable': true,
189
        'zoomable': true,
190
        'selectable': true,
191
        'unselectable': true,
192
        'editable': false,
193
        'snapEvents': true,
194
        'groupChangeable': true,
195

  
196
        'showCurrentTime': true, // show a red bar displaying the current time
197
        'showCustomTime': false, // show a blue, draggable bar displaying a custom time    
198
        'showMajorLabels': true,
199
        'showMinorLabels': true,
200
        'showNavigation': false,
201
        'showButtonNew': false,
202
        'groupsOnRight': false,
203
        'axisOnTop': false,
204
        'stackEvents': true,
205
        'animate': true,
206
        'animateZoom': true,
207
        'cluster': false,
208
        'style': 'box',
209
        'customStackOrder': false, //a function(a,b) for determining stackorder amongst a group of items. Essentially a comparator, -ve value for "a before b" and vice versa
210
        
211
        // i18n: Timeline only has built-in English text per default. Include timeline-locales.js to support more localized text.
212
        'locale': 'en',
213
        'MONTHS': new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"),
214
        'MONTHS_SHORT': new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"),
215
        'DAYS': new Array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"),
216
        'DAYS_SHORT': new Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"),
217
        'ZOOM_IN': "Zoom in",
218
        'ZOOM_OUT': "Zoom out",
219
        'MOVE_LEFT': "Move left",
220
        'MOVE_RIGHT': "Move right",
221
        'NEW': "New",
222
        'CREATE_NEW_EVENT': "Create new event"
223
    };
224

  
225
    this.clientTimeOffset = 0;    // difference between client time and the time
226
    // set via Timeline.setCurrentTime()
227
    var dom = this.dom;
228

  
229
    // remove all elements from the container element.
230
    while (dom.container.hasChildNodes()) {
231
        dom.container.removeChild(dom.container.firstChild);
232
    }
233

  
234
    // create a step for drawing the axis
235
    this.step = new links.Timeline.StepDate();
236

  
237
    // add standard item types
238
    this.itemTypes = {
239
        box:   links.Timeline.ItemBox,
240
        range: links.Timeline.ItemRange,
241
        dot:   links.Timeline.ItemDot
242
    };
243

  
244
    // initialize data
245
    this.data = [];
246
    this.firstDraw = true;
247

  
248
    // date interval must be initialized 
249
    this.setVisibleChartRange(undefined, undefined, false);
250

  
251
    // render for the first time
252
    this.render();
253

  
254
    // fire the ready event
255
    var me = this;
256
    setTimeout(function () {
257
        me.trigger('ready');
258
    }, 0);
259
};
260

  
261

  
262
/**
263
 * Main drawing logic. This is the function that needs to be called
264
 * in the html page, to draw the timeline.
265
 *
266
 * A data table with the events must be provided, and an options table.
267
 *
268
 * @param {google.visualization.DataTable}      data
269
 *                                 The data containing the events for the timeline.
270
 *                                 Object DataTable is defined in
271
 *                                 google.visualization.DataTable
272
 * @param {Object} options         A name/value map containing settings for the
273
 *                                 timeline. Optional.
274
 */
275
links.Timeline.prototype.draw = function(data, options) {
276
    this.setOptions(options);
277
    
278
    if (this.options.selectable) {
279
        links.Timeline.addClassName(this.dom.frame, "timeline-selectable");
280
    }
281

  
282
    // read the data
283
    this.setData(data);
284

  
285
    // set timer range. this will also redraw the timeline
286
    if (options && (options.start || options.end)) {
287
        this.setVisibleChartRange(options.start, options.end);
288
    }
289
    else if (this.firstDraw) {
290
        this.setVisibleChartRangeAuto();
291
    }
292

  
293
    this.firstDraw = false;
294
};
295

  
296

  
297
/**
298
 * Set options for the timeline.
299
 * Timeline must be redrawn afterwards
300
 * @param {Object} options A name/value map containing settings for the
301
 *                                 timeline. Optional.
302
 */
303
links.Timeline.prototype.setOptions = function(options) {
304
    if (options) {
305
        // retrieve parameter values
306
        for (var i in options) {
307
            if (options.hasOwnProperty(i)) {
308
                this.options[i] = options[i];
309
            }
310
        }
311
        
312
        // prepare i18n dependent on set locale
313
        if (typeof links.locales !== 'undefined' && this.options.locale !== 'en') {
314
            var localeOpts = links.locales[this.options.locale];
315
            if(localeOpts) {
316
                for (var l in localeOpts) {
317
                    if (localeOpts.hasOwnProperty(l)) {
318
                        this.options[l] = localeOpts[l];
319
                    }
320
                }
321
            }
322
        }
323

  
324
        // check for deprecated options
325
        if (options.showButtonAdd != undefined) {
326
            this.options.showButtonNew = options.showButtonAdd;
327
            console.log('WARNING: Option showButtonAdd is deprecated. Use showButtonNew instead');
328
        }
329
        if (options.intervalMin != undefined) {
330
            this.options.zoomMin = options.intervalMin;
331
            console.log('WARNING: Option intervalMin is deprecated. Use zoomMin instead');
332
        }
333
        if (options.intervalMax != undefined) {
334
            this.options.zoomMax = options.intervalMax;
335
            console.log('WARNING: Option intervalMax is deprecated. Use zoomMax instead');
336
        }
337

  
338
        if (options.scale && options.step) {
339
            this.step.setScale(options.scale, options.step);
340
        }
341
    }
342

  
343
    // validate options
344
    this.options.autoHeight = (this.options.height === "auto");
345
};
346

  
347
/**
348
 * Add new type of items
349
 * @param {String} typeName  Name of new type
350
 * @param {links.Timeline.Item} typeFactory Constructor of items
351
 */
352
links.Timeline.prototype.addItemType = function (typeName, typeFactory) {
353
    this.itemTypes[typeName] = typeFactory;
354
};
355

  
356
/**
357
 * Retrieve a map with the column indexes of the columns by column name.
358
 * For example, the method returns the map
359
 *     {
360
 *         start: 0,
361
 *         end: 1,
362
 *         content: 2,
363
 *         group: undefined,
364
 *         className: undefined
365
 *         editable: undefined
366
 *         type: undefined
367
 *     }
368
 * @param {google.visualization.DataTable} dataTable
369
 * @type {Object} map
370
 */
371
links.Timeline.mapColumnIds = function (dataTable) {
372
    var cols = {},
373
        colCount = dataTable.getNumberOfColumns(),
374
        allUndefined = true;
375

  
376
    // loop over the columns, and map the column id's to the column indexes
377
    for (var col = 0; col < colCount; col++) {
378
        var id = dataTable.getColumnId(col) || dataTable.getColumnLabel(col);
379
        cols[id] = col;
380
        if (id == 'start' || id == 'end' || id == 'content' || id == 'group' ||
381
            id == 'className' || id == 'editable' || id == 'type') {
382
            allUndefined = false;
383
        }
384
    }
385

  
386
    // if no labels or ids are defined, use the default mapping
387
    // for start, end, content, group, className, editable, type
388
    if (allUndefined) {
389
        cols.start = 0;
390
        cols.end = 1;
391
        cols.content = 2;
392
        if (colCount >= 3) {cols.group = 3}
393
        if (colCount >= 4) {cols.className = 4}
394
        if (colCount >= 5) {cols.editable = 5}
395
        if (colCount >= 6) {cols.type = 6}
396
    }
397

  
398
    return cols;
399
};
400

  
401
/**
402
 * Set data for the timeline
403
 * @param {google.visualization.DataTable | Array} data
404
 */
405
links.Timeline.prototype.setData = function(data) {
406
    // unselect any previously selected item
407
    this.unselectItem();
408

  
409
    if (!data) {
410
        data = [];
411
    }
412

  
413
    // clear all data
414
    this.stackCancelAnimation();
415
    this.clearItems();
416
    this.data = data;
417
    var items = this.items;
418
    this.deleteGroups();
419

  
420
    if (google && google.visualization &&
421
        data instanceof google.visualization.DataTable) {
422
        // map the datatable columns
423
        var cols = links.Timeline.mapColumnIds(data);
424

  
425
        // read DataTable
426
        for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) {
427
            items.push(this.createItem({
428
                'start':     ((cols.start != undefined)     ? data.getValue(row, cols.start)     : undefined),
429
                'end':       ((cols.end != undefined)       ? data.getValue(row, cols.end)       : undefined),
430
                'content':   ((cols.content != undefined)   ? data.getValue(row, cols.content)   : undefined),
431
                'group':     ((cols.group != undefined)     ? data.getValue(row, cols.group)     : undefined),
432
                'className': ((cols.className != undefined) ? data.getValue(row, cols.className) : undefined),
433
                'editable':  ((cols.editable != undefined)  ? data.getValue(row, cols.editable)  : undefined),
434
                'type':      ((cols.editable != undefined)  ? data.getValue(row, cols.type)      : undefined)
435
            }));
436
        }
437
    }
438
    else if (links.Timeline.isArray(data)) {
439
        // read JSON array
440
        for (var row = 0, rows = data.length; row < rows; row++) {
441
            var itemData = data[row];
442
            var item = this.createItem(itemData);
443
            items.push(item);
444
        }
445
    }
446
    else {
447
        throw "Unknown data type. DataTable or Array expected.";
448
    }
449

  
450
    // prepare data for clustering, by filtering and sorting by type
451
    if (this.options.cluster) {
452
        this.clusterGenerator.setData(this.items);
453
    }
454

  
455
    this.render({
456
        animate: false
457
    });
458
};
459

  
460
/**
461
 * Return the original data table.
462
 * @return {google.visualization.DataTable | Array} data
463
 */
464
links.Timeline.prototype.getData = function  () {
465
    return this.data;
466
};
467

  
468

  
469
/**
470
 * Update the original data with changed start, end or group.
471
 *
472
 * @param {Number} index
473
 * @param {Object} values   An object containing some of the following parameters:
474
 *                          {Date} start,
475
 *                          {Date} end,
476
 *                          {String} content,
477
 *                          {String} group
478
 */
479
links.Timeline.prototype.updateData = function  (index, values) {
480
    var data = this.data,
481
        prop;
482

  
483
    if (google && google.visualization &&
484
        data instanceof google.visualization.DataTable) {
485
        // update the original google DataTable
486
        var missingRows = (index + 1) - data.getNumberOfRows();
487
        if (missingRows > 0) {
488
            data.addRows(missingRows);
489
        }
490

  
491
        // map the column id's by name
492
        var cols = links.Timeline.mapColumnIds(data);
493

  
494
        // merge all fields from the provided data into the current data
495
        for (prop in values) {
496
            if (values.hasOwnProperty(prop)) {
497
                var col = cols[prop];
498
                if (col == undefined) {
499
                    // create new column
500
                    var value = values[prop];
501
                    var valueType = 'string';
502
                    if (typeof(value) == 'number')       {valueType = 'number';}
503
                    else if (typeof(value) == 'boolean') {valueType = 'boolean';}
504
                    else if (value instanceof Date)      {valueType = 'datetime';}
505
                    col = data.addColumn(valueType, prop);
506
                }
507
                data.setValue(index, col, values[prop]);
508

  
509
                // TODO: correctly serialize the start and end Date to the desired type (Date, String, or Number)
510
            }
511
        }
512
    }
513
    else if (links.Timeline.isArray(data)) {
514
        // update the original JSON table
515
        var row = data[index];
516
        if (row == undefined) {
517
            row = {};
518
            data[index] = row;
519
        }
520

  
521
        // merge all fields from the provided data into the current data
522
        for (prop in values) {
523
            if (values.hasOwnProperty(prop)) {
524
                row[prop] = values[prop];
525

  
526
                // TODO: correctly serialize the start and end Date to the desired type (Date, String, or Number)
527
            }
528
        }
529
    }
530
    else {
531
        throw "Cannot update data, unknown type of data";
532
    }
533
};
534

  
535
/**
536
 * Find the item index from a given HTML element
537
 * If no item index is found, undefined is returned
538
 * @param {Element} element
539
 * @return {Number | undefined} index
540
 */
541
links.Timeline.prototype.getItemIndex = function(element) {
542
    var e = element,
543
        dom = this.dom,
544
        frame = dom.items.frame,
545
        items = this.items,
546
        index = undefined;
547

  
548
    // try to find the frame where the items are located in
549
    while (e.parentNode && e.parentNode !== frame) {
550
        e = e.parentNode;
551
    }
552

  
553
    if (e.parentNode === frame) {
554
        // yes! we have found the parent element of all items
555
        // retrieve its id from the array with items
556
        for (var i = 0, iMax = items.length; i < iMax; i++) {
557
            if (items[i].dom === e) {
558
                index = i;
559
                break;
560
            }
561
        }
562
    }
563

  
564
    return index;
565
};
566

  
567
/**
568
 * Set a new size for the timeline
569
 * @param {string} width   Width in pixels or percentage (for example "800px"
570
 *                         or "50%")
571
 * @param {string} height  Height in pixels or percentage  (for example "400px"
572
 *                         or "30%")
573
 */
574
links.Timeline.prototype.setSize = function(width, height) {
575
    if (width) {
576
        this.options.width = width;
577
        this.dom.frame.style.width = width;
578
    }
579
    if (height) {
580
        this.options.height = height;
581
        this.options.autoHeight = (this.options.height === "auto");
582
        if (height !==  "auto" ) {
583
            this.dom.frame.style.height = height;
584
        }
585
    }
586

  
587
    this.render({
588
        animate: false
589
    });
590
};
591

  
592

  
593
/**
594
 * Set a new value for the visible range int the timeline.
595
 * Set start undefined to include everything from the earliest date to end.
596
 * Set end undefined to include everything from start to the last date.
597
 * Example usage:
598
 *    myTimeline.setVisibleChartRange(new Date("2010-08-22"),
599
 *                                    new Date("2010-09-13"));
600
 * @param {Date}   start     The start date for the timeline. optional
601
 * @param {Date}   end       The end date for the timeline. optional
602
 * @param {boolean} redraw   Optional. If true (default) the Timeline is
603
 *                           directly redrawn
604
 */
605
links.Timeline.prototype.setVisibleChartRange = function(start, end, redraw) {
606
    var range = {};
607
    if (!start || !end) {
608
        // retrieve the date range of the items
609
        range = this.getDataRange(true);
610
    }
611

  
612
    if (!start) {
613
        if (end) {
614
            if (range.min && range.min.valueOf() < end.valueOf()) {
615
                // start of the data
616
                start = range.min;
617
            }
618
            else {
619
                // 7 days before the end
620
                start = new Date(end.valueOf());
621
                start.setDate(start.getDate() - 7);
622
            }
623
        }
624
        else {
625
            // default of 3 days ago
626
            start = new Date();
627
            start.setDate(start.getDate() - 3);
628
        }
629
    }
630

  
631
    if (!end) {
632
        if (range.max) {
633
            // end of the data
634
            end = range.max;
635
        }
636
        else {
637
            // 7 days after start
638
            end = new Date(start.valueOf());
639
            end.setDate(end.getDate() + 7);
640
        }
641
    }
642

  
643
    // prevent start Date <= end Date
644
    if (end <= start) {
645
        end = new Date(start.valueOf());
646
        end.setDate(end.getDate() + 7);
647
    }
648

  
649
    // limit to the allowed range (don't let this do by applyRange,
650
    // because that method will try to maintain the interval (end-start)
651
    var min = this.options.min ? this.options.min : undefined; // date
652
    if (min != undefined && start.valueOf() < min.valueOf()) {
653
        start = new Date(min.valueOf()); // date
654
    }
655
    var max = this.options.max ? this.options.max : undefined; // date
656
    if (max != undefined && end.valueOf() > max.valueOf()) {
657
        end = new Date(max.valueOf()); // date
658
    }
659

  
660
    this.applyRange(start, end);
661

  
662
    if (redraw == undefined || redraw == true) {
663
        this.render({
664
            animate: false
665
        });  // TODO: optimize, no reflow needed
666
    }
667
    else {
668
        this.recalcConversion();
669
    }
670
};
671

  
672

  
673
/**
674
 * Change the visible chart range such that all items become visible
675
 */
676
links.Timeline.prototype.setVisibleChartRangeAuto = function() {
677
    var range = this.getDataRange(true);
678
    this.setVisibleChartRange(range.min, range.max);
679
};
680

  
681
/**
682
 * Adjust the visible range such that the current time is located in the center
683
 * of the timeline
684
 */
685
links.Timeline.prototype.setVisibleChartRangeNow = function() {
686
    var now = new Date();
687

  
688
    var diff = (this.end.valueOf() - this.start.valueOf());
689

  
690
    var startNew = new Date(now.valueOf() - diff/2);
691
    var endNew = new Date(startNew.valueOf() + diff);
692
    this.setVisibleChartRange(startNew, endNew);
693
};
694

  
695

  
696
/**
697
 * Retrieve the current visible range in the timeline.
698
 * @return {Object} An object with start and end properties
699
 */
700
links.Timeline.prototype.getVisibleChartRange = function() {
701
    return {
702
        'start': new Date(this.start.valueOf()),
703
        'end': new Date(this.end.valueOf())
704
    };
705
};
706

  
707
/**
708
 * Get the date range of the items.
709
 * @param {boolean} [withMargin]  If true, 5% of whitespace is added to the
710
 *                                left and right of the range. Default is false.
711
 * @return {Object} range    An object with parameters min and max.
712
 *                           - {Date} min is the lowest start date of the items
713
 *                           - {Date} max is the highest start or end date of the items
714
 *                           If no data is available, the values of min and max
715
 *                           will be undefined
716
 */
717
links.Timeline.prototype.getDataRange = function (withMargin) {
718
    var items = this.items,
719
        min = undefined, // number
720
        max = undefined; // number
721

  
722
    if (items) {
723
        for (var i = 0, iMax = items.length; i < iMax; i++) {
724
            var item = items[i],
725
                start = item.start != undefined ? item.start.valueOf() : undefined,
726
                end   = item.end != undefined   ? item.end.valueOf() : start;
727

  
728
            if (start != undefined) {
729
                min = (min != undefined) ? Math.min(min.valueOf(), start.valueOf()) : start;
730
            }
731

  
732
            if (end != undefined) {
733
                max = (max != undefined) ? Math.max(max.valueOf(), end.valueOf()) : end;
734
            }
735
        }
736
    }
737

  
738
    if (min && max && withMargin) {
739
        // zoom out 5% such that you have a little white space on the left and right
740
        var diff = (max - min);
741
        min = min - diff * 0.05;
742
        max = max + diff * 0.05;
743
    }
744

  
745
    return {
746
        'min': min != undefined ? new Date(min) : undefined,
747
        'max': max != undefined ? new Date(max) : undefined
748
    };
749
};
750

  
751
/**
752
 * Re-render (reflow and repaint) all components of the Timeline: frame, axis,
753
 * items, ...
754
 * @param {Object} [options]  Available options:
755
 *                            {boolean} renderTimesLeft   Number of times the
756
 *                                                        render may be repeated
757
 *                                                        5 times by default.
758
 *                            {boolean} animate           takes options.animate
759
 *                                                        as default value
760
 */
761
links.Timeline.prototype.render = function(options) {
762
    var frameResized = this.reflowFrame();
763
    var axisResized = this.reflowAxis();
764
    var groupsResized = this.reflowGroups();
765
    var itemsResized = this.reflowItems();
766
    var resized = (frameResized || axisResized || groupsResized || itemsResized);
767

  
768
    // TODO: only stackEvents/filterItems when resized or changed. (gives a bootstrap issue).
769
    // if (resized) {
770
    var animate = this.options.animate;
771
    if (options && options.animate != undefined) {
772
        animate = options.animate;
773
    }
774

  
775
    this.recalcConversion();
776
    this.clusterItems();
777
    this.filterItems();
778
    this.stackItems(animate);
779

  
780
    this.recalcItems();
781

  
782
    // TODO: only repaint when resized or when filterItems or stackItems gave a change?
783
    var needsReflow = this.repaint();
784

  
785
    // re-render once when needed (prevent endless re-render loop)
786
    if (needsReflow) {
787
        var renderTimesLeft = options ? options.renderTimesLeft : undefined;
788
        if (renderTimesLeft == undefined) {
789
            renderTimesLeft = 5;
790
        }
791
        if (renderTimesLeft > 0) {
792
            this.render({
793
                'animate': options ? options.animate: undefined,
794
                'renderTimesLeft': (renderTimesLeft - 1)
795
            });
796
        }
797
    }
798
};
799

  
800
/**
801
 * Repaint all components of the Timeline
802
 * @return {boolean} needsReflow   Returns true if the DOM is changed such that
803
 *                                 a reflow is needed.
804
 */
805
links.Timeline.prototype.repaint = function() {
806
    var frameNeedsReflow = this.repaintFrame();
807
    var axisNeedsReflow  = this.repaintAxis();
808
    var groupsNeedsReflow  = this.repaintGroups();
809
    var itemsNeedsReflow = this.repaintItems();
810
    this.repaintCurrentTime();
811
    this.repaintCustomTime();
812

  
813
    return (frameNeedsReflow || axisNeedsReflow || groupsNeedsReflow || itemsNeedsReflow);
814
};
815

  
816
/**
817
 * Reflow the timeline frame
818
 * @return {boolean} resized    Returns true if any of the frame elements
819
 *                              have been resized.
820
 */
821
links.Timeline.prototype.reflowFrame = function() {
822
    var dom = this.dom,
823
        options = this.options,
824
        size = this.size,
825
        resized = false;
826

  
827
    // Note: IE7 has issues with giving frame.clientWidth, therefore I use offsetWidth instead
828
    var frameWidth  = dom.frame ? dom.frame.offsetWidth : 0,
829
        frameHeight = dom.frame ? dom.frame.clientHeight : 0;
830

  
831
    resized = resized || (size.frameWidth !== frameWidth);
832
    resized = resized || (size.frameHeight !== frameHeight);
833
    size.frameWidth = frameWidth;
834
    size.frameHeight = frameHeight;
835

  
836
    return resized;
837
};
838

  
839
/**
840
 * repaint the Timeline frame
841
 * @return {boolean} needsReflow   Returns true if the DOM is changed such that
842
 *                                 a reflow is needed.
843
 */
844
links.Timeline.prototype.repaintFrame = function() {
845
    var needsReflow = false,
846
        dom = this.dom,
847
        options = this.options,
848
        size = this.size;
849

  
850
    // main frame
851
    if (!dom.frame) {
852
        dom.frame = document.createElement("DIV");
853
        dom.frame.className = "timeline-frame ui-widget ui-widget-content ui-corner-all";
854
        dom.frame.style.position = "relative";
855
        dom.frame.style.overflow = "hidden";
856
        dom.container.appendChild(dom.frame);
857
        needsReflow = true;
858
    }
859

  
860
    var height = options.autoHeight ?
861
        (size.actualHeight + "px") :
862
        (options.height || "100%");
863
    var width  = options.width || "100%";
864
    needsReflow = needsReflow || (dom.frame.style.height != height);
865
    needsReflow = needsReflow || (dom.frame.style.width != width);
866
    dom.frame.style.height = height;
867
    dom.frame.style.width = width;
868

  
869
    // contents
870
    if (!dom.content) {
871
        // create content box where the axis and items will be created
872
        dom.content = document.createElement("DIV");
873
        dom.content.style.position = "relative";
874
        dom.content.style.overflow = "hidden";
875
        dom.frame.appendChild(dom.content);
876

  
877
        var timelines = document.createElement("DIV");
878
        timelines.style.position = "absolute";
879
        timelines.style.left = "0px";
880
        timelines.style.top = "0px";
881
        timelines.style.height = "100%";
882
        timelines.style.width = "0px";
883
        dom.content.appendChild(timelines);
884
        dom.contentTimelines = timelines;
885

  
886
        var params = this.eventParams,
887
            me = this;
888
        if (!params.onMouseDown) {
889
            params.onMouseDown = function (event) {me.onMouseDown(event);};
890
            links.Timeline.addEventListener(dom.content, "mousedown", params.onMouseDown);
891
        }
892
        if (!params.onTouchStart) {
893
            params.onTouchStart = function (event) {me.onTouchStart(event);};
894
            links.Timeline.addEventListener(dom.content, "touchstart", params.onTouchStart);
895
        }
896
        if (!params.onMouseWheel) {
897
            params.onMouseWheel = function (event) {me.onMouseWheel(event);};
898
            links.Timeline.addEventListener(dom.content, "mousewheel", params.onMouseWheel);
899
        }
900
        if (!params.onDblClick) {
901
            params.onDblClick = function (event) {me.onDblClick(event);};
902
            links.Timeline.addEventListener(dom.content, "dblclick", params.onDblClick);
903
        }
904

  
905
        needsReflow = true;
906
    }
907
    dom.content.style.left = size.contentLeft + "px";
908
    dom.content.style.top = "0px";
909
    dom.content.style.width = size.contentWidth + "px";
910
    dom.content.style.height = size.frameHeight + "px";
911

  
912
    this.repaintNavigation();
913

  
914
    return needsReflow;
915
};
916

  
917
/**
918
 * Reflow the timeline axis. Calculate its height, width, positioning, etc...
919
 * @return {boolean} resized    returns true if the axis is resized
920
 */
921
links.Timeline.prototype.reflowAxis = function() {
922
    var resized = false,
923
        dom = this.dom,
924
        options = this.options,
925
        size = this.size,
926
        axisDom = dom.axis;
927

  
928
    var characterMinorWidth  = (axisDom && axisDom.characterMinor) ? axisDom.characterMinor.clientWidth : 0,
929
        characterMinorHeight = (axisDom && axisDom.characterMinor) ? axisDom.characterMinor.clientHeight : 0,
930
        characterMajorWidth  = (axisDom && axisDom.characterMajor) ? axisDom.characterMajor.clientWidth : 0,
931
        characterMajorHeight = (axisDom && axisDom.characterMajor) ? axisDom.characterMajor.clientHeight : 0,
932
        axisHeight = (options.showMinorLabels ? characterMinorHeight : 0) +
933
            (options.showMajorLabels ? characterMajorHeight : 0);
934

  
935
    var axisTop  = options.axisOnTop ? 0 : size.frameHeight - axisHeight,
936
        axisLine = options.axisOnTop ? axisHeight : axisTop;
937

  
938
    resized = resized || (size.axis.top !== axisTop);
939
    resized = resized || (size.axis.line !== axisLine);
940
    resized = resized || (size.axis.height !== axisHeight);
941
    size.axis.top = axisTop;
942
    size.axis.line = axisLine;
943
    size.axis.height = axisHeight;
944
    size.axis.labelMajorTop = options.axisOnTop ? 0 : axisLine +
945
        (options.showMinorLabels ? characterMinorHeight : 0);
946
    size.axis.labelMinorTop = options.axisOnTop ?
947
        (options.showMajorLabels ? characterMajorHeight : 0) :
948
        axisLine;
949
    size.axis.lineMinorTop = options.axisOnTop ? size.axis.labelMinorTop : 0;
950
    size.axis.lineMinorHeight = options.showMajorLabels ?
951
        size.frameHeight - characterMajorHeight:
952
        size.frameHeight;
953
    if (axisDom && axisDom.minorLines && axisDom.minorLines.length) {
954
        size.axis.lineMinorWidth = axisDom.minorLines[0].offsetWidth;
955
    }
956
    else {
957
        size.axis.lineMinorWidth = 1;
958
    }
959
    if (axisDom && axisDom.majorLines && axisDom.majorLines.length) {
960
        size.axis.lineMajorWidth = axisDom.majorLines[0].offsetWidth;
961
    }
962
    else {
963
        size.axis.lineMajorWidth = 1;
964
    }
965

  
966
    resized = resized || (size.axis.characterMinorWidth  !== characterMinorWidth);
967
    resized = resized || (size.axis.characterMinorHeight !== characterMinorHeight);
968
    resized = resized || (size.axis.characterMajorWidth  !== characterMajorWidth);
969
    resized = resized || (size.axis.characterMajorHeight !== characterMajorHeight);
970
    size.axis.characterMinorWidth  = characterMinorWidth;
971
    size.axis.characterMinorHeight = characterMinorHeight;
972
    size.axis.characterMajorWidth  = characterMajorWidth;
973
    size.axis.characterMajorHeight = characterMajorHeight;
974

  
975
    var contentHeight = Math.max(size.frameHeight - axisHeight, 0);
976
    size.contentLeft = options.groupsOnRight ? 0 : size.groupsWidth;
977
    size.contentWidth = Math.max(size.frameWidth - size.groupsWidth, 0);
978
    size.contentHeight = contentHeight;
979

  
980
    return resized;
981
};
982

  
983
/**
984
 * Redraw the timeline axis with minor and major labels
985
 * @return {boolean} needsReflow     Returns true if the DOM is changed such
986
 *                                   that a reflow is needed.
987
 */
988
links.Timeline.prototype.repaintAxis = function() {
989
    var needsReflow = false,
990
        dom = this.dom,
991
        options = this.options,
992
        size = this.size,
993
        step = this.step;
994

  
995
    var axis = dom.axis;
996
    if (!axis) {
997
        axis = {};
998
        dom.axis = axis;
999
    }
1000
    if (!size.axis.properties) {
1001
        size.axis.properties = {};
1002
    }
1003
    if (!axis.minorTexts) {
1004
        axis.minorTexts = [];
1005
    }
1006
    if (!axis.minorLines) {
1007
        axis.minorLines = [];
1008
    }
1009
    if (!axis.majorTexts) {
1010
        axis.majorTexts = [];
1011
    }
1012
    if (!axis.majorLines) {
1013
        axis.majorLines = [];
1014
    }
1015

  
1016
    if (!axis.frame) {
1017
        axis.frame = document.createElement("DIV");
1018
        axis.frame.style.position = "absolute";
1019
        axis.frame.style.left = "0px";
1020
        axis.frame.style.top = "0px";
1021
        dom.content.appendChild(axis.frame);
1022
    }
1023

  
1024
    // take axis offline
1025
    dom.content.removeChild(axis.frame);
1026

  
1027
    axis.frame.style.width = (size.contentWidth) + "px";
1028
    axis.frame.style.height = (size.axis.height) + "px";
1029

  
1030
    // the drawn axis is more wide than the actual visual part, such that
1031
    // the axis can be dragged without having to redraw it each time again.
1032
    var start = this.screenToTime(0);
1033
    var end = this.screenToTime(size.contentWidth);
1034

  
1035
    // calculate minimum step (in milliseconds) based on character size
1036
    if (size.axis.characterMinorWidth) {
1037
        this.minimumStep = this.screenToTime(size.axis.characterMinorWidth * 6) -
1038
            this.screenToTime(0);
1039

  
1040
        step.setRange(start, end, this.minimumStep);
1041
    }
1042

  
1043
    var charsNeedsReflow = this.repaintAxisCharacters();
1044
    needsReflow = needsReflow || charsNeedsReflow;
1045

  
1046
    // The current labels on the axis will be re-used (much better performance),
1047
    // therefore, the repaintAxis method uses the mechanism with
1048
    // repaintAxisStartOverwriting, repaintAxisEndOverwriting, and
1049
    // this.size.axis.properties is used.
1050
    this.repaintAxisStartOverwriting();
1051

  
1052
    step.start();
1053
    var xFirstMajorLabel = undefined;
1054
    var max = 0;
1055
    while (!step.end() && max < 1000) {
1056
        max++;
1057
        var cur = step.getCurrent(),
1058
            x = this.timeToScreen(cur),
1059
            isMajor = step.isMajor();
1060

  
1061
        if (options.showMinorLabels) {
1062
            this.repaintAxisMinorText(x, step.getLabelMinor(options));
1063
        }
1064

  
1065
        if (isMajor && options.showMajorLabels) {
1066
            if (x > 0) {
1067
                if (xFirstMajorLabel == undefined) {
1068
                    xFirstMajorLabel = x;
1069
                }
1070
                this.repaintAxisMajorText(x, step.getLabelMajor(options));
1071
            }
1072
            this.repaintAxisMajorLine(x);
1073
        }
1074
        else {
1075
            this.repaintAxisMinorLine(x);
1076
        }
1077

  
1078
        step.next();
1079
    }
1080

  
1081
    // create a major label on the left when needed
1082
    if (options.showMajorLabels) {
1083
        var leftTime = this.screenToTime(0),
1084
            leftText = this.step.getLabelMajor(options, leftTime),
1085
            width = leftText.length * size.axis.characterMajorWidth + 10; // upper bound estimation
1086

  
1087
        if (xFirstMajorLabel == undefined || width < xFirstMajorLabel) {
1088
            this.repaintAxisMajorText(0, leftText, leftTime);
1089
        }
1090
    }
1091

  
1092
    // cleanup left over labels
1093
    this.repaintAxisEndOverwriting();
1094

  
1095
    this.repaintAxisHorizontal();
1096

  
1097
    // put axis online
1098
    dom.content.insertBefore(axis.frame, dom.content.firstChild);
1099

  
1100
    return needsReflow;
1101
};
1102

  
1103
/**
1104
 * Create characters used to determine the size of text on the axis
1105
 * @return {boolean} needsReflow   Returns true if the DOM is changed such that
1106
 *                                 a reflow is needed.
1107
 */
1108
links.Timeline.prototype.repaintAxisCharacters = function () {
1109
    // calculate the width and height of a single character
1110
    // this is used to calculate the step size, and also the positioning of the
1111
    // axis
1112
    var needsReflow = false,
1113
        dom = this.dom,
1114
        axis = dom.axis,
1115
        text;
1116

  
1117
    if (!axis.characterMinor) {
1118
        text = document.createTextNode("0");
1119
        var characterMinor = document.createElement("DIV");
1120
        characterMinor.className = "timeline-axis-text timeline-axis-text-minor";
1121
        characterMinor.appendChild(text);
1122
        characterMinor.style.position = "absolute";
1123
        characterMinor.style.visibility = "hidden";
1124
        characterMinor.style.paddingLeft = "0px";
1125
        characterMinor.style.paddingRight = "0px";
1126
        axis.frame.appendChild(characterMinor);
1127

  
1128
        axis.characterMinor = characterMinor;
1129
        needsReflow = true;
1130
    }
1131

  
1132
    if (!axis.characterMajor) {
1133
        text = document.createTextNode("0");
1134
        var characterMajor = document.createElement("DIV");
1135
        characterMajor.className = "timeline-axis-text timeline-axis-text-major";
1136
        characterMajor.appendChild(text);
1137
        characterMajor.style.position = "absolute";
1138
        characterMajor.style.visibility = "hidden";
1139
        characterMajor.style.paddingLeft = "0px";
1140
        characterMajor.style.paddingRight = "0px";
1141
        axis.frame.appendChild(characterMajor);
1142

  
1143
        axis.characterMajor = characterMajor;
1144
        needsReflow = true;
1145
    }
1146

  
1147
    return needsReflow;
1148
};
1149

  
1150
/**
1151
 * Initialize redraw of the axis. All existing labels and lines will be
1152
 * overwritten and reused.
1153
 */
1154
links.Timeline.prototype.repaintAxisStartOverwriting = function () {
1155
    var properties = this.size.axis.properties;
1156

  
1157
    properties.minorTextNum = 0;
1158
    properties.minorLineNum = 0;
1159
    properties.majorTextNum = 0;
1160
    properties.majorLineNum = 0;
1161
};
1162

  
1163
/**
1164
 * End of overwriting HTML DOM elements of the axis.
1165
 * remaining elements will be removed
1166
 */
1167
links.Timeline.prototype.repaintAxisEndOverwriting = function () {
1168
    var dom = this.dom,
1169
        props = this.size.axis.properties,
1170
        frame = this.dom.axis.frame,
1171
        num;
1172

  
1173
    // remove leftovers
1174
    var minorTexts = dom.axis.minorTexts;
1175
    num = props.minorTextNum;
1176
    while (minorTexts.length > num) {
1177
        var minorText = minorTexts[num];
1178
        frame.removeChild(minorText);
1179
        minorTexts.splice(num, 1);
1180
    }
1181

  
1182
    var minorLines = dom.axis.minorLines;
1183
    num = props.minorLineNum;
1184
    while (minorLines.length > num) {
1185
        var minorLine = minorLines[num];
1186
        frame.removeChild(minorLine);
1187
        minorLines.splice(num, 1);
1188
    }
1189

  
1190
    var majorTexts = dom.axis.majorTexts;
1191
    num = props.majorTextNum;
1192
    while (majorTexts.length > num) {
1193
        var majorText = majorTexts[num];
1194
        frame.removeChild(majorText);
1195
        majorTexts.splice(num, 1);
1196
    }
1197

  
1198
    var majorLines = dom.axis.majorLines;
1199
    num = props.majorLineNum;
1200
    while (majorLines.length > num) {
1201
        var majorLine = majorLines[num];
1202
        frame.removeChild(majorLine);
1203
        majorLines.splice(num, 1);
1204
    }
1205
};
1206

  
1207
/**
1208
 * Repaint the horizontal line and background of the axis
1209
 */
1210
links.Timeline.prototype.repaintAxisHorizontal = function() {
1211
    var axis = this.dom.axis,
1212
        size = this.size,
1213
        options = this.options;
1214

  
1215
    // line behind all axis elements (possibly having a background color)
1216
    var hasAxis = (options.showMinorLabels || options.showMajorLabels);
1217
    if (hasAxis) {
1218
        if (!axis.backgroundLine) {
1219
            // create the axis line background (for a background color or so)
1220
            var backgroundLine = document.createElement("DIV");
1221
            backgroundLine.className = "timeline-axis";
1222
            backgroundLine.style.position = "absolute";
1223
            backgroundLine.style.left = "0px";
1224
            backgroundLine.style.width = "100%";
1225
            backgroundLine.style.border = "none";
1226
            axis.frame.insertBefore(backgroundLine, axis.frame.firstChild);
1227

  
1228
            axis.backgroundLine = backgroundLine;
1229
        }
1230

  
1231
        if (axis.backgroundLine) {
1232
            axis.backgroundLine.style.top = size.axis.top + "px";
1233
            axis.backgroundLine.style.height = size.axis.height + "px";
1234
        }
1235
    }
1236
    else {
1237
        if (axis.backgroundLine) {
1238
            axis.frame.removeChild(axis.backgroundLine);
1239
            delete axis.backgroundLine;
1240
        }
1241
    }
1242

  
1243
    // line before all axis elements
1244
    if (hasAxis) {
1245
        if (axis.line) {
1246
            // put this line at the end of all childs
1247
            var line = axis.frame.removeChild(axis.line);
1248
            axis.frame.appendChild(line);
1249
        }
1250
        else {
1251
            // make the axis line
1252
            var line = document.createElement("DIV");
1253
            line.className = "timeline-axis";
1254
            line.style.position = "absolute";
1255
            line.style.left = "0px";
1256
            line.style.width = "100%";
1257
            line.style.height = "0px";
1258
            axis.frame.appendChild(line);
1259

  
1260
            axis.line = line;
1261
        }
1262

  
1263
        axis.line.style.top = size.axis.line + "px";
1264
    }
1265
    else {
1266
        if (axis.line && axis.line.parentElement) {
1267
            axis.frame.removeChild(axis.line);
1268
            delete axis.line;
1269
        }
1270
    }
1271
};
1272

  
1273
/**
1274
 * Create a minor label for the axis at position x
1275
 * @param {Number} x
1276
 * @param {String} text
1277
 */
1278
links.Timeline.prototype.repaintAxisMinorText = function (x, text) {
1279
    var size = this.size,
1280
        dom = this.dom,
1281
        props = size.axis.properties,
1282
        frame = dom.axis.frame,
1283
        minorTexts = dom.axis.minorTexts,
1284
        index = props.minorTextNum,
1285
        label;
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff