source: oceandb/jQuery_Prototype/script/progressive.js @ 250

Last change on this file since 250 was 123, checked in by rider, 15 years ago

Timeline library

File size: 7.2 KB
Line 
1/*
2 * Timemap.js Copyright 2008 Nick Rabinowitz.
3 * Licensed under the MIT License (see LICENSE.txt)
4 */
5
6/**
7 * @fileOverview
8 * Progressive loader
9 *
10 * @author Nick Rabinowitz (www.nickrabinowitz.com)
11 */
12 
13// for JSLint
14/*global TimeMap */
15
16/**
17 * @class
18 * Progressive loader class - basically a wrapper for another remote loader that can
19 * load data progressively by date range, depending on timeline position.
20 *
21 * <p>The progressive loader can take either another loader or parameters for
22 * another loader. It expects a loader with a "url" attribute including placeholder
23 * strings [start] and [end] for the start and end dates to retrieve. The assumption
24 * is that the data service can take start and end parameters and return the data for
25 * that date range.</p>
26 *
27 * @example Usage in TimeMap.init():
28 
29    datasets: [
30        {
31            title: "Progressive JSONP Dataset",
32            type: "progressive",
33            options: {
34                type: "jsonp",
35                url: "http://www.test.com/getsomejson.php?start=[start]&end=[end]callback="
36            }
37        }
38    ]
39 *
40 * @example Usage in TimeMap.init():
41 
42    datasets: [
43        {
44            title: "Progressive KML Dataset",
45            type: "progressive",
46            options: {
47                loader: new TimeMap.loaders.kml({
48                    url: "/mydata.kml?start=[start]&end=[end]"
49                })
50            }
51        }
52    ]
53 *
54 * @constructor
55 * @param {Object} options          All options for the loader:<pre>
56 *   {TimeMap.loaders.remote} [loader]  Instantiated loader class (overrides "type")
57 *   {String} [type]                    Name of loader class to use
58 *   {String/Date} start                Start of initial date range, as date or string
59 *   {Number} interval                  Size in milliseconds of date ranges to load at a time
60 *   {String/Date} [dataMinDate]        Minimum date available in data (optional, will avoid
61 *                                      unnecessary service requests if supplied)
62 *   {String/Date} [dataMaxDate]        Maximum date available in data (optional, will avoid
63 *                                      unnecessary service requests if supplied)
64 *   {Function} [formatUrl]             Function taking (urlTemplate, start, end) and returning
65 *                                      a URL formatted as needed by the service
66 *   {Function} [formatDate]            Function to turn a date into a string formatted
67 *                                      as needed by the service
68 *   ...more                            Any other options needed by the "type" loader
69 * </pre>
70 */
71TimeMap.loaders.progressive = function(options) {
72    // get loader
73    var loader = options.loader, 
74        type = options.type;
75    if (!loader) {
76        // get loader class
77        var loaderClass = (typeof(type) == 'string') ? TimeMap.loaders[type] : type;
78        loader = new loaderClass(options);
79    }
80   
81    // quick string/date check
82    function cleanDate(d) {
83        if (typeof(d) == "string") {
84            d = TimeMapDataset.hybridParser(d);
85        }
86        return d;
87    }
88   
89    // save loader attributes
90    var baseUrl = loader.url, 
91        baseLoadFunction = loader.load,
92        interval = options.interval,
93        formatDate = options.formatDate || TimeMap.util.formatDate,
94        formatUrl =  options.formatUrl,
95        zeroDate = cleanDate(options.start), 
96        dataMinDate = cleanDate(options.dataMinDate), 
97        dataMaxDate = cleanDate(options.dataMaxDate),
98        loaded = {};
99   
100    if (!formatUrl) {
101        formatUrl = function(url, start, end) {
102            return url
103                .replace('[start]', formatDate(start))
104                .replace('[end]', formatDate(end));
105        }
106    }
107   
108    // We don't start with a TimeMap reference, so we need
109    // to stick the listener in on the first load() call
110    var addListener = function(dataset) {
111        var band = dataset.timemap.timeline.getBand(0);
112        // add listener
113        band.addOnScrollListener(function() {
114            // determine relevant blocks
115            var now = band.getCenterVisibleDate(),
116                currBlock = Math.floor((now.getTime() - zeroDate.getTime()) / interval),
117                currBlockTime = zeroDate.getTime() + (interval * currBlock)
118                nextBlockTime = currBlockTime + interval,
119                prevBlockTime = currBlockTime - interval,
120                // no callback necessary?
121                callback = function() {
122                    dataset.timemap.timeline.layout();
123                };
124           
125            // is the current block loaded?
126            if ((!dataMaxDate || currBlockTime < dataMaxDate.getTime()) &&
127                (!dataMinDate || currBlockTime > dataMinDate.getTime()) &&
128                !loaded[currBlock]) {
129                // load it
130                // console.log("loading current block (" + currBlock + ")");
131                loader.load(dataset, callback, new Date(currBlockTime), currBlock);
132            }
133            // are we close enough to load the next block, and is it loaded?
134            if (nextBlockTime < band.getMaxDate().getTime() &&
135                (!dataMaxDate || nextBlockTime < dataMaxDate.getTime()) &&
136                !loaded[currBlock + 1]) {
137                // load next block
138                // console.log("loading next block (" + (currBlock + 1) + ")");
139                loader.load(dataset, callback, new Date(nextBlockTime), currBlock + 1);
140            }
141            // are we close enough to load the previous block, and is it loaded?
142            if (prevBlockTime > band.getMinDate().getTime() &&
143                (!dataMinDate || prevBlockTime > dataMinDate.getTime()) &&
144                !loaded[currBlock - 1]) {
145                // load previous block
146                // console.log("loading prev block (" + (currBlock - 1)  + ")");
147                loader.load(dataset, callback, new Date(prevBlockTime), currBlock - 1);
148            }
149        });
150        // kill this function so that listener is only added once
151        addListener = false;
152    };
153   
154    // override load function
155    loader.load = function(dataset, callback, start, currBlock) {
156        // set start date, defaulting to zero date
157        start = cleanDate(start) || zeroDate;
158        // set current block, defaulting to 0
159        currBlock = currBlock || 0;
160        // set end by interval
161        var end = new Date(start.getTime() + interval);
162       
163        // set current block as loaded
164        // XXX: Failed loads will give a false positive here...
165        // but I'm not sure how else to avoid multiple loads :(
166        loaded[currBlock] = true;
167       
168        // put dates into URL
169        loader.url = formatUrl(baseUrl, start, end);
170        // console.log(loader.url);
171       
172        // load data
173        baseLoadFunction.call(loader, dataset, function() {
174            // add onscroll listener if not yet done
175            if (addListener) {
176                addListener(dataset);
177            }
178            // run callback
179            callback();
180        });
181    };
182   
183    return loader;
184};
Note: See TracBrowser for help on using the repository browser.