if (typeof ogs === "undefined") {
    var ogs = {};
}

ogs.namespace = function(ns_string) {
    "use strict";
    var parts = ns_string.split("."), parent = ogs, i;
    if (parts[0] === "ogs") {
        parts = parts.slice(1);
    }
    for (i = 0; i < parts.length; i += 1) {
        if (typeof parent[parts[i]] === "undefined") {
            parent[parts[i]] = {};
        }
        parent = parent[parts[i]];
    }
    return parent;
};

var geo = ogs.namespace("geo");

geo.renderers = {};

geo.features = {};

function inherit(C, P) {
    "use strict";
    var F = function() {};
    F.prototype = P.prototype;
    C.prototype = new F();
    C.uber = P.prototype;
    C.prototype.constructor = C;
}

Object.size = function(obj) {
    "use strict";
    var size = 0, key = null;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) {
            size++;
        }
    }
    return size;
};

geo.registerRenderer = function(name, func) {
    if (geo.renderers === undefined) {
        geo.renderers = {};
    }
    geo.renderers[name] = func;
};

geo.createRenderer = function(name, layer, canvas) {
    if (name in geo.renderers) {
        var ren = geo.renderers[name]({
            layer: layer,
            canvas: canvas
        });
        ren._init();
        return ren;
    }
    return null;
};

geo.registerFeature = function(category, name, func) {
    if (geo.features === undefined) {
        geo.features = {};
    }
    if (!(category in geo.features)) {
        geo.features[category] = {};
    }
    geo.features[category][name] = func;
};

geo.createFeature = function(name, layer, renderer) {
    var category = renderer.api();
    if (category in geo.features && name in geo.features[category]) {
        return geo.features[category][name]({
            layer: layer,
            renderer: renderer
        });
    }
    return null;
};

geo.object = function(cfg) {
    "use strict";
    if (!(this instanceof geo.object)) {
        return new geo.object();
    }
    var m_this = this, m_eventHandlers = {};
    this.on = function(event, handler) {
        if (Array.isArray(event)) {
            event.forEach(function(e) {
                m_this.on(e, handler);
            });
            return this;
        }
        if (!m_eventHandlers.hasOwnProperty(event)) {
            m_eventHandlers[event] = [];
        }
        m_eventHandlers[event].push(handler);
        return this;
    };
    this.trigger = function(event, args) {
        if (Array.isArray(event)) {
            event.forEach(function(e) {
                m_this.trigger(e, args);
            });
            return this;
        }
        if (m_eventHandlers.hasOwnProperty(event)) {
            m_eventHandlers[event].forEach(function(handler) {
                handler(args);
            });
        }
        return this;
    };
    this.off = function(event, arg) {
        if (Array.isArray(event)) {
            event.forEach(function(e) {
                m_this.off(e, arg);
            });
            return this;
        }
        if (!arg) {
            m_eventHandlers[event] = [];
        } else if (Array.isArray(arg)) {
            arg.forEach(function(handler) {
                m_this.off(event, handler);
            });
            return this;
        }
        if (m_eventHandlers.hasOwnProperty(event)) {
            m_eventHandlers[event] = m_eventHandlers[event].filter(function(f) {
                return f !== arg;
            });
        }
        return this;
    };
    vgl.object.call(this);
    return this;
};

inherit(geo.object, vgl.object);

geo.sceneObject = function(arg) {
    "use strict";
    if (!(this instanceof geo.sceneObject)) {
        return new geo.sceneObject();
    }
    geo.object.call(this, arg);
    var m_this = this, m_parent = null, m_children = [], s_trigger = this.trigger;
    this.parent = function(arg) {
        if (arg === undefined) {
            return m_parent;
        }
        m_parent = arg;
        return this;
    };
    this.addChild = function(child) {
        if (Array.isArray(child)) {
            child.forEach(this.addChild);
            return this;
        }
        child.parent(this);
        m_children.push(child);
        return this;
    };
    this.removeChild = function(child) {
        if (Array.isArray(child)) {
            child.forEach(this.removeChild);
            return this;
        }
        m_children = m_children.filter(function(c) {
            return c !== child;
        });
        return this;
    };
    this.children = function() {
        return m_children.slice();
    };
    this.trigger = function(event, args) {
        args = args || {};
        if (m_parent && args._triggeredBy !== m_parent) {
            args._triggeredBy = m_this;
            m_parent.trigger(event, args);
            return this;
        }
        s_trigger.call(this, event, args);
        m_children.forEach(function(child) {
            args._triggeredBy = m_this;
            child.trigger(event, args);
        });
        return this;
    };
    return this;
};

inherit(geo.sceneObject, geo.object);

geo.timestamp = function() {
    "use strict";
    if (!(this instanceof geo.timestamp)) {
        return new geo.timestamp();
    }
    vgl.timestamp.call(this);
};

inherit(geo.timestamp, vgl.timestamp);

geo.ellipsoid = function(x, y, z) {
    "use strict";
    if (!(this instanceof geo.ellipsoid)) {
        return new geo.ellipsoid(x, y, z);
    }
    x = vgl.defaultValue(x, 0);
    y = vgl.defaultValue(y, 0);
    z = vgl.defaultValue(z, 0);
    if (x < 0 || y < 0 || z < 0) {
        return console.log("[error] Al radii components must be greater than zero");
    }
    var m_radii = new vec3.fromValues(x, y, z), m_radiiSquared = new vec3.fromValues(x * x, y * y, z * z), m_minimumRadius = Math.min(x, y, z), m_maximumRadius = Math.max(x, y, z);
    this.radii = function() {
        return m_radii;
    };
    this.radiiSquared = function() {
        return m_radiiSquared;
    };
    this.maximumRadius = function() {
        return m_maximumRadius;
    };
    this.minimumRadius = function() {
        return m_minimumRadius;
    };
    this.computeGeodeticSurfaceNormal = function(lat, lon) {
        if (typeof lat === "undefined" || typeof lon === "undefined") {
            throw "[error] Valid latitude and longitude is required";
        }
        var cosLatitude = Math.cos(lat), result = vec3.create();
        result[0] = cosLatitude * Math.cos(lon);
        result[1] = cosLatitude * Math.sin(lon);
        result[2] = Math.sin(lat);
        vec3.normalize(result, result);
        return result;
    };
    this.transformPoint = function(lat, lon, elev) {
        lat = lat * (Math.PI / 180);
        lon = lon * (Math.PI / 180);
        var n = this.computeGeodeticSurfaceNormal(lat, lon), k = vec3.create(), gamma = Math.sqrt(vec3.dot(n, k)), result = vec3.create();
        vec3.multiply(k, m_radiiSquared, n);
        vec3.scale(k, k, 1 / gamma);
        vec3.scale(n, n, elev);
        vec3.add(result, n, k);
        return result;
    };
    this.transformGeometry = function(geom) {
        if (!geom) {
            throw "[error] Failed to transform to cartesian. Invalid geometry.";
        }
        var sourceData = geom.sourceData(vgl.vertexAttributeKeys.Position), sourceDataArray = sourceData.data(), noOfComponents = sourceData.attributeNumberOfComponents(vgl.vertexAttributeKeys.Position), stride = sourceData.attributeStride(vgl.vertexAttributeKeys.Position), offset = sourceData.attributeOffset(vgl.vertexAttributeKeys.Position), sizeOfDataType = sourceData.sizeOfAttributeDataType(vgl.vertexAttributeKeys.Position), index = null, count = sourceDataArray.length * (1 / noOfComponents), gamma = null, n = null, j = 0, k = vec3.create(), result = vec3.create();
        stride /= sizeOfDataType;
        offset /= sizeOfDataType;
        if (noOfComponents !== 3) {
            throw "[error] Requires positions with three components";
        }
        for (j = 0; j < count; ++j) {
            index = j * stride + offset;
            sourceDataArray[index] = sourceDataArray[index] * (Math.PI / 180);
            sourceDataArray[index + 1] = sourceDataArray[index + 1] * (Math.PI / 180);
            n = this.computeGeodeticSurfaceNormal(sourceDataArray[index + 1], sourceDataArray[index]);
            vec3.multiply(k, m_radiiSquared, n);
            gamma = Math.sqrt(vec3.dot(n, k));
            vec3.scale(k, k, 1 / gamma);
            vec3.scale(n, n, sourceDataArray[index + 2]);
            vec3.add(result, n, k);
            sourceDataArray[index] = result[0];
            sourceDataArray[index + 1] = result[1];
            sourceDataArray[index + 2] = result[2];
        }
    };
    return this;
};

geo.ellipsoid.WGS84 = vgl.freezeObject(geo.ellipsoid(6378137, 6378137, 6356752.314245179));

geo.ellipsoid.UNIT_SPHERE = vgl.freezeObject(geo.ellipsoid(1, 1, 1));

geo.mercator = {
    r_major: 6378137
};

geo.mercator.r_minor = function(spherical) {
    var r_minor;
    spherical = spherical !== undefined ? spherical : false;
    if (spherical) {
        r_minor = 6378137;
    } else {
        r_minor = 6356752.314245179;
    }
    return r_minor;
};

geo.mercator.f = function(spherical) {
    return (geo.mercator.r_major - geo.mercator.r_minor(spherical)) / geo.mercator.r_major;
};

geo.mercator.long2tilex = function(lon, z) {
    "use strict";
    var rad = (lon + 180) / 360, f = Math.floor(rad * Math.pow(2, z));
    return f;
};

geo.mercator.lat2tiley = function(lat, z) {
    "use strict";
    var rad = lat * Math.PI / 180;
    return Math.floor((1 - rad / Math.PI) / 2 * Math.pow(2, z));
};

geo.mercator.long2tilex2 = function(lon, z) {
    "use strict";
    var rad = (lon + 180) / 360, f = rad * Math.pow(2, z), ret = Math.floor(f), frac = f - ret;
    return [ ret, frac ];
};

geo.mercator.lat2tiley2 = function(lat, z) {
    "use strict";
    var rad = lat * Math.PI / 180, f = (1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math.PI) / 2 * Math.pow(2, z), ret = Math.floor(f), frac = f - ret;
    return [ ret, frac ];
};

geo.mercator.tilex2long = function(x, z) {
    "use strict";
    return x / Math.pow(2, z) * 360 - 180;
};

geo.mercator.tiley2lat = function(y, z) {
    "use strict";
    var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
    return 180 / Math.PI * Math.atan(.5 * (Math.exp(n) - Math.exp(-n)));
};

geo.mercator.y2lat = function(a) {
    "use strict";
    return 180 / Math.PI * (2 * Math.atan(Math.exp(a * Math.PI / 180)) - Math.PI / 2);
};

geo.mercator.lat2y = function(a) {
    "use strict";
    return 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + a * (Math.PI / 180) / 2));
};

geo.mercator.deg2rad = function(d) {
    "use strict";
    var r = d * (Math.PI / 180);
    return r;
};

geo.mercator.rad2deg = function(r) {
    "use strict";
    var d = r / (Math.PI / 180);
    return d;
};

geo.mercator.ll2m = function(lon, lat, spherical) {
    "use strict";
    if (lat > 89.5) {
        lat = 89.5;
    }
    if (lat < -89.5) {
        lat = -89.5;
    }
    var x = this.r_major * this.deg2rad(lon), temp = this.r_minor(spherical) / this.r_major, es = 1 - temp * temp, eccent = Math.sqrt(es), phi = this.deg2rad(lat), sinphi = Math.sin(phi), con = eccent * sinphi, com = .5 * eccent, con2 = Math.pow((1 - con) / (1 + con), com), ts = Math.tan(.5 * (Math.PI * .5 - phi)) / con2, y = -this.r_major * Math.log(ts), ret = {
        x: x,
        y: y
    };
    return ret;
};

geo.mercator.m2ll = function(x, y, spherical) {
    "use strict";
    var lon = this.rad2deg(x / this.r_major), temp = this.r_minor(spherical) / this.r_major, e = Math.sqrt(1 - temp * temp), lat = this.rad2deg(this.pjPhi2(Math.exp(-(y / this.r_major)), e)), ret = {
        lon: lon,
        lat: lat
    };
    return ret;
};

geo.mercator.pjPhi2 = function(ts, e) {
    "use strict";
    var N_ITER = 15, HALFPI = Math.PI / 2, TOL = 1e-10, con, dphi, i = N_ITER, eccnth = .5 * e, Phi = HALFPI - 2 * Math.atan(ts);
    do {
        con = e * Math.sin(Phi);
        dphi = HALFPI - 2 * Math.atan(ts * Math.pow((1 - con) / (1 + con), eccnth)) - Phi;
        Phi += dphi;
    } while (Math.abs(dphi) > TOL && --i);
    return Phi;
};

geo.latlng = function(arg1, arg2) {
    "use strict";
    if (!(this instanceof geo.latlng)) {
        return new geo.latlng(arg1, arg2);
    }
    var m_lat = arg2 === undefined ? arg1.lat() : arg1, m_lng = arg2 === undefined ? arg1.lng() : arg2;
    this.lat = function(val) {
        if (val === undefined) {
            return m_lat;
        } else {
            m_lat = val;
        }
    };
    this.lng = function(val) {
        if (val === undefined) {
            return m_lng;
        } else {
            m_lng = val;
        }
    };
    this.x = function(val) {
        if (val === undefined) {
            return this.lng();
        } else {
            m_lng = val;
        }
    };
    this.y = function(val) {
        if (val === undefined) {
            return this.lat();
        } else {
            m_lat = val;
        }
    };
    return this;
};

geo.layerOptions = function() {
    "use strict";
    if (!(this instanceof geo.layerOptions)) {
        return new geo.layerOptions();
    }
    this.opacity = .5;
    this.showAttribution = true;
    this.visible = true;
    this.binNumber = vgl.material.RenderBin.Default;
    return this;
};

geo.newLayerId = function() {
    var currentId = 1;
    return function() {
        var id = currentId;
        currentId++;
        return id;
    };
}();

geo.layer = function(arg) {
    "use strict";
    if (!(this instanceof geo.layer)) {
        return new geo.layer(arg);
    }
    arg = arg || {};
    geo.sceneObject.call(this, arg);
    var m_this = this, m_style = arg.style === undefined ? {
        opacity: .5,
        color: [ .8, .8, .8 ],
        visible: true,
        bin: 100
    } : arg.style, m_id = arg.id === undefined ? geo.newLayerId() : arg.id, m_name = "", m_gcs = "EPSG:4326", m_timeRange = [], m_source = arg.source || null, m_map = arg.map === undefined ? null : args.map, m_isReference = false, m_x = 0, m_y = 0, m_width = 0, m_height = 0, m_node = null, m_canvas = null, m_renderer = null, m_initialized = false, m_rendererName = arg.renderer === undefined ? "vglRenderer" : arg.renderer, m_dataTime = geo.timestamp(), m_updateTime = geo.timestamp(), m_drawTime = geo.timestamp();
    this.node = function() {
        return m_node;
    };
    this.id = function(val) {
        if (val === undefined) {
            return m_id;
        } else {
            m_id = id;
            this.modified();
            return this;
        }
    };
    this.name = function(val) {
        if (val === undefined) {
            return m_name;
        } else {
            m_name = val;
            this.modified();
            return this;
        }
    };
    this.opacity = function(val) {
        if (val === undefined) {
            return m_style.opacity;
        } else {
            m_style.opacity = val;
            this.modified();
            return this;
        }
    };
    this.visible = function(val) {
        if (val === undefined) {
            return m_style.visible;
        } else {
            m_style.visible = val;
            this.modified();
            return this;
        }
    };
    this.bin = function(val) {
        if (val === undefined) {
            return m_style.bin;
        } else {
            m_style.bin = val;
            this.modified();
            return this;
        }
    };
    this.gcs = function(val) {
        if (val === undefined) {
            return m_gcs;
        } else {
            m_gcs = val;
            this.modified();
            return this;
        }
    };
    this.transform = function(val) {
        geo.transform.transformLayer(val, this, m_map.baseLayer());
        return this;
    };
    this.timeRange = function(val) {
        if (val === undefined) {
            return timeRange;
        } else {
            timeRange = val.slice(0);
            this.modified();
            return this;
        }
    };
    this.source = function(val) {
        if (val === undefined) {
            return m_source;
        } else {
            m_source = val;
            this.modified();
            return this;
        }
    };
    this.map = function(val) {
        if (val === undefined) {
            return m_map;
        } else {
            m_map = val;
            m_map.node().append(m_node);
            this.modified();
            return this;
        }
    };
    this.renderer = function() {
        return m_renderer;
    };
    this.canvas = function() {
        return m_canvas;
    };
    this.viewport = function() {
        return [ m_x, m_y, m_width, m_height ];
    };
    this.dataTime = function() {
        return m_dataTime;
    };
    this.updateTime = function() {
        return m_updateTime;
    };
    this.drawTime = function() {
        return m_drawTime;
    };
    this.query = function(arg) {};
    this.referenceLayer = function(val) {
        if (val !== undefined) {
            m_isReference = val;
            this.modified();
            return this;
        }
        return m_isReference;
    };
    this.initialized = function(val) {
        if (val !== undefined) {
            m_initialized = val;
            return this;
        }
        return m_initialized;
    };
    this.toLocal = function(input) {
        throw "Not implemented";
    };
    this.fromLocal = function(input) {
        throw "Not implemented";
    };
    this._init = function() {
        if (m_initialized) {
            return this;
        }
        m_node = $(document.createElement("div"));
        m_node.attr("id", m_name);
        m_node.css("position", "absolute");
        m_node.css("width", "100%");
        m_node.css("height", "100%");
        if (m_map) {
            m_map.node().append(m_node);
        }
        if (m_canvas) {
            m_renderer = geo.createRenderer(m_rendererName, this, m_canvas);
        } else {
            m_renderer = geo.createRenderer(m_rendererName, this);
            m_canvas = m_renderer.canvas();
        }
        this.addChild(m_renderer);
        m_initialized = true;
        return this;
    };
    this._exit = function() {};
    this._update = function(request) {};
    this._resize = function(x, y, w, h) {
        m_x = x;
        m_y = y;
        m_width = w;
        m_height = h;
        this.modified();
        this.trigger(geo.event.resize, {
            x: x,
            y: y,
            width: m_width,
            height: m_height
        });
        return this;
    };
    this._draw = function() {};
    return this;
};

inherit(geo.layer, geo.sceneObject);

geo.featureLayer = function(arg) {
    "use strict";
    if (!(this instanceof geo.featureLayer)) {
        return new geo.featureLayer(arg);
    }
    geo.layer.call(this, arg);
    var m_this = this, m_features = null, s_init = this._init, s_update = this._update;
    this.create = function(featureName) {
        return this._create(featureName);
    };
    this.features = function(val) {
        return this._features(val);
    };
    this._features = function(val) {
        var i = 0;
        if (val === undefined) {
            return m_features || [];
        } else {
            m_features = val.slice(0);
            this.dataTime().modified();
            this.modified();
        }
    };
    this._create = function(featureName) {
        this._init();
        var newFeature = geo.createFeature(featureName, m_this, this.renderer());
        if (!m_features) {
            m_features = [];
        }
        m_features.push(newFeature);
        this.features(m_features);
        this.modified();
        return newFeature;
    };
    this._delete = function(feature) {
        var i;
        for (i = 0; i < m_features.length; ++i) {
            if (m_features[i] === feature) {
                m_features[i]._exit();
                this.dataTime().modified();
                this.modified();
                return m_features.splice(i, 1);
            }
        }
        console.log("feature not found ", feature);
        return this;
    };
    this._init = function() {
        if (this.initialized()) {
            return this;
        }
        s_init.call(this);
        this.on(geo.event.resize, function(event) {
            m_this.renderer()._resize(event.x, event.y, event.width, event.height);
            m_this._update({});
            m_this.renderer()._render();
        });
        this.on(geo.event.pan, function(event) {
            m_this._update({
                event: event
            });
            m_this.renderer()._render();
        });
        this.on(geo.event.zoom, function(event) {
            m_this.map().zoom(event.curr_zoom);
            m_this._update({
                event: event
            });
            m_this.renderer()._render();
        });
        return this;
    };
    this._update = function(request) {
        var i;
        if (!m_features) {
            return this;
        }
        s_update.call(this, request);
        if (!this.source() && m_features && m_features.length === 0) {
            console.log("[info] No valid data source found.");
            return;
        }
        if (this.dataTime().getMTime() > this.updateTime().getMTime()) {
            for (i = 0; i < m_features.length; ++i) {
                m_features[i].renderer(this.renderer());
            }
        }
        for (i = 0; i < m_features.length; ++i) {
            m_features[i]._update();
        }
        this.updateTime().modified();
        return this;
    };
    this._draw = function() {
        this.renderer()._render();
        return this;
    };
    return this;
};

inherit(geo.featureLayer, geo.layer);

geo.featureCollection = function() {
    "use strict";
    if (!(this instanceof geo.featureCollection)) {
        return new geo.featureCollection();
    }
    vgl.object.call(this);
    var m_newFeaturesMap = {}, m_expiredFeaturesMap = {};
    this.newFeatures = function(layerId) {
        if (m_newFeaturesMap.hasOwnProperty(layerId)) {
            return m_newFeaturesMap[layerId];
        }
        m_newFeaturesMap[layerId] = [];
        return m_newFeaturesMap[layerId];
    };
    this.setNewFeatures = function(layerId, features) {
        m_newFeaturesMap[layerId] = features;
        this.modified();
    };
    this.expiredFeatures = function(layerId) {
        if (m_expiredFeaturesMap.hasOwnProperty(layerId)) {
            return m_expiredFeaturesMap[layerId];
        }
        m_expiredFeaturesMap[layerId] = [];
        return m_expiredFeaturesMap[layerId];
    };
    this.setExpiredFeatures = function(layerId, expiredFeatures) {
        m_expiredFeaturesMap[layerId] = expiredFeatures;
        this.modified();
    };
    this.clone = function(other) {
        if (!other) {
            return;
        }
        var layerId = null;
        for (layerId in m_newFeaturesMap) {
            if (m_newFeaturesMap.hasOwnProperty(layerId)) {
                other.setFeatures(layerId, m_newFeaturesMap[layerId]);
            }
        }
    };
    this.isEmpty = function() {
        return Object.keys(m_newFeaturesMap).length === 0 && Object.keys(m_expiredFeaturesMap).length === 0;
    };
    this.resetAll = function() {
        m_newFeaturesMap = {};
        m_expiredFeaturesMap = {};
    };
};

inherit(geo.featureCollection, vgl.object);

geo.layerSource = function(id, name, path) {
    "use strict";
    var m_requestDataMTime = vgl.timestamp(), m_id = id, m_name = name, m_path = path;
    this.requestDataMTime = function() {
        return m_requestDataMTime;
    };
    this.requestDataMTimeModified = function() {
        m_requestDataMTime.modified();
    };
    this.id = function() {
        return m_id;
    };
    this.setId = function(id) {
        if (id !== null && m_id !== id) {
            m_id = id;
            this.modified();
            return true;
        }
        return false;
    };
    this.name = function() {
        return m_name;
    };
    this.setName = function(name) {
        if (name !== null && m_name !== name) {
            m_name = name;
            this.modified();
            return true;
        }
        return false;
    };
    this.path = function() {
        return m_path;
    };
    this.setPath = function(path) {
        if (path !== null && m_path !== path) {
            m_path = path;
            this.modified();
            return true;
        }
        return false;
    };
    this.init = function() {};
    this.destroy = function() {};
    this.getData = function(time) {};
    this.getMetaData = function() {};
    this.getTimeRange = function(varname) {};
    this.getSpatialRange = function(varname) {};
    this.getScalarRange = function(varname) {};
};

inherit(geo.layerSource, vgl.object);

geo.archiveLayerSource = function(id, name, path, config, vars, onError) {
    "use strict";
    if (!(this instanceof geo.archiveLayerSource)) {
        return new geo.archiveLayerSource(id, name, path, config, vars, onError);
    }
    geo.layerSource.call(this, id, name, path);
    var m_config = config, m_vars = vars, m_time = -1, m_resultCache = null, m_onError = function(errorString) {};
    if (onError) {
        m_onError = onError;
    }
    this.variableNames = function() {
        return m_vars;
    };
    this.resultCache = function() {
        return m_resultCache;
    };
    this.p_setResultCache = function(resultCache) {
        m_resultCache = resultCache;
    };
    this.destroy = function() {
        m_resultCache = null;
    };
    this.getData = function(time, callback) {
        if (m_time === time) {
            console.log("[info] No new data as timestamp has not changed.");
            return m_resultCache;
        }
        m_time = time;
        var asyncVal = false, retVal = [], errorString = null, reader = null;
        if (callback) {
            asyncVal = true;
        }
        $.ajax({
            type: "POST",
            url: "/services/data/read",
            data: {
                expr: JSON.stringify(name),
                vars: JSON.stringify(vars),
                time: JSON.stringify(time)
            },
            dataType: "json",
            async: asyncVal,
            success: function(response) {
                if (response.error !== null) {
                    errorString = "[error] " + response.error ? response.error : "no results returned from server";
                    console.log(errorString);
                    m_onError(errorString);
                } else {
                    reader = vgl.geojsonReader();
                    retVal = reader.readGJObject(jQuery.parseJSON(response.result.data[0]));
                }
            },
            error: function(jqXHR, textStatus, errorThrown) {
                errorString = "Error reading " + name + ": " + errorThrown;
                console.log(errorString);
                m_onError(errorString);
            }
        });
        if (callback) {
            callback(retVal);
        }
        m_resultCache = retVal;
        return retVal;
    };
    this.getMetaData = function(time) {};
    this.getTimeRange = function(callback) {
        var timeRange = [], asyncVal = false, errorString = null;
        if (callback) {
            asyncVal = true;
        }
        $.ajax({
            type: "POST",
            url: "/services/data/query",
            data: {
                expr: this.path(),
                vars: m_vars[0],
                fields: [ "timerange" ]
            },
            dataType: "json",
            async: asyncVal,
            success: function(response) {
                if (response.error !== null) {
                    errorString = "[error] " + response.error ? response.error : "no results returned from server";
                    console.log(errorString);
                    m_onError(errorString);
                } else {
                    return null;
                }
            },
            error: function(jqXHR, textStatus, errorThrown) {
                errorString = "Error reading timerange for " + this.path() + ": " + errorThrown;
                console.log(errorString);
                m_onError(errorString);
            }
        });
    };
    this.getSpatialRange = function(varname) {
        return [ 0, 0 ];
    };
    this.getScalarRange = function(varname) {
        var range = null, query = {
            basename: this.path()
        }, data = null, i, errorString;
        $.ajax({
            type: "POST",
            url: "/services/mongo/" + m_config.server + "/" + m_config.database + "/" + m_config.collection,
            data: {
                query: JSON.stringify(query),
                fields: JSON.stringify([ "name", "basename", "timeInfo", "variables" ])
            },
            dataType: "json",
            async: false,
            success: function(response) {
                if (response.error !== null) {
                    errorString = "[error] " + response.error ? response.error : "no results returned from server";
                    console.log(errorString);
                    m_onError(errorString);
                }
                data = response.result.data[0];
                for (i = 0; i < data.variables.length; ++i) {
                    if (data.variables[i].name === varname) {
                        range = data.variables[i].range;
                        break;
                    }
                }
            },
            error: function(jqXHR, textStatus, errorThrown) {
                errorString = "Error reading timerange for " + this.path() + ": " + errorThrown;
                console.log(errorString);
                m_onError(errorString);
            }
        });
        return range;
    };
    this.init();
    return this;
};

inherit(geo.archiveLayerSource, geo.layerSource);

geo.event = function() {
    "use strict";
    if (!(this instanceof geo.event)) {
        return new geo.event();
    }
    vgl.command.call(this);
    return this;
};

inherit(geo.event, vgl.command);

geo.event.update = "geo.update";

geo.event.opacityUpdate = "geo.opacityUpdate";

geo.event.layerAdd = "geo.layerAdd";

geo.event.layerRemove = "geo.layerRemove";

geo.event.layerToggle = "geo.layerToggle";

geo.event.layerSelect = "geo.layerSelect";

geo.event.layerUnselect = "geo.layerUnselect";

geo.event.zoom = "geo.zoom";

geo.event.center = "geo.center";

geo.event.pan = "geo.pan";

geo.event.rotate = "geo.rotate";

geo.event.resize = "geo.resize";

geo.event.animate = "geo.animate";

geo.event.query = "geo.query";

geo.event.draw = "geo.draw";

geo.event.drawEnd = "geo.drawEnd";

geo.time = {};

geo.time.incrementTime = function(time, unit, delta) {
    "use strict";
    if (unit === "days") {
        time.setDate(time.getDate() + delta);
    } else if (unit === "months") {
        time.setMonth(time.getMonth() + delta);
    } else if (unit === "years") {
        time.setYear(time.getYear() + delta);
    }
    return time;
};

geo.map = function(arg) {
    "use strict";
    if (!(this instanceof geo.map)) {
        return new geo.map(arg);
    }
    arg = arg || {};
    geo.sceneObject.call(this, arg);
    arg.layers = arg.layers === undefined ? [] : arg.layers;
    var m_this = this, m_x = 0, m_y = 0, m_width = 0, m_height = 0, m_node = $(arg.node), m_gcs = arg.gcs === undefined ? "EPSG:4326" : arg.gcs, m_uigcs = arg.uigcs === undefined ? "EPSG:4326" : arg.uigcs, m_center = arg.center === undefined ? [ 0, 0 ] : arg.center, m_zoom = arg.zoom === undefined ? 10 : arg.zoom, m_baseLayer = null, m_updateTime = geo.timestamp(), m_drawTime = geo.timestamp();
    this.gcs = function(arg) {
        if (arg === undefined) {
            return m_gcs;
        }
        m_gcs = arg;
        return this;
    };
    this.uigcs = function() {
        return m_uigcs;
    };
    this.node = function() {
        return m_node;
    };
    this.zoom = function(val) {
        if (val === undefined) {
            return m_zoom;
        } else {
            m_zoom = val;
            this.modified();
            return m_this;
        }
    };
    this.center = function(val) {
        if (val === undefined) {
            return m_center;
        } else {
            m_center = val.slice;
            this.modified();
            return m_this;
        }
    };
    this.addLayer = function(layer) {
        if (layer !== null || layer !== undefined) {
            layer.map(this);
            layer._init();
            layer._resize(m_x, m_y, m_width, m_height);
            if (layer.referenceLayer() || this.children().length === 0) {
                this.baseLayer(layer);
            }
            this.addChild(layer);
            this.modified();
            m_this.trigger(geo.event.layerAdd, {
                type: geo.event.layerAdd,
                target: m_this,
                layer: layer
            });
        }
        return this;
    };
    this.removeLayer = function(layer) {
        var i;
        if (layer !== null && layer !== undefined) {
            layer._exit();
            this.removeChild(layer);
            this.modified();
            m_this.trigger(geo.event.layerRemove, {
                type: geo.event.layerRemove,
                target: m_this,
                layer: layer
            });
        }
        return this;
    };
    this.toggle = function(layer) {
        if (layer !== null && layer !== undefined) {
            layer.visible(!layer.visible());
            m_this.modified();
            m_this.trigger(geo.event.layerToggle, {
                type: geo.event.layerToggle,
                target: m_this,
                layer: layer
            });
        }
        return this;
    };
    this.resize = function(x, y, w, h) {
        var i = 0, layers = this.children();
        m_x = x;
        m_y = y;
        m_width = w;
        m_height = h;
        for (;i < layers.length; ++i) {
            layers[i]._resize(x, y, w, h);
        }
        m_this.trigger(geo.event.resize, {
            type: geo.event.resize,
            target: m_this,
            x: m_x,
            y: m_y,
            width: w,
            height: h
        });
        this.modified();
    };
    this.gcsToDisplay = function(input) {
        var i, world, output = [];
        if (input instanceof Array && input.length > 0) {
            if (input[0] instanceof geo.latlng) {
                for (i = 0; i < input.length; ++i) {
                    world = m_baseLayer.toLocal(input)[0];
                    output.push(m_baseLayer.renderer().worldToDisplay({
                        x: world.x(),
                        y: world.y()
                    })[0]);
                }
            } else {
                output = m_baseLayer.renderer().worldToDisplay(input).slice(0);
            }
        } else if (input instanceof geo.latlng) {
            world = m_baseLayer.toLocal(input)[0];
            output.push(m_baseLayer.renderer().worldToDisplay({
                x: world.x(),
                y: world.y()
            })[0]);
        } else if (input instanceof Object) {
            output.push(m_baseLayer.renderer().worldToDisplay({
                x: input.x,
                y: input.y
            })[0]);
        } else {
            throw "Conversion method latLonToDisplay does not handle " + input;
        }
        return output;
    };
    this.displayToGcs = function(input) {
        var output;
        if (input instanceof Array && input.length > 0 || input instanceof Object) {
            output = m_baseLayer.renderer().displayToWorld(input);
            output = m_baseLayer.fromLocal(output);
        } else {
            throw "Conversion method latLonToDisplay does not handle " + input;
        }
        return output;
    };
    this.query = function(arg) {};
    this.baseLayer = function(baseLayer) {
        if (typeof baseLayer !== "undefined") {
            if (m_gcs !== baseLayer.gcs()) {
                this.gcs(baseLayer.gcs());
            }
            m_baseLayer = baseLayer;
            m_baseLayer.referenceLayer(true);
            return this;
        }
        return m_baseLayer;
    };
    this.interactorStyle = function(style) {
        if (style === undefined) {
            return m_interactorStyle;
        } else {
            m_interactorStyle = style;
            this.modified();
        }
        return m_interactorStyle;
    };
    this.draw = function() {
        var i = 0, layers = this.children();
        m_this.trigger(geo.event.draw, {
            type: geo.event.draw,
            target: m_this
        });
        this._update();
        for (i = 0; i < layers.length; ++i) {
            layers[i]._draw();
        }
        m_this.trigger(geo.event.drawEnd, {
            type: geo.event.drawEnd,
            target: m_this
        });
    };
    this._init = function(arg) {
        var i;
        if (m_node === undefined || m_node === null) {
            throw "Map require DIV node";
        }
        if (arg !== undefined && arg.layers !== undefined) {
            for (i = 0; i < arg.layers.length; ++i) {
                if (i === 0) {
                    this.baseLayer(arg.layers[i]);
                }
                this.addLayer(arg.layers[i]);
            }
        }
    };
    this._update = function() {
        var i = 0, layers = this.children();
        for (i = 0; i < layers.length; ++i) {
            layers[i]._update();
        }
    };
    this._exit = function() {
        var i = 0, layers = this.children();
        for (i = 0; i < layers.length; ++i) {
            layers[i]._exit();
        }
    };
    this._init(arg);
    return this;
};

inherit(geo.map, geo.sceneObject);

geo.feature = function(arg) {
    "use strict";
    if (!(this instanceof geo.feature)) {
        return new geo.feature(arg);
    }
    geo.object.call(this);
    arg = arg || {};
    var m_style = {}, m_layer = arg.layer === undefined ? null : arg.layer, m_gcs = arg.gcs === undefined ? "EPSG:4326" : arg.gcs, m_visible = arg.visible === undefined ? true : arg.visible, m_renderer = arg.renderer === undefined ? null : arg.renderer, m_dataTime = geo.timestamp(), m_buildTime = geo.timestamp(), m_updateTime = geo.timestamp();
    this.style = function(arg1, arg2) {
        if (arg1 === undefined) {
            return m_style;
        } else if (arg2 === undefined) {
            m_style = $.extend({}, m_style, arg1);
            this.modified();
            return this;
        } else {
            m_style[arg1] = arg2;
            this.modified();
            return this;
        }
    };
    this.layer = function() {
        return m_layer;
    };
    this.renderer = function() {
        return m_renderer;
    };
    this.drawables = function() {
        return this._drawables();
    };
    this.gcs = function(val) {
        if (val === undefined) {
            return m_gcs;
        } else {
            m_gcs = val;
            this.modified();
            return this;
        }
    };
    this.visible = function(val) {
        if (val === undefined) {
            return m_visible;
        } else {
            m_visible = val;
            this.modified();
            return this;
        }
    };
    this.dataTime = function(val) {
        if (val === undefined) {
            return m_dataTime;
        } else {
            m_dataTime = val;
            this.modified();
            return this;
        }
    };
    this.buildTime = function(val) {
        if (val === undefined) {
            return m_buildTime;
        } else {
            m_buildTime = val;
            this.modified();
            return this;
        }
    };
    this.updateTime = function(val) {
        if (val === undefined) {
            return m_updateTime;
        } else {
            m_updateTime = val;
            this.modified();
            return this;
        }
    };
    this._init = function(arg) {
        if (!m_layer) {
            throw "Feature requires a valid layer";
        }
        m_style = $.extend({}, {
            opacity: 1
        }, arg.style === undefined ? {} : arg.style);
    };
    this._build = function() {};
    this._drawables = function() {};
    this._update = function() {};
    this._exit = function() {};
    this._init(arg);
    return this;
};

inherit(geo.feature, geo.object);

geo.pointFeature = function(arg) {
    "use strict";
    if (!(this instanceof geo.pointFeature)) {
        return new geo.pointFeature(arg);
    }
    arg = arg || {};
    geo.feature.call(this, arg);
    var m_positions = arg.positions === undefined ? null : arg.positions, s_init = this._init;
    this.positions = function(val) {
        if (val === undefined) {
            return m_positions;
        } else {
            m_positions = val.slice(0);
            this.dataTime().modified();
            this.modified();
            return this;
        }
    };
    this._init = function(arg) {
        s_init.call(this, arg);
        var defaultStyle = $.extend({}, {
            size: [ 1 ],
            color: [ 1, 1, 1 ],
            point_sprites: false,
            point_sprites_image: null
        }, arg.style === undefined ? {} : arg.style);
        this.style(defaultStyle);
        if (m_positions) {
            this.dataTime().modified();
        }
    };
    this._init(arg);
    return this;
};

inherit(geo.pointFeature, geo.feature);

geo.lineFeature = function(arg) {
    "use strict";
    if (!(this instanceof geo.lineFeature)) {
        return new geo.lineFeature(arg);
    }
    arg = arg || {};
    geo.feature.call(this, arg);
    var m_positions = arg.positions === undefined ? [] : arg.positions, s_init = this._init;
    this.positions = function(val) {
        if (val === undefined) {
            return m_positions;
        } else {
            m_positions = val.slice(0);
            this.dataTime().modified();
            this.modified();
            return this;
        }
    };
    this._init = function(arg) {
        s_init.call(this, arg);
        var defaultStyle = $.extend({}, {
            width: [ 1 ],
            color: [ 1, 1, 1 ],
            pattern: "solid"
        }, arg.style === undefined ? {} : arg.style);
        this.style(defaultStyle);
        if (m_positions) {
            this.dataTime().modified();
        }
    };
    this._init(arg);
    return this;
};

inherit(geo.lineFeature, geo.feature);

geo.polygonFeature = function(arg) {
    "use strict";
    if (!(this instanceof geo.polygonFeature)) {
        return new geo.polygonFeature(arg);
    }
    arg = arg || {};
    geo.feature.call(this, arg);
    var s_init = this._init;
    this._init = function(arg) {
        s_init.call(this, arg);
        var defaultStyle = $.extend({}, {
            color: [ 1, 1, 1 ],
            fill_color: [ 1, 1, 1 ],
            fill: true
        }, arg.style === undefined ? {} : arg.style);
        this.style(defaultStyle);
    };
    this._init(arg);
    return this;
};

inherit(geo.polygonFeature, geo.feature);

geo.planeFeature = function(arg) {
    "use strict";
    if (!(this instanceof geo.planeFeature)) {
        return new geo.planeFeature(arg);
    }
    arg = arg || {};
    arg.ul = arg.ul === undefined ? [ 0, 1, 0 ] : arg.ul;
    arg.lr = arg.lr === undefined ? [ 1, 0, 0 ] : arg.lr;
    arg.depth = arg.depth === undefined ? 0 : arg.depth;
    geo.polygonFeature.call(this, arg);
    var m_origin = [ arg.ul.x, arg.lr.y, arg.depth ], m_upperLeft = [ arg.ul.x, arg.ul.y, arg.depth ], m_lowerRight = [ arg.lr.x, arg.lr.y, arg.depth ], m_defaultDepth = arg.depth, s_init = this._init;
    this.origin = function(val) {
        if (val === undefined) {
            return m_origin;
        } else if (val instanceof Array) {
            if (val.length > 3 || val.length < 2) {
                throw "Upper left point requires point in 2 or 3 dimension";
            }
            m_origin = val.slice(0);
            if (m_origin.length === 2) {
                m_origin[2] = m_defaultDepth;
            }
        } else if (val instanceof geo.latlng) {
            m_origin = [ val.x(), val.y(), m_defaultDepth ];
        }
        this.dataTime().modified();
        this.modified();
        return this;
    };
    this.upperLeft = function(val) {
        if (val === undefined) {
            return m_upperLeft;
        } else if (val instanceof Array) {
            if (val.length > 3 || val.length < 2) {
                throw "Upper left point requires point in 2 or 3 dimension";
            }
            m_upperLeft = val.slice(0);
            if (m_upperLeft.length === 2) {
                m_upperLeft[2] = m_defaultDepth;
            }
        } else if (val instanceof geo.latlng) {
            m_upperLeft = [ val.x(), val.y(), m_defaultDepth ];
        }
        this.dataTime().modified();
        this.modified();
        return this;
    };
    this.lowerRight = function(val) {
        if (val === undefined) {
            return m_lowerRight;
        } else if (val instanceof Array) {
            if (val.length > 3 || val.length < 2) {
                throw "Upper left point requires point in 2 or 3 dimension";
            }
            m_lowerRight = val.slice(0);
            if (m_lowerRight.length === 2) {
                m_lowerRight[2] = m_defaultDepth;
            }
            this.dataTime().modified();
        } else if (val instanceof geo.latlng) {
            m_lowerRight = [ val.x(), val.y(), m_defaultDepth ];
        }
        this.dataTime().modified();
        this.modified();
        return this;
    };
    this._init = function(arg) {
        var style = null;
        s_init.call(this, arg);
        style = this.style();
        if (style.image === undefined) {
            style.image = null;
        }
        this.style(style);
    };
    this._init(arg);
    return this;
};

inherit(geo.planeFeature, geo.polygonFeature);

geo.geomFeature = function(arg) {
    "use strict";
    if (!(this instanceof geo.geomFeature)) {
        return new geo.geomFeature(arg);
    }
    arg = arg || {};
    geo.feature.call(this, arg);
    arg.style = arg.style === undefined ? $.extend({}, {
        color: [ 1, 1, 1 ],
        point_sprites: false,
        point_sprites_image: none
    }, arg.style) : arg.style;
    this.style(arg.style);
    return this;
};

inherit(geo.geomFeature, geo.feature);

geo.transform = {};

geo.transform.osmTransformFeature = function(destGcs, feature, inplace) {
    "use strict";
    if (!feature) {
        console.log("[warning] Invalid (null) feature");
        return;
    }
    if (feature.gcs() === destGcs) {
        return;
    }
    if (!(feature instanceof geo.pointFeature || feature instanceof geo.lineFeature)) {
        throw "Supports only point or line feature";
    }
    var noOfComponents = null, pointOffset = 0, count = null, inPos = null, outPos = null, srcGcs = feature.gcs(), i, inplace = inplace || false, projSrcGcs = new proj4.Proj(srcGcs), projDestGcs = new proj4.Proj(destGcs), xCoord, yCoord;
    if (feature instanceof geo.pointFeature || feature instanceof geo.lineFeature) {
        if (srcGcs !== "EPSG:4326") {
            geo.transform.transformFeature("EPSG:4326", feature, true);
        }
        inPos = feature.positions();
        count = inPos.length;
        if (!(inPos instanceof Array)) {
            throw "Supports Array of 2D and 3D points";
        }
        if (inPos.length > 0 && inPos[0] instanceof geo.latlng) {
            noOfComponents = 2;
            pointOffset = 1;
        } else {
            noOfComponents = count % 2 === 0 ? 2 : count % 3 === 0 ? 3 : null;
            pointOffset = noOfComponents;
        }
        if (noOfComponents !== 2 && noOfComponents !== 3) {
            throw "Transform points require points in 2D or 3D";
        }
        for (i = 0; i < count; i += pointOffset) {
            if (inplace) {
                outPos = inPos;
            } else {
                outPos = inPos.slice(0);
            }
            if (inPos[i] instanceof geo.latlng) {
                yCoord = inPos[i].lat();
            } else {
                yCoord = inPos[i + 1];
            }
            if (yCoord > 85.0511) {
                yCoord = 85.0511;
            }
            if (yCoord < -85.0511) {
                yCoord = -85.0511;
            }
            if (inPos[i] instanceof geo.latlng) {
                outPos[i] = geo.latlng(geo.mercator.lat2y(yCoord), outPos[i].lng());
            } else {
                outPos[i + 1] = geo.mercator.lat2y(yCoord);
            }
        }
        feature.positions(outPos);
        feature.gcs(destGcs);
        return outPos;
    }
    return null;
};

geo.transform.transformFeature = function(destGcs, feature, inplace) {
    "use strict";
    if (!feature) {
        console.log("[warning] Invalid (null) feature");
        return;
    }
    if (feature.gcs() === destGcs) {
        return;
    }
    if (!(feature instanceof geo.pointFeature || feature instanceof geo.lineFeature)) {
        throw "Supports only point or line feature";
    }
    var noOfComponents = null, pointOffset = 0, count = null, inPos = null, outPos = null, projPoint = null, srcGcs = feature.gcs(), i, inplace = inplace || false, projSrcGcs = new proj4.Proj(srcGcs), dest = new proj4.Proj(destGcs);
    if (feature instanceof geo.pointFeature || feature instanceof geo.lineFeature) {
        inPos = feature.positions();
        count = inPos.length;
        if (!(inPos instanceof Array)) {
            throw "Supports Array of 2D and 3D points";
        }
        if (inPos.length > 0 && inPos[0] instanceof geo.latlng) {
            noOfComponents = 2;
            pointOffset = 1;
        } else {
            noOfComponents = count % 2 === 0 ? 2 : count % 3 === 0 ? 3 : null;
            pointOffset = noOfComponents;
        }
        if (noOfComponents !== 2 && noOfComponents !== 3) {
            throw "Transform points require points in 2D or 3D";
        }
        for (i = 0; i < count; i += pointOffset) {
            if (noOfComponents === 2) {
                projPoint = new proj4.Point(inPos[i], inPos[i + 1], 0);
            } else {
                projPoint = new proj4.Point(inPos[i], inPos[i + 1], inPos[i + 2]);
            }
            proj4.transform(projSrcGcs, projDestGcs, projPoint);
            if (inplace) {
                outPos = inPos;
            } else {
                outPos = [];
                outPos.length = inPos.length;
            }
            if (noOfComponents === 2) {
                outPos[i] = projPoint.x;
                outPos[i + 1] = projPoint.y;
            } else {
                outPos[i] = projPoint.x;
                outPos[i + 1] = projPoint.y;
                outPos[i + 2] = projPoint.z;
            }
        }
        feature.positions(outPos);
        feature.gcs(destGcs);
        return outPos;
    }
    return null;
};

geo.transform.transformLayer = function(destGcs, layer, baseLayer) {
    "use strict";
    var features, count, i;
    if (!layer) {
        throw "Requires valid layer for tranformation";
    }
    if (!baseLayer) {
        throw "Requires baseLayer used by the map";
    }
    if (layer === baseLayer) {
        return;
    }
    if (layer instanceof geo.featureLayer) {
        features = layer.features();
        count = features.length;
        i = 0;
        for (i = 0; i < count; ++i) {
            if (destGcs === "EPSG:3857" && baseLayer instanceof geo.osmLayer) {
                geo.transform.osmTransformFeature(destGcs, features[i], true);
            } else {
                geo.transform.transformFeature(destGcs, features[i], true);
            }
        }
        layer.gcs(destGcs);
    } else {
        throw "Only feature layer transformation is supported";
    }
};

geo.renderer = function(arg) {
    "use strict";
    if (!(this instanceof geo.renderer)) {
        return new geo.renderer(arg);
    }
    geo.sceneObject.call(this);
    arg = arg || {};
    var m_this = this, m_layer = arg.layer === undefined ? null : arg.layer, m_canvas = arg.canvas === undefined ? null : arg.canvas, m_initialized = false;
    this.layer = function() {
        return m_layer;
    };
    this.canvas = function(val) {
        if (val === undefined) {
            return m_canvas;
        } else {
            m_canvas = val;
            this.modified();
        }
    };
    this.initialized = function(val) {
        if (val === undefined) {
            return m_initialized;
        } else {
            m_initialized = val;
            return this;
        }
    };
    this.api = function() {
        throw "Should be implemented by derivied classes";
    };
    this.worldToGcs = function(points) {
        throw "Should be implemented by derivied classes";
    };
    this.displayToGcs = function(points) {
        throw "Should be implemented by derivied classes";
    };
    this.gcsToDisplay = function(points) {
        throw "Should be implemented by derivied classes";
    };
    this.worldToDisplay = function(points) {
        throw "Should be implemented by derivied classes";
    };
    this.displayToWorld = function(points) {
        throw "Should be implemented by derivied classes";
    };
    this.relMouseCoords = function(event) {
        var totalOffsetX = 0, totalOffsetY = 0, canvasX = 0, canvasY = 0, currentElement = this.canvas();
        do {
            totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
            totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
        } while (currentElement = currentElement.offsetParent);
        canvasX = event.pageX - totalOffsetX;
        canvasY = event.pageY - totalOffsetY;
        return {
            x: canvasX,
            y: canvasY
        };
    };
    this._init = function() {};
    this._resize = function(x, y, w, h) {};
    this._render = function() {};
    this._exit = function() {};
    this._connectMouseEvents = function() {};
    return this;
};

inherit(geo.renderer, geo.sceneObject);

geo.osmLayer = function(arg) {
    "use strict";
    if (!(this instanceof geo.osmLayer)) {
        return new geo.osmLayer(arg);
    }
    geo.featureLayer.call(this, arg);
    var m_this = this, MAP_OSM = 0, MAP_MQOSM = 1, MAP_MQAERIAL = 2, MAP_NUMTYPES = 3, m_mapType = MAP_MQOSM, m_tiles = {}, m_pendingNewTiles = [], m_pendingInactiveTiles = [], m_numberOfCachedTiles = 0, m_maximumNumberOfActiveTiles = 100, m_previousZoom = null, s_init = this._init, s_update = this._update;
    this.toLocal = function(input) {
        var i, output = [];
        if (input instanceof Array && input.length > 0) {
            output.length = input.length;
            if (input[0] instanceof geo.latlng) {
                for (i = 0; i < input.length; ++i) {
                    output[i] = geo.latlng(input[i]);
                    output[i].lat(geo.mercator.lat2y(output[i].lat()));
                }
            } else {
                output = m_baseLayer.renderer().worldToDisplay(input).slice(0);
            }
        } else if (input instanceof geo.latlng) {
            output.push(geo.latlng(input));
            output[0].lat(geo.mercator.lat2y(output[0].lat()));
        } else {
            throw "toLocal does not handle " + input;
        }
        return output;
    };
    this.fromLocal = function(input) {
        var i, output = [];
        if (input instanceof Array && input.length > 0) {
            output.length = input.length;
            if (input[0] instanceof Object) {
                for (i = 0; i < input.length; ++i) {
                    output[i].x = input[i].x;
                    output[i].y = geo.mercator.y2lat(input[i].y);
                }
            } else {
                for (i = 0; i < input.length; ++i) {
                    output[i] = input[i];
                    output[i + 1] = geo.mercator.y2lat(input[i + 1]);
                }
            }
        } else {
            throw "fromLocal does not handle " + input;
        }
        return output;
    };
    this._hasTile = function(zoom, x, y) {
        if (!m_tiles[zoom]) {
            return false;
        }
        if (!m_tiles[zoom][x]) {
            return false;
        }
        if (!m_tiles[zoom][x][y]) {
            return false;
        }
        return m_tiles[zoom][x][y];
    };
    this._addTiles = function(request, zoom, x, y) {
        if (!m_tiles[zoom]) {
            m_tiles[zoom] = {};
        }
        if (!m_tiles[zoom][x]) {
            m_tiles[zoom][x] = {};
        }
        if (m_tiles[zoom][x][y]) {
            return;
        }
        var noOfTilesX = Math.max(1, Math.pow(2, zoom)), noOfTilesY = Math.max(1, Math.pow(2, zoom)), totalLatDegrees = 360, lonPerTile = 360 / noOfTilesX, latPerTile = totalLatDegrees / noOfTilesY, llx = -180 + x * lonPerTile, lly = -totalLatDegrees * .5 + y * latPerTile, urx = -180 + (x + 1) * lonPerTile, ury = -totalLatDegrees * .5 + (y + 1) * latPerTile, feature = null, tile = new Image();
        tile.LOADING = true;
        tile.LOADED = false;
        tile.UNLOAD = false;
        tile.REMOVED = false;
        tile.REMOVING = false;
        tile.HIDING = false;
        tile.crossOrigin = "anonymous";
        tile.zoom = zoom;
        tile.index_x = x;
        tile.index_y = y;
        tile.llx = llx;
        tile.lly = lly;
        tile.urx = urx;
        tile.ury = ury;
        tile.src = "http://otile1.mqcdn.com/tiles/1.0.0/osm/" + zoom + "/" + x + "/" + (Math.pow(2, zoom) - 1 - y) + ".jpg";
        m_tiles[zoom][x][y] = tile;
        m_pendingNewTiles.push(tile);
        ++m_numberOfCachedTiles;
        return tile;
    };
    this._removeTiles = function(request) {
        var i, x, y, tile, zoom = this.map().zoom();
        if (m_previousZoom === null) {
            m_previousZoom = zoom;
        }
        if (m_previousZoom === zoom) {
            return this;
        }
        m_previousZoom = zoom;
        if (!m_tiles) {
            return this;
        }
        for (zoom in m_tiles) {
            if (m_previousZoom === zoom) {
                continue;
            }
            if (!m_tiles[zoom]) {
                continue;
            }
            for (x in m_tiles[zoom]) {
                if (!m_tiles[zoom][x]) {
                    continue;
                }
                for (y in m_tiles[zoom][x]) {
                    if (!m_tiles[zoom][x][y]) {
                        continue;
                    }
                    if (!m_tiles[zoom][x][y].LOADED) {
                        m_tiles[zoom][x][y].UNLOAD = true;
                        continue;
                    }
                    if (m_tiles[zoom][x][y].REMOVED || m_tiles[zoom][x][y].REMOVING) {
                        continue;
                    }
                    tile = m_tiles[zoom][x][y];
                    tile.REMOVING = true;
                    tile.HIDING = true;
                    m_pendingInactiveTiles.push(tile);
                }
            }
        }
        setTimeout(function() {
            var tile;
            for (i = 0; i < m_pendingInactiveTiles.length; ++i) {
                tile = m_pendingInactiveTiles[i];
                if (m_numberOfCachedTiles > m_maximumNumberOfActiveTiles) {
                    m_tiles[tile.zoom][tile.index_x][tile.index_y] = null;
                    tile.REMOVED = true;
                    tile.REMOVING = false;
                    tile.HIDING = false;
                    if (tile.feature) {
                        m_this._delete(tile.feature);
                    }
                    --m_numberOfCachedTiles;
                } else if (tile.HIDING && tile.feature) {
                    tile.REMOVING = false;
                    tile.HIDDEN = true;
                    tile.feature.visible(false);
                    tile.feature._update();
                }
            }
            m_pendingInactiveTiles = [];
            m_this._update();
            m_this._draw();
        }, 1e3);
        return this;
    };
    this._updateTiles = function(request) {
        var feature, ren = this.renderer(), node = this.node(), zoom = this.map().zoom(), llx = 0, lly = node.height(), urx = node.width(), ury = 0, temp = null, tile = null, tile1x = null, tile1y = null, tile2x = null, tile2y = null, invJ = null, i = 0, j = 0, worldPt1 = ren.displayToWorld([ llx, lly ])[0], worldPt2 = ren.displayToWorld([ urx, ury ])[0];
        worldPt1[0] = Math.max(worldPt1[0], -180);
        worldPt1[0] = Math.min(worldPt1[0], 180);
        worldPt1[1] = Math.max(worldPt1[1], -180);
        worldPt1[1] = Math.min(worldPt1[1], 180);
        worldPt2[0] = Math.max(worldPt2[0], -180);
        worldPt2[0] = Math.min(worldPt2[0], 180);
        worldPt2[1] = Math.max(worldPt2[1], -180);
        worldPt2[1] = Math.min(worldPt2[1], 180);
        tile1x = geo.mercator.long2tilex(worldPt1[0], zoom);
        tile1y = geo.mercator.lat2tiley(worldPt1[1], zoom);
        tile2x = geo.mercator.long2tilex(worldPt2[0], zoom);
        tile2y = geo.mercator.lat2tiley(worldPt2[1], zoom);
        tile1x = Math.max(tile1x, 0);
        tile1x = Math.min(Math.pow(2, zoom) - 1, tile1x);
        tile1y = Math.max(tile1y, 0);
        tile1y = Math.min(Math.pow(2, zoom) - 1, tile1y);
        tile2x = Math.max(tile2x, 0);
        tile2x = Math.min(Math.pow(2, zoom) - 1, tile2x);
        tile2y = Math.max(tile2y, 0);
        tile2y = Math.min(Math.pow(2, zoom) - 1, tile2y);
        if (tile1x > tile2x) {
            temp = tile1x;
            tile1x = tile2x;
            tile2x = temp;
        }
        if (tile2y > tile1y) {
            temp = tile1y;
            tile1x = tile2y;
            tile2y = temp;
        }
        for (i = tile1x; i <= tile2x; ++i) {
            for (j = tile2y; j <= tile1y; ++j) {
                invJ = Math.pow(2, zoom) - 1 - j;
                if (!m_this._hasTile(zoom, i, invJ)) {
                    m_this._addTiles(request, zoom, i, invJ);
                } else {
                    if (m_tiles[zoom][i][invJ].LOADED) {
                        m_tiles[zoom][i][invJ].feature.visible(true);
                    }
                }
            }
        }
        for (i = 0; i < m_pendingNewTiles.length; ++i) {
            var tile = m_pendingNewTiles[i];
            if (tile.zoom !== m_this.map().zoom()) {
                continue;
            }
            tile.onload = function() {
                this.LOADING = false;
                this.LOADED = true;
                this.UNLOAD = false;
                this.REMOVING = false;
                this.REMOVED = false;
                if ((tile.HIDDEN || this.HIDING) && this.feature) {
                    this.feature.visible(false);
                    this.HIDING = false;
                }
                this.feature._update();
                m_this._draw();
            };
            feature = this.create("planeFeature").origin([ tile.llx, tile.lly ]).upperLeft([ tile.llx, tile.ury ]).lowerRight([ tile.urx, tile.lly ]).gcs('"EPSG:3857"').style("image", tile);
            tile.feature = feature;
        }
        m_pendingNewTiles = [];
        m_this._removeTiles(request);
        this.updateTime().modified();
        return this;
    };
    this._init = function() {
        s_init.call(this);
        this.gcs("EPSG:3857");
        return this;
    };
    this._update = function(request) {
        this._updateTiles(request);
        s_update.call(this, request);
    };
    return this;
};

inherit(geo.osmLayer, geo.featureLayer);

ggl = ogs.namespace("geo.gl");

ggl.renderer = function(arg) {
    "use strict";
    if (!(this instanceof ggl.renderer)) {
        return new ggl.renderer(arg);
    }
    geo.renderer.call(this, arg);
    var m_this = this;
    this.contextRenderer = function() {
        throw "Should be implemented by derived classes";
    };
    return this;
};

inherit(ggl.renderer, geo.renderer);

geo.registerRenderer("vglRenderer", ggl.vglRenderer);

ggl.pointFeature = function(arg) {
    "use strict";
    if (!(this instanceof ggl.pointFeature)) {
        return new ggl.pointFeature(arg);
    }
    arg = arg || {};
    geo.pointFeature.call(this, arg);
    var m_actor = null, m_buildTime = vgl.timestamp(), s_init = this._init, s_update = this._update;
    this._init = function(arg) {
        s_init.call(this, arg);
    };
    this._build = function() {
        if (m_actor) {
            this.renderer().contextRenderer().removeActor(m_actor);
        }
        m_actor = vgl.utils.createPoints(this.positions(), this.style().colors);
        this.renderer().contextRenderer().addActor(m_actor);
        this.buildTime().modified();
    };
    this._update = function() {
        s_update.call(this);
        if (this.dataTime().getMTime() >= this.buildTime().getMTime()) {
            this._build();
        }
        if (this.updateTime().getMTime() <= this.getMTime()) {
            if (this.style.color instanceof vgl.lookupTable) {
                vgl.utils.updateColorMappedMaterial(this.material(), this.style.color);
            }
            this.updateTime().modified();
        }
    };
    this._init(arg);
    return this;
};

inherit(ggl.pointFeature, geo.pointFeature);

geo.registerFeature("vgl", "pointFeature", ggl.pointFeature);

ggl.geomFeature = function(arg) {
    "use strict";
    if (!(this instanceof ggl.geomFeature)) {
        return new ggl.geomFeature(arg);
    }
    arg = arg || {};
    geo.geomFeature.call(this, arg);
    var m_style = this.style(), m_geom = args.geom || null, m_actor = vgl.actor(), m_mapper = vgl.mapper(), m_material = null, m_scalar = null, m_color = arg.color || [ 1, 1, 1 ], m_buildTime = null;
    this._build = function() {
        if (m_geom !== null) {
            m_scalar = m_geom.sourceData(vgl.vertexAttributeKeys.Scalar), m_color = m_geom.sourceData(vgl.vertexAttributeKeys.Color);
            m_mapper.setGeometryData(m_geom);
        }
        this.setMapper(m_mapper);
        if (style.point_sprites !== undefined && style.point_sprites && style.point_sprites_image !== undefined && style.point_sprites_image !== null && m_noOfPrimitives === 1 && m_geom.primitive(0).primitiveType() === gl.POINTS) {
            m_material = vgl.utils.createPointSpritesMaterial(style.point_sprites_image);
        } else if (m_scalar) {
            if (m_color instanceof vgl.lookupTable) {
                m_color.updateRange(m_scalar.scalarRange());
                m_material = vgl.utils.createColorMappedMaterial(m_color);
            } else {
                m_color = vgl.lookupTable();
                m_color.updateRange(m_scalar.scalarRange());
                m_material = vgl.utils.createColorMappedMaterial(m_color);
            }
        } else if (m_color) {
            m_material = vgl.utils.createColorMaterial();
        } else {
            m_material = vgl.utils.createSolidColorMaterial();
        }
        m_actor.setMaterial(m_material);
    };
    this._update = function() {
        if (m_buildTime && m_buildTime.getMTime() < this.getMTime()) {
            if (m_color instanceof vgl.lookupTable) {
                vgl.utils.updateColorMappedMaterial(this.material(), this.style.color);
            } else {}
        } else {
            m_buildTime = vgl.timestamp();
            m_buildTime.modified();
        }
    };
    this.geometry = function(val) {
        if (val === undefined) {
            return m_geom;
        } else {
            m_geom = val;
            this.modified();
            return this;
        }
    };
    return this;
};

inherit(ggl.geomFeature, geo.geomFeature);

ggl.pointFeature = function(arg) {
    "use strict";
    if (!(this instanceof ggl.pointFeature)) {
        return new ggl.pointFeature(arg);
    }
    arg = arg || {};
    geo.pointFeature.call(this, arg);
    var m_actor = null, m_buildTime = vgl.timestamp(), s_init = this._init, s_update = this._update;
    this._init = function(arg) {
        s_init.call(this, arg);
    };
    this._build = function() {
        if (m_actor) {
            this.renderer().contextRenderer().removeActor(m_actor);
        }
        m_actor = vgl.utils.createPoints(this.positions(), this.style().colors);
        this.renderer().contextRenderer().addActor(m_actor);
        this.buildTime().modified();
    };
    this._update = function() {
        s_update.call(this);
        if (this.dataTime().getMTime() >= this.buildTime().getMTime()) {
            this._build();
        }
        if (this.updateTime().getMTime() <= this.getMTime()) {
            if (this.style.color instanceof vgl.lookupTable) {
                vgl.utils.updateColorMappedMaterial(this.material(), this.style.color);
            }
            this.updateTime().modified();
        }
    };
    this._init(arg);
    return this;
};

inherit(ggl.pointFeature, geo.pointFeature);

geo.registerFeature("vgl", "pointFeature", ggl.pointFeature);

ggl.planeFeature = function(arg) {
    "use strict";
    if (!(this instanceof ggl.planeFeature)) {
        return new ggl.planeFeature(arg);
    }
    geo.planeFeature.call(this, arg);
    var m_this = this, m_actor = null;
    this.coords = function() {
        return [ this.origin(), this.upperLeft(), this.lowerRight() ];
    };
    this._build = function() {
        var or = this.origin(), ul = this.upperLeft(), lr = this.lowerRight(), img = this.style().image, image = null, onloadCallback = null, texture = null;
        if (m_actor) {
            this.renderer().contextRenderer().removeActor(m_actor);
        }
        if (img && img instanceof Image) {
            image = img;
            onloadCallback = img.onload;
        } else if (img) {
            image = new Image();
            image.src = img;
        }
        if (!image) {
            m_actor = vgl.utils.createPlane(or[0], or[1], or[2], ul[0], ul[1], ul[2], lr[0], lr[1], lr[2]);
        } else {
            m_actor = vgl.utils.createTexturePlane(or[0], or[1], or[2], lr[0], lr[1], lr[2], ul[0], ul[1], ul[2], true);
            texture = vgl.texture();
            m_this.visible(false);
            image.onload = function() {
                texture.setImage(image);
                m_actor.material().addAttribute(texture);
                m_this.visible(true);
                onloadCallback.call(this);
            };
        }
        m_this.renderer().contextRenderer().addActor(m_actor);
        this.buildTime().modified();
    };
    this._update = function() {
        if (this.buildTime().getMTime() <= this.dataTime().getMTime()) {
            this._build();
        }
        if (this.updateTime().getMTime() <= this.getMTime()) {
            m_actor.setVisible(this.visible());
        }
        this.updateTime().modified();
    };
    this._exit = function() {
        m_this.renderer().contextRenderer().removeActor(m_actor);
    };
    return this;
};

inherit(ggl.planeFeature, geo.planeFeature);

geo.registerFeature("vgl", "planeFeature", ggl.planeFeature);

ggl.mapInteractorStyle = function() {
    "use strict";
    if (!(this instanceof ggl.mapInteractorStyle)) {
        return new ggl.mapInteractorStyle();
    }
    vgl.interactorStyle.call(this);
    var m_map, m_this = this, m_mapZoomLevel = 3, m_leftMouseButtonDown = false, m_rightMouseButtonDown = false, m_middileMouseButtonDown = false, m_initRightBtnMouseDown = false, m_drawRegionMode = false, m_drawRegionLayer, m_clickLatLng, m_width, m_height, m_renderer, m_renderWindow, m_camera, m_outsideCanvas, m_currentMousePos, m_focusDisplayPoint, m_worldPt1, m_worldPt2, m_dx, m_dy, m_dz, m_zTrans, m_coords, m_mouseLastPos = {
        x: 0,
        y: 0
    }, m_picker = new vgl.picker();
    this.map = function(newMap) {
        if (typeof newMap !== "undefined") {
            m_map = newMap;
            return m_this;
        }
        return m_map;
    };
    this.drawRegionMode = function(newValue) {
        if (typeof newValue !== "undefined") {
            m_drawRegionMode = newValue;
            if (m_drawRegionLayer) {
                m_drawRegionLayer.setVisible(newValue);
            }
            m_map.draw();
            return m_this;
        }
        return m_drawRegionMode;
    };
    this.handleMouseMove = function(event) {
        var canvas = m_this.map(), xrot = null, a = null, angle = null, mouseWorldPoint, features, lastWorldPos, currWorldPos, lastZoom, evt, newMercPerPixel, oldMercPerPixel;
        m_outsideCanvas = false;
        m_coords = m_this.viewer().relMouseCoords(event);
        m_currentMousePos = {
            x: 0,
            y: 0
        };
        if (m_coords.x < 0 || m_coords.x > m_width) {
            m_currentMousePos.x = 0;
            m_outsideCanvas = true;
        } else {
            m_currentMousePos.x = m_coords.x;
        }
        if (m_coords.y < 0 || m_coords.y > m_height) {
            m_currentMousePos.y = 0;
            m_outsideCanvas = true;
        } else {
            m_currentMousePos.y = m_coords.y;
        }
        if (m_outsideCanvas === true) {
            return true;
        }
        if (m_leftMouseButtonDown) {
            if (m_drawRegionMode) {
                mouseWorldPoint = m_map.displayToMap(m_currentMousePos.x, m_currentMousePos.y);
                m_this.setDrawRegion(m_clickLatLng.lat(), m_clickLatLng.lng(), mouseWorldPoint.y, mouseWorldPoint.x);
            } else {
                m_focusDisplayPoint = m_renderWindow.focusDisplayPoint();
                m_worldPt1 = m_renderWindow.displayToWorld(m_currentMousePos.x, m_currentMousePos.y, m_focusDisplayPoint);
                m_worldPt2 = m_renderWindow.displayToWorld(m_mouseLastPos.x, m_mouseLastPos.y, m_focusDisplayPoint);
                m_dx = m_worldPt1[0] - m_worldPt2[0];
                m_dy = m_worldPt1[1] - m_worldPt2[1];
                m_dz = m_worldPt1[2] - m_worldPt2[2];
                lastWorldPos = m_camera.position();
                m_camera.pan(-m_dx, -m_dy, -m_dz);
                currWorldPos = m_camera.position();
                evt = {
                    type: geo.event.pan,
                    last_display_pos: m_mouseLastPos,
                    curr_display_pos: m_currentMousePos,
                    last_world_pos: lastWorldPos,
                    curr_world_pos: currWorldPos
                };
                $(m_this).trigger(evt);
            }
        }
        if (m_middileMouseButtonDown) {}
        if (m_rightMouseButtonDown) {
            m_zTrans = (m_currentMousePos.y - m_mouseLastPos.y) / m_height;
            if (m_zTrans > 0) {
                m_camera.zoom(1 - Math.abs(m_zTrans));
            } else {
                m_camera.zoom(1 + Math.abs(m_zTrans));
            }
            m_renderer.resetCameraClippingRange();
            m_renderer.render();
        }
        m_mouseLastPos.x = m_currentMousePos.x;
        m_mouseLastPos.y = m_currentMousePos.y;
        return false;
    };
    this.handleMouseDown = function(event) {
        var canvas = m_this.map(), point, plane;
        if (event.button === 0) {
            m_leftMouseButtonDown = true;
        }
        if (event.button === 1) {
            m_middileMouseButtonDown = true;
        }
        if (event.button === 2) {
            m_rightMouseButtonDown = true;
        }
        m_coords = m_this.viewer().relMouseCoords(event);
        if (m_coords.x < 0) {
            m_mouseLastPos.x = 0;
        } else {
            m_mouseLastPos.x = m_coords.x;
        }
        if (m_coords.y < 0) {
            m_mouseLastPos.y = 0;
        } else {
            m_mouseLastPos.y = m_coords.y;
        }
        m_renderWindow = m_this.viewer().renderWindow();
        m_width = m_renderWindow.windowSize()[0];
        m_height = m_renderWindow.windowSize()[1];
        m_renderer = m_this.viewer().renderWindow().activeRenderer();
        m_camera = m_renderer.camera();
        if (m_drawRegionMode && m_leftMouseButtonDown) {
            point = m_map.displayToMap(m_mouseLastPos.x, m_mouseLastPos.y);
            m_clickLatLng = geo.latlng(point.y, point.x);
            m_this.setDrawRegion(point.y, point.x, point.y, point.x);
        }
        return false;
    };
    this.handleMouseUp = function(event) {
        var canvas = m_this.map(), width = null, height = null, num = null;
        if (event.button === 0) {
            m_leftMouseButtonDown = false;
            width = m_this.viewer().renderWindow().windowSize()[0];
            height = m_this.viewer().renderWindow().windowSize()[1];
            m_renderer = m_this.viewer().renderWindow().activeRenderer();
            if (m_mouseLastPos.x >= 0 && m_mouseLastPos.x <= width && m_mouseLastPos.y >= 0 && m_mouseLastPos.y <= height) {
                num = m_picker.pick(m_mouseLastPos.x, m_mouseLastPos.y, m_renderer);
            }
        }
        if (event.button === 1) {
            m_middileMouseButtonDown = false;
        }
        if (event.button === 2) {
            m_rightMouseButtonDown = false;
            m_initRightBtnMouseDown = false;
            m_this.zoom();
        }
        return false;
    };
    this.handleMouseOut = function(event) {
        if (m_leftMouseButtonDown) {
            m_leftMouseButtonDown = false;
        } else if (m_middileMouseButtonDown) {
            m_middileMouseButtonDown = false;
        }
        if (m_rightMouseButtonDown) {
            m_rightMouseButtonDown = false;
            m_initRightBtnMouseDown = false;
            m_this.zoom();
        }
    };
    this.zoom = function() {
        var oldMercPerPixel, newMercPerPixel, evt;
        m_zTrans = (m_currentMousePos.y - m_mouseLastPos.y) / m_height;
        m_worldPt1 = m_renderWindow.displayToWorld(0, 0, m_focusDisplayPoint);
        m_worldPt2 = m_renderWindow.displayToWorld(m_width, m_height, m_focusDisplayPoint);
        oldMercPerPixel = (m_worldPt2[0] - m_worldPt1[0]) / m_width;
        if (m_zTrans > 0) {
            m_camera.zoom(1 - Math.abs(m_zTrans));
        } else {
            m_camera.zoom(1 + Math.abs(m_zTrans));
        }
        m_renderer.resetCameraClippingRange();
        m_worldPt1 = m_renderWindow.displayToWorld(0, 0, m_focusDisplayPoint);
        m_worldPt2 = m_renderWindow.displayToWorld(m_width, m_height, m_focusDisplayPoint);
        newMercPerPixel = (m_worldPt2[0] - m_worldPt1[0]) / m_width;
        evt = {
            type: geo.event.zoom,
            curr_zoom: computeZoomLevel(newMercPerPixel),
            last_zoom: computeZoomLevel(oldMercPerPixel)
        };
        $(m_this).trigger(evt);
    };
    this.lastMousePosition = function(newPosition) {
        if (typeof newPosition !== "undefined") {
            m_mouseLastPos = newPosition;
            return m_this;
        }
        return m_mouseLastPos;
    };
    this.leftMouseDown = function(newValue) {
        if (typeof newValue !== "undefined") {
            m_leftMouseButtonDown = newValue;
            return m_this;
        }
        return m_leftMouseButtonDown;
    };
    this.setDrawRegion = function(lat1, lon1, lat2, lon2) {
        var evt, plane = geo.planeFeature(geo.latlng(lat1, lon1), geo.latlng(lat2, lon2), 99);
        m_map.removeLayer(m_drawRegionLayer);
        m_drawRegionLayer = geo.featureLayer({
            opacity: .5,
            showAttribution: 1
        }, plane);
        m_map.addLayer(m_drawRegionLayer);
        evt = jQuery.Event(geo.event.updateDrawRegionEvent);
        $(m_this).trigger(geo.command.updateDrawRegionEvent, evt);
        return m_this;
    };
    this.getDrawRegion = function() {
        return m_drawRegionLayer.features()[0].getCoords();
    };
    function computeZoomLevel(deltaMerc) {
        var i, metersPerPixel, metersPerPixelFull = 156412;
        metersPerPixel = geo.mercator.deg2rad(Math.abs(deltaMerc)) * geo.mercator.r_major;
        for (i = 4; i < 20; ++i) {
            if (metersPerPixel > metersPerPixelFull / Math.pow(2, i)) {
                return i - 1;
            }
        }
    }
    return this;
};

inherit(ggl.mapInteractorStyle, vgl.interactorStyle);

ggl.vglRenderer = function(arg) {
    "use strict";
    if (!(this instanceof ggl.vglRenderer)) {
        return new ggl.vglRenderer(arg);
    }
    ggl.renderer.call(this, arg);
    var m_this = this, m_viewer = null, m_interactorStyle = ggl.mapInteractorStyle(), s_init = this._init;
    this.displayToWorld = function(input) {
        var i, delta, node = this.canvas(), ren = this.contextRenderer(), cam = ren.camera(), fdp = ren.focusDisplayPoint(), output = [], temp;
        if (input instanceof Array && input.length > 0) {
            if (input[0] instanceof Object) {
                delta = 1;
                for (i = 0; i < points.length; i = +delta) {
                    temp = ren.displayToWorld(vec4.fromValues(input.x, input.y, fdp[2], 1), cam.viewMatrix(), cam.projectionMatrix(), node.width(), node.height());
                    output.push({
                        x: temp[0],
                        y: temp[1],
                        z: temp[2],
                        w: temp[3]
                    });
                }
            } else {
                delta = input.length % 3 === 0 ? 3 : 2;
                for (i = 0; i < input.length; i = +delta) {
                    output.push(ren.displayToWorld(vec4.fromValues(input[i], input[i + 1], fdp[2], 1), cam.viewMatrix(), cam.projectionMatrix(), node.width(), node.height()));
                }
            }
        } else if (input instanceof Object) {
            temp = ren.displayToWorld(vec4.fromValues(input.x, input.y, fdp[2], 1), cam.viewMatrix(), cam.projectionMatrix(), node.width(), node.height());
            output.push({
                x: temp[0],
                y: temp[1],
                z: temp[2],
                w: temp[3]
            });
        } else {
            throw "Display to world conversion requires array of 2D/3D points";
        }
        return output;
    };
    this.worldToDisplay = function(input) {
        var input, xyzFormat, i, output, toDisplay, delta, node = this.canvas(), ren = this.contextRenderer(), cam = ren.camera(), fp = cam.focalPoint(), output = [];
        toDisplay = function(x, y, z, isObject) {
            var result;
            isObject = isObject === undefined ? false : true;
            if (!isObject) {
                output.push(ren.worldToDisplay(vec4.fromValues(x, y, z, 1), cam.viewMatrix(), cam.projectionMatrix(), node.width(), node.height()));
                return;
            }
            result = ren.worldToDisplay(vec4.fromValues(x, y, z, 1), cam.viewMatrix(), cam.projectionMatrix(), node.width(), node.height());
            output.push({
                x: result[0],
                y: result[1],
                z: result[2]
            });
        };
        if (input instanceof Array && input.length > 0) {
            xyzFormat = input.length % 3 === 0 ? true : false;
            if (input[0] instanceof Object) {
                delta = 1;
                for (i = 0; i < input.length; i = +delta) {
                    toDisplay(input[i].x, input[i].y, fp[2], true);
                }
            } else if (xyzFormat) {
                delta = 3;
                for (i = 0; i < input.length; i = +delta) {
                    toDisplay(input[i], input[i + 1], input[i + 2]);
                }
            } else {
                delta = 2;
                for (i = 0; i < input.length; i = +delta) {
                    toDisplay(input[i], input[i + 1], fp[2]);
                }
            }
            return output;
        } else if (input instanceof Object) {
            toDisplay(input.x, input.y, fp[2], true);
            return output;
        }
        throw "World to display conversion requires array of 2D/3D points";
    };
    this.contextRenderer = function() {
        return m_viewer.renderWindow().activeRenderer();
    };
    this.api = function() {
        return "vgl";
    };
    this._init = function() {
        if (this.initialized()) {
            return this;
        }
        var canvas;
        s_init.call(this);
        if (!this.canvas()) {
            canvas = $(document.createElement("canvas"));
            canvas.attr("class", ".webgl-canvas");
            this.canvas(canvas);
            this.layer().node().append(canvas);
        }
        m_viewer = vgl.viewer(this.canvas().get(0));
        m_viewer.setInteractorStyle(m_interactorStyle);
        m_viewer.init();
        m_viewer.renderWindow().resize(this.canvas().width(), this.canvas().height());
        $(m_interactorStyle).on(geo.event.pan, function(event) {
            m_this.trigger(geo.event.pan, event);
        });
        $(m_interactorStyle).on(geo.event.zoom, function(event) {
            m_this.trigger(geo.event.zoom, event);
        });
        return this;
    };
    this._connectMapEvents = function() {
        var map = $(m_this.layer().map().node());
        map.on("mousemove", function(event) {
            m_viewer.handleMouseMove(event);
        });
        map.on("mouseup", function(event) {
            m_viewer.handleMouseUp(event);
        });
        map.on("mousedown", function(event) {
            m_viewer.handleMouseDown(event);
        });
        map.on("mouseout", function(event) {
            m_viewer.handleMouseOut(event);
        });
        map.on("keypress", function(event) {
            m_viewer.handleKeyPress(event);
        });
        map.on("contextmenu", function(event) {
            m_viewer.handleContextMenu(event);
        });
        m_interactorStyle.map(this.layer().map());
    };
    this.on(geo.event.layerAdd, function(event) {
        if (event.layer === m_this.layer()) {
            m_this._connectMapEvents();
        }
    });
    this._resize = function(x, y, w, h) {
        this.canvas().attr("width", w);
        this.canvas().attr("height", h);
        m_viewer.renderWindow().positionAndResize(x, y, w, h);
        this._render();
        return this;
    };
    this._render = function() {
        m_viewer.render();
        return this;
    };
    this._exit = function() {};
    return this;
};

inherit(ggl.vglRenderer, ggl.renderer);

geo.registerRenderer("vglRenderer", ggl.vglRenderer);

gd3 = ogs.namespace("geo.d3");

(function(gd3) {
    var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz", strLength = 8;
    gd3.uniqueID = function() {
        var strArray = [], i;
        strArray.length = strLength;
        for (i = 0; i < strLength; i++) {
            strArray[i] = chars.charAt(Math.floor(Math.random() * chars.length));
        }
        return strArray.join("");
    };
})(gd3);

gd3.object = function(arg) {
    "use strict";
    if (!(this instanceof geo.object)) {
        return new gd3.object(arg);
    }
    var m_id = "d3-" + gd3.uniqueID();
    this._d3id = function() {
        return m_id;
    };
    geo.sceneObject.call(this);
    return this;
};

inherit(gd3.object, geo.sceneObject);

gd3.pointFeature = function(arg) {
    "use strict";
    if (!(this instanceof gd3.pointFeature)) {
        return new gd3.pointFeature(arg);
    }
    arg = arg || {};
    geo.pointFeature.call(this, arg);
    gd3.object.call(this);
    var m_this = this;
    function georef(d, refresh) {
        if (!refresh && d.hasOwnProperty("_dispx") && d.hasOwnProperty("_dispy")) {
            return d;
        }
        var map = m_this.layer().map(), p;
        p = map.gcsToDisplay(d);
        d._dispx = function() {
            return p[0].x;
        };
        d._dispy = function() {
            return p[0].y;
        };
        return d;
    }
    var d_attr = {
        cx: function(d) {
            return georef(d, true)._dispx();
        },
        cy: function(d) {
            return georef(d)._dispy();
        },
        r: "1px"
    }, d_style = {
        fill: "black",
        stroke: "none"
    };
    var s_init = this._init, s_update = this._update, m_buildTime = geo.timestamp(), m_style = {};
    this._init = function(arg) {
        s_init.call(this, arg);
        return this;
    };
    this._drawables = function() {
        return d3.selectAll("." + this._d3id());
    };
    this._build = function() {
        var data = this.positions(), s_style = this.style();
        s_update.call(this);
        if (!data) {
            data = [];
        }
        m_style.id = m_this._d3id();
        m_style.data = data;
        m_style.append = "circle";
        m_style.style = $.extend({}, d_style);
        m_style.attributes = $.extend({}, d_attr);
        m_style.classes = [ "d3PointFeature" ];
        m_style.style.fill = d3.rgb(s_style.color[0] * 255, s_style.color[1] * 255, s_style.color[2] * 255);
        m_style.attributes.r = s_style.size[0].toString() + "px";
        m_style.style["fill-opacity"] = s_style.opacity;
        this.renderer().drawFeatures(m_style);
        m_buildTime.modified();
        this.updateTime().modified();
        return this;
    };
    this._update = function() {
        s_update.call(this);
        if (this.dataTime().getMTime() >= m_buildTime.getMTime()) {
            this._build();
        }
        return this;
    };
    this._init(arg);
    return this;
};

inherit(gd3.pointFeature, geo.pointFeature);

geo.registerFeature("d3", "pointFeature", gd3.pointFeature);

gd3.lineFeature = function(arg) {
    "use strict";
    if (!(this instanceof gd3.lineFeature)) {
        return new gd3.lineFeature(arg);
    }
    arg = arg || {};
    geo.lineFeature.call(this, arg);
    gd3.object.call(this);
    var m_this = this, s_init = this._init, m_buildTime = geo.timestamp(), s_update = this._update, m_style = {};
    function georef(d, refresh) {
        if (!refresh && d.hasOwnProperty("_dispx") && d.hasOwnProperty("_dispy")) {
            return d;
        }
        var r = m_this.renderer(), p;
        p = r.worldToDisplay([ d.lng(), d.lat() ]);
        d._dispx = function() {
            return p[0][0];
        };
        d._dispy = function() {
            return p[0][1];
        };
        return d;
    }
    this._init = function(arg) {
        s_init.call(this, arg);
        return this;
    };
    this._build = function() {
        var data = this.positions(), s_style = this.style(), line = d3.svg.line().x(function(d) {
            return georef(d, true)._dispx();
        }).y(function(d) {
            return georef(d)._dispy();
        });
        s_update.call(this);
        m_style.id = m_this._d3id();
        m_style.data = [ data ];
        m_style.append = "path";
        m_style.classes = [ "d3LineFeature" ];
        m_style.attributes = {
            d: line
        };
        m_style.style = {
            fill: "none",
            stroke: d3.rgb(s_style.color[0] * 255, s_style.color[1] * 255, s_style.color[2] * 255),
            "stroke-width": s_style.width[0].toString() + "px",
            "stroke-opacity": s_style.opacity
        };
        this.renderer().drawFeatures(m_style);
        m_buildTime.modified();
        this.updateTime().modified();
        return this;
    };
    this._update = function() {
        s_update.call(this);
        if (this.dataTime().getMTime() >= m_buildTime.getMTime()) {
            this._build();
        }
        return this;
    };
    this._init(arg);
    return this;
};

inherit(gd3.lineFeature, geo.lineFeature);

geo.registerFeature("d3", "lineFeature", gd3.lineFeature);

gd3.d3Renderer = function(arg) {
    "use strict";
    if (!(this instanceof gd3.d3Renderer)) {
        return new gd3.d3Renderer(arg);
    }
    geo.renderer.call(this, arg);
    gd3.object.call(this);
    var m_this = this, s_init = this._init, m_features = {}, m_translate = [ 0, 0 ];
    function setAttrs(select, attrs) {
        var key;
        for (key in attrs) {
            if (attrs.hasOwnProperty(key)) {
                select.attr(key, attrs[key]);
            }
        }
    }
    function getMap() {
        var layer = m_this.layer();
        if (!layer) {
            return null;
        }
        return layer.map();
    }
    function getBaseLayer() {
        var map = getMap();
        if (!map) {
            return null;
        }
        return map.baseLayer();
    }
    function getBaseRenderer() {
        var base = getBaseLayer();
        if (!base) {
            return null;
        }
        return base.renderer();
    }
    function translate(delta) {
        if (delta === undefined) {
            m_translate[0] = 0;
            m_translate[1] = 0;
        } else {
            m_translate[0] += delta.x;
            m_translate[1] += delta.y;
        }
        m_this.canvas().selectAll(".group-" + m_this._d3id()).attr("transform", "translate(" + m_translate.join() + ")");
    }
    this.latLngToDisplayGenerator = function() {
        var baseRenderer = getBaseRenderer();
        return function(pt) {
            var xy = baseRenderer.worldToDisplay([ pt.lng(), pt.lat() ]);
            return {
                x: function() {
                    return xy[0][0];
                },
                y: function() {
                    return xy[0][1];
                }
            };
        };
    };
    this._init = function(arg) {
        s_init.call(this, arg);
        if (!this.canvas()) {
            var canvas = d3.select(this.layer().node().get(0)).append("svg");
            canvas.attr("class", this._d3id());
            canvas.attr("width", this.layer().node().width());
            canvas.attr("height", this.layer().node().height());
            this.canvas(canvas);
        }
    };
    this.displayToWorld = function(pt) {
        var baseRenderer = getBaseRenderer();
        if (!baseRenderer) {
            throw "Cannot project until this layer is connected to a map with a base layer.";
        }
        return baseRenderer.displayToWorld(pt);
    };
    this.worldToDisplay = function(pt) {
        var baseRenderer = getBaseRenderer();
        if (!baseRenderer) {
            throw "Cannot project until this layer is connected to a map with a base layer.";
        }
        var vals = baseRenderer.worldToDisplay(pt);
        vals.forEach(function(v) {
            v[0] -= m_translate[0];
            v[1] -= m_translate[1];
        });
        return vals;
    };
    this.api = function() {
        return "d3";
    };
    this._resize = function(x, y, w, h) {
        m_this.canvas().attr("width", w);
        m_this.canvas().attr("height", h);
        translate();
        m_this.updateFeatures();
    };
    this._render = function() {};
    this._exit = function() {
        this.canvas().remove();
    };
    function getGroup(grp) {
        var svg = m_this.canvas(), selection = svg.selectAll(".group-" + grp).data([ 0 ]);
        selection.enter().append("g").attr("class", "group-" + grp);
        return selection;
    }
    this.drawFeatures = function(arg) {
        m_features[arg.id] = {
            data: arg.data,
            index: arg.dataIndex,
            style: arg.style,
            attributes: arg.attributes,
            classes: arg.classes,
            append: arg.append
        };
        return m_this.updateFeatures(arg.id);
    };
    this.updateFeatures = function(id) {
        var key;
        if (id === undefined) {
            for (key in m_features) {
                if (m_features.hasOwnProperty(key)) {
                    m_this.updateFeatures(key);
                }
            }
            return this;
        }
        var svg = getGroup(m_this._d3id()), data = m_features[id].data, index = m_features[id].index, style = m_features[id].style, attributes = m_features[id].attributes, classes = m_features[id].classes, append = m_features[id].append, selection = svg.selectAll("." + id).data(data, index);
        selection.enter().append(append);
        selection.exit().remove();
        setAttrs(selection, attributes);
        selection.attr("class", classes.concat([ id ]).join(" "));
        selection.style(style);
        return this;
    };
    this.on(geo.event.pan, function(event) {
        translate({
            x: event.curr_display_pos.x - event.last_display_pos.x,
            y: event.curr_display_pos.y - event.last_display_pos.y
        });
    });
    this.on(geo.event.zoom, function(event) {
        translate();
        m_this.updateFeatures();
    });
    this.on(geo.event.resize, function(event) {
        m_this._resize(event.x, event.y, event.width, event.height);
    });
    this._init(arg);
    return this;
};

inherit(gd3.d3Renderer, geo.renderer);

geo.registerRenderer("d3Renderer", gd3.d3Renderer);

var uiModule = ogs.namespace("ui");

uiModule.gis = function() {
    "use strict";
    if (!(this instanceof uiModule.gis)) {
        return new uiModule.gis();
    }
    return this;
};

uiModule.gis.selectedLayers = function() {
    "use strict";
    var layers = [];
    $("#table-layers tr").each(function(i, tr) {
        if ($("#selected", tr)[0].checked) {
            layers.push(tr.id);
        }
    });
    return layers;
};

uiModule.gis.createLayerList = function(map, rootId, heading, toggleFunct, removeFunct, rangeFunction) {
    "use strict";
    var tableRoot = uiModule.gis.createList(rootId, heading), controls = $(document.createElement("div")), toggleButton = $(document.createElement("button")), removeButton = $(document.createElement("button")), modifyButton = $(document.createElement("button")), forEachSelectedLayer = function(funct) {
        $("#table-layers tr").each(function(i, tr) {
            if ($("#selected", tr)[0].checked) {
                funct(tr.id);
            }
        });
    }, editFunction = function() {
        var editDiv = $(document.createElement("div"));
        editDiv.attr("id", "edit-layer-controls");
        editDiv.width(200);
        editDiv.height(200);
        uiModule.gis.createOpacityControl(editDiv, map, uiModule.gis.selectedLayers);
        return editDiv;
    }, popoverOptions = {
        html: true,
        container: "body",
        placement: "right",
        trigger: "click",
        title: null,
        content: editFunction,
        delay: {
            show: 100,
            hide: 100
        }
    }, KEYCODE_ESC, animationControls, timestepDisplay, ensureTimeInfo, hideTimeStepDisplay;
    controls.attr("id", "layer-control-btns");
    controls.addClass("btn-group");
    toggleButton.attr("class", "btn-toggle-layer btn-small btn-warning layer-control-btn");
    toggleButton.attr("disabled", "true");
    toggleButton.html("Toggle");
    toggleButton.width("70px");
    controls.append(toggleButton);
    toggleButton.click(function() {
        forEachSelectedLayer(function(id) {
            toggleFunct(this, id);
        });
    });
    removeButton.attr("class", "btn-edit-layer btn-small btn-danger layer-control-btn");
    removeButton.attr("disabled", "true");
    removeButton.html("Remove");
    removeButton.width("70px");
    controls.append(removeButton);
    removeButton.click(function() {
        forEachSelectedLayer(function(id) {
            removeFunct(this, id);
        });
    });
    modifyButton.attr("class", "btn-small btn-success");
    modifyButton.attr("disabled", "true");
    modifyButton.html("Modify");
    modifyButton.width("70px");
    controls.append(modifyButton);
    modifyButton.popover(popoverOptions);
    $("#drawer").on("click", function(event) {
        modifyButton.popover("hide");
    });
    $(document).keydown(function(event) {
        if (event.which === $.ui.keyCode.ESCAPE) {
            modifyButton.popover("hide");
        }
    });
    $(document).click(function(event) {
        if (!$(event.target).is(modifyButton)) {
            modifyButton.popover("hide");
        }
    });
    animationControls = $("<div>", {
        id: "animation-controls",
        "class": "btn-group"
    }).append($("<button>", {
        id: "step-backward",
        "class": "btn btn-small",
        disabled: "true"
    }).append($("<i>", {
        "class": "icon-step-backward"
    })), $("<button>", {
        id: "play",
        "class": "btn btn-small",
        disabled: "true"
    }).append($("<i>", {
        "class": "icon-play"
    })), $("<button>", {
        id: "pause",
        "class": "btn btn-small",
        disabled: "true"
    }).append($("<i>", {
        "class": "icon-pause"
    })), $("<button>", {
        id: "stop",
        "class": "btn btn-small",
        disabled: "true"
    }).append($("<i>", {
        "class": "icon-stop"
    })), $("<button>", {
        id: "step-forward",
        "class": "btn btn-small",
        disabled: "true"
    }).append($("<i>", {
        "class": "icon-step-forward"
    })));
    animationControls.css({
        left: "10px"
    });
    controls.append($(animationControls));
    ensureTimeInfo = function(layerIds, onDone) {
        var datasets = [], numberOfRequests;
        $.each(layerIds, function(i, id) {
            var dataset = $("#" + id).data("dataset");
            if (!dataset.timeInfo) {
                datasets.push(dataset);
            }
        });
        numberOfRequests = datasets.length;
        if (numberOfRequests === 0) {
            onDone(layerIds);
        }
        $.each(datasets, function(i, dataset) {
            rangeFunction(dataset.name.replace(".nc", ""), function(timeInfo) {
                dataset.timeInfo = timeInfo;
                numberOfRequests--;
                if (numberOfRequests === 0) {
                    onDone(layerIds);
                }
            });
        });
    };
    hideTimeStepDisplay = function() {
        $("#timestep-display").fadeOut("slow");
        $("#timestep-display h4").html("");
    };
    $("#play", animationControls).click(function() {
        $(this).addClass("active");
        $("#pause", animationControls).removeClass("active");
        ensureTimeInfo(uiModule.gis.selectedLayers(), map.animate);
        $("#timestep-display").fadeIn("slow");
    });
    $("#pause", animationControls).click(function() {
        $(this).addClass("active");
        $("#play", animationControls).removeClass("active");
        map.pauseAnimation();
    });
    $("#stop", animationControls).click(function() {
        $("#play", animationControls).removeClass("active");
        $("#pause", animationControls).removeClass("active");
        map.stopAnimation();
        hideTimeStepDisplay();
    });
    $("#step-forward", animationControls).click(function() {
        ensureTimeInfo(uiModule.gis.selectedLayers(), map.stepAnimationForward);
        $("#timestep-display").fadeIn("slow");
    });
    $("#step-backward", animationControls).click(function() {
        ensureTimeInfo(uiModule.gis.selectedLayers(), map.stepAnimationBackward);
        $("#timestep-display").fadeIn("slow");
    });
    $(map).on(geo.command.animateEvent, function(event) {
        if (event.currentTime >= event.endTime) {
            $("#play", animationControls).removeClass("active");
        }
    });
    $("#table-layers").on("layer-removed", function(e) {
        if ($.inArray(e.id, uiModule.gis.selectedLayers())) {
            map.stopAnimation(true);
            $("#play", animationControls).removeClass("active");
            $("#pause", animationControls).removeClass("active");
            hideTimeStepDisplay();
        }
    });
    $("#table-layers").on("layers-selection", function() {
        $.each($("#layer-control-btns button"), function(i, button) {
            $(button).removeAttr("disabled");
        });
        $.each($("#animation-controls button"), function(i, button) {
            $(button).removeAttr("disabled");
        });
    });
    $("#table-layers").on("layers-no-selection", function() {
        $.each($("#layer-control-btns button"), function(i, button) {
            $(button).attr("disabled", "true");
        });
        $.each($("#animation-controls button"), function(i, button) {
            $(button).attr("disabled", "true");
        });
    });
    timestepDisplay = $("<div>", {
        id: "timestep-display",
        style: "position: absolute; z-index: 99; top: 55px;" + "left: 10px; background: rgba(255,255,255,0.5); " + "padding: 5px; border-radius: 5px;"
    }).append($("<h4>"));
    heading = $("h4", timestepDisplay);
    $(map).on(geo.command.animateEvent, function(event) {
        var format = d3.time.format.utc("%Y-%m-%d");
        if (event.currentTime) {
            heading.html(format(event.currentTime));
        }
    });
    $("body").append(timestepDisplay);
    timestepDisplay.hide();
    controls.hide();
    $("#" + rootId + " .accordion-inner").prepend(controls);
};

uiModule.gis.createList = function(rootId, heading) {
    "use strict";
    var listRoot, itemRoot, itemHeading, itemCollection, subItemsRoot, subItemsList, tableRoot, tbody, tableRootContainer;
    listRoot = document.getElementById(rootId);
    itemRoot = document.createElement("div");
    itemRoot.setAttribute("class", "accordion-group");
    listRoot.appendChild(itemRoot);
    itemHeading = document.createElement("div");
    itemHeading.setAttribute("class", "accordion-heading");
    itemRoot.appendChild(itemHeading);
    itemCollection = document.createElement("a");
    itemCollection.setAttribute("class", "accordion-toggle");
    itemCollection.setAttribute("data-toggle", "collapse");
    itemCollection.setAttribute("data-parent", "#" + rootId);
    itemCollection.setAttribute("href", "#collapse-" + rootId);
    itemCollection.appendChild(document.createTextNode(heading));
    itemHeading.appendChild(itemCollection);
    subItemsRoot = document.createElement("div");
    subItemsRoot.setAttribute("class", "accordion-body collapse in");
    subItemsRoot.setAttribute("id", "collapse-" + rootId);
    itemRoot.appendChild(subItemsRoot);
    subItemsList = document.createElement("div");
    subItemsList.setAttribute("class", "accordion-inner");
    subItemsRoot.appendChild(subItemsList);
    tableRootContainer = document.createElement("div");
    tableRootContainer.setAttribute("class", "layer-table-container");
    subItemsList.appendChild(tableRootContainer);
    tableRoot = document.createElement("table");
    tableRoot.setAttribute("id", "table-" + rootId);
    tableRootContainer.appendChild(tableRoot);
    tbody = document.createElement("tbody");
    tableRoot.appendChild(tbody);
    return tableRoot;
};

uiModule.gis.createDataList = function(rootId, heading, layersRootId, data, callback) {
    "use strict";
    var listRoot, itemRoot, itemCollection, subItemsRoot, subItemsList, tableRoot, itemHeading;
    listRoot = $(document.getElementById(rootId));
    itemRoot = $(document.createElement("div"));
    itemRoot.attr("class", "accordion-group");
    listRoot.append(itemRoot);
    itemHeading = $(document.createElement("div"));
    itemHeading.attr("class", "accordion-heading");
    itemRoot.append(itemHeading);
    itemCollection = $(document.createElement("a"));
    itemCollection.attr("class", "accordion-toggle");
    itemCollection.attr("data-toggle", "collapse");
    itemCollection.attr("data-parent", "#" + rootId);
    itemCollection.attr("href", "#collapse-" + rootId);
    itemCollection.append(document.createTextNode(heading));
    itemHeading.append(itemCollection);
    subItemsRoot = $(document.createElement("div"));
    subItemsRoot.attr("class", "accordion-body collapse in");
    subItemsRoot.attr("id", "collapse-" + rootId);
    itemRoot.append(subItemsRoot);
    subItemsList = $(document.createElement("div"));
    subItemsList.attr("class", "accordion-inner");
    subItemsRoot.append(subItemsList);
    tableRoot = $(document.createElement("table"));
    tableRoot.attr("class", "table-hover");
    subItemsList.append(tableRoot);
    $.each(data, function(i, item) {
        var row, col, he, select, k, varname, option, button, rawTimes, time, tval;
        row = $(document.createElement("tr"));
        row.attr("class", "success");
        tableRoot.append(row);
        col = $(document.createElement("td"));
        he = $(document.createElement("h4"));
        he.html(item.basename);
        col.append(he);
        row.append(col);
        rawTimes = [ "na", "na" ];
        if (item.timeInfo.rawTimes) {
            console.log(item.timeInfo);
            rawTimes = item.timeInfo.rawTimes;
        }
        col = $(document.createElement("td"));
        time = $(document.createElement("select"));
        time.attr("class", "combobox");
        time.attr("id", item.name + "_tselect");
        col.append(time);
        for (k = 0; k < rawTimes.length; ++k) {
            tval = rawTimes[k];
            option = $(document.createElement("option"));
            option.attr("value", tval);
            option.html(tval);
            time.append(option);
        }
        col.append(time);
        row.append(col);
        col = $(document.createElement("td"));
        select = $(document.createElement("select"));
        select.attr("class", "combobox");
        select.attr("id", item.name + "_vselect");
        col.append(select);
        for (k = 0; k < item.variables.length; ++k) {
            varname = item.variables[k].name;
            option = $(document.createElement("option"));
            option.attr("value", varname);
            option.html(varname);
            select.append(option);
        }
        row.append(col);
        col = $(document.createElement("td"));
        button = $(document.createElement("button"));
        button.attr("type", "button");
        button.attr("class", "btn btn-primary");
        button.attr("id", "btn-add-" + item.name);
        button.attr("_id", item.id);
        button.attr("name", item.name);
        button.attr("basename", item.basename);
        button.attr("data-toggle", "button");
        button.attr("data-loading-text", "Loading...");
        button.html("Add");
        col.append(button);
        row.append(col);
        if (callback !== undefined) {
            $(button).on("click", callback);
        }
    });
    $(".combobox").width(Math.max.apply(Math, $(".combobox").map(function() {
        return $(this).outerWidth();
    }).get()));
    $(".combobox").select2();
};

uiModule.gis.addLayer = function(object, layersRootId, dataSet, selectfunc, togglefunc, removefunc, callback, workflowfunc, displayProgress) {
    "use strict";
    $("#layer-control-btns").show();
    var rootId, tbody, basename, layerId, tr, td, button, checkBox, toggleButton, icon, workflowButton, nameDiv, downloadStatus, progress, bar, cancel;
    rootId = "#" + layersRootId;
    layerId = $(dataSet).attr("dataset_id");
    if (layerId !== null) {
        tbody = $(rootId).find("tbody");
        basename = $(dataSet).attr("name");
        tr = $(document.createElement("tr"));
        tr.attr("id", layerId);
        $(tbody).append(tr);
        tr.data("dataset", dataSet);
        td = $(document.createElement("td"));
        td.width("20px");
        checkBox = $(document.createElement("input"));
        checkBox.attr("type", "checkbox");
        checkBox.attr("id", "selected");
        td.append(checkBox);
        tr.append(td);
        checkBox.click(function() {
            var selected = 0;
            $("#table-layers tr").each(function(i, tr) {
                if ($("#selected", tr)[0].checked) {
                    selected++;
                }
            });
            if (selected === 1) {
                $("#table-layers").trigger("layers-selection");
            } else if (selected === 0) {
                $("#table-layers").trigger("layers-no-selection");
            }
        });
        td = $(document.createElement("td"));
        td.width("30px");
        toggleButton = $(document.createElement("button"));
        toggleButton.addClass("btn btn-success btn-mini");
        toggleButton.attr("data-toggle", "button");
        toggleButton.attr("type", "button");
        toggleButton.click(layerId, function() {
            togglefunc(this, layerId);
        });
        icon = $(document.createElement("i"));
        icon.addClass("icon-eye-open icon-white");
        toggleButton.append(icon);
        td.append(toggleButton);
        tr.append(td);
        td = $(document.createElement("td"));
        td.width("45px");
        workflowButton = $(document.createElement("button"));
        workflowButton.addClass("btn btn-info btn-mini");
        workflowButton.attr("type", "button");
        workflowButton.click(layerId, function() {
            workflowfunc(this, layerId);
        });
        icon = $(document.createElement("i"));
        icon.addClass("icon-wrench icon-white");
        workflowButton.append(icon);
        td.append(workflowButton);
        tr.append(td);
        td = $(document.createElement("td"));
        nameDiv = $(document.createElement("div"));
        nameDiv.attr("id", "name");
        nameDiv.append($(document.createElement("h4")).html(basename));
        if (displayProgress) {
            downloadStatus = $(document.createElement("div"));
            downloadStatus.attr("id", "progress");
            progress = $(document.createElement("div")).addClass("progress progress-success progress-striped active");
            progress.css({
                "float": "left",
                width: "80%"
            });
            bar = $(document.createElement("div")).addClass("bar");
            progress.append(bar);
            bar.width("0%");
            cancel = $(document.createElement("button")).addClass("btn btn-mini");
            cancel.attr("type", "button");
            cancel.css({
                width: "20px",
                height: "20px",
                position: "relative",
                top: "-1px",
                left: "3px"
            });
            icon = $(document.createElement("i"));
            icon.addClass("icon-remove icon-black");
            icon.css({
                position: "relative",
                left: "-4px",
                top: "-1px"
            });
            cancel.append(icon);
            downloadStatus.append(progress).append(cancel);
            nameDiv.append(downloadStatus);
            cancel.click(layerId, function() {
                tr.trigger("cancel-download-task");
            });
        }
        td.append(nameDiv);
        tr.append(td);
        td = $(document.createElement("td"));
        $(".btn-layer").width(Math.max.apply(Math, $(".btn-layer").map(function() {
            return $(this).outerWidth();
        }).get()));
    }
    $("#" + layerId).fadeOut(0);
    $("#" + layerId).fadeIn("slow", callback);
};

uiModule.gis.layerAdded = function(elem) {
    "use strict";
    $(elem).removeClass("btn-primary");
    $(elem).addClass("btn-success");
    $(elem).addClass("disabled");
    $(elem).attr("data-loading-text", "Added");
    $(elem).attr("disabled", "disabled");
    $(elem).html("Added");
};

uiModule.gis.removeLayer = function(elem, layerId) {
    "use strict";
    var buttonId, button = $("#btn-add-" + layerId);
    if (button !== null || button !== undefined) {
        button.removeClass("disabled");
        button.removeAttr("disabled");
        button.removeClass("btn-success");
        button.removeClass("active");
        button.button("reset");
        button.addClass("btn-primary");
        $("#" + layerId).fadeOut(function() {
            $("#" + layerId).remove();
        });
        $("#table-layers").trigger("layer-removed", {
            id: layerId
        });
        return true;
    }
    return false;
};

uiModule.gis.toggleLayer = function(elem, layerId) {
    "use strict";
};

uiModule.gis.selectLayer = function(target, layerId) {
    "use strict";
    $(target).siblings().removeClass("active");
    if (target !== null || target !== undefined) {
        $(target).addClass("active");
    }
    return true;
};

uiModule.gis.hasLayer = function(layerTable, layerId) {
    "use strict";
    var found = false;
    $("tr", $(layerTable)).each(function(i, tr) {
        if (tr.id === layerId) {
            found = true;
            return;
        }
    });
    return found;
};

uiModule.gis.createMapControls = function(map, container) {
    "use strict";
    var options, parent, key, row, col, heading, input, sliderDiv;
    options = map.options();
    parent = $(container);
    function slideZoom(event, ui) {
        map.setZoom(ui.value);
        map.draw();
    }
    function stopSlider(e) {
        e.stopPropagation();
        return false;
    }
    function selCountryBound() {
        map.toggleCountryBoundries();
        map.draw();
    }
    for (key in options) {
        if (options.hasOwnProperty(key)) {
            row = $(document.createElement("tr"));
            row.attr("class", "row-fluid");
            parent.append(row);
            col = $(document.createElement("td"));
            row.append(col);
            heading = $(document.createElement("h4"));
            heading.html(key);
            col.append(heading);
            switch (key) {
              case "zoom":
                sliderDiv = $(document.createElement("div"));
                sliderDiv.slider({
                    range: "min",
                    min: 0,
                    max: 17,
                    step: 1,
                    value: options[key],
                    slide: slideZoom
                });
                $(sliderDiv).on("mousedown", stopSlider);
                col.attr("class", "span10");
                $(col).append(sliderDiv);
                break;

              case "center":
                break;

              case "country_boundries":
                input = $(document.createElement("input"));
                input.attr("type", "checkbox");
                input.attr("checked", map.options().country_boundries);
                input.click(selCountryBound);
                col.append(input);
                break;

              case "us_states":
                break;

              case "source":
                break;
            }
        }
    }
};

uiModule.gis.createOpacityControl = function(parent, map, layersFunction) {
    "use strict";
    var opacityControl, opacityDiv;
    opacityControl = $(document.createElement("div"));
    opacityControl.append("<h4>opacity</h4>");
    opacityDiv = $(document.createElement("div"));
    opacityDiv.width("100%");
    opacityDiv.addClass("ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all");
    opacityControl.append(opacityDiv);
    parent.append(opacityControl);
    opacityDiv.slider({
        range: "min",
        min: 0,
        max: 1,
        step: .01,
        value: .5
    });
    opacityDiv.slider().bind("slide", function(event, ui) {
        var layers = layersFunction();
        $.each(layers, function(i, layerId) {
            map.findLayerById(layerId).setOpacity(ui.value);
        });
        map.draw();
    });
    opacityDiv.on("mousedown", function(e) {
        e.stopPropagation();
        return false;
    });
    opacityDiv.slider();
};

var srvModule = ogs.namespace("srv");

srvModule.webSocketOptions = function() {
    "use strict";
    if (!(this instanceof srvModule.webSocketOptions)) {
        return new srvModule.webSocketOptions();
    }
    this.url = "ws://" + window.location.host + "/ws";
    this.nodes = [];
    return this;
};

srvModule.webSocket = function(options) {
    "use strict";
    this.events = {
        message_recieved: "message_recieved",
        opened: "opened",
        closed: "closed",
        ready: "ready"
    };
    if (!(this instanceof srvModule.webSocket)) {
        return new srvModule.webSocket(options);
    }
    vgl.object.call(this);
    var m_that = this, m_url = options.url || "ws://" + window.location.host + "/ws", m_nodes = options.nodes || [], m_readynodes = {}, m_binders = {}, m_open = false, m_ws, allReady, i;
    if (window.WebSocket) {
        m_ws = new window.WebSocket(m_url);
    } else if (window.MozWebSocket) {
        m_ws = window.MozWebSocket(m_url);
    } else {
        console.log("[WARNING] WebSocket Not Supported");
        return;
    }
    window.onbeforeunload = function(event) {
        m_ws.close(1e3, "Window closed");
        m_open = false;
        if (!event) {
            event = window.event;
        }
        event.stopPropagation();
        event.preventDefault();
    };
    m_ws.onopen = function() {
        m_open = true;
        $(m_that).trigger({
            type: m_that.events.opened
        });
        if (m_nodes.length === 0) {
            $(m_that).trigger({
                type: m_that.events.ready
            });
        } else {
            m_that.bind("nodemanager", function(node) {
                m_readynodes[node] = true;
                allReady = true;
                for (i = 0; i < m_nodes.length; i++) {
                    if (!m_readynodes[m_nodes[i]]) {
                        allReady = false;
                    }
                }
                if (allReady) {
                    $(m_that).trigger({
                        type: m_that.events.ready
                    });
                }
            });
            for (i = 0; i < m_nodes.length; i++) {
                m_that.message("nodemanager", m_nodes[i]);
            }
        }
    };
    m_ws.onclosed = function() {
        m_open = false;
        $(m_that).trigger({
            type: m_that.events.closed
        });
    };
    m_ws.onmessage = function(event) {
        $(m_that).trigger({
            type: m_that.events.message_recieved,
            message: event.data
        });
        var data = JSON.parse(event.data), i;
        if (m_binders.hasOwnProperty(data.target)) {
            for (i in m_binders[data.target]) {
                m_binders[data.target][i].call(m_that, data.message);
            }
        }
    };
    this.signal = function(node, slot, args, kwargs) {
        args = typeof args !== "undefined" ? args : [];
        kwargs = typeof kwargs !== "undefined" ? kwargs : {};
        var json = {
            target: node,
            message: {
                slot: slot,
                args: args,
                kwargs: kwargs
            }
        };
        m_ws.send(JSON.stringify(json));
    };
    this.message = function(node, message) {
        var json = {
            target: node,
            message: message
        };
        m_ws.send(JSON.stringify(json));
    };
    this.url = function() {
        return m_url;
    };
    this.open = function() {
        return m_open;
    };
    this.name = function() {
        return m_ws;
    };
    this.bind = function(name, callback) {
        if (!m_binders.hasOwnProperty(name)) {
            m_binders[name] = [];
        }
        m_binders[name].push(callback);
    };
    this.unbind = function(name, callback) {
        var index;
        if (m_binders.hasOwnProperty(name)) {
            index = m_binders[name].indexof(callback);
            if (index !== -1) {
                m_binders.splice(index, 1);
            }
        }
    };
    this.unbindAll = function(name) {
        if (typeof name !== "undefined") {
            if (m_binders.hasOwnProperty(name)) {
                m_binders[name] = [];
            }
        } else {
            m_binders = {};
        }
    };
    return this;
};

inherit(srvModule.webSocket, vgl.object);

var wflModule = ogs.namespace("wfl");

"use strict";

wflModule.utils = {
    merge_options: function(obj1, obj2) {
        var obj3 = {}, attrName;
        for (attrName in obj1) {
            obj3[attrName] = obj1[attrName];
        }
        for (attrName in obj2) {
            obj3[attrName] = obj2[attrName];
        }
        return obj3;
    },
    merge_options_in_place: function(obj1, obj2) {
        var attrName;
        for (attrName in obj2) {
            obj1[attrName] = obj2[attrName];
        }
        return obj1;
    },
    defaultValue: function(param, dflt) {
        return typeof param !== "undefined" ? param : dflt;
    },
    createIdCounter: function(initialId) {
        initialId = wflModule.utils.defaultValue(initialId, -1);
        return function() {
            initialId += 1;
            return initialId;
        };
    },
    moduleRegistryMap: {},
    debug: function(msg) {
        console.log(msg);
    },
    roundRect: function(ctx, x, y, width, height, radius, fill, stroke) {
        if (typeof radius === "undefined") {
            radius = 5;
        }
        ctx.beginPath();
        ctx.moveTo(x + radius, y);
        ctx.lineTo(x + width - radius, y);
        ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
        ctx.lineTo(x + width, y + height - radius);
        ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
        ctx.lineTo(x + radius, y + height);
        ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
        ctx.lineTo(x, y + radius);
        ctx.quadraticCurveTo(x, y, x + radius, y);
        ctx.closePath();
        if (stroke || typeof stroke === "undefined") {
            ctx.stroke();
        }
        if (fill) {
            ctx.fill();
        }
    }
};

var nextConnectionId = wflModule.utils.createIdCounter();

var nextWorkflowId = wflModule.utils.createIdCounter();

var nextLocationId = wflModule.utils.createIdCounter();

var nextModuleId = wflModule.utils.createIdCounter();

var nextPortId = wflModule.utils.createIdCounter();

wflModule.workflowOptions = function() {
    "use strict";
    if (!(this instanceof wflModule.workflowOptions)) {
        return new wflModule.workflowOptions();
    }
    function blankWorkflowData(name, version, connections, modules, vistrail_id, id) {
        name = wflModule.utils.defaultValue(name, "untitled");
        version = wflModule.utils.defaultValue(version, "1.0.2");
        connections = wflModule.utils.defaultValue(connections, []);
        modules = wflModule.utils.defaultValue(modules, []);
        vistrail_id = wflModule.utils.defaultValue(vistrail_id, "");
        id = wflModule.utils.defaultValue(id, nextWorkflowId());
        return {
            workflow: {
                "@name": name,
                "@version": version,
                "@{http://www.w3.org/2001/XMLSchema-instance}schemaLocation": "http://www.vistrails.org/workflow.xsd",
                connection: connections,
                module: modules,
                "@vistrail_id": vistrail_id,
                "@id": id
            }
        };
    }
    this.data = blankWorkflowData();
    this.modules = {};
    this.connections = {};
    this.drawStyle = climatePipesStyle;
    this.translated = {
        x: 0,
        y: 0
    };
    this.moduleClass = wflModule.inputModule;
    return this;
};

wflModule.workflow = function(options) {
    "use strict";
    this.events = {
        moduleAdded: "moduleAdded",
        connectionAdded: "connectionAdded"
    };
    if (!(this instanceof wflModule.workflow)) {
        return new wflModule.workflow(options);
    }
    vgl.object.call(this);
    options = typeof options !== "undefined" ? options : {};
    options = wflModule.utils.merge_options(wflModule.workflowOptions(), options);
    var m_that = this, m_data = options.data, m_modules = options.modules, m_connections = options.connections, m_style = options.drawStyle, m_translated = options.translated, m_moduleClass = options.moduleClass, m_visible = false;
    this.generateModulesFromData = function() {
        var i, mid, maxId = 0, nextId = nextModuleId(), modules = m_data.workflow.module;
        for (i = 0; i < modules.length; i++) {
            mid = parseInt(modules[i]["@id"], 10);
            m_modules[mid] = m_moduleClass({
                workflow: m_that
            }, modules[i]);
            if (mid > maxId) {
                maxId = mid;
            }
            while (maxId > nextId) {
                nextId = nextModuleId();
            }
        }
    };
    this.deleteSelectedModules = function() {
        var id;
        for (id in m_connections) {
            if (m_connections.hasOwnProperty(id)) {
                if (m_connections[id].sourceModule().isSelected() || m_connections[id].targetModule().isSelected()) {
                    m_connections[id].delete();
                    delete m_connections[id];
                }
            }
        }
        for (id in m_modules) {
            if (m_modules.hasOwnProperty(id)) {
                if (m_modules[id].isSelected()) {
                    m_modules[id].delete();
                    delete m_modules[id];
                }
            }
        }
    };
    this.addNewModule = function(data, x, y) {
        var moduleInfo = JSON.parse(data), module = {
            "@name": moduleInfo["@name"],
            "@package": moduleInfo["@package"],
            "@version": moduleInfo["@packageVersion"],
            "@namespace": moduleInfo["@namespace"],
            "@cache": "1",
            location: {
                "@x": parseFloat(x),
                "@y": -parseFloat(y),
                "@id": nextLocationId()
            },
            "@id": nextModuleId()
        };
        m_modules[module["@id"]] = m_moduleClass({
            workflow: m_that
        }, module);
        this.data().workflow.module.push(module);
    };
    this.addConnection = function(sourceModule, sourcePort, targetModule, targetPort) {
        if (sourcePort.data()["@type"] === targetPort.data()["@type"]) {
            wflModule.utils.debug("Must connect output to input");
            return;
        }
        if (sourceModule === targetModule) {
            wflModule.utils.debug("Cannot make connection between ports on same module.");
        }
        var connection = {
            "@id": nextConnectionId(),
            port: [ {
                "@moduleName": targetModule.data()["@name"],
                "@name": targetPort.data()["@name"],
                "@signature": targetPort.data()["@sigstring"],
                "@id": nextPortId(),
                "@type": targetPort.data()["@type"],
                "@moduleId": targetModule.data()["@id"]
            }, {
                "@moduleName": sourceModule.data()["@name"],
                "@name": sourcePort.data()["@name"],
                "@signature": sourcePort.data()["@sigstring"],
                "@id": nextPortId(),
                "@type": sourcePort.data()["@type"],
                "@moduleId": sourceModule.data()["@id"]
            } ]
        }, options = {
            vertical: m_moduleClass === wflModule.module,
            workflow: m_that
        };
        m_connections[connection["@id"]] = wflModule.connection(options, connection);
        this.data().workflow.connection.push(connection);
    };
    this.generateConnectionsFromData = function() {
        var i, cid, maxId = 0, nextId = nextConnectionId(), connections = m_data.workflow.connection, options = {
            vertical: m_moduleClass === wflModule.module,
            workflow: m_that
        };
        for (i = 0; i < connections.length; i++) {
            cid = parseInt(connections[i]["@id"], 10);
            m_connections[cid] = wflModule.connection(options, connections[i]);
            if (cid > maxId) {
                maxId = cid;
            }
            while (maxId > nextId) {
                nextId = nextConnectionId();
            }
        }
    };
    this.draw = function(ctx) {
        var key, pad, wx, wy, ww, wh;
        ctx.clearRect(-m_translated.x, -m_translated.y, ctx.canvas.width, ctx.canvas.height);
        ctx.fillStyle = m_style.fill;
        ctx.lineWidth = m_style.lineWidth;
        ctx.strokeStyle = m_style.stroke;
        ctx.save();
        ctx.shadowBlur = m_style.shadowBlur;
        ctx.shadowColor = m_style.shadowColor;
        pad = ctx.shadowBlur;
        wx = pad - m_translated.x;
        wy = pad - m_translated.y;
        ww = ctx.canvas.width - pad * 2;
        wh = ctx.canvas.height - pad * 2;
        wflModule.utils.roundRect(ctx, wx, wy, ww, wh, m_style.cornerRadius, true, true);
        ctx.restore();
        ctx.save();
        wflModule.utils.roundRect(ctx, wx, wy, ww, wh, m_style.cornerRadius, false, false);
        ctx.clip();
        for (key in m_modules) {
            if (m_modules.hasOwnProperty(key)) {
                m_modules[key].draw(ctx, m_style);
            }
        }
        for (key in m_connections) {
            if (m_connections.hasOwnProperty(key)) {
                m_connections[key].draw(ctx, m_style);
            }
        }
        ctx.restore();
    };
    this.setVisible = function(visible) {
        m_visible = visible;
    };
    this.visible = function() {
        return m_visible;
    };
    this.translated = function() {
        return m_translated;
    };
    this.translate = function(ctx, x, y) {
        m_translated.x += x;
        m_translated.y += y;
        ctx.translate(x, y);
    };
    this.show = function(inputContainer) {
        var key;
        this.setVisible(true);
        for (key in m_modules) {
            if (m_modules.hasOwnProperty(key)) {
                m_modules[key].show(inputContainer);
            }
        }
    };
    this.hide = function() {
        var key;
        this.setVisible(false);
        for (key in m_modules) {
            if (m_modules.hasOwnProperty(key)) {
                m_modules[key].hide();
            }
        }
    };
    this.modules = function() {
        return m_modules;
    };
    this.data = function() {
        return m_data;
    };
    this.setData = function(data) {
        m_data = data;
        m_modules = {};
        m_connections = {};
        m_translated = {
            x: 0,
            y: 0
        };
        this.generateModulesFromData();
        this.generateConnectionsFromData();
    };
    this.updateElementPositions = function() {
        var key;
        for (key in m_modules) {
            m_modules[key].updateElementPositions();
        }
    };
    this.getModuleByName = function(name) {
        var key;
        for (key in m_modules) {
            if (m_modules.hasOwnProperty(key)) {
                if (m_modules[key].data()["@name"] === name) {
                    return m_modules[key];
                }
            }
        }
        return null;
    };
    this.setDefaultWorkflowInputs = function(name, filepath, timestep) {
        this.getModuleByName("Dataset").setInput("file", filepath);
        this.getModuleByName("Variable").setInput("name", name);
        if (!isNaN(parseFloat(timestep))) {
            this.getModuleByName("Variable").setInput("time", timestep);
        }
    };
    this.moduleByPos = function(point) {
        var key, module;
        for (key in m_modules) {
            if (m_modules.hasOwnProperty(key)) {
                module = m_modules[key];
                if (module.contains(point)) {
                    return module;
                }
            }
        }
        return null;
    };
    if (options.data.hasOwnProperty("workflow")) {
        this.generateModulesFromData();
        this.generateConnectionsFromData();
    }
    return this;
};

inherit(wflModule.workflow, vgl.object);

var nextFunctionId = wflModule.utils.createIdCounter();

wflModule.module = function(options, data) {
    "use strict";
    if (!(this instanceof wflModule.module)) {
        return new wflModule.module(options, data);
    }
    vgl.object.call(this);
    var m_that = this, m_data = data, m_registryMap = wflModule.utils.moduleRegistryMap, m_registry = m_registryMap[data["@package"]][data["@name"]], m_metrics, m_ports = m_registry.portSpec, m_inPorts = {}, m_outPorts = {}, m_inPortCount = 0, m_outPortCount = 0, m_selected = false, m_isHover, m_workflow = options.workflow;
    data.location["@x"] = parseFloat(data.location["@x"]);
    data.location["@y"] = parseFloat(data.location["@y"]);
    if (!(m_ports instanceof Array)) {
        m_ports = [ m_ports ];
    }
    if (!this.hasOwnProperty("inputPortClass")) {
        this.inputPortClass = wflModule.port;
    }
    if (!this.hasOwnProperty("outputPortClass")) {
        this.outputPortClass = wflModule.port;
    }
    function createPorts() {
        var i;
        for (i = 0; i < m_ports.length; i++) {
            if (m_ports[i]["@type"] !== "output") {
                m_inPorts[m_ports[i]["@name"]] = m_that.inputPortClass({
                    module: m_that
                }, m_ports[i]);
                m_inPortCount++;
            } else {
                m_outPorts[m_ports[i]["@name"]] = m_that.outputPortClass({
                    module: m_that
                }, m_ports[i]);
                m_outPortCount++;
            }
        }
    }
    this.workflow = function() {
        return m_workflow;
    };
    this.inPortCount = function() {
        return m_inPortCount;
    };
    this.outPortCount = function() {
        return m_outPortCount;
    };
    this.getFontMetrics = function(ctx, drawStyle) {
        var result;
        ctx.save();
        ctx.font = drawStyle.module.text.font;
        result = ctx.measureText(m_data["@name"]);
        ctx.restore();
        return result;
    };
    this.recomputeMetrics = function(ctx, drawStyle) {
        var portWidth = drawStyle.module.port.width, totalPortWidth = portWidth + drawStyle.module.port.pad, inPortsWidth = m_inPortCount * totalPortWidth + drawStyle.module.text.xpad, outPortsWidth = m_outPortCount * totalPortWidth, fontMetrics = this.getFontMetrics(ctx, drawStyle), textWidth = fontMetrics.width + drawStyle.module.text.xpad * 2, moduleWidth = Math.max(inPortsWidth, outPortsWidth + drawStyle.module.text.xpad, textWidth, drawStyle.module.minWidth), textHeight = 12, moduleHeight = drawStyle.module.port.pad * 4 + portWidth * 2 + drawStyle.module.text.ypad * 2 + textHeight, mx = Math.floor(m_data.location["@x"] - moduleWidth / 2), my = -Math.floor(m_data.location["@y"]), inPortX = mx + drawStyle.module.port.pad, outPortX = mx + moduleWidth - outPortsWidth, key;
        m_metrics = {
            mx: mx,
            my: my,
            totalPortWidth: totalPortWidth,
            inPortsWidth: inPortsWidth,
            fontMetrics: fontMetrics,
            textWidth: textWidth,
            moduleWidth: moduleWidth,
            textHeight: textHeight,
            moduleHeight: moduleHeight,
            inPortX: inPortX,
            inPortY: my + drawStyle.module.port.pad,
            outPortX: outPortX,
            outPortY: my + moduleHeight - drawStyle.module.port.pad - portWidth
        };
        for (key in m_inPorts) {
            if (m_inPorts.hasOwnProperty(key)) {
                m_inPorts[key].setPosition(inPortX, m_metrics.inPortY);
                inPortX += portWidth + drawStyle.module.port.pad;
            }
        }
        for (key in m_outPorts) {
            if (m_outPorts.hasOwnProperty(key)) {
                m_outPorts[key].setPosition(outPortX, m_metrics.outPortY);
                outPortX += portWidth + drawStyle.module.port.pad;
            }
        }
    };
    this.getMetrics = function() {
        return m_metrics;
    };
    this.getInPorts = function() {
        return m_inPorts;
    };
    this.getOutPorts = function() {
        return m_outPorts;
    };
    this.getRegistry = function() {
        return m_registry;
    };
    this.data = function() {
        return m_data;
    };
    this.draw = function(ctx, drawStyle) {
        if (!m_metrics) {
            this.recomputeMetrics(ctx, drawStyle);
        }
        var portWidth = drawStyle.module.port.width, mx = m_metrics.mx, my = m_metrics.my, key;
        ctx.fillStyle = drawStyle.module.fill;
        ctx.strokeStyle = drawStyle.module.stroke;
        ctx.fillRect(mx, my, m_metrics.moduleWidth, m_metrics.moduleHeight);
        ctx.strokeRect(mx, my, m_metrics.moduleWidth, m_metrics.moduleHeight);
        for (key in m_inPorts) {
            if (m_inPorts.hasOwnProperty(key)) {
                m_inPorts[key].draw(ctx, portWidth);
            }
        }
        for (key in m_outPorts) {
            if (m_outPorts.hasOwnProperty(key)) {
                m_outPorts[key].draw(ctx, portWidth);
            }
        }
        ctx.fillStyle = drawStyle.module.text.fill;
        ctx.font = drawStyle.module.text.font;
        ctx.fillText(m_data["@name"], mx + Math.floor((m_metrics.moduleWidth - m_metrics.fontMetrics.width) / 2), my + m_metrics.textHeight + drawStyle.module.text.ypad);
    };
    this.contains = function(pos) {
        var metrics = this.getMetrics();
        return pos.x > metrics.mx && pos.x < metrics.mx + metrics.moduleWidth && pos.y > metrics.my && pos.y < metrics.my + metrics.moduleHeight;
    };
    this.portByPos = function(pos) {
        var key;
        for (key in m_inPorts) {
            if (m_inPorts.hasOwnProperty(key)) {
                if (m_inPorts[key].contains(pos)) {
                    return m_inPorts[key];
                }
            }
        }
        for (key in m_outPorts) {
            if (m_outPorts.hasOwnProperty(key)) {
                if (m_outPorts[key].contains(pos)) {
                    return m_outPorts[key];
                }
            }
        }
        return null;
    };
    this.hide = function() {
        var key;
        for (key in m_inPorts) {
            if (m_inPorts.hasOwnProperty(key)) {
                m_inPorts[key].hide();
            }
        }
    };
    this.show = function(inputContainer) {
        var key;
        for (key in m_inPorts) {
            if (m_inPorts.hasOwnProperty(key)) {
                m_inPorts[key].show(inputContainer);
            }
        }
    };
    this.getFunctionValue = function(name) {
        var i, f;
        if (m_data.hasOwnProperty("function")) {
            if (!$.isArray(m_data.function)) {
                m_data.function = [ m_data.function ];
            }
            for (i = 0; i < m_data.function.length; i++) {
                f = m_data.function[i];
                if (f["@name"] === name) {
                    return f.parameter["@val"];
                }
            }
        }
        return null;
    };
    this.toggleSelected = function() {
        m_selected = !m_selected;
    };
    this.isSelected = function() {
        return m_selected;
    };
    this.delete = function() {
        var j, moduleDataList = m_that.workflow().data().workflow.module, portName;
        for (j = 0; j < moduleDataList.length; j++) {
            if (m_that.data() === moduleDataList[j]) {
                moduleDataList.splice(j, 1);
                break;
            }
        }
        for (portName in m_inPorts) {
            if (m_inPorts.hasOwnProperty(portName)) {
                m_inPorts[portName].delete();
            }
        }
        for (portName in m_outPorts) {
            if (m_outPorts.hasOwnProperty(portName)) {
                m_outPorts[portName].delete();
            }
        }
    };
    this.addOrUpdateFunction = function(name, value, type) {
        var i, f;
        if (m_data.hasOwnProperty("function")) {
            if (!$.isArray(m_data.function)) {
                m_data.function = [ m_data.function ];
            }
            for (i = 0; i < m_data.function.length; i++) {
                f = m_data.function[i];
                if (f["@name"] === name) {
                    f.parameter["@val"] = value.toString();
                    return;
                }
            }
        } else {
            m_data.function = [];
        }
        m_data.function.push(this.newFunction(name, value, type));
    };
    this.newFunction = function(name, value, type, pos, alias, description) {
        var id = nextFunctionId();
        pos = wflModule.utils.defaultValue(pos, "0");
        alias = wflModule.utils.defaultValue(alias, "");
        description = wflModule.utils.defaultValue(description, "<no description>");
        return {
            "@name": name,
            "#tail": "\n    ",
            "@id": id,
            "@pos": pos,
            "#text": "\n      ",
            parameter: {
                "@val": value ? value.toString() : "",
                "@name": description,
                "#tail": "\n    ",
                "@pos": "0",
                "@alias": alias,
                "@id": id,
                "@type": type
            }
        };
    };
    this.setHover = function(value) {
        m_isHover = value;
    };
    this.isHover = function() {
        return m_isHover;
    };
    this.updateElementPositions = function() {};
    createPorts();
    return this;
};

inherit(wflModule.module, vgl.object);

wflModule.connectionOptions = function() {
    "use strict";
    if (!(this instanceof wflModule.connectionOptions)) {
        return new wflModule.connectionOptions();
    }
    this.vertical = false;
    this.workflow = null;
    return this;
};

wflModule.connection = function(options, data) {
    "use strict";
    if (!(this instanceof wflModule.connection)) {
        return new wflModule.connection(options, data);
    }
    vgl.object.call(this, options, data);
    options = typeof options !== "undefined" ? options : {};
    options = wflModule.utils.merge_options(wflModule.connectionOptions(), options);
    var m_that = this, m_data = data, m_vertical = options.vertical, m_workflow = options.workflow, m_sourceModule, m_targetModule;
    this.data = function() {
        return m_data;
    };
    this.delete = function() {
        var j, connectionDataList = m_workflow.data().workflow.connection;
        for (j = 0; j < connectionDataList.length; j++) {
            if (m_that.data() === connectionDataList[j]) {
                connectionDataList.splice(j, 1);
                break;
            }
        }
    };
    this.draw = function(ctx, currentWorkflowStyle) {
        this.drawCurve(ctx, currentWorkflowStyle, this.computePositions(currentWorkflowStyle));
    };
    this.drawCurve = function(ctx, currentWorkflowStyle, posInfo) {
        var offsets = this.getCurveOffsets(currentWorkflowStyle);
        ctx.beginPath();
        ctx.moveTo(posInfo.cx1, posInfo.cy1);
        ctx.bezierCurveTo(posInfo.cx1 + offsets.x1, posInfo.cy1 + offsets.y1, posInfo.cx2 + offsets.x2, posInfo.cy2 + offsets.y2, posInfo.cx2, posInfo.cy2);
        ctx.lineWidth = currentWorkflowStyle.conn.lineWidth;
        ctx.strokeStyle = currentWorkflowStyle.conn.stroke;
        ctx.stroke();
    };
    this.getCurveOffsets = function(currentWorkflowStyle) {
        return {
            x1: m_vertical ? 0 : currentWorkflowStyle.conn.bezierOffset,
            x2: m_vertical ? 0 : -currentWorkflowStyle.conn.bezierOffset,
            y1: !m_vertical ? 0 : currentWorkflowStyle.conn.bezierOffset,
            y2: !m_vertical ? 0 : -currentWorkflowStyle.conn.bezierOffset
        };
    };
    function getSourceTargetModules() {
        var port, i;
        for (i = 0; i < m_data.port.length; i++) {
            port = m_data.port[i];
            if (port["@type"] === "source" || port["@type"] === "output") {
                m_sourceModule = m_workflow.modules()[port["@moduleId"]];
            } else {
                m_targetModule = m_workflow.modules()[port["@moduleId"]];
            }
        }
    }
    this.sourceModule = function() {
        if (typeof m_sourceModule === "undefined") {
            getSourceTargetModules();
        }
        return m_sourceModule;
    };
    this.targetModule = function() {
        if (typeof m_targetModule === "undefined") {
            getSourceTargetModules();
        }
        return m_targetModule;
    };
    this.computePositions = function(currentWorkflowStyle) {
        var sourceModule = m_that.sourceModule(), targetModule = m_that.targetModule(), sourcePort, targetPort, centerOffset = Math.floor(currentWorkflowStyle.module.port.width / 2), i, port;
        for (i = 0; i < m_data.port.length; i++) {
            port = m_data.port[i];
            if (port["@type"] === "source" || port["@type"] === "output") {
                sourcePort = sourceModule.getOutPorts()[port["@name"]];
            } else {
                targetPort = targetModule.getInPorts()[port["@name"]];
            }
        }
        return {
            cx1: sourcePort.x() + centerOffset,
            cy1: sourcePort.y() + centerOffset,
            cx2: targetPort.x() + centerOffset,
            cy2: targetPort.y() + centerOffset
        };
    };
    return this;
};

inherit(wflModule.connection, vgl.object);

wflModule.portOptions = function() {
    "use strict";
    if (!(this instanceof wflModule.portOptions)) {
        return new wflModule.portOptions();
    }
    this.module = null;
    this.drawStyle = climatePipesStyle;
    return this;
};

wflModule.port = function(options, data) {
    "use strict";
    if (!(this instanceof wflModule.port)) {
        return new wflModule.port(options, data);
    }
    vgl.object.call(this);
    options = typeof options !== "undefined" ? options : {};
    options = wflModule.utils.merge_options(wflModule.portOptions(), options);
    var m_that = this, m_data = data, m_module = options.module, m_style = options.drawStyle, m_x = 0, m_y = 0, m_width = m_style.module.port.width, m_hover;
    this.drawStyle = function() {
        return m_style;
    };
    this.setPosition = function(x, y) {
        m_x = x;
        m_y = y;
    };
    this.module = function() {
        return m_module;
    };
    this.x = function() {
        return m_x;
    };
    this.y = function() {
        return m_y;
    };
    this.data = function() {
        return m_data;
    };
    this.draw = function(ctx, width) {
        ctx.fillStyle = m_style.module.port.fill;
        ctx.strokeStyle = m_style.module.port.stroke;
        ctx.fillRect(m_x, m_y, width, width);
        ctx.strokeRect(m_x, m_y, width, width);
    };
    this.drawAsCircle = function(ctx, width) {
        ctx.fillStyle = m_style.module.port.fill;
        ctx.strokeStyle = m_style.module.port.stroke;
        ctx.lineWidth = m_style.module.port.lineWidth;
        var radius = width / 2;
        ctx.beginPath();
        ctx.arc(m_x + radius, m_y + radius, radius, 0, 2 * Math.PI, false);
        ctx.closePath();
        ctx.fill();
        ctx.stroke();
    };
    this.getName = function() {
        return this.data()["@name"];
    };
    this.contains = function(pos) {
        if (pos.x >= m_x) {
            if (pos.y >= m_y) {
                if (pos.x <= m_x + m_width) {
                    if (pos.y <= m_y + m_width) {
                        return true;
                    }
                }
            }
        }
        return false;
    };
    this.setHover = function(value) {
        m_hover = value;
    };
    this.hide = function() {};
    this.show = function(inputContainer) {};
    this.delete = function() {};
    return this;
};

inherit(wflModule.port, vgl.object);

wflModule.registry = {
    "@rootDescriptorId": "0",
    "package": [ {
        "@loadConfiguration": "True",
        "@description": "basic_modules defines basic VisTrails Modules that are used in most\npipelines.",
        "#text": "\n    ",
        "@name": "Basic Modules",
        "#tail": "\n  ",
        "@version": "2.1",
        moduleDescriptor: [ {
            "#text": "\n      ",
            "@name": "Module",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "0",
            portSpec: {
                "@sortKey": "-1",
                "@name": "self",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Module",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "0",
                "@type": "output"
            },
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "-1"
        }, {
            "#text": "\n      ",
            "@name": "Converter",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "1",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "in_value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Module",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "1",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "out_value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Module",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "2",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "Constant",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "2",
            portSpec: {
                "@sortKey": "-1",
                "@name": "value_as_string",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "3",
                "@type": "output"
            },
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "Boolean",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "3",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Boolean",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "69",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Boolean",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "70",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "2"
        }, {
            "#text": "\n      ",
            "@name": "Float",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "4",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Float",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "71",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Float",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "72",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "2"
        }, {
            "#text": "\n      ",
            "@name": "Integer",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "5",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Integer",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "73",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Integer",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "74",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "4"
        }, {
            "#text": "\n      ",
            "@name": "String",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "6",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "value_as_string",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "4",
                "@type": "output"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "75",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "76",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "2"
        }, {
            "#text": "\n      ",
            "@name": "List",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "7",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "head",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Module",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "5",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "tail",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "List",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "6",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "List",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "77",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "List",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "78",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "2"
        }, {
            "#text": "\n      ",
            "@name": "Path",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "8",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Path",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "7",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Path",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "8",
                "@type": "output"
            }, {
                "@sortKey": "-1",
                "@name": "name",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "9",
                "@type": "input"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "2"
        }, {
            "#text": "\n      ",
            "@name": "File",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "9",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "File",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "10",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "File",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "11",
                "@type": "output"
            }, {
                "@sortKey": "-1",
                "@name": "self",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "File",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "12",
                "@type": "output"
            }, {
                "@sortKey": "-1",
                "@name": "create_file",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Boolean",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "13",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "local_filename",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "14",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "8"
        }, {
            "#text": "\n      ",
            "@name": "Directory",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "10",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Directory",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "15",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Directory",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "16",
                "@type": "output"
            }, {
                "@sortKey": "-1",
                "@name": "itemList",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "List",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "17",
                "@type": "output"
            }, {
                "@sortKey": "-1",
                "@name": "create_directory",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Boolean",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "18",
                "@type": "input"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "8"
        }, {
            "#text": "\n      ",
            "@name": "OutputPath",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "11",
            portSpec: {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "OutputPath",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "19",
                "@type": "output"
            },
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "8"
        }, {
            "#text": "\n      ",
            "@name": "FileSink",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "12",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "file",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "File",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "20",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "outputPath",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "OutputPath",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "21",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "overwrite",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "True",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Boolean",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "22",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "publishFile",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Boolean",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "23",
                "@type": "input"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "DirectorySink",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "13",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "dir",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Directory",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "24",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "outputPath",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "OutputPath",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "25",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "overwrite",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "True",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Boolean",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "26",
                "@type": "input"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "WriteFile",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "14",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "in_value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "27",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "suffix",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "28",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "out_value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "File",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "29",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "1"
        }, {
            "#text": "\n      ",
            "@name": "Color",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "15",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Color",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "30",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Color",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "31",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "2"
        }, {
            "#text": "\n      ",
            "@name": "StandardOutput",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "16",
            portSpec: {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Module",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "32",
                "@type": "input"
            },
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "Tuple",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "17",
            portSpec: {
                "@sortKey": "-1",
                "@name": "self",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Tuple",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "33",
                "@type": "output"
            },
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "Untuple",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "18",
            portSpec: {
                "@sortKey": "-1",
                "@name": "tuple",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Tuple",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "34",
                "@type": "input"
            },
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "ConcatenateString",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "19",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "str1",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "35",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "str2",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "36",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "str3",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "37",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "str4",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "38",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "39",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "Not",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "20",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "input",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Boolean",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "40",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Boolean",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "41",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "Dictionary",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "21",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "addPair",
                "#tail": "\n      ",
                portSpecItem: [ {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n        ",
                    "@values": "",
                    "@module": "Module",
                    "@id": "-1",
                    "@entryType": ""
                }, {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "1",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Module",
                    "@id": "-1",
                    "@entryType": ""
                } ],
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "42",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "addPairs",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "List",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "43",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Dictionary",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "79",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Dictionary",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "80",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "2"
        }, {
            "@name": "Null",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "22",
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "@name": "Variant",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "23",
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "Unpickle",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "24",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "input",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "44",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "result",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variant",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "45",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "PythonSource",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "25",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "source",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "46",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "self",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Module",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "47",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "SmartSource",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "26",
            portSpec: {
                "@sortKey": "-1",
                "@name": "source",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "48",
                "@type": "input"
            },
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "Unzip",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "27",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "archive_file",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "File",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "49",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "filename_in_archive",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "50",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "file",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "File",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "51",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "Round",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "28",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "in_value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Float",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "52",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "out_value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Integer",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "53",
                "@type": "output"
            }, {
                "@sortKey": "-1",
                "@name": "floor",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "True",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Boolean",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "54",
                "@type": "input"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "1"
        }, {
            "#text": "\n      ",
            "@name": "TupleToList",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "29",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "in_value",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Tuple",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "55",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "out_value",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "List",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "56",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "1"
        }, {
            "#text": "\n      ",
            "@name": "InputPort",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "30",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "name",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "57",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "optional",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Boolean",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "58",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "spec",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "59",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "ExternalPipe",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variant",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "60",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "Default",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variant",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "61",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "InternalPipe",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variant",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "62",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "OutputPort",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "31",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "name",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "63",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "optional",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Boolean",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "64",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "spec",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "65",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "InternalPipe",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variant",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "66",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "ExternalPipe",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variant",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "67",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "Group",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "32",
            portSpec: {
                "@sortKey": "-1",
                "@name": "self",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Group",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "True",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "68",
                "@type": "output"
            },
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "@name": "SubWorkflow",
            "@package": "org.vistrails.vistrails.basic",
            "@id": "33",
            "@namespace": "",
            "#tail": "\n  ",
            "@packageVersion": "2.1",
            "@version": "",
            "@baseDescriptorId": "32"
        } ],
        "@identifier": "org.vistrails.vistrails.basic",
        "@codepath": "basic_modules",
        "@id": "0"
    }, {
        "@loadConfiguration": "True",
        "@description": "No description available",
        "@name": "My SubWorkflows",
        "#tail": "\n  ",
        "@version": "1.6",
        "@identifier": "local.abstractions",
        "@codepath": "abstraction",
        "@id": "1"
    }, {
        "@loadConfiguration": "True",
        "@description": "No description available",
        "#text": "\n    ",
        "@name": "Climate",
        "#tail": "\n",
        "@version": "0.9.0",
        moduleDescriptor: [ {
            "#text": "\n      ",
            "@name": "Average",
            "@package": "org.opengeoscience.geoweb.climate",
            "@id": "34",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "variable",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.opengeoscience.geoweb.climate",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variable",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "81",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "axis",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "82",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "variable",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.opengeoscience.geoweb.climate",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variable",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "83",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "0.9.0",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "ToGeoJSON",
            "@package": "org.opengeoscience.geoweb.climate",
            "@id": "35",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "variable",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.opengeoscience.geoweb.climate",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variable",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "84",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "JSON",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "85",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "0.9.0",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "Dataset",
            "@package": "org.opengeoscience.geoweb.climate",
            "@id": "36",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "file",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "edu.utah.sci.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "86",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "self",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.opengeoscience.geoweb.climate",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Dataset",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "87",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "0.9.0",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "Variable",
            "@package": "org.opengeoscience.geoweb.climate",
            "@id": "37",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "dataset",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.opengeoscience.geoweb.climate",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Dataset",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "88",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "time",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "edu.utah.sci.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Integer",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "89",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "name",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "edu.utah.sci.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "90",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "self",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.opengeoscience.geoweb.climate",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variable",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "91",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "0.9.0",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "SubSelect",
            "@package": "org.opengeoscience.geoweb.climate",
            "@id": "38",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "variable",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.opengeoscience.geoweb.climate",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variable",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "92",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "axis",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "93",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "start",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "94",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "end",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.vistrails.vistrails.basic",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "String",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "95",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "variable",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.opengeoscience.geoweb.climate",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variable",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "96",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n    ",
            "@packageVersion": "0.9.0",
            "@version": "",
            "@baseDescriptorId": "0"
        }, {
            "#text": "\n      ",
            "@name": "MonthlyTimeBounds",
            "@package": "org.opengeoscience.geoweb.climate",
            "@id": "39",
            portSpec: [ {
                "@sortKey": "-1",
                "@name": "variable",
                "#tail": "\n      ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.opengeoscience.geoweb.climate",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variable",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "97",
                "@type": "input"
            }, {
                "@sortKey": "-1",
                "@name": "variable",
                "#tail": "\n    ",
                portSpecItem: {
                    "@default": "",
                    "@package": "org.opengeoscience.geoweb.climate",
                    "@pos": "0",
                    "@label": "",
                    "@namespace": "",
                    "#tail": "\n      ",
                    "@values": "",
                    "@module": "Variable",
                    "@id": "-1",
                    "@entryType": ""
                },
                "@maxConns": "-1",
                "@optional": "False",
                "@minConns": "0",
                "#text": "\n        ",
                "@id": "98",
                "@type": "output"
            } ],
            "@namespace": "",
            "#tail": "\n  ",
            "@packageVersion": "0.9.0",
            "@version": "",
            "@baseDescriptorId": "0"
        } ],
        "@identifier": "org.opengeoscience.geoweb.climate",
        "@codepath": "Climate",
        "@id": "2"
    } ],
    "@version": "1.0.3",
    "@{http://www.w3.org/2001/XMLSchema-instance}schemaLocation": "http://www.vistrails.org/registry.xsd",
    "#text": "\n  ",
    "@id": ""
};

wflModule.inputModule = function(options, data) {
    "use strict";
    if (!(this instanceof wflModule.inputModule)) {
        return new wflModule.inputModule(options, data);
    }
    this.inputPortClass = wflModule.inputPort;
    this.outputPortClass = wflModule.outputPort;
    wflModule.module.call(this, options, data);
    var m_that = this, m_metrics, m_baseRecomputeMetrics = this.recomputeMetrics, m_baseGetMetrics = this.getMetrics;
    this.recomputeMetrics = function(ctx, currentWorkflowStyle) {
        m_baseRecomputeMetrics.call(this, ctx, currentWorkflowStyle);
        var base_metrics = m_baseGetMetrics.call(this), new_metrics, portWidth = currentWorkflowStyle.module.port.width, key, textHeight = 15, totalInPortHeight = currentWorkflowStyle.module.port.inputHeight + textHeight + currentWorkflowStyle.module.port.inputYPad + currentWorkflowStyle.module.port.inpad, totalOutPortHeight = textHeight + currentWorkflowStyle.module.port.outpad, inPortsHeight = this.inPortCount() * totalInPortHeight + currentWorkflowStyle.module.text.xpad, outPortsHeight = this.outPortCount() * totalOutPortHeight, m_data = this.data(), fontMetrics = this.getFontMetrics(ctx, currentWorkflowStyle), textWidth = fontMetrics.width + currentWorkflowStyle.module.text.xpad * 2, inPortsWidth = this.getInPorts().length > 0 ? this.getInPorts()[0].getElement().width : currentWorkflowStyle.module.port.inputWidth, moduleWidth = Math.max(inPortsWidth + portWidth * 2 + currentWorkflowStyle.module.port.pad * 4, textWidth + currentWorkflowStyle.module.text.xpad * 2, currentWorkflowStyle.module.minWidth), moduleHeight = Math.max(inPortsHeight, outPortsHeight, currentWorkflowStyle.module.minWidth) + currentWorkflowStyle.module.text.ypad * 2 + textHeight, mx = Math.floor(this.data().location["@x"] - moduleWidth / 2), my = -Math.floor(this.data().location["@y"]), inPortY = my + currentWorkflowStyle.module.port.pad + textHeight + portWidth * 2, outPortY = my + moduleHeight - currentWorkflowStyle.module.port.pad - portWidth;
        new_metrics = {
            mx: mx,
            my: my,
            fontMetrics: fontMetrics,
            textWidth: textWidth,
            moduleWidth: moduleWidth,
            textHeight: textHeight,
            moduleHeight: moduleHeight,
            inPortX: mx + currentWorkflowStyle.module.port.pad,
            inPortY: inPortY,
            outPortX: mx + moduleWidth - currentWorkflowStyle.module.port.pad - portWidth,
            outPortY: outPortY,
            outPortTextX: mx + inPortsWidth + portWidth + currentWorkflowStyle.module.port.pad * 4
        };
        wflModule.utils.merge_options_in_place(base_metrics, new_metrics);
        m_metrics = base_metrics;
        for (key in this.getInPorts()) {
            if (this.getInPorts().hasOwnProperty(key)) {
                this.getInPorts()[key].setPosition(base_metrics.inPortX, inPortY);
                inPortY += currentWorkflowStyle.module.port.inputHeight + textHeight + currentWorkflowStyle.module.port.inputYPad + currentWorkflowStyle.module.port.inpad;
            }
        }
        for (key in this.getOutPorts()) {
            if (this.getOutPorts().hasOwnProperty(key)) {
                this.getOutPorts()[key].setPosition(base_metrics.outPortX, outPortY);
                outPortY -= textHeight + currentWorkflowStyle.module.port.outpad;
            }
        }
    };
    this.getMetrics = function() {
        return m_metrics;
    };
    this.draw = function(ctx, drawStyle) {
        if (!m_metrics) {
            this.recomputeMetrics(ctx, drawStyle);
        }
        var mStyle = drawStyle.module, portWidth = mStyle.port.width, mx = m_metrics.mx, my = m_metrics.my, m_inPorts = this.getInPorts(), m_outPorts = this.getOutPorts(), key;
        ctx.fillStyle = mStyle.fill;
        ctx.lineWidth = mStyle.lineWidth;
        ctx.strokeStyle = this.isSelected() ? mStyle.selectedStroke : mStyle.stroke;
        ctx.save();
        if (this.isHover()) {
            ctx.shadowBlur = mStyle.shadowBlur;
            ctx.shadowColor = mStyle.shadowColor;
        }
        ctx.translate(mx, my);
        wflModule.utils.roundRect(ctx, 0, 0, m_metrics.moduleWidth, m_metrics.moduleHeight, mStyle.cornerRadius, true, true);
        ctx.restore();
        ctx.fillStyle = mStyle.port.fill;
        ctx.strokeStyle = mStyle.port.stroke;
        for (key in m_inPorts) {
            if (m_inPorts.hasOwnProperty(key)) {
                m_inPorts[key].draw(ctx, portWidth);
            }
        }
        for (key in m_outPorts) {
            if (m_outPorts.hasOwnProperty(key)) {
                m_outPorts[key].draw(ctx, portWidth);
            }
        }
        ctx.fillStyle = mStyle.text.fill;
        ctx.font = mStyle.text.font;
        ctx.fillText(this.data()["@name"], mx + Math.floor((m_metrics.moduleWidth - m_metrics.fontMetrics.width) / 2), my + m_metrics.textHeight + mStyle.text.ypad);
    };
    this.updateElementPositions = function() {
        var key, m_inPorts = this.getInPorts();
        for (key in m_inPorts) {
            m_inPorts[key].updateElementPosition(m_inPorts[key].x(), m_inPorts[key].y());
        }
    };
    this.setInput = function(name, value) {
        var key, port, portSpec, portType, m_inPorts = this.getInPorts();
        for (key in m_inPorts) {
            if (m_inPorts.hasOwnProperty(key)) {
                port = m_inPorts[key];
                if (port.data()["@name"] === name) {
                    $(port.getElement()).val(value);
                    portSpec = port.data().portSpecItem;
                    portType = portSpec["@module"];
                    this.addOrUpdateFunction(name, value, portType);
                    return;
                }
            }
        }
    };
    return this;
};

inherit(wflModule.inputModule, wflModule.module);

wflModule.outputPort = function(options, data) {
    "use strict";
    if (!(this instanceof wflModule.outputPort)) {
        return new wflModule.outputPort(options, data);
    }
    wflModule.port.call(this, options, data);
    var m_name_width = 0;
    this.draw = function(ctx, width) {
        this.drawAsCircle(ctx, width);
    };
    this.drawName = function(ctx, width) {
        var drawStyle = this.drawStyle(), metrics;
        ctx.fillStyle = drawStyle.module.text.fill;
        ctx.font = drawStyle.module.text.font;
        if (m_name_width === 0) {
            metrics = ctx.measureText(this.data()["@name"]);
            m_name_width = metrics.width;
        }
        ctx.fillText(this.data()["@name"], this.x() - width - m_name_width, this.y() + width);
    };
    return this;
};

inherit(wflModule.outputPort, wflModule.port);

wflModule.inputPort = function(options, data) {
    "use strict";
    if (!(this instanceof wflModule.inputPort)) {
        return new wflModule.inputPort(options, data);
    }
    wflModule.outputPort.call(this, options, data);
    var m_that = this, m_input_elem, m_baseSetPosition = this.setPosition;
    this.getElement = function() {
        return m_input_elem;
    };
    this.drawName = function(ctx, width) {
        var drawStyle = this.drawStyle();
        ctx.fillStyle = drawStyle.module.text.fill;
        ctx.font = drawStyle.module.text.font;
        ctx.fillText(this.data()["@name"], this.x() + width * 2, this.y() + width);
    };
    function createElementFromType(placeholder) {
        m_input_elem = document.createElement("input");
        m_input_elem.type = "text";
        m_input_elem.placeholder = wflModule.utils.defaultValue(placeholder, "");
        $(m_input_elem).css({
            position: "absolute",
            "pointer-events": "auto"
        }).change(function() {
            m_that.module().addOrUpdateFunction(m_that.data()["@name"], $(this).val(), m_that.data().portSpecItem["@module"]);
        });
        m_that.setElementValueFromData();
    }
    this.setPosition = function(x, y) {
        m_baseSetPosition(x, y);
        this.updateElementPosition(x, y);
    };
    this.updateElementPosition = function(x, y) {
        var translated = this.module().workflow().translated(), drawStyle = this.drawStyle();
        $(m_input_elem).css({
            top: y + translated.y - drawStyle.shadowBlur,
            left: x + translated.x + drawStyle.module.port.width + drawStyle.module.port.pad - drawStyle.shadowBlur
        });
    };
    this.show = function(inputContainer) {
        inputContainer.appendChild(m_input_elem);
        $(m_input_elem).show();
    };
    this.hide = function() {
        $(m_input_elem).hide();
        $(m_input_elem).detach();
    };
    this.setElementValueFromData = function() {
        $(m_input_elem).val(m_that.module().getFunctionValue(m_that.data()["@name"]));
    };
    this.delete = function() {
        $(m_input_elem).remove();
    };
    createElementFromType(this.data()["@name"]);
    return this;
};

inherit(wflModule.inputPort, wflModule.outputPort);

wflModule.layerSource = function(id, name, path, config, vars, workflow, onError) {
    "use strict";
    if (!(this instanceof wflModule.layerSource)) {
        return new wflModule.layerSource(id, name, path, config, vars, workflow, onError);
    }
    geo.archiveLayerSource.call(this, id, name, path, config, vars, onError);
    var m_time = -1, m_that = this, m_onError = wflModule.utils.defaultValue(onError, function(errorString) {}), m_workflow = workflow;
    this.getData = function(time, callback) {
        var asyncVal = false, retVal = [], errorString = null, reader = null;
        if (m_time === time) {
            console.log("[info] No new data as timestamp has not changed.");
            return m_that.resultCache();
        }
        m_time = time;
        function replacer(key, value) {
            if (typeof value === "number") {
                return value.toString();
            }
            return value;
        }
        if (callback) {
            asyncVal = true;
        }
        if (!isNaN(parseFloat(m_time))) {
            try {
                m_workflow.getModuleByName("Variable").setInput("time", m_time);
            } catch (e) {
                console.log("[info] Unable to set time on workflow");
            }
        }
        $.ajax({
            type: "POST",
            url: "/services/vistrail/execute/",
            data: {
                workflowJSON: JSON.stringify(m_workflow.data(), replacer, 2)
            },
            dataType: "json",
            async: asyncVal,
            success: function(response) {
                if (response.error !== null) {
                    errorString = "[error] " + response.error ? response.error : "no results returned from server";
                    console.log(errorString);
                    m_onError(errorString);
                } else {
                    reader = vgl.geojsonReader();
                    retVal = reader.readGJObject(jQuery.parseJSON(response.result));
                }
            },
            error: function(jqXHR, textStatus, errorThrown) {
                errorString = "Error reading " + this.name() + ": " + errorThrown;
                console.log(errorString);
                m_onError(errorString);
            }
        });
        if (callback) {
            callback(retVal);
        }
        m_that.p_setResultCache(retVal);
        return retVal;
    };
    this.workflow = function() {
        return m_workflow;
    };
    return this;
};

inherit(wflModule.layerSource, geo.archiveLayerSource);

wflModule.editorOptions = function() {
    "use strict";
    if (!(this instanceof wflModule.editorOptions)) {
        return new wflModule.editorOptions();
    }
    this.div = document.createElement("div");
    this.drawStyle = climatePipesStyle;
    return this;
};

wflModule.editor = function(options) {
    "use strict";
    this.events = {
        moduleAdded: "moduleAdded",
        connectionAdded: "connectionAdded"
    };
    if (!(this instanceof wflModule.editor)) {
        return new wflModule.editor(options);
    }
    vgl.object.call(this);
    options = typeof options !== "undefined" ? options : {};
    options = wflModule.utils.merge_options(wflModule.editorOptions(), options);
    if (typeof options.div === "string") {
        options.div = document.getElementById(options.div);
    }
    var m_that = this, m_mainDiv = options.div, m_style = options.drawStyle, m_moduleDiv = document.createElement("div"), m_moduleTable = document.createElement("table"), m_canvasContainer = document.createElement("div"), m_canvasInput = document.createElement("div"), m_canvas = document.createElement("canvas"), m_context, m_workflow;
    this.setWorkflow = function(workflow) {
        m_workflow = workflow;
    };
    this.workflow = function() {
        return m_workflow;
    };
    function refreshContext() {
        m_context = m_canvas.getContext("2d");
    }
    refreshContext();
    this.resize = function() {
        var rect = m_canvasContainer.getBoundingClientRect(), translated = m_workflow.translated();
        m_canvas.width = rect.width;
        m_canvas.height = rect.height;
        refreshContext();
        m_context.translate(translated.x, translated.y);
        m_that.drawWorkflow();
    };
    this.show = function() {
        m_that.resize();
        m_workflow.show(m_canvasInput);
    };
    function addDraggableStyleSheetRule() {
        var stylesheet = document.styleSheets[0], selector = "[draggable]", rule = [ "{-moz-user-select: none;", "-khtml-user-select: none;", "-webkit-user-select: none;", "user-select: none;", "-khtml-user-drag: element;", "-webkit-user-drag: element;", "cursor: move;}" ].join(""), pos;
        if (stylesheet.insertRule) {
            try {
                pos = stylesheet.cssRules ? stylesheet.cssRules.length : 0;
                stylesheet.insertRule(selector + rule, pos);
            } catch (e) {
                if (stylesheet.addRule) {
                    stylesheet.addRule(selector, rule, -1);
                }
            }
        } else if (stylesheet.addRule) {
            stylesheet.addRule(selector, rule, -1);
        }
    }
    function setupDom() {
        m_moduleTable.appendChild(document.createElement("tBody"));
        m_moduleDiv.appendChild(m_moduleTable);
        m_canvasContainer.appendChild(m_canvas);
        m_canvasContainer.appendChild(m_canvasInput);
        m_mainDiv.appendChild(m_moduleDiv);
        m_mainDiv.appendChild(m_canvasContainer);
    }
    function setupCSS() {
        $(m_moduleDiv).css({
            height: "100%",
            width: 225,
            overflow: "auto",
            "float": "left"
        });
        $(m_canvasContainer).css({
            position: "relative",
            height: "100%",
            overflow: "hidden",
            cursor: m_style.cursor
        });
        $(m_canvasInput).css({
            position: "absolute",
            top: m_style.shadowBlur,
            left: m_style.shadowBlur,
            bottom: m_style.shadowBlur,
            right: m_style.shadowBlur,
            overflow: "hidden",
            "pointer-events": "none"
        });
    }
    function addTextureFillToStyle() {
        var modulePattern = new Image(), workflowPattern = new Image();
        modulePattern.onload = function() {
            m_style.module.fill = m_context.createPattern(modulePattern, "repeat");
        };
        modulePattern.src = "/common/img/squairy_light.png";
        workflowPattern.onload = function() {
            m_style.fill = m_context.createPattern(workflowPattern, "repeat");
        };
        workflowPattern.src = "/common/img/tweed.png";
    }
    function ctxMousePos(event) {
        var totalOffsetX = 0, totalOffsetY = 0, currentElement = m_canvas, translated = m_workflow.translated();
        do {
            totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
            totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
            currentElement = currentElement.offsetParent;
        } while (currentElement);
        return {
            x: event.pageX - totalOffsetX - translated.x,
            y: event.pageY - totalOffsetY - translated.y
        };
    }
    function setupDragAndDrop() {
        var $canvas = $(m_canvas);
        $canvas.on("dragover", function(e) {
            if (e.originalEvent) {
                e = e.originalEvent;
            }
            if (e.preventDefault) {
                e.preventDefault();
            }
            e.dataTransfer.dropEffect = "copy";
        });
        $canvas.on("drop", function(e) {
            var ctxPos;
            if (e.originalEvent) {
                e = e.originalEvent;
            }
            if (e.preventDefault) {
                e.preventDefault();
            }
            if (e.stopPropagation) {
                e.stopPropagation();
            }
            ctxPos = ctxMousePos(e);
            m_workflow.addNewModule(e.dataTransfer.getData("Text"), ctxPos.x, ctxPos.y);
            m_workflow.show(m_canvasInput);
            m_that.drawWorkflow();
            return false;
        });
    }
    function addModuleToList(moduleInfo, $moduleTableBody) {
        var $text = $(document.createElement("div")), $td = $(document.createElement("td")), $tr = $(document.createElement("tr"));
        $text.append(moduleInfo["@name"]).attr("draggable", "true").data("moduleInfo", moduleInfo).on("dragstart", function(e) {
            if (e.originalEvent) {
                e = e.originalEvent;
            }
            e.dataTransfer.effectAllowed = "copy";
            e.dataTransfer.setData("Text", JSON.stringify($(this).data("moduleInfo")));
        });
        $moduleTableBody.append($tr.append($td.append($text)));
    }
    function setupWorkflowModuleList() {
        var $moduleTableBody = $(m_moduleTable).find("tbody:last"), pkg, identifier, moduleInfo, i, j;
        for (i = 0; i < wflModule.registry.package.length; i++) {
            pkg = wflModule.registry.package[i];
            identifier = pkg["@identifier"];
            if (!wflModule.utils.moduleRegistryMap.hasOwnProperty(identifier)) {
                wflModule.utils.moduleRegistryMap[identifier] = {};
            }
            if (pkg.hasOwnProperty("moduleDescriptor")) {
                for (j = 0; j < pkg.moduleDescriptor.length; j++) {
                    moduleInfo = pkg.moduleDescriptor[j];
                    wflModule.utils.moduleRegistryMap[identifier][moduleInfo["@name"]] = moduleInfo;
                    addModuleToList(moduleInfo, $moduleTableBody);
                }
            }
        }
    }
    function setupInteraction() {
        var $canvas = $(m_canvas), panning, firstPoint, lastPoint, lastPanEvent, draggingPort, draggingPortPos, draggingPortModule, draggingModule, hoverModule = null, hoverPort = null, tempConnection = wflModule.connection();
        $canvas.mousedown(function(e) {
            var module;
            firstPoint = lastPoint = ctxMousePos(e);
            module = m_workflow.moduleByPos(lastPoint);
            if (module !== null) {
                draggingPort = module.portByPos(lastPoint);
                if (draggingPort) {
                    draggingPortPos = lastPoint;
                    draggingPortModule = module;
                } else {
                    draggingModule = module;
                }
                return;
            }
            panning = true;
            lastPanEvent = e;
        });
        $canvas.mousemove(function(e) {
            var newPoint;
            function getNewHoverPort() {
                hoverPort = hoverModule.portByPos(newPoint);
                if (hoverPort !== null) {
                    hoverPort.setHover(true);
                    $(m_canvasContainer).css("cursor", m_style.module.port.cursor);
                }
            }
            function getNewHoverModule() {
                hoverModule = m_workflow.moduleByPos(newPoint);
                if (hoverModule !== null) {
                    hoverModule.setHover(true);
                    m_that.drawWorkflow();
                    $(m_canvasContainer).css("cursor", m_style.module.cursor);
                }
            }
            if (draggingModule) {
                newPoint = ctxMousePos(e);
                draggingModule.data().location["@x"] += newPoint.x - lastPoint.x;
                draggingModule.data().location["@y"] -= newPoint.y - lastPoint.y;
                draggingModule.recomputeMetrics($canvas[0].getContext("2d"), m_style);
                lastPoint = newPoint;
                m_workflow.draw(m_context);
            } else if (draggingPort) {
                lastPoint = ctxMousePos(e);
                m_workflow.draw(m_context);
                tempConnection.drawCurve(m_context, m_style, {
                    cx1: draggingPortPos.x,
                    cy1: draggingPortPos.y,
                    cx2: lastPoint.x,
                    cy2: lastPoint.y
                });
            } else if (panning) {
                m_workflow.translate(this.getContext("2d"), e.clientX - lastPanEvent.clientX, e.clientY - lastPanEvent.clientY);
                lastPanEvent = e;
                m_that.drawWorkflow();
                m_workflow.updateElementPositions();
            } else {
                newPoint = ctxMousePos(e);
                if (hoverModule !== null) {
                    if (!hoverModule.contains(newPoint)) {
                        hoverModule.setHover(false);
                        m_that.drawWorkflow();
                        $(m_canvasContainer).css("cursor", m_style.cursor);
                        getNewHoverModule();
                    }
                } else {
                    getNewHoverModule();
                }
                if (hoverModule !== null) {
                    if (hoverPort !== null) {
                        if (!hoverPort.contains(newPoint)) {
                            hoverPort.setHover(false);
                            $(m_canvasContainer).css("cursor", m_style.module.cursor);
                            getNewHoverPort();
                        }
                    } else {
                        getNewHoverPort();
                    }
                }
            }
        });
        $canvas.mouseup(function(e) {
            panning = false;
            if (draggingPort) {
                var port, modules = m_workflow.modules(), key, module;
                for (key in modules) {
                    if (modules.hasOwnProperty(key)) {
                        module = modules[key];
                        if (module.contains(lastPoint)) {
                            port = module.portByPos(lastPoint);
                            if (port) {
                                m_workflow.addConnection(draggingPortModule, draggingPort, module, port);
                                break;
                            }
                        }
                    }
                }
                draggingPort = null;
                draggingPortModule = null;
                draggingPortPos = null;
                m_that.drawWorkflow();
            } else if (draggingModule) {
                if (firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y) {
                    draggingModule.toggleSelected();
                    m_that.drawWorkflow();
                }
            }
            draggingModule = null;
        });
        $canvas.mouseout(function(e) {
            panning = false;
        });
    }
    function setup() {
        addDraggableStyleSheetRule();
        setupDom();
        setupCSS();
        addTextureFillToStyle();
        setupDragAndDrop();
        setupWorkflowModuleList();
        setupInteraction();
    }
    this.drawWorkflow = function() {
        m_workflow.draw(m_context, m_style);
    };
    setup();
    return this;
};

inherit(wflModule.editor, vgl.object);

var baseStyle = {
    fill: "darkgray",
    module: {
        text: {
            fill: "#000000",
            font: "15pt wingdings teal",
            xpad: 20,
            ypad: 10
        },
        port: {
            width: 15,
            pad: 5,
            fill: "lightgray",
            stroke: "black",
            lineWidth: 1
        },
        fill: "lightgray",
        stroke: "black",
        selectedStroke: "yellow",
        lineWidth: 2,
        minWidth: 100,
        ypad: 40,
        xpad: 20
    },
    conn: {
        stroke: "black",
        lineWidth: 2,
        bezierOffset: 75
    }
};

var vistrailStyle = jQuery.extend(true, {}, baseStyle);

var climatePipesStyle = jQuery.extend(true, {}, baseStyle);

climatePipesStyle.fill = "teal";

climatePipesStyle.module.port.inputHeight = 30;

climatePipesStyle.module.port.inputWidth = 220;

climatePipesStyle.module.port.inputYPad = 5;

climatePipesStyle.module.port.inpad = 10;

climatePipesStyle.module.port.outpad = 10;

climatePipesStyle.module.port.fill = "white";

climatePipesStyle.module.port.stroke = "lightgreen";

climatePipesStyle.module.port.lineWidth = 2;

climatePipesStyle.module.port.cursor = "crosshair";

climatePipesStyle.module.shadowBlur = 10;

climatePipesStyle.module.shadowColor = "yellow";

climatePipesStyle.module.cornerRadius = 25;

climatePipesStyle.module.lineWidth = 5;

climatePipesStyle.module.stroke = "lightgreen";

climatePipesStyle.module.cursor = "pointer";

climatePipesStyle.shadowBlur = 10;

climatePipesStyle.shadowColor = "lightblue";

climatePipesStyle.cornerRadius = 25;

climatePipesStyle.lineWidth = 5;

climatePipesStyle.stroke = "lightgreen";

climatePipesStyle.cursor = "move";

climatePipesStyle.conn.stroke = "lightgreen";

climatePipesStyle.conn.lineWidth = 4;

var staticWorkflows = {
    Default: {
        workflow: {
            "@name": "untitled",
            "@version": "1.0.3",
            "@{http://www.w3.org/2001/XMLSchema-instance}schemaLocation": "http://www.vistrails.org/workflow.xsd",
            connection: [ {
                "#tail": "\n  ",
                "#text": "\n    ",
                "@id": "0",
                port: [ {
                    "@moduleName": "Dataset",
                    "@name": "self",
                    "#tail": "\n    ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Dataset)",
                    "@id": "0",
                    "@type": "source",
                    "@moduleId": "0"
                }, {
                    "@moduleName": "Variable",
                    "@name": "dataset",
                    "#tail": "\n  ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Dataset)",
                    "@id": "1",
                    "@type": "destination",
                    "@moduleId": "1"
                } ]
            }, {
                "#tail": "\n  ",
                "#text": "\n    ",
                "@id": "1",
                port: [ {
                    "@moduleName": "Variable",
                    "@name": "self",
                    "#tail": "\n    ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Variable)",
                    "@id": "2",
                    "@type": "source",
                    "@moduleId": "1"
                }, {
                    "@moduleName": "ToGeoJSON",
                    "@name": "variable",
                    "#tail": "\n  ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Variable)",
                    "@id": "3",
                    "@type": "destination",
                    "@moduleId": "2"
                } ]
            } ],
            module: [ {
                "@name": "Dataset",
                "@package": "org.opengeoscience.geoweb.climate",
                "@version": "0.9.0",
                "@namespace": "",
                "#tail": "\n  ",
                "@cache": "1",
                location: {
                    "#tail": "\n  ",
                    "@x": "320.0",
                    "@y": "-120.0",
                    "@id": "0"
                },
                "#text": "\n    ",
                "@id": "0"
            }, {
                "@name": "Variable",
                "@package": "org.opengeoscience.geoweb.climate",
                "@version": "0.9.0",
                "@namespace": "",
                "#tail": "\n  ",
                "@cache": "1",
                location: {
                    "#tail": "\n  ",
                    "@x": "750.0",
                    "@y": "-160.0",
                    "@id": "1"
                },
                "#text": "\n    ",
                "@id": "1"
            }, {
                "@name": "ToGeoJSON",
                "@package": "org.opengeoscience.geoweb.climate",
                "@version": "0.9.0",
                "@namespace": "",
                "#tail": "\n",
                "@cache": "1",
                location: {
                    "#tail": "\n  ",
                    "@x": "1200.0",
                    "@y": "-260.0",
                    "@id": "2"
                },
                "#text": "\n    ",
                "@id": "2"
            } ],
            "@vistrail_id": "",
            "#text": "\n  ",
            "@id": "0"
        }
    },
    "10 Year Average": {
        workflow: {
            "@name": "untitled",
            "@version": "1.0.3",
            "@{http://www.w3.org/2001/XMLSchema-instance}schemaLocation": "http://www.vistrails.org/workflow.xsd",
            connection: [ {
                "#tail": "\n  ",
                "#text": "\n    ",
                "@id": "1",
                port: [ {
                    "@moduleName": "Dataset",
                    "@name": "self",
                    "#tail": "\n    ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Dataset)",
                    "@id": "2",
                    "@type": "source",
                    "@moduleId": "1"
                }, {
                    "@moduleName": "Variable",
                    "@name": "dataset",
                    "#tail": "\n  ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Dataset)",
                    "@id": "3",
                    "@type": "destination",
                    "@moduleId": "2"
                } ]
            }, {
                "#tail": "\n  ",
                "#text": "\n    ",
                "@id": "2",
                port: [ {
                    "@moduleName": "Variable",
                    "@name": "self",
                    "#tail": "\n    ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Variable)",
                    "@id": "4",
                    "@type": "source",
                    "@moduleId": "2"
                }, {
                    "@moduleName": "SubSelect",
                    "@name": "variable",
                    "#tail": "\n  ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Variable)",
                    "@id": "5",
                    "@type": "destination",
                    "@moduleId": "3"
                } ]
            }, {
                "#tail": "\n  ",
                "#text": "\n    ",
                "@id": "3",
                port: [ {
                    "@moduleName": "SubSelect",
                    "@name": "variable",
                    "#tail": "\n    ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Variable)",
                    "@id": "6",
                    "@type": "source",
                    "@moduleId": "3"
                }, {
                    "@moduleName": "MonthlyTimeBounds",
                    "@name": "variable",
                    "#tail": "\n  ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Variable)",
                    "@id": "7",
                    "@type": "destination",
                    "@moduleId": "4"
                } ]
            }, {
                "#tail": "\n  ",
                "#text": "\n    ",
                "@id": "4",
                port: [ {
                    "@moduleName": "MonthlyTimeBounds",
                    "@name": "variable",
                    "#tail": "\n    ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Variable)",
                    "@id": "8",
                    "@type": "source",
                    "@moduleId": "4"
                }, {
                    "@moduleName": "Average",
                    "@name": "variable",
                    "#tail": "\n  ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Variable)",
                    "@id": "9",
                    "@type": "destination",
                    "@moduleId": "5"
                } ]
            }, {
                "#tail": "\n  ",
                "#text": "\n    ",
                "@id": "5",
                port: [ {
                    "@moduleName": "Average",
                    "@name": "variable",
                    "#tail": "\n    ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Variable)",
                    "@id": "10",
                    "@type": "source",
                    "@moduleId": "5"
                }, {
                    "@moduleName": "ToGeoJSON",
                    "@name": "variable",
                    "#tail": "\n  ",
                    "@signature": "(org.opengeoscience.geoweb.climate:Variable)",
                    "@id": "11",
                    "@type": "destination",
                    "@moduleId": "6"
                } ]
            } ],
            module: [ {
                "function": {
                    "@name": "file",
                    "#tail": "\n  ",
                    "@id": "6",
                    "@pos": "0",
                    "#text": "\n      ",
                    parameter: {
                        "@val": "",
                        "@name": "<no description>",
                        "#tail": "\n    ",
                        "@pos": "0",
                        "@alias": "",
                        "@id": "8",
                        "@type": "org.vistrails.vistrails.basic:String"
                    }
                },
                "@name": "Dataset",
                "@package": "org.opengeoscience.geoweb.climate",
                "@version": "0.9.0",
                "@namespace": "",
                "#tail": "\n  ",
                "@cache": "1",
                location: {
                    "#tail": "\n    ",
                    "@x": "-259.0",
                    "@y": "-33.0",
                    "@id": "1"
                },
                "#text": "\n    ",
                "@id": "1"
            }, {
                "function": {
                    "@name": "name",
                    "#tail": "\n  ",
                    "@id": "1",
                    "@pos": "0",
                    "#text": "\n      ",
                    parameter: {
                        "@val": "",
                        "@name": "<no description>",
                        "#tail": "\n    ",
                        "@pos": "0",
                        "@alias": "",
                        "@id": "9",
                        "@type": "org.vistrails.vistrails.basic:String"
                    }
                },
                "@name": "Variable",
                "@package": "org.opengeoscience.geoweb.climate",
                "@version": "0.9.0",
                "@namespace": "",
                "#tail": "\n  ",
                "@cache": "1",
                location: {
                    "#tail": "\n    ",
                    "@x": "50.0",
                    "@y": "-177.0",
                    "@id": "7"
                },
                "#text": "\n    ",
                "@id": "2"
            }, {
                "function": [ {
                    "@name": "axis",
                    "#tail": "\n    ",
                    "@id": "2",
                    "@pos": "0",
                    "#text": "\n      ",
                    parameter: {
                        "@val": "time",
                        "@name": "<no description>",
                        "#tail": "\n    ",
                        "@pos": "0",
                        "@alias": "",
                        "@id": "2",
                        "@type": "org.vistrails.vistrails.basic:String"
                    }
                }, {
                    "@name": "end",
                    "#tail": "\n    ",
                    "@id": "3",
                    "@pos": "1",
                    "#text": "\n      ",
                    parameter: {
                        "@val": "1983-01-01",
                        "@name": "<no description>",
                        "#tail": "\n    ",
                        "@pos": "0",
                        "@alias": "",
                        "@id": "3",
                        "@type": "org.vistrails.vistrails.basic:String"
                    }
                }, {
                    "@name": "start",
                    "#tail": "\n  ",
                    "@id": "4",
                    "@pos": "2",
                    "#text": "\n      ",
                    parameter: {
                        "@val": "1973-01-01",
                        "@name": "<no description>",
                        "#tail": "\n    ",
                        "@pos": "0",
                        "@alias": "",
                        "@id": "4",
                        "@type": "org.vistrails.vistrails.basic:String"
                    }
                } ],
                "@name": "SubSelect",
                "@package": "org.opengeoscience.geoweb.climate",
                "@version": "0.9.0",
                "@namespace": "",
                "#tail": "\n  ",
                "@cache": "1",
                location: {
                    "#tail": "\n    ",
                    "@x": "380.0",
                    "@y": "-208.0",
                    "@id": "3"
                },
                "#text": "\n    ",
                "@id": "3"
            }, {
                "@name": "MonthlyTimeBounds",
                "@package": "org.opengeoscience.geoweb.climate",
                "@version": "0.9.0",
                "@namespace": "",
                "#tail": "\n  ",
                "@cache": "1",
                location: {
                    "#tail": "\n  ",
                    "@x": "705.0",
                    "@y": "-336.0",
                    "@id": "4"
                },
                "#text": "\n    ",
                "@id": "4"
            }, {
                "function": {
                    "@name": "axis",
                    "#tail": "\n  ",
                    "@id": "5",
                    "@pos": "0",
                    "#text": "\n      ",
                    parameter: {
                        "@val": "t",
                        "@name": "<no description>",
                        "#tail": "\n    ",
                        "@pos": "0",
                        "@alias": "",
                        "@id": "5",
                        "@type": "org.vistrails.vistrails.basic:String"
                    }
                },
                "@name": "Average",
                "@package": "org.opengeoscience.geoweb.climate",
                "@version": "0.9.0",
                "@namespace": "",
                "#tail": "\n  ",
                "@cache": "1",
                location: {
                    "#tail": "\n    ",
                    "@x": "992.0",
                    "@y": "-508.0",
                    "@id": "5"
                },
                "#text": "\n    ",
                "@id": "5"
            }, {
                "@name": "ToGeoJSON",
                "@package": "org.opengeoscience.geoweb.climate",
                "@version": "0.9.0",
                "@namespace": "",
                "#tail": "\n",
                "@cache": "1",
                location: {
                    "#tail": "\n  ",
                    "@x": "1317.0",
                    "@y": "-532.0",
                    "@id": "6"
                },
                "#text": "\n    ",
                "@id": "6"
            } ],
            "@vistrail_id": "",
            "#text": "\n  ",
            "@id": "0"
        }
    }
};