-
Notifications
You must be signed in to change notification settings - Fork 1
/
view.timeline.js
186 lines (170 loc) · 5.21 KB
/
view.timeline.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*jshint multistr:true */
this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
// turn off unnecessary logging from VMM Timeline
if (typeof VMM !== 'undefined') {
VMM.debug = false;
}
// ## Timeline
//
// Timeline view using http://timeline.verite.co/
my.Timeline = Backbone.View.extend({
template: ' \
<div class="recline-timeline"> \
<div id="vmm-timeline-id"></div> \
</div> \
',
// These are the default (case-insensitive) names of field that are used if found.
// If not found, the user will need to define these fields on initialization
startFieldNames: ['date','startdate', 'start', 'start-date'],
endFieldNames: ['end','endDate'],
elementId: '#vmm-timeline-id',
initialize: function(options) {
var self = this;
this.timeline = new VMM.Timeline(this.elementId);
this._timelineIsInitialized = false;
this.listenTo(this.model.fields, 'reset', function() {
self._setupTemporalField();
});
this.listenTo(this.model.records, 'all', function() {
self.reloadData();
});
var stateData = _.extend({
startField: null,
endField: null,
// by default timelinejs (and browsers) will parse ambiguous dates in US format (mm/dd/yyyy)
// set to true to interpret dd/dd/dddd as dd/mm/yyyy
nonUSDates: false,
timelineJSOptions: {}
},
options.state
);
this.state = new recline.Model.ObjectState(stateData);
this._setupTemporalField();
},
render: function() {
var tmplData = {};
var htmls = Mustache.render(this.template, tmplData);
this.$el.html(htmls);
// can only call _initTimeline once view in DOM as Timeline uses $
// internally to look up element
if ($(this.elementId).length > 0) {
this._initTimeline();
}
},
show: function() {
// only call _initTimeline once view in DOM as Timeline uses $ internally to look up element
if (this._timelineIsInitialized === false) {
this._initTimeline();
}
},
_initTimeline: function() {
var data = this._timelineJSON();
var config = this.state.get("timelineJSOptions");
config.id = this.elementId;
this.timeline.init(config, data);
this._timelineIsInitialized = true
},
reloadData: function() {
if (this._timelineIsInitialized) {
var data = this._timelineJSON();
this.timeline.reload(data);
}
},
// Convert record to JSON for timeline
//
// Designed to be overridden in client apps
convertRecord: function(record, fields) {
return this._convertRecord(record, fields);
},
// Internal method to generate a Timeline formatted entry
_convertRecord: function(record, fields) {
var start = this._parseDate(record.get(this.state.get('startField')));
var end = this._parseDate(record.get(this.state.get('endField')));
if (start) {
var tlEntry = {
"startDate": start,
"endDate": end,
"headline": String(record.get('title') || ''),
"text": record.get('description') || record.summary(),
"tag": record.get('tags')
};
return tlEntry;
} else {
return null;
}
},
_timelineJSON: function() {
var self = this;
var out = {
'timeline': {
'type': 'default',
'headline': '',
'date': [
]
}
};
this.model.records.each(function(record) {
var newEntry = self.convertRecord(record, self.fields);
if (newEntry) {
out.timeline.date.push(newEntry);
}
});
// if no entries create a placeholder entry to prevent Timeline crashing with error
if (out.timeline.date.length === 0) {
var tlEntry = {
"startDate": '2000,1,1',
"headline": 'No data to show!'
};
out.timeline.date.push(tlEntry);
}
return out;
},
// convert dates into a format TimelineJS will handle
// TimelineJS does not document this at all so combo of read the code +
// trial and error
// Summary (AFAICt):
// Preferred: [-]yyyy[,mm,dd,hh,mm,ss]
// Supported: mm/dd/yyyy
_parseDate: function(date) {
if (!date) {
return null;
}
var out = $.trim(date);
out = out.replace(/(\d)th/g, '$1');
out = out.replace(/(\d)st/g, '$1');
out = $.trim(out);
if (out.match(/\d\d\d\d-\d\d-\d\d(T.*)?/)) {
out = out.replace(/-/g, ',').replace('T', ',').replace(':',',');
}
if (out.match(/\d\d-\d\d-\d\d.*/)) {
out = out.replace(/-/g, '/');
}
if (this.state.get('nonUSDates')) {
var parts = out.match(/(\d\d)\/(\d\d)\/(\d\d.*)/);
if (parts) {
out = [parts[2], parts[1], parts[3]].join('/');
}
}
return out;
},
_setupTemporalField: function() {
this.state.set({
startField: this._checkField(this.startFieldNames),
endField: this._checkField(this.endFieldNames)
});
},
_checkField: function(possibleFieldNames) {
var modelFieldNames = this.model.fields.pluck('id');
for (var i = 0; i < possibleFieldNames.length; i++){
for (var j = 0; j < modelFieldNames.length; j++){
if (modelFieldNames[j].toLowerCase() == possibleFieldNames[i].toLowerCase())
return modelFieldNames[j];
}
}
return null;
}
});
})(jQuery, recline.View);