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 (function(){ 16 var window = this, 17 TimeMap = window.TimeMap, 18 TimeMapDataset = window.TimeMapDataset, 19 TimeMapItem = window.TimeMapItem, 20 util = TimeMap.util; 21 22 /*---------------------------------------------------------------------------- 23 * TimeMap manipulation: stuff affecting every dataset 24 *---------------------------------------------------------------------------*/ 25 26 // XXX: This should $.extend the prototype, I think 27 28 /** 29 * Delete all datasets, clearing them from map and timeline. Note 30 * that this is more efficient than calling clear() on each dataset. 31 */ 32 TimeMap.prototype.clear = function() { 33 var tm = this; 34 tm.eachItem(function(item) { 35 item.event = item.placemark = null; 36 }); 37 tm.map.removeAllPolylines(); 38 tm.map.removeAllMarkers(); 39 tm.eventSource.clear(); 40 tm.datasets = []; 41 }; 42 43 /** 44 * Delete one dataset, clearing it from map and timeline 45 * 46 * @param {String} id Id of dataset to delete 47 */ 48 TimeMap.prototype.deleteDataset = function(id) { 49 this.datasets[id].clear(); 50 delete this.datasets[id]; 51 }; 52 53 /** 54 * Hides placemarks for a given dataset 55 * 56 * @param {String} id The id of the dataset to hide 57 */ 58 TimeMap.prototype.hideDataset = function (id){ 59 if (id in this.datasets) { 60 this.datasets[id].hide(); 61 } 62 }; 63 64 /** 65 * Hides all the datasets on the map 66 */ 67 TimeMap.prototype.hideDatasets = function(){ 68 var tm = this; 69 tm.each(function(ds) { 70 ds.visible = false; 71 }); 72 tm.filter("map"); 73 tm.filter("timeline"); 74 }; 75 76 /** 77 * Shows placemarks for a given dataset 78 * 79 * @param {String} id The id of the dataset to hide 80 */ 81 TimeMap.prototype.showDataset = function(id) { 82 if (id in this.datasets) { 83 this.datasets[id].show(); 84 } 85 }; 86 87 /** 88 * Shows all the datasets on the map 89 */ 90 TimeMap.prototype.showDatasets = function() { 91 var tm = this; 92 tm.each(function(ds) { 93 ds.visible = true; 94 }); 95 tm.filter("map"); 96 tm.filter("timeline"); 97 }; 98 99 /** 100 * Change the default map type 101 * 102 * @param {String} mapType The maptype for the map (see {@link TimeMap.mapTypes} for options) 103 */ 104 TimeMap.prototype.changeMapType = function (mapType) { 105 var tm = this; 106 // check for no change 107 if (mapType == tm.opts.mapType) { 108 return; 109 } 110 // look for mapType 111 if (typeof(mapType) == 'string') { 112 mapType = TimeMap.mapTypes[mapType]; 113 } 114 // no mapType specified 115 if (!mapType) { 116 return; 117 } 118 // change it 119 tm.opts.mapType = mapType; 120 tm.map.setMapType(mapType); 121 }; 122 123 /*---------------------------------------------------------------------------- 124 * TimeMap manipulation: stuff affecting the timeline 125 *---------------------------------------------------------------------------*/ 126 127 /** 128 * Refresh the timeline, maintaining the current date 129 */ 130 TimeMap.prototype.refreshTimeline = function () { 131 var topband = this.timeline.getBand(0); 132 var centerDate = topband.getCenterVisibleDate(); 133 if (util.TimelineVersion() == "1.2") { 134 topband.getEventPainter().getLayout()._laidout = false; 135 } 136 this.timeline.layout(); 137 topband.setCenterVisibleDate(centerDate); 138 }; 139 140 /** 141 * Change the intervals on the timeline. 142 * 143 * @param {String|Array} intervals New intervals. If string, looks up in TimeMap.intervals. 144 */ 145 TimeMap.prototype.changeTimeIntervals = function (intervals) { 146 var tm = this; 147 // check for no change or no intervals 148 if (!intervals || intervals == tm.opts.bandIntervals) { 149 return; 150 } 151 // resolve string references if necessary 152 intervals = util.lookup(intervals, TimeMap.intervals); 153 tm.opts.bandIntervals = intervals; 154 // internal function - change band interval 155 function changeInterval(band, interval) { 156 band.getEther()._interval = Timeline.DateTime.gregorianUnitLengths[interval]; 157 band.getEtherPainter()._unit = interval; 158 } 159 // grab date 160 var topband = tm.timeline.getBand(0), 161 centerDate = topband.getCenterVisibleDate(), 162 x; 163 // change interval for each band 164 for (x=0; x<tm.timeline.getBandCount(); x++) { 165 changeInterval(tm.timeline.getBand(x), intervals[x]); 166 } 167 // re-layout timeline 168 if (topband.getEventPainter().getLayout) { 169 topband.getEventPainter().getLayout()._laidout = false; 170 } 171 tm.timeline.layout(); 172 topband.setCenterVisibleDate(centerDate); 173 }; 174 175 176 /*---------------------------------------------------------------------------- 177 * TimeMapDataset manipulation: global settings, stuff affecting every item 178 *---------------------------------------------------------------------------*/ 179 180 /** 181 * Delete all items, clearing them from map and timeline 182 */ 183 TimeMapDataset.prototype.clear = function() { 184 var ds = this; 185 ds.each(function(item) { 186 item.clear(true); 187 }); 188 ds.items = []; 189 ds.timemap.timeline.layout(); 190 }; 191 192 /** 193 * Delete one item, clearing it from map and timeline 194 * 195 * @param {TimeMapItem} item Item to delete 196 */ 197 TimeMapDataset.prototype.deleteItem = function(item) { 198 var ds = this, x; 199 for (x=0; x < ds.items.length; x++) { 200 if (ds.items[x] == item) { 201 item.clear(); 202 ds.items.splice(x, 1); 203 break; 204 } 205 } 206 }; 207 208 /** 209 * Show dataset 210 */ 211 TimeMapDataset.prototype.show = function() { 212 var ds = this, 213 tm = ds.timemap; 214 if (!ds.visible) { 215 ds.visible = true; 216 tm.filter("map"); 217 tm.filter("timeline"); 218 } 219 }; 220 221 /** 222 * Hide dataset 223 */ 224 TimeMapDataset.prototype.hide = function() { 225 var ds = this, 226 tm = ds.timemap; 227 if (ds.visible) { 228 ds.visible = false; 229 tm.filter("map"); 230 tm.filter("timeline"); 231 } 232 }; 233 234 /** 235 * Change the theme for every item in a dataset 236 * 237 * @param {TimeMapTheme|String} theme New theme, or string key in {@link TimeMap.themes} 238 */ 239 TimeMapDataset.prototype.changeTheme = function(newTheme) { 240 var ds = this; 241 newTheme = util.lookup(newTheme, TimeMap.themes); 242 ds.opts.theme = newTheme; 243 ds.each(function(item) { 244 item.changeTheme(newTheme, true); 245 }); 246 ds.timemap.timeline.layout(); 247 }; 248 249 250 /*---------------------------------------------------------------------------- 251 * TimeMapItem manipulation: manipulate events and placemarks 252 *---------------------------------------------------------------------------*/ 253 254 /** 255 * Show event and placemark 256 */ 257 TimeMapItem.prototype.show = function() { 258 var item = this; 259 item.showEvent(); 260 item.showPlacemark(); 261 item.visible = true; 262 }; 263 264 /** 265 * Hide event and placemark 266 */ 267 TimeMapItem.prototype.hide = function() { 268 var item = this; 269 item.hideEvent(); 270 item.hidePlacemark(); 271 item.visible = false; 272 }; 273 274 /** 275 * Delete placemark from map and event from timeline 276 * @param [suppressLayout] Whether to suppress laying out the timeline 277 * (e.g. for batch operations) 278 */ 279 TimeMapItem.prototype.clear = function(suppressLayout) { 280 var item = this, 281 i; 282 // remove event 283 if (item.event) { 284 // this is just ridiculous 285 item.dataset.timemap.timeline.getBand(0) 286 .getEventSource()._events._events.remove(item.event); 287 if (!suppressLayout) { 288 item.timeline.layout(); 289 } 290 } 291 // remove placemark 292 function removeOverlay(p) { 293 try { 294 if (item.getType() == 'marker') { 295 item.map.removeMarker(p); 296 } 297 else { 298 item.map.removePolyline(p); 299 } 300 } catch(e) {} 301 } 302 if (item.placemark) { 303 item.hidePlacemark(); 304 if (item.getType() == "array") { 305 item.placemark.forEach(removeOverlay); 306 } else { 307 removeOverlay(item.placemark); 308 } 309 } 310 item.event = item.placemark = null; 311 }; 312 313 /** 314 * Create a new event for the item. 315 * 316 * @param {Date} start Start date for the event 317 * @param {Date} [end] End date for the event 318 */ 319 TimeMapItem.prototype.createEvent = function(start, end) { 320 var item = this, 321 theme = item.opts.theme, 322 instant = (end === undefined), 323 title = item.getTitle(); 324 // create event 325 var event = new Timeline.DefaultEventSource.Event(start, end, null, null, instant, title, 326 null, null, null, theme.eventIcon, theme.eventColor, null); 327 // add references 328 event.item = item; 329 item.event = event; 330 item.dataset.eventSource.add(event); 331 item.timeline.layout(); 332 }; 333 334 /** 335 * Change the theme for an item 336 * 337 * @param {TimeMapTheme|String} theme New theme, or string key in {@link TimeMap.themes} 338 * @param [suppressLayout] Whether to suppress laying out the timeline 339 * (e.g. for batch operations) 340 */ 341 TimeMapItem.prototype.changeTheme = function(newTheme, suppressLayout) { 342 var item = this, 343 type = item.getType(), 344 event = item.event, 345 placemark = item.placemark, 346 i; 347 newTheme = util.lookup(newTheme, TimeMap.themes); 348 item.opts.theme = newTheme; 349 // internal function - takes type, placemark 350 function changePlacemark(pm) { 351 pm.addData(newTheme); 352 // XXX: Need to update this in Mapstraction - most implementations not available 353 pm.update(); 354 } 355 // change placemark 356 if (placemark) { 357 if (type == 'array') { 358 placemark.forEach(changePlacemark); 359 } else { 360 changePlacemark(placemark); 361 } 362 } 363 // change event 364 if (event) { 365 event._color = newTheme.eventColor; 366 event._icon = newTheme.eventIcon; 367 if (!suppressLayout) { 368 item.timeline.layout(); 369 } 370 } 371 }; 372 373 374 /** 375 * Find the next or previous item chronologically 376 * 377 * @param {Boolean} [backwards=false] Whether to look backwards (i.e. find previous) 378 * @param {Boolean} [inDataset=false] Whether to only look in this item's dataset 379 * @return {TimeMapItem} Next/previous item, if any 380 */ 381 TimeMapItem.prototype.getNextPrev = function(backwards, inDataset) { 382 var item = this, 383 eventSource = item.dataset.timemap.timeline.getBand(0).getEventSource(), 384 // iterator dates are non-inclusive, hence the juggle here 385 i = backwards ? 386 eventSource.getEventReverseIterator( 387 new Date(eventSource.getEarliestDate().getTime() - 1), 388 item.event.getStart()) : 389 eventSource.getEventIterator( 390 item.event.getStart(), 391 new Date(eventSource.getLatestDate().getTime() + 1) 392 ), 393 next = null; 394 if (!item.event) { 395 return; 396 } 397 while (next === null) { 398 if (i.hasNext()) { 399 next = i.next().item; 400 if (inDataset && next.dataset != item.dataset) { 401 next = null; 402 } 403 } else { 404 break; 405 } 406 } 407 return next; 408 }; 409 410 /** 411 * Find the next item chronologically 412 * 413 * @param {Boolean} [inDataset=false] Whether to only look in this item's dataset 414 * @return {TimeMapItem} Next item, if any 415 */ 416 TimeMapItem.prototype.getNext = function(inDataset) { 417 return this.getNextPrev(false, inDataset); 418 }; 419 420 /** 421 * Find the previous item chronologically 422 * 423 * @requires Timeline v.2.2.0 or greater 424 * 425 * @param {Boolean} [inDataset=false] Whether to only look in this item's dataset 426 * @return {TimeMapItem} Next item, if any 427 */ 428 TimeMapItem.prototype.getPrev = function(inDataset) { 429 return this.getNextPrev(true, inDataset); 430 }; 431 432 })();