1 /*
  2  * Timemap.js Copyright 2010 Nick Rabinowitz.
  3  * Licensed under the MIT License (see LICENSE.txt)
  4  */
  5 
  6 /**
  7  * @fileOverview
  8  * Additional TimeMap manipulation functions.
  9  * Functions in this file are used to manipulate a TimeMap, TimeMapDataset, or
 10  * TimeMapItem after the initial load process.
 11  *
 12  * @author Nick Rabinowitz (www.nickrabinowitz.com)
 13  */
 14 
 15 /*globals TimeMap, TimeMapDataset, TimeMapItem, Timeline */
 16  
 17 (function(){
 18     var window = this,
 19         TimeMap = window.TimeMap, 
 20         TimeMapDataset = window.TimeMapDataset, 
 21         TimeMapItem = window.TimeMapItem;
 22         
 23 /*----------------------------------------------------------------------------
 24  * TimeMap manipulation: stuff affecting every dataset
 25  *---------------------------------------------------------------------------*/
 26  
 27 /**
 28  * Delete all datasets, clearing them from map and timeline. Note
 29  * that this is more efficient than calling clear() on each dataset.
 30  */
 31 TimeMap.prototype.clear = function() {
 32     var tm = this;
 33     tm.eachItem(function(item) {
 34         item.event = item.placemark = null;
 35     });
 36     tm.map.clearOverlays();
 37     tm.eventSource.clear();
 38     tm.datasets = [];
 39 };
 40 
 41 /**
 42  * Delete one dataset, clearing it from map and timeline
 43  *
 44  * @param {String} id    Id of dataset to delete
 45  */
 46 TimeMap.prototype.deleteDataset = function(id) {
 47     this.datasets[id].clear();
 48     delete this.datasets[id];
 49 };
 50 
 51 /**
 52  * Hides placemarks for a given dataset
 53  * 
 54  * @param {String} id   The id of the dataset to hide
 55  */
 56 TimeMap.prototype.hideDataset = function (id){
 57     if (id in this.datasets) {
 58     	this.datasets[id].hide();
 59     }
 60 };
 61 
 62 /**
 63  * Hides all the datasets on the map
 64  */
 65 TimeMap.prototype.hideDatasets = function(){
 66     var tm = this;
 67 	tm.each(function(ds) {
 68 		ds.visible = false;
 69 	});
 70     tm.filter("map");
 71     tm.filter("timeline");
 72 };
 73 
 74 /**
 75  * Shows placemarks for a given dataset
 76  * 
 77  * @param {String} id   The id of the dataset to hide
 78  */
 79 TimeMap.prototype.showDataset = function(id) {
 80     if (id in this.datasets) {
 81 	    this.datasets[id].show();
 82     }
 83 };
 84 
 85 /**
 86  * Shows all the datasets on the map
 87  */
 88 TimeMap.prototype.showDatasets = function() {
 89     var tm = this;
 90 	tm.each(function(ds) {
 91 		ds.visible = true;
 92 	});
 93     tm.filter("map");
 94     tm.filter("timeline");
 95 };
 96  
 97 /**
 98  * Change the default map type
 99  *
100  * @param {String|Object} mapType   New map type If string, looks up in TimeMap.mapTypes.
101  */
102 TimeMap.prototype.changeMapType = function (mapType) {
103     var tm = this;
104     // check for no change
105     if (mapType == tm.opts.mapType) {
106         return;
107     }
108     // look for mapType
109     if (typeof(mapType) == 'string') {
110         mapType = TimeMap.mapTypes[mapType];
111     }
112     // no mapType specified
113     if (!mapType) {
114         return;
115     }
116     // change it
117     tm.opts.mapType = mapType;
118     tm.map.setMapType(mapType);
119 };
120 
121 /*----------------------------------------------------------------------------
122  * TimeMap manipulation: stuff affecting the timeline
123  *---------------------------------------------------------------------------*/
124 
125 /**
126  * Refresh the timeline, maintaining the current date
127  */
128 TimeMap.prototype.refreshTimeline = function () {
129     var topband = this.timeline.getBand(0);
130     var centerDate = topband.getCenterVisibleDate();
131     if (TimeMap.util.TimelineVersion() == "1.2") {
132         topband.getEventPainter().getLayout()._laidout = false;
133     }
134     this.timeline.layout();
135     topband.setCenterVisibleDate(centerDate);
136 };
137 
138 /**
139  * Change the intervals on the timeline.
140  *
141  * @param {String|Array} intervals   New intervals. If string, looks up in TimeMap.intervals.
142  */
143 TimeMap.prototype.changeTimeIntervals = function (intervals) {
144     var tm = this;
145     // check for no change
146     if (intervals == tm.opts.bandIntervals) {
147         return;
148     }
149     // look for intervals
150     if (typeof(intervals) == 'string') {
151         intervals = TimeMap.intervals[intervals];
152     }
153     // no intervals specified
154     if (!intervals) {
155         return;
156     }
157     tm.opts.bandIntervals = intervals;
158     // internal function - change band interval
159     function changeInterval(band, interval) {
160         band.getEther()._interval = Timeline.DateTime.gregorianUnitLengths[interval];
161         band.getEtherPainter()._unit = interval;
162     };
163     // grab date
164     var topband = tm.timeline.getBand(0),
165         centerDate = topband.getCenterVisibleDate(),
166         x;
167     // change interval for each band
168     for (x=0; x<tm.timeline.getBandCount(); x++) {
169         changeInterval(tm.timeline.getBand(x), intervals[x]);
170     }
171     // re-layout timeline
172     topband.getEventPainter().getLayout()._laidout = false;
173     tm.timeline.layout();
174     topband.setCenterVisibleDate(centerDate);
175 };
176 
177 
178 /*----------------------------------------------------------------------------
179  * TimeMapDataset manipulation: global settings, stuff affecting every item
180  *---------------------------------------------------------------------------*/
181 
182 /**
183  * Delete all items, clearing them from map and timeline
184  */
185 TimeMapDataset.prototype.clear = function() {
186     var ds = this;
187     ds.each(function(item) {
188         item.clear();
189     });
190     ds.items = [];
191     ds.timemap.timeline.layout();
192 };
193 
194 /**
195  * Delete one item, clearing it from map and timeline
196  * 
197  * @param {TimeMapItem} item      Item to delete
198  */
199 TimeMapDataset.prototype.deleteItem = function(item) {
200     var ds = this, x;
201     for (x=0; x < ds.items.length; x++) {
202         if (ds.items[x] == item) {
203             item.clear();
204             ds.items.splice(x, 1);
205             break;
206         }
207     }
208     ds.timemap.timeline.layout();
209 };
210 
211 /**
212  * Show dataset
213  */
214 TimeMapDataset.prototype.show = function() {
215     var ds = this,
216         tm = ds.timemap;
217     if (!ds.visible) {
218         ds.visible = true;
219         tm.filter("map");
220         tm.filter("timeline");
221         tm.timeline.layout();
222     }
223 };
224 
225 /**
226  * Hide dataset
227  */
228 TimeMapDataset.prototype.hide = function() {
229     var ds = this,
230         tm = ds.timemap;
231     if (ds.visible) {
232         ds.visible = false;
233         tm.filter("map");
234         tm.filter("timeline");
235     }
236 };
237 
238  /**
239  * Change the theme for every item in a dataset
240  *
241  * @param {TimeMapTheme} theme       New theme settings
242  */
243  TimeMapDataset.prototype.changeTheme = function(newTheme) {
244     var ds = this;
245     ds.opts.theme = newTheme;
246     ds.each(function(item) {
247         item.changeTheme(newTheme);
248     });
249     ds.timemap.timeline.layout();
250  };
251  
252  
253 /*----------------------------------------------------------------------------
254  * TimeMapItem manipulation: manipulate events and placemarks
255  *---------------------------------------------------------------------------*/
256 
257 /** 
258  * Show event and placemark
259  */
260 TimeMapItem.prototype.show = function() {
261     var item = this;
262     item.showEvent();
263     item.showPlacemark();
264     item.visible = true;
265 };
266 
267 /** 
268  * Hide event and placemark
269  */
270 TimeMapItem.prototype.hide = function() {
271     var item = this;
272     item.hideEvent();
273     item.hidePlacemark();
274     item.visible = false;
275 };
276 
277 /**
278  * Delete placemark from map and event from timeline
279  */
280 TimeMapItem.prototype.clear = function() {
281     var item = this,
282         i;
283     if (item.event) {
284         // this is just ridiculous
285         item.dataset.timemap.timeline.getBand(0)
286             .getEventSource()._events._events.remove(item.event);
287     }
288     if (item.placemark) {
289         item.hidePlacemark();
290         function removeOverlay(p) {
291             try {
292                 item.map.removeOverlay(p);
293             } catch(e) {}
294         };
295         if (item.getType() == "array") {
296             for (i=0; i<item.placemark.length; i++) {
297                 removeOverlay(item.placemark[i]);
298             }
299         } else {
300             removeOverlay(item.placemark);
301         }
302     }
303     item.event = item.placemark = null;
304 };
305 
306  /**
307  * Create a new event for the item.
308  * 
309  * @param {Date} s      Start date for the event
310  * @param {Date} e      (Optional) End date for the event
311  */
312 TimeMapItem.prototype.createEvent = function(s, e) {
313     var item = this,
314         theme = item.opts.theme,
315         instant = (e === undefined),
316         title = item.getTitle();
317     // create event
318     var event = new Timeline.DefaultEventSource.Event(s, e, null, null, instant, title, 
319         null, null, null, theme.eventIcon, theme.eventColor, null);
320     // add references
321     event.item = item;
322     item.event = event;
323     item.dataset.eventSource.add(event);
324 };
325  
326  /**
327  * Change the theme for an item
328  *
329  * @param {TimeMapTheme} theme   New theme settings
330  */
331  TimeMapItem.prototype.changeTheme = function(newTheme) {
332     var item = this,
333         type = item.getType(),
334         event = item.event,
335         placemark = item.placemark,
336         i;
337     item.opts.theme = newTheme;
338     // change placemark
339     if (placemark) {
340         // internal function - takes type, placemark
341         function changePlacemark(pm, type, theme) {
342             type = type || TimeMap.util.getPlacemarkType(pm);
343             switch (type) {
344                 case "marker":
345                     pm.setImage(theme.icon.image);
346                     break;
347                 case "polygon":
348                     pm.setFillStyle({
349                         'color': newTheme.fillColor,
350                         'opacity': newTheme.fillOpacity
351                     });
352                     // no break to get stroke style too
353                 case "polyline":
354                     pm.setStrokeStyle({
355                         'color': newTheme.lineColor,
356                         'weight': newTheme.lineWeight,
357                         'opacity': newTheme.lineOpacity
358                     });
359                     break;
360             }
361         };
362         if (type == 'array') {
363             for (i=0; i<placemark.length; i++) {
364                 changePlacemark(placemark[i], false, newTheme);
365             }
366         } else {
367             changePlacemark(placemark, type, newTheme);
368         }
369     }
370     // change event
371     if (event) {
372         event._color = newTheme.eventColor;
373         event._icon = newTheme.eventIcon;
374     }
375 };

376 
377 /** 
378  * Find the next or previous item chronologically
379  *
380  * @param {Boolean} [backwards=false]   Whether to look backwards (i.e. find previous) 
381  * @param {Boolean} [inDataset=false]   Whether to only look in this item's dataset
382  * @return {TimeMapItem}                Next/previous item, if any
383  */
384 TimeMapItem.prototype.getNextPrev = function(backwards, inDataset) {
385     var item = this,
386         eventSource = item.dataset.timemap.timeline.getBand(0).getEventSource(),
387         // iterator dates are non-inclusive, hence the juggle here
388         i = backwards ? 
389             eventSource.getEventReverseIterator(
390                 new Date(eventSource.getEarliestDate().getTime() - 1),
391                 item.event.getStart()) :
392             eventSource.getEventIterator(
393                 item.event.getStart(), 
394                 new Date(eventSource.getLatestDate().getTime() + 1)
395             ),
396         next = null;
397     if (!item.event) {
398         return;
399     }
400     while (next === null) {
401         if (i.hasNext()) {
402             next = i.next().item;
403             if (inDataset && next.dataset != item.dataset) {
404                 next = null;
405             }
406         } else {
407             break;
408         }
409     }
410     return next;
411 };
412 
413 /** 
414  * Find the next item chronologically
415  *
416  * @param {Boolean} [inDataset=false]   Whether to only look in this item's dataset
417  * @return {TimeMapItem}                Next item, if any
418  */
419 TimeMapItem.prototype.getNext = function(inDataset) {
420     return this.getNextPrev(false, inDataset);
421 }
422 
423 /** 
424  * Find the previous item chronologically
425  *
426  * @requires Timeline v.2.2.0 or greater
427  *
428  * @param {Boolean} [inDataset=false]   Whether to only look in this item's dataset
429  * @return {TimeMapItem}                Next item, if any
430  */
431 TimeMapItem.prototype.getPrev = function(inDataset) {
432     return this.getNextPrev(true, inDataset);
433 }
434 
435 })();