<commit_before>define(function(require, exports, module) {
    "use strict";

    main.consumes = ["Plugin", "auth", "http", "api", "error_handler", "metrics"];
    main.provides = ["vfs.endpoint"];

    return main;

    function main(options, imports, register) {
        var Plugin = imports.Plugin;
        var auth = imports.auth;
        var http = imports.http;
        var api = imports.api;
        var errorHandler = imports.error_handler;
        var metrics = imports.metrics;

        var PARALLEL_SEARCHES = 2;

        /***** Initialization *****/

        var plugin = new Plugin("Ajax.org", main.consumes);
        var emit = plugin.getEmitter();

        var urlServers, lastVfs;
        var query = require("url").parse(document.location.href, true).query;
        if (query.vfs) {
            if (!query.vfs.match(/^https:\/\/.*\/vfs$/))
                alert("Bad VFS URL passed, expected: https://host/vfs");
            urlServers = [{
                url: query.vfs,
                region: "url"
            }];
        }
        if (query.vfs || query.region) {
            var vfs = recallVfs();
            if (vfs) {
                if (query.vfs && query.vfs !== vfs.url)
                    deleteOldVfs();
                else if (query.region && query.region !== vfs.region)
                    deleteOldVfs();
            }
        }
        if (query.vfs)
            options.updateServers = false;

        var region = query.region || options.region;

        var servers;
        var pendingServerReqs = [];

        initDefaultServers();

        options.pid = options.pid || 1;

        /***** Methods *****/

        function initDefaultServers(baseURI) {
            if (options.getServers)
                return options.getServers(init);
            init();

            function init() {
                options.getServers = undefined;
                var loc = require("url").parse(baseURI || document.baseURI || window.location.href);
                var defaultServers = [{
                    url: loc.protocol + "//" + loc.hostname + (loc.port ? ":" + loc.port : "") + "/vfs",
                    region: "default"
                }];
                servers = (urlServers || options.servers || defaultServers).map(function(server) {
                    server.url = server.url.replace(/\/*$/, "");
                    return server;
                });
                pendingServerReqs.forEach(function(cb) {
                    cb(null, servers);
                });
            }
        }

        function getServers(callback) {
            if (typeof options.getServers == "function")
                return pendingServerReqs.push(callback);

            if (!options.updateServers)
                return callback(null, servers);

            // first time take the ones from the options
            var _servers = servers;
            if (_servers) {
                servers = null;
                return callback(null, _servers);
            }

            api.vfs.get("servers", function(err, servers) {
                if (err) return callback(err);

                return callback(null, servers.servers);
            });
        }

        function getVfsEndpoint(version, callback) {
            getServers(function(err, _servers) {
                if (err) {
                    if (err.code !== "EDISCONNECT")
                        errorHandler.reportError(new Error("Could not get list of VFS servers"), { cause: err });
                    metrics.increment("vfs.failed.connect_getservers", 1, true);
                    initDefaultServers();
                    _servers = servers;
                }

                getVfsUrl(version, _servers, function(err, vfsid, url, region) {
                    if (err) return callback(err);

                    callback(null, {
                        url: url,
                        region: region,
                        home: vfsid + "/home",
                        project: vfsid + "/workspace",
                        socket: vfsid + "/socket",
                        ping: vfsid,
                        serviceUrl: vfsid,
                    });
                });
            });
        }

        function isOnline(callback) {
            http.request("/_ping", {
                timeout: 3000,
                headers: {
                    Accept: "application/json"
                }
            }, function(err, data, res) {
                callback(err, !err);
            });
        }

        function isServerAlive(url, callback) {
            auth.request(url, {
                headers: {
                    Accept: "application/json"
                }
            }, function(err, data, res) {
                if (err)
                    deleteOldVfs();

                callback(err, !err);
            });
        }

        function getVfsUrl(version, vfsServers, callback) {
            var vfs = recallVfs();

            if (vfs && vfs.vfsid) {
                auth.request(vfs.vfsid, {
                    method: "GET",
                    headers: {
                        Accept: "application/json"
                    }
                }, function(err, res) {
                    if (err) {
                        deleteOldVfs();
                        return getVfsUrl(version, vfsServers, callback);
                    }
                    callback(null, vfs.vfsid, vfs.url, vfs.region);
                });
                return;
            }

            var servers = shuffleServers(version, vfsServers);

            // check for version
            if (vfsServers.length && !servers.length)
                return onProtocolChange(callback);

            var latestServer = 0;
            var foundServer = false;

            /* Create a callback that is only ever called once */
            var mainCallback = callback;
            callback = function() {
                if (!foundServer) {
                    foundServer = true;
                    var args = Array.prototype.slice.call(arguments);
                    return mainCallback.apply(this, args);
                }
            };

            // just take the first server that doesn't return an error
            function tryNext(i) {
                if (foundServer) return false;
                if (i >= servers.length) {
                    metrics.increment("vfs.failed.connect_all", 1, true);
                    return callback(new Error("Disconnected: Could not reach your workspace. Please try again later."));
                }

                var server = servers[i];
                auth.request(server.url + "/" + options.pid, {
                    method: "POST",
                    timeout: 120000,
                    body: {
                        version: version
                    },
                    headers: {
                        Accept: "application/json"
                    }
                }, function(err, res) {
                    // the workspace is not configured correctly
                    if (err && res && res.error) {
                        if (err.code == 429) {
                            // rate limited
                            setTimeout(function() {
                                tryNext(i);
                            }, res.error.retryIn || 10000);
                            return;
                        }
                        else if (err.code == 412 && res.error && res.error.subtype == "protocol_mismatch") {
                            return onProtocolChange(callback);
                        }
                        else if (err.code == 412) {
                            callback(fatalError(res.error.message, "dashboard"));
                            return;
                        }
                        else if (err.code === 428 && res.error) {
                            emit("restore", {
                                projectState: res.error.projectState,
                                premium: res.error.premium,
                                progress: res.error.progress || {
                                    progress: 0,
                                    nextProgress: 0,
                                    message: ""
                                }
                            });
                            setTimeout(function() {
                                tryNext(i);
                            }, res.error.retryIn || 10000);
                            return;
                        }
                        else if (err.code == 403) {
                            if (res.error.blocked)
                                callback(fatalError(res.error.message, "dashboard"));

                            // forbidden. User doesn't have access
                            // wait a while before trying again
                            setTimeout(function() {
                                tryNext(i);
                            }, 10000);
                            return;
                        }
                        else if (err.code === 500 && res && res.error && res.error.cause) {
                            return callback(res.error.cause.message);
                        }
                    }

                    if (err) {
                        setTimeout(function() {
                            tryNext(++latestServer);
                        }, 2000);
                        return;
                    }

                    if (!foundServer) {
                        var vfs = rememberVfs(server, res.vfsid);
                        callback(null, vfs.vfsid, server.url, server.region);
                    }
                });
            }

            function startParallelSearches (totalRunners) {
                var attemptedServers = {};
                for (var s = 0; s < servers.length && s < totalRunners; s++)  {
                    latestServer = s;
                    var server = servers[s];
                    var serverHostUrl = getHostFromServerUrl(server.url);
                    if (!attemptedServers[serverHostUrl]) {
                        attemptedServers[serverHostUrl] = true;
                        tryNext(s);
                    }
                }
            }

            startParallelSearches(PARALLEL_SEARCHES);
        }

        function getHostFromServerUrl(serverUrl) {
            // server.url looks like: https://vfs-gce-ae-09-2.c9.io or https://vfs.c9.dev/vfs we're grabbing the base url of the host (without the -2)
            var serverHostUrl = serverUrl.replace(/^(https:..[^.]+-\d+)(-\d+)(.*)/, "$1$3");
            if (serverHostUrl) {
                return serverHostUrl;
            }
            return serverUrl;
        }

        function onProtocolChange(callback) {
            // I'm keeping this vague because we don't want users to blame
            // a "cloud9 update" for losing work
            deleteOldVfs();
            return callback(fatalError("Protocol change detected", "reload"));
        }

        function shuffleServers(version, servers) {
            servers = servers.slice();
            var isBetaClient = region === "beta";
            servers = servers.filter(function(s) {
                var isBetaServer = s.region === "beta";
                return isBetaServer === isBetaClient;
            });
            servers = servers.filter(function(s) {
                return s.version == undefined || s.version == version;
            });
            return servers.sort(function(a, b) {
                if (a.region == b.region) {
                    if (a.load < b.load)
                        return -1;
                    else
                        return 1;
                }
                else if (a.region == region)
                    return -1;
                else if (b.region == region)
                    return 1;
                else
                    return 0;
            });
        }

        function rememberVfs(server, vfsid) {
            var vfs = {
                url: server.url,
                region: server.region,
                pid: options.pid,
                vfsid: server.url + "/" + options.pid + "/" + vfsid,
                readonly: options.readonly
            };

            var data = JSON.stringify(vfs);

            var oldData = lastVfs || window.sessionStorage.getItem("vfsid");
            if (oldData && oldData !== data)
                deleteOldVfs();

            lastVfs = data;

            return vfs;
        }

        function recallVfs() {
            var vfs;
            try {
                vfs = JSON.parse(lastVfs || window.sessionStorage.getItem("vfsid"));
                if (!lastVfs) {
                    window.sessionStorage.removeItem("vfsid");
                    lastVfs = JSON.stringify(vfs);
                }
            } catch (e) {}

            if (!vfs)
                return null;

            if (vfs.pid !== options.pid || vfs.readonly != options.readonly) {
                deleteOldVfs();
                return null;
            }

            return vfs;
        }

        function deleteOldVfs() {
            var vfs;
            try {
                vfs = JSON.parse(lastVfs || window.sessionStorage.getItem("vfsid"));
            } catch (e) {}

            window.sessionStorage.removeItem("vfsid");
            lastVfs = null;
            if (!vfs) return;

            auth.request(vfs.vfsid, {
                method: "DELETE",
                headers: {
                    Accept: "application/json"
                }
            }, function(err) {
                if (err) console.error(vfs.vfsid, "deleted", err);
                });
        }

        function fatalError(msg, action) {
            var err = new Error(msg);
            err.fatal = true;
            err.action = action || "reload";
            return err;
        }

        function saveToSessionStorage() {
            try {
                window.sessionStorage.setItem("vfsid", lastVfs);
            } catch(e) {
                // could throw a quota exception
            }
        }

        plugin.on("load", function() {
            window.addEventListener("unload", saveToSessionStorage);
        });

        plugin.on("unload", function() {
            window.removeEventListener("unload", saveToSessionStorage);
        });

        /***** Register and define API *****/

        /**
         **/
        plugin.freezePublicAPI({
            /**
             * Returns the URLs for the home and project REST API and the socket
             */
            get: getVfsEndpoint,

            /**
             * Checks if the client has a network connection
             */
            isOnline: isOnline,

            /**
             *
             */
            clearCache: deleteOldVfs,

            /**
             * Checks if the current VFS server is still alive
             */
            isServerAlive: isServerAlive
        });

        register(null, {
            "vfs.endpoint": plugin
        });
    }
});<commit_msg>Fix protocol change error when beta VFS server is broken<commit_after>define(function(require, exports, module) {
    "use strict";

    main.consumes = ["Plugin", "auth", "http", "api", "error_handler", "metrics"];
    main.provides = ["vfs.endpoint"];

    return main;

    function main(options, imports, register) {
        var Plugin = imports.Plugin;
        var auth = imports.auth;
        var http = imports.http;
        var api = imports.api;
        var errorHandler = imports.error_handler;
        var metrics = imports.metrics;

        var PARALLEL_SEARCHES = 2;

        /***** Initialization *****/

        var plugin = new Plugin("Ajax.org", main.consumes);
        var emit = plugin.getEmitter();

        var urlServers, lastVfs;
        var query = require("url").parse(document.location.href, true).query;
        if (query.vfs) {
            if (!query.vfs.match(/^https:\/\/.*\/vfs$/))
                alert("Bad VFS URL passed, expected: https://host/vfs");
            urlServers = [{
                url: query.vfs,
                region: "url"
            }];
        }
        if (query.vfs || query.region) {
            var vfs = recallVfs();
            if (vfs) {
                if (query.vfs && query.vfs !== vfs.url)
                    deleteOldVfs();
                else if (query.region && query.region !== vfs.region)
                    deleteOldVfs();
            }
        }
        if (query.vfs)
            options.updateServers = false;

        var region = query.region || options.region;

        var servers;
        var pendingServerReqs = [];

        initDefaultServers();

        options.pid = options.pid || 1;

        /***** Methods *****/

        function initDefaultServers(baseURI) {
            if (options.getServers)
                return options.getServers(init);
            init();

            function init() {
                options.getServers = undefined;
                var loc = require("url").parse(baseURI || document.baseURI || window.location.href);
                var defaultServers = [{
                    url: loc.protocol + "//" + loc.hostname + (loc.port ? ":" + loc.port : "") + "/vfs",
                    region: "default"
                }];
                servers = (urlServers || options.servers || defaultServers).map(function(server) {
                    server.url = server.url.replace(/\/*$/, "");
                    return server;
                });
                pendingServerReqs.forEach(function(cb) {
                    cb(null, servers);
                });
            }
        }

        function getServers(callback) {
            if (typeof options.getServers == "function")
                return pendingServerReqs.push(callback);

            if (!options.updateServers)
                return callback(null, servers);

            // first time take the ones from the options
            var _servers = servers;
            if (_servers) {
                servers = null;
                return callback(null, _servers);
            }

            api.vfs.get("servers", function(err, servers) {
                if (err) return callback(err);

                return callback(null, servers.servers);
            });
        }

        function getVfsEndpoint(version, callback) {
            getServers(function(err, _servers) {
                if (err) {
                    if (err.code !== "EDISCONNECT")
                        errorHandler.reportError(new Error("Could not get list of VFS servers"), { cause: err });
                    metrics.increment("vfs.failed.connect_getservers", 1, true);
                    initDefaultServers();
                    _servers = servers;
                }

                getVfsUrl(version, _servers, function(err, vfsid, url, region) {
                    if (err) return callback(err);

                    callback(null, {
                        url: url,
                        region: region,
                        home: vfsid + "/home",
                        project: vfsid + "/workspace",
                        socket: vfsid + "/socket",
                        ping: vfsid,
                        serviceUrl: vfsid,
                    });
                });
            });
        }

        function isOnline(callback) {
            http.request("/_ping", {
                timeout: 3000,
                headers: {
                    Accept: "application/json"
                }
            }, function(err, data, res) {
                callback(err, !err);
            });
        }

        function isServerAlive(url, callback) {
            auth.request(url, {
                headers: {
                    Accept: "application/json"
                }
            }, function(err, data, res) {
                if (err)
                    deleteOldVfs();

                callback(err, !err);
            });
        }

        function getVfsUrl(version, vfsServers, callback) {
            var vfs = recallVfs();

            if (vfs && vfs.vfsid) {
                auth.request(vfs.vfsid, {
                    method: "GET",
                    headers: {
                        Accept: "application/json"
                    }
                }, function(err, res) {
                    if (err) {
                        deleteOldVfs();
                        return getVfsUrl(version, vfsServers, callback);
                    }
                    callback(null, vfs.vfsid, vfs.url, vfs.region);
                });
                return;
            }

            var servers = shuffleServers(version, vfsServers);

            // check for version
            if (vfsServers.length && !servers.length) {
                if (region === "beta")
                    return callback(fatalError("Staging VFS server(s) not working", "reload"));
                return onProtocolChange(callback);
            }

            var latestServer = 0;
            var foundServer = false;

            /* Create a callback that is only ever called once */
            var mainCallback = callback;
            callback = function() {
                if (!foundServer) {
                    foundServer = true;
                    var args = Array.prototype.slice.call(arguments);
                    return mainCallback.apply(this, args);
                }
            };

            // just take the first server that doesn't return an error
            function tryNext(i) {
                if (foundServer) return false;
                if (i >= servers.length) {
                    metrics.increment("vfs.failed.connect_all", 1, true);
                    return callback(new Error("Disconnected: Could not reach your workspace. Please try again later."));
                }

                var server = servers[i];
                auth.request(server.url + "/" + options.pid, {
                    method: "POST",
                    timeout: 120000,
                    body: {
                        version: version
                    },
                    headers: {
                        Accept: "application/json"
                    }
                }, function(err, res) {
                    // the workspace is not configured correctly
                    if (err && res && res.error) {
                        if (err.code == 429) {
                            // rate limited
                            setTimeout(function() {
                                tryNext(i);
                            }, res.error.retryIn || 10000);
                            return;
                        }
                        else if (err.code == 412 && res.error && res.error.subtype == "protocol_mismatch") {
                            return onProtocolChange(callback);
                        }
                        else if (err.code == 412) {
                            callback(fatalError(res.error.message, "dashboard"));
                            return;
                        }
                        else if (err.code === 428 && res.error) {
                            emit("restore", {
                                projectState: res.error.projectState,
                                premium: res.error.premium,
                                progress: res.error.progress || {
                                    progress: 0,
                                    nextProgress: 0,
                                    message: ""
                                }
                            });
                            setTimeout(function() {
                                tryNext(i);
                            }, res.error.retryIn || 10000);
                            return;
                        }
                        else if (err.code == 403) {
                            if (res.error.blocked)
                                callback(fatalError(res.error.message, "dashboard"));

                            // forbidden. User doesn't have access
                            // wait a while before trying again
                            setTimeout(function() {
                                tryNext(i);
                            }, 10000);
                            return;
                        }
                        else if (err.code === 500 && res && res.error && res.error.cause) {
                            return callback(res.error.cause.message);
                        }
                    }

                    if (err) {
                        setTimeout(function() {
                            tryNext(++latestServer);
                        }, 2000);
                        return;
                    }

                    if (!foundServer) {
                        var vfs = rememberVfs(server, res.vfsid);
                        callback(null, vfs.vfsid, server.url, server.region);
                    }
                });
            }

            function startParallelSearches (totalRunners) {
                var attemptedServers = {};
                for (var s = 0; s < servers.length && s < totalRunners; s++)  {
                    latestServer = s;
                    var server = servers[s];
                    var serverHostUrl = getHostFromServerUrl(server.url);
                    if (!attemptedServers[serverHostUrl]) {
                        attemptedServers[serverHostUrl] = true;
                        tryNext(s);
                    }
                }
            }

            startParallelSearches(PARALLEL_SEARCHES);
        }

        function getHostFromServerUrl(serverUrl) {
            // server.url looks like: https://vfs-gce-ae-09-2.c9.io or https://vfs.c9.dev/vfs we're grabbing the base url of the host (without the -2)
            var serverHostUrl = serverUrl.replace(/^(https:..[^.]+-\d+)(-\d+)(.*)/, "$1$3");
            if (serverHostUrl) {
                return serverHostUrl;
            }
            return serverUrl;
        }

        function onProtocolChange(callback) {
            // I'm keeping this vague because we don't want users to blame
            // a "cloud9 update" for losing work
            deleteOldVfs();
            return callback(fatalError("Protocol change detected", "reload"));
        }

        function shuffleServers(version, servers) {
            servers = servers.slice();
            var isBetaClient = region === "beta";
            servers = servers.filter(function(s) {
                var isBetaServer = s.region === "beta";
                return isBetaServer === isBetaClient;
            });
            servers = servers.filter(function(s) {
                return s.version == undefined || s.version == version;
            });
            return servers.sort(function(a, b) {
                if (a.region == b.region) {
                    if (a.load < b.load)
                        return -1;
                    else
                        return 1;
                }
                else if (a.region == region)
                    return -1;
                else if (b.region == region)
                    return 1;
                else
                    return 0;
            });
        }

        function rememberVfs(server, vfsid) {
            var vfs = {
                url: server.url,
                region: server.region,
                pid: options.pid,
                vfsid: server.url + "/" + options.pid + "/" + vfsid,
                readonly: options.readonly
            };

            var data = JSON.stringify(vfs);

            var oldData = lastVfs || window.sessionStorage.getItem("vfsid");
            if (oldData && oldData !== data)
                deleteOldVfs();

            lastVfs = data;

            return vfs;
        }

        function recallVfs() {
            var vfs;
            try {
                vfs = JSON.parse(lastVfs || window.sessionStorage.getItem("vfsid"));
                if (!lastVfs) {
                    window.sessionStorage.removeItem("vfsid");
                    lastVfs = JSON.stringify(vfs);
                }
            } catch (e) {}

            if (!vfs)
                return null;

            if (vfs.pid !== options.pid || vfs.readonly != options.readonly) {
                deleteOldVfs();
                return null;
            }

            return vfs;
        }

        function deleteOldVfs() {
            var vfs;
            try {
                vfs = JSON.parse(lastVfs || window.sessionStorage.getItem("vfsid"));
            } catch (e) {}

            window.sessionStorage.removeItem("vfsid");
            lastVfs = null;
            if (!vfs) return;

            auth.request(vfs.vfsid, {
                method: "DELETE",
                headers: {
                    Accept: "application/json"
                }
            }, function(err) {
                if (err) console.error(vfs.vfsid, "deleted", err);
                });
        }

        function fatalError(msg, action) {
            var err = new Error(msg);
            err.fatal = true;
            err.action = action || "reload";
            return err;
        }

        function saveToSessionStorage() {
            try {
                window.sessionStorage.setItem("vfsid", lastVfs);
            } catch(e) {
                // could throw a quota exception
            }
        }

        plugin.on("load", function() {
            window.addEventListener("unload", saveToSessionStorage);
        });

        plugin.on("unload", function() {
            window.removeEventListener("unload", saveToSessionStorage);
        });

        /***** Register and define API *****/

        /**
         **/
        plugin.freezePublicAPI({
            /**
             * Returns the URLs for the home and project REST API and the socket
             */
            get: getVfsEndpoint,

            /**
             * Checks if the client has a network connection
             */
            isOnline: isOnline,

            /**
             *
             */
            clearCache: deleteOldVfs,

            /**
             * Checks if the current VFS server is still alive
             */
            isServerAlive: isServerAlive
        });

        register(null, {
            "vfs.endpoint": plugin
        });
    }
});<|endoftext|>


<commit_before>var $ = require('jquery');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var UserActions = require('../../../../../../javascripts/cartodb3/data/user-actions');
var StackLayoutModel = require('../../../../../../javascripts/cartodb3/components/stack-layout/stack-layout-model');
var LayerDefinitionModel = require('../../../../../../javascripts/cartodb3/data/layer-definition-model');
var DataLayerView = require('../../../../../../javascripts/cartodb3/editor/layers/layer-views/data-layer-view');
var AnalysisDefinitionNodeModel = require('../../../../../../javascripts/cartodb3/data/analysis-definition-node-model');
var StyleDefinitionModel = require('../../../../../../javascripts/cartodb3/editor/style/style-definition-model');
var QueryRowsCollection = require('../../../../../../javascripts/cartodb3/data/query-rows-collection');
var QuerySchemaModel = require('../../../../../../javascripts/cartodb3/data/query-schema-model');
var QueryGeometryModel = require('../../../../../../javascripts/cartodb3/data/query-geometry-model');
var FactoryModals = require('../../../factories/modals');

describe('editor/layers/data-layer-view', function () {
  beforeEach(function () {
    spyOn(QueryGeometryModel.prototype, 'fetch');
    spyOn(QuerySchemaModel.prototype, 'fetch');
    spyOn(QueryRowsCollection.prototype, 'fetch');

    this.layerDefinitionsCollection = new Backbone.Collection([]);
    this.layerDefinitionsCollection.save = jasmine.createSpy('save');

    this.modals = FactoryModals.createModalService();

    spyOn(this.modals, 'create');

    var a0 = new AnalysisDefinitionNodeModel({
      id: 'a0',
      type: 'buffer',
      params: {}
    }, {
      configModel: {}
    });

    this.model = new LayerDefinitionModel({
      id: 'layer-A',
      name: 'table_name',
      letter: 'a',
      source: 'a0',
      visible: true
    }, {
      configModel: {},
      collection: this.layerDefinitionsCollection
    });
    this.model.styleModel = new StyleDefinitionModel({}, {
      configModel: {}
    });
    spyOn(this.model, 'canBeDeletedByUser');
    spyOn(this.model, 'getNumberOfAnalyses').and.returnValue(3);
    spyOn(this.model, 'getAnalysisDefinitionNodeModel').and.returnValue(a0);

    a0.queryGeometryModel.set('simple_geom', 'polygon');
    this.queryGeometryModel = a0.queryGeometryModel;

    var c2 = new AnalysisDefinitionNodeModel({
      id: 'c2',
      type: 'buffer',
      params: {}
    }, {
      configModel: {}
    });
    spyOn(this.model, 'findAnalysisDefinitionNodeModel').and.returnValue(c2);

    this.stackLayoutModel = new StackLayoutModel(null, {stackLayoutItems: {}});
    spyOn(this.stackLayoutModel, 'nextStep');

    this.newAnalysesViewSpy = jasmine.createSpy('newAnalysesView').and.callFake(function (el) {
      var view = new CoreView({
        el: el
      });
      view.render = function () {
        this.$el.html('ANALYSES');
        return this;
      };
      return view;
    });

    this.userActions = UserActions({
      userModel: {},
      analysisDefinitionsCollection: {},
      analysisDefinitionNodesCollection: {},
      layerDefinitionsCollection: {},
      widgetDefinitionsCollection: {}
    });
    this.promise = $.Deferred();
    spyOn(this.userActions, 'saveLayer').and.returnValue(this.promise);

    var widgetDefinitionsCollection = new Backbone.Collection();
    widgetDefinitionsCollection.widgetsOwnedByLayer = function () { return 0; };

    var visDefinitionModel = new Backbone.Model();

    this.view = new DataLayerView({
      modals: this.modals,
      model: this.model,
      userActions: this.userActions,
      stackLayoutModel: this.stackLayoutModel,
      newAnalysesView: this.newAnalysesViewSpy,
      layerDefinitionsCollection: this.layerDefinitionsCollection,
      configModel: {},
      stateDefinitionModel: {},
      visDefinitionModel: visDefinitionModel,
      widgetDefinitionsCollection: widgetDefinitionsCollection,
      analysisDefinitionNodesCollection: {}
    });

    spyOn(this.view, '_renameLayer').and.callThrough();

    this.view.$el.appendTo(document.body);
    this.view.render();
  });

  afterEach(function () {
    var parent = this.view.el.parentNode;
    parent && parent.removeChild(this.view.el);
    this.view.clean();
  });

  it('should have no leaks', function () {
    expect(this.view).toHaveNoLeaks();
  });

  it('should render title of layer', function () {
    expect(this.view.$el.text()).toContain('table_name');
  });

  it('should add sortable class', function () {
    expect(this.view.$el.hasClass('js-sortable-item')).toBeTruthy();
  });

  it('should be displayed as hidden when layer is hidden', function () {
    expect(this.view.$('.js-thumbnail').hasClass('is-hidden')).toBe(false);
    expect(this.view.$('.js-title').hasClass('is-hidden')).toBe(false);
    expect(this.view.$('.js-analyses').hasClass('is-hidden')).toBe(false);
    expect(this.view.$('.js-analyses-widgets-info').hasClass('is-hidden')).toBe(false);

    this.model.set('visible', false);

    expect(this.view.$('.js-thumbnail').hasClass('is-hidden')).toBe(true);
    expect(this.view.$('.js-title').hasClass('is-hidden')).toBe(true);
    expect(this.view.$('.js-analyses').hasClass('is-hidden')).toBe(true);
    expect(this.view.$('.js-analyses-widgets-info').hasClass('is-hidden')).toBe(true);
  });

  it('should render warning icon when layer doesn\'t have geometry', function () {
    expect(this.view.$('.js-warningIcon').length).toBe(0);
    spyOn(this.view, '_queryGeometryHasGeom').and.returnValue(false);
    this.view.render();
    expect(this.view.$('.js-warningIcon').length).toBe(1);
    expect(this.view.$('.js-warningIcon').data('tipsy')).not.toBeUndefined();
  });

  it('should be with errors when layer has errors', function () {
    expect(this.view.$('.js-error').length).toBe(0);
    this.model.set('error', 'an error');
    expect(this.view.$('.js-error').length).toBe(1);
  });

  it('should not add js-sortable-item class if layer is torque', function () {
    this.model.set('type', 'torque');
    this.view.render();
    expect(this.view.$el.hasClass('js-sortable-item')).toBeFalsy();
    expect(this.view.$el.hasClass('is-animated')).toBeTruthy();
  });

  describe('when layer has an analysis', function () {
    it('should render analysis views', function () {
      expect(this.view.$el.text()).toContain('ANALYSES');

      // Assert args being what's expected too
      expect(this.newAnalysesViewSpy).toHaveBeenCalled();
      expect(this.newAnalysesViewSpy.calls.argsFor(0)[0]).toEqual(this.view.$('.js-analyses'));
      expect(this.newAnalysesViewSpy.calls.argsFor(0)[1]).toEqual(this.model);
    });
  });

  describe('context menu', function () {
    it('should toggle the context-menu box when option is clicked', function () {
      expect(this.view.$el.find('.CDB-Box-modal').length).toBe(0);

      this.view.$('.js-toggle-menu').click();

      expect($('body').find('.CDB-Box-modal').css('display')).toEqual('block');

      this.view.$('.js-toggle-menu').click();

      expect($('body').find('.CDB-Box-modal').length).toBe(0);
    });

    describe('Delete layer…', function () {
      it('should not be posible to delete a layer if canBeDeletedByUser return false', function () {
        this.model.canBeDeletedByUser.and.returnValue(false);

        this.view.$('.js-toggle-menu').click();
        expect($('body').find(".CDB-Box-modal button[title='editor.layers.options.delete']").length).toEqual(0);
      });

      it('should be posible to delete a layer if canBeDeletedByUser', function () {
        this.model.canBeDeletedByUser.and.returnValue(true);

        this.view.$('.js-toggle-menu').click();
        expect($('body').find(".CDB-Box-modal button[title='editor.layers.options.delete']").length).toEqual(1);
      });
    });
  });

  describe('when title is clicked', function () {
    beforeEach(function () {
      jasmine.clock().install();
      this.view.$('.js-title').click();
      jasmine.clock().tick(300);
    });

    it('should edit layer', function () {
      expect(this.stackLayoutModel.nextStep).toHaveBeenCalled();
      expect(this.stackLayoutModel.nextStep.calls.argsFor(0)[0]).toEqual(this.view.model);
      expect(this.stackLayoutModel.nextStep.calls.argsFor(0)[1]).toEqual('layer-content');
    });

    afterEach(function () {
      jasmine.clock().uninstall();
    });
  });

  describe('when title is doubleclicked', function () {
    beforeEach(function () {
      jasmine.clock().install();
      this.view.$('.js-title').click();
      this.view.$('.js-title').click();
      jasmine.clock().tick(300);
    });

    it('should show inline editor', function () {
      expect(this.view.$('.js-input').is(':visible')).toBe(true);
    });

    afterEach(function () {
      jasmine.clock().uninstall();
    });
  });

  describe('when thumbnail is clicked', function () {
    beforeEach(function () {
      this.view.$('.js-thumbnail').click();
    });

    it('should edit layer', function () {
      expect(this.stackLayoutModel.nextStep).toHaveBeenCalled();
      expect(this.stackLayoutModel.nextStep.calls.argsFor(0)[0]).toEqual(this.view.model);
      expect(this.stackLayoutModel.nextStep.calls.argsFor(0)[1]).toEqual('layer-content');
    });
  });

  describe('when the toggle icon is clicked', function () {
    beforeEach(function () {
      this.model.set({autoStyle: 'foo'});
    });

    it('should toggle the layer', function () {
      this.view.$('.js-toggle').click();

      expect(this.model.get('visible')).toEqual(false);
      expect(this.userActions.saveLayer).toHaveBeenCalled();
      expect(this.userActions.saveLayer.calls.argsFor(0)[0]).toEqual(this.view.model);
      expect(this.userActions.saveLayer.calls.argsFor(0)[1]).toEqual({
        shouldPreserveAutoStyle: true
      });
      expect(this.model.get('autoStyle')).toEqual('foo');

      this.userActions.saveLayer.calls.reset();
      this.view.$('.js-toggle').click();
      expect(this.model.get('visible')).toEqual(true);
      expect(this.userActions.saveLayer).toHaveBeenCalled();
      expect(this.userActions.saveLayer.calls.argsFor(0)[0]).toEqual(this.view.model);
      expect(this.userActions.saveLayer.calls.argsFor(0)[1]).toEqual({
        shouldPreserveAutoStyle: true
      });
      expect(this.model.get('autoStyle')).toEqual('foo');
    });
  });

  describe('expanded/collapsed states', function () {
    it('should expand and collapse the layer', function () {
      expect(this.view.$('.js-analyses').hasClass('is-collapsed')).toBe(false);

      // Show the context menu and collapse the layer
      this.view.$('.js-toggle-menu').click();
      $('body').find('.CDB-Box-modal button[title="editor.layers.options.collapse"]').click();

      expect(this.view.$('.js-analyses').hasClass('is-collapsed')).toBe(true);

      // Show the context menu and expand the layer
      this.view.$('.js-toggle-menu').click();
      $('body').find('.CDB-Box-modal button[title="editor.layers.options.expand"]').click();

      // Show the context menu and expand the layer again
      expect(this.view.$('.js-analyses').hasClass('is-collapsed')).toBe(false);
    });
  });

  describe('rename layer', function () {
    it('should rename the layer', function () {
      // Show the context menu and collapse the layer
      this.view.$('.js-toggle-menu').click();
      $('body').find('.CDB-Box-modal button[title="editor.layers.options.rename"]').click();

      expect(this.view.$('.js-input').is(':visible')).toBe(true);
      this.view.$('.js-input').val('Foo');

      var e = $.Event('keyup');
      e.which = 13;
      this.view.$('.js-input').trigger(e);

      expect(this.model.get('table_name_alias')).toBe('Foo');
      expect(this.userActions.saveLayer).toHaveBeenCalledWith(this.model);
    });
  });

  describe('geometry type', function () {
    it('should show polygon svg', function () {
      expect(this.view.$('svg').length).toBe(1);
      expect(this.view.$('svg circle').length).toBe(4);
    });
  });

  describe('._showContextMenu', function () {
    it('should show center map option if geom and clicking on it should call _centerMap', function () {
      spyOn(this.view, '_centerMap');
      this.view._showContextMenu();

      expect(this.view._menuView.$('button[title^="editor.layers.options.center-map"]').length).toBe(1);

      this.view._menuView.$('button[title^="editor.layers.options.center-map"]').click();
      expect(this.view._centerMap).toHaveBeenCalled();
    });

    it('should not show center map option if no geom', function () {
      this.queryGeometryModel.set('simple_geom', '');
      this.view._showContextMenu();

      expect(this.view._menuView.$('button[title^="editor.layers.options.center-map"]').length).toBe(0);

      this.queryGeometryModel.set('simple_geom', 'polygon');
    });
  });

  describe('._centerMap', function () {
    it('should call to zoomToData with query from queryGeometryModel', function () {
      var theView = this.view;
      this.queryGeometryModel.set('simple_geom', '');

      // The only way we have to know if zoomToData has been called is to provoke an
      // error. This way we know that the query has been extracted from our querySchemaModel
      // and that zoomToData has been called.
      expect(function () {
        theView._centerMap();
      }).toThrowError('query is required');

      this.queryGeometryModel.set('simple_geom', 'polygon');
    });
  });
});
<commit_msg>fix data-layer-view specs<commit_after>var $ = require('jquery');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var UserActions = require('../../../../../../javascripts/cartodb3/data/user-actions');
var StackLayoutModel = require('../../../../../../javascripts/cartodb3/components/stack-layout/stack-layout-model');
var LayerDefinitionModel = require('../../../../../../javascripts/cartodb3/data/layer-definition-model');
var DataLayerView = require('../../../../../../javascripts/cartodb3/editor/layers/layer-views/data-layer-view');
var AnalysisDefinitionNodeModel = require('../../../../../../javascripts/cartodb3/data/analysis-definition-node-model');
var StyleDefinitionModel = require('../../../../../../javascripts/cartodb3/editor/style/style-definition-model');
var QueryRowsCollection = require('../../../../../../javascripts/cartodb3/data/query-rows-collection');
var QuerySchemaModel = require('../../../../../../javascripts/cartodb3/data/query-schema-model');
var QueryGeometryModel = require('../../../../../../javascripts/cartodb3/data/query-geometry-model');
var FactoryModals = require('../../../factories/modals');

describe('editor/layers/data-layer-view', function () {
  beforeEach(function () {
    spyOn(QueryGeometryModel.prototype, 'fetch');
    spyOn(QuerySchemaModel.prototype, 'fetch');
    spyOn(QueryRowsCollection.prototype, 'fetch');

    this.layerDefinitionsCollection = new Backbone.Collection([]);
    this.layerDefinitionsCollection.save = jasmine.createSpy('save');

    this.modals = FactoryModals.createModalService();

    spyOn(this.modals, 'create');

    var a0 = new AnalysisDefinitionNodeModel({
      id: 'a0',
      type: 'buffer',
      params: {}
    }, {
      configModel: {}
    });

    this.model = new LayerDefinitionModel({
      id: 'layer-A',
      name: 'table_name',
      letter: 'a',
      source: 'a0',
      visible: true
    }, {
      configModel: {},
      collection: this.layerDefinitionsCollection
    });
    this.model.styleModel = new StyleDefinitionModel({}, {
      configModel: {}
    });
    spyOn(this.model, 'canBeDeletedByUser');
    spyOn(this.model, 'getNumberOfAnalyses').and.returnValue(3);
    spyOn(this.model, 'getAnalysisDefinitionNodeModel').and.returnValue(a0);

    a0.queryGeometryModel.set('simple_geom', 'polygon');
    this.queryGeometryModel = a0.queryGeometryModel;

    var c2 = new AnalysisDefinitionNodeModel({
      id: 'c2',
      type: 'buffer',
      params: {}
    }, {
      configModel: {}
    });
    spyOn(this.model, 'findAnalysisDefinitionNodeModel').and.returnValue(c2);

    this.stackLayoutModel = new StackLayoutModel(null, {stackLayoutItems: {}});
    spyOn(this.stackLayoutModel, 'nextStep');

    this.newAnalysesViewSpy = jasmine.createSpy('newAnalysesView').and.callFake(function (el) {
      var view = new CoreView({
        el: el
      });
      view.render = function () {
        this.$el.html('ANALYSES');
        return this;
      };
      return view;
    });

    this.userActions = UserActions({
      userModel: {},
      analysisDefinitionsCollection: {},
      analysisDefinitionNodesCollection: {},
      layerDefinitionsCollection: {},
      widgetDefinitionsCollection: {}
    });
    this.promise = $.Deferred();
    spyOn(this.userActions, 'saveLayer').and.returnValue(this.promise);

    var widgetDefinitionsCollection = new Backbone.Collection();
    widgetDefinitionsCollection.widgetsOwnedByLayer = function () { return 0; };

    var visDefinitionModel = new Backbone.Model();

    this.view = new DataLayerView({
      modals: this.modals,
      model: this.model,
      userActions: this.userActions,
      stackLayoutModel: this.stackLayoutModel,
      newAnalysesView: this.newAnalysesViewSpy,
      layerDefinitionsCollection: this.layerDefinitionsCollection,
      configModel: {},
      stateDefinitionModel: {},
      visDefinitionModel: visDefinitionModel,
      widgetDefinitionsCollection: widgetDefinitionsCollection,
      analysisDefinitionNodesCollection: {}
    });

    spyOn(this.view, '_renameLayer').and.callThrough();

    this.view.$el.appendTo(document.body);
    this.view.render();
  });

  afterEach(function () {
    var parent = this.view.el.parentNode;
    parent && parent.removeChild(this.view.el);
    this.view.clean();
  });

  it('should have no leaks', function () {
    expect(this.view).toHaveNoLeaks();
  });

  it('should render title of layer', function () {
    expect(this.view.$el.text()).toContain('table_name');
  });

  it('should add sortable class', function () {
    expect(this.view.$el.hasClass('js-sortable-item')).toBeTruthy();
  });

  it('should be displayed as hidden when layer is hidden', function () {
    expect(this.view.$('.js-thumbnail').hasClass('is-hidden')).toBe(false);
    expect(this.view.$('.js-title').hasClass('is-hidden')).toBe(false);
    expect(this.view.$('.js-analyses').hasClass('is-hidden')).toBe(false);
    expect(this.view.$('.js-analyses-widgets-info').hasClass('is-hidden')).toBe(false);

    this.model.set('visible', false);

    expect(this.view.$('.js-thumbnail').hasClass('is-hidden')).toBe(true);
    expect(this.view.$('.js-title').hasClass('is-hidden')).toBe(true);
    expect(this.view.$('.js-analyses').hasClass('is-hidden')).toBe(true);
    expect(this.view.$('.js-analyses-widgets-info').hasClass('is-hidden')).toBe(true);
  });

  it('should render geocode button when layer doesn\'t have geometry', function () {
    expect(this.view.$('.js-geocode').length).toBe(0);
    spyOn(this.view, '_queryGeometryHasGeom').and.returnValue(false);
    this.view.render();
    expect(this.view.$('.js-geocode').length).toBe(1);
    expect(this.view.$('.js-geocode').data('tipsy')).not.toBeUndefined();
  });

  it('should be with errors when layer has errors', function () {
    expect(this.view.$('.js-error').length).toBe(0);
    this.model.set('error', 'an error');
    expect(this.view.$('.js-error').length).toBe(1);
  });

  it('should not add js-sortable-item class if layer is torque', function () {
    this.model.set('type', 'torque');
    this.view.render();
    expect(this.view.$el.hasClass('js-sortable-item')).toBeFalsy();
    expect(this.view.$el.hasClass('is-animated')).toBeTruthy();
  });

  describe('when layer has an analysis', function () {
    it('should render analysis views', function () {
      expect(this.view.$el.text()).toContain('ANALYSES');

      // Assert args being what's expected too
      expect(this.newAnalysesViewSpy).toHaveBeenCalled();
      expect(this.newAnalysesViewSpy.calls.argsFor(0)[0]).toEqual(this.view.$('.js-analyses'));
      expect(this.newAnalysesViewSpy.calls.argsFor(0)[1]).toEqual(this.model);
    });
  });

  describe('context menu', function () {
    it('should toggle the context-menu box when option is clicked', function () {
      expect(this.view.$el.find('.CDB-Box-modal').length).toBe(0);

      this.view.$('.js-toggle-menu').click();

      expect($('body').find('.CDB-Box-modal').css('display')).toEqual('block');

      this.view.$('.js-toggle-menu').click();

      expect($('body').find('.CDB-Box-modal').length).toBe(0);
    });

    describe('Delete layer…', function () {
      it('should not be posible to delete a layer if canBeDeletedByUser return false', function () {
        this.model.canBeDeletedByUser.and.returnValue(false);

        this.view.$('.js-toggle-menu').click();
        expect($('body').find(".CDB-Box-modal button[title='editor.layers.options.delete']").length).toEqual(0);
      });

      it('should be posible to delete a layer if canBeDeletedByUser', function () {
        this.model.canBeDeletedByUser.and.returnValue(true);

        this.view.$('.js-toggle-menu').click();
        expect($('body').find(".CDB-Box-modal button[title='editor.layers.options.delete']").length).toEqual(1);
      });
    });
  });

  describe('when title is clicked', function () {
    beforeEach(function () {
      jasmine.clock().install();
      this.view.$('.js-title').click();
      jasmine.clock().tick(300);
    });

    it('should edit layer', function () {
      expect(this.stackLayoutModel.nextStep).toHaveBeenCalled();
      expect(this.stackLayoutModel.nextStep.calls.argsFor(0)[0]).toEqual(this.view.model);
      expect(this.stackLayoutModel.nextStep.calls.argsFor(0)[1]).toEqual('layer-content');
    });

    afterEach(function () {
      jasmine.clock().uninstall();
    });
  });

  describe('when title is doubleclicked', function () {
    beforeEach(function () {
      jasmine.clock().install();
      this.view.$('.js-title').click();
      this.view.$('.js-title').click();
      jasmine.clock().tick(300);
    });

    it('should show inline editor', function () {
      expect(this.view.$('.js-input').is(':visible')).toBe(true);
    });

    afterEach(function () {
      jasmine.clock().uninstall();
    });
  });

  describe('when thumbnail is clicked', function () {
    beforeEach(function () {
      this.view.$('.js-thumbnail').click();
    });

    it('should edit layer', function () {
      expect(this.stackLayoutModel.nextStep).toHaveBeenCalled();
      expect(this.stackLayoutModel.nextStep.calls.argsFor(0)[0]).toEqual(this.view.model);
      expect(this.stackLayoutModel.nextStep.calls.argsFor(0)[1]).toEqual('layer-content');
    });
  });

  describe('when the toggle icon is clicked', function () {
    beforeEach(function () {
      this.model.set({autoStyle: 'foo'});
    });

    it('should toggle the layer', function () {
      this.view.$('.js-toggle').click();

      expect(this.model.get('visible')).toEqual(false);
      expect(this.userActions.saveLayer).toHaveBeenCalled();
      expect(this.userActions.saveLayer.calls.argsFor(0)[0]).toEqual(this.view.model);
      expect(this.userActions.saveLayer.calls.argsFor(0)[1]).toEqual({
        shouldPreserveAutoStyle: true
      });
      expect(this.model.get('autoStyle')).toEqual('foo');

      this.userActions.saveLayer.calls.reset();
      this.view.$('.js-toggle').click();
      expect(this.model.get('visible')).toEqual(true);
      expect(this.userActions.saveLayer).toHaveBeenCalled();
      expect(this.userActions.saveLayer.calls.argsFor(0)[0]).toEqual(this.view.model);
      expect(this.userActions.saveLayer.calls.argsFor(0)[1]).toEqual({
        shouldPreserveAutoStyle: true
      });
      expect(this.model.get('autoStyle')).toEqual('foo');
    });
  });

  describe('expanded/collapsed states', function () {
    it('should expand and collapse the layer', function () {
      expect(this.view.$('.js-analyses').hasClass('is-collapsed')).toBe(false);

      // Show the context menu and collapse the layer
      this.view.$('.js-toggle-menu').click();
      $('body').find('.CDB-Box-modal button[title="editor.layers.options.collapse"]').click();

      expect(this.view.$('.js-analyses').hasClass('is-collapsed')).toBe(true);

      // Show the context menu and expand the layer
      this.view.$('.js-toggle-menu').click();
      $('body').find('.CDB-Box-modal button[title="editor.layers.options.expand"]').click();

      // Show the context menu and expand the layer again
      expect(this.view.$('.js-analyses').hasClass('is-collapsed')).toBe(false);
    });
  });

  describe('rename layer', function () {
    it('should rename the layer', function () {
      // Show the context menu and collapse the layer
      this.view.$('.js-toggle-menu').click();
      $('body').find('.CDB-Box-modal button[title="editor.layers.options.rename"]').click();

      expect(this.view.$('.js-input').is(':visible')).toBe(true);
      this.view.$('.js-input').val('Foo');

      var e = $.Event('keyup');
      e.which = 13;
      this.view.$('.js-input').trigger(e);

      expect(this.model.get('table_name_alias')).toBe('Foo');
      expect(this.userActions.saveLayer).toHaveBeenCalledWith(this.model);
    });
  });

  describe('geometry type', function () {
    it('should show polygon svg', function () {
      expect(this.view.$('svg').length).toBe(1);
      expect(this.view.$('svg circle').length).toBe(4);
    });
  });

  describe('._showContextMenu', function () {
    it('should show center map option if geom and clicking on it should call _centerMap', function () {
      spyOn(this.view, '_centerMap');
      this.view._showContextMenu();

      expect(this.view._menuView.$('button[title^="editor.layers.options.center-map"]').length).toBe(1);

      this.view._menuView.$('button[title^="editor.layers.options.center-map"]').click();
      expect(this.view._centerMap).toHaveBeenCalled();
    });

    it('should not show center map option if no geom', function () {
      this.queryGeometryModel.set('simple_geom', '');
      this.view._showContextMenu();

      expect(this.view._menuView.$('button[title^="editor.layers.options.center-map"]').length).toBe(0);

      this.queryGeometryModel.set('simple_geom', 'polygon');
    });
  });

  describe('._centerMap', function () {
    it('should call to zoomToData with query from queryGeometryModel', function () {
      var theView = this.view;
      this.queryGeometryModel.set('simple_geom', '');

      // The only way we have to know if zoomToData has been called is to provoke an
      // error. This way we know that the query has been extracted from our querySchemaModel
      // and that zoomToData has been called.
      expect(function () {
        theView._centerMap();
      }).toThrowError('query is required');

      this.queryGeometryModel.set('simple_geom', 'polygon');
    });
  });
});
<|endoftext|>


<commit_before>module.exports = {
    reporter: function (errors) {
        var err;
        for (var i = 0; i < errors.length; i++) {
            err = errors[i].error;
            console.log(errors[i].file + ":" + err.line + ":" + err.character + ": " + err.reason);
        }
    }
};
<commit_msg>tools: Drop unused reporter.js<commit_after><|endoftext|>


<commit_before>/**
 * Created by spadolski on 7/26/16.
 */
/*
 * jQuery Shorten plugin 1.0.0
 *
 * Copyright (c) 2013 Viral Patel
 * http://viralpatel.net
 *
 * Dual licensed under the MIT license:
 *   http://www.opensource.org/licenses/mit-license.php
 */

(function($) {
    $.fn.shorten = function(settings) {
        "use strict";

        var config = {
            showChars: 1000,
            minHideChars: 10,
            ellipsesText: "...",
            moreText: "more",
            lessText: "less",
            onLess: function() {},
            onMore: function() {},
            errMsg: null,
            force: false
        };

        if (settings) {
            $.extend(config, settings);
        }

        if ($(this).data('jquery.shorten') && !config.force) {
            return false;
        }
        $(this).data('jquery.shorten', true);

        $(document).off("click", '.morelink');

        $(document).on({
            click: function() {

                var $this = $(this);
                if ($this.hasClass('less')) {
                    $this.removeClass('less');
                    $this.html(config.moreText);
                    $this.parent().prev().animate({'height':'0'+'%'}, function () { $this.parent().prev().prev().show(); }).hide('fast', function() {
                        config.onLess();
                      });

                } else {
                    $this.addClass('less');
                    $this.html(config.lessText);
                    $this.parent().prev().animate({'height':'100'+'%'}, function () { $this.parent().prev().prev().hide(); }).show('fast', function() {
                        config.onMore();
                      });
                }
                return false;
            }
        }, '.morelink');

        return this.each(function() {
            var $this = $(this);

            var content = $this.html();
            var contentlen = $this.text().length;
            if (contentlen > config.showChars + config.minHideChars) {
                var c = content.substr(0, config.showChars);
                if (c.indexOf('<') >= 0) // If there's HTML don't want to cut it
                {
                    var inTag = false; // I'm in a tag?
                    var bag = ''; // Put the characters to be shown here
                    var countChars = 0; // Current bag size
                    var openTags = []; // Stack for opened tags, so I can close them later
                    var tagName = null;

                    for (var i = 0, r = 0; r <= config.showChars; i++) {
                        if (content[i] == '<' && !inTag) {
                            inTag = true;

                            // This could be "tag" or "/tag"
                            tagName = content.substring(i + 1, content.indexOf('>', i));

                            // If its a closing tag
                            if (tagName[0] == '/') {

                                if (tagName != '/' + openTags[0]) {
                                    config.errMsg = 'ERROR en HTML: the top of the stack should be the tag that closes';
                                } else {
                                    openTags.shift(); // Pops the last tag from the open tag stack (the tag is closed in the retult HTML!)
                                }

                            } else {
                                // There are some nasty tags that don't have a close tag like

                                if (tagName.toLowerCase() != 'br') {
                                    openTags.unshift(tagName); // Add to start the name of the tag that opens
                                }
                            }
                        }
                        if (inTag && content[i] == '>') {
                            inTag = false;
                        }

                        if (inTag) { bag += content.charAt(i); } // Add tag name chars to the result
                        else {
                            r++;
                            if (countChars <= config.showChars) {
                                bag += content.charAt(i); // Fix to ie 7 not allowing you to reference string characters using the []
                                countChars++;
                            } else // Now I have the characters needed
                            {
                                if (openTags.length > 0) // I have unclosed tags
                                {
                                    //console.log('They were open tags');
                                    //console.log(openTags);
                                    for (j = 0; j < openTags.length; j++) {
                                        //console.log('Cierro tag ' + openTags[j]);
                                        bag += '</' + openTags[j] + '>'; // Close all tags that were opened

                                        // You could shift the tag from the stack to check if you end with an empty stack, that means you have closed all open tags
                                    }
                                    break;
                                }
                            }
                        }
                    }
                    c = $('
').html(bag + '' + config.ellipsesText + '').html();
                }else{
                    c+=config.ellipsesText;
                }

                var html = '
' + c +
                    '

' + content +
                    '
' + config.moreText + '';

                $this.html(html);
                $this.find(".allcontent").hide(); // Hide all text
                $('.shortcontent p:last', $this).css('margin-bottom', 0); //Remove bottom margin on last paragraph as it's likely shortened
            }
        });

    };

})(jQuery);<commit_msg>Increase jquery.shorten limit from 1k to 2k symbols<commit_after>/**
 * Created by spadolski on 7/26/16.
 */
/*
 * jQuery Shorten plugin 1.0.0
 *
 * Copyright (c) 2013 Viral Patel
 * http://viralpatel.net
 *
 * Dual licensed under the MIT license:
 *   http://www.opensource.org/licenses/mit-license.php
 */

(function($) {
    $.fn.shorten = function(settings) {
        "use strict";

        var config = {
            showChars: 2000,
            minHideChars: 10,
            ellipsesText: "...",
            moreText: "more",
            lessText: "less",
            onLess: function() {},
            onMore: function() {},
            errMsg: null,
            force: false
        };

        if (settings) {
            $.extend(config, settings);
        }

        if ($(this).data('jquery.shorten') && !config.force) {
            return false;
        }
        $(this).data('jquery.shorten', true);

        $(document).off("click", '.morelink');

        $(document).on({
            click: function() {

                var $this = $(this);
                if ($this.hasClass('less')) {
                    $this.removeClass('less');
                    $this.html(config.moreText);
                    $this.parent().prev().animate({'height':'0'+'%'}, function () { $this.parent().prev().prev().show(); }).hide('fast', function() {
                        config.onLess();
                      });

                } else {
                    $this.addClass('less');
                    $this.html(config.lessText);
                    $this.parent().prev().animate({'height':'100'+'%'}, function () { $this.parent().prev().prev().hide(); }).show('fast', function() {
                        config.onMore();
                      });
                }
                return false;
            }
        }, '.morelink');

        return this.each(function() {
            var $this = $(this);

            var content = $this.html();
            var contentlen = $this.text().length;
            if (contentlen > config.showChars + config.minHideChars) {
                var c = content.substr(0, config.showChars);
                if (c.indexOf('<') >= 0) // If there's HTML don't want to cut it
                {
                    var inTag = false; // I'm in a tag?
                    var bag = ''; // Put the characters to be shown here
                    var countChars = 0; // Current bag size
                    var openTags = []; // Stack for opened tags, so I can close them later
                    var tagName = null;

                    for (var i = 0, r = 0; r <= config.showChars; i++) {
                        if (content[i] == '<' && !inTag) {
                            inTag = true;

                            // This could be "tag" or "/tag"
                            tagName = content.substring(i + 1, content.indexOf('>', i));

                            // If its a closing tag
                            if (tagName[0] == '/') {

                                if (tagName != '/' + openTags[0]) {
                                    config.errMsg = 'ERROR en HTML: the top of the stack should be the tag that closes';
                                } else {
                                    openTags.shift(); // Pops the last tag from the open tag stack (the tag is closed in the retult HTML!)
                                }

                            } else {
                                // There are some nasty tags that don't have a close tag like

                                if (tagName.toLowerCase() != 'br') {
                                    openTags.unshift(tagName); // Add to start the name of the tag that opens
                                }
                            }
                        }
                        if (inTag && content[i] == '>') {
                            inTag = false;
                        }

                        if (inTag) { bag += content.charAt(i); } // Add tag name chars to the result
                        else {
                            r++;
                            if (countChars <= config.showChars) {
                                bag += content.charAt(i); // Fix to ie 7 not allowing you to reference string characters using the []
                                countChars++;
                            } else // Now I have the characters needed
                            {
                                if (openTags.length > 0) // I have unclosed tags
                                {
                                    //console.log('They were open tags');
                                    //console.log(openTags);
                                    for (j = 0; j < openTags.length; j++) {
                                        //console.log('Cierro tag ' + openTags[j]);
                                        bag += '</' + openTags[j] + '>'; // Close all tags that were opened

                                        // You could shift the tag from the stack to check if you end with an empty stack, that means you have closed all open tags
                                    }
                                    break;
                                }
                            }
                        }
                    }
                    c = $('
').html(bag + '' + config.ellipsesText + '').html();
                }else{
                    c+=config.ellipsesText;
                }

                var html = '
' + c +
                    '

' + content +
                    '
' + config.moreText + '';

                $this.html(html);
                $this.find(".allcontent").hide(); // Hide all text
                $('.shortcontent p:last', $this).css('margin-bottom', 0); //Remove bottom margin on last paragraph as it's likely shortened
            }
        });

    };

})(jQuery);
<|endoftext|>


<commit_before>$(document).ready(function() {

  configureDataTables();
  configureCodeBox();
  configureGolf();

  $("form#run-code button#approve-button").live("click", function(e) {
    e.preventDefault();
    if(confirm("Are you sure you want to mark this problem as approved?"))
      $(this).parents("form").attr("action", "/problem/approve").submit();
  });

  $("form#run-code button#reject-button").live("click", function(e) {
    e.preventDefault();
    if(confirm("Are you sure you want to reject this problem? It will be permanently deleted.")) {
      $(this).parents("form").attr("action", "/problem/reject").submit();
    }
  });

  $("form#run-code button#edit-button").live("click", function(e) {
    e.preventDefault();
    $(this).parents("form").attr("action", "/problem/edit").submit();
  });

  $("button.user-follow-button").live("click", function(e) {
      e.preventDefault();
      var $form = $(this).parents("form");
      var $button = $(this);
      $.ajax({type: "POST",
              url: "/rest" + $form.attr("action"),
              dataType: "json",
              success: function(data) {
                if (data) {
                  $button.text(data["next-label"]);
                  $form.attr("action", data["next-action"]);
                }
              },
             });
      return false;
  });

  $("#all-users-link").html("[show all]");

  $("#user-table,#server-user-table").addClass("js-enabled");

  $("#user-table,#server-user-table input.following").live("click", function(e) {
    e.preventDefault();
    var $checkbox = $(this)
    var $form = $checkbox.parents("form")
    $.ajax({type: "POST",
            url: "/rest" + $form.attr("action"),
            dataType: "json",
            success: function(data) {
              if (data) {
                $checkbox.attr("checked", data["following"]);
                $form.attr("action", data["next-action"]);
              }
             },
           });
    return false;
  });

});

var difficulty = {
    "Elementary": 0,
    "Easy": 1,
    "Medium": 2,
    "Hard": 3,
    "": 4
};

// dataTable will call this function in preparation for sorting a column.
// We're responsible for giving it the "real" data to sort on, for all the
// rows at once
jQuery.fn.dataTableExt.afnSortData['difficulty'] = function(oSettings, iColumn)
{
    var aData = [];
    // fnGetTrNodes returns a context we can use in jquery to iterate over
    // only the  elements for this column. General approach taken from
    // http://datatables.net/plug-ins/sorting#functions_data_source
    $('td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings)).each(function () {
	aData.push(difficulty[$(this).text()]);
    });
    return aData;
}

// See comments for above function to make sense of this mess
jQuery.fn.dataTableExt.afnSortData['user-name'] = function(oSettings, iColumn)
{
    var aData = [];
    $('td:eq('+iColumn+') a.user-profile-link', oSettings.oApi._fnGetTrNodes(oSettings)).each(function () {
	aData.push($(this).text());
    });
    return aData;
}

// See comments for above function to make sense of this mess
jQuery.fn.dataTableExt.afnSortData['following'] = function(oSettings, iColumn)
{
    var aData = [];
    $('td:eq('+iColumn+') span.following', oSettings.oApi._fnGetTrNodes(oSettings)).each(function () {
        var followingText = $(this).text();
        if (!followingText || followingText == "") { followingText = "no" }
	aData.push(followingText);
    });
    return aData;
}

function configureDataTables(){

    $('#problem-table').dataTable( {
        "iDisplayLength": 100,
        "aaSorting": [[5, "desc"], [1, "asc"], [4, "desc"]],
        "aoColumns": [
            {"sType": "string"},
            {"sSortDataType": "difficulty", "sType": "numeric"},
            {"sType": "string"},
            {"sType": "string"},
            {"sType": "numeric"},
            {"sType": "string"}
        ]
    } );

    $('#unapproved-problems').dataTable( {
        "iDisplayLength": 25,
        "aaSorting": [[3, "desc"]],
        "aoColumns": [
            {"sType": "string"},
            {"sType": "string"},
            {"sType": "string"},
            {"sType": "string"}
        ]
    } );

    $('#user-table').dataTable( {
        "iDisplayLength":100,
        "aaSorting": [[0, "asc"]],
        "aoColumns": [
            {"sType": "numeric"},
            {"sSortDataType": "user-name"},
            {"sType": "numeric"},
            {"sSortDataType": "following"}
        ]
    } );

    $('#server-user-table').dataTable( {
        "aoColumns": [
            null,
            null,
            null,
            {"bSortable": false}
        ],
        "iDisplayLength":10,
        "bProcessing": true,
        "bServerSide": true,
        "sAjaxSource": "/datatable/users"
    } );
}

function setIconColor(element, color, timeOut) {
  timeOut = (typeof timeOut == "undefined") ? 0 : timeOut
  setTimeout (function() {
      element.src = element.src.replace(new RegExp("(.*/images/).*(light.png)"), "$1" + color + "$2");
  }, timeOut);
}

function changeToCodeView() {
  $('#code-div').show('fast');
  $('#golfgraph').hide('fast');
  $('#graph-link').html("View Chart");
}

function configureCodeBox(){
    //For no javascript version we have the code-box text area
    //If we have javascript on then we remove it and replace it with
    //the proper div
    var oldBox = $('#code-box');
    var disableJavaScriptCodeBox = $('#disable-javascript-codebox');
    if (disableJavaScriptCodeBox.length){
      return;
    }
    var hiddenCodeInput = "<input type=\"hidden\" value=\"blank\" name=\"code\" id=\"code\">";
    oldBox.replaceWith("

" + oldBox.val() + "

"+hiddenCodeInput);

    if ($("#run-button").length || $("#submission-button").length){
       var editor = ace.edit("editor");
       editor.setTheme("ace/theme/textmate");

       var ClojureMode = require("ace/mode/clojure").Mode;
       var session = editor.getSession();

       var clickHandler = function() {
         var text = session.getValue(),
           id = $('#id').attr("value"),
           images = $(".testcases").find("img"),
           cont = true,
           high = false,
           animationTime = 800,
           waitTimePerItem = 500,
           waitTime = waitTimePerItem,

           beforeSendCallback = function(data) {
             var anim = function() {
               if(cont) {
                 images.animate({
                   opacity: high ? 1.0 : 0.1,
                 }, animationTime);
                 high = !high;
                 setTimeout(anim,animationTime);
               }
             };

             $("#message-text").text("Executing unit tests...");
             $("#error-message-text").text("");
             images.each( function(index, element) {
               setIconColor(element, "blue");
             });
             setTimeout(changeToCodeView,0);
             setTimeout(anim,0);
           },
           successCallback = function(data) {
             var failingTest = data.failingTest,
                 getColorFor = function(index) {
                     return index === failingTest ? "red" : "green";
                 },
                 testWasExecuted = function(index) {
                     return index <= failingTest;
                 },
                 setColor = function(index,element) {
                     var color = getColorFor(index);
                     waitTime = waitTimePerItem * (index+1);
                     setIconColor(element, color, waitTime);
                 },
                 setMessages = function() {
                     $("#message-text").html(data.message);
                     $("#error-message-text").html(data.error);
                     $("#golfgraph").html(data.golfChart);
                     $("#golfscore").html(data.golfScore);
                     configureGolf();
                 },
                 stopAnimation = function() {
                     cont = false;
                     images.stop(true);
                     images.css({ opacity: 1.0, });
                 };

             setTimeout(stopAnimation, waitTime);
             images.filter( testWasExecuted ).
                 each(setColor);
             setTimeout (setMessages, waitTime);
           };

         $.ajax({type: "POST",
           url: "/rest/problem/"+id,
           dataType: "json",
           data: { id: id, code: text, },
           timout: 20000, // default clojail timeout is 10000
           beforeSend: beforeSendCallback,
           success: successCallback,
           error: function(data, str, error) {
             $("#message-text").text("An Error occured: "+error);
           },
         });
         return false;
       };

       $("#run-button").click(clickHandler);

       session.setMode(new ClojureMode());
       session.setUseSoftTabs(true);
       session.setTabSize(2);

       document.getElementById('editor').style.fontSize='13px';
       $("#run-button").click(function(){
         var text = editor.getSession().getValue();
         $('#code').val(text);
       });

       $("#submission-button").click(function(){
         var text = editor.getSession().getValue();
         $('#code').val(text);
       });
    }
}

function configureGolf(){
  $('#graph-link').show();
  $('#golfgraph').hide();
  $('#graph-link').click(function() {
    $('#code-div').toggle('fast', function() {
      // Animation complete.
    });
    $('#golfgraph').toggle('fast', function() {
      // Animation complete.
    });
    var text = $('#graph-link').html();
    if (text && text == 'View Chart'){
       $('#graph-link').html("View Code");
    } else {
       $('#graph-link').html("View Chart");
    }
  });

}
<commit_msg>Change the default page size to 100 on all-users<commit_after>$(document).ready(function() {

  configureDataTables();
  configureCodeBox();
  configureGolf();

  $("form#run-code button#approve-button").live("click", function(e) {
    e.preventDefault();
    if(confirm("Are you sure you want to mark this problem as approved?"))
      $(this).parents("form").attr("action", "/problem/approve").submit();
  });

  $("form#run-code button#reject-button").live("click", function(e) {
    e.preventDefault();
    if(confirm("Are you sure you want to reject this problem? It will be permanently deleted.")) {
      $(this).parents("form").attr("action", "/problem/reject").submit();
    }
  });

  $("form#run-code button#edit-button").live("click", function(e) {
    e.preventDefault();
    $(this).parents("form").attr("action", "/problem/edit").submit();
  });

  $("button.user-follow-button").live("click", function(e) {
      e.preventDefault();
      var $form = $(this).parents("form");
      var $button = $(this);
      $.ajax({type: "POST",
              url: "/rest" + $form.attr("action"),
              dataType: "json",
              success: function(data) {
                if (data) {
                  $button.text(data["next-label"]);
                  $form.attr("action", data["next-action"]);
                }
              },
             });
      return false;
  });

  $("#all-users-link").html("[show all]");

  $("#user-table,#server-user-table").addClass("js-enabled");

  $("#user-table,#server-user-table input.following").live("click", function(e) {
    e.preventDefault();
    var $checkbox = $(this)
    var $form = $checkbox.parents("form")
    $.ajax({type: "POST",
            url: "/rest" + $form.attr("action"),
            dataType: "json",
            success: function(data) {
              if (data) {
                $checkbox.attr("checked", data["following"]);
                $form.attr("action", data["next-action"]);
              }
             },
           });
    return false;
  });

});

var difficulty = {
    "Elementary": 0,
    "Easy": 1,
    "Medium": 2,
    "Hard": 3,
    "": 4
};

// dataTable will call this function in preparation for sorting a column.
// We're responsible for giving it the "real" data to sort on, for all the
// rows at once
jQuery.fn.dataTableExt.afnSortData['difficulty'] = function(oSettings, iColumn)
{
    var aData = [];
    // fnGetTrNodes returns a context we can use in jquery to iterate over
    // only the  elements for this column. General approach taken from
    // http://datatables.net/plug-ins/sorting#functions_data_source
    $('td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings)).each(function () {
	aData.push(difficulty[$(this).text()]);
    });
    return aData;
}

// See comments for above function to make sense of this mess
jQuery.fn.dataTableExt.afnSortData['user-name'] = function(oSettings, iColumn)
{
    var aData = [];
    $('td:eq('+iColumn+') a.user-profile-link', oSettings.oApi._fnGetTrNodes(oSettings)).each(function () {
	aData.push($(this).text());
    });
    return aData;
}

// See comments for above function to make sense of this mess
jQuery.fn.dataTableExt.afnSortData['following'] = function(oSettings, iColumn)
{
    var aData = [];
    $('td:eq('+iColumn+') span.following', oSettings.oApi._fnGetTrNodes(oSettings)).each(function () {
        var followingText = $(this).text();
        if (!followingText || followingText == "") { followingText = "no" }
	aData.push(followingText);
    });
    return aData;
}

function configureDataTables(){

    $('#problem-table').dataTable( {
        "iDisplayLength": 100,
        "aaSorting": [[5, "desc"], [1, "asc"], [4, "desc"]],
        "aoColumns": [
            {"sType": "string"},
            {"sSortDataType": "difficulty", "sType": "numeric"},
            {"sType": "string"},
            {"sType": "string"},
            {"sType": "numeric"},
            {"sType": "string"}
        ]
    } );

    $('#unapproved-problems').dataTable( {
        "iDisplayLength": 25,
        "aaSorting": [[3, "desc"]],
        "aoColumns": [
            {"sType": "string"},
            {"sType": "string"},
            {"sType": "string"},
            {"sType": "string"}
        ]
    } );

    $('#user-table').dataTable( {
        "iDisplayLength":100,
        "aaSorting": [[0, "asc"]],
        "aoColumns": [
            {"sType": "numeric"},
            {"sSortDataType": "user-name"},
            {"sType": "numeric"},
            {"sSortDataType": "following"}
        ]
    } );

    $('#server-user-table').dataTable( {
        "aoColumns": [
            null,
            null,
            null,
            {"bSortable": false}
        ],
        "iDisplayLength":100,
        "bProcessing": true,
        "bServerSide": true,
        "sAjaxSource": "/datatable/users"
    } );
}

function setIconColor(element, color, timeOut) {
  timeOut = (typeof timeOut == "undefined") ? 0 : timeOut
  setTimeout (function() {
      element.src = element.src.replace(new RegExp("(.*/images/).*(light.png)"), "$1" + color + "$2");
  }, timeOut);
}

function changeToCodeView() {
  $('#code-div').show('fast');
  $('#golfgraph').hide('fast');
  $('#graph-link').html("View Chart");
}

function configureCodeBox(){
    //For no javascript version we have the code-box text area
    //If we have javascript on then we remove it and replace it with
    //the proper div
    var oldBox = $('#code-box');
    var disableJavaScriptCodeBox = $('#disable-javascript-codebox');
    if (disableJavaScriptCodeBox.length){
      return;
    }
    var hiddenCodeInput = "<input type=\"hidden\" value=\"blank\" name=\"code\" id=\"code\">";
    oldBox.replaceWith("

" + oldBox.val() + "

"+hiddenCodeInput);

    if ($("#run-button").length || $("#submission-button").length){
       var editor = ace.edit("editor");
       editor.setTheme("ace/theme/textmate");

       var ClojureMode = require("ace/mode/clojure").Mode;
       var session = editor.getSession();

       var clickHandler = function() {
         var text = session.getValue(),
           id = $('#id').attr("value"),
           images = $(".testcases").find("img"),
           cont = true,
           high = false,
           animationTime = 800,
           waitTimePerItem = 500,
           waitTime = waitTimePerItem,

           beforeSendCallback = function(data) {
             var anim = function() {
               if(cont) {
                 images.animate({
                   opacity: high ? 1.0 : 0.1,
                 }, animationTime);
                 high = !high;
                 setTimeout(anim,animationTime);
               }
             };

             $("#message-text").text("Executing unit tests...");
             $("#error-message-text").text("");
             images.each( function(index, element) {
               setIconColor(element, "blue");
             });
             setTimeout(changeToCodeView,0);
             setTimeout(anim,0);
           },
           successCallback = function(data) {
             var failingTest = data.failingTest,
                 getColorFor = function(index) {
                     return index === failingTest ? "red" : "green";
                 },
                 testWasExecuted = function(index) {
                     return index <= failingTest;
                 },
                 setColor = function(index,element) {
                     var color = getColorFor(index);
                     waitTime = waitTimePerItem * (index+1);
                     setIconColor(element, color, waitTime);
                 },
                 setMessages = function() {
                     $("#message-text").html(data.message);
                     $("#error-message-text").html(data.error);
                     $("#golfgraph").html(data.golfChart);
                     $("#golfscore").html(data.golfScore);
                     configureGolf();
                 },
                 stopAnimation = function() {
                     cont = false;
                     images.stop(true);
                     images.css({ opacity: 1.0, });
                 };

             setTimeout(stopAnimation, waitTime);
             images.filter( testWasExecuted ).
                 each(setColor);
             setTimeout (setMessages, waitTime);
           };

         $.ajax({type: "POST",
           url: "/rest/problem/"+id,
           dataType: "json",
           data: { id: id, code: text, },
           timout: 20000, // default clojail timeout is 10000
           beforeSend: beforeSendCallback,
           success: successCallback,
           error: function(data, str, error) {
             $("#message-text").text("An Error occured: "+error);
           },
         });
         return false;
       };

       $("#run-button").click(clickHandler);

       session.setMode(new ClojureMode());
       session.setUseSoftTabs(true);
       session.setTabSize(2);

       document.getElementById('editor').style.fontSize='13px';
       $("#run-button").click(function(){
         var text = editor.getSession().getValue();
         $('#code').val(text);
       });

       $("#submission-button").click(function(){
         var text = editor.getSession().getValue();
         $('#code').val(text);
       });
    }
}

function configureGolf(){
  $('#graph-link').show();
  $('#golfgraph').hide();
  $('#graph-link').click(function() {
    $('#code-div').toggle('fast', function() {
      // Animation complete.
    });
    $('#golfgraph').toggle('fast', function() {
      // Animation complete.
    });
    var text = $('#graph-link').html();
    if (text && text == 'View Chart'){
       $('#graph-link').html("View Code");
    } else {
       $('#graph-link').html("View Chart");
    }
  });

}
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'

import GraphTypeSelector from 'src/dashboards/components/GraphTypeSelector'
import GaugeOptions from 'src/dashboards/components/GaugeOptions'
import SingleStatOptions from 'src/dashboards/components/SingleStatOptions'
import AxesOptions from 'src/dashboards/components/AxesOptions'

import {buildDefaultYLabel} from 'shared/presenters'

class DisplayOptions extends Component {
  constructor(props) {
    super(props)

    const {axes, queryConfigs} = props

    this.state = {
      axes: this.setDefaultLabels(axes, queryConfigs),
    }
  }

  componentWillReceiveProps(nextProps) {
    const {axes, queryConfigs} = nextProps

    this.setState({axes: this.setDefaultLabels(axes, queryConfigs)})
  }

  setDefaultLabels(axes, queryConfigs) {
    return queryConfigs.length
      ? {
          ...axes,
          y: {...axes.y, defaultYLabel: buildDefaultYLabel(queryConfigs[0])},
        }
      : axes
  }

  render() {
    const {
      colors,
      onSetBase,
      onSetScale,
      onSetLabel,
      selectedGraphType,
      onSelectGraphType,
      onSetPrefixSuffix,
      onSetYAxisBoundMin,
      onSetYAxisBoundMax,
      onAddThreshold,
      onDeleteThreshold,
      onChooseColor,
      onValidateColorValue,
      onUpdateColorValue,
    } = this.props
    const {axes} = this.state

    return (

        <GraphTypeSelector
          selectedGraphType={selectedGraphType}
          onSelectGraphType={onSelectGraphType}
        />
        {selectedGraphType === 'gauge'
          ? <GaugeOptions
              colors={colors}
              onChooseColor={onChooseColor}
              onValidateColorValue={onValidateColorValue}
              onUpdateColorValue={onUpdateColorValue}
              onAddThreshold={onAddThreshold}
              onDeleteThreshold={onDeleteThreshold}
            />
          : null}
        {selectedGraphType === 'single-stat'
          ? <SingleStatOptions
              colors={colors}
              onChooseColor={onChooseColor}
              onValidateColorValue={onValidateColorValue}
              onUpdateColorValue={onUpdateColorValue}
              onAddThreshold={onAddThreshold}
              onDeleteThreshold={onDeleteThreshold}
            />
          : null}
        {selectedGraphType === !('single-stat' || 'gauge')
          ? <AxesOptions
              selectedGraphType={selectedGraphType}
              axes={axes}
              onSetBase={onSetBase}
              onSetLabel={onSetLabel}
              onSetScale={onSetScale}
              onSetPrefixSuffix={onSetPrefixSuffix}
              onSetYAxisBoundMin={onSetYAxisBoundMin}
              onSetYAxisBoundMax={onSetYAxisBoundMax}
            />
          : null}

    )
  }
}
const {arrayOf, func, shape, string} = PropTypes

DisplayOptions.propTypes = {
  onAddThreshold: func.isRequired,
  onDeleteThreshold: func.isRequired,
  onChooseColor: func.isRequired,
  onValidateColorValue: func.isRequired,
  onUpdateColorValue: func.isRequired,
  selectedGraphType: string.isRequired,
  onSelectGraphType: func.isRequired,
  onSetPrefixSuffix: func.isRequired,
  onSetYAxisBoundMin: func.isRequired,
  onSetYAxisBoundMax: func.isRequired,
  onSetScale: func.isRequired,
  onSetLabel: func.isRequired,
  onSetBase: func.isRequired,
  axes: shape({}).isRequired,
  colors: arrayOf(
    shape({
      type: string.isRequired,
      hex: string.isRequired,
      id: string.isRequired,
      name: string.isRequired,
      value: string.isRequired,
    }).isRequired
  ),
  queryConfigs: arrayOf(shape()).isRequired,
}

export default DisplayOptions
<commit_msg>Implement stronger system for rendering appropriate graph options<commit_after>import React, {Component, PropTypes} from 'react'

import GraphTypeSelector from 'src/dashboards/components/GraphTypeSelector'
import GaugeOptions from 'src/dashboards/components/GaugeOptions'
import SingleStatOptions from 'src/dashboards/components/SingleStatOptions'
import AxesOptions from 'src/dashboards/components/AxesOptions'

import {buildDefaultYLabel} from 'shared/presenters'

class DisplayOptions extends Component {
  constructor(props) {
    super(props)

    const {axes, queryConfigs} = props

    this.state = {
      axes: this.setDefaultLabels(axes, queryConfigs),
    }
  }

  componentWillReceiveProps(nextProps) {
    const {axes, queryConfigs} = nextProps

    this.setState({axes: this.setDefaultLabels(axes, queryConfigs)})
  }

  setDefaultLabels(axes, queryConfigs) {
    return queryConfigs.length
      ? {
          ...axes,
          y: {...axes.y, defaultYLabel: buildDefaultYLabel(queryConfigs[0])},
        }
      : axes
  }

  renderOptions = () => {
    const {
      colors,
      onSetBase,
      onSetScale,
      onSetLabel,
      selectedGraphType,
      onSetPrefixSuffix,
      onSetYAxisBoundMin,
      onSetYAxisBoundMax,
      onAddThreshold,
      onDeleteThreshold,
      onChooseColor,
      onValidateColorValue,
      onUpdateColorValue,
    } = this.props
    const {axes} = this.state

    switch (selectedGraphType) {
      case 'gauge':
        return (
          <GaugeOptions
            colors={colors}
            onChooseColor={onChooseColor}
            onValidateColorValue={onValidateColorValue}
            onUpdateColorValue={onUpdateColorValue}
            onAddThreshold={onAddThreshold}
            onDeleteThreshold={onDeleteThreshold}
          />
        )
      case 'single-stat':
        return (
          <SingleStatOptions
            colors={colors}
            onChooseColor={onChooseColor}
            onValidateColorValue={onValidateColorValue}
            onUpdateColorValue={onUpdateColorValue}
            onAddThreshold={onAddThreshold}
            onDeleteThreshold={onDeleteThreshold}
          />
        )
      default:
        return (
          <AxesOptions
            selectedGraphType={selectedGraphType}
            axes={axes}
            onSetBase={onSetBase}
            onSetLabel={onSetLabel}
            onSetScale={onSetScale}
            onSetPrefixSuffix={onSetPrefixSuffix}
            onSetYAxisBoundMin={onSetYAxisBoundMin}
            onSetYAxisBoundMax={onSetYAxisBoundMax}
          />
        )
    }
  }

  render() {
    const {selectedGraphType, onSelectGraphType} = this.props

    return (

        <GraphTypeSelector
          selectedGraphType={selectedGraphType}
          onSelectGraphType={onSelectGraphType}
        />
        {this.renderOptions()}

    )
  }
}
const {arrayOf, func, shape, string} = PropTypes

DisplayOptions.propTypes = {
  onAddThreshold: func.isRequired,
  onDeleteThreshold: func.isRequired,
  onChooseColor: func.isRequired,
  onValidateColorValue: func.isRequired,
  onUpdateColorValue: func.isRequired,
  selectedGraphType: string.isRequired,
  onSelectGraphType: func.isRequired,
  onSetPrefixSuffix: func.isRequired,
  onSetYAxisBoundMin: func.isRequired,
  onSetYAxisBoundMax: func.isRequired,
  onSetScale: func.isRequired,
  onSetLabel: func.isRequired,
  onSetBase: func.isRequired,
  axes: shape({}).isRequired,
  colors: arrayOf(
    shape({
      type: string.isRequired,
      hex: string.isRequired,
      id: string.isRequired,
      name: string.isRequired,
      value: string.isRequired,
    }).isRequired
  ),
  queryConfigs: arrayOf(shape()).isRequired,
}

export default DisplayOptions
<|endoftext|>