Index: oceandb/jQuery_Prototype/script/progressive.js
===================================================================
--- oceandb/jQuery_Prototype/script/progressive.js	(revision 123)
+++ oceandb/jQuery_Prototype/script/progressive.js	(revision 123)
@@ -0,0 +1,184 @@
+/* 
+ * Timemap.js Copyright 2008 Nick Rabinowitz.
+ * Licensed under the MIT License (see LICENSE.txt)
+ */
+
+/**
+ * @fileOverview
+ * Progressive loader
+ *
+ * @author Nick Rabinowitz (www.nickrabinowitz.com)
+ */
+ 
+// for JSLint
+/*global TimeMap */
+
+/**
+ * @class
+ * Progressive loader class - basically a wrapper for another remote loader that can
+ * load data progressively by date range, depending on timeline position.
+ *
+ * <p>The progressive loader can take either another loader or parameters for 
+ * another loader. It expects a loader with a "url" attribute including placeholder
+ * strings [start] and [end] for the start and end dates to retrieve. The assumption 
+ * is that the data service can take start and end parameters and return the data for 
+ * that date range.</p>
+ *
+ * @example Usage in TimeMap.init():
+ 
+    datasets: [
+        {
+            title: "Progressive JSONP Dataset",
+            type: "progressive",
+            options: {
+                type: "jsonp",
+                url: "http://www.test.com/getsomejson.php?start=[start]&end=[end]callback="
+            }
+        }
+    ]
+ *
+ * @example Usage in TimeMap.init():
+ 
+    datasets: [
+        {
+            title: "Progressive KML Dataset",
+            type: "progressive",
+            options: {
+                loader: new TimeMap.loaders.kml({
+                    url: "/mydata.kml?start=[start]&end=[end]"
+                })
+            }
+        }
+    ]
+ *
+ * @constructor
+ * @param {Object} options          All options for the loader:<pre>
+ *   {TimeMap.loaders.remote} [loader]  Instantiated loader class (overrides "type")
+ *   {String} [type]                    Name of loader class to use
+ *   {String/Date} start                Start of initial date range, as date or string
+ *   {Number} interval                  Size in milliseconds of date ranges to load at a time
+ *   {String/Date} [dataMinDate]        Minimum date available in data (optional, will avoid
+ *                                      unnecessary service requests if supplied)
+ *   {String/Date} [dataMaxDate]        Maximum date available in data (optional, will avoid
+ *                                      unnecessary service requests if supplied)
+ *   {Function} [formatUrl]             Function taking (urlTemplate, start, end) and returning
+ *                                      a URL formatted as needed by the service
+ *   {Function} [formatDate]            Function to turn a date into a string formatted
+ *                                      as needed by the service
+ *   ...more                            Any other options needed by the "type" loader
+ * </pre>
+ */
+TimeMap.loaders.progressive = function(options) {
+    // get loader
+    var loader = options.loader, 
+        type = options.type;
+    if (!loader) {
+        // get loader class
+        var loaderClass = (typeof(type) == 'string') ? TimeMap.loaders[type] : type;
+        loader = new loaderClass(options);
+    }
+    
+    // quick string/date check
+    function cleanDate(d) {
+        if (typeof(d) == "string") {
+            d = TimeMapDataset.hybridParser(d);
+        }
+        return d;
+    }
+    
+    // save loader attributes
+    var baseUrl = loader.url, 
+        baseLoadFunction = loader.load,
+        interval = options.interval,
+        formatDate = options.formatDate || TimeMap.util.formatDate,
+        formatUrl =  options.formatUrl,
+        zeroDate = cleanDate(options.start), 
+        dataMinDate = cleanDate(options.dataMinDate), 
+        dataMaxDate = cleanDate(options.dataMaxDate),
+        loaded = {};
+    
+    if (!formatUrl) {
+        formatUrl = function(url, start, end) {
+            return url
+                .replace('[start]', formatDate(start))
+                .replace('[end]', formatDate(end));
+        }
+    }
+    
+    // We don't start with a TimeMap reference, so we need
+    // to stick the listener in on the first load() call
+    var addListener = function(dataset) {
+        var band = dataset.timemap.timeline.getBand(0);
+        // add listener
+        band.addOnScrollListener(function() {
+            // determine relevant blocks
+            var now = band.getCenterVisibleDate(),
+                currBlock = Math.floor((now.getTime() - zeroDate.getTime()) / interval),
+                currBlockTime = zeroDate.getTime() + (interval * currBlock)
+                nextBlockTime = currBlockTime + interval,
+                prevBlockTime = currBlockTime - interval,
+                // no callback necessary?
+                callback = function() {
+                    dataset.timemap.timeline.layout();
+                };
+            
+            // is the current block loaded?
+            if ((!dataMaxDate || currBlockTime < dataMaxDate.getTime()) &&
+                (!dataMinDate || currBlockTime > dataMinDate.getTime()) &&
+                !loaded[currBlock]) {
+                // load it
+                // console.log("loading current block (" + currBlock + ")");
+                loader.load(dataset, callback, new Date(currBlockTime), currBlock);
+            }
+            // are we close enough to load the next block, and is it loaded?
+            if (nextBlockTime < band.getMaxDate().getTime() &&
+                (!dataMaxDate || nextBlockTime < dataMaxDate.getTime()) &&
+                !loaded[currBlock + 1]) {
+                // load next block
+                // console.log("loading next block (" + (currBlock + 1) + ")");
+                loader.load(dataset, callback, new Date(nextBlockTime), currBlock + 1);
+            }
+            // are we close enough to load the previous block, and is it loaded?
+            if (prevBlockTime > band.getMinDate().getTime() &&
+                (!dataMinDate || prevBlockTime > dataMinDate.getTime()) &&
+                !loaded[currBlock - 1]) {
+                // load previous block
+                // console.log("loading prev block (" + (currBlock - 1)  + ")");
+                loader.load(dataset, callback, new Date(prevBlockTime), currBlock - 1);
+            }
+        });
+        // kill this function so that listener is only added once
+        addListener = false;
+    };
+    
+    // override load function
+    loader.load = function(dataset, callback, start, currBlock) {
+        // set start date, defaulting to zero date
+        start = cleanDate(start) || zeroDate;
+        // set current block, defaulting to 0
+        currBlock = currBlock || 0;
+        // set end by interval
+        var end = new Date(start.getTime() + interval);
+        
+        // set current block as loaded
+        // XXX: Failed loads will give a false positive here...
+        // but I'm not sure how else to avoid multiple loads :(
+        loaded[currBlock] = true;
+        
+        // put dates into URL
+        loader.url = formatUrl(baseUrl, start, end);
+        // console.log(loader.url);
+        
+        // load data
+        baseLoadFunction.call(loader, dataset, function() {
+            // add onscroll listener if not yet done
+            if (addListener) {
+                addListener(dataset);
+            }
+            // run callback
+            callback();
+        });
+    };
+    
+    return loader;
+};
