<commit_before>/*
 * L.TimeDimension.Layer.WMS: wms Layer associated to a TimeDimension
 */

L.TimeDimension.Layer.WMS = L.TimeDimension.Layer.extend({

    initialize: function(layer, options) {
        L.TimeDimension.Layer.prototype.initialize.call(this, layer, options);
        this._timeCacheBackward = this.options.cacheBackward || this.options.cache || 0;
        this._timeCacheForward = this.options.cacheForward || this.options.cache || 0;
        this._wmsVersion = this.options.wmsVersion || this.options.version || "1.1.1";
        this._proxy = this.options.proxy || null;
        this._updateTimeDimension = this.options.updateTimeDimension || false;
        this._setDefaultTime = this.options.setDefaultTime || false;
        this._updateTimeDimensionMode = this.options.updateTimeDimensionMode || 'intersect'; // 'union' or 'replace'
        this._layers = {};
        this._defaultTime = 0;
        this._availableTimes = [];
        this._capabilitiesRequested = false;
        if (this._updateTimeDimension || this.options.requestTimeFromCapabilities) {
            this._requestTimeDimensionFromCapabilities();
        }

        this._baseLayer.on('load', (function() {
            this._baseLayer.setLoaded(true);
            this.fire('timeload', {
                time: this._defaultTime
            });
        }).bind(this));
    },

    getEvents : function(){
        var clearCache = L.bind(this._unvalidateCache, this);
        return {
            moveend: clearCache,
            zoomend: clearCache
        }
    },

    eachLayer: function(method, context) {
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop)) {
                method.call(context, this._layers[prop]);
            }
        }
        return L.TimeDimension.Layer.prototype.eachLayer.call(this, method, context);
    },

    _onNewTimeLoading: function(ev) {
        // console.log('Layer._onNewTimeLoading: ' + this._baseLayer.wmsParams.layers + ' with time: ' + new Date(ev.time).toISOString());
        var layer = this._getLayerForTime(ev.time);
        if (!this._map.hasLayer(layer)) {
            this._map.addLayer(layer);
            // console.log('Layer._onNewTimeLoading: layer added to map');
        }
    },

    isReady: function(time) {
        var layer = this._getLayerForTime(time);
        return layer.isLoaded();
    },

    onAdd: function(map) {
        L.TimeDimension.Layer.prototype.onAdd.call(this, map);
        if (this._availableTimes.length == 0) {
            this._requestTimeDimensionFromCapabilities();
        } else {
            this._updateTimeDimensionAvailableTimes();
        }
    },

    _update: function() {
        if (!this._map)
            return;
        var time = this._timeDimension.getCurrentTime();
        // It will get the layer for this time (create or get)
        // Then, the layer will be loaded if necessary, adding it to the map (and show it after loading).
        // If it already on the map (but probably hidden), it will be shown
        var layer = this._getLayerForTime(time);
        if (this._currentLayer == null) {
            this._currentLayer = layer;
        }
        if (!this._map.hasLayer(layer)) {
            this._map.addLayer(layer);
        } else {
            this._showLayer(layer, time);
        }
    },

    setOpacity: function(opacity){
        L.TimeDimension.Layer.prototype.setOpacity.apply(this, arguments);
        //apply to all preloaded caches
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop) && this._layers[prop].setOpacity) {
                this._layers[prop].setOpacity(opacity);
            }
        }
    },

    setParams: function(params, noRedraw) {
        L.extend(this._baseLayer.options, params);
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop) && this._layers[prop].setParams) {
                this._layers[prop].setLoaded(false);//mark it as unloaded
                this._layers[prop].setParams(params, noRedraw);
            }
        }
        return this;
    },

    _unvalidateCache : function(){
        var time = this._timeDimension.getCurrentTime();
        for (var prop in this._layers) {
            if (time != prop && this._layers.hasOwnProperty(prop)) {
                this._layers[prop].setLoaded(false); //mark it as unloaded
                this._layers[prop].redraw();
            }
        }
    },

    _evictCachedTimes : function(keepforward, keepbackward){
        // Cache management
        var times = this._getLoadedTimes();
        var strTime = String(this._currentTime);
        var index = times.indexOf(strTime);
        var remove = [];
        // remove times before current time
        if (keepbackward > -1) {
            var objectsToRemove = index - keepbackward;
            if (objectsToRemove > 0) {
                remove = times.splice(0, objectsToRemove);
                this._removeLayers(remove);
            }
        }
        if (keepforward > -1) {
            index = times.indexOf(strTime);
            var objectsToRemove = times.length - index - keepforward - 1;
            if (objectsToRemove > 0) {
                remove = times.splice(index + keepforward + 1, objectsToRemove);
                this._removeLayers(remove);
            }
        }
    },
    _showLayer: function(layer, time) {
        if (this._currentLayer && this._currentLayer !== layer) {
            this._currentLayer.hide();
        }
        layer.show();
        if (this._currentLayer && this._currentLayer === layer) {
            return;
        }
        this._currentLayer = layer;
        this._currentTime = time;
        console.log('Show layer ' + layer.wmsParams.layers + ' with time: ' + new Date(time).toISOString());

        this._evictCachedTimes(this._timeCacheBackward, this._timeCacheForward);
    },

    _getLayerForTime: function(time) {
        if (time == 0 || time == this._defaultTime || time == null) {
            return this._baseLayer;
        }
        if (this._layers.hasOwnProperty(time)) {
            return this._layers[time];
        }
        var nearestTime = this._getNearestTime(time);
        if (this._layers.hasOwnProperty(nearestTime)) {
            return this._layers[nearestTime];
        }

        var wmsParams = this._baseLayer.options;
        wmsParams.time = new Date(nearestTime).toISOString();

        var newLayer = null;
        if (this._baseLayer instanceof L.TileLayer) {
            newLayer = L.tileLayer.wms(this._baseLayer.getURL(), wmsParams);
        } else {
            newLayer = L.nonTiledLayer.wms(this._baseLayer.getURL(), wmsParams);
        }
        this._layers[time] = newLayer;

        newLayer.on('load', (function(layer, time) {
            layer.setLoaded(true);
            if (this._timeDimension && time == this._timeDimension.getCurrentTime() && !this._timeDimension.isLoading()) {
                this._showLayer(layer, time);
            }
            // console.log('Loaded layer ' + layer.wmsParams.layers + ' with time: ' + new Date(time).toISOString());
            this.fire('timeload', {
                time: time
            });
        }).bind(this, newLayer, time));

        // Hack to hide the layer when added to the map.
        // It will be shown when timeload event is fired from the map (after all layers are loaded)
        newLayer.onAdd = (function(map) {
            Object.getPrototypeOf(this).onAdd.call(this, map);
            this.hide();
        }).bind(newLayer);
        return newLayer;
    },

    _getLoadedTimes: function() {
        var result = [];
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop)) {
                result.push(prop);
            }
        }
        return result.sort(function(a, b) {
            return a - b;
        });
    },

    _removeLayers: function(times) {
        for (var i = 0, l = times.length; i < l; i++) {
            if(this._map)
                this._map.removeLayer(this._layers[times[i]]);
            delete this._layers[times[i]];
        }
    },

    setMinimumForwardCache: function(value) {
        if (value > this._timeCacheForward) {
            this._timeCacheForward = value;
        }
    },

    _requestTimeDimensionFromCapabilities: function() {
        if (this._capabilitiesRequested) {
            return;
        }
        this._capabilitiesRequested = true;
        var wms = this._baseLayer.getURL();
        var url = wms + "?service=WMS&version=" +
            this._wmsVersion + "&request=GetCapabilities";
        if (this._proxy) {
            url = this._proxy + '?url=' + encodeURIComponent(url);
        }
        $.get(url, (function(data) {
            this._defaultTime = Date.parse(this._getDefaultTimeFromCapabilities(data));
            this._setDefaultTime = this._setDefaultTime || (this._timeDimension && this._timeDimension.getAvailableTimes().length == 0);
            this.setAvailableTimes(this._parseTimeDimensionFromCapabilities(data));
            if (this._setDefaultTime && this._timeDimension) {
                this._timeDimension.setCurrentTime(this._defaultTime);
            }
        }).bind(this));
    },

    _parseTimeDimensionFromCapabilities: function(xml) {
        var layers = $(xml).find('Layer[queryable="1"]');
        var layerName = this._baseLayer.wmsParams.layers;
        var layerNameElement = layers.find("Name").filter(function(index) {
            return $(this).text() === layerName;
        });
        var times = null;
        if (layerNameElement) {
            var layer = layerNameElement.parent();
            times = this._getTimesFromLayerCapabilities(layer);
            if (!times) {
                times = this._getTimesFromLayerCapabilities(layer.parent());
            }
        }
        return times;
    },

    _getTimesFromLayerCapabilities: function(layer) {
        var times = null;
        var dimension = layer.find("Dimension[name='time']");
        if (dimension && dimension.length && dimension[0].textContent.length) {
            times = dimension[0].textContent.trim();
        } else {
            var extent = layer.find("Extent[name='time']");
            if (extent && extent.length && extent[0].textContent.length) {
                times = extent[0].textContent.trim();
            }
        }
        return times;
    },

    _getDefaultTimeFromCapabilities: function(xml) {
        var layers = $(xml).find('Layer[queryable="1"]');
        var layerName = this._baseLayer.wmsParams.layers;
        var layerNameElement = layers.find("Name").filter(function(index) {
            return $(this).text() === layerName;
        });
        var defaultTime = 0;
        if (layerNameElement) {
            var layer = layerNameElement.parent();
            defaultTime = this._getDefaultTimeFromLayerCapabilities(layer);
            if (defaultTime == 0) {
                defaultTime = this._getDefaultTimeFromLayerCapabilities(layer.parent());
            }
        }
        return defaultTime;
    },

    _getDefaultTimeFromLayerCapabilities: function(layer) {
        var defaultTime = 0;
        var dimension = layer.find("Dimension[name='time']");
        if (dimension && dimension.attr("default")) {
            defaultTime = dimension.attr("default");
        } else {
            var extent = layer.find("Extent[name='time']");
            if (extent && extent.attr("default")) {
                defaultTime = extent.attr("default");
            }
        }
        return defaultTime;
    },

    setAvailableTimes: function(times) {
        this._availableTimes = L.TimeDimension.Util.parseTimesExpression(times);
        this._updateTimeDimensionAvailableTimes();
    },

    _updateTimeDimensionAvailableTimes: function() {
        if ((this._timeDimension && this._updateTimeDimension) ||
            (this._timeDimension && this._timeDimension.getAvailableTimes().length == 0)) {
            this._timeDimension.setAvailableTimes(this._availableTimes, this._updateTimeDimensionMode);
            if (this._defaultTime > 0) {
                this._timeDimension.setCurrentTime(this._defaultTime);
            }
        }
    },

    _getNearestTime: function(time) {
        if (this._layers.hasOwnProperty(time)) {
            return time;
        }
        if (this._availableTimes.length == 0) {
            return time;
        }
        var index = 0;
        var len = this._availableTimes.length;
        for (; index < len; index++) {
            if (time < this._availableTimes[index]) {
                break;
            }
        }
        // We've found the first index greater than the time. Get the previous
        if (index > 0) {
            index--;
        }
        if (time != this._availableTimes[index]) {
            console.log('Search layer time: ' + new Date(time).toISOString());
            console.log('Return layer time: ' + new Date(this._availableTimes[index]).toISOString());
        }
        return this._availableTimes[index];
    },

});

if (!L.NonTiledLayer) {
    L.NonTiledLayer = (L.Layer || L.Class).extend({});
}

L.NonTiledLayer.include({
    _visible: true,
    _loaded: false,

    _originalUpdate: L.NonTiledLayer.prototype._update,
    _originalOnRemove: L.NonTiledLayer.prototype.onRemove,

    _update: function() {
        if (!this._visible && this._loaded) {
            return;
        }
        this._originalUpdate();
    },

    onRemove: function (map) {
        this._loaded = false;
        this._originalOnRemove(map);
    },

    setLoaded: function(loaded) {
        this._loaded = loaded;
    },

    isLoaded: function() {
        return this._loaded;
    },

    hide: function() {
        this._visible = false;
        this._div.style.display = 'none';
    },

    show: function() {
        this._visible = true;
        this._div.style.display = 'block';
    },

    getURL: function() {
        return this._wmsUrl;
    }

});

L.TileLayer.include({
    _visible: true,
    _loaded: false,

    _originalUpdate: L.TileLayer.prototype._update,

    _update: function() {
        if (!this._visible && this._loaded) {
            return;
        }
        this._originalUpdate();
    },

    setLoaded: function(loaded) {
        this._loaded = loaded;
    },

    isLoaded: function() {
        return this._loaded;
    },

    hide: function() {
        this._visible = false;
        if (this._container) {
            this._container.style.display = 'none';
        }
    },

    show: function() {
        this._visible = true;
        if (this._container) {
            this._container.style.display = 'block';
        }
    },

    getURL: function() {
        return this._url;
    }

});

L.timeDimension.layer.wms = function(layer, options) {
    return new L.TimeDimension.Layer.WMS(layer, options);
};
<commit_msg>Use base layer version if set<commit_after>/*
 * L.TimeDimension.Layer.WMS: wms Layer associated to a TimeDimension
 */

L.TimeDimension.Layer.WMS = L.TimeDimension.Layer.extend({

    initialize: function(layer, options) {
        L.TimeDimension.Layer.prototype.initialize.call(this, layer, options);
        this._timeCacheBackward = this.options.cacheBackward || this.options.cache || 0;
        this._timeCacheForward = this.options.cacheForward || this.options.cache || 0;
        this._wmsVersion = this.options.wmsVersion || this.options.version || layer.options.version || "1.1.1";
        this._proxy = this.options.proxy || null;
        this._updateTimeDimension = this.options.updateTimeDimension || false;
        this._setDefaultTime = this.options.setDefaultTime || false;
        this._updateTimeDimensionMode = this.options.updateTimeDimensionMode || 'intersect'; // 'union' or 'replace'
        this._layers = {};
        this._defaultTime = 0;
        this._availableTimes = [];
        this._capabilitiesRequested = false;
        if (this._updateTimeDimension || this.options.requestTimeFromCapabilities) {
            this._requestTimeDimensionFromCapabilities();
        }

        this._baseLayer.on('load', (function() {
            this._baseLayer.setLoaded(true);
            this.fire('timeload', {
                time: this._defaultTime
            });
        }).bind(this));
    },

    getEvents : function(){
        var clearCache = L.bind(this._unvalidateCache, this);
        return {
            moveend: clearCache,
            zoomend: clearCache
        }
    },

    eachLayer: function(method, context) {
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop)) {
                method.call(context, this._layers[prop]);
            }
        }
        return L.TimeDimension.Layer.prototype.eachLayer.call(this, method, context);
    },

    _onNewTimeLoading: function(ev) {
        // console.log('Layer._onNewTimeLoading: ' + this._baseLayer.wmsParams.layers + ' with time: ' + new Date(ev.time).toISOString());
        var layer = this._getLayerForTime(ev.time);
        if (!this._map.hasLayer(layer)) {
            this._map.addLayer(layer);
            // console.log('Layer._onNewTimeLoading: layer added to map');
        }
    },

    isReady: function(time) {
        var layer = this._getLayerForTime(time);
        return layer.isLoaded();
    },

    onAdd: function(map) {
        L.TimeDimension.Layer.prototype.onAdd.call(this, map);
        if (this._availableTimes.length == 0) {
            this._requestTimeDimensionFromCapabilities();
        } else {
            this._updateTimeDimensionAvailableTimes();
        }
    },

    _update: function() {
        if (!this._map)
            return;
        var time = this._timeDimension.getCurrentTime();
        // It will get the layer for this time (create or get)
        // Then, the layer will be loaded if necessary, adding it to the map (and show it after loading).
        // If it already on the map (but probably hidden), it will be shown
        var layer = this._getLayerForTime(time);
        if (this._currentLayer == null) {
            this._currentLayer = layer;
        }
        if (!this._map.hasLayer(layer)) {
            this._map.addLayer(layer);
        } else {
            this._showLayer(layer, time);
        }
    },

    setOpacity: function(opacity){
        L.TimeDimension.Layer.prototype.setOpacity.apply(this, arguments);
        //apply to all preloaded caches
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop) && this._layers[prop].setOpacity) {
                this._layers[prop].setOpacity(opacity);
            }
        }
    },

    setParams: function(params, noRedraw) {
        L.extend(this._baseLayer.options, params);
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop) && this._layers[prop].setParams) {
                this._layers[prop].setLoaded(false);//mark it as unloaded
                this._layers[prop].setParams(params, noRedraw);
            }
        }
        return this;
    },

    _unvalidateCache : function(){
        var time = this._timeDimension.getCurrentTime();
        for (var prop in this._layers) {
            if (time != prop && this._layers.hasOwnProperty(prop)) {
                this._layers[prop].setLoaded(false); //mark it as unloaded
                this._layers[prop].redraw();
            }
        }
    },

    _evictCachedTimes : function(keepforward, keepbackward){
        // Cache management
        var times = this._getLoadedTimes();
        var strTime = String(this._currentTime);
        var index = times.indexOf(strTime);
        var remove = [];
        // remove times before current time
        if (keepbackward > -1) {
            var objectsToRemove = index - keepbackward;
            if (objectsToRemove > 0) {
                remove = times.splice(0, objectsToRemove);
                this._removeLayers(remove);
            }
        }
        if (keepforward > -1) {
            index = times.indexOf(strTime);
            var objectsToRemove = times.length - index - keepforward - 1;
            if (objectsToRemove > 0) {
                remove = times.splice(index + keepforward + 1, objectsToRemove);
                this._removeLayers(remove);
            }
        }
    },
    _showLayer: function(layer, time) {
        if (this._currentLayer && this._currentLayer !== layer) {
            this._currentLayer.hide();
        }
        layer.show();
        if (this._currentLayer && this._currentLayer === layer) {
            return;
        }
        this._currentLayer = layer;
        this._currentTime = time;
        console.log('Show layer ' + layer.wmsParams.layers + ' with time: ' + new Date(time).toISOString());

        this._evictCachedTimes(this._timeCacheBackward, this._timeCacheForward);
    },

    _getLayerForTime: function(time) {
        if (time == 0 || time == this._defaultTime || time == null) {
            return this._baseLayer;
        }
        if (this._layers.hasOwnProperty(time)) {
            return this._layers[time];
        }
        var nearestTime = this._getNearestTime(time);
        if (this._layers.hasOwnProperty(nearestTime)) {
            return this._layers[nearestTime];
        }

        var wmsParams = this._baseLayer.options;
        wmsParams.time = new Date(nearestTime).toISOString();

        var newLayer = null;
        if (this._baseLayer instanceof L.TileLayer) {
            newLayer = L.tileLayer.wms(this._baseLayer.getURL(), wmsParams);
        } else {
            newLayer = L.nonTiledLayer.wms(this._baseLayer.getURL(), wmsParams);
        }
        this._layers[time] = newLayer;

        newLayer.on('load', (function(layer, time) {
            layer.setLoaded(true);
            if (this._timeDimension && time == this._timeDimension.getCurrentTime() && !this._timeDimension.isLoading()) {
                this._showLayer(layer, time);
            }
            // console.log('Loaded layer ' + layer.wmsParams.layers + ' with time: ' + new Date(time).toISOString());
            this.fire('timeload', {
                time: time
            });
        }).bind(this, newLayer, time));

        // Hack to hide the layer when added to the map.
        // It will be shown when timeload event is fired from the map (after all layers are loaded)
        newLayer.onAdd = (function(map) {
            Object.getPrototypeOf(this).onAdd.call(this, map);
            this.hide();
        }).bind(newLayer);
        return newLayer;
    },

    _getLoadedTimes: function() {
        var result = [];
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop)) {
                result.push(prop);
            }
        }
        return result.sort(function(a, b) {
            return a - b;
        });
    },

    _removeLayers: function(times) {
        for (var i = 0, l = times.length; i < l; i++) {
            if(this._map)
                this._map.removeLayer(this._layers[times[i]]);
            delete this._layers[times[i]];
        }
    },

    setMinimumForwardCache: function(value) {
        if (value > this._timeCacheForward) {
            this._timeCacheForward = value;
        }
    },

    _requestTimeDimensionFromCapabilities: function() {
        if (this._capabilitiesRequested) {
            return;
        }
        this._capabilitiesRequested = true;
        var wms = this._baseLayer.getURL();
        var url = wms + "?service=WMS&version=" +
            this._wmsVersion + "&request=GetCapabilities";
        if (this._proxy) {
            url = this._proxy + '?url=' + encodeURIComponent(url);
        }
        $.get(url, (function(data) {
            this._defaultTime = Date.parse(this._getDefaultTimeFromCapabilities(data));
            this._setDefaultTime = this._setDefaultTime || (this._timeDimension && this._timeDimension.getAvailableTimes().length == 0);
            this.setAvailableTimes(this._parseTimeDimensionFromCapabilities(data));
            if (this._setDefaultTime && this._timeDimension) {
                this._timeDimension.setCurrentTime(this._defaultTime);
            }
        }).bind(this));
    },

    _parseTimeDimensionFromCapabilities: function(xml) {
        var layers = $(xml).find('Layer[queryable="1"]');
        var layerName = this._baseLayer.wmsParams.layers;
        var layerNameElement = layers.find("Name").filter(function(index) {
            return $(this).text() === layerName;
        });
        var times = null;
        if (layerNameElement) {
            var layer = layerNameElement.parent();
            times = this._getTimesFromLayerCapabilities(layer);
            if (!times) {
                times = this._getTimesFromLayerCapabilities(layer.parent());
            }
        }
        return times;
    },

    _getTimesFromLayerCapabilities: function(layer) {
        var times = null;
        var dimension = layer.find("Dimension[name='time']");
        if (dimension && dimension.length && dimension[0].textContent.length) {
            times = dimension[0].textContent.trim();
        } else {
            var extent = layer.find("Extent[name='time']");
            if (extent && extent.length && extent[0].textContent.length) {
                times = extent[0].textContent.trim();
            }
        }
        return times;
    },

    _getDefaultTimeFromCapabilities: function(xml) {
        var layers = $(xml).find('Layer[queryable="1"]');
        var layerName = this._baseLayer.wmsParams.layers;
        var layerNameElement = layers.find("Name").filter(function(index) {
            return $(this).text() === layerName;
        });
        var defaultTime = 0;
        if (layerNameElement) {
            var layer = layerNameElement.parent();
            defaultTime = this._getDefaultTimeFromLayerCapabilities(layer);
            if (defaultTime == 0) {
                defaultTime = this._getDefaultTimeFromLayerCapabilities(layer.parent());
            }
        }
        return defaultTime;
    },

    _getDefaultTimeFromLayerCapabilities: function(layer) {
        var defaultTime = 0;
        var dimension = layer.find("Dimension[name='time']");
        if (dimension && dimension.attr("default")) {
            defaultTime = dimension.attr("default");
        } else {
            var extent = layer.find("Extent[name='time']");
            if (extent && extent.attr("default")) {
                defaultTime = extent.attr("default");
            }
        }
        return defaultTime;
    },

    setAvailableTimes: function(times) {
        this._availableTimes = L.TimeDimension.Util.parseTimesExpression(times);
        this._updateTimeDimensionAvailableTimes();
    },

    _updateTimeDimensionAvailableTimes: function() {
        if ((this._timeDimension && this._updateTimeDimension) ||
            (this._timeDimension && this._timeDimension.getAvailableTimes().length == 0)) {
            this._timeDimension.setAvailableTimes(this._availableTimes, this._updateTimeDimensionMode);
            if (this._defaultTime > 0) {
                this._timeDimension.setCurrentTime(this._defaultTime);
            }
        }
    },

    _getNearestTime: function(time) {
        if (this._layers.hasOwnProperty(time)) {
            return time;
        }
        if (this._availableTimes.length == 0) {
            return time;
        }
        var index = 0;
        var len = this._availableTimes.length;
        for (; index < len; index++) {
            if (time < this._availableTimes[index]) {
                break;
            }
        }
        // We've found the first index greater than the time. Get the previous
        if (index > 0) {
            index--;
        }
        if (time != this._availableTimes[index]) {
            console.log('Search layer time: ' + new Date(time).toISOString());
            console.log('Return layer time: ' + new Date(this._availableTimes[index]).toISOString());
        }
        return this._availableTimes[index];
    },

});

if (!L.NonTiledLayer) {
    L.NonTiledLayer = (L.Layer || L.Class).extend({});
}

L.NonTiledLayer.include({
    _visible: true,
    _loaded: false,

    _originalUpdate: L.NonTiledLayer.prototype._update,
    _originalOnRemove: L.NonTiledLayer.prototype.onRemove,

    _update: function() {
        if (!this._visible && this._loaded) {
            return;
        }
        this._originalUpdate();
    },

    onRemove: function (map) {
        this._loaded = false;
        this._originalOnRemove(map);
    },

    setLoaded: function(loaded) {
        this._loaded = loaded;
    },

    isLoaded: function() {
        return this._loaded;
    },

    hide: function() {
        this._visible = false;
        this._div.style.display = 'none';
    },

    show: function() {
        this._visible = true;
        this._div.style.display = 'block';
    },

    getURL: function() {
        return this._wmsUrl;
    }

});

L.TileLayer.include({
    _visible: true,
    _loaded: false,

    _originalUpdate: L.TileLayer.prototype._update,

    _update: function() {
        if (!this._visible && this._loaded) {
            return;
        }
        this._originalUpdate();
    },

    setLoaded: function(loaded) {
        this._loaded = loaded;
    },

    isLoaded: function() {
        return this._loaded;
    },

    hide: function() {
        this._visible = false;
        if (this._container) {
            this._container.style.display = 'none';
        }
    },

    show: function() {
        this._visible = true;
        if (this._container) {
            this._container.style.display = 'block';
        }
    },

    getURL: function() {
        return this._url;
    }

});

L.timeDimension.layer.wms = function(layer, options) {
    return new L.TimeDimension.Layer.WMS(layer, options);
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { connect as reduxConnect } from 'react-redux';
import invariant from 'invariant';
import find from 'lodash.find';

import Plurality from './constants/Plurality';
import { createRequest } from './actionCreators';
import { fetchResource } from './sagas';
import { deriveContentTypeOptions } from './contentTypes';

/**
 * Connect a component to data from the WP-API.
 * @param {String} [contentType] The content type for which the WP-API request will be made.
 * @param {String} [routeParamsPropName] From which object on props will the WP-API route parameters be derived?
 * @param {Boolean} [routeParamSubjectKey] The key on `params` that will be used as the identifier of desired content.
 * @returns {Function}
 */
export default function connectWordPress ({
  contentType,
  routeParamsPropName = 'params',
  routeParamSubjectKey = 'id'
} = {}) {
  return target => {
    invariant(
      !target.__pepperoni,
      'The component "%s" is already wrapped by Pepperoni.',
      target.name
    );

    const getContentTypeOptions = contentTypes => {
      return typeof contentType === 'undefined'
        ? deriveContentTypeOptions(target.name, contentTypes)
        : contentTypes[contentType];
    };

    function mapStateToProps (state, ownProps) {
      const { contentTypes } = state.$$pepperoni.config;

      const contentTypeOpts = getContentTypeOptions(contentTypes);
      const nameSingular = contentTypeOpts.name[Plurality.SINGULAR];
      const namePlural = contentTypeOpts.name[Plurality.PLURAL];

      const subjectId = ownProps[routeParamsPropName][routeParamSubjectKey];
      const contentTypeCollection = state.$$pepperoni.entities[namePlural];

      let back;

      if (routeParamSubjectKey != 'id') {
        back = find(contentTypeCollection, a => a[routeParamSubjectKey] === subjectId);
      } else {
        back = contentTypeCollection ? contentTypeCollection[subjectId] : null;
      }

      return {
        $$pepperoni: state.$$pepperoni,
        [nameSingular]: back
      };
    }

    class PepperoniComponentWrapper extends Component {
      constructor (props, context) {
        super(props, context);
      }

      componentWillMount () {
        const { contentTypes } = this.props.$$pepperoni.config;
        const entities = this.props.$$pepperoni.entities;

        const params = this.props[routeParamsPropName];
        const subjectId = params[routeParamSubjectKey];

        const contentTypeOpts = getContentTypeOptions(contentTypes);
        const namePlural = contentTypeOpts.name[Plurality.PLURAL];
        const canonicalName = contentTypeOpts.name.canonical;

        if (
          !entities[namePlural] ||
          entities[namePlural] && !entities[namePlural][subjectId]
        ) {
          this.props.dispatch(
            createRequest(canonicalName, subjectId, { params })
          );
        }
      }

      render () {
        return React.createElement(target, this.props);
      }
    }

    PepperoniComponentWrapper.__pepperoni = true;

    PepperoniComponentWrapper.fetchData = (renderProps) => [
      [fetchResource, {
        contentType,
        subject: renderProps[routeParamsPropName][routeParamSubjectKey]
      }]
    ];

    return reduxConnect(mapStateToProps)(PepperoniComponentWrapper);
  };
};
<commit_msg>Accept store and get content type options in fetchData;<commit_after>import React, { Component } from 'react';
import { connect as reduxConnect } from 'react-redux';
import invariant from 'invariant';
import find from 'lodash.find';

import Plurality from './constants/Plurality';
import { createRequest } from './actionCreators';
import { fetchResource } from './sagas';
import { deriveContentTypeOptions } from './contentTypes';

/**
 * Connect a component to data from the WP-API.
 * @param {String} [contentType] The content type for which the WP-API request will be made.
 * @param {String} [routeParamsPropName] From which object on props will the WP-API route parameters be derived?
 * @param {Boolean} [routeParamSubjectKey] The key on `params` that will be used as the identifier of desired content.
 * @returns {Function}
 */
export default function connectWordPress ({
  contentType,
  routeParamsPropName = 'params',
  routeParamSubjectKey = 'id'
} = {}) {
  return target => {
    invariant(
      !target.__pepperoni,
      'The component "%s" is already wrapped by Pepperoni.',
      target.name
    );

    const getContentTypeOptions = contentTypes => {
      return typeof contentType === 'undefined'
        ? deriveContentTypeOptions(target.name, contentTypes)
        : contentTypes[contentType];
    };

    function mapStateToProps (state, ownProps) {
      const { contentTypes } = state.$$pepperoni.config;

      const contentTypeOpts = getContentTypeOptions(contentTypes);
      const nameSingular = contentTypeOpts.name[Plurality.SINGULAR];
      const namePlural = contentTypeOpts.name[Plurality.PLURAL];

      const subjectId = ownProps[routeParamsPropName][routeParamSubjectKey];
      const contentTypeCollection = state.$$pepperoni.entities[namePlural];

      let back;

      if (routeParamSubjectKey != 'id') {
        back = find(contentTypeCollection, a => a[routeParamSubjectKey] === subjectId);
      } else {
        back = contentTypeCollection ? contentTypeCollection[subjectId] : null;
      }

      return {
        $$pepperoni: state.$$pepperoni,
        [nameSingular]: back
      };
    }

    class PepperoniComponentWrapper extends Component {
      componentWillMount () {
        const { contentTypes } = this.props.$$pepperoni.config;
        const entities = this.props.$$pepperoni.entities;

        const params = this.props[routeParamsPropName];
        const subjectId = params[routeParamSubjectKey];

        const contentTypeOpts = getContentTypeOptions(contentTypes);
        const namePlural = contentTypeOpts.name[Plurality.PLURAL];
        const canonicalName = contentTypeOpts.name.canonical;

        if (
          !entities[namePlural] ||
          entities[namePlural] && !entities[namePlural][subjectId]
        ) {
          this.props.dispatch(
            createRequest(canonicalName, subjectId, { params })
          );
        }
      }

      render () {
        return React.createElement(target, this.props);
      }
    }

    PepperoniComponentWrapper.__pepperoni = true;

    PepperoniComponentWrapper.fetchData = (renderProps, store) => {
      const contentTypeOptions = getContentTypeOptions(store.$$pepperoni.config.contentTypes);
      return [
        [fetchResource, {
          contentType: contentTypeOptions.name.canonical,
          subject: renderProps[routeParamsPropName][routeParamSubjectKey]
        }]
      ];
    };

    return reduxConnect(mapStateToProps)(PepperoniComponentWrapper);
  };
};
<|endoftext|>


<commit_before>(function($) {

"use strict";

if (Echo.Utils.isComponentDefined("Echo.Control")) return;

// TODO: replace "Plugins" with different name to avoid conflict with e2 scripts
if (!Echo.Plugins) Echo.Plugins = {};

Echo.Control = function() {};

// static interface

Echo.Control.create = function(manifest) {
	var control = Echo.Utils.getNestedValue(window, manifest.name);
	// prevent multiple re-definitions
	if (control) return control;
	var _constructor = function(config) {
		var self = this;
		if (!config || !config.target) return;
		this.manifest = manifest;
		this.init([
			"vars",
			"extension",
			["config", config],
			"events",
			"labels",
			"css",
			"renderers",
			["user", function() {
				self.init([["plugins", manifest.constructor]]);
			}]
		]);
	};
	Echo.Utils.inherit(_constructor, manifest.inherits || Echo.Control);
	if (manifest.methods) {
		$.extend(_constructor.prototype, manifest.methods);
	}
	_constructor.prototype.templates = manifest.templates;
	Echo.Utils.setNestedValue(window, manifest.name, _constructor);
	return _constructor;
};

Echo.Control.skeleton = function(name) {
	return {
		"name": name,
		"config": {},
		"labels": {},
		"events": {},
		"methods": {},
		"renderers": {},
		"templates": {},
		"constructor": function(){ this.render(); }
	};
};

// dynamic interface (available for class instances)

Echo.Control.prototype.substitute = function(template, data) {
	var control = this;
	var name = control._cssClassFromControlName();
	var extract = function(value) {
		return $.isFunction(value) ? value() : value;
	};
	var instructions = {
		"class": function(value) {
			return name + "-" + value;
		},
		"data": function(key) {
			return Echo.Utils.getNestedValue(data, key, "");
		},
		"label": function(key) {
			return control.labels.get(key, "");
		},
		"self": function(key) {
			var value = Echo.Utils.getNestedValue(control, key, function() {
				var _value = Echo.Utils.getNestedValue(control.data, key,
					function() { return control.config.get(key, ""); });
				return extract(_value);
			});
			return extract(value);
		}
	};
	var processor = function(match, key, value) {
		return instructions[key] ? instructions[key](value) : match;
	};
	return template.replace(Echo.Vars.regexps.templateSubstitution, processor);
};

Echo.Control.prototype.compileTemplate = function(template, data, transformations) {
	var control = this, templates = {};
	var cssPrefix = this._cssClassFromControlName() + "-";
	templates.raw = $.isFunction(template) ? template.call(this) : template;
	templates.processed = this.substitute(templates.raw, data || {});
	if (transformations) {
		templates.dom = $("<div/>").html(templates.processed);
		$.map(transformations, function(transformation) {
			templates.dom = control._templateTransformer.call(control, {
				"data": data || {},
				"template": templates.dom,
				"transformation": transformation
			});
		});
		templates.processed = templates.dom.html();
	}
	return Echo.Utils.toDOM(templates.processed, cssPrefix, function() {
		control.render.apply(control, arguments);
	});
};

Echo.Control.prototype.render = function(name, element, dom, extra) {
	// render specific element
	if (name) {
		var renderer = this.extension.renderers[name];
		if (renderer) {
			var iteration = 0;
			renderer.next = function() {
				renderer.functions[++iteration].apply(this, arguments);
			};
			renderer.functions[iteration].apply(this, [element, dom, extra]);
		}
		return;
	}

	// render the whole control
	this.dom = this.compileTemplate(this.template, this.data || {}, this.extension.template);
	this.config.get("target")
		.addClass(this._cssClassFromControlName())
		.empty()
		.append(this.dom.content);
	this.events.publish({"topic": "onRender"});
	return this.dom.content;
};

Echo.Control.prototype.rerender = function(config) {
	var control = this;
	config = config || {};

	// no DOM available yet, nothing to rerender -> exit
	if (!this.dom) return;

	// rerender the whole control
	if ($.isEmptyObject(config)) {
		if (this.dom) {
			this.dom.content.replaceWith(this.render());
			this.events.publish({"topic": "onRerender"});
		}
		return;
	}

	// if the list of elements passed, call rerender for each element
	if (config.elements) {
		$.map(config.elements, function(element) {
			var _config = $.extend({}, config, {"element": element, "elements": null});
			control.rerender(_config);
		});
		return;
	}

	// exit if no element found or we have unexpected type of argument
	if (!config.element || !this.dom.get(config.element)) return;

	if (config.recursive) {
		var cssPrefix = this._cssClassFromControlName() + "-";
		var template = $.isFunction(this.template) ? this.template() : this.template;
		var html = this.substitute(template, this.data || {});
		var newNode = $("." + cssPrefix + config.element, $(html));
		var oldNode = this.dom.get(config.element);
		newNode = Echo.Utils.toDOM(newNode, cssPrefix, function(name, element, dom) {
			control.render.apply(control, [{
				"element": name,
				"args": [element, dom]
			}]);
		}).content;
		oldNode.replaceWith(newNode);
	} else {
		this.render.apply(control, [{
			"element": name,
			"args": [this.dom.get(name), this.dom]
		}]);
	}
};

Echo.Control.prototype.parentRenderer = function(name, args) {
	var renderer = this.extension.renderers[name];
	if (!renderer || !renderer.next) return args[0]; // return DOM element
	renderer.next.apply(this, args);
};

Echo.Control.prototype.refresh = function() {
	// TODO: develop unified refresh mechanism
	this.events.publish({"topic": "onRefresh"});
};

Echo.Control.prototype.extendTemplate = function(html, action, anchor) {
	this.extension.template.push({"html": html, "action": action, "anchor": anchor});
};

Echo.Control.prototype.extendRenderer = function() {
	this.init.renderer.apply(this, arguments);
};

Echo.Control.prototype.template = function() {
	return this.templates.main;
};

Echo.Control.prototype.init = function(subsystems) {
	var control = this;
	$.map(subsystems, function(args) {
		if (!$.isArray(args)) {
			args = [args];
		}
		var name = args.shift();
		var result = control.init[name].apply(control, args);
		if (typeof result != "undefined") {
			control[name] = result;
		}
	});
};

// plugins-related functions

Echo.Control.prototype.enablePlugin = function(name) {
	this.config.set("plugins." + name + ".enabled", true);
};

Echo.Control.prototype.disablePlugin = function(name) {
	this.config.set("plugins." + name + ".enabled", false);
};

Echo.Control.prototype.isPluginEnabled = function(name) {
	return this.config.get("plugins." + name + ".enabled", true);
};

Echo.Control.prototype.isPluginApplicable = function(plugin) {
	var self = this, applicable = false;
	$.each(plugin.manifest.applications || [], function(i, application) {
		var component = Echo.Utils.getNestedValue(window, application);
		if (component && self instanceof component) {
			applicable = true;
			return false; // break
		}
	});
	return applicable;
};

Echo.Control.prototype.destroy = function() {};

// internal functions

Echo.Control.prototype.init.vars = function() {
	return {"cache": {}};
};

Echo.Control.prototype.init.extension = function() {
	return {"renderers": {}, "template": []};
};

Echo.Control.prototype.init.config = function(data) {
	var _normalizer = {};
	_normalizer.target = $;
	_normalizer.plugins = function(list) {
		var data = Echo.Utils.foldl({"hash": {}, "order": []}, list || [],
			function(plugin, acc) {
				var pos = $.inArray(plugin.name, acc.order);
				if (pos >= 0) {
					acc.order.splice(pos, 1);
				}
				acc.order.push(plugin.name);
				acc.hash[plugin.name] = plugin;
			});
		this.set("pluginsOrder", data.order);
		return data.hash;
	};
	data = $.extend({"plugins": []}, data || {});
	var protocol = window.location.protocol == "http:" ? "http:" : "https:";
	var defaults = $.extend({
		"appkey": "",
		"apiBaseURL": protocol + "//api.echoenabled.com",
		"debug": false,
		// TODO: need to handle the situation when the app
		//       was initialized from another app, the context should
		//       be constructed in a special way in this case
		"context": Echo.Utils.getUniqueString()
	}, this.manifest.config || {});
	// TODO: find better home for normalizer...
	var normalizer = this.manifest.config.normalizer;
	return new Echo.Configuration(data, defaults, function(key, value) {
		var handler = normalizer && normalizer[key] || _normalizer && _normalizer[key];
		return handler ? handler.call(this, value) : value;
	});
};

Echo.Control.prototype.init.events = function() {
	var control = this;
	var events = {
		"prepare": function(params) {
			params.topic = control.manifest.name + "." + params.topic;
			params.context = control.config.get("context");
			params.callback = $.proxy(params.callback, control);
			return params;
		},
		"publish": function(params) {
			Echo.Events.publish(events.prepare(params));
		},
		"subscribe": function(params) {
			Echo.Events.subscribe(events.prepare(params));
		},
		"unsubscribe": Echo.Events.unsubscribe
	};
	$.each(control.manifest.events, function(topic, callback) {
		events.subscribe({
			"topic": topic,
			"handler": $.proxy(callback, control)
		});
	});
	return events;
};

Echo.Control.prototype.init.labels = function() {
	return new Echo.Labels(this.manifest.labels, this.name);
};

Echo.Control.prototype.init.css = function() {
	Echo.Utils.addCSS(this.baseCSS, 'control');
	if (!this.manifest.css) return;
	Echo.Utils.addCSS(this.substitute(this.manifest.css), this.manifest.name);
};

Echo.Control.prototype.init.renderer = function(name, renderer) {
	var renderers = this.extension.renderers;
	renderers[name] = renderers[name] || {"functions": []};
	renderers[name].functions.unshift(renderer);
};

Echo.Control.prototype.init.renderers = function() {
	var control = this;
	$.each(this.manifest.renderers, function() {
		control.init.renderer.apply(control, arguments);
	});
};

Echo.Control.prototype.init.user = function(callback) {
	var control = this;
	return Echo.UserSession({
		"appkey": this.config.get("appkey"),
		"ready": $.proxy(callback, control)
	});
};

Echo.Control.prototype.init.plugins = function(callback) {
	var control = this;
	control.plugins = {};
	this._loadPluginsDependencies(function() {
		$.map(control.config.get("pluginsOrder"), function(name) {
			var plugin = Echo.Plugins[name];
			if (plugin && control.isPluginApplicable(plugin)) {
				control.plugins[name] = new plugin({
					"component": control
				});
			}
		});
		callback && callback.call(this);
	});
};

// TODO: define this function later, need to select loader first
Echo.Control.prototype._loadPluginsDependencies = function(callback) {
	var plugins = this.config.get("pluginsOrder");
	var scripts = Echo.Utils.foldl([], plugins, function(name, acc) {
		var plugin = Echo.Plugins[name];
		if (plugin && plugin.dependencies && plugin.dependencies.length) {
			return acc.concat(plugin.dependencies);
		}
	});
	// TODO: need to load "scripts" and execute callback after that
	callback && callback.call(this);
};

Echo.Control.prototype._cssClassFromControlName = function() {
	return this.manifest.name.toLowerCase().replace(/-/g, "").replace(/\./g, "-");
};

Echo.Control.prototype._templateTransformer = function(args) {
	var classify = {
		"insertBefore": "before",
		"insertAfter": "after",
		"insertAsFirstChild": "prepend",
		"insertAsLastChild": "append",
		"replace": "replaceWith"
	};
	var action = classify[args.transformation.action];
	if (!action) return args.template;
	var html = args.transformation.html;
	var anchor = args.transformation.anchor;
	var content = $.isFunction(html) ? html() : html;
	$("." + anchor, args.template)[action](this.substitute(content, args.data));
	return args.template;
};

Echo.Control.prototype.baseCSS =
	'.echo-primaryBackgroundColor {  }' +
	'.echo-secondaryBackgroundColor { background-color: #F4F4F4; }' +
	'.echo-trinaryBackgroundColor { background-color: #ECEFF5; }' +
	'.echo-primaryColor { color: #3A3A3A; }' +
	'.echo-secondaryColor { color: #C6C6C6; }' +
	'.echo-primaryFont { font-family: Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 16px; }' +
	'.echo-secondaryFont { font-family: Arial, sans-serif; font-size: 11px; }' +
	'.echo-linkColor, .echo-linkColor a { color: #476CB8; }' +
	'.echo-clickable { cursor: pointer; }' +
	'.echo-relative { position: relative; }' +
	'.echo-clear { clear: both; }';

})(jQuery);
<commit_msg>rerender function inside Echo.Control component updated according to the new contract<commit_after>(function($) {

"use strict";

if (Echo.Utils.isComponentDefined("Echo.Control")) return;

// TODO: replace "Plugins" with different name to avoid conflict with e2 scripts
if (!Echo.Plugins) Echo.Plugins = {};

Echo.Control = function() {};

// static interface

Echo.Control.create = function(manifest) {
	var control = Echo.Utils.getNestedValue(window, manifest.name);
	// prevent multiple re-definitions
	if (control) return control;
	var _constructor = function(config) {
		var self = this;
		if (!config || !config.target) return;
		this.manifest = manifest;
		this.init([
			"vars",
			"extension",
			["config", config],
			"events",
			"labels",
			"css",
			"renderers",
			["user", function() {
				self.init([["plugins", manifest.constructor]]);
			}]
		]);
	};
	Echo.Utils.inherit(_constructor, manifest.inherits || Echo.Control);
	if (manifest.methods) {
		$.extend(_constructor.prototype, manifest.methods);
	}
	_constructor.prototype.templates = manifest.templates;
	Echo.Utils.setNestedValue(window, manifest.name, _constructor);
	return _constructor;
};

Echo.Control.skeleton = function(name) {
	return {
		"name": name,
		"config": {},
		"labels": {},
		"events": {},
		"methods": {},
		"renderers": {},
		"templates": {},
		"constructor": function(){ this.render(); }
	};
};

// dynamic interface (available for class instances)

Echo.Control.prototype.substitute = function(template, data) {
	var control = this;
	var name = control._cssClassFromControlName();
	var extract = function(value) {
		return $.isFunction(value) ? value() : value;
	};
	var instructions = {
		"class": function(value) {
			return name + "-" + value;
		},
		"data": function(key) {
			return Echo.Utils.getNestedValue(data, key, "");
		},
		"label": function(key) {
			return control.labels.get(key, "");
		},
		"self": function(key) {
			var value = Echo.Utils.getNestedValue(control, key, function() {
				var _value = Echo.Utils.getNestedValue(control.data, key,
					function() { return control.config.get(key, ""); });
				return extract(_value);
			});
			return extract(value);
		}
	};
	var processor = function(match, key, value) {
		return instructions[key] ? instructions[key](value) : match;
	};
	return template.replace(Echo.Vars.regexps.templateSubstitution, processor);
};

Echo.Control.prototype.compileTemplate = function(template, data, transformations) {
	var control = this, templates = {};
	var cssPrefix = this._cssClassFromControlName() + "-";
	templates.raw = $.isFunction(template) ? template.call(this) : template;
	templates.processed = this.substitute(templates.raw, data || {});
	if (transformations) {
		templates.dom = $("<div/>").html(templates.processed);
		$.map(transformations, function(transformation) {
			templates.dom = control._templateTransformer.call(control, {
				"data": data || {},
				"template": templates.dom,
				"transformation": transformation
			});
		});
		templates.processed = templates.dom.html();
	}
	return Echo.Utils.toDOM(templates.processed, cssPrefix, function() {
		control.render.apply(control, arguments);
	});
};

Echo.Control.prototype.render = function(name, element, dom, extra) {
	// render specific element
	if (name) {
		var renderer = this.extension.renderers[name];
		if (renderer) {
			var iteration = 0;
			renderer.next = function() {
				renderer.functions[++iteration].apply(this, arguments);
			};
			renderer.functions[iteration].apply(this, [element, dom, extra]);
		}
		return;
	}

	// render the whole control
	this.dom = this.compileTemplate(this.template, this.data || {}, this.extension.template);
	this.config.get("target")
		.addClass(this._cssClassFromControlName())
		.empty()
		.append(this.dom.content);
	this.events.publish({"topic": "onRender"});
	return this.dom.content;
};

Echo.Control.prototype.rerender = function(name, extra) {
	var control = this;
	extra = extra || {};

	// no DOM available yet, nothing to rerender -> exit
	if (!this.dom) return;

	// rerender the whole control
	if (!name) {
		if (this.dom) {
			this.dom.content.replaceWith(this.render());
			this.events.publish({"topic": "onRerender"});
		}
		return;
	}

	// if the list of elements passed, call rerender for each element
	if ($.isArray(name)) {
		$.map(name, function(element) {
			control.rerender(element, extra);
		});
		return;
	}

	// exit if no element found
	if (!name || !this.dom.get(name)) return;

	if (extra.recursive) {
		var cssPrefix = this._cssClassFromControlName() + "-";
		var template = $.isFunction(this.template) ? this.template() : this.template;
		var html = this.substitute(template, this.data || {});
		var newNode = $("." + cssPrefix + name, $(html));
		var oldNode = this.dom.get(name);
		newNode = Echo.Utils.toDOM(newNode, cssPrefix, function(name, element, dom) {
			control.render.call(control, name, element, dom, extra);
		}).content;
		oldNode.replaceWith(newNode);
	} else {
		this.render(name, this.dom.get(name), this.dom, extra);
	}
};

Echo.Control.prototype.parentRenderer = function(name, args) {
	var renderer = this.extension.renderers[name];
	if (!renderer || !renderer.next) return args[0]; // return DOM element
	renderer.next.apply(this, args);
};

Echo.Control.prototype.refresh = function() {
	// TODO: develop unified refresh mechanism
	this.events.publish({"topic": "onRefresh"});
};

Echo.Control.prototype.extendTemplate = function(html, action, anchor) {
	this.extension.template.push({"html": html, "action": action, "anchor": anchor});
};

Echo.Control.prototype.extendRenderer = function() {
	this.init.renderer.apply(this, arguments);
};

Echo.Control.prototype.template = function() {
	return this.templates.main;
};

Echo.Control.prototype.init = function(subsystems) {
	var control = this;
	$.map(subsystems, function(args) {
		if (!$.isArray(args)) {
			args = [args];
		}
		var name = args.shift();
		var result = control.init[name].apply(control, args);
		if (typeof result != "undefined") {
			control[name] = result;
		}
	});
};

// plugins-related functions

Echo.Control.prototype.enablePlugin = function(name) {
	this.config.set("plugins." + name + ".enabled", true);
};

Echo.Control.prototype.disablePlugin = function(name) {
	this.config.set("plugins." + name + ".enabled", false);
};

Echo.Control.prototype.isPluginEnabled = function(name) {
	return this.config.get("plugins." + name + ".enabled", true);
};

Echo.Control.prototype.isPluginApplicable = function(plugin) {
	var self = this, applicable = false;
	$.each(plugin.manifest.applications || [], function(i, application) {
		var component = Echo.Utils.getNestedValue(window, application);
		if (component && self instanceof component) {
			applicable = true;
			return false; // break
		}
	});
	return applicable;
};

Echo.Control.prototype.destroy = function() {};

// internal functions

Echo.Control.prototype.init.vars = function() {
	return {"cache": {}};
};

Echo.Control.prototype.init.extension = function() {
	return {"renderers": {}, "template": []};
};

Echo.Control.prototype.init.config = function(data) {
	var _normalizer = {};
	_normalizer.target = $;
	_normalizer.plugins = function(list) {
		var data = Echo.Utils.foldl({"hash": {}, "order": []}, list || [],
			function(plugin, acc) {
				var pos = $.inArray(plugin.name, acc.order);
				if (pos >= 0) {
					acc.order.splice(pos, 1);
				}
				acc.order.push(plugin.name);
				acc.hash[plugin.name] = plugin;
			});
		this.set("pluginsOrder", data.order);
		return data.hash;
	};
	data = $.extend({"plugins": []}, data || {});
	var protocol = window.location.protocol == "http:" ? "http:" : "https:";
	var defaults = $.extend({
		"appkey": "",
		"apiBaseURL": protocol + "//api.echoenabled.com",
		"debug": false,
		// TODO: need to handle the situation when the app
		//       was initialized from another app, the context should
		//       be constructed in a special way in this case
		"context": Echo.Utils.getUniqueString()
	}, this.manifest.config || {});
	// TODO: find better home for normalizer...
	var normalizer = this.manifest.config.normalizer;
	return new Echo.Configuration(data, defaults, function(key, value) {
		var handler = normalizer && normalizer[key] || _normalizer && _normalizer[key];
		return handler ? handler.call(this, value) : value;
	});
};

Echo.Control.prototype.init.events = function() {
	var control = this;
	var events = {
		"prepare": function(params) {
			params.topic = control.manifest.name + "." + params.topic;
			params.context = control.config.get("context");
			params.callback = $.proxy(params.callback, control);
			return params;
		},
		"publish": function(params) {
			Echo.Events.publish(events.prepare(params));
		},
		"subscribe": function(params) {
			Echo.Events.subscribe(events.prepare(params));
		},
		"unsubscribe": Echo.Events.unsubscribe
	};
	$.each(control.manifest.events, function(topic, callback) {
		events.subscribe({
			"topic": topic,
			"handler": $.proxy(callback, control)
		});
	});
	return events;
};

Echo.Control.prototype.init.labels = function() {
	return new Echo.Labels(this.manifest.labels, this.name);
};

Echo.Control.prototype.init.css = function() {
	Echo.Utils.addCSS(this.baseCSS, 'control');
	if (!this.manifest.css) return;
	Echo.Utils.addCSS(this.substitute(this.manifest.css), this.manifest.name);
};

Echo.Control.prototype.init.renderer = function(name, renderer) {
	var renderers = this.extension.renderers;
	renderers[name] = renderers[name] || {"functions": []};
	renderers[name].functions.unshift(renderer);
};

Echo.Control.prototype.init.renderers = function() {
	var control = this;
	$.each(this.manifest.renderers, function() {
		control.init.renderer.apply(control, arguments);
	});
};

Echo.Control.prototype.init.user = function(callback) {
	var control = this;
	return Echo.UserSession({
		"appkey": this.config.get("appkey"),
		"ready": $.proxy(callback, control)
	});
};

Echo.Control.prototype.init.plugins = function(callback) {
	var control = this;
	control.plugins = {};
	this._loadPluginsDependencies(function() {
		$.map(control.config.get("pluginsOrder"), function(name) {
			var plugin = Echo.Plugins[name];
			if (plugin && control.isPluginApplicable(plugin)) {
				control.plugins[name] = new plugin({
					"component": control
				});
			}
		});
		callback && callback.call(this);
	});
};

// TODO: define this function later, need to select loader first
Echo.Control.prototype._loadPluginsDependencies = function(callback) {
	var plugins = this.config.get("pluginsOrder");
	var scripts = Echo.Utils.foldl([], plugins, function(name, acc) {
		var plugin = Echo.Plugins[name];
		if (plugin && plugin.dependencies && plugin.dependencies.length) {
			return acc.concat(plugin.dependencies);
		}
	});
	// TODO: need to load "scripts" and execute callback after that
	callback && callback.call(this);
};

Echo.Control.prototype._cssClassFromControlName = function() {
	return this.manifest.name.toLowerCase().replace(/-/g, "").replace(/\./g, "-");
};

Echo.Control.prototype._templateTransformer = function(args) {
	var classify = {
		"insertBefore": "before",
		"insertAfter": "after",
		"insertAsFirstChild": "prepend",
		"insertAsLastChild": "append",
		"replace": "replaceWith"
	};
	var action = classify[args.transformation.action];
	if (!action) return args.template;
	var html = args.transformation.html;
	var anchor = args.transformation.anchor;
	var content = $.isFunction(html) ? html() : html;
	$("." + anchor, args.template)[action](this.substitute(content, args.data));
	return args.template;
};

Echo.Control.prototype.baseCSS =
	'.echo-primaryBackgroundColor {  }' +
	'.echo-secondaryBackgroundColor { background-color: #F4F4F4; }' +
	'.echo-trinaryBackgroundColor { background-color: #ECEFF5; }' +
	'.echo-primaryColor { color: #3A3A3A; }' +
	'.echo-secondaryColor { color: #C6C6C6; }' +
	'.echo-primaryFont { font-family: Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 16px; }' +
	'.echo-secondaryFont { font-family: Arial, sans-serif; font-size: 11px; }' +
	'.echo-linkColor, .echo-linkColor a { color: #476CB8; }' +
	'.echo-clickable { cursor: pointer; }' +
	'.echo-relative { position: relative; }' +
	'.echo-clear { clear: both; }';

})(jQuery);
<|endoftext|>


<commit_before>class VolumeEffect {
    constructor (audioContext, volume, startSeconds, endSeconds) {
        this.audioContext = audioContext;

        this.input = this.audioContext.createGain();
        this.output = this.audioContext.createGain();

        this.gain = this.audioContext.createGain();

        // Smoothly ramp the gain up before the start time, and down after the end time.
        this.rampLength = 0.01;
        this.gain.gain.setValueAtTime(1.0, Math.max(0, startSeconds - this.rampLength));
        this.gain.gain.exponentialRampToValueAtTime(volume, startSeconds);
        this.gain.gain.setValueAtTime(volume, endSeconds);
        this.gain.gain.exponentialRampToValueAtTime(1.0, endSeconds + this.rampLength);

        this.input.connect(this.gain);
        this.gain.connect(this.output);
    }
}

export default VolumeEffect;
<commit_msg>Use a waveshaper to hard limit samples<commit_after>class VolumeEffect {
    constructor (audioContext, volume, startSeconds, endSeconds) {
        this.audioContext = audioContext;

        this.input = this.audioContext.createGain();
        this.output = this.audioContext.createGain();

        this.gain = this.audioContext.createGain();

        // Smoothly ramp the gain up before the start time, and down after the end time.
        this.rampLength = 0.01;
        this.gain.gain.setValueAtTime(1.0, Math.max(0, startSeconds - this.rampLength));
        this.gain.gain.exponentialRampToValueAtTime(volume, startSeconds);
        this.gain.gain.setValueAtTime(volume, endSeconds);
        this.gain.gain.exponentialRampToValueAtTime(1.0, endSeconds + this.rampLength);

        // Use a waveshaper node to prevent sample values from exceeding -1 or 1.
        // Without this, gain can cause samples to exceed this range, then they
        // are clipped on save, and the sound is distorted on load.
        this.waveShaper = this.audioContext.createWaveShaper();
        this.waveShaper.curve = new Float32Array([-1, 1]);
        this.waveShaper.oversample = 'none';

        this.input.connect(this.gain);
        this.gain.connect(this.waveShaper);
        this.waveShaper.connect(this.output);
    }
}

export default VolumeEffect;
<|endoftext|>


<commit_before>var _ = require('lodash');
var util = require('util');
var request = require('request');
var moment = require('moment');
var EventEmitter = require('events').EventEmitter;
var ripple = require('ripple-lib');
var sjcl = ripple.sjcl;
var check = require('check-types');
var Promise = require('bluebird')

/* --------------------------------- CONSTS --------------------------------- */

var DEBUG = true;

var REQUEST_STATUS = {
  QUEUED: 1,
  REQUESTING: 2
};

var DEFAULT_PORT = 51235

var IPP_PATTERN = /\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\:([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])\b/;

/* --------------------------------- HELPERS -------------------------------- */

function abortIfNot(expression, message) {
  if (!expression) {
    if (!DEBUG) {
      throw new Error(message);
    } else {
      console.error(message);
      process.exit();
    }
  }
}

function crawlUrl(domainOrIp) {
  return 'https://' + withDefaultPort(domainOrIp) + '/crawl';
}

function withDefaultPort(domainOrIp) {
  return domainOrIp.indexOf(':') !== -1 ? domainOrIp :
                                          domainOrIp + ':' + DEFAULT_PORT;
}

function normalizePubKey(pubKeyStr) {
  if (pubKeyStr.length > 50 && pubKeyStr[0] == 'n') {
    return pubKeyStr;
  }

  var bits = sjcl.codec.base64.toBits(pubKeyStr);
  var bytes = sjcl.codec.bytes.fromBits(bits);
  return ripple.Base.encode_check(ripple.Base.VER_NODE_PUBLIC, bytes);
}

/*
* deals with a variety of (ip, port) possibilities that occur
* in rippled responses and normalizes them to the format 'ip:port'
*/
function normalizeIpp(ip, port) {
  if (ip) {
    var split = ip.split(':'),
        splitIp = split[0],
        splitPort = split[1];

    out_ip = splitIp
    out_port = port || splitPort || DEFAULT_PORT
    var ipp = out_ip + ':' + out_port
  } else {
    throw new Error("ip is undefined")
  }

  return ipp
}

/* --------------------------------- CRAWLER -------------------------------- */

function Crawler(maxRequests, logger) {
  EventEmitter.call(this);

  // maxRequests checks
  maxRequests = maxRequests ? maxRequests : 30;
  check.assert.number(maxRequests, "Invalid max requests");
  if (maxRequests < 1) {
    throw new Error("Invalid max requests");
  }

  // logger checks
  logger = logger ? logger : console
  if (!logger.log || !logger.error) {
    throw new Error("Invalid logger");
  }
  if (typeof logger.log != "function" || typeof logger.error != "function") {
    throw new TypeError("log and error must be functions");
  }

  this.maxRequests = maxRequests;
  this.currentRequests = 0; // active requests
  this.rawResponses = {}; // {$ip_and_port : $normalised_response}
  this.queued = {}; // {$ip_and_port : REQUEST_STATUS.*}
  this.errors = {}; // {$ip_and_port : $error_code_int}
  this.peersData = {}; // {b58_normed(pubKey) : {...}}
  this.logger = logger;
}

util.inherits(Crawler, EventEmitter);

Crawler.prototype.getCrawl = function(entryIp) {
  var self = this;
  return new Promise(function(resolve, reject) {
    if (entryIp === undefined) {
      throw new Error("Invalid ip address")
    }
    if (entryIp.split(' ').length != 1 || !IPP_PATTERN.test(entryIp)) {
      throw new Error("Invalid ip address (perhaps port missing)")
    }
    self.once('done', function(response) {
      return resolve(response)
    }).enter(entryIp)
  })
}

/*
* Enter at ip to start crawl
*/
Crawler.prototype.enter = function(ip) {
  this.startTime = moment().format();
  this.entryIP = withDefaultPort(ip);
  this.crawl(withDefaultPort(ip), 0);
};

/**
* @param {String} ipp - ip and port to crawl
* @param {Number} hops - from initial entryPoint
*/
Crawler.prototype.crawl = function(ipp, hops) {
  var self = this;
  self.queued[ipp] = REQUEST_STATUS.REQUESTING;

  self.crawlOne(ipp, function(error, response, body) {
    self.dequeue(ipp);

    if (error) {
      self.logger.error(ipp, 'has error:', error);
      self.errors[ipp] = error;
    } else {
      // save raw body
      self.rawResponses[ipp] = body;

      // Normalize body and loop over each normalized peer
      body.overlay.active.forEach(function(p) {
        try {
          self.enqueueIfNeeded(normalizeIpp(p.ip, p.port));
        } catch (error) {
          self.logger.error(p.public_key, 'has error:', error);
        }
      });
    }

    // Crawl peers
    if (!self.requestMore(hops)) {
      self.endTime = moment().format();
      self.emit('done', { start:    self.startTime,
                          end:      self.endTime,
                          entry:    self.entryIP,
                          data:     self.rawResponses,
                          errors:   self.errors
                        });
    }
  });
};

/*
* Crawls over one node at ipp and retreives json
*/
Crawler.prototype.crawlOne = function(ipp, cb) {
  var self;
  this.currentRequests++;
  self = this;
  self.crawlRequest(ipp, function(err, response, json) {
    self.currentRequests--;
    self.emit('request', err, response, json);
    cb(err, response, json);
  });
};

/*
* Actually sends the request to retreive the json
*/
Crawler.prototype.crawlRequest = function(ip, onResponse) {
  var self = this;
  var options = { url: crawlUrl(ip),
                  timeout: 5000,
                  rejectUnauthorized: false,
                  requestCert: true,
                  agent: false };
  request(options, function(err, response, body) {
    onResponse(err, response, body ? JSON.parse(body) : null);
  });
}

Crawler.prototype.requestMore = function(hops) {
  var self = this;
  var ipps = Object.keys(self.queued);

  ipps.forEach(function(queuedIpp) {
    if (self.currentRequests < self.maxRequests) {
      if (self.queued[queuedIpp] === REQUEST_STATUS.QUEUED) {
        self.crawl(queuedIpp, hops + 1);
      }
    } else {
      return false;
    }
  });

  return ipps.length !== 0;
}

/*
* Enqueue if this node hasn't already been crawled
*/
Crawler.prototype.enqueueIfNeeded = function(ipp) {
  if (ipp) {
    if ((this.rawResponses[ipp] === undefined) &&
        (this.queued[ipp] === undefined) &&
        (this.errors[ipp] === undefined)) {
      this.enqueue(ipp);
    }
  }
}

Crawler.prototype.enqueue = function(ipp) {
  abortIfNot(this.queued[ipp] === undefined, 'queued already');
  this.queued[ipp] = REQUEST_STATUS.QUEUED;
};

Crawler.prototype.dequeue = function(ipp) {
  abortIfNot(this.queued[ipp] !== undefined, 'not queued already');
  delete this.queued[ipp];
};

exports.Crawler = Crawler;
exports.normalizeIpp = normalizeIpp;
exports.normalizePubKey = normalizePubKey;

<commit_msg>change src/crawler.js returned json to match new strucuture described in readme<commit_after>var _ = require('lodash');
var util = require('util');
var request = require('request');
var moment = require('moment');
var EventEmitter = require('events').EventEmitter;
var ripple = require('ripple-lib');
var sjcl = ripple.sjcl;
var check = require('check-types');
var Promise = require('bluebird')

/* --------------------------------- CONSTS --------------------------------- */

var DEBUG = true;

var REQUEST_STATUS = {
  QUEUED: 1,
  REQUESTING: 2
};

var DEFAULT_PORT = 51235

var IPP_PATTERN = /\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\:([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])\b/;

/* --------------------------------- HELPERS -------------------------------- */

function abortIfNot(expression, message) {
  if (!expression) {
    if (!DEBUG) {
      throw new Error(message);
    } else {
      console.error(message);
      process.exit();
    }
  }
}

function crawlUrl(domainOrIp) {
  return 'https://' + withDefaultPort(domainOrIp) + '/crawl';
}

function withDefaultPort(domainOrIp) {
  return domainOrIp.indexOf(':') !== -1 ? domainOrIp :
                                          domainOrIp + ':' + DEFAULT_PORT;
}

function normalizePubKey(pubKeyStr) {
  if (pubKeyStr.length > 50 && pubKeyStr[0] == 'n') {
    return pubKeyStr;
  }

  var bits = sjcl.codec.base64.toBits(pubKeyStr);
  var bytes = sjcl.codec.bytes.fromBits(bits);
  return ripple.Base.encode_check(ripple.Base.VER_NODE_PUBLIC, bytes);
}

/*
* deals with a variety of (ip, port) possibilities that occur
* in rippled responses and normalizes them to the format 'ip:port'
*/
function normalizeIpp(ip, port) {
  if (ip) {
    var split = ip.split(':'),
        splitIp = split[0],
        splitPort = split[1];

    out_ip = splitIp
    out_port = port || splitPort || DEFAULT_PORT
    var ipp = out_ip + ':' + out_port
  } else {
    throw new Error("ip is undefined")
  }

  return ipp
}

/* --------------------------------- CRAWLER -------------------------------- */

function Crawler(maxRequests, logger) {
  EventEmitter.call(this);

  // maxRequests checks
  maxRequests = maxRequests ? maxRequests : 30;
  check.assert.number(maxRequests, "Invalid max requests");
  if (maxRequests < 1) {
    throw new Error("Invalid max requests");
  }

  // logger checks
  logger = logger ? logger : console
  if (!logger.log || !logger.error) {
    throw new Error("Invalid logger");
  }
  if (typeof logger.log != "function" || typeof logger.error != "function") {
    throw new TypeError("log and error must be functions");
  }

  this.maxRequests = maxRequests;
  this.currentRequests = 0; // active requests
  this.queued = {}; // {$ip_and_port : REQUEST_STATUS.*}
  this.done = {}; // {$ip_and_port : 1}
  this.rawResponses = []; // [{$ip_and_port : $raw_response}]
  this.errors = []; // {$ip_and_port/public_key : $error_object}
  this.logger = logger;
}

util.inherits(Crawler, EventEmitter);

Crawler.prototype.getCrawl = function(entryIp) {
  var self = this;
  return new Promise(function(resolve, reject) {
    if (entryIp === undefined) {
      throw new Error("Invalid ip address")
    }
    if (entryIp.split(' ').length != 1 || !IPP_PATTERN.test(entryIp)) {
      throw new Error("Invalid ip address (perhaps port missing)")
    }
    self.once('done', function(response) {
      return resolve(response)
    }).enter(entryIp)
  })
}

/*
* Enter at ip to start crawl
*/
Crawler.prototype.enter = function(ip) {
  this.startTime = moment().format();
  this.entryIP = withDefaultPort(ip);
  this.crawl(withDefaultPort(ip), 0);
};

/**
* @param {String} ipp - ip and port to crawl
* @param {Number} hops - from initial entryPoint
*/
Crawler.prototype.crawl = function(ipp, hops) {
  var self = this;
  self.queued[ipp] = REQUEST_STATUS.REQUESTING;

  self.crawlOne(ipp, function(error, response, body) {
    self.dequeue(ipp);

    if (error) {
      // save error
      var err = {}
      err[ipp] = error;
      self.errors.push(err);

      // log error
      self.logger.error(ipp, 'has error:', error);
    } else {

      // mark ipp as done (received response)
      self.done[ipp] = 1;

      // save raw body
      var resp = {};
      resp[ipp] = body;
      self.rawResponses.push(resp);

      // Normalize body and loop over each normalized peer
      body.overlay.active.forEach(function(p) {
        try {
          self.enqueueIfNeeded(normalizeIpp(p.ip, p.port));
        } catch (error) {
          self.logger.error(p.public_key, 'has error:', error);
        }
      });
    }

    // Crawl peers
    if (!self.requestMore(hops)) {
      self.endTime = moment().format();
      self.emit('done', { start:    self.startTime,
                          end:      self.endTime,
                          entry:    self.entryIP,
                          data:     self.rawResponses,
                          errors:   self.errors
                        });
    }
  });
};

/*
* Crawls over one node at ipp and retreives json
*/
Crawler.prototype.crawlOne = function(ipp, cb) {
  var self;
  this.currentRequests++;
  self = this;
  self.crawlRequest(ipp, function(err, response, json) {
    self.currentRequests--;
    self.emit('request', err, response, json);
    cb(err, response, json);
  });
};

/*
* Actually sends the request to retreive the json
*/
Crawler.prototype.crawlRequest = function(ip, onResponse) {
  var self = this;
  var options = { url: crawlUrl(ip),
                  timeout: 5000,
                  rejectUnauthorized: false,
                  requestCert: true,
                  agent: false };
  request(options, function(err, response, body) {
    onResponse(err, response, body ? JSON.parse(body) : null);
  });
}

Crawler.prototype.requestMore = function(hops) {
  var self = this;
  var ipps = Object.keys(self.queued);

  ipps.forEach(function(queuedIpp) {
    if (self.currentRequests < self.maxRequests) {
      if (self.queued[queuedIpp] === REQUEST_STATUS.QUEUED) {
        self.crawl(queuedIpp, hops + 1);
      }
    } else {
      return false;
    }
  });

  return ipps.length !== 0;
}

/*
* Enqueue if this node hasn't already been crawled
*/
Crawler.prototype.enqueueIfNeeded = function(ipp) {
  if (ipp) {
    if ((this.done[ipp] === undefined) &&
        (this.queued[ipp] === undefined) &&
        (this.errors[ipp] === undefined)) {
      this.enqueue(ipp);
    }
  }
}

Crawler.prototype.enqueue = function(ipp) {
  abortIfNot(this.queued[ipp] === undefined, 'queued already');
  this.queued[ipp] = REQUEST_STATUS.QUEUED;
};

Crawler.prototype.dequeue = function(ipp) {
  abortIfNot(this.queued[ipp] !== undefined, 'not queued already');
  delete this.queued[ipp];
};

exports.Crawler = Crawler;
exports.normalizeIpp = normalizeIpp;
exports.normalizePubKey = normalizePubKey;

<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
// crawler - crawls a leto setup for template parameters
//////////////////////////////////////////////////////////////////////////
//
// Main script!
/* ----------------------------------------------------------------------
													Object Structures
-------------------------------------------------------------------------

*/
//////////////////////////////////////////////////////////////////////////
// Requires
var maker = require("maker").createMaker(),
	async = require("async"),
	fs = require("fs");

//////////////////////////////////////////////////////////////////////////
// Namespace (lol)
var SHOW_DEBUG_PRINTS = true;
var log = function( a ) { if(SHOW_DEBUG_PRINTS) console.log(a); };	// A log function we can turn off

//////////////////////////////////////////////////////////////////////////
// Constructor
function crawler() {
	var _this = this;

} // end crawler()

//////////////////////////////////////////////////////////////////////////
// Crawls through the leto setup and pulls out all of our
crawler.prototype.crawl = function( source, leto_setup, callback ) {
	var _this = this;

	var procedure = leto_setup.procedure,
		templateParams = {},
		oldParams = {},
		alreadyHadParamsFile = false,
		movingPlanPaths = [],
		movingPlans = {},
		asyncCallQueue = [];

	// load the previously existing params file
	try {
		oldParams = require( source + "/leto_params.json" );
		alreadyHadParamsFile = true;
	} catch( err ) {
		log( "Couldn't find leto_params.json, generating one from scratch. You can open it and add comments to add tooltips to template parameters in the registry" );
	}

	function requireFromString(src, filename) {
	    var m = new module.constructor();
	    m._compile(src, filename);
	    return m.exports;
	}

	function getReplaceParams( step ) {
		return function( cb ) {
			for( var iKeyword in step.keywords ) {
				var paramName = step.keywords[iKeyword];
				templateParams[iKeyword] = oldParams[paramName] || "";
			}

			cb();
		}
	}

	function getChangeParams( step ) {
		return function( cb ) {
			for( var iChange in step.changes ) {
				for( var iArg in step.changes[iChange].args ) {
					var argContents = step.changes[iChange].args[iArg];
					if( typeof(argContents) != "string" )
						continue;

					var argParams = maker.getTemplateParams( argContents );
					for( var iParam in argParams ) {
						var pathParamName = argParams[iParam];
						templateParams[pathParamName] = oldParams[pathParamName] || "";
					}
				}
			}

			cb();
		}
	}

	function getTemplateParams( step ) {
		return function( cb ) {

			maker.loadTemplateDir( source + "/" + step.sourcedir, function(templates) {

				for( var iTemplate in step.templates ) {

					// Load params from the destination paths
					var destPathParams = maker.getTemplateParams( step.templates[iTemplate].dest );
					for( var iParam in destPathParams ) {
						var pathParamName = destPathParams[iParam];
						templateParams[pathParamName] = oldParams[pathParamName] || "";
					}

					// Load params from the template file contents
					var templateObj = maker.getTemplate( step.templates[iTemplate].name );

					var fileContentsParams = maker.getTemplateParams( templateObj );
					for( var iParam in fileContentsParams ) {
						var fileParamName = fileContentsParams[iParam];
						templateParams[fileParamName] = oldParams[fileParamName] || "";
					}
				}

				cb();
			});
		}
	}

	function getMoveParams( step ) {
		return function( cb ) {

			function getFolderParamsRecursive( folder ) {

				for( var iChild in folder ) {
					if( iChild == "files" ) {
						for( var iFilePath in folder[iChild] ) {
							var params = maker.getTemplateParams( folder[iChild][iFilePath] );
							for( var iParam in params ) {
								var paramName = params[iParam];
								templateParams[paramName] = oldParams[paramName] || "";
							}
						}
					} else {
						getFolderParamsRecursive( folder[iChild] );
					}
				}
			}

			var movingPlanPath = source + "/" + step.plan;

			try {
				getFolderParamsRecursive( require(movingPlanPath) );
			} catch( err ) {
				log( "Crawl failed: Couldn't load your moving plan: " + movingPlanPath );
				log( err );
				cb( err );
			}

			cb();
		}
	}

	function getExecuteParams( step ) {

		return function( cb ) {
			cb();
		}
	}

	function writeFile( filePath, object ) {

		var error = fs.writeFileSync( filePath, object );

	    if( error ) {
	        log( error );
	    }

	    callback();
	}

	function getFunctionsParams() {
		try {
			// Pull all template parameters our of the functions file
			var strFunctionsFule = fs.readFileSync( source + "/" + leto_setup.functions, 'utf8' );
		} catch ( err ) {
			console.log( "Failed to load functions file at path: " + source + "/" + leto_setup.functions );
			console.log( err );
		}

		var functionsParams = maker.getTemplateParams( strFunctionsFule );

		// Add these parameters to the list
		for( var iParam in functionsParams ) {
			var functionsParamName = functionsParams[iParam];
			templateParams[functionsParamName] = oldParams[functionsParamName] || "";
		}

		// Users can override template parametrs using the exports from
		// the overrides file, so we need to compile the file and require it,
		// and then see what it overrides. To be able to compile it, lets templatize
		// it with some random strings.
		var fakeContents = {};
		for( iParam in functionsParams ) {
			fakeContents[functionsParams[iParam]] = Math.random().toString(36).substring(7);
		}

		// Templatize the string contents
		overrideTemplate = maker.renderTemplateToString( maker.template(strFunctionsFule, fakeContents) );

		// Require the newly templatized string
		var templatedModule = requireFromString( overrideTemplate );

		// Delete contents that are being overridden
		for( var iParam in templatedModule ) {
			delete templateParams[iParam];
		}
	}

	function runCrawlSeries( finishedCallback ) {

		// Grab our setup procedure from the recently cloned repo
		var procedure = leto_setup.procedure;

		for( var iStep=0; iStep<procedure.length; ++iStep ) {

			switch( procedure[iStep].type ) {
				case "replace":
				asyncCallQueue.push( getReplaceParams(procedure[iStep]) );
			  	break;
		  	case "template":
				asyncCallQueue.push( getTemplateParams(procedure[iStep]) );
			  	break;
		  	case "move":
				asyncCallQueue.push( getMoveParams(procedure[iStep]) );
			  	break;
		  	case "execute":
				asyncCallQueue.push( getExecuteParams(procedure[iStep]) );
			  	break;
			  case "change":
				asyncCallQueue.push( getChangeParams(procedure[iStep]) );
			  	break;

				default:
			  	console.log( "Step type " + procedure[iStep].type + " not recognized" );
			}
		}

		// Run our async call queue
		async.series( asyncCallQueue, finishedCallback );

	}

	var hasTemplateStep = false;
	for( var iStep in procedure ) {
		if( procedure[iStep].type == "template" )
			hasTemplateStep = true;
	}

	runCrawlSeries( function(err) {

		// Grab parameters from our functions file if it exists
		if( leto_setup.functions != undefined )
			getFunctionsParams();

		var strGypObject = JSON.stringify( templateParams, null, 4 );

		writeFile( source + "/leto_params.json", strGypObject );

		console.log( "\nThe following params were found: " );
		for( var iParam in templateParams )
			console.log( iParam )
	});

} // end crawl()

exports.crawler = crawler;<commit_msg>Make sure we remove params that we override with externals<commit_after>//////////////////////////////////////////////////////////////////////////
// crawler - crawls a leto setup for template parameters
//////////////////////////////////////////////////////////////////////////
//
// Main script!
/* ----------------------------------------------------------------------
													Object Structures
-------------------------------------------------------------------------

*/
//////////////////////////////////////////////////////////////////////////
// Requires
var maker = require("maker").createMaker(),
	async = require("async"),
	fs = require("fs");

//////////////////////////////////////////////////////////////////////////
// Namespace (lol)
var SHOW_DEBUG_PRINTS = true;
var log = function( a ) { if(SHOW_DEBUG_PRINTS) console.log(a); };	// A log function we can turn off

//////////////////////////////////////////////////////////////////////////
// Constructor
function crawler() {
	var _this = this;

} // end crawler()

//////////////////////////////////////////////////////////////////////////
// Crawls through the leto setup and pulls out all of our
crawler.prototype.crawl = function( source, leto_setup, callback ) {
	var _this = this;

	var procedure = leto_setup.procedure,
		templateParams = {},
		oldParams = {},
		alreadyHadParamsFile = false,
		movingPlanPaths = [],
		movingPlans = {},
		asyncCallQueue = [];

	// load the previously existing params file
	try {
		oldParams = require( source + "/leto_params.json" );
		alreadyHadParamsFile = true;
	} catch( err ) {
		log( "Couldn't find leto_params.json, generating one from scratch. You can open it and add comments to add tooltips to template parameters in the registry" );
	}

	function requireFromString(src, filename) {
	    var m = new module.constructor();
	    m._compile(src, filename);
	    return m.exports;
	}

	function getReplaceParams( step ) {
		return function( cb ) {
			for( var iKeyword in step.keywords ) {
				var paramName = step.keywords[iKeyword];
				templateParams[iKeyword] = oldParams[paramName] || "";
			}

			cb();
		}
	}

	function getChangeParams( step ) {
		return function( cb ) {
			for( var iChange in step.changes ) {
				for( var iArg in step.changes[iChange].args ) {
					var argContents = step.changes[iChange].args[iArg];
					if( typeof(argContents) != "string" )
						continue;

					var argParams = maker.getTemplateParams( argContents );
					for( var iParam in argParams ) {
						var pathParamName = argParams[iParam];
						templateParams[pathParamName] = oldParams[pathParamName] || "";
					}
				}
			}

			cb();
		}
	}

	function getTemplateParams( step ) {
		return function( cb ) {

			maker.loadTemplateDir( source + "/" + step.sourcedir, function(templates) {

				for( var iTemplate in step.templates ) {

					// Load params from the destination paths
					var destPathParams = maker.getTemplateParams( step.templates[iTemplate].dest );
					for( var iParam in destPathParams ) {
						var pathParamName = destPathParams[iParam];
						templateParams[pathParamName] = oldParams[pathParamName] || "";
					}

					// Load params from the template file contents
					var templateObj = maker.getTemplate( step.templates[iTemplate].name );

					var fileContentsParams = maker.getTemplateParams( templateObj );
					for( var iParam in fileContentsParams ) {
						var fileParamName = fileContentsParams[iParam];
						templateParams[fileParamName] = oldParams[fileParamName] || "";
					}
				}

				cb();
			});
		}
	}

	function getMoveParams( step ) {
		return function( cb ) {

			function getFolderParamsRecursive( folder ) {

				for( var iChild in folder ) {
					if( iChild == "files" ) {
						for( var iFilePath in folder[iChild] ) {
							var params = maker.getTemplateParams( folder[iChild][iFilePath] );
							for( var iParam in params ) {
								var paramName = params[iParam];
								templateParams[paramName] = oldParams[paramName] || "";
							}
						}
					} else {
						getFolderParamsRecursive( folder[iChild] );
					}
				}
			}

			var movingPlanPath = source + "/" + step.plan;

			try {
				getFolderParamsRecursive( require(movingPlanPath) );
			} catch( err ) {
				log( "Crawl failed: Couldn't load your moving plan: " + movingPlanPath );
				log( err );
				cb( err );
			}

			cb();
		}
	}

	function getExecuteParams( step ) {

		return function( cb ) {
			cb();
		}
	}

	function writeFile( filePath, object ) {

		var error = fs.writeFileSync( filePath, object );

	    if( error ) {
	        log( error );
	    }

	    callback();
	}

	function getExternalsParams() {
		// Delete contents that are being overridden
		for( var iExternal in leto_setup.externals ) {
			delete templateParams[iExternal];
		}
	}

	function getFunctionsParams() {
		try {
			// Pull all template parameters our of the functions file
			var strFunctionsFule = fs.readFileSync( source + "/" + leto_setup.functions, 'utf8' );
		} catch ( err ) {
			console.log( "Failed to load functions file at path: " + source + "/" + leto_setup.functions );
			console.log( err );
		}

		var functionsParams = maker.getTemplateParams( strFunctionsFule );

		// Add these parameters to the list
		for( var iParam in functionsParams ) {
			var functionsParamName = functionsParams[iParam];
			templateParams[functionsParamName] = oldParams[functionsParamName] || "";
		}

		// Users can override template parametrs using the exports from
		// the overrides file, so we need to compile the file and require it,
		// and then see what it overrides. To be able to compile it, lets templatize
		// it with some random strings.
		var fakeContents = {};
		for( iParam in functionsParams ) {
			fakeContents[functionsParams[iParam]] = Math.random().toString(36).substring(7);
		}

		// Templatize the string contents
		overrideTemplate = maker.renderTemplateToString( maker.template(strFunctionsFule, fakeContents) );

		// Require the newly templatized string
		var templatedModule = requireFromString( overrideTemplate );

		// Delete contents that are being overridden
		for( var iParam in templatedModule ) {
			delete templateParams[iParam];
		}
	}

	function runCrawlSeries( finishedCallback ) {

		// Grab our setup procedure from the recently cloned repo
		var procedure = leto_setup.procedure;

		for( var iStep=0; iStep<procedure.length; ++iStep ) {

			switch( procedure[iStep].type ) {
				case "replace":
				asyncCallQueue.push( getReplaceParams(procedure[iStep]) );
			  	break;
		  	case "template":
				asyncCallQueue.push( getTemplateParams(procedure[iStep]) );
			  	break;
		  	case "move":
				asyncCallQueue.push( getMoveParams(procedure[iStep]) );
			  	break;
		  	case "execute":
				asyncCallQueue.push( getExecuteParams(procedure[iStep]) );
			  	break;
			  case "change":
				asyncCallQueue.push( getChangeParams(procedure[iStep]) );
			  	break;

				default:
			  	console.log( "Step type " + procedure[iStep].type + " not recognized" );
			}
		}

		// Run our async call queue
		async.series( asyncCallQueue, finishedCallback );

	}

	var hasTemplateStep = false;
	for( var iStep in procedure ) {
		if( procedure[iStep].type == "template" )
			hasTemplateStep = true;
	}

	runCrawlSeries( function(err) {

		// Grab parameters from our functions file if it exists
		if( leto_setup.functions != undefined )
			getFunctionsParams();

		// Grab parameters from our externals file if they exist
		if( leto_setup.externals != undefined )
			getExternalsParams();

		var strGypObject = JSON.stringify( templateParams, null, 4 );

		writeFile( source + "/leto_params.json", strGypObject );

		console.log( "\nThe following params were found: " );
		for( var iParam in templateParams )
			console.log( iParam )
	});

} // end crawl()

exports.crawler = crawler;<|endoftext|>