var L = require('leaflet'),
    reqwest = require('reqwest');

L.Realtime = L.GeoJSON.extend({
    includes: L.Mixin.Events,

    options: {
        start: true,
        interval: 60 * 1000,
        getFeatureId: function(f) {
            return f.properties.id;
        }
    },

    initialize: function(src, options) {
        L.GeoJSON.prototype.initialize.call(this, undefined, options);

        if (typeof(src) === 'function') {
            this._src = src;
        } else {
            this._src = function(responseHandler, errorHandler) {
                reqwest(src).then(responseHandler, errorHandler);
            };
        }

        this.features = {};

        if (this.options.start) {
            this.start();
        }
    },

    start: function() {
        if (!this._timer) {
            this._timer = setInterval(L.bind(this.update, this),
                this.options.interval);
            this.update();
        }
    },

    stop: function() {
        if (this._timer) {
            clearTimeout(this._timer);
            delete this._timer;
        }
    },

    isRunning: function() {
        return this._timer;
    },

    update: function() {
        var responseHandler = L.bind(this._onNewData, this),
            errorHandler = L.bind(this._onError, this);

        this._src(responseHandler, errorHandler);
    },

    _onNewData: function(response) {
        var oef = this.options.onEachFeature,
            known = {};

        this.options.onEachFeature = L.bind(function onEachFeature(f, l) {
            var fId,
                oldLayer;

            if (oef) {
                oef(f, l);
            }

            fId = this.options.getFeatureId(f);
            oldLayer = this.features[fId];

            if (oldLayer) {
                this.removeLayer(oldLayer);
            }
            this.features[fId] = l;
            known[fId] = true;
        }, this);

        this.addData(response);
        this._removeUnknown(known);

        this.fire('newdata', {
            features: this._features
        });

        this.options.onEachFeature = oef;
    },

    _onError: function(err, msg) {
        this.fire('error', {
            error: err,
            message: msg
        });
    },

    _removeUnknown: function(known) {
        var fId;
        for (fId in this._features) {
            if (!known[fId]) {
                this.removeLayer(this._features[fId]);
                delete this.features[fId];
            }
        }
    }
});

L.realtime = function(src, options) {
    return new L.Realtime(src, options);
};

module.exports = L.Realtime;
@@ -1,12 +1,27 @@
+%22use strict%22;%0A%0A
 var L = requ
@@ -271,16 +271,290 @@
 ies.id;%0A
+        %7D,%0A        updateFeature: function(f, oldLayer, newLayer) %7B%0A            if (f.geometry.type === 'Point') %7B%0A                oldLayer.setLatLng(newLayer.getLatLng());%0A                return oldLayer;%0A            %7D else %7B%0A                return newLayer;%0A            %7D%0A
         
@@ -927,32 +927,33 @@
 %7D%0A%0A        this.
+_
 features = %7B%7D;%0A%0A
@@ -1632,24 +1632,110 @@
 r);%0A    %7D,%0A%0A
+    getLayer: function(featureId) %7B%0A        return this._features%5BfeatureId%5D;%0A    %7D,%0A%0A
     _onNewDa
@@ -1823,24 +1823,139 @@
   known = %7B%7D
+,%0A            layersToRemove = %5B%5D,%0A            enter = %7B%7D,%0A            update = %7B%7D,%0A            exit,%0A            i
 ;%0A%0A        t
@@ -2065,16 +2065,42 @@
 oldLayer
+,%0A                newLayer
 ;%0A%0A     
@@ -2231,24 +2231,25 @@
 ayer = this.
+_
 features%5BfId
@@ -2272,32 +2272,152 @@
 if (oldLayer) %7B%0A
+                newLayer = this.options.updateFeature(f, oldLayer, l);%0A                if (newLayer !== oldLayer) %7B%0A    
                 
@@ -2448,34 +2448,270 @@
 r);%0A            
+ 
+   
 %7D%0A
+                if (newLayer !== l) %7B%0A                    layersToRemove.push(l);%0A                %7D%0A%0A                l = newLayer;%0A                update%5BfId%5D = f;%0A            %7D else %7B%0A                enter%5BfId%5D = f;%0A            %7D%0A%0A
             this
@@ -2707,24 +2707,25 @@
        this.
+_
 features%5BfId
@@ -2816,24 +2816,31 @@
 se);%0A       
+ exit =
  this._remov
@@ -2860,76 +2860,267 @@
 n);%0A
-%0A        this.fire('newdata', %7B%0A            features: this._features
+        for (i = 0; i %3C layersToRemove.length; i++) %7B%0A            this.removeLayer(layersToRemove%5Bi%5D);%0A        %7D%0A%0A        this.fire('newdata', %7B%0A            features: this._features,%0A            enter: enter,%0A            update: update,%0A            exit: exit
 %0A   
@@ -3365,16 +3365,42 @@
  var fId
+,%0A            removed = %7B%7D
 ;%0A      
@@ -3513,24 +3513,76 @@
 ures%5BfId%5D);%0A
+                removed%5BfId%5D = this._features%5BfId%5D;%0A
             
@@ -3597,16 +3597,17 @@
 te this.
+_
 features
@@ -3632,24 +3632,49 @@
  %7D%0A        %7D
+%0A%0A        return removed;
 %0A    %7D%0A%7D);%0A%0A

Support for updating an existing layer rather than replacing it. Support for enter/update/exit semantics. features->_features and getLayer.