Changeset 122 for oceandb/jQuery_Prototype/script
- Timestamp:
- Dec 8, 2009, 4:34:19 PM (15 years ago)
- Location:
- oceandb/jQuery_Prototype/script
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
oceandb/jQuery_Prototype/script/oceandb.js
r114 r122 1 1 var map; // Google Map2 物件 2 2 var ge; // Google Earth Plugin 物件 3 var icon; 3 var icon; // Google Map2 icon creator 4 4 var tm; // Google Map2 Timeline 5 5 … … 104 104 // 設定地圖高度 105 105 var map_height=document.documentElement.clientHeight - 68; 106 106 107 $('#mapcontainer').css( { height: map_height } ); 107 108 $('#main').css( { height: map_height } ); … … 123 124 // 註冊 window 物件的 onResize Event 124 125 // 參考: http://docs.jquery.com/Events 125 $(window).resize(function() {126 $(window).resize(function() { 126 127 // 當改變瀏覽器大小時,重新設定地圖高度 127 128 var map_height=document.documentElement.clientHeight - 68; … … 135 136 mapId: "map", // Id of map div element (required) 136 137 timelineId: "timeline", // Id of timeline div element (required) 137 datasets: [ 138 datasets: [ 138 139 { 139 id: "artists", 140 title: "Artists", 141 theme: TimeMapDataset.orangeTheme({eventIconPath: "./image"}), 142 // note that the lines below are now the preferred syntax 143 type: "basic", 140 title: "海研三號 OR3-1412", 141 theme: "red", 142 type: "kml", // KML 144 143 options: { 145 items: [ 146 { 147 "start" : "1981", 148 "end" : "2009-01-11", 149 "point" : { 150 "lat" : 23.8123, 151 "lon" : 121.123 152 }, 153 "title" : "中央氣象局", 154 "options" : { 155 // set the full HTML for the info window 156 "infoHtml": "<div class='custominfostyle'><b>中央氣象局.</div>" 157 } 158 }, 159 { 160 "start" : "1991", 161 "end" : "2010", 162 "point" : { 163 "lat" : 22.5234, 164 "lon" : 120.534 165 }, 166 "title" : "海洋科技研究中心", 167 "options" : { 168 // load HTML from another file via AJAX 169 // Note that this may break in IE if you're running it with 170 // a local file, due to cross-site scripting restrictions 171 "infoUrl": "http://www.nchc.org.tw", 172 "theme": TimeMapDataset.redTheme({eventIconPath: "./image"}) 173 } 174 }, 175 { 176 "start" : "2001", 177 "end" : "2020", 178 "point" : { 179 "lat" : 24.8345, 180 "lon" : 122.845 181 }, 182 "title" : "經濟部水利署", 183 "options" : { 184 // use the default title/description info window 185 "description": "Renaissance Man", 186 "theme": TimeMapDataset.yellowTheme({eventIconPath: "image/"}) 187 } 188 } 189 ] 144 url: "data/kml/OR3-1412.kml" // Load KML file 145 } 146 } 147 /* 148 { 149 title: "海研三號 OR3-1412", 150 theme: "green", 151 type: "progressive", 152 options: { 153 // Data to be loaded in JSON from a remote URL 154 type: "json", // JSON 155 // url with start/end placeholders 156 // url: "http://s2home.nchc.org.tw/oid/data/test.json", 157 // TestCase 158 // url: "http://www.nickrabinowitz.com/projects/timemap/progsvc.php?start=[start]&end=[end]&callback=", 159 start: "1989-01-01", 160 // lower cutoff date for data 161 dataMinDate: "2009-12-31", 162 // four months in milliseconds 163 interval: 10368000000, 164 // function to turn date into string appropriate for service 165 formatDate: function(d) { 166 return TimeMap.util.formatDate(d, 1); 190 167 } 191 } 192 ], 193 bandIntervals: [ 168 } 169 } 170 */ 171 ], 172 bandInfo: [ //上軸時間間隔與大小設定 173 { 174 width: "70%", 175 intervalUnit: Timeline.DateTime.MONTH, 176 intervalPixels: 210 177 }, 178 { //下軸時間間隔與大小設定 179 width: "30%", intervalUnit: Timeline.DateTime.YEAR, 180 intervalPixels: 150, 181 showEventText: false, 182 trackHeight: 0.2, 183 trackGap: 0.2 184 } 185 ], 186 /* 187 bandIntervals: [ 194 188 Timeline.DateTime.DECADE, 195 189 Timeline.DateTime.CENTURY 196 ]197 }); 198 // manipulate the timemap further here if you like190 191 ] */ 192 }); 199 193 200 194 if (GBrowserIsCompatible()) { -
oceandb/jQuery_Prototype/script/timemap.js
r112 r122 1 1 /*! 2 * Time MapCopyright 2008 Nick Rabinowitz.2 * Timemap.js Copyright 2008 Nick Rabinowitz. 3 3 * Licensed under the MIT License (see LICENSE.txt) 4 4 */ 5 5 6 /**--------------------------------------------------------------------------- 7 * TimeMap 8 * 6 /** 7 * @overview 8 * Timemap.js is intended to sync a SIMILE Timeline with a Google Map. 9 * Dependencies: Google Maps API v2, SIMILE Timeline v1.2 - 2.3.1 10 * Thanks to Jorn Clausen (http://www.oe-files.de) for initial concept and code. 11 * 12 * @name timemap.js 9 13 * @author Nick Rabinowitz (www.nickrabinowitz.com) 10 * The TimeMap object is intended to sync a SIMILE Timeline with a Google Map. 11 * Dependencies: Google Maps API v2, SIMILE Timeline v1.2 or v2.2.0 12 * Thanks to Jörn Clausen (http://www.oe-files.de) for initial concept and code. 13 *---------------------------------------------------------------------------*/ 14 * @version 1.6pre 15 */ 14 16 15 17 // globals - for JSLint 16 /*global GBrowserIsCompatible, GLargeMapControl, GLatLngBounds, GMap2 */ 17 /*global GMapTypeControl, GDownloadUrl, GEvent, GGroundOverlay, GIcon */ 18 /*global GMarker, GPolygon, GPolyline, GSize, GLatLng, G_DEFAULT_ICON */ 19 /*global G_DEFAULT_MAP_TYPES, G_NORMAL_MAP, G_PHYSICAL_MAP, G_HYBRID_MAP */ 20 /*global G_MOON_VISIBLE_MAP, G_SKY_VISIBLE_MAP, G_SATELLITE_MAP, Timeline */ 21 22 // A couple of aliases to save a few bytes 23 24 25 var DT = Timeline.DateTime, 26 // Google icon path 27 GIP = "http://www.google.com/intl/en_us/mapfiles/ms/icons/"; 18 /*global GBrowserIsCompatible, GLargeMapControl, GMap2, GIcon */ 19 /*global GMapTypeControl, GDownloadUrl, GGroundOverlay */ 20 /*global GMarker, GPolygon, GPolyline, GSize, G_DEFAULT_ICON */ 21 /*global G_HYBRID_MAP, G_MOON_VISIBLE_MAP, G_SKY_VISIBLE_MAP */ 22 23 (function(){ 24 25 // borrowing some space-saving devices from jquery 26 var 27 // Will speed up references to window, and allows munging its name. 28 window = this, 29 // Will speed up references to undefined, and allows munging its name. 30 undefined, 31 // aliases for Timeline objects 32 Timeline = window.Timeline, DateTime = Timeline.DateTime, 33 // aliases for Google variables (anything that gets used more than once) 34 G_DEFAULT_MAP_TYPES = window.G_DEFAULT_MAP_TYPES, 35 G_NORMAL_MAP = window.G_NORMAL_MAP, 36 G_PHYSICAL_MAP = window.G_PHYSICAL_MAP, 37 G_SATELLITE_MAP = window.G_SATELLITE_MAP, 38 GLatLng = window.GLatLng, 39 GLatLngBounds = window.GLatLngBounds, 40 GEvent = window.GEvent, 41 // Google icon path 42 GIP = "http://www.google.com/intl/en_us/mapfiles/ms/icons/", 43 // aliases for class names, allowing munging 44 TimeMap, TimeMapDataset, TimeMapTheme, TimeMapItem; 28 45 29 46 /*---------------------------------------------------------------------------- 30 * TimeMap Class - holds references to timeline, map, and datasets47 * TimeMap Class 31 48 *---------------------------------------------------------------------------*/ 32 49 33 50 /** 34 * Creates a new TimeMap with map placemarks synched to timeline events 51 * @class 52 * The TimeMap object holds references to timeline, map, and datasets. 35 53 * This will create the visible map, but not the timeline, which must be initialized separately. 36 54 * … … 38 56 * @param {element} tElement The timeline element. 39 57 * @param {element} mElement The map element. 40 * @param {Object} options A container for optional arguments:58 * @param {Object} [options] A container for optional arguments:<pre> 41 59 * {Boolean} syncBands Whether to synchronize all bands in timeline 42 60 * {GLatLng} mapCenter Point for map center … … 45 63 * {Array} mapTypes The set of maptypes available for the map 46 64 * {Function/String} mapFilter How to hide/show map items depending on timeline state; 47 options: "hidePastFuture", "showMomentOnly" 65 options: "hidePastFuture", "showMomentOnly", or function 48 66 * {Boolean} showMapTypeCtrl Whether to display the map type control 49 67 * {Boolean} showMapCtrl Whether to show map navigation control … … 51 69 * {Function} openInfoWindow Function redefining how info window opens 52 70 * {Function} closeInfoWindow Function redefining how info window closes 53 */ 54 function TimeMap(tElement, mElement, options) { 55 // save elements 71 * </pre> 72 */ 73 TimeMap = function(tElement, mElement, options) { 74 75 // save DOM elements 76 /** 77 * Map element 78 * @type DOM Element 79 */ 56 80 this.mElement = mElement; 81 /** 82 * Timeline element 83 * @type DOM Element 84 */ 57 85 this.tElement = tElement; 58 // initialize array of datasets 86 87 /** 88 * Map of datasets 89 * @type Object 90 */ 59 91 this.datasets = {}; 60 // initialize filters 61 this.filters = {}; 62 // initialize map bounds 92 /** 93 * Filter chains for this timemap 94 * @type Object 95 */ 96 this.chains = {}; 97 /** 98 * Bounds of the map 99 * @type GLatLngBounds 100 */ 63 101 this.mapBounds = new GLatLngBounds(); 64 102 65 103 // set defaults for options 66 // other options can be set directly on the map or timeline 67 this.opts = options || {}; // make sure the options object isn't null 104 var defaults = { 105 mapCenter: new GLatLng(0,0), 106 mapZoom: 0, 107 mapType: G_PHYSICAL_MAP, 108 mapTypes: [G_NORMAL_MAP, G_SATELLITE_MAP, G_PHYSICAL_MAP], 109 showMapTypeCtrl: true, 110 showMapCtrl: true, 111 syncBands: true, 112 mapFilter: 'hidePastFuture', 113 centerOnItems: true, 114 theme: 'red' 115 }; 116 117 /** 118 * Container for optional settings passed in the "options" parameter 119 * @type Object 120 */ 121 this.opts = options = util.merge(options, defaults); 122 123 // only these options will cascade to datasets and items 124 options.mergeOnly = ['mergeOnly', 'theme', 'eventIconPath', 'openInfoWindow', 125 'closeInfoWindow', 'noPlacemarkLoad', 'noEventLoad'] 126 68 127 // allow map types to be specified by key 69 if (typeof(options.mapType) == 'string') { 70 options.mapType = TimeMap.mapTypes[options.mapType]; 71 } 128 options.mapType = util.lookup(options.mapType, TimeMap.mapTypes); 72 129 // allow map filters to be specified by key 73 if (typeof(options.mapFilter) == 'string') { 74 options.mapFilter = TimeMap.filters[options.mapFilter]; 75 } 76 // these options only needed for map initialization 77 var mapCenter = options.mapCenter || new GLatLng(0,0), 78 mapZoom = options.mapZoom || 0, 79 mapType = options.mapType || G_PHYSICAL_MAP, 80 mapTypes = options.mapTypes || [G_NORMAL_MAP, G_SATELLITE_MAP, G_PHYSICAL_MAP], 81 showMapTypeCtrl = ('showMapTypeCtrl' in options) ? options.showMapTypeCtrl : true, 82 showMapCtrl = ('showMapCtrl' in options) ? options.showMapCtrl : true; 83 84 // these options need to be saved for later 85 this.opts.syncBands = ('syncBands' in options) ? options.syncBands : true; 86 this.opts.mapFilter = options.mapFilter || TimeMap.filters.hidePastFuture; 87 this.opts.centerOnItems = ('centerMapOnItems' in options) ? options.centerMapOnItems : true; 130 options.mapFilter = util.lookup(options.mapFilter, TimeMap.filters); 131 // allow theme options to be specified in options 132 options.theme = TimeMapTheme.create(options.theme, options); 88 133 89 134 // initialize map 135 this.initMap(); 136 }; 137 138 /** 139 * Initialize the map. 140 */ 141 TimeMap.prototype.initMap = function() { 142 var options = this.opts, map, i; 90 143 if (GBrowserIsCompatible()) { 91 var map = this.map = new GMap2(this.mElement); 92 if (showMapCtrl) { 144 145 /** 146 * The associated GMap object 147 * @type GMap2 148 */ 149 this.map = map = new GMap2(this.mElement); 150 151 // set controls 152 if (options.showMapCtrl) { 93 153 map.addControl(new GLargeMapControl()); 94 154 } 95 if ( showMapTypeCtrl) {155 if (options.showMapTypeCtrl) { 96 156 map.addControl(new GMapTypeControl()); 97 157 } 158 98 159 // drop all existing types 99 var i;100 160 for (i=G_DEFAULT_MAP_TYPES.length-1; i>0; i--) { 101 161 map.removeMapType(G_DEFAULT_MAP_TYPES[i]); 102 162 } 103 163 // you can't remove the last maptype, so add a new one first 104 map.addMapType( mapTypes[0]);164 map.addMapType(options.mapTypes[0]); 105 165 map.removeMapType(G_DEFAULT_MAP_TYPES[0]); 106 166 // add the rest of the new types 107 for (i=1; i<mapTypes.length; i++) { 108 map.addMapType(mapTypes[i]); 109 } 167 for (i=1; i<options.mapTypes.length; i++) { 168 map.addMapType(options.mapTypes[i]); 169 } 170 // set basic parameters 110 171 map.enableDoubleClickZoom(); 111 172 map.enableScrollWheelZoom(); 112 173 map.enableContinuousZoom(); 113 174 // initialize map center and zoom 114 map.setCenter( mapCenter,mapZoom);175 map.setCenter(options.mapCenter, options.mapZoom); 115 176 // must be called after setCenter, for reasons unclear 116 map.setMapType( mapType);117 } 118 } 177 map.setMapType(options.mapType); 178 } 179 }; 119 180 120 181 /** 121 182 * Current library version. 122 */ 123 TimeMap.version = "1.5pre"; 183 * @type String 184 */ 185 TimeMap.version = "1.6pre"; 186 187 /** 188 * @name TimeMap.util 189 * @namespace 190 * Namespace for TimeMap utility functions. 191 */ 192 var util = TimeMap.util = {}; 124 193 125 194 /** 126 195 * Intializes a TimeMap. 127 196 * 128 * This is an attempt to create a general initialization script that will197 * <p>This is an attempt to create a general initialization script that will 129 198 * work in most cases. If you need a more complex initialization, write your 130 * own script instead of using this one. 131 * 132 * The idea here is to throw all of the standard intialization settings into199 * own script instead of using this one.</p> 200 * 201 * <p>The idea here is to throw all of the standard intialization settings into 133 202 * a large object and then pass it to the TimeMap.init() function. The full 134 203 * data format is outlined below, but if you leave elements off the script 135 * will use default settings instead. 136 * 137 * Call TimeMap.init() inside of an onLoad() function (or a jQuery204 * will use default settings instead.</p> 205 * 206 * <p>Call TimeMap.init() inside of an onLoad() function (or a jQuery 138 207 * $.(document).ready() function, or whatever you prefer). See the examples 139 * for usage. 208 * for usage.</p> 140 209 * 141 210 * @param {Object} config Full set of configuration options. 142 211 * See examples/timemapinit_usage.js for format. 212 * @return {TimeMap} The initialized TimeMap object, for future reference 143 213 */ 144 214 TimeMap.init = function(config) { 145 215 146 216 // check required elements 217 var err = "TimeMap.init: No id for "; 147 218 if (!('mapId' in config) || !config.mapId) { 148 throw "TimeMap.init: No id formap";219 throw err + "map"; 149 220 } 150 221 if (!('timelineId' in config) || !config.timelineId) { 151 throw "TimeMap.init: No id fortimeline";222 throw err + "timeline"; 152 223 } 153 224 154 225 // set defaults 155 config = config || {}; // make sure the config object isn't null 156 config.options = config.options || {}; 157 config.datasets = config.datasets || []; 158 config.bandInfo = config.bandInfo || false; 159 config.scrollTo = config.scrollTo || "earliest"; 226 var defaults = { 227 options: {}, 228 datasets: [], 229 bands: false, 230 bandInfo: false, 231 bandIntervals: "wk", 232 scrollTo: "earliest" 233 }; 234 // merge options and defaults 235 config = util.merge(config, defaults); 236 160 237 if (!config.bandInfo && !config.bands) { 161 var intervals = config.bandIntervals ||162 config.options.bandIntervals ||163 [DT.WEEK, DT.MONTH];164 238 // allow intervals to be specified by key 165 if (typeof(intervals) == 'string') { 166 intervals = TimeMap.intervals[intervals]; 167 } 168 // save for later reference 169 config.options.bandIntervals = intervals; 239 var intervals = util.lookup(config.bandIntervals, TimeMap.intervals); 170 240 // make default band info 171 241 config.bandInfo = [ … … 180 250 intervalPixels: 100, 181 251 showEventText: false, 182 overview: true,252 overview: true, 183 253 trackHeight: 0.4, 184 254 trackGap: 0.2 … … 194 264 195 265 // create the dataset objects 196 var datasets = [], x, ds, dsOptions, dsId;266 var datasets = [], x, ds, dsOptions, topOptions, dsId; 197 267 for (x=0; x < config.datasets.length; x++) { 198 268 ds = config.datasets[x]; 199 dsOptions = ds.options || {}; 200 dsOptions.title = ds.title || ''; 201 dsOptions.theme = ds.theme; 202 dsOptions.dateParser = ds.dateParser; 269 // put top-level data into options 270 topOptions = { 271 title: ds.title, 272 theme: ds.theme, 273 dateParser: ds.dateParser 274 }; 275 dsOptions = util.merge(ds.options, topOptions); 203 276 dsId = ds.id || "ds" + x; 204 277 datasets[x] = tm.createDataset(dsId, dsOptions); … … 239 312 } 240 313 bands[x] = Timeline.createBandInfo(bandInfo); 241 if (x > 0 && TimeMap.TimelineVersion() == "1.2") {314 if (x > 0 && util.TimelineVersion() == "1.2") { 242 315 // set all to the same layout 243 316 bands[x].eventPainter.setLayout(bands[0].eventPainter.getLayout()); … … 257 330 var data = config.datasets[x], options, type, callback, loaderClass, loader; 258 331 // support some older syntax 259 options = data. options || data.data|| {};332 options = data.data || data.options || {}; 260 333 type = data.type || options.type; 261 callback = function() { loadManager.increment() };334 callback = function() { loadManager.increment(); }; 262 335 // get loader class 263 336 loaderClass = (typeof(type) == 'string') ? TimeMap.loaders[type] : type; … … 275 348 276 349 /** 277 * Load manager - static singleton for managing multiple asynchronous loads350 * @class Static singleton for managing multiple asynchronous loads 278 351 */ 279 352 TimeMap.loadManager = new function() { … … 284 357 * @param {TimeMap} tm TimeMap instance 285 358 * @param {int} target Number of datasets we're loading 286 * @param {Object} options Container for optional functions 359 * @param {Object} options Container for optional settings:<pre> 360 * {Function} dataLoadedFunction Custom function replacing default completion function; 361 * should take one parameter, the TimeMap object 362 * {String/Date} scrollTo Where to scroll the timeline when load is complete 363 * Options: "earliest", "latest", "now", date string, Date 364 * {Function} dataDisplayedFunction Custom function to fire once data is loaded and displayed; 365 * should take one parameter, the TimeMap object 366 * </pre> 287 367 */ 288 368 this.init = function(tm, target, config) { … … 304 384 305 385 /** 306 * Function to fire when all loads are complete 386 * Function to fire when all loads are complete. 387 * Default behavior is to scroll to a given date (if provided) and 388 * layout the timeline. 307 389 */ 308 390 this.complete = function() { 391 var tm = this.tm; 309 392 // custom function including timeline scrolling and layout 310 393 var func = this.opts.dataLoadedFunction; … … 332 415 } 333 416 // either the parse worked, or it was a date to begin with 334 if (scrollTo.constructor == Date) d = scrollTo; 417 if (scrollTo.constructor == Date) { 418 d = scrollTo; 419 } 335 420 } 336 t his.tm.timeline.getBand(0).setCenterVisibleDate(d);337 } 338 t his.tm.timeline.layout();421 tm.timeline.getBand(0).setCenterVisibleDate(d); 422 } 423 tm.timeline.layout(); 339 424 // custom function to be called when data is loaded 340 425 func = this.opts.dataDisplayedFunction; … … 347 432 348 433 /** 349 * Map of different data loader functions. 350 * New loaders should add their loader function to this map; loader 434 * @namespace 435 * Namespace for different data loader functions. 436 * New loaders should add their factories or constructors to this object; loader 351 437 * functions are passed an object with parameters in TimeMap.init(). 352 438 */ … … 354 440 355 441 /** 442 * @class 356 443 * Basic loader class, for pre-loaded data. 357 444 * Other types of loaders should take the same parameter. 358 445 * 359 * @param {Object} options All options for the loader: 446 * @constructor 447 * @param {Object} options All options for the loader:<pre> 360 448 * {Array} data Array of items to load 361 449 * {Function} preloadFunction Function to call on data before loading 362 450 * {Function} transformFunction Function to call on individual items before loading 451 * </pre> 363 452 */ 364 453 TimeMap.loaders.basic = function(options) { 365 // get standard functions 454 // get standard functions and document 366 455 TimeMap.loaders.mixin(this, options); 367 // allow "value" for backwards compatibility 368 this.data = options.items || options.value || []; 369 } 456 /** 457 * Function to call on data object before loading 458 * @name TimeMap.loaders.basic#preload 459 * @function 460 * @parameter {Object} data Data to preload 461 * @return {Object[]} data Array of item data 462 */ 463 464 /** 465 * Function to call on a single item data object before loading 466 * @name TimeMap.loaders.basic#transform 467 * @function 468 * @parameter {Object} data Data to transform 469 * @return {Object} data Transformed data for one item 470 */ 471 472 /** 473 * Array of item data to load. 474 * @type Object[] 475 */ 476 this.data = options.items || 477 // allow "value" for backwards compatibility 478 options.value || []; 479 }; 370 480 371 481 /** … … 382 492 // run callback 383 493 callback(); 384 } 385 386 /** 494 }; 495 496 /** 497 * @class 387 498 * Generic class for loading remote data with a custom parser function 388 499 * 389 * @param {Object} options All options for the loader: 500 * @constructor 501 * @param {Object} options All options for the loader:<pre> 390 502 * {Array} url URL of file to load (NB: must be local address) 391 * {Function} parserFunction Parser function to turn data intoJavaScript array503 * {Function} parserFunction Parser function to turn a string into a JavaScript array 392 504 * {Function} preloadFunction Function to call on data before loading 393 505 * {Function} transformFunction Function to call on individual items before loading 506 * </pre> 394 507 */ 395 508 TimeMap.loaders.remote = function(options) { 396 // get standard functions 509 // get standard functions and document 397 510 TimeMap.loaders.mixin(this, options); 398 // get URL to load 511 /** 512 * Parser function to turn a string into a JavaScript array 513 * @name TimeMap.loaders.remote#parse 514 * @function 515 * @parameter {String} s String to parse 516 * @return {Array} data Array of item data 517 */ 518 519 /** 520 * Function to call on data object before loading 521 * @name TimeMap.loaders.remote#preload 522 * @function 523 * @parameter {Object} data Data to preload 524 * @return {Object[]} data Array of item data 525 */ 526 527 /** 528 * Function to call on a single item data object before loading 529 * @name TimeMap.loaders.remote#transform 530 * @function 531 * @parameter {Object} data Data to transform 532 * @return {Object} data Transformed data for one item 533 */ 534 535 /** 536 * URL to load 537 * @type String 538 */ 399 539 this.url = options.url; 400 } 401 402 /** 403 * KMLload function.540 }; 541 542 /** 543 * Remote load function. 404 544 * 405 545 * @param {TimeMapDataset} dataset Dataset to load data into … … 408 548 TimeMap.loaders.remote.prototype.load = function(dataset, callback) { 409 549 var loader = this; 550 551 // XXX: It would be nice to save the callback function here, 552 // and be able to cancel it (or cancel all) if needed 553 410 554 // get items 411 555 GDownloadUrl(this.url, function(result) { … … 418 562 callback(); 419 563 }); 420 } 564 }; 421 565 422 566 /** … … 424 568 * 425 569 * @param {Function} loader Loader to add functions to 426 * @param {Object} options Options for the loader: 570 * @param {Object} options Options for the loader:<pre> 427 571 * {Function} parserFunction Parser function to turn data into JavaScript array 428 572 * {Function} preloadFunction Function to call on data before loading 429 573 * {Function} transformFunction Function to call on individual items before loading 574 * </pre> 430 575 */ 431 576 TimeMap.loaders.mixin = function(loader, options) { … … 435 580 loader.preload = options.preloadFunction || dummy; 436 581 loader.transform = options.transformFunction || dummy; 437 }438 439 /**440 * Map of common timeline intervals. Add custom intervals here if you441 * want to refer to them by key rather than as literals.442 */443 TimeMap.intervals = {444 'sec': [DT.SECOND, DT.MINUTE],445 'min': [DT.MINUTE, DT.HOUR],446 'hr': [DT.HOUR, DT.DAY],447 'day': [DT.DAY, DT.WEEK],448 'wk': [DT.WEEK, DT.MONTH],449 'mon': [DT.MONTH, DT.YEAR],450 'yr': [DT.YEAR, DT.DECADE],451 'dec': [DT.DECADE, DT.CENTURY]452 };453 454 /**455 * Map of Google map types. Using keys rather than literals allows456 * for serialization of the map type.457 */458 TimeMap.mapTypes = {459 'normal':G_NORMAL_MAP,460 'satellite':G_SATELLITE_MAP,461 'hybrid':G_HYBRID_MAP,462 'physical':G_PHYSICAL_MAP,463 'moon':G_MOON_VISIBLE_MAP,464 'sky':G_SKY_VISIBLE_MAP465 582 }; 466 583 … … 473 590 */ 474 591 TimeMap.prototype.createDataset = function(id, options) { 475 options = options || {}; // make sure the options object isn't null476 if (!("title" in options)) {477 options.title = id;478 }479 592 var dataset = new TimeMapDataset(this, options); 480 593 this.datasets[id] = dataset; 481 594 // add event listener 482 595 if (this.opts.centerOnItems) { 483 var tm = this;596 var map = this.map, bounds = this.mapBounds; 484 597 GEvent.addListener(dataset, 'itemsloaded', function() { 485 var map = tm.map, bounds = tm.mapBounds;486 // determine the zoom level from the bounds487 map.setZoom(map.getBoundsZoomLevel(bounds));488 // determine the center from the bounds489 map.setCenter(bounds.getCenter());598 // determine the center and zoom level from the bounds 599 map.setCenter( 600 bounds.getCenter(), 601 map.getBoundsZoomLevel(bounds) 602 ); 490 603 }); 491 604 } … … 497 610 * iteration method, as it allows for future iterator options. 498 611 * 499 * @param {Function} f The function to run 612 * @param {Function} f The function to run, taking one dataset as an argument 500 613 */ 501 614 TimeMap.prototype.each = function(f) { … … 508 621 509 622 /** 623 * Run a function on each item in each dataset in the timemap. 624 * 625 * @param {Function} f The function to run, taking one item as an argument 626 */ 627 TimeMap.prototype.eachItem = function(f) { 628 this.each(function(ds) { 629 ds.each(function(item) { 630 f(item); 631 }); 632 }); 633 }; 634 635 /** 636 * Get all items from all datasets. 637 * 638 * @return {TimeMapItem[]} Array of all items 639 */ 640 TimeMap.prototype.getItems = function(index) { 641 var items = []; 642 this.eachItem(function(item) { 643 items.push(item); 644 }); 645 return items; 646 }; 647 648 /** 510 649 * Initialize the timeline - this must happen separately to allow full control of 511 650 * timeline properties. … … 523 662 } 524 663 525 // initialize timeline 664 /** 665 * The associated timeline object 666 * @type Timeline 667 */ 526 668 this.timeline = Timeline.create(this.tElement, bands); 527 669 … … 563 705 // filter chain for timeline events 564 706 this.addFilterChain("timeline", 707 // on 565 708 function(item) { 566 709 item.showEvent(); 567 710 }, 711 // off 568 712 function(item) { 569 713 item.hideEvent(); 714 }, 715 // pre 716 null, 717 // post 718 function(tm) { 719 tm.eventSource._events._index(); 720 tm.timeline.layout(); 570 721 } 571 722 ); … … 599 750 */ 600 751 TimeMap.prototype.filter = function(fid) { 601 var filter s = this.filters[fid];752 var filterChain = this.chains[fid], chain; 602 753 // if no filters exist, forget it 603 if (!filter s || !filters.chain || filters.chain.length === 0) {754 if (!filterChain) { 604 755 return; 756 } 757 chain = filterChain.chain; 758 if (!chain || chain.length === 0) { 759 return; 760 } 761 // pre-filter function 762 if (filterChain.pre) { 763 filterChain.pre(this); 605 764 } 606 765 // run items through filter 607 766 this.each(function(ds) { 608 767 ds.each(function(item) { 609 F_LOOP: { 610 for (var i = filters.chain.length - 1; i >= 0; i--) { 611 if (!filters.chain[i](item)) { 768 var done = false; 769 F_LOOP: while (!done) { 770 for (var i = chain.length - 1; i >= 0; i--) { 771 if (!chain[i](item)) { 612 772 // false condition 613 filter s.off(item);773 filterChain.off(item); 614 774 break F_LOOP; 615 775 } 616 776 } 617 777 // true condition 618 filters.on(item); 778 filterChain.on(item); 779 done = true; 619 780 } 620 781 }); 621 782 }); 783 // post-filter function 784 if (filterChain.post) { 785 filterChain.post(this); 786 } 622 787 }; 623 788 … … 628 793 * @param {Function} fon Function to run on an item if filter is true 629 794 * @param {Function} foff Function to run on an item if filter is false 630 */ 631 TimeMap.prototype.addFilterChain = function(fid, fon, foff) { 632 this.filters[fid] = { 795 * @param {Function} [pre] Function to run before the filter runs 796 * @param {Function} [post] Function to run after the filter runs 797 */ 798 TimeMap.prototype.addFilterChain = function(fid, fon, foff, pre, post) { 799 this.chains[fid] = { 633 800 chain:[], 634 801 on: fon, 635 off: foff 802 off: foff, 803 pre: pre, 804 post: post 636 805 }; 637 806 }; … … 642 811 * @param {String} fid Id of the filter chain 643 812 */ 644 TimeMap.prototype.removeFilterChain = function(fid , on, off) {645 this. filters[fid] = null;813 TimeMap.prototype.removeFilterChain = function(fid) { 814 this.chains[fid] = null; 646 815 }; 647 816 … … 653 822 */ 654 823 TimeMap.prototype.addFilter = function(fid, f) { 655 if (this.filters[fid] && this.filters[fid].chain) { 656 this.filters[fid].chain.push(f); 824 var filterChain = this.chains[fid]; 825 if (filterChain && filterChain.chain) { 826 filterChain.chain.push(f); 657 827 } 658 828 }; … … 662 832 * 663 833 * @param {String} fid Id of the filter chain 664 * XXX: Support index here 665 */ 666 TimeMap.prototype.removeFilter = function(fid) { 667 if (this.filters[fid] && this.filters[fid].chain) { 668 this.filters[fid].chain.pop(); 669 } 670 }; 671 672 /** 673 * Map of different filter functions. Adding new filters to this 674 * map allows them to be specified by string name. 834 * @param {Function} [f] The function to remove 835 */ 836 TimeMap.prototype.removeFilter = function(fid, f) { 837 var filterChain = this.chains[fid]; 838 if (filterChain && filterChain.chain) { 839 var chain = filterChain.chain; 840 if (!f) { 841 // just remove the last filter added 842 chain.pop(); 843 } 844 else { 845 // look for the specific filter to remove 846 for(var i = 0; i < chain.length; i++){ 847 if(chain[i] == f){ 848 chain.splice(i, 1); 849 } 850 } 851 } 852 } 853 }; 854 855 /** 856 * @namespace 857 * Namespace for different filter functions. Adding new filters to this 858 * object allows them to be specified by string name. 675 859 */ 676 860 TimeMap.filters = {}; 677 861 678 862 /** 679 * Static filter function: Hide items not shown on the timeline863 * Static filter function: Hide items not in the visible area of the timeline. 680 864 * 681 865 * @param {TimeMapItem} item Item to test for filter … … 683 867 */ 684 868 TimeMap.filters.hidePastFuture = function(item) { 685 var topband = item.dataset.timemap.timeline.getBand(0) ;686 var maxVisibleDate = topband.getMaxVisibleDate().getTime();687 varminVisibleDate = topband.getMinVisibleDate().getTime();688 if (item.event !== null) {689 var itemStart = item.event.getStart().getTime() ;690 varitemEnd = item.event.getEnd().getTime();869 var topband = item.dataset.timemap.timeline.getBand(0), 870 maxVisibleDate = topband.getMaxVisibleDate().getTime(), 871 minVisibleDate = topband.getMinVisibleDate().getTime(); 872 if (item.event) { 873 var itemStart = item.event.getStart().getTime(), 874 itemEnd = item.event.getEnd().getTime(); 691 875 // hide items in the future 692 876 if (itemStart > maxVisibleDate) { … … 703 887 704 888 /** 705 * Static filter function: Hide items not shown on the timeline 889 * Static filter function: Hide items not present at the exact 890 * center date of the timeline (will only work for duration events). 706 891 * 707 892 * @param {TimeMapItem} item Item to test for filter … … 709 894 */ 710 895 TimeMap.filters.showMomentOnly = function(item) { 711 var topband = item.dataset.timemap.timeline.getBand(0) ;712 varmomentDate = topband.getCenterVisibleDate().getTime();713 if (item.event !== null) {714 var itemStart = item.event.getStart().getTime() ;715 varitemEnd = item.event.getEnd().getTime();896 var topband = item.dataset.timemap.timeline.getBand(0), 897 momentDate = topband.getCenterVisibleDate().getTime(); 898 if (item.event) { 899 var itemStart = item.event.getStart().getTime(), 900 itemEnd = item.event.getEnd().getTime(); 716 901 // hide items in the future 717 902 if (itemStart > momentDate) { … … 728 913 729 914 /*---------------------------------------------------------------------------- 730 * TimeMapDataset Class - holds references to items and visual themes915 * TimeMapDataset Class 731 916 *---------------------------------------------------------------------------*/ 732 917 733 918 /** 734 * Create a new TimeMap dataset to hold a set of items 919 * @class 920 * The TimeMapDataset object holds an array of items and dataset-level 921 * options and settings, including visual themes. 735 922 * 736 923 * @constructor 737 924 * @param {TimeMap} timemap Reference to the timemap object 738 * @param {Object} options Object holding optional arguments: 925 * @param {Object} [options] Object holding optional arguments:<pre> 926 * {String} id Key for this dataset in the datasets map 739 927 * {String} title Title of the dataset (for the legend) 740 928 * {String or theme object} theme Theme settings. … … 742 930 * {Function} openInfoWindow Function redefining how info window opens 743 931 * {Function} closeInfoWindow Function redefining how info window closes 744 */ 745 function TimeMapDataset(timemap, options) { 746 // hold reference to timemap 932 * </pre> 933 */ 934 TimeMapDataset = function(timemap, options) { 935 936 /** 937 * Reference to parent TimeMap 938 * @type TimeMap 939 */ 747 940 this.timemap = timemap; 748 // initialize timeline event source 941 /** 942 * EventSource for timeline events 943 * @type Timeline.EventSource 944 */ 749 945 this.eventSource = new Timeline.DefaultEventSource(); 750 // initialize array of items 946 /** 947 * Array of child TimeMapItems 948 * @type Array 949 */ 751 950 this.items = []; 752 // for show/hide functions 951 /** 952 * Whether the dataset is visible 953 * @type Boolean 954 */ 753 955 this.visible = true; 754 956 755 957 // set defaults for options 756 this.opts = options || {}; // make sure the options object isn't null 757 this.opts.title = options.title || ""; 758 759 // get theme by key or object 760 if (typeof(options.theme) == "string") { 761 options.theme = TimeMapDataset.themes[options.theme]; 762 } 763 this.opts.theme = options.theme || this.timemap.opts.theme || new TimeMapDatasetTheme({}); 764 // allow icon path override in options or timemap options 765 this.opts.theme.eventIconPath = options.eventIconPath || 766 this.timemap.opts.eventIconPath || this.opts.theme.eventIconPath; 767 this.opts.theme.eventIcon = options.eventIconPath + this.opts.theme.eventIconImage; 768 769 // allow for other data parsers (e.g. Gregorgian) by key or function 770 if (typeof(options.dateParser) == "string") { 771 options.dateParser = TimeMapDataset.dateParsers[options.dateParser]; 772 } 773 this.opts.dateParser = options.dateParser || TimeMapDataset.hybridParser; 774 775 // get functions 776 this.getItems = function() { return this.items; }; 958 var defaults = { 959 title: 'Untitled', 960 dateParser: TimeMapDataset.hybridParser 961 }; 962 963 /** 964 * Container for optional settings passed in the "options" parameter 965 * @type Object 966 */ 967 this.opts = options = util.merge(options, defaults, timemap.opts); 968 969 // allow date parser to be specified by key 970 options.dateParser = util.lookup(options.dateParser, TimeMap.dateParsers); 971 // allow theme options to be specified in options 972 options.theme = TimeMapTheme.create(options.theme, options); 973 974 /** 975 * Return an array of this dataset's items 976 * 977 * @param {int} [index] Index of single item to return 978 * @return {TimeMapItem[]} Single item, or array of all items if no index was supplied 979 */ 980 this.getItems = function(index) { 981 if (index !== undefined) { 982 if (index < this.items.length) { 983 return this.items[index]; 984 } 985 else { 986 return null; 987 } 988 } 989 return this.items; 990 }; 991 992 /** 993 * Return the title of the dataset 994 * 995 * @return {String} Dataset title 996 */ 777 997 this.getTitle = function() { return this.opts.title; }; 778 } 779 780 /** 781 * Wrapper to fix Timeline Gregorian parser for invalid strings 998 }; 999 1000 /** 1001 * Better Timeline Gregorian parser... shouldn't be necessary :(. 1002 * Gregorian dates are years with "BC" or "AD" 782 1003 * 783 1004 * @param {String} s String to parse into a Date object … … 785 1006 */ 786 1007 TimeMapDataset.gregorianParser = function(s) { 787 d = DT.parseGregorianDateTime(s); 788 // check for invalid dates 789 if (!d.getFullYear()) d = null; 790 return d; 791 }; 792 793 /** 794 * Parse dates with the ISO 8601 parser, then fall back on the Gregorian 795 * parser if the first parse fails 1008 if (!s) { 1009 return null; 1010 } else if (s instanceof Date) { 1011 return s; 1012 } 1013 // look for BC 1014 var bc = Boolean(s.match(/b\.?c\.?/i)); 1015 // parse - parseInt will stop at non-number characters 1016 var year = parseInt(s, 10); 1017 // look for success 1018 if (!isNaN(year)) { 1019 // deal with BC 1020 if (bc) { 1021 year = 1 - year; 1022 } 1023 // make Date and return 1024 var d = new Date(0); 1025 d.setUTCFullYear(year); 1026 return d; 1027 } 1028 else { 1029 return null; 1030 } 1031 }; 1032 1033 /** 1034 * Parse date strings with a series of date parser functions, until one works. 1035 * In order: 1036 * <ol> 1037 * <li>Date.parse() (so Date.js should work here, if it works with Timeline...)</li> 1038 * <li>Gregorian parser</li> 1039 * <li>The Timeline ISO 8601 parser</li> 1040 * </ol> 796 1041 * 797 1042 * @param {String} s String to parse into a Date object … … 799 1044 */ 800 1045 TimeMapDataset.hybridParser = function(s) { 801 var d = DT.parseIso8601DateTime(s); 802 if (!d) { 803 d = TimeMapDataset.gregorianParser(s); 804 } 1046 // try native date parse 1047 var d = new Date(Date.parse(s)); 1048 if (isNaN(d)) { 1049 if (typeof(s) == "string") { 1050 // look for Gregorian dates 1051 if (s.match(/^-?\d{1,6} ?(a\.?d\.?|b\.?c\.?e?\.?|c\.?e\.?)?$/i)) { 1052 d = TimeMapDataset.gregorianParser(s); 1053 } 1054 // try ISO 8601 parse 1055 else { 1056 try { 1057 d = DateTime.parseIso8601DateTime(s); 1058 } catch(e) { 1059 d = null; 1060 } 1061 } 1062 } 1063 else { 1064 return null; 1065 } 1066 } 1067 // d should be a date or null 805 1068 return d; 806 };807 808 /**809 * Map of supported date parsers. Add custom date parsers here if you810 * want to refer to them by key rather than as a function name.811 */812 TimeMapDataset.dateParsers = {813 'hybrid': TimeMapDataset.hybridParser,814 'iso8601': DT.parseIso8601DateTime,815 'gregorian': TimeMapDataset.gregorianParser816 1069 }; 817 1070 … … 829 1082 830 1083 /** 831 * Add items tomap and timeline.1084 * Add an array of items to the map and timeline. 832 1085 * Each item has both a timeline event and a map placemark. 833 1086 * 834 * @param {Object} data Data to be loaded. See loadItem() below for the format. 835 * @param {Function} transform If data is not in the above format, transformation function to make it so 1087 * @param {Object} data Data to be loaded. See loadItem() for the format. 1088 * @param {Function} [transform] If data is not in the above format, transformation function to make it so 1089 * @see TimeMapDataset#loadItem 836 1090 */ 837 1091 TimeMapDataset.prototype.loadItems = function(data, transform) { … … 842 1096 }; 843 1097 844 /* 1098 /** 845 1099 * Add one item to map and timeline. 846 1100 * Each item has both a timeline event and a map placemark. 847 1101 * 848 * @param {Object} data Data to be loaded, in the following format: 1102 * @param {Object} data Data to be loaded, in the following format: <pre> 849 1103 * {String} title Title of the item (visible on timeline) 850 1104 * {DateTime} start Start time of the event on the timeline … … 862 1116 * {Float} west Western longitude of the overlay 863 1117 * {Object} options Optional arguments to be passed to the TimeMapItem (@see TimeMapItem) 864 * @param {Function} transform If data is not in the above format, transformation function to make it so 1118 * </pre> 1119 * @param {Function} [transform] If data is not in the above format, transformation function to make it so 1120 * @return {TimeMapItem} The created item (for convenience, as it's already been added) 1121 * @see TimeMapItem 865 1122 */ 866 1123 TimeMapDataset.prototype.loadItem = function(data, transform) { 1124 867 1125 // apply transformation, if any 868 1126 if (transform !== undefined) { … … 870 1128 } 871 1129 // transform functions can return a null value to skip a datum in the set 872 if ( data === null) {1130 if (!data) { 873 1131 return; 874 1132 } 875 1133 876 // use item theme if provided, defaulting to dataset theme 877 var options = data.options || {}; 878 if (typeof(options.theme) == "string") { 879 options.theme = TimeMapDataset.themes[options.theme]; 880 } 881 var theme = options.theme || this.opts.theme; 882 theme.eventIconPath = options.eventIconPath || this.opts.theme.eventIconPath; 883 theme.eventIcon = theme.eventIconPath + theme.eventIconImage; 884 885 var tm = this.timemap; 1134 // set defaults for options 1135 options = util.merge(data.options, this.opts); 1136 // allow theme options to be specified in options 1137 var theme = options.theme = TimeMapTheme.create(options.theme, options); 886 1138 887 1139 // create timeline event 888 1140 var parser = this.opts.dateParser, start = data.start, end = data.end, instant; 889 start = (start === undefined||start === "") ? null : parser(start);890 end = (end === undefined||end === "") ? null : parser(end);891 instant = (end === undefined);1141 start = start ? parser(start) : null; 1142 end = end ? parser(end) : null; 1143 instant = !end; 892 1144 var eventIcon = theme.eventIcon, 893 1145 title = data.title, 894 1146 // allow event-less placemarks - these will be always present on map 895 1147 event = null; 896 if (start !== null) { 1148 if (start !== null) { 897 1149 var eventClass = Timeline.DefaultEventSource.Event; 898 if ( TimeMap.TimelineVersion() == "1.2") {1150 if (util.TimelineVersion() == "1.2") { 899 1151 // attributes by parameter 900 1152 event = new eventClass(start, end, null, null, … … 909 1161 // attributes in object 910 1162 event = new eventClass({ 911 "start": start,912 "end": end,913 "instant": instant,914 "text": title,915 "icon": eventIcon,916 "color": theme.eventColor,917 "textColor": textColor1163 start: start, 1164 end: end, 1165 instant: instant, 1166 text: title, 1167 icon: eventIcon, 1168 color: theme.eventColor, 1169 textColor: textColor 918 1170 }); 919 1171 } … … 921 1173 922 1174 // set the icon, if any, outside the closure 923 var markerIcon = ("icon" in data) ? data.icon :theme.icon,924 bounds = tm.mapBounds; // save some bytes925 1175 var markerIcon = theme.icon, 1176 tm = this.timemap, 1177 bounds = tm.mapBounds; 926 1178 927 1179 // internal function: create map placemark … … 931 1183 var placemark = null, type = "", point = null; 932 1184 // point placemark 933 if ("point" in pdata) { 1185 if (pdata.point) { 1186 var lat = pdata.point.lat, lon = pdata.point.lon; 1187 if (lat === undefined || lon === undefined) { 1188 // give up 1189 return null; 1190 } 934 1191 point = new GLatLng( 935 1192 parseFloat(pdata.point.lat), … … 940 1197 bounds.extend(point); 941 1198 } 942 placemark = new GMarker(point, { icon: markerIcon }); 1199 // create marker 1200 placemark = new GMarker(point, { 1201 icon: markerIcon, 1202 title: pdata.title 1203 }); 943 1204 type = "marker"; 944 1205 point = placemark.getLatLng(); 945 1206 } 946 1207 // polyline and polygon placemarks 947 else if ( "polyline" in pdata || "polygon" in pdata) {1208 else if (pdata.polyline || pdata.polygon) { 948 1209 var points = [], line; 949 if ( "polyline" in pdata) {1210 if (pdata.polyline) { 950 1211 line = pdata.polyline; 951 1212 } else { 952 1213 line = pdata.polygon; 953 1214 } 954 for (var x=0; x<line.length; x++) { 955 point = new GLatLng( 956 parseFloat(line[x].lat), 957 parseFloat(line[x].lon) 958 ); 959 points.push(point); 960 // add point to visible map bounds 961 if (tm.opts.centerOnItems) { 962 bounds.extend(point); 1215 if (line && line.length) { 1216 for (var x=0; x<line.length; x++) { 1217 point = new GLatLng( 1218 parseFloat(line[x].lat), 1219 parseFloat(line[x].lon) 1220 ); 1221 points.push(point); 1222 // add point to visible map bounds 1223 if (tm.opts.centerOnItems) { 1224 bounds.extend(point); 1225 } 963 1226 } 964 }965 if ("polyline" in pdata) {966 placemark = new GPolyline(points,967 theme.lineColor,968 theme.lineWeight,969 theme.lineOpacity);970 type = "polyline";971 point = placemark.getVertex(Math.floor(placemark.getVertexCount()/2));972 } else {973 placemark = new GPolygon(points,974 theme.polygonLineColor,975 theme.polygonLineWeight,976 theme.polygonLineOpacity,977 theme.fillColor,978 theme.fillOpacity);979 type = "polygon";980 point = placemark.getBounds().getCenter();1227 if ("polyline" in pdata) { 1228 placemark = new GPolyline(points, 1229 theme.lineColor, 1230 theme.lineWeight, 1231 theme.lineOpacity); 1232 type = "polyline"; 1233 point = placemark.getVertex(Math.floor(placemark.getVertexCount()/2)); 1234 } else { 1235 placemark = new GPolygon(points, 1236 theme.polygonLineColor, 1237 theme.polygonLineWeight, 1238 theme.polygonLineOpacity, 1239 theme.fillColor, 1240 theme.fillOpacity); 1241 type = "polygon"; 1242 point = placemark.getBounds().getCenter(); 1243 } 981 1244 } 982 1245 } … … 1019 1282 for (i=0; i<types.length; i++) { 1020 1283 if (types[i] in data) { 1021 pdata = {}; 1284 // put in title (only used for markers) 1285 pdata = {title: title}; 1022 1286 pdata[types[i]] = data[types[i]]; 1023 1287 pdataArr.push(pdata); … … 1029 1293 // create the placemark 1030 1294 var p = createPlacemark(pdataArr[i]); 1031 // take the first point and type as a default 1032 point = point || p.point; 1033 type = type || p.type; 1034 placemark.push(p.placemark); 1295 // check that the placemark was valid 1296 if (p && p.placemark) { 1297 // take the first point and type as a default 1298 point = point || p.point; 1299 type = type || p.type; 1300 placemark.push(p.placemark); 1301 } 1035 1302 } 1036 1303 } … … 1041 1308 1042 1309 options.title = title; 1043 options.type = type || "none"; 1044 options.theme = theme; 1310 options.type = type; 1045 1311 // check for custom infoPoint and convert to GLatLng 1046 1312 if (options.infoPoint) { … … 1058 1324 if (event !== null) { 1059 1325 event.item = item; 1060 this.eventSource.add(event); 1326 // allow for custom event loading 1327 if (!this.opts.noEventLoad) { 1328 // add event to timeline 1329 this.eventSource.add(event); 1330 } 1061 1331 } 1062 1332 // add placemark(s) if any exist … … 1068 1338 item.openInfoWindow(); 1069 1339 }); 1070 // add placemark and event to map and timeline 1071 tm.map.addOverlay(placemark[i]); 1340 // allow for custom placemark loading 1341 if (!this.opts.noPlacemarkLoad) { 1342 // add placemark to map 1343 tm.map.addOverlay(placemark[i]); 1344 } 1072 1345 // hide placemarks until the next refresh 1073 1346 placemark[i].hide(); … … 1081 1354 1082 1355 /*---------------------------------------------------------------------------- 1083 * Predefined visual themes for datasets, based on Google markers1356 * TimeMapTheme Class 1084 1357 *---------------------------------------------------------------------------*/ 1085 1358 1086 1359 /** 1087 * Create a new theme for a TimeMap dataset, defining colors and images 1360 * @class 1361 * Predefined visual themes for datasets, defining colors and images for 1362 * map markers and timeline events. 1088 1363 * 1089 1364 * @constructor 1090 * @param {Object} options A container for optional arguments:1365 * @param {Object} [options] A container for optional arguments:<pre> 1091 1366 * {GIcon} icon Icon for marker placemarks 1092 1367 * {String} color Default color in hex for events, polylines, polygons … … 1100 1375 * {String} fillOpacity Opacity for polygon fill 1101 1376 * {String} eventColor Background color for duration events 1102 * {URL} eventIcon Icon URL for instant events 1103 */ 1104 function TimeMapDatasetTheme(options) { 1377 * {String} eventIconPath Path to instant event icon directory 1378 * {String} eventIconImage Filename of instant event icon image 1379 * {URL} eventIcon URL for instant event icons (overrides path + image) 1380 * {Boolean} classicTape Whether to use the "classic" style timeline event tape 1381 * (NB: this needs additional css to work - see examples/artists.html) 1382 * </pre> 1383 */ 1384 TimeMapTheme = function(options) { 1385 1105 1386 // work out various defaults - the default theme is Google's reddish color 1106 options = options || {}; 1107 1108 if (!options.icon) { 1387 var defaults = { 1388 color: "#FE766A", 1389 lineOpacity: 1, 1390 lineWeight: 2, 1391 fillOpacity: 0.25, 1392 eventTextColor: null, 1393 eventIconPath: "timemap/images/", 1394 eventIconImage: "red-circle.png", 1395 classicTape: false, 1396 iconImage: GIP + "red-dot.png" 1397 }; 1398 1399 // merge defaults with options 1400 var settings = util.merge(options, defaults); 1401 1402 // kill mergeOnly if necessary 1403 delete settings.mergeOnly; 1404 1405 // make default map icon if not supplied 1406 if (!settings.icon) { 1109 1407 // make new red icon 1110 1408 var markerIcon = new GIcon(G_DEFAULT_ICON); 1111 this.iconImage = options.iconImage || GIP + "red-dot.png"; 1112 markerIcon.image = this.iconImage; 1409 markerIcon.image = settings.iconImage; 1113 1410 markerIcon.iconSize = new GSize(32, 32); 1114 1411 markerIcon.shadow = GIP + "msmarker.shadow.png"; … … 1116 1413 markerIcon.iconAnchor = new GPoint(16, 33); 1117 1414 markerIcon.infoWindowAnchor = new GPoint(18, 3); 1118 } 1119 1120 this.icon = options.icon || markerIcon; 1121 this.color = options.color || "#FE766A"; 1122 this.lineColor = options.lineColor || this.color; 1123 this.polygonLineColor = options.polygonLineColor || this.lineColor; 1124 this.lineOpacity = options.lineOpacity || 1; 1125 this.polgonLineOpacity = options.polgonLineOpacity || this.lineOpacity; 1126 this.lineWeight = options.lineWeight || 2; 1127 this.polygonLineWeight = options.polygonLineWeight || this.lineWeight; 1128 this.fillColor = options.fillColor || this.color; 1129 this.fillOpacity = options.fillOpacity || 0.25; 1130 this.eventColor = options.eventColor || this.color; 1131 this.eventTextColor = options.eventTextColor || null; 1132 this.eventIconPath = options.eventIconPath || "timemap/images/"; 1133 this.eventIconImage = options.eventIconImage || "red-circle.png"; 1134 this.eventIcon = options.eventIcon || this.eventIconPath + this.eventIconImage; 1135 1136 // whether to use the older "tape" event style for the newer Timeline versions 1137 // NB: this needs additional css to work - see examples/artists.html 1138 this.classicTape = ("classicTape" in options) ? options.classicTape : false; 1139 } 1140 1141 TimeMapDataset.redTheme = function(options) { 1142 return new TimeMapDatasetTheme(options); 1143 }; 1144 1145 TimeMapDataset.blueTheme = function(options) { 1146 options = options || {}; 1147 options.iconImage = GIP + "blue-dot.png"; 1148 options.color = "#5A7ACF"; 1149 options.eventIconImage = "blue-circle.png"; 1150 return new TimeMapDatasetTheme(options); 1151 }; 1152 1153 TimeMapDataset.greenTheme = function(options) { 1154 options = options || {}; 1155 options.iconImage = GIP + "green-dot.png"; 1156 options.color = "#19CF54"; 1157 options.eventIconImage = "green-circle.png"; 1158 return new TimeMapDatasetTheme(options); 1159 }; 1160 1161 TimeMapDataset.ltblueTheme = function(options) { 1162 options = options || {}; 1163 options.iconImage = GIP + "ltblue-dot.png"; 1164 options.color = "#5ACFCF"; 1165 options.eventIconImage = "ltblue-circle.png"; 1166 return new TimeMapDatasetTheme(options); 1167 }; 1168 1169 TimeMapDataset.purpleTheme = function(options) { 1170 options = options || {}; 1171 options.iconImage = GIP + "purple-dot.png"; 1172 options.color = "#8E67FD"; 1173 options.eventIconImage = "purple-circle.png"; 1174 return new TimeMapDatasetTheme(options); 1175 }; 1176 1177 TimeMapDataset.orangeTheme = function(options) { 1178 options = options || {}; 1179 options.iconImage = GIP + "orange-dot.png"; 1180 options.color = "#FF9900"; 1181 options.eventIconImage = "orange-circle.png"; 1182 return new TimeMapDatasetTheme(options); 1183 }; 1184 1185 TimeMapDataset.yellowTheme = function(options) { 1186 options = options || {}; 1187 options.iconImage = GIP + "yellow-dot.png"; 1188 options.color = "#ECE64A"; 1189 options.eventIconImage = "yellow-circle.png"; 1190 return new TimeMapDatasetTheme(options); 1191 }; 1192 1193 /** 1194 * Map of themes. Add custom themes to this map if you want 1195 * to load them by key rather than as an object. 1196 */ 1197 TimeMapDataset.themes = { 1198 'red': TimeMapDataset.redTheme(), 1199 'blue': TimeMapDataset.blueTheme(), 1200 'green': TimeMapDataset.greenTheme(), 1201 'ltblue': TimeMapDataset.ltblueTheme(), 1202 'orange': TimeMapDataset.orangeTheme(), 1203 'yellow': TimeMapDataset.yellowTheme(), 1204 'purple': TimeMapDataset.purpleTheme() 1415 settings.icon = markerIcon; 1416 } 1417 1418 // cascade some settings as defaults 1419 defaults = { 1420 lineColor: settings.color, 1421 polygonLineColor: settings.color, 1422 polgonLineOpacity: settings.lineOpacity, 1423 polygonLineWeight: settings.lineWeight, 1424 fillColor: settings.color, 1425 eventColor: settings.color, 1426 eventIcon: settings.eventIconPath + settings.eventIconImage 1427 }; 1428 settings = util.merge(settings, defaults); 1429 1430 // return configured options as theme 1431 return settings; 1432 }; 1433 1434 /** 1435 * Create a theme, based on an optional new or pre-set theme 1436 * 1437 * @param {TimeMapTheme} [theme] Existing theme to clone 1438 * @param {Object} [options] Container for optional arguments - @see TimeMapTheme() 1439 * @return {TimeMapTheme} Configured theme 1440 */ 1441 TimeMapTheme.create = function(theme, options) { 1442 // test for string matches and missing themes 1443 if (theme) { 1444 theme = TimeMap.util.lookup(theme, TimeMap.themes); 1445 } else { 1446 return new TimeMapTheme(options); 1447 } 1448 1449 // see if we need to clone - guessing fewer keys in options 1450 var clone = false, key; 1451 for (key in options) { 1452 if (theme.hasOwnProperty(key)) { 1453 clone = {}; 1454 break; 1455 } 1456 } 1457 // clone if necessary 1458 if (clone) { 1459 for (key in theme) { 1460 if (theme.hasOwnProperty(key)) { 1461 clone[key] = options[key] || theme[key]; 1462 } 1463 } 1464 // fix event icon path, allowing full image path in options 1465 clone.eventIcon = options.eventIcon || 1466 clone.eventIconPath + clone.eventIconImage; 1467 return clone; 1468 } 1469 else { 1470 return theme; 1471 } 1205 1472 }; 1206 1473 1207 1474 1208 1475 /*---------------------------------------------------------------------------- 1209 * TimeMapItem Class - holds references to map placemark and timeline event1476 * TimeMapItem Class 1210 1477 *---------------------------------------------------------------------------*/ 1211 1478 1212 1479 /** 1213 * Create a new TimeMap item with a map placemark and a timeline event 1480 * @class 1481 * The TimeMapItem object holds references to one or more map placemarks and 1482 * an associated timeline event. 1214 1483 * 1215 1484 * @constructor … … 1217 1486 * @param {Event} event The timeline event 1218 1487 * @param {TimeMapDataset} dataset Reference to the parent dataset object 1219 * @param {Object} options A container for optional arguments:1488 * @param {Object} [options] A container for optional arguments:<pre> 1220 1489 * {String} title Title of the item 1221 1490 * {String} description Plain-text description of the item … … 1226 1495 * {Function} openInfoWindow Function redefining how info window opens 1227 1496 * {Function} closeInfoWindow Function redefining how info window closes 1228 */ 1229 function TimeMapItem(placemark, event, dataset, options) { 1230 // initialize vars 1231 this.event = event; 1232 this.dataset = dataset; 1233 this.map = dataset.timemap.map; 1497 * {String/TimeMapTheme} theme Theme applying to this item, overriding dataset theme 1498 * </pre> 1499 */ 1500 TimeMapItem = function(placemark, event, dataset, options) { 1501 1502 /** 1503 * This item's timeline event 1504 * @type Timeline.Event 1505 */ 1506 this.event = event; 1507 1508 /** 1509 * This item's parent dataset 1510 * @type TimeMapDataset 1511 */ 1512 this.dataset = dataset; 1513 1514 /** 1515 * The timemap's map object 1516 * @type GMap2 1517 */ 1518 this.map = dataset.timemap.map; 1234 1519 1235 1520 // initialize placemark(s) with some type juggling 1236 if (placemark && TimeMap.isArray(placemark) && placemark.length === 0) {1521 if (placemark && util.isArray(placemark) && placemark.length === 0) { 1237 1522 placemark = null; 1238 1523 } … … 1240 1525 placemark = placemark[0]; 1241 1526 } 1527 /** 1528 * This item's placemark(s) 1529 * @type GMarker/GPolyline/GPolygon/GOverlay/Array 1530 */ 1242 1531 this.placemark = placemark; 1243 1532 1244 1533 // set defaults for options 1245 this.opts = options || {}; 1246 this.opts.type = options.type || ''; 1247 this.opts.title = options.title || ''; 1248 this.opts.description = options.description || ''; 1249 this.opts.infoPoint = options.infoPoint || null; 1250 this.opts.infoHtml = options.infoHtml || ''; 1251 this.opts.infoUrl = options.infoUrl || ''; 1252 1253 // get functions 1534 var defaults = { 1535 type: 'none', 1536 title: 'Untitled', 1537 description: '', 1538 infoPoint: null, 1539 infoHtml: '', 1540 infoUrl: '', 1541 closeInfoWindow: TimeMapItem.closeInfoWindowBasic 1542 }; 1543 this.opts = options = util.merge(options, defaults, dataset.opts); 1544 1545 // select default open function 1546 if (!options.openInfoWindow) { 1547 if (options.infoUrl !== "") { 1548 // load via AJAX if URL is provided 1549 options.openInfoWindow = TimeMapItem.openInfoWindowAjax; 1550 } else { 1551 // otherwise default to basic window 1552 options.openInfoWindow = TimeMapItem.openInfoWindowBasic; 1553 } 1554 } 1555 1556 // getter functions 1557 1558 /** 1559 * Return the placemark type for this item 1560 * 1561 * @return {String} Placemark type 1562 */ 1254 1563 this.getType = function() { return this.opts.type; }; 1564 1565 /** 1566 * Return the title for this item 1567 * 1568 * @return {String} Item title 1569 */ 1255 1570 this.getTitle = function() { return this.opts.title; }; 1571 1572 /** 1573 * Return the item's "info point" (the anchor for the map info window) 1574 * 1575 * @return {GLatLng} Info point 1576 */ 1256 1577 this.getInfoPoint = function() { 1257 1578 // default to map center if placemark not set … … 1259 1580 }; 1260 1581 1261 // items initialize visible 1582 /** 1583 * Whether the item is visible 1584 * @type Boolean 1585 */ 1262 1586 this.visible = true; 1263 // placemarks initialize hidden 1587 1588 /** 1589 * Whether the item's placemark is visible 1590 * @type Boolean 1591 */ 1264 1592 this.placemarkVisible = false; 1265 // events initialize visible 1593 1594 /** 1595 * Whether the item's event is visible 1596 * @type Boolean 1597 */ 1266 1598 this.eventVisible = true; 1267 1599 1268 // allow for custom open/close functions, set at item, dataset, or timemap level 1269 this.openInfoWindow = options.openInfoWindow || 1270 dataset.opts.openInfoWindow || 1271 dataset.timemap.opts.openInfoWindow || 1272 false; 1273 if (!this.openInfoWindow) { 1274 if (this.opts.infoUrl !== "") { 1275 // load via AJAX if URL is provided 1276 this.openInfoWindow = TimeMapItem.openInfoWindowAjax; 1277 } else { 1278 // otherwise default to basic window 1279 this.openInfoWindow = TimeMapItem.openInfoWindowBasic; 1280 } 1281 } 1282 this.closeInfoWindow = options.closeInfoWindow || 1283 dataset.opts.closeInfoWindow || 1284 dataset.timemap.opts.closeInfoWindow || 1285 TimeMapItem.closeInfoWindowBasic; 1286 } 1600 /** 1601 * Open the info window for this item. 1602 * By default this is the map infoWindow, but you can set custom functions 1603 * for whatever behavior you want when the event or placemark is clicked 1604 * @function 1605 */ 1606 this.openInfoWindow = options.openInfoWindow; 1607 1608 /** 1609 * Close the info window for this item. 1610 * By default this is the map infoWindow, but you can set custom functions 1611 * for whatever behavior you want. 1612 * @function 1613 */ 1614 this.closeInfoWindow = options.closeInfoWindow; 1615 }; 1287 1616 1288 1617 /** 1289 * Show the map placemark 1618 * Show the map placemark(s) 1290 1619 */ 1291 1620 TimeMapItem.prototype.showPlacemark = function() { … … 1303 1632 1304 1633 /** 1305 * Hide the map placemark 1634 * Hide the map placemark(s) 1306 1635 */ 1307 1636 TimeMapItem.prototype.hidePlacemark = function() { … … 1320 1649 1321 1650 /** 1322 * Show the timeline event 1651 * Show the timeline event. 1323 1652 * NB: Will likely require calling timeline.layout() 1324 1653 */ … … 1334 1663 1335 1664 /** 1336 * Show the timeline event 1337 * NB: Will likely require calling timeline.layout() 1665 * Hide the timeline event. 1666 * NB: Will likely require calling timeline.layout(), 1667 * AND calling eventSource._events._index() (ugh) 1338 1668 */ 1339 1669 TimeMapItem.prototype.hideEvent = function() { 1340 1670 if (this.event) { 1341 if (this.eventVisible == true){1671 if (this.eventVisible){ 1342 1672 this.dataset.timemap.timeline.getBand(0) 1343 1673 .getEventSource()._events._events.remove(this.event); … … 1413 1743 1414 1744 /*---------------------------------------------------------------------------- 1415 * Utility functions , attached to TimeMap to avoid namespace issues1745 * Utility functions 1416 1746 *---------------------------------------------------------------------------*/ 1417 1747 … … 1422 1752 * @return {String} Trimmed string 1423 1753 */ 1424 TimeMap. trim = function(str) {1754 TimeMap.util.trim = function(str) { 1425 1755 str = str && String(str) || ''; 1426 1756 return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); … … 1433 1763 * @return {Boolean} Whether the object is an array 1434 1764 */ 1435 TimeMap. isArray = function(o) {1765 TimeMap.util.isArray = function(o) { 1436 1766 return o && !(o.propertyIsEnumerable('length')) && 1437 1767 typeof o === 'object' && typeof o.length === 'number'; … … 1443 1773 * @param {XML Node} n Node in which to look for tag 1444 1774 * @param {String} tag Name of tag to look for 1445 * @param {String} ns Optional namespace1775 * @param {String} [ns] XML namespace to look in 1446 1776 * @return {String} Tag value as string 1447 1777 */ 1448 TimeMap. getTagValue = function(n, tag, ns) {1778 TimeMap.util.getTagValue = function(n, tag, ns) { 1449 1779 var str = ""; 1450 var nList = TimeMap. getNodeList(n, tag, ns);1780 var nList = TimeMap.util.getNodeList(n, tag, ns); 1451 1781 if (nList.length > 0) { 1452 1782 n = nList[0].firstChild; … … 1464 1794 * Empty container for mapping XML namespaces to URLs 1465 1795 */ 1466 TimeMap. nsMap = {};1467 1468 /** 1469 * Cross-browser implementation of getElementsByTagNameNS 1796 TimeMap.util.nsMap = {}; 1797 1798 /** 1799 * Cross-browser implementation of getElementsByTagNameNS. 1470 1800 * Note: Expects any applicable namespaces to be mapped in 1471 * TimeMap. nsMap. XXX: There may be better ways to do this.1801 * TimeMap.util.nsMap. XXX: There may be better ways to do this. 1472 1802 * 1473 1803 * @param {XML Node} n Node in which to look for tag 1474 1804 * @param {String} tag Name of tag to look for 1475 * @param {String} ns Optional namespace1805 * @param {String} [ns] XML namespace to look in 1476 1806 * @return {XML Node List} List of nodes with the specified tag name 1477 1807 */ 1478 TimeMap.getNodeList = function(n, tag, ns) { 1808 TimeMap.util.getNodeList = function(n, tag, ns) { 1809 var nsMap = TimeMap.util.nsMap; 1479 1810 if (ns === undefined) { 1480 1811 // no namespace 1481 1812 return n.getElementsByTagName(tag); 1482 1813 } 1483 if (n.getElementsByTagNameNS && TimeMap.nsMap[ns]) {1814 if (n.getElementsByTagNameNS && nsMap[ns]) { 1484 1815 // function and namespace both exist 1485 return n.getElementsByTagNameNS( TimeMap.nsMap[ns], tag);1816 return n.getElementsByTagNameNS(nsMap[ns], tag); 1486 1817 } 1487 1818 // no function, try the colon tag name … … 1493 1824 * 1494 1825 * @param {Object} coords GLatLng, array, or string to convert 1495 * @param {Boolean} reversedWhether the points are KML-style lon/lat, rather than lat/lon1826 * @param {Boolean} [reversed] Whether the points are KML-style lon/lat, rather than lat/lon 1496 1827 * @return {Object} TimeMap.init()-style point 1497 1828 */ 1498 TimeMap.makePoint = function(coords, reversed) { 1499 var latlon = null; 1829 TimeMap.util.makePoint = function(coords, reversed) { 1830 var latlon = null, 1831 trim = TimeMap.util.trim; 1500 1832 // GLatLng 1501 1833 if (coords.lat && coords.lng) { … … 1503 1835 } 1504 1836 // array of coordinates 1505 if (TimeMap. isArray(coords)) {1837 if (TimeMap.util.isArray(coords)) { 1506 1838 latlon = coords; 1507 1839 } 1508 1840 // string 1509 if ( latlon === null) {1841 if (!latlon) { 1510 1842 // trim extra whitespace 1511 coords = TimeMap.trim(coords);1843 coords = trim(coords); 1512 1844 if (coords.indexOf(',') > -1) { 1513 1845 // split on commas … … 1518 1850 } 1519 1851 } 1520 if (reversed) latlon.reverse(); 1852 // deal with extra coordinates (i.e. KML altitude) 1853 if (latlon.length > 2) { 1854 latlon = latlon.slice(0, 2); 1855 } 1856 // deal with backwards (i.e. KML-style) coordinates 1857 if (reversed) { 1858 latlon.reverse(); 1859 } 1521 1860 return { 1522 "lat": TimeMap.trim(latlon[0]),1523 "lon": TimeMap.trim(latlon[1])1861 "lat": trim(latlon[0]), 1862 "lon": trim(latlon[1]) 1524 1863 }; 1525 1864 }; … … 1527 1866 /** 1528 1867 * Make TimeMap.init()-style polyline/polygons from a whitespace-delimited 1529 * string of coordinates (such as those in GeoRSS and KML) 1530 * XXX: Any reason for this to take arrays of GLatLngs as well? 1868 * string of coordinates (such as those in GeoRSS and KML). 1531 1869 * 1532 1870 * @param {Object} coords String to convert 1533 * @param {Boolean} reversedWhether the points are KML-style lon/lat, rather than lat/lon1871 * @param {Boolean} [reversed] Whether the points are KML-style lon/lat, rather than lat/lon 1534 1872 * @return {Object} Formated coordinate array 1535 1873 */ 1536 TimeMap. makePoly = function(coords, reversed) {1874 TimeMap.util.makePoly = function(coords, reversed) { 1537 1875 var poly = [], latlon; 1538 var coordArr = TimeMap. trim(coords).split(/[\r\n\f ]+/);1539 if (coordArr.length == 0) return [];1876 var coordArr = TimeMap.util.trim(coords).split(/[\r\n\f ]+/); 1877 if (coordArr.length === 0) return []; 1540 1878 // loop through coordinates 1541 1879 for (var x=0; x<coordArr.length; x++) { 1542 latlon = (coordArr[x].indexOf(',') ) ?1880 latlon = (coordArr[x].indexOf(',') > 0) ? 1543 1881 // comma-separated coordinates (KML-style lon/lat) 1544 latlon =coordArr[x].split(",") :1882 coordArr[x].split(",") : 1545 1883 // space-separated coordinates - increment to step by 2s 1546 latlon = [coordArr[x], coordArr[++x]]; 1547 if (reversed) latlon.reverse(); 1884 [coordArr[x], coordArr[++x]]; 1885 // deal with extra coordinates (i.e. KML altitude) 1886 if (latlon.length > 2) { 1887 latlon = latlon.slice(0, 2); 1888 } 1889 // deal with backwards (i.e. KML-style) coordinates 1890 if (reversed) { 1891 latlon.reverse(); 1892 } 1548 1893 poly.push({ 1549 1894 "lat": latlon[0], … … 1558 1903 * 1559 1904 * @param {Date} d Date to format 1560 * @param {int} precision Optional precision indicator:1905 * @param {int} [precision] Precision indicator:<pre> 1561 1906 * 3 (default): Show full date and time 1562 1907 * 2: Show full date and time, omitting seconds 1563 1908 * 1: Show date only 1909 *</pre> 1564 1910 * @return {String} Formatted string 1565 1911 */ 1566 TimeMap. formatDate = function(d, precision) {1912 TimeMap.util.formatDate = function(d, precision) { 1567 1913 // default to high precision 1568 1914 precision = precision || 3; … … 1570 1916 if (d) { 1571 1917 // check for date.js support 1572 if (d.toISOString ) {1918 if (d.toISOString && precision == 3) { 1573 1919 return d.toISOString(); 1574 1920 } … … 1598 1944 1599 1945 /** 1600 * Determine the SIMILE Timeline version 1601 * XXX: quite rough at the moment 1946 * Determine the SIMILE Timeline version. 1602 1947 * 1603 1948 * @return {String} At the moment, only "1.2", "2.2.0", or what Timeline provides 1604 1949 */ 1605 TimeMap. TimelineVersion = function() {1950 TimeMap.util.TimelineVersion = function() { 1606 1951 // check for Timeline.version support - added in 2.3.0 1607 1952 if (Timeline.version) { … … 1614 1959 } 1615 1960 }; 1961 1962 1963 /** 1964 * Identify the placemark type. 1965 * XXX: Not 100% happy with this implementation, which relies heavily on duck-typing. 1966 * 1967 * @param {Object} pm Placemark to identify 1968 * @return {String} Type of placemark, or false if none found 1969 */ 1970 TimeMap.util.getPlacemarkType = function(pm) { 1971 if ('getIcon' in pm) { 1972 return 'marker'; 1973 } 1974 if ('getVertex' in pm) { 1975 return 'setFillStyle' in pm ? 'polygon' : 'polyline'; 1976 } 1977 return false; 1978 }; 1979 1980 /** 1981 * Merge two or more objects, giving precendence to those 1982 * first in the list (i.e. don't overwrite existing keys). 1983 * Original objects will not be modified. 1984 * 1985 * @param {Object} obj1 Base object 1986 * @param {Object} [objN] Objects to merge into base 1987 * @return {Object} Merged object 1988 */ 1989 TimeMap.util.merge = function() { 1990 var opts = {}, args = arguments, obj, key, x, y; 1991 // must... make... subroutine... 1992 var mergeKey = function(o1, o2, key) { 1993 // note: existing keys w/undefined values will be overwritten 1994 if (o1.hasOwnProperty(key) && o2[key] === undefined) { 1995 o2[key] = o1[key]; 1996 } 1997 }; 1998 for (x=0; x<args.length; x++) { 1999 obj = args[x]; 2000 if (obj) { 2001 // allow non-base objects to constrain what will be merged 2002 if (x > 0 && 'mergeOnly' in obj) { 2003 for (y=0; y<obj.mergeOnly.length; y++) { 2004 key = obj.mergeOnly[y]; 2005 mergeKey(obj, opts, key); 2006 } 2007 } 2008 // otherwise, just merge everything 2009 else { 2010 for (key in obj) { 2011 mergeKey(obj, opts, key); 2012 } 2013 } 2014 } 2015 } 2016 return opts; 2017 }; 2018 2019 /** 2020 * Attempt look up a key in an object, returning either the value, 2021 * undefined if the key is a string but not found, or the key if not a string 2022 * 2023 * @param {String|Object} key Key to look up 2024 * @param {Object} map Object in which to look 2025 * @return {Object} Value, undefined, or key 2026 */ 2027 TimeMap.util.lookup = function(key, map) { 2028 if (typeof(key) == 'string') { 2029 return map[key]; 2030 } 2031 else { 2032 return key; 2033 } 2034 }; 2035 2036 2037 /*---------------------------------------------------------------------------- 2038 * Lookup maps 2039 * (need to be at end because some call util functions on initialization) 2040 *---------------------------------------------------------------------------*/ 2041 2042 /** 2043 * Lookup map of common timeline intervals. 2044 * Add custom intervals here if you want to refer to them by key rather 2045 * than as a function name. 2046 * @type Object 2047 */ 2048 TimeMap.intervals = { 2049 sec: [DateTime.SECOND, DateTime.MINUTE], 2050 min: [DateTime.MINUTE, DateTime.HOUR], 2051 hr: [DateTime.HOUR, DateTime.DAY], 2052 day: [DateTime.DAY, DateTime.WEEK], 2053 wk: [DateTime.WEEK, DateTime.MONTH], 2054 mon: [DateTime.MONTH, DateTime.YEAR], 2055 yr: [DateTime.YEAR, DateTime.DECADE], 2056 dec: [DateTime.DECADE, DateTime.CENTURY] 2057 }; 2058 2059 /** 2060 * Lookup map of Google map types. 2061 * @type Object 2062 */ 2063 TimeMap.mapTypes = { 2064 normal: G_NORMAL_MAP, 2065 satellite: G_SATELLITE_MAP, 2066 hybrid: G_HYBRID_MAP, 2067 physical: G_PHYSICAL_MAP, 2068 moon: G_MOON_VISIBLE_MAP, 2069 sky: G_SKY_VISIBLE_MAP 2070 }; 2071 2072 /** 2073 * Lookup map of supported date parser functions. 2074 * Add custom date parsers here if you want to refer to them by key rather 2075 * than as a function name. 2076 * @type Object 2077 */ 2078 TimeMap.dateParsers = { 2079 hybrid: TimeMapDataset.hybridParser, 2080 iso8601: DateTime.parseIso8601DateTime, 2081 gregorian: TimeMapDataset.gregorianParser 2082 }; 2083 2084 /** 2085 * @namespace 2086 * Pre-set event/placemark themes in a variety of colors. 2087 * Add custom themes here if you want to refer to them by key rather 2088 * than as a function name. 2089 */ 2090 TimeMap.themes = { 2091 2092 /** 2093 * Red theme: #FE766A 2094 * This is the default. 2095 * 2096 * @type TimeMapTheme 2097 */ 2098 red: new TimeMapTheme(), 2099 2100 /** 2101 * Blue theme: #5A7ACF 2102 * 2103 * @type TimeMapTheme 2104 */ 2105 blue: new TimeMapTheme({ 2106 iconImage: GIP + "blue-dot.png", 2107 color: "#5A7ACF", 2108 eventIconImage: "blue-circle.png" 2109 }), 2110 2111 /** 2112 * Green theme: #19CF54 2113 * 2114 * @type TimeMapTheme 2115 */ 2116 green: new TimeMapTheme({ 2117 iconImage: GIP + "green-dot.png", 2118 color: "#19CF54", 2119 eventIconImage: "green-circle.png" 2120 }), 2121 2122 /** 2123 * Light blue theme: #5ACFCF 2124 * 2125 * @type TimeMapTheme 2126 */ 2127 ltblue: new TimeMapTheme({ 2128 iconImage: GIP + "ltblue-dot.png", 2129 color: "#5ACFCF", 2130 eventIconImage: "ltblue-circle.png" 2131 }), 2132 2133 /** 2134 * Purple theme: #8E67FD 2135 * 2136 * @type TimeMapTheme 2137 */ 2138 purple: new TimeMapTheme({ 2139 iconImage: GIP + "purple-dot.png", 2140 color: "#8E67FD", 2141 eventIconImage: "purple-circle.png" 2142 }), 2143 2144 /** 2145 * Orange theme: #FF9900 2146 * 2147 * @type TimeMapTheme 2148 */ 2149 orange: new TimeMapTheme({ 2150 iconImage: GIP + "orange-dot.png", 2151 color: "#FF9900", 2152 eventIconImage: "orange-circle.png" 2153 }), 2154 2155 /** 2156 * Yellow theme: #ECE64A 2157 * 2158 * @type TimeMapTheme 2159 */ 2160 yellow: new TimeMapTheme({ 2161 iconImage: GIP + "yellow-dot.png", 2162 color: "#ECE64A", 2163 eventIconImage: "yellow-circle.png" 2164 }) 2165 }; 2166 2167 // save to window 2168 window.TimeMap = TimeMap; 2169 window.TimeMapDataset = TimeMapDataset; 2170 window.TimeMapTheme = TimeMapTheme; 2171 window.TimeMapItem = TimeMapItem; 2172 2173 })();
Note: See TracChangeset
for help on using the changeset viewer.