<commit_before>/**
 * Visualization for classes.
 * @constructor
 */
var ClassView = function (parent, container) {

    this.container = container;
    this.cacheUMLExplorer = parent;

    this.graph = null;
    this.paper = null;
    this.loader = null;

    this.links = [];
    this.objects = [];

    this.PAPER_SCALE = 1;
    this.MIN_PAPER_SCALE = 0.2;
    this.MAX_PAPER_SCALE = 4;

    this.CLASS_DOC_PATH = "/csp/documatic/%25CSP.Documatic.cls";

    this.init();

};

ClassView.prototype.showLoader = function (html) {

    var d2;

    if (this.loader) this.removeLoader();

    this.resetView();

    this.loader = document.createElement("div");

    if (html) {
        this.loader.appendChild(d2 = document.createElement("div"));
        d2.innerHTML = html;
        this.loader.className = "centralText";
    } else {
        this.loader.className = "spinner";
    }
    this.container.appendChild(this.loader);

};

ClassView.prototype.removeLoader = function () {

    if (!this.loader) return;
    this.loader.parentNode.removeChild(this.loader);
    this.loader = null;

};

ClassView.prototype.resetView = function () {

    this.links = [];
    this.objects = [];
    this.paper.setOrigin(0, 0);
    this.graph.clear();

};

ClassView.prototype.openClassDoc = function (className, nameSpace) {

    window.open(
        this.CLASS_DOC_PATH + "?LIBRARY=" + encodeURIComponent(nameSpace)
            + "&CLASSNAME=" + encodeURIComponent(className),
        "_blank"
    );

};

/**
 * @param {string} name
 * @param classMetaData
 * @returns {joint.shapes.uml.Class}
 */
ClassView.prototype.createClassInstance = function (name, classMetaData) {

    var attrArr, methArr,
        classParams = classMetaData["parameters"],
        classProps = classMetaData["properties"],
        classMethods = classMetaData["methods"],
        self = this;

    var insertString = function (array, string, extraString) {
        string.match(/.{1,44}/g).forEach(function (p) {
            array.push(p + (extraString ? extraString : ""));
        });
    };

    return new joint.shapes.uml.Class({
        name: name + "\x1b" + JSON.stringify({ url: function () {alert(name)} }),
        attributes: attrArr = (function (params, ps) {
            var arr = [], n;
            for (n in params) {
                insertString(arr, n + (params[n]["type"] ? ": " + params[n]["type"] : ""));
            }
            for (n in ps) {
                insertString(
                    arr,
                    (ps[n]["private"] ? "- " : "+ ") + n
                        + (ps[n]["type"] ? ": " + ps[n]["type"] : "")
                );
            }
            return arr;
        })(classParams, classProps),
        methods: methArr = (function (met) {
            var arr = [], n;
            for (n in met) {
                insertString(
                    arr,
                    (met[n]["private"] ? "- " : "+ ") + n
                        + (met[n]["returns"] ? ": " + met[n]["returns"] : ""),
                    (met[n]["classMethod"] ?
                        "\x1b" + JSON.stringify({STYLES:{
                            textDecoration: "underline"
                        }}) : "")
                );
            }
            return arr;
        })(classMethods),
        directProps: {
            nameClickHandler: function () {
                self.openClassDoc(name, classMetaData["NAMESPACE"]);
            }
        },
        size: {
            width: 300,
            height: Math.max(attrArr.length*12.1, 15) + Math.max(methArr.length*12.1, 15) + 40
        }
    });

};

ClassView.prototype.render = function (data) {

    var p, pp, className, classInstance,
        uml = joint.shapes.uml, relFrom, relTo,
        classes = {}, connector;

    if (!data["classes"]) {
        console.error("Wrong data: no 'classes' property.", data);
        return;
    }

    for (className in data["classes"]) {
        classInstance = this.createClassInstance(className, data["classes"][className]);
        this.objects.push(classInstance);
        classes[className] = {
            instance: classInstance
        };

        this.graph.addCell(classInstance);

    }

    for (p in data["inheritance"]) {
        relFrom = (classes[p] || {}).instance;
        for (pp in data["inheritance"][p]) {
            relTo = (classes[pp] || {}).instance;
            if (relFrom && relTo) {
                this.graph.addCell(connector = new uml.Generalization({
                    source: { id: relFrom.id },
                    target: { id: relTo.id },
                    router: { name: "manhattan" },
                    connector: { name: "rounded" }
                }));
                this.links.push(connector);
            }
        }
    }

    for (p in data["aggregation"]) {
        relTo = (classes[p] || {}).instance;
        for (pp in data["aggregation"][p]) {
            relFrom = (classes[pp] || {}).instance;
            if (relFrom && relTo) {
                this.graph.addCell(connector = new uml.Aggregation({
                    source: { id: relFrom.id },
                    target: { id: relTo.id },
                    router: { name: "manhattan" },
                    connector: { name: "rounded" }
                }));
                this.links.push(connector);
            }
        }
    }

    joint.layout.DirectedGraph.layout(this.graph, {
        setLinkVertices: false,
        nodeSep: 100,
        rankSep: 100
    });

    this.updateSizes();

    for (var i in this.links) {
        this.paper.findViewByModel(this.links[i]).update();
    }

    var bb = this.paper.getContentBBox(), q = this.paper;
    this.paper.setOrigin(
        q.options.width/2 - bb.width/2,
        q.options.height/2 - Math.min(q.options.height/2 - 100, bb.height/2)
    );

};

ClassView.prototype.loadClass = function (className) {

    var self = this;

    this.showLoader();
    this.cacheUMLExplorer.source.getClassView(className, function (err, data) {
        //console.log(data);
        self.removeLoader();
        if (err) {
            self.showLoader("Unable to get " + self.cacheUMLExplorer.classTree.SELECTED_CLASS_NAME);
            console.error.call(console, err);
        } else {
            self.cacheUMLExplorer.classView.render(data);
        }
    });

    this.cacheUMLExplorer.elements.className.textContent = className;
    location.hash = "class:" + className;

};

ClassView.prototype.loadPackage = function (packageName) {

    var self = this;

    this.showLoader();
    this.cacheUMLExplorer.source.getPackageView(packageName, function (err, data) {
        //console.log(data);
        self.removeLoader();
        if (err) {
            self.showLoader("Unable to get package " + packageName);
            console.error.call(console, err);
        } else {
            self.cacheUMLExplorer.classView.render(data);
        }
    });

    this.cacheUMLExplorer.elements.className.textContent = packageName;
    location.hash = "package:" + packageName;

};

ClassView.prototype.updateSizes = function () {
    this.paper.setDimensions(this.container.offsetWidth, this.container.offsetHeight);
};

/**
 * Scale view according to delta.
 *
 * @param {number|string} delta
 */
ClassView.prototype.zoom = function (delta) {

    var scaleOld = this.PAPER_SCALE, scaleDelta;
    var sw = this.cacheUMLExplorer.elements.classViewContainer.offsetWidth,
        sh = this.cacheUMLExplorer.elements.classViewContainer.offsetHeight,
        side = delta > 0 ? 1 : -1,
        ox = this.paper.options.origin.x,
        oy = this.paper.options.origin.y;
    if (typeof delta === "number") {
        this.PAPER_SCALE += delta * Math.min(
            0.3,
            Math.abs(this.PAPER_SCALE - (delta < 0 ? this.MIN_PAPER_SCALE : this.MAX_PAPER_SCALE))/2
        );
    } else { this.PAPER_SCALE = 1; }
    this.paper.scale(this.PAPER_SCALE, this.PAPER_SCALE);
    scaleDelta = side *
        (side > 0 ? this.PAPER_SCALE / scaleOld - 1 : (scaleOld - this.PAPER_SCALE) / scaleOld);
    this.paper.setOrigin(
        ox - (sw/2 - ox)*scaleDelta,
        oy - (sh/2 - oy)*scaleDelta
    );

};

ClassView.prototype.init = function () {

    var p, self = this,
        relP = { x: 0, y: 0, trigger: false };

    this.graph = new joint.dia.Graph;

    this.paper = new joint.dia.Paper({
        el: this.container,
        width: this.container.offsetWidth,
        height: this.container.offsetHeight,
        gridSize: 20,
        model: this.graph,
        origin: {
            x: 0,
            y: 0
        }
    });

    // enables links re-routing when dragging objects
    this.graph.on("change:position", function (object) {
        if (_.contains(self.objects, object))
            for (p in self.links) {
                self.paper.findViewByModel(self.links[p]).update();
            }
    });

    this.paper.on("blank:pointerdown", function (e) {
        relP.x = e.pageX; relP.y = e.pageY; relP.trigger = true;
    });

    this.paper.on("blank:pointerup", function (e) {
        if (!relP.trigger) return;
        self.paper.setOrigin(
            self.paper.options.origin.x + e.pageX - relP.x,
            self.paper.options.origin.y + e.pageY - relP.y
        );
        relP.trigger = false;
    });

    window.addEventListener("resize", function () {
        self.updateSizes();
    });

    var moveHandler = function (e) {
        if (!relP.trigger) return;
        self.paper.setOrigin(
            self.paper.options.origin.x + e.pageX - relP.x,
            self.paper.options.origin.y + e.pageY - relP.y
        );
        relP.x = e.pageX; relP.y = e.pageY;
    };

    this.cacheUMLExplorer.elements.classViewContainer.addEventListener("mousemove", moveHandler);
    this.cacheUMLExplorer.elements.classViewContainer.addEventListener("touchmove", moveHandler);
    this.cacheUMLExplorer.elements.classViewContainer.addEventListener("mousewheel", function (e) {
        self.zoom(Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail))));
    });
    this.cacheUMLExplorer.elements.zoomInButton.addEventListener("click", function () {
        self.zoom(1);
    });
    this.cacheUMLExplorer.elements.zoomOutButton.addEventListener("click", function () {
        self.zoom(-1);
    });
    this.cacheUMLExplorer.elements.zoomNormalButton.addEventListener("click", function () {
        self.zoom(null);
    });

};<commit_msg>abstract classes visual differences<commit_after>/**
 * Visualization for classes.
 * @constructor
 */
var ClassView = function (parent, container) {

    this.container = container;
    this.cacheUMLExplorer = parent;

    this.graph = null;
    this.paper = null;
    this.loader = null;

    this.links = [];
    this.objects = [];

    this.PAPER_SCALE = 1;
    this.MIN_PAPER_SCALE = 0.2;
    this.MAX_PAPER_SCALE = 4;

    this.CLASS_DOC_PATH = "/csp/documatic/%25CSP.Documatic.cls";

    this.init();

};

ClassView.prototype.showLoader = function (html) {

    var d2;

    if (this.loader) this.removeLoader();

    this.resetView();

    this.loader = document.createElement("div");

    if (html) {
        this.loader.appendChild(d2 = document.createElement("div"));
        d2.innerHTML = html;
        this.loader.className = "centralText";
    } else {
        this.loader.className = "spinner";
    }
    this.container.appendChild(this.loader);

};

ClassView.prototype.removeLoader = function () {

    if (!this.loader) return;
    this.loader.parentNode.removeChild(this.loader);
    this.loader = null;

};

ClassView.prototype.resetView = function () {

    this.links = [];
    this.objects = [];
    this.paper.setOrigin(0, 0);
    this.graph.clear();

};

ClassView.prototype.openClassDoc = function (className, nameSpace) {

    window.open(
        this.CLASS_DOC_PATH + "?LIBRARY=" + encodeURIComponent(nameSpace)
            + "&CLASSNAME=" + encodeURIComponent(className),
        "_blank"
    );

};

/**
 * @param {string} name
 * @param classMetaData
 * @returns {joint.shapes.uml.Class}
 */
ClassView.prototype.createClassInstance = function (name, classMetaData) {

    var attrArr, methArr, nameArr,
        classParams = classMetaData["parameters"],
        classProps = classMetaData["properties"],
        classMethods = classMetaData["methods"],
        self = this;

    var insertString = function (array, string, extraString) {
        string.match(/.{1,44}/g).forEach(function (p) {
            array.push(p + (extraString ? extraString : ""));
        });
    };

    return new joint.shapes.uml.Class({
        name: nameArr = (classMetaData["ABSTRACT"] ? ["<<Abstract>>", name] : [name]),
        attributes: attrArr = (function (params, ps) {
            var arr = [], n;
            for (n in params) {
                insertString(arr, n + (params[n]["type"] ? ": " + params[n]["type"] : ""));
            }
            for (n in ps) {
                insertString(
                    arr,
                    (ps[n]["private"] ? "- " : "+ ") + n
                        + (ps[n]["type"] ? ": " + ps[n]["type"] : "")
                );
            }
            return arr;
        })(classParams, classProps),
        methods: methArr = (function (met) {
            var arr = [], n;
            for (n in met) {
                insertString(
                    arr,
                    (met[n]["private"] ? "- " : "+ ") + n
                        + (met[n]["returns"] ? ": " + met[n]["returns"] : ""),
                    (met[n]["classMethod"] ?
                        "\x1b" + JSON.stringify({STYLES:{
                            textDecoration: "underline"
                        }}) : "")
                );
            }
            return arr;
        })(classMethods),
        directProps: {
            nameClickHandler: function () {
                self.openClassDoc(name, classMetaData["NAMESPACE"]);
            }
        },
        size: {
            width: 300,
            height: Math.max(nameArr.length*12.1, 0) + Math.max(attrArr.length*12.1, 0)
                + Math.max(methArr.length*12.1, 0) + 30
        }
    });

};

ClassView.prototype.render = function (data) {

    var p, pp, className, classInstance,
        uml = joint.shapes.uml, relFrom, relTo,
        classes = {}, connector;

    if (!data["classes"]) {
        console.error("Wrong data: no 'classes' property.", data);
        return;
    }

    for (className in data["classes"]) {
        classInstance = this.createClassInstance(className, data["classes"][className]);
        this.objects.push(classInstance);
        classes[className] = {
            instance: classInstance
        };

        this.graph.addCell(classInstance);

    }

    for (p in data["inheritance"]) {
        relFrom = (classes[p] || {}).instance;
        for (pp in data["inheritance"][p]) {
            relTo = (classes[pp] || {}).instance;
            if (relFrom && relTo) {
                this.graph.addCell(connector = new uml.Generalization({
                    source: { id: relFrom.id },
                    target: { id: relTo.id },
                    router: { name: "manhattan" },
                    connector: { name: "rounded" }
                }));
                this.links.push(connector);
            }
        }
    }

    for (p in data["aggregation"]) {
        relTo = (classes[p] || {}).instance;
        for (pp in data["aggregation"][p]) {
            relFrom = (classes[pp] || {}).instance;
            if (relFrom && relTo) {
                this.graph.addCell(connector = new uml.Aggregation({
                    source: { id: relFrom.id },
                    target: { id: relTo.id },
                    router: { name: "manhattan" },
                    connector: { name: "rounded" }
                }));
                this.links.push(connector);
            }
        }
    }

    joint.layout.DirectedGraph.layout(this.graph, {
        setLinkVertices: false,
        nodeSep: 100,
        rankSep: 100
    });

    this.updateSizes();

    for (var i in this.links) {
        this.paper.findViewByModel(this.links[i]).update();
    }

    var bb = this.paper.getContentBBox(), q = this.paper;
    this.paper.setOrigin(
        q.options.width/2 - bb.width/2,
        q.options.height/2 - Math.min(q.options.height/2 - 100, bb.height/2)
    );

};

ClassView.prototype.loadClass = function (className) {

    var self = this;

    this.showLoader();
    this.cacheUMLExplorer.source.getClassView(className, function (err, data) {
        //console.log(data);
        self.removeLoader();
        if (err) {
            self.showLoader("Unable to get " + self.cacheUMLExplorer.classTree.SELECTED_CLASS_NAME);
            console.error.call(console, err);
        } else {
            self.cacheUMLExplorer.classView.render(data);
        }
    });

    this.cacheUMLExplorer.elements.className.textContent = className;
    location.hash = "class:" + className;

};

ClassView.prototype.loadPackage = function (packageName) {

    var self = this;

    this.showLoader();
    this.cacheUMLExplorer.source.getPackageView(packageName, function (err, data) {
        //console.log(data);
        self.removeLoader();
        if (err) {
            self.showLoader("Unable to get package " + packageName);
            console.error.call(console, err);
        } else {
            self.cacheUMLExplorer.classView.render(data);
        }
    });

    this.cacheUMLExplorer.elements.className.textContent = packageName;
    location.hash = "package:" + packageName;

};

ClassView.prototype.updateSizes = function () {
    this.paper.setDimensions(this.container.offsetWidth, this.container.offsetHeight);
};

/**
 * Scale view according to delta.
 *
 * @param {number|string} delta
 */
ClassView.prototype.zoom = function (delta) {

    var scaleOld = this.PAPER_SCALE, scaleDelta;
    var sw = this.cacheUMLExplorer.elements.classViewContainer.offsetWidth,
        sh = this.cacheUMLExplorer.elements.classViewContainer.offsetHeight,
        side = delta > 0 ? 1 : -1,
        ox = this.paper.options.origin.x,
        oy = this.paper.options.origin.y;
    if (typeof delta === "number") {
        this.PAPER_SCALE += delta * Math.min(
            0.3,
            Math.abs(this.PAPER_SCALE - (delta < 0 ? this.MIN_PAPER_SCALE : this.MAX_PAPER_SCALE))/2
        );
    } else { this.PAPER_SCALE = 1; }
    this.paper.scale(this.PAPER_SCALE, this.PAPER_SCALE);
    scaleDelta = side *
        (side > 0 ? this.PAPER_SCALE / scaleOld - 1 : (scaleOld - this.PAPER_SCALE) / scaleOld);
    this.paper.setOrigin(
        ox - (sw/2 - ox)*scaleDelta,
        oy - (sh/2 - oy)*scaleDelta
    );

};

ClassView.prototype.init = function () {

    var p, self = this,
        relP = { x: 0, y: 0, trigger: false };

    this.graph = new joint.dia.Graph;

    this.paper = new joint.dia.Paper({
        el: this.container,
        width: this.container.offsetWidth,
        height: this.container.offsetHeight,
        gridSize: 20,
        model: this.graph,
        origin: {
            x: 0,
            y: 0
        }
    });

    // enables links re-routing when dragging objects
    this.graph.on("change:position", function (object) {
        if (_.contains(self.objects, object))
            for (p in self.links) {
                self.paper.findViewByModel(self.links[p]).update();
            }
    });

    this.paper.on("blank:pointerdown", function (e) {
        relP.x = e.pageX; relP.y = e.pageY; relP.trigger = true;
    });

    this.paper.on("blank:pointerup", function (e) {
        if (!relP.trigger) return;
        self.paper.setOrigin(
            self.paper.options.origin.x + e.pageX - relP.x,
            self.paper.options.origin.y + e.pageY - relP.y
        );
        relP.trigger = false;
    });

    window.addEventListener("resize", function () {
        self.updateSizes();
    });

    var moveHandler = function (e) {
        if (!relP.trigger) return;
        self.paper.setOrigin(
            self.paper.options.origin.x + e.pageX - relP.x,
            self.paper.options.origin.y + e.pageY - relP.y
        );
        relP.x = e.pageX; relP.y = e.pageY;
    };

    this.cacheUMLExplorer.elements.classViewContainer.addEventListener("mousemove", moveHandler);
    this.cacheUMLExplorer.elements.classViewContainer.addEventListener("touchmove", moveHandler);
    this.cacheUMLExplorer.elements.classViewContainer.addEventListener("mousewheel", function (e) {
        self.zoom(Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail))));
    });
    this.cacheUMLExplorer.elements.zoomInButton.addEventListener("click", function () {
        self.zoom(1);
    });
    this.cacheUMLExplorer.elements.zoomOutButton.addEventListener("click", function () {
        self.zoom(-1);
    });
    this.cacheUMLExplorer.elements.zoomNormalButton.addEventListener("click", function () {
        self.zoom(null);
    });

};<|endoftext|>


<commit_before>/*
 * Copyright 2019 The CodeWorld Authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

(() => {
    let available = false;
    let active = false;

    // Checked by parent.updateUI
    window.debugAvailable = false;
    window.debugActive = false;

    // These functions are provided by a debugmode-supported entrypoint when
    // calling initDebugMode
    //  debugGetNode :: { x :: Double, y :: Double } -> Int
    //   Returns the nodeId of the shape at the coordinate (x,y) of the canvas.
    //   Negative return indicates no shape at that point.
    let debugGetNode = null;
    //  debugSetActive :: Bool -> ()
    //   Indicates to the entry point when debugmode has been turned off and on
    let debugSetActive = null;
    //  debugGetPicture :: () -> Object
    //   Gets an object showing the current state of the Picture being drawn. Is
    //   only called directly after debugSetActive(true).
    let debugGetPicture = null;
    //  debugHighlightShape :: (Bool, Int) -> ()
    //   Indicates to the entry point should highlight or select a shape or tree
    //   of shapes. A true value indicates highlight (change color and bring to
    //   front) and false indicates select (change color and do not change
    //   position). A negative value indicates to stop highlighting or selecting.
    //   At most one shape may be highlighted and one shape selected at a time.
    let debugHighlightShape = null;
    // debugDrawShape :: (Canvas, Int) -> ()
    //  Draws the node with a coordinate plane in the background.
    let debugDrawShape = null;

    let cachedPic = null;
    let canvas = null;

    // Globals

    function initDebugMode(getNode, setActive, getPicture, highlightShape,
        drawShape) {
        debugGetNode = getNode;
        debugSetActive = setActive;
        debugGetPicture = getPicture;
        debugHighlightShape = highlightShape;
        debugDrawShape = drawShape;

        if (!available) {
            canvas = document.getElementById('screen');

            canvas.addEventListener('mousemove', evt => {
                if (active) {
                    const nodeId = debugGetNode({
                        x: evt.clientX,
                        y: evt.clientY
                    });

                    debugHighlightShape(true, nodeId);
                }
            });

            canvas.addEventListener('mouseout', evt => {
                if (active) {
                    debugHighlightShape(true, -1);
                }
            });

            canvas.addEventListener('click', evt => {
                if (active) {
                    const nodeId = debugGetNode({
                        x: evt.clientX,
                        y: evt.clientY
                    });

                    if (nodeId >= 0) {
                        parent.openTreeDialog(nodeId);
                    }
                }
            });

            available = true;
            window.debugAvailable = true;
        }

    }
    window.initDebugMode = initDebugMode;

    function startDebugMode() {
        if (!available) {
            throw new Error('Debug mode is not available.');
        }

        active = true;
        debugSetActive(true);
        cachedPic = debugGetPicture();

        parent.initTreeDialog(cachedPic, debugHighlightShape,
            debugDrawShape, () => stopDebugMode());
        parent.openTreeDialog(0);

        window.debugActive = true;
        parent.updateUI();
    }
    window.startDebugMode = startDebugMode;

    function stopDebugMode() {
        active = false;
        debugSetActive(false);
        cachedPic = null;

        debugHighlightShape(true, -1);
        debugHighlightShape(false, -1);

        parent.destroyTreeDialog();

        window.debugActive = false;
        parent.updateUI();
    }
    window.stopDebugMode = stopDebugMode;

    function toggleDebugMode() {
        if (active) {
            stopDebugMode();
        } else {
            startDebugMode();
        }
    }
    window.toggleDebugMode = toggleDebugMode;
})();
<commit_msg>Try to do a parent.updateUI() when enabling debug mode.<commit_after>/*
 * Copyright 2019 The CodeWorld Authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

(() => {
    let available = false;
    let active = false;

    // Checked by parent.updateUI
    window.debugAvailable = false;
    window.debugActive = false;

    // These functions are provided by a debugmode-supported entrypoint when
    // calling initDebugMode
    //  debugGetNode :: { x :: Double, y :: Double } -> Int
    //   Returns the nodeId of the shape at the coordinate (x,y) of the canvas.
    //   Negative return indicates no shape at that point.
    let debugGetNode = null;
    //  debugSetActive :: Bool -> ()
    //   Indicates to the entry point when debugmode has been turned off and on
    let debugSetActive = null;
    //  debugGetPicture :: () -> Object
    //   Gets an object showing the current state of the Picture being drawn. Is
    //   only called directly after debugSetActive(true).
    let debugGetPicture = null;
    //  debugHighlightShape :: (Bool, Int) -> ()
    //   Indicates to the entry point should highlight or select a shape or tree
    //   of shapes. A true value indicates highlight (change color and bring to
    //   front) and false indicates select (change color and do not change
    //   position). A negative value indicates to stop highlighting or selecting.
    //   At most one shape may be highlighted and one shape selected at a time.
    let debugHighlightShape = null;
    // debugDrawShape :: (Canvas, Int) -> ()
    //  Draws the node with a coordinate plane in the background.
    let debugDrawShape = null;

    let cachedPic = null;
    let canvas = null;

    // Globals

    function initDebugMode(getNode, setActive, getPicture, highlightShape,
        drawShape) {
        debugGetNode = getNode;
        debugSetActive = setActive;
        debugGetPicture = getPicture;
        debugHighlightShape = highlightShape;
        debugDrawShape = drawShape;

        if (!available) {
            canvas = document.getElementById('screen');

            canvas.addEventListener('mousemove', evt => {
                if (active) {
                    const nodeId = debugGetNode({
                        x: evt.clientX,
                        y: evt.clientY
                    });

                    debugHighlightShape(true, nodeId);
                }
            });

            canvas.addEventListener('mouseout', evt => {
                if (active) {
                    debugHighlightShape(true, -1);
                }
            });

            canvas.addEventListener('click', evt => {
                if (active) {
                    const nodeId = debugGetNode({
                        x: evt.clientX,
                        y: evt.clientY
                    });

                    if (nodeId >= 0) {
                        parent.openTreeDialog(nodeId);
                    }
                }
            });

            available = true;
            window.debugAvailable = true;
            if (parent && parent.updateUI) {
              parent.updateUI();
            }
        }
    }
    window.initDebugMode = initDebugMode;

    function startDebugMode() {
        if (!available) {
            throw new Error('Debug mode is not available.');
        }

        active = true;
        debugSetActive(true);
        cachedPic = debugGetPicture();

        parent.initTreeDialog(cachedPic, debugHighlightShape,
            debugDrawShape, () => stopDebugMode());
        parent.openTreeDialog(0);

        window.debugActive = true;
        parent.updateUI();
    }
    window.startDebugMode = startDebugMode;

    function stopDebugMode() {
        active = false;
        debugSetActive(false);
        cachedPic = null;

        debugHighlightShape(true, -1);
        debugHighlightShape(false, -1);

        parent.destroyTreeDialog();

        window.debugActive = false;
        parent.updateUI();
    }
    window.stopDebugMode = stopDebugMode;

    function toggleDebugMode() {
        if (active) {
            stopDebugMode();
        } else {
            startDebugMode();
        }
    }
    window.toggleDebugMode = toggleDebugMode;
})();
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {
  sankey,
  sankeyLinkHorizontal,
  sankeyLeft,
  sankeyRight,
  sankeyCenter,
  sankeyJustify
} from 'd3-sankey';
import XYPlot from 'plot/xy-plot';

import {MarginPropType, getInnerDimensions} from 'utils/chart-utils';
import VerticalRectSeries from 'plot/series/vertical-rect-series';
import LabelSeries from 'plot/series/label-series';
import Voronoi from 'plot/voronoi';
import {DISCRETE_COLOR_RANGE} from 'theme';

import SankeyLink from './sankey-link';
const NOOP = f => f;

const ALIGNMENTS = {
  justify: sankeyJustify,
  center: sankeyCenter,
  left: sankeyLeft,
  right: sankeyRight
};

const DEFAULT_MARGINS = {
  top: 20,
  left: 20,
  right: 20,
  bottom: 20
};

class Sankey extends Component {
  render() {

    const {
      align,
      animation,
      children,
      className,
      hasVoronoi,
      height,
      hideLabels,
      labelRotation,
      layout,
      links,
      linkOpacity,
      margin,
      nodePadding,
      nodes,
      nodeWidth,
      onValueClick,
      onValueMouseOver,
      onValueMouseOut,
      onLinkClick,
      onLinkMouseOver,
      onLinkMouseOut,
      style,
      width
    } = this.props;
    const nodesCopy = [...new Array(nodes.length)].map((e, i) => ({...nodes[i]}));
    const linksCopy = [...new Array(links.length)].map((e, i) => ({...links[i]}));

    const {marginLeft, marginTop, marginRight, marginBottom} = getInnerDimensions({
      margin, height, width
    }, DEFAULT_MARGINS);
    const sankeyInstance = sankey()
      .extent([
        [marginLeft, marginTop],
        [width - marginRight, height - marginBottom - marginTop]
      ])
      .nodeWidth(nodeWidth)
      .nodePadding(nodePadding)
      .nodes(nodesCopy)
      .links(linksCopy)
      .nodeAlign(ALIGNMENTS[align])
      .iterations(layout);
    sankeyInstance(nodesCopy);

    const nWidth = sankeyInstance.nodeWidth();
    const path = sankeyLinkHorizontal();

    return (
      <XYPlot
        {...this.props}
        yType="literal"
        className={`rv-sankey ${className}`}>
        {linksCopy.map((link, i) => (
          <SankeyLink
            style={style.links}
            data={path(link)}
            opacity={link.opacity || linkOpacity}
            color={link.color}
            onLinkClick={onLinkClick}
            onLinkMouseOver={onLinkMouseOver}
            onLinkMouseOut={onLinkMouseOut}
            strokeWidth={Math.max(link.width, 1)}
            node={link}
            nWidth={nWidth}
            key={`link-${i}`}/>
        ))}
        <VerticalRectSeries
          animation={animation}
          className={`${className} rv-sankey__node`}
          data={nodesCopy.map(node => ({
            ...node,
            y: node.y1 - marginTop,
            y0: node.y0 - marginTop,
            x: node.x1,
            x0: node.x0,
            color: node.color || DISCRETE_COLOR_RANGE[0],
            sourceLinks: null,
            targetLinks: null
          }))}
          style={style.rects}
          onValueClick={onValueClick}
          onValueMouseOver={onValueMouseOver}
          onValueMouseOut={onValueMouseOut}
          colorType="literal" />
        {!hideLabels && (
          <LabelSeries
            animation={animation}
            className={className}
            rotation={labelRotation}
            data={nodesCopy.map(node => {
              return {
                x: node.x0 + (node.x0 < width / 2 ? nWidth + 10 : -10),
                y: (node.y0 + node.y1) / 2 - marginTop,
                label: node.name,
                style: {
                  dy: '-.5em',
                  ...style.labels
                }
              };
            })}
            />
        )}
        {hasVoronoi && (
          <Voronoi
            className="rv-sankey__voronoi"
            extent={[
              [-marginLeft, -marginTop],
              [width + marginRight, height + marginBottom]]}
            nodes={nodesCopy}
            onClick={onValueClick}
            onHover={onValueMouseOver}
            onBlur={onValueMouseOut}
            x={d => d.x0 + (d.x1 - d.x0) / 2}
            y={d => d.y0 + (d.y1 - d.y0) / 2}
          />
        )}
        {children}
      </XYPlot>
    );
  }

}

Sankey.defaultProps = {
  align: 'justify',
  className: '',
  hasVoronoi: false,
  hideLabels: false,
  labelRotation: 0,
  layout: 50,
  margin: DEFAULT_MARGINS,
  nodePadding: 10,
  nodeWidth: 10,
  onValueMouseOver: NOOP,
  onValueClick: NOOP,
  onValueMouseOut: NOOP,
  onLinkClick: NOOP,
  onLinkMouseOver: NOOP,
  onLinkMouseOut: NOOP,
  style: {
    links: {},
    rects: {},
    labels: {}
  }
};

Sankey.propTypes = {
  align: PropTypes.oneOf(['justify', 'left', 'right', 'center']),
  className: PropTypes.string,
  hasVoronoi: PropTypes.bool,
  height: PropTypes.number.isRequired,
  hideLabels: PropTypes.bool,
  labelRotation: PropTypes.number,
  layout: PropTypes.number,
  links: PropTypes.arrayOf(PropTypes.shape({
    source: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.object
    ]).isRequired,
    target: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.object
    ]).isRequired
  })).isRequired,
  margin: MarginPropType,
  nodePadding: PropTypes.number,
  nodes: PropTypes.arrayOf(PropTypes.object).isRequired,
  nodeWidth: PropTypes.number,
  onValueMouseOver: PropTypes.func,
  onValueClick: PropTypes.func,
  onValueMouseOut: PropTypes.func,
  onLinkClick: PropTypes.func,
  onLinkMouseOver: PropTypes.func,
  onLinkMouseOut: PropTypes.func,
  style: PropTypes.shape({
    links: PropTypes.object,
    rects: PropTypes.object,
    labels: PropTypes.object
  }),
  width: PropTypes.number.isRequired
};
export default Sankey;
<commit_msg>center sankey chart labels (#915)<commit_after>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {
  sankey,
  sankeyLinkHorizontal,
  sankeyLeft,
  sankeyRight,
  sankeyCenter,
  sankeyJustify
} from 'd3-sankey';
import XYPlot from 'plot/xy-plot';

import {MarginPropType, getInnerDimensions} from 'utils/chart-utils';
import VerticalRectSeries from 'plot/series/vertical-rect-series';
import LabelSeries from 'plot/series/label-series';
import Voronoi from 'plot/voronoi';
import {DISCRETE_COLOR_RANGE} from 'theme';

import SankeyLink from './sankey-link';
const NOOP = f => f;

const ALIGNMENTS = {
  justify: sankeyJustify,
  center: sankeyCenter,
  left: sankeyLeft,
  right: sankeyRight
};

const DEFAULT_MARGINS = {
  top: 20,
  left: 20,
  right: 20,
  bottom: 20
};

class Sankey extends Component {
  render() {

    const {
      align,
      animation,
      children,
      className,
      hasVoronoi,
      height,
      hideLabels,
      labelRotation,
      layout,
      links,
      linkOpacity,
      margin,
      nodePadding,
      nodes,
      nodeWidth,
      onValueClick,
      onValueMouseOver,
      onValueMouseOut,
      onLinkClick,
      onLinkMouseOver,
      onLinkMouseOut,
      style,
      width
    } = this.props;
    const nodesCopy = [...new Array(nodes.length)].map((e, i) => ({...nodes[i]}));
    const linksCopy = [...new Array(links.length)].map((e, i) => ({...links[i]}));

    const {marginLeft, marginTop, marginRight, marginBottom} = getInnerDimensions({
      margin, height, width
    }, DEFAULT_MARGINS);
    const sankeyInstance = sankey()
      .extent([
        [marginLeft, marginTop],
        [width - marginRight, height - marginBottom - marginTop]
      ])
      .nodeWidth(nodeWidth)
      .nodePadding(nodePadding)
      .nodes(nodesCopy)
      .links(linksCopy)
      .nodeAlign(ALIGNMENTS[align])
      .iterations(layout);
    sankeyInstance(nodesCopy);

    const nWidth = sankeyInstance.nodeWidth();
    const path = sankeyLinkHorizontal();

    return (
      <XYPlot
        {...this.props}
        yType="literal"
        className={`rv-sankey ${className}`}>
        {linksCopy.map((link, i) => (
          <SankeyLink
            style={style.links}
            data={path(link)}
            opacity={link.opacity || linkOpacity}
            color={link.color}
            onLinkClick={onLinkClick}
            onLinkMouseOver={onLinkMouseOver}
            onLinkMouseOut={onLinkMouseOut}
            strokeWidth={Math.max(link.width, 1)}
            node={link}
            nWidth={nWidth}
            key={`link-${i}`}/>
        ))}
        <VerticalRectSeries
          animation={animation}
          className={`${className} rv-sankey__node`}
          data={nodesCopy.map(node => ({
            ...node,
            y: node.y1 - marginTop,
            y0: node.y0 - marginTop,
            x: node.x1,
            x0: node.x0,
            color: node.color || DISCRETE_COLOR_RANGE[0],
            sourceLinks: null,
            targetLinks: null
          }))}
          style={style.rects}
          onValueClick={onValueClick}
          onValueMouseOver={onValueMouseOver}
          onValueMouseOut={onValueMouseOut}
          colorType="literal" />
        {!hideLabels && (
          <LabelSeries
            animation={animation}
            className={className}
            rotation={labelRotation}
            labelAnchorY="text-before-edge"
            data={nodesCopy.map(node => {
              return {
                x: node.x0 + (node.x0 < width / 2 ? nWidth + 10 : -10),
                y: (node.y0 + node.y1) / 2 - marginTop,
                label: node.name,
                style: {
                  dy: '-.5em',
                  ...style.labels
                }
              };
            })}
            />
        )}
        {hasVoronoi && (
          <Voronoi
            className="rv-sankey__voronoi"
            extent={[
              [-marginLeft, -marginTop],
              [width + marginRight, height + marginBottom]]}
            nodes={nodesCopy}
            onClick={onValueClick}
            onHover={onValueMouseOver}
            onBlur={onValueMouseOut}
            x={d => d.x0 + (d.x1 - d.x0) / 2}
            y={d => d.y0 + (d.y1 - d.y0) / 2}
          />
        )}
        {children}
      </XYPlot>
    );
  }

}

Sankey.defaultProps = {
  align: 'justify',
  className: '',
  hasVoronoi: false,
  hideLabels: false,
  labelRotation: 0,
  layout: 50,
  margin: DEFAULT_MARGINS,
  nodePadding: 10,
  nodeWidth: 10,
  onValueMouseOver: NOOP,
  onValueClick: NOOP,
  onValueMouseOut: NOOP,
  onLinkClick: NOOP,
  onLinkMouseOver: NOOP,
  onLinkMouseOut: NOOP,
  style: {
    links: {},
    rects: {},
    labels: {}
  }
};

Sankey.propTypes = {
  align: PropTypes.oneOf(['justify', 'left', 'right', 'center']),
  className: PropTypes.string,
  hasVoronoi: PropTypes.bool,
  height: PropTypes.number.isRequired,
  hideLabels: PropTypes.bool,
  labelRotation: PropTypes.number,
  layout: PropTypes.number,
  links: PropTypes.arrayOf(PropTypes.shape({
    source: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.object
    ]).isRequired,
    target: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.object
    ]).isRequired
  })).isRequired,
  margin: MarginPropType,
  nodePadding: PropTypes.number,
  nodes: PropTypes.arrayOf(PropTypes.object).isRequired,
  nodeWidth: PropTypes.number,
  onValueMouseOver: PropTypes.func,
  onValueClick: PropTypes.func,
  onValueMouseOut: PropTypes.func,
  onLinkClick: PropTypes.func,
  onLinkMouseOver: PropTypes.func,
  onLinkMouseOut: PropTypes.func,
  style: PropTypes.shape({
    links: PropTypes.object,
    rects: PropTypes.object,
    labels: PropTypes.object
  }),
  width: PropTypes.number.isRequired
};
export default Sankey;
<|endoftext|>


<commit_before>var activeChannel;

window.ChannelCollectionView = Backbone.Marionette.CompositeView.extend({
  template: 'channels/channel_list',
  itemView: ChannelItemView,

  initialize: function(opts) {
    var self = this;

    this.collection.bind('add',   this.add, this);
    this.collection.bind('reset', this.reset, this);
  },

  appendHtml: function(collectionView, itemView) {
    collectionView.$('#channel-listing').append(itemView.el);
  }

});
<commit_msg>removed unnecesary [sic] code<commit_after>window.ChannelCollectionView = Backbone.Marionette.CompositeView.extend({
  template: 'channels/channel_list',
  itemView: ChannelItemView,

  appendHtml: function(collectionView, itemView) {
    collectionView.$('#channel-listing').append(itemView.el);
  }
});
<|endoftext|>


<commit_before>/**
 * Admin
 *
 * for admin pages
 */
var Admin = {};

/**
 * Navigation
 *
 * @return void
 */
Admin.navigation = function() {
	$('ul.sf-menu').supersubs({
		minWidth: 12,
		maxWidth: 27,
		extraWidth: 1
	}).superfish({
		delay: 200,
		animation: {opacity:'show',height:'show'},
		speed: 'fast',
		autoArrows: true,
		dropShadows: false,
		disableHI: true
	});

	var $sidebar = $('#sidebar-menu');
	var $topLevelMenus = $('#sidebar-menu > li > .hasChild');

	// no item is current, fallback to current controller index
	var $current = $('.sidebar .current');
	if ($current.length == 0) {
		var selector = _.template(
			'a.sidebar-item[href^="<%= basePath %>admin/' +
			'<%= params.plugin %>/' +
			'<%= params.controller %>"]:first'
		);
		if ($(selector(Croogo)).addClass('current').length == 0) {
			var selector = _.template(
				'a.sidebar-item[href="<%= basePath %>admin/' +
				'<%= params.plugin %>"]'
			);
			$(selector(Croogo)).addClass('current');
		}
	}
	// traverse parent elements and mark as current
	$($current.selector).parentsUntil('.sidebar', 'ul').each(function() {
		$(this).siblings('a.sidebar-item').addClass('current')
	});
	if (window.innerWidth >= 979) {
		$topLevelMenus.parent().find('> .current').next('ul').toggle();
	}

	var dropdownOpen = function() {
		$(this)
			.addClass('dropdown-open')
			.removeClass('dropdown-close')
			.siblings('.sidebar-item')
			.addClass('dropdown-open')
			.removeClass('dropdown-close');
	};

	var dropdownClose = function() {
		$(this)
			.addClass('dropdown-close')
			.removeClass('dropdown-open')
			.siblings('.sidebar-item')
			.addClass('dropdown-close')
			.removeClass('dropdown-open');
	};

	$topLevelMenus.on('click blur', function(e) {
		var $this = $(this);
		var $ul = $(this).next('ul')
		var sidebarWidth = $sidebar.width();

		if (e.type == 'blur' && window.innerWidth > 979) {
			return;
		}

		if ($ul.is(':visible')) {

			var onComplete = function() {
				dropdownClose.call($ul.get(0));
				$ul.css({'margin-left': sidebarWidth + 'px', 'margin-top': 'inherit'})
			}

			if (window.innerWidth <= 979) {
				$ul.hide('fade', 'fast', onComplete);
			} else {
				$ul.slideUp('fast', onComplete);
			}
		} else {
			$topLevelMenus.siblings('ul:visible').slideUp('fast', function() {
				dropdownClose.call(this);
			});
			dropdownOpen.call(this);
			if (window.innerWidth <= 979) {
				$ul.css({'position': 'absolute', 'margin-left': sidebarWidth + 1 + 'px', 'margin-top': '-42px'})
				$ul.show('fade', 'fast');
			} else {
				$ul.css({'margin-left': 0, 'position': 'relative'});
				$ul.slideDown('fast');
			}
		}
		e.stopPropagation();
		return false;
	});

	$(window).on('resize', function() {
		$('#sidebar-menu > li ul:visible').each(function() {
			$(this).toggle()
			dropdownClose.call(this);
		});
	});
}

/**
 * Forms
 *
 * @return void
 */
Admin.form = function() {
	// Tooltips activation
	$('[rel=tooltip],*[data-title],input[title],textarea[title]').tooltip();
	$('a.tooltip').tipsy({gravity: 's', html: false}); // Legacy tooltip

	var ajaxToggle = function(e) {
		var $this = $(this);
		$this.addClass('loading').find('i').attr('class', 'icon-none');
		var url = $this.data('url');
		$.post(url, function(data) {
			$this.parent().html(data);
		});
	}

	// Row Actions
	$('body')
		.on('click', 'a[data-row-action]', Admin.processLink)
		.on('click', 'a.ajax-toggle', ajaxToggle)
	;
}

/**
 * Helper to process row action links
 */
Admin.processLink = function(event) {
	var $el = $(event.currentTarget);
	var checkbox = $(event.currentTarget.attributes["href"].value);
	var form = checkbox.get(0).form;
	var action = $el.data('row-action');
	var confirmMessage = $el.data('confirm-message');
	if (confirmMessage && !confirm(confirmMessage)) {
		return false;
	}
	$('input[type=checkbox]', form).prop('checked', false);
	checkbox.prop("checked", true);
	$('#bulk-action select', form).val(action);
	form.submit();
	return false;
}

/**
 * Extra stuff
 *
 * rounded corners, striped table rows, etc
 *
 * @return void
 */
Admin.extra = function() {
	// Activates the first tab in #content
	$('#content .nav-tabs > li:first-child a').tab('show');

	// Box toggle
	$('body').on('click', '.box-title', function() {
		$(this).next().slideToggle();
	});

	$('.tabs').tabs(); // legacy tabs from jquery-ui
	$('textarea').not('.content').elastic();
	$("div.message").addClass("notice");
	$('#loading p').addClass('ui-corner-bl ui-corner-br');
}

/**
 * Document ready
 *
 * @return void
 */
$(document).ready(function() {
	Admin.navigation();
	Admin.form();
	Admin.extra();
});
<commit_msg>adding semicolons in admin.js<commit_after>/**
 * Admin
 *
 * for admin pages
 */
var Admin = {};

/**
 * Navigation
 *
 * @return void
 */
Admin.navigation = function() {
	$('ul.sf-menu').supersubs({
		minWidth: 12,
		maxWidth: 27,
		extraWidth: 1
	}).superfish({
		delay: 200,
		animation: {opacity:'show',height:'show'},
		speed: 'fast',
		autoArrows: true,
		dropShadows: false,
		disableHI: true
	});

	var $sidebar = $('#sidebar-menu');
	var $topLevelMenus = $('#sidebar-menu > li > .hasChild');

	// no item is current, fallback to current controller index
	var $current = $('.sidebar .current');
	if ($current.length == 0) {
		var selector = _.template(
			'a.sidebar-item[href^="<%= basePath %>admin/' +
			'<%= params.plugin %>/' +
			'<%= params.controller %>"]:first'
		);
		if ($(selector(Croogo)).addClass('current').length == 0) {
			var selector = _.template(
				'a.sidebar-item[href="<%= basePath %>admin/' +
				'<%= params.plugin %>"]'
			);
			$(selector(Croogo)).addClass('current');
		}
	}
	// traverse parent elements and mark as current
	$($current.selector).parentsUntil('.sidebar', 'ul').each(function() {
		$(this).siblings('a.sidebar-item').addClass('current')
	});
	if (window.innerWidth >= 979) {
		$topLevelMenus.parent().find('> .current').next('ul').toggle();
	}

	var dropdownOpen = function() {
		$(this)
			.addClass('dropdown-open')
			.removeClass('dropdown-close')
			.siblings('.sidebar-item')
			.addClass('dropdown-open')
			.removeClass('dropdown-close');
	};

	var dropdownClose = function() {
		$(this)
			.addClass('dropdown-close')
			.removeClass('dropdown-open')
			.siblings('.sidebar-item')
			.addClass('dropdown-close')
			.removeClass('dropdown-open');
	};

	$topLevelMenus.on('click blur', function(e) {
		var $this = $(this);
		var $ul = $(this).next('ul');
		var sidebarWidth = $sidebar.width();

		if (e.type == 'blur' && window.innerWidth > 979) {
			return;
		}

		if ($ul.is(':visible')) {

			var onComplete = function() {
				dropdownClose.call($ul.get(0));
				$ul.css({'margin-left': sidebarWidth + 'px', 'margin-top': 'inherit'})
			}

			if (window.innerWidth <= 979) {
				$ul.hide('fade', 'fast', onComplete);
			} else {
				$ul.slideUp('fast', onComplete);
			}
		} else {
			$topLevelMenus.siblings('ul:visible').slideUp('fast', function() {
				dropdownClose.call(this);
			});
			dropdownOpen.call(this);
			if (window.innerWidth <= 979) {
				$ul.css({'position': 'absolute', 'margin-left': sidebarWidth + 1 + 'px', 'margin-top': '-42px'});
				$ul.show('fade', 'fast');
			} else {
				$ul.css({'margin-left': 0, 'position': 'relative'});
				$ul.slideDown('fast');
			}
		}
		e.stopPropagation();
		return false;
	});

	$(window).on('resize', function() {
		$('#sidebar-menu > li ul:visible').each(function() {
			$(this).toggle();
			dropdownClose.call(this);
		});
	});
}

/**
 * Forms
 *
 * @return void
 */
Admin.form = function() {
	// Tooltips activation
	$('[rel=tooltip],*[data-title],input[title],textarea[title]').tooltip();
	$('a.tooltip').tipsy({gravity: 's', html: false}); // Legacy tooltip

	var ajaxToggle = function(e) {
		var $this = $(this);
		$this.addClass('loading').find('i').attr('class', 'icon-none');
		var url = $this.data('url');
		$.post(url, function(data) {
			$this.parent().html(data);
		});
	}

	// Row Actions
	$('body')
		.on('click', 'a[data-row-action]', Admin.processLink)
		.on('click', 'a.ajax-toggle', ajaxToggle)
	;
}

/**
 * Helper to process row action links
 */
Admin.processLink = function(event) {
	var $el = $(event.currentTarget);
	var checkbox = $(event.currentTarget.attributes["href"].value);
	var form = checkbox.get(0).form;
	var action = $el.data('row-action');
	var confirmMessage = $el.data('confirm-message');
	if (confirmMessage && !confirm(confirmMessage)) {
		return false;
	}
	$('input[type=checkbox]', form).prop('checked', false);
	checkbox.prop("checked", true);
	$('#bulk-action select', form).val(action);
	form.submit();
	return false;
}

/**
 * Extra stuff
 *
 * rounded corners, striped table rows, etc
 *
 * @return void
 */
Admin.extra = function() {
	// Activates the first tab in #content
	$('#content .nav-tabs > li:first-child a').tab('show');

	// Box toggle
	$('body').on('click', '.box-title', function() {
		$(this).next().slideToggle();
	});

	$('.tabs').tabs(); // legacy tabs from jquery-ui
	$('textarea').not('.content').elastic();
	$("div.message").addClass("notice");
	$('#loading p').addClass('ui-corner-bl ui-corner-br');
}

/**
 * Document ready
 *
 * @return void
 */
$(document).ready(function() {
	Admin.navigation();
	Admin.form();
	Admin.extra();
});
<|endoftext|>


<commit_before>// Modules
var Type = require('type-of-is')

// Constants
var DRAFT = 'http://json-schema.org/draft-04/schema#'

function getPropertyFormat(value) {
    var type = Type.string(value).toLowerCase()

    if (type === 'date') return 'date-time'

    return null
}

function getPropertyType(value) {
    var type = Type.string(value).toLowerCase()

    if (type === 'date') return 'string'
    if (type === 'regexp') return 'string'
    if (type === 'function') return 'string'

    return type
}

function getUniqueKeys(a, b, c) {
    a = Object.keys(a)
    b = Object.keys(b)
    c = c || []

    var value
    var cIndex
    var aIndex

    for (var keyIndex = 0, keyLength = b.length; keyIndex < keyLength; keyIndex++) {
        value = b[keyIndex]
        aIndex = a.indexOf(value)
        cIndex = c.indexOf(value)

        if (aIndex === -1) {
            if (cIndex !== -1) {
                // Value is optional, it doesn't exist in A but exists in B(n)
                c.splice(cIndex, 1)
            }
        } else if (cIndex === -1) {
            // Value is required, it exists in both B and A, and is not yet present in C
            c.push(value)
        }
    }

    return c
}

function processArray(array, output, nested) {
    var format
    var oneOf
    var type

    if (nested && output) {
        output = { items: output }
    } else {
        output = output || {}
        output.type = getPropertyType(array)
        output.items = output.items || {}
        type = output.items.type || null
    }

    // Determine whether each item is different
    for (var arrIndex = 0, arrLength = array.length; arrIndex < arrLength; arrIndex++) {
        var elementType = getPropertyType(array[arrIndex])
        var elementFormat = getPropertyFormat(array[arrIndex])

        if (type && elementType !== type) {
            output.items.oneOf = []
            oneOf = true
            break
        } else {
            type = elementType
            format = elementFormat
        }
    }

    // Setup type otherwise
    if (!oneOf && type) {
        output.items.type = type
        if (format) {
            output.items.format = format
        }
    } else if (oneOf) {
        output.items = {
            oneOf: [{ type: type }],
            required: output.items.required
        }
    }

    // Process each item depending
    if (typeof output.items.oneOf !== 'undefined' || type === 'object') {
        for (var itemIndex = 0, itemLength = array.length; itemIndex < itemLength; itemIndex++) {
            var value = array[itemIndex]
            var itemType = getPropertyType(value)
            var itemFormat = getPropertyFormat(value)
            var arrayItem
            if (itemType === 'object') {
                if (output.items.properties) {
                    output.items.required = getUniqueKeys(output.items.properties, value, output.items.required)
                }
                arrayItem = processObject(value, oneOf ? {} : output.items.properties, true)
            } else if (itemType === 'array') {
                arrayItem = processArray(value, oneOf ? {} : output.items.properties, true)
            } else {
                arrayItem = {}
                arrayItem.type = itemType
                if (itemFormat) {
                    arrayItem.format = itemFormat
                }
            }
            if (oneOf) {
                var childType = Type.string(value).toLowerCase()
                var tempObj = {}
                if (!arrayItem.type && childType === 'object') {
                    tempObj.properties = arrayItem
                    tempObj.type = 'object'
                    arrayItem = tempObj
                }
                output.items.oneOf.push(arrayItem)
            } else {
                if (output.items.type !== 'object') {
                    continue;
                }
                output.items.properties = arrayItem
            }
        }
    }
    return nested ? output.items : output
}

function processObject(object, output, nested) {
    if (nested && output) {
        output = { properties: output }
    } else {
        output = output || {}
        output.type = getPropertyType(object)
        output.properties = output.properties || {}
    }

    for (var key in object) {
        var value = object[key]
        var type = getPropertyType(value)
        var format = getPropertyFormat(value)

        type = type === 'undefined' ? 'null' : type

        if (type === 'object') {
            output.properties[key] = processObject(value, output.properties[key])
            continue
        }

        if (type === 'array') {
            output.properties[key] = processArray(value, output.properties[key])
            continue
        }

        output.properties[key] = {}
        output.properties[key].type = type

        if (format) {
            output.properties[key].format = format
        }
    }

    return nested ? output.properties : output
}

module.exports = function(title, object) {
    var processOutput
    var output = {
        $schema: DRAFT
    }

    // Determine title exists
    if (typeof title !== 'string') {
        object = title
        title = undefined
    } else {
        output.title = title
    }

    // Set initial object type
    output.type = Type.string(object).toLowerCase()

    // Process object
    if (output.type === 'object') {
        processOutput = processObject(object)
        output.type = processOutput.type
        output.properties = processOutput.properties
    }

    if (output.type === 'array') {
        processOutput = processArray(object)
        output.type = processOutput.type
        output.items = processOutput.items

        if (output.title) {
            output.items.title = output.title
            output.title += ' Set'
        }
    }

    // Output
    return output
}
<commit_msg>fix(json): Handle objects differently for #22<commit_after>// Modules
var Type = require('type-of-is')

// Constants
var DRAFT = 'http://json-schema.org/draft-04/schema#'

function getPropertyFormat(value) {
    var type = Type.string(value).toLowerCase()

    if (type === 'date') return 'date-time'

    return null
}

function getPropertyType(value) {
    var type = Type.string(value).toLowerCase()

    if (type === 'date') return 'string'
    if (type === 'regexp') return 'string'
    if (type === 'function') return 'string'

    return type
}

function getUniqueKeys(a, b, c) {
    a = Object.keys(a)
    b = Object.keys(b)
    c = c || []

    var value
    var cIndex
    var aIndex

    for (var keyIndex = 0, keyLength = b.length; keyIndex < keyLength; keyIndex++) {
        value = b[keyIndex]
        aIndex = a.indexOf(value)
        cIndex = c.indexOf(value)

        if (aIndex === -1) {
            if (cIndex !== -1) {
                // Value is optional, it doesn't exist in A but exists in B(n)
                c.splice(cIndex, 1)
            }
        } else if (cIndex === -1) {
            // Value is required, it exists in both B and A, and is not yet present in C
            c.push(value)
        }
    }

    return c
}

function processArray(array, output, nested) {
    var format
    var oneOf
    var type

    if (nested && output) {
        output = { items: output }
    } else {
        output = output || {}
        output.type = getPropertyType(array)
        output.items = output.items || {}
        type = output.items.type || null
    }

    // Determine whether each item is different
    for (var arrIndex = 0, arrLength = array.length; arrIndex < arrLength; arrIndex++) {
        var elementType = getPropertyType(array[arrIndex])
        var elementFormat = getPropertyFormat(array[arrIndex])

        if (type && elementType !== type) {
            output.items.oneOf = []
            oneOf = true
            break
        } else {
            type = elementType
            format = elementFormat
        }
    }

    // Setup type otherwise
    if (!oneOf && type) {
        output.items.type = type
        if (format) {
            output.items.format = format
        }
    } else if (oneOf && type !== 'object') {
        output.items = {
            oneOf: [{ type: type }],
            required: output.items.required
        }
    }

    // Process each item depending
    if (typeof output.items.oneOf !== 'undefined' || type === 'object') {
        for (var itemIndex = 0, itemLength = array.length; itemIndex < itemLength; itemIndex++) {
            var value = array[itemIndex]
            var itemType = getPropertyType(value)
            var itemFormat = getPropertyFormat(value)
            var arrayItem
            if (itemType === 'object') {
                if (output.items.properties) {
                    output.items.required = getUniqueKeys(output.items.properties, value, output.items.required)
                }
                arrayItem = processObject(value, oneOf ? {} : output.items.properties, true)
            } else if (itemType === 'array') {
                arrayItem = processArray(value, oneOf ? {} : output.items.properties, true)
            } else {
                arrayItem = {}
                arrayItem.type = itemType
                if (itemFormat) {
                    arrayItem.format = itemFormat
                }
            }
            if (oneOf) {
                var childType = Type.string(value).toLowerCase()
                var tempObj = {}
                if (!arrayItem.type && childType === 'object') {
                    tempObj.properties = arrayItem
                    tempObj.type = 'object'
                    arrayItem = tempObj
                }
                output.items.oneOf.push(arrayItem)
            } else {
                if (output.items.type !== 'object') {
                    continue;
                }
                output.items.properties = arrayItem
            }
        }
    }
    return nested ? output.items : output
}

function processObject(object, output, nested) {
    if (nested && output) {
        output = { properties: output }
    } else {
        output = output || {}
        output.type = getPropertyType(object)
        output.properties = output.properties || {}
    }

    for (var key in object) {
        var value = object[key]
        var type = getPropertyType(value)
        var format = getPropertyFormat(value)

        type = type === 'undefined' ? 'null' : type

        if (type === 'object') {
            output.properties[key] = processObject(value, output.properties[key])
            continue
        }

        if (type === 'array') {
            output.properties[key] = processArray(value, output.properties[key])
            continue
        }

        output.properties[key] = {}
        output.properties[key].type = type

        if (format) {
            output.properties[key].format = format
        }
    }

    return nested ? output.properties : output
}

module.exports = function(title, object) {
    var processOutput
    var output = {
        $schema: DRAFT
    }

    // Determine title exists
    if (typeof title !== 'string') {
        object = title
        title = undefined
    } else {
        output.title = title
    }

    // Set initial object type
    output.type = Type.string(object).toLowerCase()

    // Process object
    if (output.type === 'object') {
        processOutput = processObject(object)
        output.type = processOutput.type
        output.properties = processOutput.properties
    }

    if (output.type === 'array') {
        processOutput = processArray(object)
        output.type = processOutput.type
        output.items = processOutput.items

        if (output.title) {
            output.items.title = output.title
            output.title += ' Set'
        }
    }

    // Output
    return output
}
<|endoftext|>


<commit_before>/**
 * Created by Godfery on 2016/9/5 0005.
 */

require('colors');
const glob = require('glob');
const Path = require('path');
const fse = require('fs-extra');
const fs = require('fs');
const async = require('async');
const color = require('./util/color');

const rootPath = Path.join(__dirname, '../lib');
const baseColor = '#00bcd4';

/**
 * 导入资源
 * @param {Object} options
 * @param {String []} paths
 * @param {String} dist
 */
function importResources({ paths = [], dist = '', needDirPath = true } = {}, doneCallback) {
  try {
    if (dist.length === 0) {
      throw  'ERROR: [option.dist] is required';
    }
  } catch (e) {
    console.log(e.red);
    process.env.exit(0);
  }
  console.log('ImportResources ...');
  /**
   * 获取全部文件的路径
   * @param paths
   * @param callback
   */
  const getPathsAllFiles = (paths, callback) => {
    let allFiles = [];
    const absolutePaths = paths.map(path => Path.join(rootPath, path));
    async.eachLimit(absolutePaths, 1, (path, next) => {
      glob(path, {}, (err, files) => {
        if (err) {
          console.log(err);
        } else {
          allFiles = [...allFiles, ...files];
        }
        next();
      });
    }, () => callback && callback(allFiles));
  }

  /**
   * 复制文件
   * @param files
   * @param callback
   */
  const copyFiles = (files, callback) => {
    async.eachLimit(files, 1, (filePath, next) => {
      const outputPath = needDirPath ? Path.relative(rootPath, filePath) : Path.basename(filePath);
      console.log(`Copy ${outputPath}`);
      fse.copy(filePath, Path.join(dist, outputPath), function (err) {
        if (err) {
          console.error(err);
        }
        next();
      });
    }, () => {
      callback && callback();
      doneCallback && doneCallback();
    });
  }

  getPathsAllFiles(paths, file => copyFiles(file, () => {
    console.log('ImportResources ' + '[SUCCESS]'.green);
  }));

  return module.exports;
};

/**
 * 画板方法
 * @param {Object} options
 */
function palette({ baseColor: themeColor = baseColor, src = 'css/rsuite.min.css', dist } = {}, doneCallback) {

  try {
    if (!dist) {
      throw  `ERROR: [option.dist] is required`;
    }
  } catch (e) {
    console.log(e.red);
    return;
  }

  const originColors = color.calcColors(baseColor);
  const themeColors = color.calcColors(themeColor);
  const distPath = Path.dirname(dist);
  fse.ensureDir(distPath, (err) => {
    if (err) console.log(err);
    fs.readFile(Path.join(rootPath, src), 'utf-8', (err, data) => {
      originColors.forEach((color, index) => {
        data = data.replace(new RegExp(color, 'g'), themeColors[index]);
      });
      fs.writeFile(dist, data, (err) => {
        if (err) {
          console.log("Failed :" + err.red);
        } else {
          console.log(`Palette ${dist}` + '[SUCCESS]'.green);
        }
        doneCallback && doneCallback();
      });
    });
  });

  return module.exports;
}

module.exports = {
  importResources,
  palette
};
<commit_msg>update:Pallet script<commit_after>/**
 * Created by Godfery on 2016/9/5 0005.
 */

require('colors');
const glob = require('glob');
const Path = require('path');
const fse = require('fs-extra');
const fs = require('fs');
const async = require('async');
const color = require('./util/color');

const rootPath = Path.join(__dirname, '../lib');
const baseColor = '#00bcd4';

/**
 * 导入资源
 * @param {Object} options
 * @param {String []} paths
 * @param {String} dist
 */
function importResources({ paths = [], dist = '', needDirPath = true } = {}, doneCallback) {
  try {
    if (dist.length === 0) {
      throw  'ERROR: [option.dist] is required';
    }
  } catch (e) {
    console.log(e.red);
    process.env.exit(0);
  }
  console.log('ImportResources ...');
  /**
   * 获取全部文件的路径
   * @param paths
   * @param callback
   */
  const getPathsAllFiles = (paths, callback) => {
    let allFiles = [];
    const absolutePaths = paths.map(path => Path.join(rootPath, path));
    async.eachLimit(absolutePaths, 1, (path, next) => {
      glob(path, {}, (err, files) => {
        if (err) {
          console.log(err);
        } else {
          allFiles = [...allFiles, ...files];
        }
        next();
      });
    }, () => callback && callback(allFiles));
  }

  /**
   * 复制文件
   * @param files
   * @param callback
   */
  const copyFiles = (files, callback) => {
    async.eachLimit(files, 1, (filePath, next) => {
      const outputPath = needDirPath ? Path.relative(rootPath, filePath) : Path.basename(filePath);
      console.log(`Copy ${outputPath}`);
      fse.copy(filePath, Path.join(dist, outputPath), function (err) {
        if (err) {
          console.error(err);
        }
        next();
      });
    }, () => {
      callback && callback();
      doneCallback && doneCallback();
    });
  }

  getPathsAllFiles(paths, file => copyFiles(file, () => {
    console.log('ImportResources ' + '[SUCCESS]'.green);
  }));

  return module.exports;
};

/**
 * 画板方法
 * @param {Object} options
 */
function palette({ baseColor: themeColor = baseColor, src = 'css/rsuite.min.css', dist } = {}, doneCallback) {

  try {
    if (!dist) {
      throw  `ERROR: [option.dist] is required`;
    }
  } catch (e) {
    console.log(e.red);
    return;
  }

  const originColors = color.calcColors(baseColor);
  const themeColors = color.calcColors(themeColor);
  const distPath = Path.dirname(dist);

  const ensureDir = path => new Promise((resolve) => {
    fse.ensureDir(path, () => resolve());
  });

  const readData = path => new Promise(resolve => {
    fs.readFile(path, 'utf-8', (err, data) => {
      resolve(data || '');
    });
  });

  const writeData = (path, data) => new Promise((resolve, reject) => {
    fs.writeFile(path, data, (err) => {
      if (err) {
        reject(err);
      }
      resolve();
    });
  });

  const generateThemes = async function () {
    await ensureDir(distPath);
    let data = await readData(Path.join(rootPath, src));
    originColors.forEach((color, index) => {
      data = data.replace(new RegExp(color, 'g'), themeColors[index]);
    });
    try {
      await  writeData(dist, data);
      console.log(`Palette ${dist}` + '[SUCCESS]'.green);
      doneCallback && doneCallback();
    } catch (e) {
      console.log(`Palette ${dist}` + '[SUCCESS]'.green);
    }
  };

  generateThemes();

  // fse.ensureDir(distPath, (err) => {
  //   if (err) console.log(err);
  //   fs.readFile(Path.join(rootPath, src), 'utf-8', (err, data) => {
  //     originColors.forEach((color, index) => {
  //       data = data.replace(new RegExp(color, 'g'), themeColors[index]);
  //     });
  //     fs.writeFile(dist, data, (err) => {
  //       if (err) {
  //         console.log("Failed :" + err.red);
  //       } else {
  //         console.log(`Palette ${dist}` + '[SUCCESS]'.green);
  //       }
  //       doneCallback && doneCallback();
  //     });
  //   });
  // });

  return module.exports;
}

module.exports = {
  importResources,
  palette
};
<|endoftext|>


<commit_before>var _ = require('lodash');

import Utils from './core/Utils';
import Timer from './core/Timer';
import Orchestrator from './core/Orchestrator';

export default class Core {
  constructor(data, options = {}) {
    this.data = data;
    this.timer = new Timer(options);
    this.orchestrator = new Orchestrator(this.timer, this.data);
  }

  getItem(item_id) {
    // In case we passed the item object directly return it.
    if (item_id != null && typeof item_id == 'object') {
      return item_id;
    }

    return _.find(this.data, (item) => item.id == item_id);
  }

  getProperty(prop_name, item_id_or_obj) {
    // If we passed the item name get the object from it.
    item_id_or_obj = this.getItem(item_id_or_obj);

    // Return false if we have no item
    if (!item_id_or_obj) {
      return false;
    }

    return _.find(item_id_or_obj.properties, property => property.name == prop_name);
  }

  getValues(item_id_or_obj) {
    // If we passed the item name get the object from it.
    item_id_or_obj = this.getItem(item_id_or_obj)

    // Return false if we have no item
    if (!item_id_or_obj) {
      return undefined;
    }

    return item_id_or_obj.values;
  }

  getValue(prop_name, item_id_or_obj) {
    // If we passed the item name get the object from it.
    var values = this.getValues(item_id_or_obj);

    // Return false if we have no item
    if (!values) {
      return undefined;
    }

    if (values[prop_name] != null) {
      return values[prop_name];
    }
    else {
      return undefined;
    }
  }

  getKeyAt(property, time_in_seconds) {
    return _.find(property.keys, key => key.time == time_in_seconds);
  }

  setValue(property, new_val, time_in_seconds = false) {
    if (time_in_seconds === false) {
      time_in_seconds = this.timer.getCurrentTime() / 1000;
    }
    var key = this.getKeyAt(property, time_in_seconds);

    if (key) {
      // If we found a key, simply update the value.
      key.val = new_val;
    }
    else {
      // If no key, create it and add it to the array.
      key = {val: new_val, time: time_in_seconds};
      property.keys.push(key);
      // Also sort the keys.
      property.keys = Utils.sortKeys(property.keys);
    }
  }

  getTotalDuration() {
    return this.orchestrator.getTotalDuration();
  }
}

module.exports = Core;
<commit_msg>update core setValue method to only create a key if needed<commit_after>var _ = require('lodash');

import Utils from './core/Utils';
import Timer from './core/Timer';
import Orchestrator from './core/Orchestrator';

export default class Core {
  constructor(data, options = {}) {
    this.data = data;
    this.timer = new Timer(options);
    this.orchestrator = new Orchestrator(this.timer, this.data);
  }

  getItem(item_id) {
    // In case we passed the item object directly return it.
    if (item_id != null && typeof item_id == 'object') {
      return item_id;
    }

    return _.find(this.data, (item) => item.id == item_id);
  }

  getProperty(prop_name, item_id_or_obj) {
    // If we passed the item name get the object from it.
    item_id_or_obj = this.getItem(item_id_or_obj);

    // Return false if we have no item
    if (!item_id_or_obj) {
      return false;
    }

    return _.find(item_id_or_obj.properties, property => property.name == prop_name);
  }

  getValues(item_id_or_obj) {
    // If we passed the item name get the object from it.
    item_id_or_obj = this.getItem(item_id_or_obj)

    // Return false if we have no item
    if (!item_id_or_obj) {
      return undefined;
    }

    return item_id_or_obj.values;
  }

  getValue(prop_name, item_id_or_obj) {
    // If we passed the item name get the object from it.
    var values = this.getValues(item_id_or_obj);

    // Return false if we have no item
    if (!values) {
      return undefined;
    }

    if (values[prop_name] != null) {
      return values[prop_name];
    }
    else {
      return undefined;
    }
  }

  getKeyAt(property, time_in_seconds) {
    return _.find(property.keys, key => key.time == time_in_seconds);
  }

  setValue(property, new_val, time_in_seconds = false) {
    if (time_in_seconds === false) {
      time_in_seconds = this.timer.getCurrentTime() / 1000;
    }
    var key = this.getKeyAt(property, time_in_seconds);

    if (key) {
      // If we found a key, simply update the value.
      key.val = new_val;
    }
    else {
      if (property.keys.length === 0) {
        // If the property doesn't have any key simply the the value.
        property.val = new_val;
      }
      else {
        // If we are not on a key but the property has other keys,
        // create it and add it to the keys array.
        key = {val: new_val, time: time_in_seconds, _property: property};
        property.keys.push(key);
        // Also sort the keys.
        property.keys = Utils.sortKeys(property.keys);
      }
    }
  }

  getTotalDuration() {
    return this.orchestrator.getTotalDuration();
  }
}

module.exports = Core;
<|endoftext|>


<commit_before>(function(tiy, $) {
    'use strict';

    // Public API methods
    tiy.loadModule = loadModule;
    tiy.showMessage = showMessage;

    // locals & constants
    let tiyoData = null;
    let navUI = null;
    let mainUI = null;
    let notifyUI = null;
    let modulesLoaded = [];
    let moduleDisplayed = null;
    let dataLoaded = false;
    let uiLoaded = false;

    const MAIN_TEMPLATE = 'templates/main.html';
    const NAV_TEMPLATE = 'templates/nav.html';
    const NOTIFICATIONS_TEMPLATE = 'templates/notifications.html';
    const PANEL_ANIMATION_TIME = 200;

    function main() {
        let spaceUsed = Object.keys(localStorage).reduce(function(total, k) {
            return ((typeof(total) === 'number' && total) || 0) + ((localStorage[k].length * 2)/1024/1024);
        });
        console.info('Initializing TIYO assistant... total localStorage used: ~%sMB', spaceUsed.toFixed(2));

        addFontAwesomeStyleSheet();
        setupNotificationsUI();

        collectData()
        .then(function getNavUI(data) {
            tiyoData = data;
            dataLoaded = true;
            console.log('Data gathered', data);
            return $.get(chrome.extension.getURL(NAV_TEMPLATE));
        })
        .then(function getMainUI(html) {
            navUI = $(html);
            $('.main .header:first').append(navUI);

            modulesLoaded.forEach(addNavIcon);
            navUI.on('click', 'li', toggleModuleUI);

            return $.get(chrome.extension.getURL(MAIN_TEMPLATE));
        })
        .then(function callModuleRenders(html) {
            mainUI = $(html);
            $('.main .content').prepend(mainUI);
            uiLoaded = true;

            setupContentClose();

            console.log('UI base loaded, calling render methods', mainUI);
            modulesLoaded.forEach(function(mod) {
                doRender(mod);
            });
        });
    }

    /**
    * Chrome extensions have issues with access files from css like fonts, so
    * we need to embed the styelsheet using the chromse extension URL, which
    * is dynamically generated. The remainder of the FA styles are in a static
    * stylesheet in /vendor
    */
    function addFontAwesomeStyleSheet() {
        $('head').append(
            $('<style>')
            .attr('type', 'text/css')
            .text(
                `@font-face {
                    font-family: FontAwesome;
                    src: url('${chrome.extension.getURL('vendor/fontawesome-webfont.woff')}');
                }`
            )
        );
    }

    function getUser() {
        try {
            return  JSON.parse(
                $( "#IntercomSettingsScriptTag" )
                .text().split(";")[0]
                .replace("window.intercomSettings =", "")
                .split(";")[0]
                .replace(";", "")
                .trim()
            );
        } catch (e) {
            return;
        }
    }

    function collectData() {
        let pathname = window.location.pathname.split(/\//),
            data = {
                user: null,
                path: null,
                content: null,
                group: null,
                students: []
            },
            group = $('.card-block dt:contains("Group")').next().find('a');

        data.user = getUser();

        // Admin view of path
        if (/\/admin\/paths\/[0-9]+/.test(window.location.pathname)) {
            data.path = {
                id: Number(pathname[3]),
                title: $('.content .breadcrumb li:eq(1)').text(),
                onPage: true
            };
        }

        // Admin view of content piece
        if (/\/admin\/(lessons|assignmnents)\/[0-9]+/.test(window.location.pathname)) {
            data.content = {
                id: Number(pathname[3]),
                title: $('.content .breadcrumb li:eq(3)').text(),
                type: pathname[2],
                isAdmin: true
            };
        }

        // Student view of content piece
        if (/\/paths\/[0-9]+\/units\/[0-9]+\/[^\/]+\/[0-9]+/.test(window.location.pathname)) {
            data.path = {
                id: Number(pathname[2]),
                title: $('.m-pathheader-info-title').text(),
                onPage: false
            };
            data.content = {
                id: Number(pathname[6]),
                title: $('.m-lessonheader-title').text(),
                type: pathname[5],
                isAdmin: false
            };
        }

        let p;
        if (group.length) {
            data.group = {
                title: group.text(),
                id: Number(group.attr('href').match(/\/([0-9]+)/)[1])
            };

            p = $.get(group.attr('href')).then(function(html) {
                let students = $(html).find('#students tr td:first-child a');
                students.each(function() {
                    let studentElem = $(this).find('.profile-placeholder-medium, img').remove().end();
                    data.students.push({
                        id: Number(studentElem.attr('href').match(/\/([0-9]+)/)[1]),
                        name: studentElem.text()
                    });
                });
                return data;
            });
        } else {
            p = Promise.resolve(data);
        }

        return p;
    }

    function addNavIcon(mod) {
        if (!navUI) { return; }

        navUI.find('ul').append(
            `<li data-module='${mod.name}' title='${mod.name}' aria-title='${mod.name}'><i class='fa ${mod.navIcon} fa-lg' aria-hidden='true'></i></li>`
        );
    }

    function toggleModuleUI(e) {
        e.preventDefault();
        var name = $(this).data('module');
        var mod = modulesLoaded.filter(function(m) { return m.name === name;});
        if (!mod.length) { return; }

        if (moduleDisplayed === mod[0].name) {
            console.log('hiding ', mod[0]);
            closeContent(e);
        } else {
            console.log('showing ', mod[0]);
            moduleDisplayed = mod[0].name;
            $('.tiyo-assistant-module').hide();
            $(`[data-module="${mod[0].name}"]`).show().trigger('showing');
            mainUI.slideDown(PANEL_ANIMATION_TIME);
        }
    }

    function closeContent(e) {
        e.preventDefault();
        $(`[data-module="${moduleDisplayed}"]`).show().trigger('hiding');
        moduleDisplayed = null;
        mainUI.slideUp(PANEL_ANIMATION_TIME);
    }

    function setupContentClose() {
        $('.tiyo-assistant-close').click(closeContent);
    }

    function loadModule(api) {
        if (!api) { return; }
        if (!api.name) { api.name = Date.getTime(); }

        console.info('loading module', api);

        modulesLoaded.push(api);

        addNavIcon(api);
        doRender(api);
    }

    function doRender(mod) {
        if (uiLoaded && typeof(mod.render) === 'function') {
            var elem = createModuleWrapper(mod);
            mod.render(tiyoData, elem);
        }
    }

    function createModuleWrapper(mod) {
        var elem = $(`<article data-module='${mod.name}'>`).addClass('tiyo-assistant-module');
        $(mainUI).find('.tiyo-assistant-content').append(elem);
        return elem;
    }

    function setupNotificationsUI() {
        $.get(chrome.extension.getURL(NOTIFICATIONS_TEMPLATE)).then(function(html) {
            notifyUI = $(html).appendTo('body');
            notifyUI.on('click', '.close', function() {
                $(this).parent().fadeOut(function() { $(this).remove(); });
            });
        });
    }

    function showMessage(msg, options = {}) {
        options.type = options.type || 'danger';
        options.duration = (Number(options.duration) || options.duration === 0) ? options.duration : 4;

        let dismissable = (options.canDismiss === false) ? '' : 'alert-dismissible';

        let msgNode = $(`<p class='alert alert-${options.type} ${dismissable}'>`).text(msg);

        if (dismissable.length) {
            msgNode.append(`<button class='close'>×</button>`);
        }

        notifyUI.append(msgNode);

        if (options.duration && options.duration > 0) {
            setTimeout(function() {
                msgNode.fadeOut(function() { $(this).remove(); });
            }, (options.duration * 1000));
        }
    }

    // Kick things off...
    main();
    // Then export our module API
    window.tiy = tiy;

})(window.tiy || {}, window.jQuery);
<commit_msg>Remove duplicate split<commit_after>(function(tiy, $) {
    'use strict';

    // Public API methods
    tiy.loadModule = loadModule;
    tiy.showMessage = showMessage;

    // locals & constants
    let tiyoData = null;
    let navUI = null;
    let mainUI = null;
    let notifyUI = null;
    let modulesLoaded = [];
    let moduleDisplayed = null;
    let dataLoaded = false;
    let uiLoaded = false;

    const MAIN_TEMPLATE = 'templates/main.html';
    const NAV_TEMPLATE = 'templates/nav.html';
    const NOTIFICATIONS_TEMPLATE = 'templates/notifications.html';
    const PANEL_ANIMATION_TIME = 200;

    function main() {
        let spaceUsed = Object.keys(localStorage).reduce(function(total, k) {
            return ((typeof(total) === 'number' && total) || 0) + ((localStorage[k].length * 2)/1024/1024);
        });
        console.info('Initializing TIYO assistant... total localStorage used: ~%sMB', spaceUsed.toFixed(2));

        addFontAwesomeStyleSheet();
        setupNotificationsUI();

        collectData()
        .then(function getNavUI(data) {
            tiyoData = data;
            dataLoaded = true;
            console.log('Data gathered', data);
            return $.get(chrome.extension.getURL(NAV_TEMPLATE));
        })
        .then(function getMainUI(html) {
            navUI = $(html);
            $('.main .header:first').append(navUI);

            modulesLoaded.forEach(addNavIcon);
            navUI.on('click', 'li', toggleModuleUI);

            return $.get(chrome.extension.getURL(MAIN_TEMPLATE));
        })
        .then(function callModuleRenders(html) {
            mainUI = $(html);
            $('.main .content').prepend(mainUI);
            uiLoaded = true;

            setupContentClose();

            console.log('UI base loaded, calling render methods', mainUI);
            modulesLoaded.forEach(function(mod) {
                doRender(mod);
            });
        });
    }

    /**
    * Chrome extensions have issues with access files from css like fonts, so
    * we need to embed the styelsheet using the chromse extension URL, which
    * is dynamically generated. The remainder of the FA styles are in a static
    * stylesheet in /vendor
    */
    function addFontAwesomeStyleSheet() {
        $('head').append(
            $('<style>')
            .attr('type', 'text/css')
            .text(
                `@font-face {
                    font-family: FontAwesome;
                    src: url('${chrome.extension.getURL('vendor/fontawesome-webfont.woff')}');
                }`
            )
        );
    }

    function getUser() {
        try {
            return  JSON.parse(
                $( "#IntercomSettingsScriptTag" )
                .text()
                .split(";")[0]
                .replace("window.intercomSettings =", "")
                .replace(";", "")
                .trim()
            );
        } catch (e) {
            return;
        }
    }

    function collectData() {
        let pathname = window.location.pathname.split(/\//),
            data = {
                user: null,
                path: null,
                content: null,
                group: null,
                students: []
            },
            group = $('.card-block dt:contains("Group")').next().find('a');

        data.user = getUser();

        // Admin view of path
        if (/\/admin\/paths\/[0-9]+/.test(window.location.pathname)) {
            data.path = {
                id: Number(pathname[3]),
                title: $('.content .breadcrumb li:eq(1)').text(),
                onPage: true
            };
        }

        // Admin view of content piece
        if (/\/admin\/(lessons|assignmnents)\/[0-9]+/.test(window.location.pathname)) {
            data.content = {
                id: Number(pathname[3]),
                title: $('.content .breadcrumb li:eq(3)').text(),
                type: pathname[2],
                isAdmin: true
            };
        }

        // Student view of content piece
        if (/\/paths\/[0-9]+\/units\/[0-9]+\/[^\/]+\/[0-9]+/.test(window.location.pathname)) {
            data.path = {
                id: Number(pathname[2]),
                title: $('.m-pathheader-info-title').text(),
                onPage: false
            };
            data.content = {
                id: Number(pathname[6]),
                title: $('.m-lessonheader-title').text(),
                type: pathname[5],
                isAdmin: false
            };
        }

        let p;
        if (group.length) {
            data.group = {
                title: group.text(),
                id: Number(group.attr('href').match(/\/([0-9]+)/)[1])
            };

            p = $.get(group.attr('href')).then(function(html) {
                let students = $(html).find('#students tr td:first-child a');
                students.each(function() {
                    let studentElem = $(this).find('.profile-placeholder-medium, img').remove().end();
                    data.students.push({
                        id: Number(studentElem.attr('href').match(/\/([0-9]+)/)[1]),
                        name: studentElem.text()
                    });
                });
                return data;
            });
        } else {
            p = Promise.resolve(data);
        }

        return p;
    }

    function addNavIcon(mod) {
        if (!navUI) { return; }

        navUI.find('ul').append(
            `<li data-module='${mod.name}' title='${mod.name}' aria-title='${mod.name}'><i class='fa ${mod.navIcon} fa-lg' aria-hidden='true'></i></li>`
        );
    }

    function toggleModuleUI(e) {
        e.preventDefault();
        var name = $(this).data('module');
        var mod = modulesLoaded.filter(function(m) { return m.name === name;});
        if (!mod.length) { return; }

        if (moduleDisplayed === mod[0].name) {
            console.log('hiding ', mod[0]);
            closeContent(e);
        } else {
            console.log('showing ', mod[0]);
            moduleDisplayed = mod[0].name;
            $('.tiyo-assistant-module').hide();
            $(`[data-module="${mod[0].name}"]`).show().trigger('showing');
            mainUI.slideDown(PANEL_ANIMATION_TIME);
        }
    }

    function closeContent(e) {
        e.preventDefault();
        $(`[data-module="${moduleDisplayed}"]`).show().trigger('hiding');
        moduleDisplayed = null;
        mainUI.slideUp(PANEL_ANIMATION_TIME);
    }

    function setupContentClose() {
        $('.tiyo-assistant-close').click(closeContent);
    }

    function loadModule(api) {
        if (!api) { return; }
        if (!api.name) { api.name = Date.getTime(); }

        console.info('loading module', api);

        modulesLoaded.push(api);

        addNavIcon(api);
        doRender(api);
    }

    function doRender(mod) {
        if (uiLoaded && typeof(mod.render) === 'function') {
            var elem = createModuleWrapper(mod);
            mod.render(tiyoData, elem);
        }
    }

    function createModuleWrapper(mod) {
        var elem = $(`<article data-module='${mod.name}'>`).addClass('tiyo-assistant-module');
        $(mainUI).find('.tiyo-assistant-content').append(elem);
        return elem;
    }

    function setupNotificationsUI() {
        $.get(chrome.extension.getURL(NOTIFICATIONS_TEMPLATE)).then(function(html) {
            notifyUI = $(html).appendTo('body');
            notifyUI.on('click', '.close', function() {
                $(this).parent().fadeOut(function() { $(this).remove(); });
            });
        });
    }

    function showMessage(msg, options = {}) {
        options.type = options.type || 'danger';
        options.duration = (Number(options.duration) || options.duration === 0) ? options.duration : 4;

        let dismissable = (options.canDismiss === false) ? '' : 'alert-dismissible';

        let msgNode = $(`<p class='alert alert-${options.type} ${dismissable}'>`).text(msg);

        if (dismissable.length) {
            msgNode.append(`<button class='close'>×</button>`);
        }

        notifyUI.append(msgNode);

        if (options.duration && options.duration > 0) {
            setTimeout(function() {
                msgNode.fadeOut(function() { $(this).remove(); });
            }, (options.duration * 1000));
        }
    }

    // Kick things off...
    main();
    // Then export our module API
    window.tiy = tiy;

})(window.tiy || {}, window.jQuery);
<|endoftext|>


<commit_before>Reveal.initialize({

  controls: true,
  progress: false,
  history: true,
  center: true,

  transition: 'fade', // none/fade/slide/convex/concave/zoom

  // Optional reveal.js plugins

  dependencies: [
    { src: '/js/vendor/libs/classList.js', condition: function() { return !document.body.classList; } },
    { src: 'js/vendor/plugins/highlight/highlight.js', async: true, condition: function() { return !!document.querySelector( 'pre code' ); }, callback: function() { hljs.initHighlightingOnLoad(); } },
    { src: 'js/vendor/plugins/notes/notes.js', async: true },
    { src: 'js/vendor/plugins/chartist/chartist.js', async: true },
    { src: 'js/vendor/plugins/math/math.js', async: true },
    { src: 'js/vendor/plugins/print-pdf/print-pdf.js', async: true },
    { src: 'js/vendor/plugins/vivus/vivus.min.js', async: true },
    { src: 'js/vendor/plugins/zoom-js/zoom.js', async: true }
  ]
});

<commit_msg>Update scripts path in main.js<commit_after>Reveal.initialize({

  controls: true,
  progress: false,
  history: true,
  center: true,

  transition: 'fade', // none/fade/slide/convex/concave/zoom

  // Optional reveal.js plugins

  dependencies: [
    { src: 'vendor/reveal.js/lib/js/classList.js', condition: function() { return !document.body.classList; } },
    { src: 'vendor/reveal.js/plugin/highlight/highlight.js', async: true, condition: function() { return !!document.querySelector( 'pre code' ); }, callback: function() { hljs.initHighlightingOnLoad(); } },
    { src: 'vendor/reveal.js/plugin/notes/notes.js', async: true },
    { src: 'vendor/reveal.js/plugin/math/math.js', async: true },
    { src: 'vendor/reveal.js/plugin/print-pdf/print-pdf.js', async: true },
    { src: 'vendor/reveal.js/plugin/zoom-js/zoom.js', async: true },
    { src: 'vendor/chartist/dist/chartist.min.js', async: true },
    { src: 'vendor/chartist/dist/vivus.min.js', async: true }
  ]
});
<|endoftext|>