<commit_before>/*
 * Copyright 2014-2017 Jiří Janoušek <janousek.jiri@gmail.com>
 *
 * 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.
 */

require("prototype");
require("signals");

/**
 * Manages actions
 *
 * Actions can be shown in various user interface components such as menu, tray icon menu, Unity HUD, Unity Laucher
 * Quicklist, or invoked by keyboard shortcut, remote control, etc.
 *
 * Some actions are provided by the Nuvola Player backend (e. g. @link{BrowserAction|browser actions}),
 * some are created by JavaScript API objects (e. g. @link{PlayerAction|media player actions}) and
 * web app integration scripts can create custom actions with @link{Actions.addAction} or @link{Actions.addRadioAction}.
 */
var Actions = $prototype(null, SignalsMixin);

/**
 * Initializes new Actions object
 */
Actions.$init = function()
{
    /**
     * Emitted when an action is activated.
     *
     * @param String name      action name
     * @param Variant param    action parameter, usually null
     *
     * ```
     * MyObject.$init = function()
     * {
     *     Nuvola.actions.connect("ActionActivated", this);
     * }
     *
     * MyObject._onActionActivated = function(emitter, name, param)
     * {
     *     console.log("Action activated: " + name);
     * }
     * ```
     */
    this.addSignal("ActionActivated");

    /**
     * Emitted when an action has been enabled or disabled.
     *
     * @param String name        action name
     * @param Boolean enabled    true if the action is enabled
     *
     * ```
     * MyObject.$init = function()
     * {
     *     Nuvola.actions.connect("ActionEnabledChanged", this);
     * }
     *
     * MyObject._ActionEnabledChanged = function(emitter, name, enabled)
     * {
     *     console.log("Action " + name + " " + (enabled ? "enabled" : "disabled") + ".");
     * }
     * ```
     */
    this.addSignal("ActionEnabledChanged");
    this.connect("ActionActivated", this);
    this.connect("ActionEnabledChanged", this);
    this.buttons = {};
    this.enabledFlagsCache = {};
    this.statesCache = {};
}

/**
 * Adds new simple or toggle action.
 *
 * when action is activated, signal ActionActivated is emitted.
 *
 * @param String group               action group, e.g. "playback"
 * @param String scope               action scope, use "win" for window-specific actions (preferred) or "app" for
 *                                   app-specific actions
 * @param String name                action name, should be in ``dash-separated-lower-case``, e.g. ``toggle-play``
 * @param String|null label          label shown in user interface, e.g. ``Play``
 * @param String|null mnemo_label    label shown in user interface with keyboard navigation using Alt key and letter
 *                                   prefixed with underscore, e.g. Alt+p for ``_Play``
 * @param String|null icon           icon name for action
 * @param String|null keybinding     in-app keyboard shortcut, e.g. ``<ctrl>P``
 * @param Boolean|null state         ``null`` for simple actions, ``true``/``false`` for toggle actions (on/off)
 *
 * ```
 * // Add new simple action ``play`` with icon ``media-playback-start``
 * Nuvola.actions.addAction("playback", "win", "play", "Play", null, "media-playback-start", null);
 *
 * // Add new toggle action ``thumbs-up`` with initial state ``true`` (on)
 * Nuvola.actions.addAction("playback", "win", "thumbs-up", "Thumbs up", null, null, null, true);
 * ```
 */
Actions.addAction = function(group, scope, name, label, mnemo_label, icon, keybinding, state)
{
    var state = state !== undefined ? state: null;
    Nuvola._callIpcMethodVoid("/nuvola/actions/add-action",
     [group, scope, name, label || "", mnemo_label || "", icon || "", keybinding || "", state]);
}

/**
 * Adds new radio action (action with multiple states/option like old radios)
 *
 * when action is activated, signal ActionActivated is emitted.
 *
 * @param String group       action group, e.g. "playback"
 * @param String scope       action scope, use "win" for window-specific actions (preferred) or "app" for
 *                           app-specific actions
 * @param String name        action name, should be in ``dash-separated-lower-case``, e.g. ``toggle-play``
 * @param variant stateId    initial state of the action. must be one of states specified in ``options`` array
 * @param Array options      array of options definition in form ``[stateId, label, mnemo_label, icon, keybinding]``.
 *                           ``stateId`` is unique identifier (Number or String), other parameters are described
 *                           in ``addAction`` method.
 *
 * ```
 * // define rating options - 5 states with state id 0-5 representing 0-5 stars
 * var ratingOptions = [
 *     // stateId, label, mnemo_label, icon, keybinding
 *     [0, "Rating: 0 stars", null, null, null, null],
 *     [1, "Rating: 1 star", null, null, null, null],
 *     [2, "Rating: 2 stars", null, null, null, null],
 *     [3, "Rating: 3 stars", null, null, null, null],
 *     [4, "Rating: 4 stars", null, null, null, null],
 *     [5, "Rating: 5 stars", null, null, null, null]
 * ];
 *
 * // Add new radio action named ``rating`` with initial state ``0`` (0 stars)
 * Nuvola.actions.addRadioAction("playback", "win", "rating", 0, ratingOptions);
 * ```
 */
Actions.addRadioAction = function(group, scope, name, stateId, options)
{
    Nuvola._callIpcMethodVoid("/nuvola/actions/add-radio-action", [group, scope, name, stateId, options]);
}

Actions._onActionActivated = function(arg1, action)
{
    console.log("JS API: Action activated: " + action);
}

/**
 * Checks whether action is enabled
 *
 * @param String name    action name
 * @deprecated since Nuvola 4.8. Use async variant instead.
 * @return true is action is enabled, false otherwise
 */
Actions.isEnabled = function(name) {
    Nuvola.log("Actions.isEnabled is deprecated since Nuvola 4.8. Use async variant instead.");
    return Nuvola._callIpcMethodSync("/nuvola/actions/is-enabled", [name]);
}

/**
 * Checks whether action is enabled
 *
 * @since Nuvola 4.8
 * @async
 * @param String name    action name
 * @return true is action is enabled, false otherwise
 */
Actions.isEnabledAsync = function(name) {
    return Nuvola.Async.call("/nuvola/actions/is-enabled", [name]);
}

/**
 * Sets whether action is enabled
 *
 * **Note:** consider use of method @link{Actions.updateEnabledFlag} that is more effective, because it performs
 * caching of enabled flags and updates them only if necessary.
 *
 * @param String name        action name
 * @param Boolean enabled    true is action is enabled, false otherwise
 */
Actions.setEnabled = function(name, enabled)
{
    Nuvola._callIpcMethodVoid("/nuvola/actions/set-enabled", [name, enabled]);
}

/**
 * Update action enabled flag
 *
 * This method uses cache of action enabled flags to update them only if necessary.
 *
 * @param String name        action name
 * @param Boolean enabled    true is action is enabled, false otherwise
 */
Actions.updateEnabledFlag = function(name, enabled)
{
    if (enabled !== this.enabledFlagsCache[name])
    {
        this.enabledFlagsCache[name] = enabled;
        this.setEnabled(name, enabled);
    }
}

/**
 * Bulk update of action enabled flags
 *
 * This method uses cache of action enabled flags to update them only if necessary.
 *
 * @param Object enabledFlags    mapping of ``action name``: ``enabled flag``
 */
Actions.updateEnabledFlags = function(enabledFlags)
{
    for (var name in enabledFlags)
        this.updateEnabledFlag(name, enabledFlags[name]);
}

/**
 * Get current state of toggle or radio actions.
 *
 * @param String name    action name
 * @return current state: ``true/false`` for toggle actions, one of stateId entries of radio actions
 *
 * ```
 * var thumbsUp = Nuvola.actions.getState("thumbs-up");
 * console.log("Thumbs up is toggled " + (thumbsUp ? "on" : "off"));
 *
 * var stars = Nuvola.actions.getState("rating");
 * console.log("Number of stars: " + stars);
 * ```
 */
Actions.getState = function(name)
{
    return Nuvola._callIpcMethodSync("/nuvola/actions/get-state", [name]);
}

/**
 * Set current state of toggle or radio actions.
 *
 * **Note:** consider use of method @link{Actions.updateState} that is more effective, because it performs caching of
 * states and updates them only if necessary.
 *
 * @param String name      action name
 * @param variant state    current state: ``true/false`` for toggle actions, one of stateId entries of radio actions
 *
 * ```
 * // toggle thumbs-up off
 * Nuvola.actions.setState("thumbs-up", false);
 *
 * // Set 5 stars
 * Nuvola.actions.setState("rating", 5);
 * ```
 */
Actions.setState = function(name, state)
{
    Nuvola._callIpcMethodVoid("/nuvola/actions/set-state", [name, state]);
}

/**
 * Update of action state
 *
 * This method uses cache of action states to update them only if necessary.
 *
 * @param String name      action name
 * @param variant state    current state: ``true/false`` for toggle actions, one of stateId entries of radio actions
 */
Actions.updateState = function(name, state)
{
    if (state !== this.statesCache[name])
    {
        this.statesCache[name] = state;
        this.setState(name, state);
    }
}

/**
 * Bulk update of action states
 *
 * This method uses cache of action states to update them only if necessary.
 *
 * @param Object states    mapping of ``action name``: ``state``
 */
Actions.updateStates = function(states)
{
    for (var name in states)
        this.updateState(name, states[name]);
}

/**
 * Activate (invoke) action
 *
 * @param String name                   action name
 * @param optional Variant parameter    parameter to the action
 */
Actions.activate = function(name, parameter)
{
    Nuvola._callIpcMethodVoid("/nuvola/actions/activate", [name, parameter == null ? null : parameter]);
}

/**
 * Bind HTML button to an action
 *
 * The button.disabled and action.enabled properties are synchronized and action is activated when button is clicked.
 *
 * @param HTMLButton button             HTML button element
 * @param String name                   action name
 * @param optional Variant parameter    parameter to the action
 *
 * ```
 * var navigateBack = Nuvola.makeElement("button", null, "<");
 * var elm = document.getElementById("bar");
 * elm.appendChild(navigateBack);
 * Nuvola.actions.bindButton(navigateBack, Nuvola.BrowserAction.GO_BACK);
 * ```
 */
Actions.bindButton = function(button, name, parameter) {
    this.buttons[name] = button;
    this.isEnabledAsync(name).then(function(enabled) {
        button.disabled = !enabled;
    });

    var that = this;
    button.addEventListener('click', function() {
        that.activate(name, parameter);
    });
}

Actions._onActionEnabledChanged = function(object, name, enabled)
{
    if (this.buttons[name])
        this.buttons[name].disabled = !enabled;
}

// export public items
Nuvola.Actions = Actions;

/**
 * Instance object of @link{Actions} prototype connected to Nuvola backend.
 */
Nuvola.actions = $object(Actions);
<commit_msg>Add Actions.getStateAsync<commit_after>/*
 * Copyright 2014-2017 Jiří Janoušek <janousek.jiri@gmail.com>
 *
 * 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.
 */

require("prototype");
require("signals");

/**
 * Manages actions
 *
 * Actions can be shown in various user interface components such as menu, tray icon menu, Unity HUD, Unity Laucher
 * Quicklist, or invoked by keyboard shortcut, remote control, etc.
 *
 * Some actions are provided by the Nuvola Player backend (e. g. @link{BrowserAction|browser actions}),
 * some are created by JavaScript API objects (e. g. @link{PlayerAction|media player actions}) and
 * web app integration scripts can create custom actions with @link{Actions.addAction} or @link{Actions.addRadioAction}.
 */
var Actions = $prototype(null, SignalsMixin);

/**
 * Initializes new Actions object
 */
Actions.$init = function()
{
    /**
     * Emitted when an action is activated.
     *
     * @param String name      action name
     * @param Variant param    action parameter, usually null
     *
     * ```
     * MyObject.$init = function()
     * {
     *     Nuvola.actions.connect("ActionActivated", this);
     * }
     *
     * MyObject._onActionActivated = function(emitter, name, param)
     * {
     *     console.log("Action activated: " + name);
     * }
     * ```
     */
    this.addSignal("ActionActivated");

    /**
     * Emitted when an action has been enabled or disabled.
     *
     * @param String name        action name
     * @param Boolean enabled    true if the action is enabled
     *
     * ```
     * MyObject.$init = function()
     * {
     *     Nuvola.actions.connect("ActionEnabledChanged", this);
     * }
     *
     * MyObject._ActionEnabledChanged = function(emitter, name, enabled)
     * {
     *     console.log("Action " + name + " " + (enabled ? "enabled" : "disabled") + ".");
     * }
     * ```
     */
    this.addSignal("ActionEnabledChanged");
    this.connect("ActionActivated", this);
    this.connect("ActionEnabledChanged", this);
    this.buttons = {};
    this.enabledFlagsCache = {};
    this.statesCache = {};
}

/**
 * Adds new simple or toggle action.
 *
 * when action is activated, signal ActionActivated is emitted.
 *
 * @param String group               action group, e.g. "playback"
 * @param String scope               action scope, use "win" for window-specific actions (preferred) or "app" for
 *                                   app-specific actions
 * @param String name                action name, should be in ``dash-separated-lower-case``, e.g. ``toggle-play``
 * @param String|null label          label shown in user interface, e.g. ``Play``
 * @param String|null mnemo_label    label shown in user interface with keyboard navigation using Alt key and letter
 *                                   prefixed with underscore, e.g. Alt+p for ``_Play``
 * @param String|null icon           icon name for action
 * @param String|null keybinding     in-app keyboard shortcut, e.g. ``<ctrl>P``
 * @param Boolean|null state         ``null`` for simple actions, ``true``/``false`` for toggle actions (on/off)
 *
 * ```
 * // Add new simple action ``play`` with icon ``media-playback-start``
 * Nuvola.actions.addAction("playback", "win", "play", "Play", null, "media-playback-start", null);
 *
 * // Add new toggle action ``thumbs-up`` with initial state ``true`` (on)
 * Nuvola.actions.addAction("playback", "win", "thumbs-up", "Thumbs up", null, null, null, true);
 * ```
 */
Actions.addAction = function(group, scope, name, label, mnemo_label, icon, keybinding, state)
{
    var state = state !== undefined ? state: null;
    Nuvola._callIpcMethodVoid("/nuvola/actions/add-action",
     [group, scope, name, label || "", mnemo_label || "", icon || "", keybinding || "", state]);
}

/**
 * Adds new radio action (action with multiple states/option like old radios)
 *
 * when action is activated, signal ActionActivated is emitted.
 *
 * @param String group       action group, e.g. "playback"
 * @param String scope       action scope, use "win" for window-specific actions (preferred) or "app" for
 *                           app-specific actions
 * @param String name        action name, should be in ``dash-separated-lower-case``, e.g. ``toggle-play``
 * @param variant stateId    initial state of the action. must be one of states specified in ``options`` array
 * @param Array options      array of options definition in form ``[stateId, label, mnemo_label, icon, keybinding]``.
 *                           ``stateId`` is unique identifier (Number or String), other parameters are described
 *                           in ``addAction`` method.
 *
 * ```
 * // define rating options - 5 states with state id 0-5 representing 0-5 stars
 * var ratingOptions = [
 *     // stateId, label, mnemo_label, icon, keybinding
 *     [0, "Rating: 0 stars", null, null, null, null],
 *     [1, "Rating: 1 star", null, null, null, null],
 *     [2, "Rating: 2 stars", null, null, null, null],
 *     [3, "Rating: 3 stars", null, null, null, null],
 *     [4, "Rating: 4 stars", null, null, null, null],
 *     [5, "Rating: 5 stars", null, null, null, null]
 * ];
 *
 * // Add new radio action named ``rating`` with initial state ``0`` (0 stars)
 * Nuvola.actions.addRadioAction("playback", "win", "rating", 0, ratingOptions);
 * ```
 */
Actions.addRadioAction = function(group, scope, name, stateId, options)
{
    Nuvola._callIpcMethodVoid("/nuvola/actions/add-radio-action", [group, scope, name, stateId, options]);
}

Actions._onActionActivated = function(arg1, action)
{
    console.log("JS API: Action activated: " + action);
}

/**
 * Checks whether action is enabled
 *
 * @param String name    action name
 * @deprecated since Nuvola 4.8. Use async variant instead.
 * @return true is action is enabled, false otherwise
 */
Actions.isEnabled = function(name) {
    Nuvola.log("Actions.isEnabled is deprecated since Nuvola 4.8. Use async variant instead.");
    return Nuvola._callIpcMethodSync("/nuvola/actions/is-enabled", [name]);
}

/**
 * Checks whether action is enabled
 *
 * @since Nuvola 4.8
 * @async
 * @param String name    action name
 * @return true is action is enabled, false otherwise
 */
Actions.isEnabledAsync = function(name) {
    return Nuvola.Async.call("/nuvola/actions/is-enabled", [name]);
}

/**
 * Sets whether action is enabled
 *
 * **Note:** consider use of method @link{Actions.updateEnabledFlag} that is more effective, because it performs
 * caching of enabled flags and updates them only if necessary.
 *
 * @param String name        action name
 * @param Boolean enabled    true is action is enabled, false otherwise
 */
Actions.setEnabled = function(name, enabled)
{
    Nuvola._callIpcMethodVoid("/nuvola/actions/set-enabled", [name, enabled]);
}

/**
 * Update action enabled flag
 *
 * This method uses cache of action enabled flags to update them only if necessary.
 *
 * @param String name        action name
 * @param Boolean enabled    true is action is enabled, false otherwise
 */
Actions.updateEnabledFlag = function(name, enabled)
{
    if (enabled !== this.enabledFlagsCache[name])
    {
        this.enabledFlagsCache[name] = enabled;
        this.setEnabled(name, enabled);
    }
}

/**
 * Bulk update of action enabled flags
 *
 * This method uses cache of action enabled flags to update them only if necessary.
 *
 * @param Object enabledFlags    mapping of ``action name``: ``enabled flag``
 */
Actions.updateEnabledFlags = function(enabledFlags)
{
    for (var name in enabledFlags)
        this.updateEnabledFlag(name, enabledFlags[name]);
}

/**
 * Get current state of toggle or radio actions.
 *
 * @deprecated since Nuvola 4.8. Use async variant instead.
 * @param String name    action name
 * @return current state: ``true/false`` for toggle actions, one of stateId entries of radio actions
 *
 * ```
 * var thumbsUp = Nuvola.actions.getState("thumbs-up");
 * console.log("Thumbs up is toggled " + (thumbsUp ? "on" : "off"));
 *
 * var stars = Nuvola.actions.getState("rating");
 * console.log("Number of stars: " + stars);
 * ```
 */
Actions.getState = function(name) {
    Nuvola.log("Actions.getState is deprecated since Nuvola 4.8. Use async variant instead.");
    return Nuvola._callIpcMethodSync("/nuvola/actions/get-state", [name]);
}

/**
 * Get current state of toggle or radio actions.
 *
 * @since Nuvola 4.8
 * @async
 * @param String name    action name
 * @return current state: ``true/false`` for toggle actions, one of stateId entries of radio actions
 *
 * ```
 * Nuvola.actions.getStateAsync("thumbs-up").then(function(state) {
 *     console.log("Thumbs up is toggled " + (state ? "on" : "off"));
 * });
 *
 * Nuvola.actions.getStateAsync("rating").then(function(stars) {
 *     console.log("Number of stars: " + stars);
 * });
 * ```
 */
Actions.getStateAsync = function(name) {
    return Nuvola.Async.call("/nuvola/actions/get-state", [name]);
}

/**
 * Set current state of toggle or radio actions.
 *
 * **Note:** consider use of method @link{Actions.updateState} that is more effective, because it performs caching of
 * states and updates them only if necessary.
 *
 * @param String name      action name
 * @param variant state    current state: ``true/false`` for toggle actions, one of stateId entries of radio actions
 *
 * ```
 * // toggle thumbs-up off
 * Nuvola.actions.setState("thumbs-up", false);
 *
 * // Set 5 stars
 * Nuvola.actions.setState("rating", 5);
 * ```
 */
Actions.setState = function(name, state)
{
    Nuvola._callIpcMethodVoid("/nuvola/actions/set-state", [name, state]);
}

/**
 * Update of action state
 *
 * This method uses cache of action states to update them only if necessary.
 *
 * @param String name      action name
 * @param variant state    current state: ``true/false`` for toggle actions, one of stateId entries of radio actions
 */
Actions.updateState = function(name, state)
{
    if (state !== this.statesCache[name])
    {
        this.statesCache[name] = state;
        this.setState(name, state);
    }
}

/**
 * Bulk update of action states
 *
 * This method uses cache of action states to update them only if necessary.
 *
 * @param Object states    mapping of ``action name``: ``state``
 */
Actions.updateStates = function(states)
{
    for (var name in states)
        this.updateState(name, states[name]);
}

/**
 * Activate (invoke) action
 *
 * @param String name                   action name
 * @param optional Variant parameter    parameter to the action
 */
Actions.activate = function(name, parameter)
{
    Nuvola._callIpcMethodVoid("/nuvola/actions/activate", [name, parameter == null ? null : parameter]);
}

/**
 * Bind HTML button to an action
 *
 * The button.disabled and action.enabled properties are synchronized and action is activated when button is clicked.
 *
 * @param HTMLButton button             HTML button element
 * @param String name                   action name
 * @param optional Variant parameter    parameter to the action
 *
 * ```
 * var navigateBack = Nuvola.makeElement("button", null, "<");
 * var elm = document.getElementById("bar");
 * elm.appendChild(navigateBack);
 * Nuvola.actions.bindButton(navigateBack, Nuvola.BrowserAction.GO_BACK);
 * ```
 */
Actions.bindButton = function(button, name, parameter) {
    this.buttons[name] = button;
    this.isEnabledAsync(name).then(function(enabled) {
        button.disabled = !enabled;
    });

    var that = this;
    button.addEventListener('click', function() {
        that.activate(name, parameter);
    });
}

Actions._onActionEnabledChanged = function(object, name, enabled)
{
    if (this.buttons[name])
        this.buttons[name].disabled = !enabled;
}

// export public items
Nuvola.Actions = Actions;

/**
 * Instance object of @link{Actions} prototype connected to Nuvola backend.
 */
Nuvola.actions = $object(Actions);
<|endoftext|>


<commit_before>// ==UserScript==
// @name       mail.yeah.net adds remover
// @namespace  http://gubnota.github.io/fenki_js_helpers/plugin/monkey/mail_yeah_net_adds/
// @version    0.1.2
// @downloadURL http://gubnota.github.io/fenki_js_helpers/plugin/monkey/mail_yeah_net_adds/main.user.js
// @description removes adds block(s) in mail.yeah.net
// @include      http://mail.yeah.net/*
// @copyright  2015+, Vladislav Muravyev
// @author Vladislav Muravyev
// @grant          none
// @icon http://gubnota.github.io/fenki_js_helpers/plugin/monkey/mail_yeah_net_adds/yeah.ico
// @run-at document-end
// ==/UserScript==
(function(){
var t = setInterval(function(){
var el = document.querySelectorAll('.xd0,.gWel-promt,.gWel-tabs-promt,.gWel-tabs-panel,.gWel-tabs-panel-hasBg,#_mail_tabitem_2_34,.js-component-popup'); for(var i =0; i<el.length; i++){
    el[i].remove();//clearInterval(t);
}

var el = document.querySelectorAll('#dvMultiTab li'); for(var i=0; i<el.length; i++) {
	if(el[i].title.match('一元夺宝')) el[i].remove();
}
var el = document.querySelectorAll('.tN0'); for(var i=0; i<el.length; i++) {
if(el[i].id.match('_AdDiv')) el[i].remove();
}
var el = document.querySelectorAll('.sV0'); for(var i=0; i<el.length; i++) {
if(el[i].innerHTML.match('马上领取|免费|广告')) el[i].remove();
}
var el = document.querySelectorAll('.En0'); for(var i=0; i<el.length; i++) {
if(el[i].innerHTML.match('马上领取|免费|广告')) el[i].remove();
}
    },100);
})();
<commit_msg>yeah mail ad blocker<commit_after>// ==UserScript==
// @name       mail.yeah.net adds remover
// @namespace  http://gubnota.github.io/fenki_js_helpers/plugin/monkey/mail_yeah_net_adds/
// @version    0.1.3
// @downloadURL http://gubnota.github.io/fenki_js_helpers/plugin/monkey/mail_yeah_net_adds/main.user.js
// @description removes adds block(s) in mail.yeah.net
// @include      http://mail.yeah.net/*
// @copyright  2015+, Vladislav Muravyev
// @author Vladislav Muravyev
// @grant          none
// @icon http://gubnota.github.io/fenki_js_helpers/plugin/monkey/mail_yeah_net_adds/yeah.ico
// @run-at document-end
// ==/UserScript==
(function(){
var t = setInterval(function(){
var el = document.querySelectorAll('.xd0,.gWel-promt,.gWel-tabs-promt,.gWel-tabs-panel,.gWel-tabs-panel-hasBg,#_mail_tabitem_2_34,.js-component-popup'); for(var i =0; i<el.length; i++){
    el[i].remove();//clearInterval(t);
}

var el = document.querySelectorAll('.nui-block'); for(var i=0; i<el.length; i++) {
	el[i].remove();
}

var el = document.querySelectorAll('#dvMultiTab li'); for(var i=0; i<el.length; i++) {
	if(el[i].title.match('一元夺宝')) el[i].remove();
}
var el = document.querySelectorAll('.tN0'); for(var i=0; i<el.length; i++) {
if(el[i].id.match('_AdDiv')) el[i].remove();
}
var el = document.querySelectorAll('.sV0'); for(var i=0; i<el.length; i++) {
if(el[i].innerHTML.match('马上领取|免费|广告')) el[i].remove();
}
var el = document.querySelectorAll('.En0'); for(var i=0; i<el.length; i++) {
if(el[i].innerHTML.match('马上领取|免费|广告')) el[i].remove();
}
    },100);
})();
<|endoftext|>


<commit_before>'use strict';

/**
 * Config
 * @todo DRY
 */
module.exports = {
  routes: require('./routes'),
  options: require('./options')
};
<commit_msg>Configuration restructured.<commit_after>'use strict';

/**
 * Config
 * @todo DRY
 */
module.exports = {
  middleware: require('./middleware'),
  routes: require('./routes'),
  redis: require('./redis'),
  options: require('./options')
};
<|endoftext|>


<commit_before>const fs = require('fs');
const defaultChromeOptions = require('../settings/chromeDesktopOptions');
const defaultAndroidChromeOptions = require('../settings/chromeAndroidOptions');
const getViewPort = require('../../support/getViewPort');
const util = require('../../support/util');

module.exports = function(seleniumOptions, browserOptions, options, baseDir) {
  // Fixing save password popup, only on Desktop
  if (!options.android) {
    seleniumOptions.setUserPreferences({
      'profile.password_manager_enable': false,
      'profile.default_content_setting_values.notifications': 2,
      credentials_enable_service: false
    });
  }

  if (options.headless) {
    seleniumOptions.headless();
  }

  // If we run in Docker we need to always use no-sandbox
  if (options.docker) {
    seleniumOptions.addArguments('--no-sandbox');
    seleniumOptions.addArguments('--disable-setuid-sandbox');
  }

  if (options.xvfb && (options.xvfb === true || options.xvfb === 'true')) {
    seleniumOptions.addArguments('--disable-gpu');
  }

  const viewPort = getViewPort(options);
  // If viewport is defined (only on desktop) then set start args
  if (viewPort) {
    seleniumOptions.addArguments('--window-position=0,0');
    seleniumOptions.addArguments('--window-size=' + viewPort.replace('x', ','));
  }

  // If we are recording responses and we also block on domain
  if (
    browserOptions.blockDomainsExcept &&
    browserOptions.webPageReplayHostResolver &&
    browserOptions.webPageReplayRecord
  ) {
    const firstPartyDomains = util.toArray(browserOptions.blockDomainsExcept);
    let excludes = '';
    for (let domain of firstPartyDomains) {
      excludes += ` MAP ${domain}:80 127.0.0.1:${browserOptions.webPageReplayHTTPPort},`;
      excludes += ` MAP ${domain}:443 127.0.0.1:${browserOptions.webPageReplayHTTPSPort},`;
    }
    excludes += ' EXCLUDE localhost';

    seleniumOptions.addArguments('--host-resolver-rules=' + excludes);
  } // If we are replaying with WebPageReplay
  else if (browserOptions.webPageReplayHostResolver) {
    seleniumOptions.addArguments(
      `--host-resolver-rules= "MAP *:80 127.0.0.1:${browserOptions.webPageReplayHTTPPort},  MAP *:443 127.0.0.1:${browserOptions.webPageReplayHTTPSPort}, EXCLUDE localhost"`
    );
  } // If we do not use WebPageReplay but wanna block on domain
  else if (browserOptions.blockDomainsExcept) {
    let excludes = '';
    let excludesDomains = util.toArray(browserOptions.blockDomainsExcept);
    for (let domain of excludesDomains) {
      excludes += 'MAP * 127.0.0.1, EXCLUDE ' + domain + ',';
    }
    seleniumOptions.addArguments('--host-resolver-rules=' + excludes);
  }

  if (options.extension) {
    const extensions = !Array.isArray(options.extension)
      ? [options.extension]
      : options.extension;
    for (const extension of extensions) {
      seleniumOptions.addExtensions(
        fs.readFileSync(extension, { encoding: 'base64' })
      );
    }
  }

  const perfLogConf = { enableNetwork: true, enablePage: true };
  seleniumOptions.setPerfLoggingPrefs(perfLogConf);

  if (options.userAgent) {
    seleniumOptions.addArguments('--user-agent=' + options.userAgent);
  }

  if (browserOptions.ignoreCertificateErrors) {
    seleniumOptions.addArguments('--ignore-certificate-errors');
  }

  if (browserOptions.collectNetLog) {
    const dir = !browserOptions.android ? baseDir : '/data/local/tmp';
    seleniumOptions.addArguments(`--log-net-log=${dir}/chromeNetlog.json`);
    const level = browserOptions.netLogCaptureMode || 'IncludeSensitive';
    seleniumOptions.addArguments(`--net-log-capture-mode=${level}`);
  }

  if (browserOptions.android) {
    seleniumOptions.addArguments(defaultAndroidChromeOptions);
    seleniumOptions.addArguments(
      '--remote-debugging-port=' + options.devToolsPort
    );
  } else {
    seleniumOptions.addArguments(defaultChromeOptions);
  }

  if (browserOptions.args) {
    seleniumOptions.addArguments(browserOptions.args);
  }

  if (browserOptions.binaryPath) {
    seleniumOptions.setChromeBinaryPath(browserOptions.binaryPath);
  }

  if (browserOptions.mobileEmulation) {
    seleniumOptions.setMobileEmulation(browserOptions.mobileEmulation);
  }

  const connectivity = options.connectivity || {};

  if (
    connectivity.engine === 'tsproxy' &&
    connectivity.tsproxy &&
    connectivity.profile !== 'native'
  ) {
    if (!connectivity.tsproxy.bind) {
      // If you run tsproxy on desktop instead of throttle or Docker networs
      // Configure SOCKS proxy, see https://www.chromium.org/developers/design-documents/network-stack/socks-proxy
      seleniumOptions.addArguments(
        '--proxy-server=socks5://localhost:' + connectivity.tsproxy.port
      );
      seleniumOptions.addArguments(
        '--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE localhost"'
      );
    } else {
      // If you run on Android and want to use TSProxy on your desktop
      seleniumOptions.addArguments(
        '--proxy-server=socks://' +
          connectivity.tsproxy.bind +
          ':' +
          connectivity.tsproxy.port
      );
    }
  }

  // See https://bugs.chromium.org/p/chromium/issues/detail?id=818483
  // Coming again in Chrome 76
  seleniumOptions.excludeSwitches('enable-automation');

  const android = browserOptions.android;
  if (android) {
    if (android.package) {
      seleniumOptions.androidPackage(android.package);
      if (android.activity) {
        seleniumOptions.androidActivity(android.activity);
        if (android.process) {
          seleniumOptions.androidProcess(android.process);
        }
      }
    } else {
      seleniumOptions.androidChrome();
    }
    seleniumOptions.androidDeviceSerial(android.deviceSerial);
  }

  return seleniumOptions;
};
<commit_msg>Log all Selenium options for Chrome as debug.<commit_after>const fs = require('fs');
const defaultChromeOptions = require('../settings/chromeDesktopOptions');
const defaultAndroidChromeOptions = require('../settings/chromeAndroidOptions');
const getViewPort = require('../../support/getViewPort');
const util = require('../../support/util');
const log = require('intel').getLogger('browsertime.chrome');

module.exports = function(seleniumOptions, browserOptions, options, baseDir) {
  // Fixing save password popup, only on Desktop
  if (!options.android) {
    seleniumOptions.setUserPreferences({
      'profile.password_manager_enable': false,
      'profile.default_content_setting_values.notifications': 2,
      credentials_enable_service: false
    });
  }

  if (options.headless) {
    seleniumOptions.headless();
  }

  // If we run in Docker we need to always use no-sandbox
  if (options.docker) {
    seleniumOptions.addArguments('--no-sandbox');
    seleniumOptions.addArguments('--disable-setuid-sandbox');
  }

  if (options.xvfb && (options.xvfb === true || options.xvfb === 'true')) {
    seleniumOptions.addArguments('--disable-gpu');
  }

  const viewPort = getViewPort(options);
  // If viewport is defined (only on desktop) then set start args
  if (viewPort) {
    seleniumOptions.addArguments('--window-position=0,0');
    seleniumOptions.addArguments('--window-size=' + viewPort.replace('x', ','));
  }

  // If we are recording responses and we also block on domain
  if (
    browserOptions.blockDomainsExcept &&
    browserOptions.webPageReplayHostResolver &&
    browserOptions.webPageReplayRecord
  ) {
    const firstPartyDomains = util.toArray(browserOptions.blockDomainsExcept);
    let excludes = '';
    for (let domain of firstPartyDomains) {
      excludes += ` MAP ${domain}:80 127.0.0.1:${browserOptions.webPageReplayHTTPPort},`;
      excludes += ` MAP ${domain}:443 127.0.0.1:${browserOptions.webPageReplayHTTPSPort},`;
    }
    excludes += ' EXCLUDE localhost';

    seleniumOptions.addArguments('--host-resolver-rules=' + excludes);
  } // If we are replaying with WebPageReplay
  else if (browserOptions.webPageReplayHostResolver) {
    seleniumOptions.addArguments(
      `--host-resolver-rules= "MAP *:80 127.0.0.1:${browserOptions.webPageReplayHTTPPort},  MAP *:443 127.0.0.1:${browserOptions.webPageReplayHTTPSPort}, EXCLUDE localhost"`
    );
  } // If we do not use WebPageReplay but wanna block on domain
  else if (browserOptions.blockDomainsExcept) {
    let excludes = '';
    let excludesDomains = util.toArray(browserOptions.blockDomainsExcept);
    for (let domain of excludesDomains) {
      excludes += 'MAP * 127.0.0.1, EXCLUDE ' + domain + ',';
    }
    seleniumOptions.addArguments('--host-resolver-rules=' + excludes);
  }

  if (options.extension) {
    const extensions = !Array.isArray(options.extension)
      ? [options.extension]
      : options.extension;
    for (const extension of extensions) {
      seleniumOptions.addExtensions(
        fs.readFileSync(extension, { encoding: 'base64' })
      );
    }
  }

  const perfLogConf = { enableNetwork: true, enablePage: true };
  seleniumOptions.setPerfLoggingPrefs(perfLogConf);

  if (options.userAgent) {
    seleniumOptions.addArguments('--user-agent=' + options.userAgent);
  }

  if (browserOptions.ignoreCertificateErrors) {
    seleniumOptions.addArguments('--ignore-certificate-errors');
  }

  if (browserOptions.collectNetLog) {
    const dir = !browserOptions.android ? baseDir : '/data/local/tmp';
    seleniumOptions.addArguments(`--log-net-log=${dir}/chromeNetlog.json`);
    const level = browserOptions.netLogCaptureMode || 'IncludeSensitive';
    seleniumOptions.addArguments(`--net-log-capture-mode=${level}`);
  }

  if (browserOptions.android) {
    seleniumOptions.addArguments(defaultAndroidChromeOptions);
    seleniumOptions.addArguments(
      '--remote-debugging-port=' + options.devToolsPort
    );
  } else {
    seleniumOptions.addArguments(defaultChromeOptions);
  }

  if (browserOptions.args) {
    seleniumOptions.addArguments(browserOptions.args);
  }

  if (browserOptions.binaryPath) {
    seleniumOptions.setChromeBinaryPath(browserOptions.binaryPath);
  }

  if (browserOptions.mobileEmulation) {
    seleniumOptions.setMobileEmulation(browserOptions.mobileEmulation);
  }

  const connectivity = options.connectivity || {};

  if (
    connectivity.engine === 'tsproxy' &&
    connectivity.tsproxy &&
    connectivity.profile !== 'native'
  ) {
    if (!connectivity.tsproxy.bind) {
      // If you run tsproxy on desktop instead of throttle or Docker networs
      // Configure SOCKS proxy, see https://www.chromium.org/developers/design-documents/network-stack/socks-proxy
      seleniumOptions.addArguments(
        '--proxy-server=socks5://localhost:' + connectivity.tsproxy.port
      );
      seleniumOptions.addArguments(
        '--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE localhost"'
      );
    } else {
      // If you run on Android and want to use TSProxy on your desktop
      seleniumOptions.addArguments(
        '--proxy-server=socks://' +
          connectivity.tsproxy.bind +
          ':' +
          connectivity.tsproxy.port
      );
    }
  }

  // See https://bugs.chromium.org/p/chromium/issues/detail?id=818483
  // Coming again in Chrome 76
  seleniumOptions.excludeSwitches('enable-automation');

  const android = browserOptions.android;
  if (android) {
    if (android.package) {
      seleniumOptions.androidPackage(android.package);
      if (android.activity) {
        seleniumOptions.androidActivity(android.activity);
        if (android.process) {
          seleniumOptions.androidProcess(android.process);
        }
      }
    } else {
      seleniumOptions.androidChrome();
    }
    seleniumOptions.androidDeviceSerial(android.deviceSerial);
  }

  log.debug('Setting the following Selenium options: %j', seleniumOptions);
  return seleniumOptions;
};
<|endoftext|>


<commit_before>// self-bound function
macro $fn {
  rule { {$body ...} } => {
    $fn () {$body ...}
  }
  rule { $args { $body... } } => {
    (function $args { $body...}).bind(this)
  }
}

// finds --> recursively within a block and replaces it with a call to the given callback
macro $arrows {
  // recurse into () / {} / [] while leaving the delims themselves intact
  rule { ($n ($a...)) } => {( $arrows($n $a...) )}
  rule { ($n {$a...}) } => {{ $arrows($n $a...) }}
  rule { ($n [$a...]) } => {[ $arrows($n $a...) ]}

  // --> becomes $n
  rule { ($n --> $b...) } => { $n $arrows($n $b...) }

  // if nothing else matches return token unmodified
  rule { ($n $a) } => { $a }

  // only process one arg at a time
  rule { ($n $a $b...) } => {
    $arrows($n $a)
    $arrows($n $b...)
  }

  // $arrows( _c ) and $arrows() are no-ops
  rule { ($n) } => {}
  rule { () } => {}
}

// might not be usable
macro $async_block {
  rule { $block $rest ... } => {
    var _rest = $fn{ $rest ... };
    $arrows(_rest $block)
  }
}

macro $async_wait {
  rule {
    { $body ... } $rest ...
  } => {
    var _rest = $fn{ $rest ... }
    $arrows(_rest $body ...)
  }
}

macro $async_tick {
  // swallow (optional) trailing ; on $async:tick;
  rule { ; $rest ... } => { $async_tick $rest ... }
  rule { $rest ... } => {
    process.nextTick($fn{ $rest ... });
  }
}

macro $async_pause {
  rule { ; $rest ... } => { $async_pause $rest ... }
  rule { $rest ... } => {
    ((typeof process == 'object' && process.nextTick) ?
      process.nextTick : setTimeout)($fn{ $rest... }, 0)
  }
}

macro $async_sleep {
  rule { ( $x sec ) $rest ... } => {
    $async_sleep( ($x * 1000) ) $rest ...
  }
  rule { ( $x s ) $rest ... } => {
    $async_sleep( $x sec ) $rest ...
  }
  rule { ( $x ms ) $rest ... } => {
    $async_sleep( $x ) $rest ...
  }
  rule { ( $x ) $rest ... } => {
    setTimeout($fn{ $rest ... }, $x);
  }
}

// i want to try to refactor this to use $arrows
macro $async_var {
  rule { $name:ident = $cmd:expr $rest ... } => {
    $async_var ( --> , $name) = $cmd $rest ...
  }

  // with error callback
  rule {
    ( $argl ... --> $argr ... ) =
    $cmd ... ( $cmdl ...  --> $callback $cmdr ... ) ;
    $rest ...
  } => {
    $cmd ... ( $cmdl ...
      ($fn ( $argl ... error $argr ... ) {
        if ( error ) { return $callback ( error ); }
        $rest ...
      })
    $cmdr ... ) ;
  }

  // for no error callback
  rule {
    ( $args (,) ... ) = $cmd ...
    ($cmdl ... --> $cmdr ... ) ; $rest ...
  } => {
    $cmd ... ( $cmdl ... ($fn ( $args (,) ... ) { $rest ... }) $cmdr ... )
  }
}

macro $async {
  rule { : var   } => { $async_var   }
  rule { : wait  } => { $async_wait  }
  rule { : pause } => { $async_pause }
  rule { : tick  } => { $async_tick  }
  rule { : sleep } => { $async_sleep }
  rule { : block } => { $async_block }
}

export $async
<commit_msg>:sleep() with no arg becomes :pause<commit_after>// self-bound function
macro $fn {
  rule { {$body ...} } => {
    $fn () {$body ...}
  }
  rule { $args { $body... } } => {
    (function $args { $body...}).bind(this)
  }
}

// finds --> recursively within a block and replaces it with a call to the given callback
macro $arrows {
  // recurse into () / {} / [] while leaving the delims themselves intact
  rule { ($n ($a...)) } => {( $arrows($n $a...) )}
  rule { ($n {$a...}) } => {{ $arrows($n $a...) }}
  rule { ($n [$a...]) } => {[ $arrows($n $a...) ]}

  // --> becomes $n
  rule { ($n --> $b...) } => { $n $arrows($n $b...) }

  // if nothing else matches return token unmodified
  rule { ($n $a) } => { $a }

  // only process one arg at a time
  rule { ($n $a $b...) } => {
    $arrows($n $a)
    $arrows($n $b...)
  }

  // $arrows( _c ) and $arrows() are no-ops
  rule { ($n) } => {}
  rule { () } => {}
}

// might not be usable
macro $async_block {
  rule { $block $rest ... } => {
    var _rest = $fn{ $rest ... };
    $arrows(_rest $block)
  }
}

macro $async_wait {
  rule {
    { $body ... } $rest ...
  } => {
    var _rest = $fn{ $rest ... }
    $arrows(_rest $body ...)
  }
}

macro $async_tick {
  // swallow (optional) trailing ; on $async:tick;
  rule { ; $rest ... } => { $async_tick $rest ... }
  rule { $rest ... } => {
    process.nextTick($fn{ $rest ... });
  }
}

macro $async_pause {
  rule { ; $rest ... } => { $async_pause $rest ... }
  rule { $rest ... } => {
    ((typeof process == 'object' && process.nextTick) ?
      process.nextTick : setTimeout)($fn{ $rest... }, 0)
  }
}

macro $async_sleep {
  rule { ($x...) ; $rest ... } => {
    $async_sleep($x...) $rest ...
  }
  rule { ($x sec) $rest ... } => {
    $async_sleep(($x * 1000)) $rest ...
  }
  rule { ($x s) $rest ... } => {
    $async_sleep($x sec) $rest ...
  }
  rule { ($x ms) $rest ... } => {
    $async_sleep($x) $rest ...
  }
  rule { ($x) $rest ... } => {
    setTimeout($fn{ $rest ... }, $x);
  }
  rule { () } => { $async_pause }
}

// i want to try to refactor this to use $arrows
macro $async_var {
  rule { $name:ident = $cmd:expr $rest ... } => {
    $async_var ( --> , $name) = $cmd $rest ...
  }

  // with error callback
  rule {
    ( $argl ... --> $argr ... ) =
    $cmd ... ( $cmdl ...  --> $callback $cmdr ... ) ;
    $rest ...
  } => {
    $cmd ... ( $cmdl ...
      ($fn ( $argl ... error $argr ... ) {
        if ( error ) { return $callback ( error ); }
        $rest ...
      })
    $cmdr ... ) ;
  }

  // for no error callback
  rule {
    ( $args (,) ... ) = $cmd ...
    ($cmdl ... --> $cmdr ... ) ; $rest ...
  } => {
    $cmd ... ( $cmdl ... ($fn ( $args (,) ... ) { $rest ... }) $cmdr ... )
  }
}

macro $async {
  rule { : var   } => { $async_var   }
  rule { : wait  } => { $async_wait  }
  rule { : pause } => { $async_pause }
  rule { : tick  } => { $async_tick  }
  rule { : sleep } => { $async_sleep }
  rule { : block } => { $async_block }
}

export $async
<|endoftext|>


<commit_before>/** @babel */

import {CompositeDisposable} from 'atom'

import React from 'react'

import StatusBarTileController from './status-bar-tile-controller'

/**
 * `EtchWrapper` is a React component that renders Etch components
 * and correctly manages their lifecycles as the application progresses.
 *
 *    <EtchWrapper type="span" reattachDomNode={true} className="wrapper">
 *      <EtchComponent etchProp={stuff} />
 *    </EtchWrapper>
 *
 * The `type` property specifies the DOM node type to wrap around the
 * Etch component's element, and defaults to 'div'. Any other props you
 * pass to the wrapper component will be applied to the DOM node.
 *
 * `reattachDomNode` determines whether or not to place the wrapped component
 * element back in the React component's DOM node if we find it's missing;
 * this could happen due to changing the `type` property. If you pass the
 * wrapped component element into a method that moves the element, you should
 * specify `false` for this option.
 *
 * The component takes a single JSX child, which describes the type and props
 * of the Etch component to render. Any time this changes, the wrapper will
 * update (or destroy and recreate) the Etch component as necessary.
 *
 * Note that the component cleans up its own DOM node, and calls
 * `component.destroy(false)` (if your component has a `destroy` method)
 * and you should pass the `false` as the second argument to
 * `etch.destroy(this)` (e.g. `etch.destroy(this, false)`) inside your
 * component instance.
 *
 * The component instance is available at `this.getWrappedComponent` if you need
 * to call methods on it from the outside (though you should really consider
 * setting a prop instead. ;)
 */
class EtchWrapper extends React.Component {
  static propTypes = {
    children: React.PropTypes.element.isRequired,
    type: React.PropTypes.string,
    reattachDomNode: React.PropTypes.bool,
  }

  static defaultProps = {
    type: 'div',
    reattachDomNode: true,
  }

  componentDidMount () {
    this.createComponent(this.getWrappedComponentDetails(this.props.children))
  }

  componentWillReceiveProps (newProps) {
    const oldDetails = this.getWrappedComponentDetails(this.props.children)
    const newDetails = this.getWrappedComponentDetails(newProps.children)
    if (oldDetails.type !== newDetails.type) {
      // The wrapped component type changed, so we need to destroy the old
      // component and create a new one of the new type.
      this.destroyComponent()
      this.createComponent(newDetails)
    }
  }

  async componentDidUpdate (prevProps) {
    const oldDetails = this.getWrappedComponentDetails(prevProps.children)
    const newDetails = this.getWrappedComponentDetails(this.props.children)

    if (oldDetails.type === newDetails.type) {
      // We didn't change the wrapped (Etch) component type,
      // so we need to update the instance with the new props.
      await this.updateComponent(this.getWrappedComponentDetails(this.props.children))
    }

    // If we just recreated our DOM node by changing the node type, we
    // need to reattach the wrapped component's element.
    if (this.props.reattachDomNode && !this.container.contains(this.component.element)) {
      this.container.appendChild(this.component.element)
    }
  }

  render () {
    const Type = this.props.type
    const {type, children, reattachDomNode, ...props} = this.props
    return <Type {...props} ref={c => this.container = c} />
  }

  componentWillUnmount () {
    this.destroyComponent()
  }

  getWrappedComponentDetails (ourChildren) {
    // e.g. <EtchWrapper><EtchChild prop={1} other={2}>Hi</EtchChild></EtchWrapper>
    const etchElement = React.Children.toArray(ourChildren)[0]
    // etchElement === {type: EtchChild, props: {prop: 1, other: 2, children: 'Hi'}}
    const {type, props} = etchElement
    // type === EtchChild, props === {prop: 1, other: 2, children: 'Hi'}
    const {children, ...remainingProps} = props
    // children === 'Hi', remainingProps === {prop: 1, other: 2}
    return {type, children, props: remainingProps}
  }

  // For compatability with Atom's ViewProvider
  getElement () {
    return this.container
  }

  // Etch component interactions

  getWrappedComponent () {
    return this.component
  }

  createComponent ({type, props, children}) {
    this.component = new type(props, children)
    this.container.appendChild(this.component.element)
  }

  updateComponent ({props, children}) {
    return this.component.update(props, children)
  }

  destroyComponent () {
    if (this.container.contains(this.component.element)) {
      this.container.removeChild(this.component.element)
    }
    this.component.destroy && this.component.destroy(false)
    delete this.component
  }
}

class StatusBar extends React.Component {
  static propTypes = {
    statusBar: React.PropTypes.object,
    legacy: React.PropTypes.bool,
  }

  componentDidMount () {
    this.consumeStatusBar(this.props)
  }

  componentDidUpdate () {
    this.consumeStatusBar(this.props)
  }

  render () {
    return
 this.container = c}>{this.props.children}

  }

  consumeStatusBar (props) {
    if (this.tile) return
    if (!props.statusBar) return

    if (!props.legacy) {
      props.statusBar.disableGitInfoTile()
    }
    const componentElement = this.container.children[0]
    this.tile = props.statusBar.addRightTile({item: componentElement, priority: -50})
  }
}

export default class GithubPackageController extends React.Component {
  static propTypes = {
    workspace: React.PropTypes.object.isRequired,
    repository: React.PropTypes.object,
    statusBar: React.PropTypes.object,
    legacyStatusBar: React.PropTypes.bool,
  }

  static defaultProps = {
    legacyStatusBar: false,
  }

  constructor (props, context) {
    super(props, context)

    this.subscriptions = new CompositeDisposable()

    // this.changeObserver = process.platform === 'linux'
    //   ? new WorkspaceChangeObserver(window, props.workspace)
    //   : new FileSystemChangeObserver()
  }

  render () {
    return (

        <StatusBar statusBar={this.props.statusBar} legacy={this.props.legacyStatusBar}>
          <EtchWrapper type="span">
            <StatusBarTileController
              workspace={this.props.workspace}
              repository={this.props.repository}
              toggleGitPanel={this.toggleGitPanel}
            />
          </EtchWrapper>
        </StatusBar>

    )
  }
}
<commit_msg>Add Portal component<commit_after>/** @babel */

import {CompositeDisposable} from 'atom'

import React from 'react'
import ReactDom from 'react-dom'

import StatusBarTileController from './status-bar-tile-controller'

/**
 * `EtchWrapper` is a React component that renders Etch components
 * and correctly manages their lifecycles as the application progresses.
 *
 *    <EtchWrapper type="span" reattachDomNode={true} className="wrapper">
 *      <EtchComponent etchProp={stuff} />
 *    </EtchWrapper>
 *
 * The `type` property specifies the DOM node type to wrap around the
 * Etch component's element, and defaults to 'div'. Any other props you
 * pass to the wrapper component will be applied to the DOM node.
 *
 * `reattachDomNode` determines whether or not to place the wrapped component
 * element back in the React component's DOM node if we find it's missing;
 * this could happen due to changing the `type` property. If you pass the
 * wrapped component element into a method that moves the element, you should
 * specify `false` for this option.
 *
 * The component takes a single JSX child, which describes the type and props
 * of the Etch component to render. Any time this changes, the wrapper will
 * update (or destroy and recreate) the Etch component as necessary.
 *
 * Note that the component cleans up its own DOM node, and calls
 * `component.destroy(false)` (if your component has a `destroy` method)
 * and you should pass the `false` as the second argument to
 * `etch.destroy(this)` (e.g. `etch.destroy(this, false)`) inside your
 * component instance.
 *
 * The component instance is available at `this.getWrappedComponent` if you need
 * to call methods on it from the outside (though you should really consider
 * setting a prop instead. ;)
 */
class EtchWrapper extends React.Component {
  static propTypes = {
    children: React.PropTypes.element.isRequired,
    type: React.PropTypes.string,
    reattachDomNode: React.PropTypes.bool,
  }

  static defaultProps = {
    type: 'div',
    reattachDomNode: true,
  }

  componentDidMount () {
    this.createComponent(this.getWrappedComponentDetails(this.props.children))
  }

  componentWillReceiveProps (newProps) {
    const oldDetails = this.getWrappedComponentDetails(this.props.children)
    const newDetails = this.getWrappedComponentDetails(newProps.children)
    if (oldDetails.type !== newDetails.type) {
      // The wrapped component type changed, so we need to destroy the old
      // component and create a new one of the new type.
      this.destroyComponent()
      this.createComponent(newDetails)
    }
  }

  async componentDidUpdate (prevProps) {
    const oldDetails = this.getWrappedComponentDetails(prevProps.children)
    const newDetails = this.getWrappedComponentDetails(this.props.children)

    if (oldDetails.type === newDetails.type) {
      // We didn't change the wrapped (Etch) component type,
      // so we need to update the instance with the new props.
      await this.updateComponent(this.getWrappedComponentDetails(this.props.children))
    }

    // If we just recreated our DOM node by changing the node type, we
    // need to reattach the wrapped component's element.
    if (this.props.reattachDomNode && !this.container.contains(this.component.element)) {
      this.container.appendChild(this.component.element)
    }
  }

  render () {
    const Type = this.props.type
    const {type, children, reattachDomNode, ...props} = this.props
    return <Type {...props} ref={c => this.container = c} />
  }

  componentWillUnmount () {
    this.destroyComponent()
  }

  getWrappedComponentDetails (ourChildren) {
    // e.g. <EtchWrapper><EtchChild prop={1} other={2}>Hi</EtchChild></EtchWrapper>
    const etchElement = React.Children.toArray(ourChildren)[0]
    // etchElement === {type: EtchChild, props: {prop: 1, other: 2, children: 'Hi'}}
    const {type, props} = etchElement
    // type === EtchChild, props === {prop: 1, other: 2, children: 'Hi'}
    const {children, ...remainingProps} = props
    // children === 'Hi', remainingProps === {prop: 1, other: 2}
    return {type, children, props: remainingProps}
  }

  // For compatability with Atom's ViewProvider
  getElement () {
    return this.container
  }

  // Etch component interactions

  getWrappedComponent () {
    return this.component
  }

  createComponent ({type, props, children}) {
    this.component = new type(props, children)
    this.container.appendChild(this.component.element)
  }

  updateComponent ({props, children}) {
    return this.component.update(props, children)
  }

  destroyComponent () {
    if (this.container.contains(this.component.element)) {
      this.container.removeChild(this.component.element)
    }
    this.component.destroy && this.component.destroy(false)
    delete this.component
  }
}

class StatusBar extends React.Component {
  static propTypes = {
    statusBar: React.PropTypes.object,
    legacy: React.PropTypes.bool,
  }

  componentDidMount () {
    this.consumeStatusBar(this.props)
  }

  componentDidUpdate () {
    this.consumeStatusBar(this.props)
  }

  render () {
    return
 this.container = c}>{this.props.children}

  }

  consumeStatusBar (props) {
    if (this.tile) return
    if (!props.statusBar) return

    if (!props.legacy) {
      props.statusBar.disableGitInfoTile()
    }
    const componentElement = this.container.children[0]
    this.tile = props.statusBar.addRightTile({item: componentElement, priority: -50})
  }
}

/**
 * `Portal` is a mechanism for rendering a React subtree at a different place
 * in the DOM.
 *
 *    <Portal type="span" className="portal-class" appendNode={true}>
 *      <Stuff />
 *    </Portal>
 *
 * Given the above example, there will be a span with the class "portal-class"
 * created and appended to the document body, and then `<Stuff />` will be
 * rendered into it. Note that this uses `unstable_renderSubtreeIntoContainer`
 * to preserve context in the subtree.
 *
 * `getComponent()` allows access to the rendered subtree instance
 * (`Stuff` in the example above).
 *
 * Pass `false` (the default) to `appendNode` to skip adding the node to the
 * DOM. `type` defaults to "div" and `className` defaults to
 * "react-atom-portal".
 */
class Portal extends React.Component {
  static propTypes = {
    type: React.PropTypes.string,
    className: React.PropTypes.string,
    appendNode: React.PropTypes.bool,
  }

  static defaultProps = {
    type: 'div',
    className: 'react-atom-portal',
    appendNode: false,
  }

  componentDidMount () {
    this.node = document.createElement(this.props.type)
    this.node.className = this.props.className
    if (this.props.appendNode) {
      document.body.appendChild(this.node)
    }
    this.renderPortal(this.props)
  }

  componentWillReceiveProps (newProps) {
    this.renderPortal(newProps)
  }

  componentWillUnmount () {
    ReactDom.unmountComponentAtNode(this.node)
    if (this.props.appendNode) {
      document.body.removeChild(this.node)
    }
  }

  renderPortal (props) {
    this.portal = ReactDom.unstable_renderSubtreeIntoContainer(
      this, props.children, this.node
    )
  }

  render () {
    return null
  }

  getComponent () {
    return this.portal
  }
}

export default class GithubPackageController extends React.Component {
  static propTypes = {
    workspace: React.PropTypes.object.isRequired,
    repository: React.PropTypes.object,
    statusBar: React.PropTypes.object,
    legacyStatusBar: React.PropTypes.bool,
  }

  static defaultProps = {
    legacyStatusBar: false,
  }

  constructor (props, context) {
    super(props, context)

    this.subscriptions = new CompositeDisposable()

    // this.changeObserver = process.platform === 'linux'
    //   ? new WorkspaceChangeObserver(window, props.workspace)
    //   : new FileSystemChangeObserver()
  }

  render () {
    return (

        <StatusBar statusBar={this.props.statusBar} legacy={this.props.legacyStatusBar}>
          <EtchWrapper type="span">
            <StatusBarTileController
              workspace={this.props.workspace}
              repository={this.props.repository}
              toggleGitPanel={this.toggleGitPanel}
            />
          </EtchWrapper>
        </StatusBar>

    )
  }
}
<|endoftext|>


<commit_before>
include("common.jscad");
include("screws.jscad");
include("lattice.jscad");
include("plate.jscad");

var tol = 0.5;
var wallThickness = 2.0;

var sparSpace;
var plateHoleOffset;

function getParameterDefinitions()
{
  return [
    { name: 'showReferences', type: 'choice', values: ["yes", "no"], initial: "yes", caption: "Show References?" },
    { name: 'plateType', type: 'choice', values: ["top","middle","bottom"], initial: "middle", caption: "Plate Type" }
  ];
}

function main(params)
{
    var bodyDiameter = 40;
    var output = [];

    var screw14 = new screw("1/4-20", {head: "pan-square"});
    var nut14 = new nut("1/4-20", {screw: screw14});
    var washerM6 = new washer("M6", {screw: screw14}); // just use M6 washers for now
    var plateThickness = 3.0;

    // Axis
    if ( params.showReferences === 'yes' )
    {
        var extents = [bodyDiameter,bodyDiameter,20];
        output.push(makeAxis(extents));
    }

    if ( params.plateType == "top" )
    {
        var plateArgs = {
            rad: ( screw14.type.head[screw14.head].diameter + (wallThickness * 2) ) / 2.0,
            radMultiplier: 6,
            mountingHoleDiameter: screw14.type.fit.close,
            plateThickness: plateThickness,
            style: 'solid',
            latticeAngle: 45,
            latticeCount: 4,
            cutMountingHoles: false,
        }
    }
    else if ( params.plateType == "middle" )
    {
        var plateArgs = {
            rad: ( screw14.type.head[screw14.head].diameter / 2.0),
            radMultiplier: 1,
            mountingHoleDiameter: screw14.type.fit.free,
            plateThickness: plateThickness,
            style: 'lattice',
            latticeAngle: 33.5,
            latticeCount: 3,
            latticeXOffset: -1.20,
            cutMountingHoles: true,
        }
    }
    else if ( params.plateType == "bottom" )
    {
        var plateArgs = {
            rad: ( nut14.diameter + (wallThickness * 2) ) / 2.0,
            radMultiplier: 6,
            mountingHoleDiameter: screw14.type.fit.close,
            plateThickness: plateThickness,
            style: 'solid',
            cutMountingHoles: false,
        }
    }

    var stirPlate = plate(plateArgs);

    if ( params.plateType == "middle" )
    {
        var motorDiameter = 30;
        var motorSpindleDiameter = 10;
        var motorScrewDistance = 8;
        var motorMountDiameter = Math.max((motorScrewDistance + wallThickness) * 2, sparSpace / 2);
        var screwM25 = new screw("M2.5");

        var motorMount = CSG.cylinder({
                start: [0, 0, 0],
                end:   [0, 0, plateThickness],
                radius: ( motorMountDiameter / 2 ) + 1
            });
        stirPlate = stirPlate.union(motorMount)
                             .subtract(CSG.cylinder({
                                    start: [0, 0, 0],
                                    end:   [0, 0, plateThickness],
                                    radius: ( ( motorSpindleDiameter + tol ) / 2 )
                                }))
                             .subtract(screwM25.hole({length:plateThickness}).translate([motorScrewDistance,0,0]))
                             .subtract(screwM25.hole({length:plateThickness}).translate([-motorScrewDistance,0,0]));
    }
    else if ( params.plateType == "top" )
    {
        var centerHoleDiameter = 52;
        stirPlate = stirPlate.union(cylinder({d: centerHoleDiameter + ( wallThickness * 3 ),
                                                 h: plateThickness,
                                                 center: false}));
        stirPlate = stirPlate.subtract(cylinder({d: centerHoleDiameter + (tol*2),
                                                 h: plateThickness * 2.5,
                                                 center: true}));

        var mountingPostHeight = screw14.type.head[screw14.head].height + wallThickness - plateThickness;
        var mountingPostDiameter = screw14.type.head[screw14.head].diameter + (wallThickness * 2);

        var mountingPost = CSG.cylinder({
                start: [0, 0, 0],
                end:   [0, 0, mountingPostHeight],
                radius: ( mountingPostDiameter / 2 )
            }).translate([plateHoleOffset, plateHoleOffset, -mountingPostHeight ]);

        stirPlate = stirPlate.union(mountingPost);
        mountingPost = mountingPost.rotateZ(90);
        stirPlate = stirPlate.union(mountingPost);
        mountingPost = mountingPost.rotateZ(90);
        stirPlate = stirPlate.union(mountingPost);
        mountingPost = mountingPost.rotateZ(90);
        stirPlate = stirPlate.union(mountingPost);

        var screwHeadRecess = screw14.headRecess().setColor([255,0,0]).translate([plateHoleOffset, plateHoleOffset, -( ( screw14.type.head[screw14.head].height + tol ) - plateThickness ) ]);
        var screwHole = screw14.hole({fit: 'close'}).translate([plateHoleOffset, plateHoleOffset, -( screw14.type.head[screw14.head].height + tol )]);

        stirPlate = stirPlate.subtract(screwHeadRecess).subtract(screwHole);
        screwHeadRecess = screwHeadRecess.rotateZ(90);
        screwHole = screwHole.rotateZ(90);
        stirPlate = stirPlate.subtract(screwHeadRecess).subtract(screwHole);
        screwHeadRecess = screwHeadRecess.rotateZ(90);
        screwHole = screwHole.rotateZ(90);
        stirPlate = stirPlate.subtract(screwHeadRecess).subtract(screwHole);
        screwHeadRecess = screwHeadRecess.rotateZ(90);
        screwHole = screwHole.rotateZ(90);
        stirPlate = stirPlate.subtract(screwHeadRecess).subtract(screwHole);
    }
    else if ( params.plateType == "bottom" )
    {
        nut14.screw.length = nut14.height + wallThickness;
        var nutInsert = nut14.insert().translate([plateHoleOffset, plateHoleOffset, wallThickness]);
        //nutInsert = nutInsert.subtract(nut14.insertHole().translate([plateHoleOffset, plateHoleOffset, wallThickness]));
        var insertHole = nut14.insertHole().translate([plateHoleOffset, plateHoleOffset, wallThickness]);
        stirPlate = stirPlate.union(nutInsert)
                             .subtract(insertHole);

        nutInsert = nutInsert.rotateZ(90);
        insertHole = insertHole.rotateZ(90);
        stirPlate = stirPlate.union(nutInsert)
                             .subtract(insertHole);

        nutInsert = nutInsert.rotateZ(90);
        insertHole = insertHole.rotateZ(90);
        stirPlate = stirPlate.union(nutInsert)
                             .subtract(insertHole);

        nutInsert = nutInsert.rotateZ(90);
        insertHole = insertHole.rotateZ(90);
        stirPlate = stirPlate.union(nutInsert)
                             .subtract(insertHole);
    }
    output.push(stirPlate);

    return output;
}
<commit_msg>add middle plate configuration for small motor<commit_after>
include("common.jscad");
include("screws.jscad");
include("lattice.jscad");
include("plate.jscad");

var tol = 0.5;
var wallThickness = 2.0;

var sparSpace;
var plateHoleOffset;

function getParameterDefinitions()
{
  return [
    { name: 'showReferences', type: 'choice', values: ["yes", "no"], initial: "yes", caption: "Show References?" },
    { name: 'plateType', type: 'choice', values: ["top","middle","bottom"], initial: "middle", caption: "Plate Type" }
  ];
}

function main(params)
{
    var bodyDiameter = 40;
    var output = [];

    var screw14 = new screw("1/4-20", {head: "pan-square"});
    var nut14 = new nut("1/4-20", {screw: screw14});
    var washerM6 = new washer("M6", {screw: screw14}); // just use M6 washers for now
    var plateThickness = 3.0;

    // Axis
    if ( params.showReferences === 'yes' )
    {
        var extents = [bodyDiameter,bodyDiameter,20];
        output.push(makeAxis(extents));
    }

    if ( params.plateType == "top" )
    {
        var plateArgs = {
            rad: ( screw14.type.head[screw14.head].diameter + (wallThickness * 2) ) / 2.0,
            radMultiplier: 6,
            mountingHoleDiameter: screw14.type.fit.close,
            plateThickness: plateThickness,
            style: 'solid',
            latticeAngle: 45,
            latticeCount: 4,
            cutMountingHoles: false,
        }
    }
    else if ( params.plateType == "middle" )
    {
        var plateArgs = {
            rad: ( screw14.type.head[screw14.head].diameter / 2.0),
            radMultiplier: 1,
            mountingHoleDiameter: screw14.type.fit.free,
            plateThickness: plateThickness,
            style: 'lattice',
            latticeAngle: 33.5,
            latticeCount: 3,
            latticeXOffset: -1.20,
            cutMountingHoles: true,
        }
    }
    else if ( params.plateType == "bottom" )
    {
        var plateArgs = {
            rad: ( nut14.diameter + (wallThickness * 2) ) / 2.0,
            radMultiplier: 6,
            mountingHoleDiameter: screw14.type.fit.close,
            plateThickness: plateThickness,
            style: 'solid',
            cutMountingHoles: false,
        }
    }

    var stirPlate = plate(plateArgs);

    if ( params.plateType == "middle" )
    {
        //// 12v 6000RPM motor
        //var motorDiameter = 30;
        //var motorSpindleDiameter = 10;
        //var motorScrewDistance = 8;
        //var screwM25 = new screw("M2.5");

        // 6v 7000RPM motor
        var motorDiameter = 25;
        var motorSpindleDiameter = 7.0;
        var motorScrewDistance = 8;
        var screwM25 = new screw("EG1.5");

        var motorMountDiameter = Math.max((motorScrewDistance + wallThickness) * 2, sparSpace / 2);

        var motorMount = CSG.cylinder({
                start: [0, 0, 0],
                end:   [0, 0, plateThickness],
                radius: ( motorMountDiameter / 2 ) + 1
            });
        stirPlate = stirPlate.union(motorMount)
                             .subtract(CSG.cylinder({
                                    start: [0, 0, 0],
                                    end:   [0, 0, plateThickness],
                                    radius: ( ( motorSpindleDiameter + tol ) / 2 )
                                }))
                             .subtract(screwM25.hole({length:plateThickness}).translate([motorScrewDistance,0,0]))
                             .subtract(screwM25.hole({length:plateThickness}).translate([-motorScrewDistance,0,0]));
    }
    else if ( params.plateType == "top" )
    {
        var centerHoleDiameter = 52;
        stirPlate = stirPlate.union(cylinder({d: centerHoleDiameter + ( wallThickness * 3 ),
                                                 h: plateThickness,
                                                 center: false}));
        stirPlate = stirPlate.subtract(cylinder({d: centerHoleDiameter + (tol*2),
                                                 h: plateThickness * 2.5,
                                                 center: true}));

        var mountingPostHeight = screw14.type.head[screw14.head].height + wallThickness - plateThickness;
        var mountingPostDiameter = screw14.type.head[screw14.head].diameter + (wallThickness * 2);

        var mountingPost = CSG.cylinder({
                start: [0, 0, 0],
                end:   [0, 0, mountingPostHeight],
                radius: ( mountingPostDiameter / 2 )
            }).translate([plateHoleOffset, plateHoleOffset, -mountingPostHeight ]);

        stirPlate = stirPlate.union(mountingPost);
        mountingPost = mountingPost.rotateZ(90);
        stirPlate = stirPlate.union(mountingPost);
        mountingPost = mountingPost.rotateZ(90);
        stirPlate = stirPlate.union(mountingPost);
        mountingPost = mountingPost.rotateZ(90);
        stirPlate = stirPlate.union(mountingPost);

        var screwHeadRecess = screw14.headRecess().setColor([255,0,0]).translate([plateHoleOffset, plateHoleOffset, -( ( screw14.type.head[screw14.head].height + tol ) - plateThickness ) ]);
        var screwHole = screw14.hole({fit: 'close'}).translate([plateHoleOffset, plateHoleOffset, -( screw14.type.head[screw14.head].height + tol )]);

        stirPlate = stirPlate.subtract(screwHeadRecess).subtract(screwHole);
        screwHeadRecess = screwHeadRecess.rotateZ(90);
        screwHole = screwHole.rotateZ(90);
        stirPlate = stirPlate.subtract(screwHeadRecess).subtract(screwHole);
        screwHeadRecess = screwHeadRecess.rotateZ(90);
        screwHole = screwHole.rotateZ(90);
        stirPlate = stirPlate.subtract(screwHeadRecess).subtract(screwHole);
        screwHeadRecess = screwHeadRecess.rotateZ(90);
        screwHole = screwHole.rotateZ(90);
        stirPlate = stirPlate.subtract(screwHeadRecess).subtract(screwHole);
    }
    else if ( params.plateType == "bottom" )
    {
        nut14.screw.length = nut14.height + wallThickness;
        var nutInsert = nut14.insert().translate([plateHoleOffset, plateHoleOffset, wallThickness]);
        //nutInsert = nutInsert.subtract(nut14.insertHole().translate([plateHoleOffset, plateHoleOffset, wallThickness]));
        var insertHole = nut14.insertHole().translate([plateHoleOffset, plateHoleOffset, wallThickness]);
        stirPlate = stirPlate.union(nutInsert)
                             .subtract(insertHole);

        nutInsert = nutInsert.rotateZ(90);
        insertHole = insertHole.rotateZ(90);
        stirPlate = stirPlate.union(nutInsert)
                             .subtract(insertHole);

        nutInsert = nutInsert.rotateZ(90);
        insertHole = insertHole.rotateZ(90);
        stirPlate = stirPlate.union(nutInsert)
                             .subtract(insertHole);

        nutInsert = nutInsert.rotateZ(90);
        insertHole = insertHole.rotateZ(90);
        stirPlate = stirPlate.union(nutInsert)
                             .subtract(insertHole);
    }
    output.push(stirPlate);

    return output;
}
<|endoftext|>


<commit_before>import _ from 'underscore';
import $ from 'jquery';

import View from 'girder/views/View';

import { model } from '../utils/node';
import jstree from '../jstree';

/**
 * This view is wrapper around the jstree interface allowing it to
 * be used like a normal girder view.  It wraps a number of the
 * methods provided by jstree to provide an abstract interface.
 */
const TreeView = View.extend({
    events: {
        'select_node.jstree': '_onSelect',
        'state_ready.jstree': '_onLoad'
    },

    /**
     * Initialize the view.
     *
     * @param {boolean} [settings.multiple=false]
     *   Allow multiselection with ctrl/shift keys.
     * @param {string[]} [settings.selectable]
     *   A list of selectable types.
     */
    initialize(settings = {}) {
        this.loaded = false;
        this.jstreeConfig = {
            core: {
                multiple: settings.multiple,
                check_callback: settings.check_callback
            }
        };

        if (settings.selectable) {
            this.jstreeConfig.selectable = settings.selectable;
        }
    },

    render() {
        this._destroy();
        jstree(this.el, this.jstreeConfig);
    },

    destroy() {
        this._destroy();
        return View.prototype.destroy.apply(this, arguments);
    },

    /**
     * Get the jstree instance created when rendering for directly
     * interacting with the tree.
     */
    instance() {
        return this.$el.jstree(true);
    },

    /**
     * Save the tree state in local storage.  This uses jstree's `state`
     * plugin to store a serialized version of the tree state in local
     * storage so it persists between renders.  This generally isn't
     * necessary to be called because jstree automatically handles it.
     */
    saveState() {
        this.instance().save_state();
    },

    /**
     * Forcibly clear the saved state of the tree.
     */
    clearState() {
        this.instance().clear_state();
    },

    /**
     * Reload the full tree from the server.  This may be necessary
     * if models are created, modified, or destroyed outside of this
     * view.
     *
     * @returns {Promise} Resolves when the tree is reloaded.
     */
    reload() {
        const defer = $.Deferred();
        this.$el.one('refresh.jstree', () => defer.resolve(this));
        this.instance().refresh();
        return defer.promise();
    },

    /**
     * Get a path string to a node for a human readable representation
     * of the node.
     * (WARNING: the path to a resource is not necessarily unique)
     */
    path(node) {
        return '/' + this.instance().get_path(node).join('/');
    },

    /**
     * Get a list of selected nodes.  If multiselect is turned off
     * this will be an array with at most one node.
     */
    getSelected() {
        return _.map(
            this.instance().get_selected(true),
            _.property('original')
        );
    },

    /*
    select(node) {
    },
    */

    _destroy() {
        this.$el.jstree('destroy');
    },

    _onSelect(e, data) {
        // Ignore initial selection events because they are fired
        // when reloading the state and may contain invalid selections.
        if (!this.loaded) {
            return;
        }
        const node = data.node;
        const event = 'g:treeview:select';

        if (node) {
            const modelObj = model(node);

            if (modelObj) {
                const type = modelObj._modelType;
                this.trigger(
                    `${event}:${type}`, modelObj, node, data.selected
                );
            }

            this.trigger(
                event, modelObj, data.node, data.selected
            );
        }
    },

    _onLoad() {
        this.loaded = true;
        this.trigger('g:treeview:load', this);
    }
});

export default TreeView;
<commit_msg>Bubble several jstree events up to the backbone view<commit_after>import _ from 'underscore';
import $ from 'jquery';

import View from 'girder/views/View';

import { model } from '../utils/node';
import jstree from '../jstree';

/**
 * This view is wrapper around the jstree interface allowing it to
 * be used like a normal girder view.  It wraps a number of the
 * methods provided by jstree to provide an abstract interface.
 */
const TreeView = View.extend({
    events: {
        'select_node.jstree': '_onSelect',
        'state_ready.jstree': '_onLoad',
        'before_open.jstree': '_triggerEvent',
        'after_open.jstree': '_triggerEvent',
        'after_close.jstree': '_triggerEvent',
        'model.jstree': '_triggerEvent'
    },

    /**
     * Initialize the view.
     *
     * @param {boolean} [settings.multiple=false]
     *   Allow multiselection with ctrl/shift keys.
     * @param {string[]} [settings.selectable]
     *   A list of selectable types.
     */
    initialize(settings = {}) {
        this.loaded = false;
        this.jstreeConfig = {
            core: {
                multiple: settings.multiple,
                check_callback: settings.check_callback
            }
        };

        if (settings.selectable) {
            this.jstreeConfig.selectable = settings.selectable;
        }
    },

    render() {
        this._destroy();
        jstree(this.el, this.jstreeConfig);
    },

    destroy() {
        this._destroy();
        return View.prototype.destroy.apply(this, arguments);
    },

    /**
     * Get the jstree instance created when rendering for directly
     * interacting with the tree.
     */
    instance() {
        return this.$el.jstree(true);
    },

    /**
     * Save the tree state in local storage.  This uses jstree's `state`
     * plugin to store a serialized version of the tree state in local
     * storage so it persists between renders.  This generally isn't
     * necessary to be called because jstree automatically handles it.
     */
    saveState() {
        this.instance().save_state();
    },

    /**
     * Forcibly clear the saved state of the tree.
     */
    clearState() {
        this.instance().clear_state();
    },

    /**
     * Reload the full tree from the server.  This may be necessary
     * if models are created, modified, or destroyed outside of this
     * view.
     *
     * @returns {Promise} Resolves when the tree is reloaded.
     */
    reload() {
        const defer = $.Deferred();
        this.$el.one('refresh.jstree', () => defer.resolve(this));
        this.instance().refresh();
        return defer.promise();
    },

    /**
     * Get a path string to a node for a human readable representation
     * of the node.
     * (WARNING: the path to a resource is not necessarily unique)
     */
    path(node) {
        return '/' + this.instance().get_path(node).join('/');
    },

    /**
     * Get a list of selected nodes.  If multiselect is turned off
     * this will be an array with at most one node.
     */
    getSelected() {
        return _.map(
            this.instance().get_selected(true),
            _.property('original')
        );
    },

    /*
    select(node) {
    },
    */

    _destroy() {
        this.$el.jstree('destroy');
    },

    _onSelect(e, data) {
        // Ignore initial selection events because they are fired
        // when reloading the state and may contain invalid selections.
        if (!this.loaded) {
            return;
        }
        const node = data.node;
        const event = 'g:treeview:select';

        if (node) {
            const modelObj = model(node);

            if (modelObj) {
                const type = modelObj._modelType;
                this.trigger(
                    `${event}:${type}`, modelObj, node, data.selected
                );
            }

            this.trigger(
                event, modelObj, data.node, data.selected
            );
        }
    },

    _onLoad() {
        this.loaded = true;
        this.trigger('g:treeview:load', this);
    },

    _triggerEvent(event) {
        this.trigger(`g:jstree:${event.type}`, this, event);
    }
});

export default TreeView;
<|endoftext|>


<commit_before>// PLUGIN: TWITTER

(function (Popcorn) {

  /**
   * Twitter popcorn plug-in
   * Appends a Twitter widget to an element on the page.
   * Options parameter will need a start, end, target and source.
   * Optional parameters are height and width.
   * Start is the time that you want this plug-in to execute
   * End is the time that you want this plug-in to stop executing
   * Src is the hash tag or twitter user to get tweets from
   * Target is the id of the document element that the images are
   *  appended to, this target element must exist on the DOM
   * Height is the height of the widget, defaults to 200
   * Width is the width of the widget, defaults to 250
   *
   * @param {Object} options
   *
   * Example:
     var p = Popcorn('#video')
        .twitter({
          start:          5,                // seconds, mandatory
          end:            15,               // seconds, mandatory
          src:            '@stevesong',     // mandatory, also accepts hash tags
          height:         200,              // optional
          width:          250,              // optional
          target:         'twitterdiv'      // mandatory
        } )
   *
   */
  Popcorn.plugin( "twitter" , {

      manifest: {
        about:{
          name:    "Popcorn Twitter Plugin",
          version: "0.1",
          author:  "Scott Downe",
          website: "http://scottdowne.wordpress.com/"
        },
        options:{
          start   : {elem:'input', type:'number', label:'In'},
          end     : {elem:'input', type:'number', label:'Out'},
          src     : {elem:'input', type:'text',   label:'User to follow'},
          target  : 'twitter-container',
          height  : {elem:'input', type:'number', label:'Height'},
          width   : {elem:'input', type:'number', label:'Width'}
        }
      },

      _setup: function( options ) {

        // setup widget div that is unique per track
        options._container = document.createElement( 'div' ); // create the div to store the widget
        options._container.setAttribute('id', Popcorn.guid()); // use this id to connect it to the widget
        options._container.style.display = "none"; // display none by default
        document.getElementById( options.target ).appendChild( options._container ); // add the widget's div to the target div

        if (document.getElementById(options.target)) {
          document.getElementById(options.target).appendChild(options._container);
        }

        // setup info for the widget
        var src     = options.src || "",
            width   = options.width || 250,
            height  = options.height || 200,
            profile = /^@/.test( src ),
            hash    = /^#/.test( src ),
            widgetOptions = {
              version: 2,
              id: options._container.getAttribute( 'id' ),  // use this id to connect it to the div
              rpp: 30,
              width: width,
              height: height,
              interval: 6000,
              theme: {
                shell: {
                  background: '#ffffff',
                  color: '#000000'
                },
                tweets: {
                  background: '#ffffff',
                  color: '#444444',
                  links: '#1985b5'
                }
              },
              features: {
                loop: true,
                timestamp: true,
                avatars: true,
                hashtags: true,
                toptweets: true,
                live: true,
                scrollbar: false,
                behavior: 'default'
              }
            };

        // create widget
        if ( profile ) {

          widgetOptions.type = "profile";

          new TWTR.Widget( widgetOptions ).render().setUser( src ).start();

        } else if ( hash ) {

          widgetOptions.type = "search";
          widgetOptions.search = src;
          widgetOptions.subject = src;

          new TWTR.Widget( widgetOptions ).render().start();

        }
      },

      /**
       * @member Twitter
       * The start function will be executed when the currentTime
       * of the video  reaches the start time provided by the
       * options variable
       */
      start: function( event, options ) {
        options._container.style.display = "inline";
      },

      /**
       * @member Twitter
       * The end function will be executed when the currentTime
       * of the video  reaches the end time provided by the
       * options variable
       */
      end: function( event, options ) {
        options._container.style.display = "none";
      }
    });

})( Popcorn );
<commit_msg>Twitter API support to IDE plugin<commit_after>// PLUGIN: TWITTER

(function (Popcorn) {

  /**
   * Twitter popcorn plug-in
   * Appends a Twitter widget to an element on the page.
   * Options parameter will need a start, end, target and source.
   * Optional parameters are height and width.
   * Start is the time that you want this plug-in to execute
   * End is the time that you want this plug-in to stop executing
   * Src is the hash tag or twitter user to get tweets from
   * Target is the id of the document element that the images are
   *  appended to, this target element must exist on the DOM
   * Height is the height of the widget, defaults to 200
   * Width is the width of the widget, defaults to 250
   *
   * @param {Object} options
   *
   * Example:
     var p = Popcorn('#video')
        .twitter({
          start:          5,                // seconds, mandatory
          end:            15,               // seconds, mandatory
          src:            '@stevesong',     // mandatory, also accepts hash tags
          height:         200,              // optional
          width:          250,              // optional
          target:         'twitterdiv'      // mandatory
        } )
   *
   */
  if ( !Twttr ) {

    var head = document.getElementsByTagName("head")[0] || document.documentElement,
      script = document.createElement("script");

    script.src = "api script url";

    head.appendChild( script );
  }

  Popcorn.plugin( "twitter" , {

      manifest: {
        about:{
          name:    "Popcorn Twitter Plugin",
          version: "0.1",
          author:  "Scott Downe",
          website: "http://scottdowne.wordpress.com/"
        },
        options:{
          start   : {elem:'input', type:'number', label:'In'},
          end     : {elem:'input', type:'number', label:'Out'},
          src     : {elem:'input', type:'text',   label:'User to follow'},
          target  : 'twitter-container',
          height  : {elem:'input', type:'number', label:'Height'},
          width   : {elem:'input', type:'number', label:'Width'}
        }
      },

      _setup: function( options ) {

        // setup widget div that is unique per track
        options._container = document.createElement( 'div' ); // create the div to store the widget
        options._container.setAttribute('id', Popcorn.guid()); // use this id to connect it to the widget
        options._container.style.display = "none"; // display none by default
        document.getElementById( options.target ).appendChild( options._container ); // add the widget's div to the target div

        if (document.getElementById(options.target)) {
          document.getElementById(options.target).appendChild(options._container);
        }

        // setup info for the widget
        var src     = options.src || "",
            width   = options.width || 250,
            height  = options.height || 200,
            profile = /^@/.test( src ),
            hash    = /^#/.test( src ),
            widgetOptions = {
              version: 2,
              id: options._container.getAttribute( 'id' ),  // use this id to connect it to the div
              rpp: 30,
              width: width,
              height: height,
              interval: 6000,
              theme: {
                shell: {
                  background: '#ffffff',
                  color: '#000000'
                },
                tweets: {
                  background: '#ffffff',
                  color: '#444444',
                  links: '#1985b5'
                }
              },
              features: {
                loop: true,
                timestamp: true,
                avatars: true,
                hashtags: true,
                toptweets: true,
                live: true,
                scrollbar: false,
                behavior: 'default'
              }
            };

        // create widget
        if ( profile ) {

          widgetOptions.type = "profile";

          new TWTR.Widget( widgetOptions ).render().setUser( src ).start();

        } else if ( hash ) {

          widgetOptions.type = "search";
          widgetOptions.search = src;
          widgetOptions.subject = src;

          new TWTR.Widget( widgetOptions ).render().start();

        }
      },

      /**
       * @member Twitter
       * The start function will be executed when the currentTime
       * of the video  reaches the start time provided by the
       * options variable
       */
      start: function( event, options ) {
        options._container.style.display = "inline";
      },

      /**
       * @member Twitter
       * The end function will be executed when the currentTime
       * of the video  reaches the end time provided by the
       * options variable
       */
      end: function( event, options ) {
        options._container.style.display = "none";
      }
    });

})( Popcorn );
<|endoftext|>