<commit_before>b3e.editor.ConnectionSystem = function(editor) {
  "use strict";

  var ctrl = false;
  var shift = false;
  var alt = false;
  var connections = [];
  var redConnections = [];
  var lastOutNode = null;

  this.update = function(delta) {
    var project = editor.project.get();
    if (!project) return;

    var tree = project.trees.getSelected();
    if (!tree) return;

    // Update cut circle position
    var point = tree.view.getLocalPoint();
    var x = tree._cutCircle.x = point.x;
    var y = tree._cutCircle.y = point.y;

    // Verify shift creation
    var _class = editor._game.canvas.className;
    if (key.shift) {
      if ( _class.indexOf('cursor') < 0) {
        editor.setCursor('connecting');
      }
    } else {
      if (_class.indexOf('cursor-connecting') >= 0 && !connections.length) {
        editor.setCursor('none');
      }
    }

    // Verify cut tool
    if (key.isPressed('c') && !key.shift && !key.ctrl && !key.alt) {
      if (_class.indexOf('cursor') < 0) {
        editor.setCursor('erasing');
        tree._cutCircle.visible = true;
      } else if (_class.indexOf('cursor-erasing') >= 0) {
        // Verify collision and remove connections
        var step = Math.PI/40;
        var radius = tree._cutCircle.radius;
        for (var theta=0; theta<Math.PI*2; theta+=step) {
          // Check for all points in a circle
          var _x = x + radius*Math.cos(theta);
          var _y = y + radius*Math.sin(theta);

          var _connections = tree.connections.getAll().slice();
          for (var i=0; i<_connections.length; i++) {
            var shape = _connections[i].display;
            if (shape.hitTest(_x, _y)) {
              tree.connections.remove(_connections[i]);
            }
          }
        }
      }
    } else {
      if (_class.indexOf('cursor-erasing') >= 0) {
        tree._cutCircle.visible = false;
        editor.setCursor('none');
      }
    }
  };

  this._reconnectLastOutNode = function(connection) {
    if (!lastOutNode) return;
    connection.outNode = lastOutNode;
    lastOutNode.inConnections.push(connection);
  };

  this.onMouseDown = function(e) {
    lastOutNode = null;

    // Accepts only left click
    if (e.nativeEvent.which !== 1) return;
    ctrl = e.nativeEvent.ctrlKey;
    shift = e.nativeEvent.shiftKey;
    alt = e.nativeEvent.altKey;

    // Get current project
    var project = editor.project.get();
    if (!project) return;

    // Get selected tree
    var tree = project.trees.getSelected();
    if (!tree) return;

    // Get node under the click
    var point = tree.view.getLocalPoint();
    var x = point.x;
    var y = point.y;
    var node = tree.nodes.getUnderPoint(x, y);
    var connection = tree.connections.getUnderPoint(x, y);

    // User clicked on the node
    if (node) {
      // User left click on OUT anchor
      if (node.display.hitOutAnchor(x, y) || shift) {
        editor.setCursor('connecting');

        var availableNodes = [node];
        if (shift && ctrl) {
          availableNodes = tree._selectedNodes.slice();
        }

        for (var j=0; j<availableNodes.length; j++) {
          var _n = availableNodes[j];
          var max = _n.maxOutConnections;
          if (max >= 0 && _n.outConnections.length >= max) {
            continue;
          }
          var c = tree.connections.add(_n, null);
          connections.push(c);
        }

      // User left click on IN anchor
      } else if (node.display.hitInAnchor(x, y)) {
        if (!node.inConnections.length) return;

        connections = node.inConnections;
        node.inConnections = [];
        lastOutNode = node;
        for (var i=0; i<connections.length; i++) {
          connections[i].outNode = null;
        }
      }
    }

    // User clicked on the connection
    else if (connection) {
      connections = [connection];
      lastOutNode = connection.outNode;

      connection.outNode = null;
      lastOutNode.inConnections.remove(connection);
    }
  };

  this.onMouseMove = function(e) {
    // Return if not draggin any connection
    if (!connections.length) return;

    // Get current project
    var project = editor.project.get();
    if (!project) return;

    // Get selected tree
    var tree = project.trees.getSelected();
    if (!tree) return;

    // Get current mouse position
    var point = tree.view.getLocalPoint();
    var x = point.x;
    var y = point.y;
    var i;

    // Redraw all dragging connections
    for (i=0; i<connections.length; i++) {
      connections[i].display.redraw(null, null, x, y);
    }

    // Remove all red connections
    if (redConnections.length) {
      for (i=0; i<redConnections.length; i++) {
        redConnections[i].display.markToRemove = false;
        redConnections[i].display.redraw();
      }
      redConnections = [];
    }

    // Verify if node has input limit and mark connections to be removed
    var node = tree.nodes.getUnderPoint(x, y);
    if (node) {
      var max = node.maxInConnections;
      var pool = node.inConnections.concat(connections);
      if (max >= 0 && node.inConnections.length >= max) {
        for (var k=0; k<(pool.length-max); k++) {
          var connection = pool[k];
          redConnections.push(connection);
          connection.display.markToRemove = true;
          connection.display.redraw();
        }
      }
    }

  };

  this.onMouseUp = function(e) {
    // Accepts only left click
    if (e.nativeEvent.which !== 1) return;

    // Return if there is no connection
    if (!connections.length) return;

    // Get current project
    var project = editor.project.get();
    if (!project) return;

    // Get selected tree
    var tree = project.trees.getSelected();
    if (!tree) return;

    // Get node under cursor, if any
    var point = tree.view.getLocalPoint();
    var x = point.x;
    var y = point.y;
    var node = tree.nodes.getUnderPoint(x, y);

    editor.setCursor('none');

    // Remove all red connections
    if (redConnections.length) {
      for (i=0; i<redConnections.length; i++) {
        redConnections[i].display.markToRemove = false;
        redConnections[i].display.redraw();
      }
      redConnections = [];
    }

    //
    project.history._beginBatch();

    for (var i=0; i<connections.length; i++) {
      var connection = connections[i];
      var inNode = connection.inNode;

      // Remove connection if there is no node
      if (!node) {
        // must return the original connection configuration in order to
        // register correctly on the history manager
        this._reconnectLastOutNode(connection);
        tree.connections.remove(connection);
        continue;
      }

      // Remove connection if node is connecting itself
      if (node === inNode) {
        // must return the original connection configuration in order to
        // register correctly on the history manager
        this._reconnectLastOutNode(connection);
        tree.connections.remove(connection);
        continue;
      }

      // Remove connection if target node is an input
      if (node.category === 'input') {
        // must return the original connection configuration in order to
        // register correctly on the history manager
        this._reconnectLastOutNode(connection);
        tree.connections.remove(connection);
        continue;
      }

      // Remove connection if nodes are already connected
      var doubleConnection = false;
      for (var j=0; j<inNode.outConnections.length; j++) {
        var outNode = inNode.outConnections[j].outNode;
        if (outNode === node) {
          this._reconnectLastOutNode(connection);
          tree.connections.remove(connection);
          doubleConnection = true;
          break;
        }
      }
      if (doubleConnection) {
        continue;
      }

      // Remove connection if node can't have more inputs
      var max = node.maxInConnections;
      if (max >= 0 && node.inConnections.length >= max) {
        tree.connections.remove(node.inConnections[0]);
      }

      // Do the connection
      // but before return the original connection configuration in order to
      // register the correctly the action on the history manager
      this._reconnectLastOutNode(connection);
      tree.connections.remove(connection);
      tree.connections.add(inNode, node);

      connection.display.redraw();
    }

    project.history._endBatch();

    connections = [];
  };

  editor._game.stage.on('stagemousedown', this.onMouseDown, this);
  editor._game.stage.on('stagemousemove', this.onMouseMove, this);
  editor._game.stage.on('stagemouseup', this.onMouseUp, this);
};
<commit_msg>Fixing a bug where nodes can be connected to INPUT nodes<commit_after>b3e.editor.ConnectionSystem = function(editor) {
  "use strict";

  var ctrl = false;
  var shift = false;
  var alt = false;
  var connections = [];
  var redConnections = [];
  var lastOutNode = null;

  this.update = function(delta) {
    var project = editor.project.get();
    if (!project) return;

    var tree = project.trees.getSelected();
    if (!tree) return;

    // Update cut circle position
    var point = tree.view.getLocalPoint();
    var x = tree._cutCircle.x = point.x;
    var y = tree._cutCircle.y = point.y;

    // Verify shift creation
    var _class = editor._game.canvas.className;
    if (key.shift) {
      if ( _class.indexOf('cursor') < 0) {
        editor.setCursor('connecting');
      }
    } else {
      if (_class.indexOf('cursor-connecting') >= 0 && !connections.length) {
        editor.setCursor('none');
      }
    }

    // Verify cut tool
    if (key.isPressed('c') && !key.shift && !key.ctrl && !key.alt) {
      if (_class.indexOf('cursor') < 0) {
        editor.setCursor('erasing');
        tree._cutCircle.visible = true;
      } else if (_class.indexOf('cursor-erasing') >= 0) {
        // Verify collision and remove connections
        var step = Math.PI/40;
        var radius = tree._cutCircle.radius;
        for (var theta=0; theta<Math.PI*2; theta+=step) {
          // Check for all points in a circle
          var _x = x + radius*Math.cos(theta);
          var _y = y + radius*Math.sin(theta);

          var _connections = tree.connections.getAll().slice();
          for (var i=0; i<_connections.length; i++) {
            var shape = _connections[i].display;
            if (shape.hitTest(_x, _y)) {
              tree.connections.remove(_connections[i]);
            }
          }
        }
      }
    } else {
      if (_class.indexOf('cursor-erasing') >= 0) {
        tree._cutCircle.visible = false;
        editor.setCursor('none');
      }
    }
  };

  this._reconnectLastOutNode = function(connection) {
    if (!lastOutNode) return;
    connection.outNode = lastOutNode;
    lastOutNode.inConnections.push(connection);
  };

  this.onMouseDown = function(e) {
    lastOutNode = null;

    // Accepts only left click
    if (e.nativeEvent.which !== 1) return;
    ctrl = e.nativeEvent.ctrlKey;
    shift = e.nativeEvent.shiftKey;
    alt = e.nativeEvent.altKey;

    // Get current project
    var project = editor.project.get();
    if (!project) return;

    // Get selected tree
    var tree = project.trees.getSelected();
    if (!tree) return;

    // Get node under the click
    var point = tree.view.getLocalPoint();
    var x = point.x;
    var y = point.y;
    var node = tree.nodes.getUnderPoint(x, y);
    var connection = tree.connections.getUnderPoint(x, y);

    // User clicked on the node
    if (node) {
      // User left click on OUT anchor
      if (node.display.hitOutAnchor(x, y) || shift) {
        editor.setCursor('connecting');

        var availableNodes = [node];
        if (shift && ctrl) {
          availableNodes = tree._selectedNodes.slice();
        }

        for (var j=0; j<availableNodes.length; j++) {
          var _n = availableNodes[j];
          var max = _n.maxOutConnections;
          if (_n.category === b3e.OUTPUT) {
            continue;
          }

          if (max >= 0 && _n.outConnections.length >= max) {
            continue;
          }
          var c = tree.connections.add(_n, null);
          connections.push(c);
        }

      // User left click on IN anchor
      } else if (node.display.hitInAnchor(x, y)) {
        if (!node.inConnections.length) return;

        connections = node.inConnections;
        node.inConnections = [];
        lastOutNode = node;
        for (var i=0; i<connections.length; i++) {
          connections[i].outNode = null;
        }
      }
    }

    // User clicked on the connection
    else if (connection) {
      connections = [connection];
      lastOutNode = connection.outNode;

      connection.outNode = null;
      lastOutNode.inConnections.remove(connection);
    }
  };

  this.onMouseMove = function(e) {
    // Return if not draggin any connection
    if (!connections.length) return;

    // Get current project
    var project = editor.project.get();
    if (!project) return;

    // Get selected tree
    var tree = project.trees.getSelected();
    if (!tree) return;

    // Get current mouse position
    var point = tree.view.getLocalPoint();
    var x = point.x;
    var y = point.y;
    var i;

    // Redraw all dragging connections
    for (i=0; i<connections.length; i++) {
      connections[i].display.redraw(null, null, x, y);
    }

    // Remove all red connections
    if (redConnections.length) {
      for (i=0; i<redConnections.length; i++) {
        redConnections[i].display.markToRemove = false;
        redConnections[i].display.redraw();
      }
      redConnections = [];
    }

    // Verify if node has input limit and mark connections to be removed
    var node = tree.nodes.getUnderPoint(x, y);
    if (node) {
      var max = node.maxInConnections;
      var pool = node.inConnections.concat(connections);
      if (max >= 0 && node.inConnections.length >= max) {
        for (var k=0; k<(pool.length-max); k++) {
          var connection = pool[k];
          redConnections.push(connection);
          connection.display.markToRemove = true;
          connection.display.redraw();
        }
      }
    }

  };

  this.onMouseUp = function(e) {
    // Accepts only left click
    if (e.nativeEvent.which !== 1) return;

    // Return if there is no connection
    if (!connections.length) return;

    // Get current project
    var project = editor.project.get();
    if (!project) return;

    // Get selected tree
    var tree = project.trees.getSelected();
    if (!tree) return;

    // Get node under cursor, if any
    var point = tree.view.getLocalPoint();
    var x = point.x;
    var y = point.y;
    var node = tree.nodes.getUnderPoint(x, y);

    editor.setCursor('none');

    // Remove all red connections
    if (redConnections.length) {
      for (i=0; i<redConnections.length; i++) {
        redConnections[i].display.markToRemove = false;
        redConnections[i].display.redraw();
      }
      redConnections = [];
    }

    //
    project.history._beginBatch();

    for (var i=0; i<connections.length; i++) {
      var connection = connections[i];
      var inNode = connection.inNode;

      // Remove connection if there is no node
      if (!node) {
        // must return the original connection configuration in order to
        // register correctly on the history manager
        this._reconnectLastOutNode(connection);
        tree.connections.remove(connection);
        continue;
      }

      // Remove connection if node is connecting itself
      if (node === inNode) {
        // must return the original connection configuration in order to
        // register correctly on the history manager
        this._reconnectLastOutNode(connection);
        tree.connections.remove(connection);
        continue;
      }

      // Remove connection if target node is an input
      if (node.category === 'input') {
        // must return the original connection configuration in order to
        // register correctly on the history manager
        this._reconnectLastOutNode(connection);
        tree.connections.remove(connection);
        continue;
      }

      // Remove connection if nodes are already connected
      var doubleConnection = false;
      for (var j=0; j<inNode.outConnections.length; j++) {
        var outNode = inNode.outConnections[j].outNode;
        if (outNode === node) {
          this._reconnectLastOutNode(connection);
          tree.connections.remove(connection);
          doubleConnection = true;
          break;
        }
      }
      if (doubleConnection) {
        continue;
      }

      // Remove connection if node can't have more inputs
      var max = node.maxInConnections;
      if (max >= 0 && node.inConnections.length >= max) {
        tree.connections.remove(node.inConnections[0]);
      }

      // Do the connection
      // but before return the original connection configuration in order to
      // register the correctly the action on the history manager
      this._reconnectLastOutNode(connection);
      tree.connections.remove(connection);
      tree.connections.add(inNode, node);

      connection.display.redraw();
    }

    project.history._endBatch();

    connections = [];
  };

  editor._game.stage.on('stagemousedown', this.onMouseDown, this);
  editor._game.stage.on('stagemousemove', this.onMouseMove, this);
  editor._game.stage.on('stagemouseup', this.onMouseUp, this);
};
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, brackets, window, $, Mustache */

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

    // Brackets modules
    var ProjectManager          = brackets.getModule("project/ProjectManager"),
        SidebarView             = brackets.getModule("project/SidebarView"),
        PreferencesManager      = brackets.getModule("preferences/PreferencesManager"),
        Commands                = brackets.getModule("command/Commands"),
        CommandManager          = brackets.getModule("command/CommandManager"),
        KeyBindingManager       = brackets.getModule("command/KeyBindingManager"),
        Menus                   = brackets.getModule("command/Menus"),
        EditorManager           = brackets.getModule("editor/EditorManager"),
        ExtensionUtils          = brackets.getModule("utils/ExtensionUtils"),
        FileSystem              = brackets.getModule("filesystem/FileSystem"),
        AppInit                 = brackets.getModule("utils/AppInit"),
        KeyEvent                = brackets.getModule("utils/KeyEvent"),
        FileUtils               = brackets.getModule("file/FileUtils"),
        PopUpManager            = brackets.getModule("widgets/PopUpManager"),
        Strings                 = brackets.getModule("strings"),
        ProjectsMenuTemplate    = require("text!htmlContent/projects-menu.html");

    var KeyboardPrefs = JSON.parse(require("text!keyboard.json"));

    /** @const {string} Recent Projects commands ID */
    var TOGGLE_DROPDOWN = "recentProjects.toggle";

    /** @const {number} Maximum number of displayed recent projects */
    var MAX_PROJECTS = 20;

    /** @type {$.Element} jQuery elements used for the dropdown menu */
    var $dropdownItem,
        $dropdownToggle,
        $dropdown,
        $links;

    /**
     * Get the stored list of recent projects, fixing up paths as appropriate.
     * Warning: unlike most paths in Brackets, these lack a trailing "/"
     */
    function getRecentProjects() {
        var recentProjects = PreferencesManager.getViewState("recentProjects") || [],
            i;

        for (i = 0; i < recentProjects.length; i++) {
            // We have to canonicalize & then de-canonicalize the path here, since our pref format uses no trailing "/"
            recentProjects[i] = FileUtils.stripTrailingSlash(ProjectManager.updateWelcomeProjectPath(recentProjects[i] + "/"));
        }
        return recentProjects;
    }

    /**
     * Add a project to the stored list of recent projects, up to MAX_PROJECTS.
     */
    function add() {
        var root = FileUtils.stripTrailingSlash(ProjectManager.getProjectRoot().fullPath),
            recentProjects = getRecentProjects(),
            index = recentProjects.indexOf(root);

        if (index !== -1) {
            recentProjects.splice(index, 1);
        }
        recentProjects.unshift(root);
        if (recentProjects.length > MAX_PROJECTS) {
            recentProjects = recentProjects.slice(0, MAX_PROJECTS);
        }
        PreferencesManager.setViewState("recentProjects", recentProjects);
    }

    /**
     * Check the list of items to see if any of them are hovered, and if so trigger a mouseenter.
     * Normally the mouseenter event handles this, but when a previous item is deleted and the next
     * item moves up to be underneath the mouse, we don't get a mouseenter event for that item.
     */
    function checkHovers(pageX, pageY) {
        $dropdown.children().each(function () {
            var offset = $(this).offset(),
                width  = $(this).outerWidth(),
                height = $(this).outerHeight();

            if (pageX >= offset.left && pageX <= offset.left + width &&
                    pageY >= offset.top && pageY <= offset.top + height) {
                $(".recent-folder-link", this).triggerHandler("mouseenter");
            }
        });
    }

    /**
     * Create the "delete" button that shows up when you hover over a project.
     */
    function renderDelete() {
        return $("

")
            .mouseup(function (e) {
                // Don't let the click bubble upward.
                e.stopPropagation();

                // Remove the project from the preferences.
                var recentProjects = getRecentProjects(),
                    index = recentProjects.indexOf($(this).parent().data("path")),
                    newProjects = [],
                    i;
                for (i = 0; i < recentProjects.length; i++) {
                    if (i !== index) {
                        newProjects.push(recentProjects[i]);
                    }
                }
                PreferencesManager.setViewState("recentProjects", newProjects);
                $(this).closest("li").remove();
                checkHovers(e.pageX, e.pageY);

                if (newProjects.length === 1) {
                    $dropdown.find(".divider").remove();
                }
            });
    }

    /**
     * Hide the delete button.
     */
    function removeDeleteButton() {
        $("#recent-folder-delete").remove();
    }

    /**
     * Show the delete button over a given target.
     */
    function addDeleteButton($target) {
        removeDeleteButton();
        renderDelete()
            .css("top", $target.position().top + 6)
            .appendTo($target);
    }

    /**
     * Selects the next or previous item in the list
     * @param {number} direction  +1 for next, -1 for prev
     */
    function selectNextItem(direction) {
        var $links   = $dropdown.find("a"),
            index    = $dropdownItem ? $links.index($dropdownItem) : (direction > 0 ? -1 : 0),
            $newItem = $links.eq((index + direction) % $links.length);

        if ($dropdownItem) {
            $dropdownItem.removeClass("selected");
        }
        $newItem.addClass("selected");

        $dropdownItem = $newItem;
        removeDeleteButton();
    }

    /**
     * Deletes the selected item and
     * move the focus to next item in list.
     *
     * @return {boolean} TRUE if project is removed
     */
    function removeSelectedItem(e) {
        var recentProjects = getRecentProjects(),
            $cacheItem = $dropdownItem,
            index = recentProjects.indexOf($cacheItem.data("path"));

        // When focus is not on project item
        if (index === -1) {
            return false;
        }

        // remove project
        recentProjects.splice(index, 1);
        PreferencesManager.setViewState("recentProjects", recentProjects);
        checkHovers(e.pageX, e.pageY);

        if (recentProjects.length === 1) {
            $dropdown.find(".divider").remove();
        }
        selectNextItem(+1);
        $cacheItem.closest("li").remove();
        return true;
    }

    /**
     * Handles the Key Down events
     * @param {KeyboardEvent} event
     * @return {boolean} True if the key was handled
     */
    function keydownHook(event) {
        var keyHandled = false;

        switch (event.keyCode) {
        case KeyEvent.DOM_VK_UP:
            selectNextItem(-1);
            keyHandled = true;
            break;
        case KeyEvent.DOM_VK_DOWN:
            selectNextItem(+1);
            keyHandled = true;
            break;
        case KeyEvent.DOM_VK_ENTER:
        case KeyEvent.DOM_VK_RETURN:
            if ($dropdownItem) {
                $dropdownItem.trigger("click");
            }
            keyHandled = true;
            break;
        case KeyEvent.DOM_VK_BACK_SPACE:
        case KeyEvent.DOM_VK_DELETE:
            if ($dropdownItem) {
                removeSelectedItem(event);
                keyHandled = true;
            }
            break;
        }

        if (keyHandled) {
            event.stopImmediatePropagation();
            event.preventDefault();
        }
        return keyHandled;
    }

    /**
     * Close the dropdown.
     */
    function closeDropdown() {
        // Since we passed "true" for autoRemove to addPopUp(), this will
        // automatically remove the dropdown from the DOM. Also, PopUpManager
        // will call cleanupDropdown().
        if ($dropdown) {
            PopUpManager.removePopUp($dropdown);
        }
    }

    /**
     * Remove the various event handlers that close the dropdown. This is called by the
     * PopUpManager when the dropdown is closed.
     */
    function cleanupDropdown() {
        $("html").off("click", closeDropdown);
        $("#project-files-container").off("scroll", closeDropdown);
        $(SidebarView).off("hide", closeDropdown);
        $("#titlebar .nav").off("click", closeDropdown);
        $dropdown = null;

        EditorManager.focusEditor();
        $(window).off("keydown", keydownHook);
    }

    /**
     * Adds the click and mouse enter/leave events to the dropdown
     */
    function _handleListEvents() {
        $dropdown
            .on("click", "a", function () {
                var $link = $(this),
                    id    = $link.attr("id"),
                    path  = $link.data("path");

                if (path) {
                    ProjectManager.openProject(path)
                        .fail(function () {
                            // Remove the project from the list only if it does not exist on disk
                            var recentProjects = getRecentProjects(),
                                index = recentProjects.indexOf(path);
                            if (index !== -1) {
                                FileSystem.resolve(path, function (err, item) {
                                    if (err) {
                                        recentProjects.splice(index, 1);
                                    }
                                });
                            }
                        });
                    closeDropdown();

                } else if (id === "open-folder-link") {
                    CommandManager.execute(Commands.FILE_OPEN_FOLDER);
                }

            })
            .on("mouseenter", "a", function () {
                if ($dropdownItem) {
                    $dropdownItem.removeClass("selected");
                }
                $dropdownItem = $(this).addClass("selected");

                if ($dropdownItem.hasClass("recent-folder-link")) {
                    // Note: we can't depend on the event here because this can be triggered
                    // manually from checkHovers().
                    addDeleteButton($(this));
                }
            })
            .on("mouseleave", "a", function () {
                var $link = $(this).removeClass("selected");

                if ($link.get(0) === $dropdownItem.get(0)) {
                    $dropdownItem = null;
                }
                if ($link.hasClass("recent-folder-link")) {
                    removeDeleteButton();
                }
            });
    }

    /**
     * Parses the path and returns an object with the full path, the folder name and the path without the folder.
     * @param {string} path The full path to the folder.
     * @return {{path: string, folder: string, rest: string}}
     */
    function parsePath(path) {
        var lastSlash = path.lastIndexOf("/"), folder, rest;
        if (lastSlash === path.length - 1) {
            lastSlash = path.slice(0, path.length - 1).lastIndexOf("/");
        }
        if (lastSlash >= 0) {
            rest = " - " + (lastSlash ? path.slice(0, lastSlash) : "/");
            folder = path.slice(lastSlash + 1);
        } else {
            rest = "/";
            folder = path;
        }

        return {path: path, folder: folder, rest: rest};
    }

    /**
     * Create the list of projects in the dropdown menu.
     * @return {string} The html content
     */
    function renderList() {
        var recentProjects = getRecentProjects(),
            currentProject = FileUtils.stripTrailingSlash(ProjectManager.getProjectRoot().fullPath),
            templateVars   = {
                projectList : [],
                Strings     : Strings
            };

        recentProjects.forEach(function (root) {
            if (root !== currentProject) {
                templateVars.projectList.push(parsePath(root));
            }
        });

        return Mustache.render(ProjectsMenuTemplate, templateVars);
    }

    /**
     * Show or hide the recent projects dropdown.
     *
     * @param {{pageX:number, pageY:number}} position - the absolute position where to open the dropdown
     */
    function showDropdown(position) {
        // If the dropdown is already visible, just return (so the root click handler on html
        // will close it).
        if ($dropdown) {
            return;
        }

        Menus.closeAll();

        $dropdown = $(renderList());
        var toggleOffset = $dropdownToggle.offset();
        $dropdown
            .css({
                left: position.pageX,
                top: position.pageY
            })
            .appendTo($("body"));

        PopUpManager.addPopUp($dropdown, cleanupDropdown, true);

        // TODO: should use capture, otherwise clicking on the menus doesn't close it. More fallout
        // from the fact that we can't use the Boostrap (1.4) dropdowns.
        $("html").on("click", closeDropdown);

        // Hide the menu if the user scrolls in the project tree. Otherwise the Lion scrollbar
        // overlaps it.
        // TODO: This duplicates logic that's already in ProjectManager (which calls Menus.close()).
        // We should fix this when the popup handling is centralized in PopupManager, as well
        // as making Esc close the dropdown. See issue #1381.
        $("#project-files-container").on("scroll", closeDropdown);

        // Hide the menu if the sidebar is hidden.
        // TODO: Is there some more general way we could handle this for dropdowns?
        $(SidebarView).on("hide", closeDropdown);

        // Hacky: if we detect a click in the menubar, close ourselves.
        // TODO: again, we should have centralized popup management.
        $("#titlebar .nav").on("click", closeDropdown);

        _handleListEvents();
        $(window).on("keydown", keydownHook);
    }

    /**
     * Show or hide the recent projects dropdown from the toogle command.
     */
    function handleKeyEvent() {
        if (!$dropdown) {
            if (!SidebarView.isVisible()) {
                SidebarView.show();
            }

            showDropdown();
            $dropdown.focus();
            $links = $dropdown.find("a");
            // By default, select the most recent project (which is at the top of the list underneath Open Folder),
            // but if there are none, select Open Folder instead.
            $dropdownItem = $links.eq($links.length > 1 ? 1 : 0);
            $dropdownItem.addClass("selected");

            // If focusing the dropdown caused a modal bar to close, we need to refocus the dropdown
            window.setTimeout(function () {
                $dropdown.focus();
            }, 0);
        }
    }

    PreferencesManager.convertPreferences(module, {"recentProjects": "user"}, true);

    // Register command handlers
    CommandManager.register(Strings.CMD_TOGGLE_RECENT_PROJECTS, TOGGLE_DROPDOWN, handleKeyEvent);
    KeyBindingManager.addBinding(TOGGLE_DROPDOWN, KeyboardPrefs.recentProjects);

    // Initialize extension
    AppInit.appReady(function () {
        ExtensionUtils.loadStyleSheet(module, "styles/styles.css");

        $(ProjectManager).on("projectOpen", add);
        $(ProjectManager).on("beforeProjectClose", add);
    });

    AppInit.htmlReady(function () {
        $("#project-title")
            .wrap("

")
            .after("");

        var cmenuAdapter = {
            open: showDropdown,
            close: closeDropdown,
            isOpen: function () {
                return !!$dropdown;
            }
        };
        Menus.ContextMenu.assignContextMenuToSelector("#project-dropdown-toggle", cmenuAdapter);
    });
});
<commit_msg>Removes the unused  element<commit_after>/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, brackets, window, $, Mustache */

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

    // Brackets modules
    var ProjectManager          = brackets.getModule("project/ProjectManager"),
        SidebarView             = brackets.getModule("project/SidebarView"),
        PreferencesManager      = brackets.getModule("preferences/PreferencesManager"),
        Commands                = brackets.getModule("command/Commands"),
        CommandManager          = brackets.getModule("command/CommandManager"),
        KeyBindingManager       = brackets.getModule("command/KeyBindingManager"),
        Menus                   = brackets.getModule("command/Menus"),
        EditorManager           = brackets.getModule("editor/EditorManager"),
        ExtensionUtils          = brackets.getModule("utils/ExtensionUtils"),
        FileSystem              = brackets.getModule("filesystem/FileSystem"),
        AppInit                 = brackets.getModule("utils/AppInit"),
        KeyEvent                = brackets.getModule("utils/KeyEvent"),
        FileUtils               = brackets.getModule("file/FileUtils"),
        PopUpManager            = brackets.getModule("widgets/PopUpManager"),
        Strings                 = brackets.getModule("strings"),
        ProjectsMenuTemplate    = require("text!htmlContent/projects-menu.html");

    var KeyboardPrefs = JSON.parse(require("text!keyboard.json"));

    /** @const {string} Recent Projects commands ID */
    var TOGGLE_DROPDOWN = "recentProjects.toggle";

    /** @const {number} Maximum number of displayed recent projects */
    var MAX_PROJECTS = 20;

    /** @type {$.Element} jQuery elements used for the dropdown menu */
    var $dropdownItem,
        $dropdown,
        $links;

    /**
     * Get the stored list of recent projects, fixing up paths as appropriate.
     * Warning: unlike most paths in Brackets, these lack a trailing "/"
     */
    function getRecentProjects() {
        var recentProjects = PreferencesManager.getViewState("recentProjects") || [],
            i;

        for (i = 0; i < recentProjects.length; i++) {
            // We have to canonicalize & then de-canonicalize the path here, since our pref format uses no trailing "/"
            recentProjects[i] = FileUtils.stripTrailingSlash(ProjectManager.updateWelcomeProjectPath(recentProjects[i] + "/"));
        }
        return recentProjects;
    }

    /**
     * Add a project to the stored list of recent projects, up to MAX_PROJECTS.
     */
    function add() {
        var root = FileUtils.stripTrailingSlash(ProjectManager.getProjectRoot().fullPath),
            recentProjects = getRecentProjects(),
            index = recentProjects.indexOf(root);

        if (index !== -1) {
            recentProjects.splice(index, 1);
        }
        recentProjects.unshift(root);
        if (recentProjects.length > MAX_PROJECTS) {
            recentProjects = recentProjects.slice(0, MAX_PROJECTS);
        }
        PreferencesManager.setViewState("recentProjects", recentProjects);
    }

    /**
     * Check the list of items to see if any of them are hovered, and if so trigger a mouseenter.
     * Normally the mouseenter event handles this, but when a previous item is deleted and the next
     * item moves up to be underneath the mouse, we don't get a mouseenter event for that item.
     */
    function checkHovers(pageX, pageY) {
        $dropdown.children().each(function () {
            var offset = $(this).offset(),
                width  = $(this).outerWidth(),
                height = $(this).outerHeight();

            if (pageX >= offset.left && pageX <= offset.left + width &&
                    pageY >= offset.top && pageY <= offset.top + height) {
                $(".recent-folder-link", this).triggerHandler("mouseenter");
            }
        });
    }

    /**
     * Create the "delete" button that shows up when you hover over a project.
     */
    function renderDelete() {
        return $("

")
            .mouseup(function (e) {
                // Don't let the click bubble upward.
                e.stopPropagation();

                // Remove the project from the preferences.
                var recentProjects = getRecentProjects(),
                    index = recentProjects.indexOf($(this).parent().data("path")),
                    newProjects = [],
                    i;
                for (i = 0; i < recentProjects.length; i++) {
                    if (i !== index) {
                        newProjects.push(recentProjects[i]);
                    }
                }
                PreferencesManager.setViewState("recentProjects", newProjects);
                $(this).closest("li").remove();
                checkHovers(e.pageX, e.pageY);

                if (newProjects.length === 1) {
                    $dropdown.find(".divider").remove();
                }
            });
    }

    /**
     * Hide the delete button.
     */
    function removeDeleteButton() {
        $("#recent-folder-delete").remove();
    }

    /**
     * Show the delete button over a given target.
     */
    function addDeleteButton($target) {
        removeDeleteButton();
        renderDelete()
            .css("top", $target.position().top + 6)
            .appendTo($target);
    }

    /**
     * Selects the next or previous item in the list
     * @param {number} direction  +1 for next, -1 for prev
     */
    function selectNextItem(direction) {
        var $links   = $dropdown.find("a"),
            index    = $dropdownItem ? $links.index($dropdownItem) : (direction > 0 ? -1 : 0),
            $newItem = $links.eq((index + direction) % $links.length);

        if ($dropdownItem) {
            $dropdownItem.removeClass("selected");
        }
        $newItem.addClass("selected");

        $dropdownItem = $newItem;
        removeDeleteButton();
    }

    /**
     * Deletes the selected item and
     * move the focus to next item in list.
     *
     * @return {boolean} TRUE if project is removed
     */
    function removeSelectedItem(e) {
        var recentProjects = getRecentProjects(),
            $cacheItem = $dropdownItem,
            index = recentProjects.indexOf($cacheItem.data("path"));

        // When focus is not on project item
        if (index === -1) {
            return false;
        }

        // remove project
        recentProjects.splice(index, 1);
        PreferencesManager.setViewState("recentProjects", recentProjects);
        checkHovers(e.pageX, e.pageY);

        if (recentProjects.length === 1) {
            $dropdown.find(".divider").remove();
        }
        selectNextItem(+1);
        $cacheItem.closest("li").remove();
        return true;
    }

    /**
     * Handles the Key Down events
     * @param {KeyboardEvent} event
     * @return {boolean} True if the key was handled
     */
    function keydownHook(event) {
        var keyHandled = false;

        switch (event.keyCode) {
        case KeyEvent.DOM_VK_UP:
            selectNextItem(-1);
            keyHandled = true;
            break;
        case KeyEvent.DOM_VK_DOWN:
            selectNextItem(+1);
            keyHandled = true;
            break;
        case KeyEvent.DOM_VK_ENTER:
        case KeyEvent.DOM_VK_RETURN:
            if ($dropdownItem) {
                $dropdownItem.trigger("click");
            }
            keyHandled = true;
            break;
        case KeyEvent.DOM_VK_BACK_SPACE:
        case KeyEvent.DOM_VK_DELETE:
            if ($dropdownItem) {
                removeSelectedItem(event);
                keyHandled = true;
            }
            break;
        }

        if (keyHandled) {
            event.stopImmediatePropagation();
            event.preventDefault();
        }
        return keyHandled;
    }

    /**
     * Close the dropdown.
     */
    function closeDropdown() {
        // Since we passed "true" for autoRemove to addPopUp(), this will
        // automatically remove the dropdown from the DOM. Also, PopUpManager
        // will call cleanupDropdown().
        if ($dropdown) {
            PopUpManager.removePopUp($dropdown);
        }
    }

    /**
     * Remove the various event handlers that close the dropdown. This is called by the
     * PopUpManager when the dropdown is closed.
     */
    function cleanupDropdown() {
        $("html").off("click", closeDropdown);
        $("#project-files-container").off("scroll", closeDropdown);
        $(SidebarView).off("hide", closeDropdown);
        $("#titlebar .nav").off("click", closeDropdown);
        $dropdown = null;

        EditorManager.focusEditor();
        $(window).off("keydown", keydownHook);
    }

    /**
     * Adds the click and mouse enter/leave events to the dropdown
     */
    function _handleListEvents() {
        $dropdown
            .on("click", "a", function () {
                var $link = $(this),
                    id    = $link.attr("id"),
                    path  = $link.data("path");

                if (path) {
                    ProjectManager.openProject(path)
                        .fail(function () {
                            // Remove the project from the list only if it does not exist on disk
                            var recentProjects = getRecentProjects(),
                                index = recentProjects.indexOf(path);
                            if (index !== -1) {
                                FileSystem.resolve(path, function (err, item) {
                                    if (err) {
                                        recentProjects.splice(index, 1);
                                    }
                                });
                            }
                        });
                    closeDropdown();

                } else if (id === "open-folder-link") {
                    CommandManager.execute(Commands.FILE_OPEN_FOLDER);
                }

            })
            .on("mouseenter", "a", function () {
                if ($dropdownItem) {
                    $dropdownItem.removeClass("selected");
                }
                $dropdownItem = $(this).addClass("selected");

                if ($dropdownItem.hasClass("recent-folder-link")) {
                    // Note: we can't depend on the event here because this can be triggered
                    // manually from checkHovers().
                    addDeleteButton($(this));
                }
            })
            .on("mouseleave", "a", function () {
                var $link = $(this).removeClass("selected");

                if ($link.get(0) === $dropdownItem.get(0)) {
                    $dropdownItem = null;
                }
                if ($link.hasClass("recent-folder-link")) {
                    removeDeleteButton();
                }
            });
    }

    /**
     * Parses the path and returns an object with the full path, the folder name and the path without the folder.
     * @param {string} path The full path to the folder.
     * @return {{path: string, folder: string, rest: string}}
     */
    function parsePath(path) {
        var lastSlash = path.lastIndexOf("/"), folder, rest;
        if (lastSlash === path.length - 1) {
            lastSlash = path.slice(0, path.length - 1).lastIndexOf("/");
        }
        if (lastSlash >= 0) {
            rest = " - " + (lastSlash ? path.slice(0, lastSlash) : "/");
            folder = path.slice(lastSlash + 1);
        } else {
            rest = "/";
            folder = path;
        }

        return {path: path, folder: folder, rest: rest};
    }

    /**
     * Create the list of projects in the dropdown menu.
     * @return {string} The html content
     */
    function renderList() {
        var recentProjects = getRecentProjects(),
            currentProject = FileUtils.stripTrailingSlash(ProjectManager.getProjectRoot().fullPath),
            templateVars   = {
                projectList : [],
                Strings     : Strings
            };

        recentProjects.forEach(function (root) {
            if (root !== currentProject) {
                templateVars.projectList.push(parsePath(root));
            }
        });

        return Mustache.render(ProjectsMenuTemplate, templateVars);
    }

    /**
     * Show or hide the recent projects dropdown.
     *
     * @param {{pageX:number, pageY:number}} position - the absolute position where to open the dropdown
     */
    function showDropdown(position) {
        // If the dropdown is already visible, just return (so the root click handler on html
        // will close it).
        if ($dropdown) {
            return;
        }

        Menus.closeAll();

        $dropdown = $(renderList())
            .css({
                left: position.pageX,
                top: position.pageY
            })
            .appendTo($("body"));

        PopUpManager.addPopUp($dropdown, cleanupDropdown, true);

        // TODO: should use capture, otherwise clicking on the menus doesn't close it. More fallout
        // from the fact that we can't use the Boostrap (1.4) dropdowns.
        $("html").on("click", closeDropdown);

        // Hide the menu if the user scrolls in the project tree. Otherwise the Lion scrollbar
        // overlaps it.
        // TODO: This duplicates logic that's already in ProjectManager (which calls Menus.close()).
        // We should fix this when the popup handling is centralized in PopupManager, as well
        // as making Esc close the dropdown. See issue #1381.
        $("#project-files-container").on("scroll", closeDropdown);

        // Hide the menu if the sidebar is hidden.
        // TODO: Is there some more general way we could handle this for dropdowns?
        $(SidebarView).on("hide", closeDropdown);

        // Hacky: if we detect a click in the menubar, close ourselves.
        // TODO: again, we should have centralized popup management.
        $("#titlebar .nav").on("click", closeDropdown);

        _handleListEvents();
        $(window).on("keydown", keydownHook);
    }

    /**
     * Show or hide the recent projects dropdown from the toogle command.
     */
    function handleKeyEvent() {
        if (!$dropdown) {
            if (!SidebarView.isVisible()) {
                SidebarView.show();
            }

            showDropdown();
            $dropdown.focus();
            $links = $dropdown.find("a");
            // By default, select the most recent project (which is at the top of the list underneath Open Folder),
            // but if there are none, select Open Folder instead.
            $dropdownItem = $links.eq($links.length > 1 ? 1 : 0);
            $dropdownItem.addClass("selected");

            // If focusing the dropdown caused a modal bar to close, we need to refocus the dropdown
            window.setTimeout(function () {
                $dropdown.focus();
            }, 0);
        }
    }

    PreferencesManager.convertPreferences(module, {"recentProjects": "user"}, true);

    // Register command handlers
    CommandManager.register(Strings.CMD_TOGGLE_RECENT_PROJECTS, TOGGLE_DROPDOWN, handleKeyEvent);
    KeyBindingManager.addBinding(TOGGLE_DROPDOWN, KeyboardPrefs.recentProjects);

    // Initialize extension
    AppInit.appReady(function () {
        ExtensionUtils.loadStyleSheet(module, "styles/styles.css");

        $(ProjectManager).on("projectOpen", add);
        $(ProjectManager).on("beforeProjectClose", add);
    });

    AppInit.htmlReady(function () {
        $("#project-title")
            .wrap("

")
            .after("");

        var cmenuAdapter = {
            open: showDropdown,
            close: closeDropdown,
            isOpen: function () {
                return !!$dropdown;
            }
        };
        Menus.ContextMenu.assignContextMenuToSelector("#project-dropdown-toggle", cmenuAdapter);
    });
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2020 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.fs.fileDropZone',
  name: 'FilePreview',
  extends: 'foam.u2.View',

  documentation: 'iframe for file preview',

  css: `
    .file-iframe {
      height: 100%;
      width: 100%;
    }

    img {
      max-width: 100%;
      max-height: 100%;
    }

    .file-image-div {
      height: 100%;
      width: auto;
      max-height: 244px;
      max-width: 300px;
    }
  `,

  properties: [
    {
      name: 'selected'
    }
  ],

  methods: [
    function initE() {
      this.SUPER();

      this
        .addClass(this.myClass())
        .start('div')
          .addClass('file-image-div')
          .style({
            visibility: 'hidden'
          })
          .start('img')
            .addClass('file-image')
          .end()
        .end()
        .start('iframe')
          .addClass('file-iframe')
          .style({
            visibility: 'hidden'
          })
        .end();
      this.data$.sub(() => this.showData());
    },

    function showData() {
      let iFrame = document.getElementsByClassName('file-iframe')[0],
          image = document.getElementsByClassName('file-image')[0],
          div = document.getElementsByClassName('file-image-div')[0],
          url = '',
          pos;

      iFrame.style.visibility = 'hidden';
      div.style.visibility = 'hidden';
      div.style.display = 'none';

      if ( this.selected == undefined || this.selected == this.data.length ) {
        pos = this.data.length - 1;
      } else {
        pos = this.selected;
      }

      if ( ! this.data[pos] ) {
        return;
      }
      url = URL.createObjectURL(this.data[pos].data.blob);

      if (this.data[pos].mimeType !== "application/pdf") {
        image.src = url;
        div.style.visibility = 'visible';
        div.style.display = 'block';
      } else {
        iFrame.src = url;
        iFrame.style.visibility = 'visible';
      }
    }
  ]
})
<commit_msg>fix for multiple preview windows<commit_after>/**
 * @license
 * Copyright 2020 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.fs.fileDropZone',
  name: 'FilePreview',
  extends: 'foam.u2.View',

  documentation: 'iframe for file preview',

  properties: [
    {
      name: 'selected'
    }
  ],

  methods: [
    function initE() {
      this.SUPER();

      this
        .addClass(this.myClass())
        .start('div')
          .addClass('file-image-div' + this.id)
          .style({
            'visibility': 'hidden',
            'height': '100%',
            'width:': 'auto',
            'max-height': '244px',
            'max-width': '300px'
          })
          .start('img')
            .addClass('file-image' + this.id)
            .style({
              'max-width': '100%',
              'max-height': '100%'
            })
          .end()
        .end()
        .start('iframe')
          .addClass('file-iframe' + this.id)
          .style({
            'visibility': 'hidden',
            'height': '100%',
            'width': '100%'
          })
        .end();
      this.data$.sub(() => this.showData());
    },

    function showData() {
    debugger;
      let iFrame = document.getElementsByClassName('file-iframe' + this.id)[0],
          image = document.getElementsByClassName('file-image' + this.id)[0],
          div = document.getElementsByClassName('file-image-div' + this.id)[0],
          url = '',
          pos;

      iFrame.style.visibility = 'hidden';
      div.style.visibility = 'hidden';
      div.style.display = 'none';

      if ( this.selected == undefined || this.selected == this.data.length ) {
        pos = this.data.length - 1;
      } else {
        pos = this.selected;
      }

      if ( ! this.data[pos] ) {
        return;
      }
      url = URL.createObjectURL(this.data[pos].data.blob);

      if (this.data[pos].mimeType !== "application/pdf") {
        image.src = url;
        div.style.visibility = 'visible';
        div.style.display = 'block';
      } else {
        iFrame.src = url;
        iFrame.style.visibility = 'visible';
      }
    }
  ]
})
<|endoftext|>


<commit_before>// LIBRARIES
var express = require('express');
var bodyParser = require('body-parser');
var util = require('util');
var errorHandler = require('errorhandler')
var fs = require('fs');
var routes = require('./routes');

// PARAMETERS
// var MANIFEST_FILE = 'public/ElectionManifest.json';
var MANIFEST_FILE = process.env.HOME + '/.eVotingSystem/Public/Manifest/ElectionManifest.json';

// INITIALIZATION (read the manifest file)

var manifset = null;
if (fs.existsSync(MANIFEST_FILE)) {
    manifest = JSON.parse(fs.readFileSync(MANIFEST_FILE));
}
else { // Initialization failed
    console.log('Cannot find an election manifest file.');
    console.log('Server not started.');
    process.exit(1);
}

var index = routes.index(manifest);

// CREATE THE APP

var app = express();

// CONFIGURATION

app.set('views', './views');
app.set('view engine', 'ejs');

app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('./public')); // was: __dirname + '/public'
// app.use(express.static(__dirname + './public'));

// Error handling (not for production)
app.use(errorHandler({ dumpExceptions: true, showStack: true }));

// ROUTES

app.get('', index);
app.get('/index.html', index);

// STARGING THE SERVER

var server = app.listen(3000, function() {
    console.log('Listening on %s, port %d', server.address().address, server.address().port);
});

<commit_msg>BB: add logging<commit_after>// LIBRARIES

var express = require('express');
var bodyParser = require('body-parser');
var util = require('util');
var errorHandler = require('errorhandler')
var fs = require('fs');
var morgan = require('morgan')

var routes = require('./routes');

// PARAMETERS

var MANIFEST_FILE = process.env.HOME + '/.eVotingSystem/Public/Manifest/ElectionManifest.json';

// INITIALIZATION (read the manifest file)

var manifset = null;
if (fs.existsSync(MANIFEST_FILE)) {
    manifest = JSON.parse(fs.readFileSync(MANIFEST_FILE));
}
else { // Initialization failed
    console.log('Cannot find an election manifest file.');
    console.log('Server not started.');
    process.exit(1);
}

var index = routes.index(manifest);

// CREATE THE APP

var app = express();

// CONFIGURATION

app.set('views', './views');
app.set('view engine', 'ejs');

app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('./public')); // was: __dirname + '/public'
// app.use(express.static(__dirname + './public'));

// Error handling (not for production)
app.use(errorHandler({ dumpExceptions: true, showStack: true }));

// Logging
app.use( morgan(':remote-addr [:date] :method :url :status / :referrer ', {}) );

// ROUTES

app.get('', index);
app.get('/index.html', index);

// STARGING THE SERVER

var server = app.listen(3000, function() {
    console.log('Listening on %s, port %d', server.address().address, server.address().port);
});

<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';

import { SELECTION_ALL_PHASES } from 'interface/report/PhaseParser';

import './PhaseSelector.scss';

class PhaseSelector extends React.PureComponent {
  static propTypes = {
    phases: PropTypes.object.isRequired,
    selectedPhase: PropTypes.string.isRequired,
    handlePhaseSelection: PropTypes.func.isRequired,
  };

  constructor(...args){
    super(...args);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.props.handlePhaseSelection(e.target.value || SELECTION_ALL_PHASES);
  }

  render() {
    const {phases, selectedPhase } = this.props;
    return (
      <select
        className="form-control phase"
        defaultValue={selectedPhase}
        onChange={this.handleChange}
      >
        <option key="all" value={SELECTION_ALL_PHASES}>All Phases</option>
        {Object.keys(phases).map(key =>
          <option key={key} value={key}>{phases[key].name}</option>
        )}
      </select>
    );
  }
}

export default PhaseSelector;
<commit_msg>add ref<commit_after>import React from 'react';
import PropTypes from 'prop-types';

import { SELECTION_ALL_PHASES } from 'interface/report/PhaseParser';

import './PhaseSelector.scss';

class PhaseSelector extends React.PureComponent {
  static propTypes = {
    phases: PropTypes.object.isRequired,
    selectedPhase: PropTypes.string.isRequired,
    handlePhaseSelection: PropTypes.func.isRequired,
  };

  constructor(...args){
    super(...args);
    this.phaseRef = React.createRef();
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.props.handlePhaseSelection(e.target.value || SELECTION_ALL_PHASES);
  }

  render() {
    const {phases, selectedPhase } = this.props;
    return (
      <select
        className="form-control phase"
        defaultValue={selectedPhase}
        onChange={this.handleChange}
        ref={this.phaseRef}
      >
        <option key="all" value={SELECTION_ALL_PHASES}>All Phases</option>
        {Object.keys(phases).map(key =>
          <option key={key} value={key}>{phases[key].name}</option>
        )}
      </select>
    );
  }
}

export default PhaseSelector;
<|endoftext|>


<commit_before>/*!
 * OS.js - JavaScript Operating System
 *
 * Copyright (c) 2011-2015, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */

(function(Utils, API, GUIElement, Window) {
  'use strict';

  var OSjs = window.OSjs = window.OSjs || {};

  OSjs.Helpers = OSjs.Helpers || {};

  var URL_FMT = 'https://marketplace.firefox.com/api/';

  function buildURL(path, args) {
    var str = [];
    Object.keys(args).forEach(function(i) {
      str.push(i + '=' + args[i]);
    });
    return URL_FMT + path + (str.length ? '?' : '') + str.join('&');
  }

  function apiCall(url, callback) {
    API.curl({
      body: {
        url: url,
        binary: false,
        mime: 'application/json',
        method: 'GET'
      }
    }, function(error, response) {
      if ( error ) {
        callback(error);
        return;
      }

      if ( !response.body ) {
        callback('Response was empty');
        return;
      }

      var data = JSON.parse(response.body);
      callback(false, data);
    });
  }

  // TODO: REMOVE WHEN #89 is done
  function IframeElement(name, src, cb) {
    this.frameCallback = cb || function() {};
    this.frameSource = src;
    this.frameWindow = null;
    this.frame = null;

    GUIElement.apply(this, [name, {
      isIframe: true
    }]);
  }

  IframeElement.prototype = Object.create(GUIElement.prototype);

  IframeElement.prototype.init = function() {
    var self = this;
    var el = GUIElement.prototype.init.apply(this, ['GUIFirefoxAppIframe']);
    this.frame = document.createElement('iframe');
    /*
    this.frame.style.position = 'absolute';
    this.frame.style.top = '0px';
    this.frame.style.right = '0px';
    this.frame.style.bottom = '0px';
    this.frame.style.left = '0px';
    */
    this.frame.style.width = '320px';
    this.frame.style.height = '480px';
    this.frame.style.border = '0 none';
    this.frame.frameborder = '0';
    this.frame.onload = function() {
      self.frameWindow = self.frame.contentWindow;
      self.frameCallback(self.frameWindow);
    };
    this.frame.src = this.frameSource;
    el.appendChild(this.frame);
    return el;
  };

  /////////////////////////////////////////////////////////////////////////////
  // FFAPP WINDOW
  /////////////////////////////////////////////////////////////////////////////

  var FirefoxAppWindow = function(id, title, icon, url) {
    Window.apply(this, ['FirefoxAppWindow' + id.toString(), {
      icon: icon,
      title: title,
      width: 320,
      height: 480,
      allow_resise: false,
      allow_restore: false,
      allow_maximize: false
    }, null]);

    this.iframeWindow = null;
    this.firefoxAppId  = id;
    this.firefoxAppUrl = url;
  };

  FirefoxAppWindow.prototype = Object.create(Window.prototype);

  FirefoxAppWindow.prototype.init = function(wmRef, app) {
    var self = this;
    var root = Window.prototype.init.apply(this, arguments);

    var w = this._addGUIElement(new IframeElement('FirefoxIframe' + this.firefoxAppId.toString(), this.firefoxAppUrl, function(contentWindow) {
      self._addHook('focus', function() {
        if ( contentWindow ) {
          contentWindow.focus();
          w.frame.focus();
        }
      });
      self._addHook('blur', function() {
        if ( contentWindow ) {
          contentWindow.blur();
          w.frame.blur();
        }
      });
    }), root);

    return root;
  };

  /////////////////////////////////////////////////////////////////////////////
  // API
  /////////////////////////////////////////////////////////////////////////////

  var SingletonInstance = null;

  /**
   * The FirefoxMarketplace wrapper class
   *
   * This is a private class and can only be aquired through
   * OSjs.Helpers.FirefoxMarketplace.createInsatance()
   *
   * @link http://firefox-marketplace-api.readthedocs.org/en/latest
   *
   * @see OSjs.Helpers.FirefoxMarketplace.createInsatance()
   * @api OSjs.Helpers.FirefoxMarketplace.FirefoxMarketplace
   *
   * @private
   * @class
   */
  function FirefoxMarketplace(clientId) {
  }

  /**
   * Destroy the class
   */
  FirefoxMarketplace.prototype.destroy = function() {
  };

  /**
   * Initializes (preloads) the API
   */
  FirefoxMarketplace.prototype.init = function(callback) {
    callback = callback || function() {};
    callback();
  };

  FirefoxMarketplace.prototype._call = function(func, data, callback) {
    var url = buildURL(func, data);
    apiCall(url, callback);
  };

  FirefoxMarketplace.prototype._metadata = function(appId, callback) {
    var func = 'v2/apps/app/' + appId.toString() + '/';
    var data = {};

    callback = callback || function() {};

    this._call(func, data, function(error, response) {
      if ( error ) { callback(error); }

      var url = response.manifest_url;

      apiCall(url, function(error, metadata) {
        if ( error ) { callback(error); }

        callback(false, metadata, url);
      });

    });
  };

  FirefoxMarketplace.prototype.search = function(q, callback) {
    var func = 'v1/fireplace/search/featured';
    var data = {
      type: 'app',
      app_type: 'hosted'
    };

    if ( q ) {
      func = 'v1/apps/search/';
      data.q = q;
    }

    callback = callback || function() {};

    this._call(func, data, function(error, response) {
      if ( error ) { callback(error); }

      callback(false, response.objects);
    });
  };

  FirefoxMarketplace.prototype.launch = function(id, callback) {
    callback = callback || function() {};

    this._metadata(id, function(error, metadata, url) {
      if ( error ) { callback(error); }

      metadata = metadata || {};

      var resolve = document.createElement('a');
      resolve.href = url;

      var launcher = resolve.host + (metadata.launch_path ? ('/' + metadata.launch_path) : '');
      launcher = resolve.protocol + '//' + launcher.replace(/\/+/g, '/');

      var wm = OSjs.Core.getWindowManager();
      var icon = metadata.icons ? metadata.icons["128"] : null;
      if ( icon.match(/^\//) ) {
        icon = resolve.protocol + '//' + resolve.host + icon;
      }
      var win = new FirefoxAppWindow(id, metadata.name, icon, launcher);
      wm.addWindow(win);

      callback(false, win);
    });
  };

  /////////////////////////////////////////////////////////////////////////////
  // EXPORTS
  /////////////////////////////////////////////////////////////////////////////

  OSjs.Helpers.FirefoxMarketplace = OSjs.Helpers.FirefoxMarketplace || {};

  /**
   * Gets the currently running instance
   *
   * @api OSjs.Helpers.FirefoxMarketplace.getInstance()
   *
   * @return  FirefoxMarketplace       Can also be null
   */
  OSjs.Helpers.FirefoxMarketplace.getInstance = function() {
    return SingletonInstance;
  };

  /**
   * Create an instance of FirefoxMarketplace
   *
   * @param   Object    args      Arguments
   * @param   Function  callback  Callback function => fn(error, instance)
   *
   * @option  args    Array     scope     What scope to load
   *
   * @api OSjs.Helpers.FirefoxMarketplace.createInstance()
   *
   * @return  void
   */
  OSjs.Helpers.FirefoxMarketplace.createInstance = function(args, callback) {
    args = args || {};

    if ( !SingletonInstance ) {
      SingletonInstance = new FirefoxMarketplace();
      SingletonInstance.init(function() {
        callback(false, SingletonInstance);
      });
      return;
    }

    callback(false, SingletonInstance);
  };

})(OSjs.Utils, OSjs.API, OSjs.Core.GUIElement, OSjs.Core.Window);
<commit_msg>FirefoxMarketplace: Added code documentation (#98)<commit_after>/*!
 * OS.js - JavaScript Operating System
 *
 * Copyright (c) 2011-2015, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */

(function(Utils, API, GUIElement, Window) {
  'use strict';

  var OSjs = window.OSjs = window.OSjs || {};

  OSjs.Helpers = OSjs.Helpers || {};

  var URL_FMT = 'https://marketplace.firefox.com/api/';

  function buildURL(path, args) {
    var str = [];
    Object.keys(args).forEach(function(i) {
      str.push(i + '=' + args[i]);
    });
    return URL_FMT + path + (str.length ? '?' : '') + str.join('&');
  }

  function apiCall(url, callback) {
    API.curl({
      body: {
        url: url,
        binary: false,
        mime: 'application/json',
        method: 'GET'
      }
    }, function(error, response) {
      if ( error ) {
        callback(error);
        return;
      }

      if ( !response.body ) {
        callback('Response was empty');
        return;
      }

      var data = JSON.parse(response.body);
      callback(false, data);
    });
  }

  // TODO: REMOVE WHEN #89 is done
  function IframeElement(name, src, cb) {
    this.frameCallback = cb || function() {};
    this.frameSource = src;
    this.frameWindow = null;
    this.frame = null;

    GUIElement.apply(this, [name, {
      isIframe: true
    }]);
  }

  IframeElement.prototype = Object.create(GUIElement.prototype);

  IframeElement.prototype.init = function() {
    var self = this;
    var el = GUIElement.prototype.init.apply(this, ['GUIFirefoxAppIframe']);
    this.frame = document.createElement('iframe');
    /*
    this.frame.style.position = 'absolute';
    this.frame.style.top = '0px';
    this.frame.style.right = '0px';
    this.frame.style.bottom = '0px';
    this.frame.style.left = '0px';
    */
    this.frame.style.width = '320px';
    this.frame.style.height = '480px';
    this.frame.style.border = '0 none';
    this.frame.frameborder = '0';
    this.frame.onload = function() {
      self.frameWindow = self.frame.contentWindow;
      self.frameCallback(self.frameWindow);
    };
    this.frame.src = this.frameSource;
    el.appendChild(this.frame);
    return el;
  };

  /////////////////////////////////////////////////////////////////////////////
  // FFAPP WINDOW
  /////////////////////////////////////////////////////////////////////////////

  var FirefoxAppWindow = function(id, title, icon, url) {
    Window.apply(this, ['FirefoxAppWindow' + id.toString(), {
      icon: icon,
      title: title,
      width: 320,
      height: 480,
      allow_resise: false,
      allow_restore: false,
      allow_maximize: false
    }, null]);

    this.iframeWindow = null;
    this.firefoxAppId  = id;
    this.firefoxAppUrl = url;
  };

  FirefoxAppWindow.prototype = Object.create(Window.prototype);

  FirefoxAppWindow.prototype.init = function(wmRef, app) {
    var self = this;
    var root = Window.prototype.init.apply(this, arguments);

    var w = this._addGUIElement(new IframeElement('FirefoxIframe' + this.firefoxAppId.toString(), this.firefoxAppUrl, function(contentWindow) {
      self._addHook('focus', function() {
        if ( contentWindow ) {
          contentWindow.focus();
          w.frame.focus();
        }
      });
      self._addHook('blur', function() {
        if ( contentWindow ) {
          contentWindow.blur();
          w.frame.blur();
        }
      });
    }), root);

    return root;
  };

  /////////////////////////////////////////////////////////////////////////////
  // API
  /////////////////////////////////////////////////////////////////////////////

  var SingletonInstance = null;

  /**
   * The FirefoxMarketplace wrapper class
   *
   * This is a private class and can only be aquired through
   * OSjs.Helpers.FirefoxMarketplace.createInsatance()
   *
   * @link http://firefox-marketplace-api.readthedocs.org/en/latest
   *
   * @see OSjs.Helpers.FirefoxMarketplace.createInsatance()
   * @api OSjs.Helpers.FirefoxMarketplace.FirefoxMarketplace
   *
   * @private
   * @class
   */
  function FirefoxMarketplace(clientId) {
  }

  /**
   * Destroy the class
   */
  FirefoxMarketplace.prototype.destroy = function() {
  };

  /**
   * Initializes (preloads) the API
   */
  FirefoxMarketplace.prototype.init = function(callback) {
    callback = callback || function() {};
    callback();
  };

  /**
   * Call the Marketplace API
   *
   * @param   String      func          The URI (function)
   * @param   Object      data          Tuple with arguments
   * @param   Function    callback      Callback => fn(error, result, url)
   *
   * @return  void
   *
   * @method  FirefoxMarketplace::_call()
   */
  FirefoxMarketplace.prototype._call = function(func, data, callback) {
    var url = buildURL(func, data);
    apiCall(url, callback);
  };

  /**
   * Get metadata for application by id
   *
   * @param   int         id            Application ID
   * @param   Function    callback      Callback => fn(error, result, url)
   *
   * @return  void
   *
   * @method  FirefoxMarketplace::_metadata()
   */
  FirefoxMarketplace.prototype._metadata = function(appId, callback) {
    var func = 'v2/apps/app/' + appId.toString() + '/';
    var data = {};

    callback = callback || function() {};

    this._call(func, data, function(error, response) {
      if ( error ) { callback(error); }

      var url = response.manifest_url;

      apiCall(url, function(error, metadata) {
        if ( error ) { callback(error); }

        callback(false, metadata, url);
      });

    });
  };

  /**
   * Search for an application
   *
   * If no query is given, the featured list is retrieved
   *
   * @param   String      q             Query string (optional)
   * @param   Function    callback      Callback => fn(error, result)
   *
   * @return  void
   *
   * @method  FirefoxMarketplace::search()
   */
  FirefoxMarketplace.prototype.search = function(q, callback) {
    var func = 'v1/fireplace/search/featured';
    var data = {
      type: 'app',
      app_type: 'hosted'
    };

    if ( q ) {
      func = 'v1/apps/search/';
      data.q = q;
    }

    callback = callback || function() {};

    this._call(func, data, function(error, response) {
      if ( error ) { callback(error); }

      callback(false, response.objects);
    });
  };

  /**
   * Launch an application by Id
   *
   * @param   int         id            Application ID
   * @param   Function    callback      Callback => fn(error, result)
   *
   * @return  void
   *
   * @method  FirefoxMarketplace::launch()
   */
  FirefoxMarketplace.prototype.launch = function(id, callback) {
    callback = callback || function() {};

    this._metadata(id, function(error, metadata, url) {
      if ( error ) { callback(error); }

      metadata = metadata || {};

      var resolve = document.createElement('a');
      resolve.href = url;

      var launcher = resolve.host + (metadata.launch_path ? ('/' + metadata.launch_path) : '');
      launcher = resolve.protocol + '//' + launcher.replace(/\/+/g, '/');

      var wm = OSjs.Core.getWindowManager();
      var icon = metadata.icons ? metadata.icons["128"] : null;
      if ( icon.match(/^\//) ) {
        icon = resolve.protocol + '//' + resolve.host + icon;
      }
      var win = new FirefoxAppWindow(id, metadata.name, icon, launcher);
      wm.addWindow(win);

      callback(false, win);
    });
  };

  /////////////////////////////////////////////////////////////////////////////
  // EXPORTS
  /////////////////////////////////////////////////////////////////////////////

  OSjs.Helpers.FirefoxMarketplace = OSjs.Helpers.FirefoxMarketplace || {};

  /**
   * Gets the currently running instance
   *
   * @api OSjs.Helpers.FirefoxMarketplace.getInstance()
   *
   * @return  FirefoxMarketplace       Can also be null
   */
  OSjs.Helpers.FirefoxMarketplace.getInstance = function() {
    return SingletonInstance;
  };

  /**
   * Create an instance of FirefoxMarketplace
   *
   * @param   Object    args      Arguments
   * @param   Function  callback  Callback function => fn(error, instance)
   *
   * @option  args    Array     scope     What scope to load
   *
   * @api OSjs.Helpers.FirefoxMarketplace.createInstance()
   *
   * @return  void
   */
  OSjs.Helpers.FirefoxMarketplace.createInstance = function(args, callback) {
    args = args || {};

    if ( !SingletonInstance ) {
      SingletonInstance = new FirefoxMarketplace();
      SingletonInstance.init(function() {
        callback(false, SingletonInstance);
      });
      return;
    }

    callback(false, SingletonInstance);
  };

})(OSjs.Utils, OSjs.API, OSjs.Core.GUIElement, OSjs.Core.Window);
<|endoftext|>


<commit_before>import React, { useState } from 'react';

import { Box, CheckBox } from 'grommet';

const SimpleCheckBox = ({ checked: checkedProp, ...rest }) => {
  const [checked, setChecked] = useState(!!checkedProp);
  const onChange = (event) => setChecked(event.target.checked);

  return (
    <Box align="center" pad="large">
      <CheckBox {...rest} checked={checked} onChange={onChange} />
    </Box>
  );
};

export const NoLabel = () => <SimpleCheckBox />;

NoLabel.storyName = 'No label';

export default {
  title: 'Input/CheckBox/No label',
};
<commit_msg>Fix checkbox no label accessibility violation (#6449)<commit_after>import React, { useState } from 'react';

import { Box, CheckBox } from 'grommet';

const SimpleCheckBox = ({ checked: checkedProp, ...rest }) => {
  const [checked, setChecked] = useState(!!checkedProp);
  const onChange = (event) => setChecked(event.target.checked);

  return (
    <Box align="center" pad="large">
      <CheckBox {...rest} checked={checked} onChange={onChange} />
    </Box>
  );
};

export const NoLabel = () => (
  <SimpleCheckBox a11yTitle="Checkbox without a label" />
);

NoLabel.storyName = 'No label';

export default {
  title: 'Input/CheckBox/No label',
};
<|endoftext|>


<commit_before>import GLBoost from '../../globals';
import Geometry from '../../low_level/geometries/Geometry';
import Vector3 from '../../low_level/math/Vector3';
import Vector4 from '../../low_level/math/Vector4';

export default class JointPrimitive extends Geometry {
  constructor(glBoostContext, length, lineCount = 1) {
    super(glBoostContext);

    this._setupVertexData(length, lineCount);
  }

  _setupVertexData(length, lineCount) {

    let arrowheadWidth = length/10;
    let arrowheadLength = length/7.5;
    let stickLength = length - arrowheadLength;
    let halfLength = 0;//length/2;

    let positions = [];

    for (let i=0; i<lineCount; i++) {
      let lineOffset = (i - lineCount/2) * arrowheadWidth;
      let lineOtherOffset = arrowheadWidth;
      if( ( i % 2 ) !== 1 ) {
        lineOtherOffset = -lineOtherOffset;
      }
/*
      // Stick part
      positions.push(new Vector3(halfLength, lineOtherOffset, lineOffset));
      positions.push(new Vector3(-stickLength+halfLength, lineOtherOffset , lineOffset));

      // 1st line of a triangle
      positions.push(new Vector3(-stickLength+halfLength, arrowheadWidth+lineOtherOffset, lineOffset));
      positions.push(new Vector3(-stickLength+halfLength, -arrowheadWidth+lineOtherOffset, lineOffset));

      // 2nd line of a triangle
      positions.push(new Vector3(-stickLength+halfLength, -arrowheadWidth+lineOtherOffset, lineOffset));
      positions.push(new Vector3(-length+halfLength, lineOtherOffset, lineOffset));

      // 3rd line of a triangle
      positions.push(new Vector3(-length+halfLength, lineOtherOffset, lineOffset));
      positions.push(new Vector3(-stickLength+halfLength, arrowheadWidth+lineOtherOffset, lineOffset));

      // Stick part
      positions.push(new Vector3(lineOtherOffset, -halfLength, lineOffset));
      positions.push(new Vector3(lineOtherOffset, stickLength-halfLength, lineOffset));

      // 1st line of a triangle
      positions.push(new Vector3(arrowheadWidth+lineOtherOffset, stickLength-halfLength, lineOffset));
      positions.push(new Vector3(-arrowheadWidth+lineOtherOffset, stickLength-halfLength, lineOffset));

      // 2nd line of a triangle
      positions.push(new Vector3(-arrowheadWidth+lineOtherOffset, stickLength-halfLength, lineOffset));
      positions.push(new Vector3(lineOtherOffset, length-halfLength, lineOffset));

      // 3rd line of a triangle
      positions.push(new Vector3(lineOtherOffset, length-halfLength, lineOffset));
      positions.push(new Vector3(arrowheadWidth+lineOtherOffset, stickLength-halfLength, lineOffset));
*/

      // Stick part
      positions.push(new Vector3(lineOtherOffset, lineOffset, halfLength));
      positions.push(new Vector3(lineOtherOffset, lineOffset, -stickLength+halfLength));

      // 1st line of a triangle
      positions.push(new Vector3(arrowheadWidth+lineOtherOffset, lineOffset, -stickLength+halfLength));
      positions.push(new Vector3(-arrowheadWidth+lineOtherOffset, lineOffset, -stickLength+halfLength));

      // 2nd line of a triangle
      positions.push(new Vector3(-arrowheadWidth+lineOtherOffset, lineOffset, -stickLength+halfLength));
      positions.push(new Vector3(lineOtherOffset, lineOffset, -length+halfLength));

      // 3rd line of a triangle
      positions.push(new Vector3(lineOtherOffset, lineOffset, -length+halfLength));
      positions.push(new Vector3(arrowheadWidth+lineOtherOffset, lineOffset, -stickLength+halfLength));

    }

    this.setVerticesData({
      position: positions
    }, null, GLBoost.LINES);
  }

}

GLBoost["JointPrimitive"] = JointPrimitive;
<commit_msg>Fix Arrow direction of JointPrimitive<commit_after>import GLBoost from '../../globals';
import Geometry from '../../low_level/geometries/Geometry';
import Vector3 from '../../low_level/math/Vector3';
import Vector4 from '../../low_level/math/Vector4';

export default class JointPrimitive extends Geometry {
  constructor(glBoostContext, length, lineCount = 1) {
    super(glBoostContext);

    this._setupVertexData(length, lineCount);
  }

  _setupVertexData(length, lineCount) {

    let arrowheadWidth = length/10;
    let arrowheadLength = length/7.5;
    let stickLength = length - arrowheadLength;
    let halfLength = 0;//length/2;

    let positions = [];

    for (let i=0; i<lineCount; i++) {
      let lineOffset = (i - lineCount/2) * arrowheadWidth;
      let lineOtherOffset = arrowheadWidth;
      if( ( i % 2 ) !== 1 ) {
        lineOtherOffset = -lineOtherOffset;
      }

      // Stick part
      positions.push(new Vector3(-halfLength, lineOtherOffset, lineOffset));
      positions.push(new Vector3(stickLength-halfLength, lineOtherOffset , lineOffset));

      // 1st line of a triangle
      positions.push(new Vector3(stickLength-halfLength, arrowheadWidth+lineOtherOffset, lineOffset));
      positions.push(new Vector3(stickLength-halfLength, -arrowheadWidth+lineOtherOffset, lineOffset));

      // 2nd line of a triangle
      positions.push(new Vector3(stickLength-halfLength, -arrowheadWidth+lineOtherOffset, lineOffset));
      positions.push(new Vector3(length-halfLength, lineOtherOffset, lineOffset));

      // 3rd line of a triangle
      positions.push(new Vector3(length-halfLength, lineOtherOffset, lineOffset));
      positions.push(new Vector3(stickLength-halfLength, arrowheadWidth+lineOtherOffset, lineOffset));

/*
      // Stick part
      positions.push(new Vector3(halfLength, lineOtherOffset, lineOffset));
      positions.push(new Vector3(-stickLength+halfLength, lineOtherOffset , lineOffset));

      // 1st line of a triangle
      positions.push(new Vector3(-stickLength+halfLength, arrowheadWidth+lineOtherOffset, lineOffset));
      positions.push(new Vector3(-stickLength+halfLength, -arrowheadWidth+lineOtherOffset, lineOffset));

      // 2nd line of a triangle
      positions.push(new Vector3(-stickLength+halfLength, -arrowheadWidth+lineOtherOffset, lineOffset));
      positions.push(new Vector3(-length+halfLength, lineOtherOffset, lineOffset));

      // 3rd line of a triangle
      positions.push(new Vector3(-length+halfLength, lineOtherOffset, lineOffset));
      positions.push(new Vector3(-stickLength+halfLength, arrowheadWidth+lineOtherOffset, lineOffset));
/*
      // Stick part
      positions.push(new Vector3(lineOtherOffset, -halfLength, lineOffset));
      positions.push(new Vector3(lineOtherOffset, stickLength-halfLength, lineOffset));

      // 1st line of a triangle
      positions.push(new Vector3(arrowheadWidth+lineOtherOffset, stickLength-halfLength, lineOffset));
      positions.push(new Vector3(-arrowheadWidth+lineOtherOffset, stickLength-halfLength, lineOffset));

      // 2nd line of a triangle
      positions.push(new Vector3(-arrowheadWidth+lineOtherOffset, stickLength-halfLength, lineOffset));
      positions.push(new Vector3(lineOtherOffset, length-halfLength, lineOffset));

      // 3rd line of a triangle
      positions.push(new Vector3(lineOtherOffset, length-halfLength, lineOffset));
      positions.push(new Vector3(arrowheadWidth+lineOtherOffset, stickLength-halfLength, lineOffset));
*/

/*
      // Stick part
      positions.push(new Vector3(lineOtherOffset, lineOffset, halfLength));
      positions.push(new Vector3(lineOtherOffset, lineOffset, -stickLength+halfLength));

      // 1st line of a triangle
      positions.push(new Vector3(arrowheadWidth+lineOtherOffset, lineOffset, -stickLength+halfLength));
      positions.push(new Vector3(-arrowheadWidth+lineOtherOffset, lineOffset, -stickLength+halfLength));

      // 2nd line of a triangle
      positions.push(new Vector3(-arrowheadWidth+lineOtherOffset, lineOffset, -stickLength+halfLength));
      positions.push(new Vector3(lineOtherOffset, lineOffset, -length+halfLength));

      // 3rd line of a triangle
      positions.push(new Vector3(lineOtherOffset, lineOffset, -length+halfLength));
      positions.push(new Vector3(arrowheadWidth+lineOtherOffset, lineOffset, -stickLength+halfLength));
*/
    }

    this.setVerticesData({
      position: positions
    }, null, GLBoost.LINES);
  }

}

GLBoost["JointPrimitive"] = JointPrimitive;
<|endoftext|>


<commit_before>'use strict'

let whitelist
let whitelistArray = new Array()

chrome.storage.sync.get(function(items) {
	if (items.whitelist) {
		whitelist = items.whitelist
		whitelistArray = whitelist.split('\n')
	}
})

function inWhitelist(url) {
	let listed = false
	whitelistArray.forEach(function(item) {
		if (url.startsWith(item)) {
			listed = true
		}
	})
	return listed
}

function sleepTab(html, tab) {
	const pageInfo = {
		url: tab.url,
		title: tab.title,
		hibernationInfo: chrome.i18n.getMessage('hibernationPageInfo'),
		buttonText: chrome.i18n.getMessage('hibernationPageButton'),
		favIconUrl: tab.favIconUrl
	}
	const pageHtml = html.replace(/\{\/\*pageInfoObject\*\/\}/, JSON.stringify(pageInfo))
	const dataURL = 'data:text/html;base64,' + encodeURIComponent(pageHtml)
	chrome.tabs.update(tab.id, {url: dataURL})
}

chrome.browserAction.onClicked.addListener(function() {
	const xmlHttp = new XMLHttpRequest()
	xmlHttp.open('GET', chrome.runtime.getURL('hibernationPage/index.html'), true)
	xmlHttp.onload = function() {
		const html = xmlHttp.responseText

		chrome.windows.getAll({populate: true}, function(windows) {
			windows.forEach(function(win) {
				win.tabs.forEach(function(tab) {
					if (tab.active || tab.highlighted || tab.pinned) return
					if (tab.status !== 'complete') return
					if (!tab.url.match(/^https?:\/\//)) return
					if (inWhitelist(tab.url)) return

					sleepTab(html, tab)
				})
			})
		})
	}
	xmlHttp.send(null)
})

chrome.runtime.onInstalled.addListener(function() {
	chrome.contextMenus.create({
		id : 'SleepTab',
		title : chrome.i18n.getMessage('contextMenuTitle'),
		contexts : ['page']
	})
})

chrome.contextMenus.onClicked.addListener(function(info, tab) {
	const xmlHttp = new XMLHttpRequest()
	xmlHttp.open('GET', chrome.runtime.getURL('hibernationPage/index.html'), true)
	xmlHttp.onload = function() {
		const html = xmlHttp.responseText
		sleepTab(html, tab)
	}
	xmlHttp.send(null)
})

chrome.commands.onCommand.addListener(function() {
	chrome.tabs.query({active: true, lastFocusedWindow: true}, function(tabs) {
		const xmlHttp = new XMLHttpRequest()
		xmlHttp.open('GET', chrome.runtime.getURL('hibernationPage/index.html'), true)
		xmlHttp.onload = function() {
			const html = xmlHttp.responseText
			sleepTab(html, tabs[0])
		}
		xmlHttp.send(null)
	})
})
<commit_msg>data-uri: use base64 instead of escape sequences to mitigate some edgecases<commit_after>'use strict'

let whitelist
let whitelistArray = new Array()

chrome.storage.sync.get(function(items) {
	if (items.whitelist) {
		whitelist = items.whitelist
		whitelistArray = whitelist.split('\n')
	}
})

function inWhitelist(url) {
	let listed = false
	whitelistArray.forEach(function(item) {
		if (url.startsWith(item)) {
			listed = true
		}
	})
	return listed
}

function sleepTab(html, tab) {
	const pageInfo = {
		url: tab.url,
		title: tab.title,
		hibernationInfo: chrome.i18n.getMessage('hibernationPageInfo'),
		buttonText: chrome.i18n.getMessage('hibernationPageButton'),
		favIconUrl: tab.favIconUrl
	}
	const pageHtml = html.replace(/\{\/\*pageInfoObject\*\/\}/, JSON.stringify(pageInfo))
	const dataURL = 'data:text/html;base64,' + btoa(pageHtml)
	chrome.tabs.update(tab.id, {url: dataURL})
}

chrome.browserAction.onClicked.addListener(function() {
	const xmlHttp = new XMLHttpRequest()
	xmlHttp.open('GET', chrome.runtime.getURL('hibernationPage/index.html'), true)
	xmlHttp.onload = function() {
		const html = xmlHttp.responseText

		chrome.windows.getAll({populate: true}, function(windows) {
			windows.forEach(function(win) {
				win.tabs.forEach(function(tab) {
					if (tab.active || tab.highlighted || tab.pinned) return
					if (tab.status !== 'complete') return
					if (!tab.url.match(/^https?:\/\//)) return
					if (inWhitelist(tab.url)) return

					sleepTab(html, tab)
				})
			})
		})
	}
	xmlHttp.send(null)
})

chrome.runtime.onInstalled.addListener(function() {
	chrome.contextMenus.create({
		id : 'SleepTab',
		title : chrome.i18n.getMessage('contextMenuTitle'),
		contexts : ['page']
	})
})

chrome.contextMenus.onClicked.addListener(function(info, tab) {
	const xmlHttp = new XMLHttpRequest()
	xmlHttp.open('GET', chrome.runtime.getURL('hibernationPage/index.html'), true)
	xmlHttp.onload = function() {
		const html = xmlHttp.responseText
		sleepTab(html, tab)
	}
	xmlHttp.send(null)
})

chrome.commands.onCommand.addListener(function() {
	chrome.tabs.query({active: true, lastFocusedWindow: true}, function(tabs) {
		const xmlHttp = new XMLHttpRequest()
		xmlHttp.open('GET', chrome.runtime.getURL('hibernationPage/index.html'), true)
		xmlHttp.onload = function() {
			const html = xmlHttp.responseText
			sleepTab(html, tabs[0])
		}
		xmlHttp.send(null)
	})
})
<|endoftext|>


<commit_before>var defaultOptions = {
  url: 'http://localhost:8080/mqb'
}

function WebConnection(onReady, onMessage, options) {
  var self = this;

  this.onMessage = onMessage;

  // Merge passed in options into default options
  this.options = util.extend(defaultOptions, options);

  this.sock = new SockJS(this.options.url, this.options.protocols, this.options.sockjs);
  this.sock.onopen = function() {
    self.clientId = self.sock._connid;
    onReady();
  };
  this.sock.onmessage = self.onData.bind(self);
  this.sock.onclose = function() {
    util.warn("WebConnection closed");
  };
}

util.inherit(WebConnection, Connection);

WebConnection.prototype.onData = function(message) {
  var self = this;
  try {
    var message = util.parse(message.data);
    self.onMessage(message);
  } catch (e) {
    util.error("Message parsing failed: ", e.message, e.stack);
  }
}

WebConnection.prototype.send = function(routingKey, message, links) {
  util.info('Sending', routingKey, message, links);
  // Adding links that need to be established to headers
  var headers = {};
  for (x in links) {
    headers['link_' + x] = links[x];
  }
  // Push message
  this.sock.send(util.stringify({message: message, routingKey: routingKey, headers: headers}));
}

WebConnection.prototype.joinWorkerPool = function(name) {
  util.info('Joined worker pool', name);
  this.sock.send(util.stringify({type: 'joinWorkerPool', name: name}));
}

// TODO: Implement join channel callback
WebConnection.prototype.addToChannel = function(name) {
  // Adding other client is not supported
  this.sock.send(util.stringify({type: 'joinChannel', name: name}));
}

var NowConnection = WebConnection;
<commit_msg>joinChannel working on websocket<commit_after>var defaultOptions = {
  url: 'http://192.168.2.109:8080/mqb'
}

function WebConnection(onReady, onMessage, options) {
  var self = this;

  this.onMessage = onMessage;

  // Merge passed in options into default options
  this.options = util.extend(defaultOptions, options);

  this.sock = new SockJS(this.options.url, this.options.protocols, this.options.sockjs);
  this.sock.onopen = function() {
    self.clientId = self.sock._connid;
    onReady();
  };
  this.sock.onmessage = self.onData.bind(self);
  this.sock.onclose = function() {
    util.warn("WebConnection closed");
  };
}

util.inherit(WebConnection, Connection);

WebConnection.prototype.onData = function(message) {
  var self = this;
  try {
    var message = util.parse(message.data);
    self.onMessage(message);
  } catch (e) {
    util.error("Message parsing failed: ", e.message, e.stack);
  }
}

WebConnection.prototype.send = function(routingKey, message, links) {
  util.info('Sending', routingKey, message, links);
  // Adding links that need to be established to headers
  var headers = {};
  for (x in links) {
    headers['link_' + x] = links[x];
  }
  // Push message
  this.sock.send(util.stringify({message: message, routingKey: routingKey, headers: headers}));
}

WebConnection.prototype.joinWorkerPool = function(name) {
  util.info('Joined worker pool', name);
  this.sock.send(util.stringify({type: 'joinWorkerPool', name: name}));
}

// TODO: Implement join channel callback
WebConnection.prototype.joinChannel = function(name) {
  // Adding other client is not supported
  this.sock.send(util.stringify({type: 'joinChannel', name: name}));
}

var NowConnection = WebConnection;
<|endoftext|>