<commit_before>/*
 * Copyright 2014-2015 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("notification");
require("launcher");
require("actions");
require("mediakeys");
require("storage");
require("browser");
require("core");
require("gettext");

// Translations
var _ = Nuvola.Translate.gettext;
var C_ = Nuvola.Translate.pgettext;

/**
 * @enum Base media player @link{Actions|actions}
 */
var PlayerAction = {
    /**
     * Start playback
     */
    PLAY: "play",
    /**
     * Toggle playback (play/pause)
     */
    TOGGLE_PLAY: "toggle-play",
    /**
     * Pause playback
     */
    PAUSE: "pause",
    /**
     * Stop playback
     */
    STOP: "stop",
    /**
     * Skip to next track
     */
    PREV_SONG: "prev-song",
    /**
     * Skip to previous track
     */
    NEXT_SONG: "next-song",
    /**
     * Show playback notification
     */
    PLAYBACK_NOTIFICATION: "playback-notification",
}

/**
 * @enum Media player playback states
 */
var PlaybackState = {
    /**
     * Track is not playing nor paused.
     */
    UNKNOWN: 0,
    /**
     * Playback is paused.
     */
    PAUSED: 1,
    /**
     * Track is playing.
     */
    PLAYING: 2,
}

// New key
var RUN_IN_BACKGROUND = "player.run_in_background";
// Deprecated key - for backward compatibility
var BACKGROUND_PLAYBACK = "player.background_playback";

var RUN_IN_BACKGROUND_OPTIONS = [
    ["always", C_("Background playback", "Always")],
    ["playing", C_("Background playback", "When song is playing")],
    ["never", C_("Background playback", "Never")]
];

/**
 * Media player controller.
 */
var MediaPlayer = $prototype(null);

/**
 * Initializes media player
 */
MediaPlayer.$init = function()
{
    this._state = null;
    this._artworkFile = null;
    this._canGoPrev = null;
    this._canGoNext = null;
    this._canPlay = null;
    this._canPause = null;
    this._extraActions = [];
    this._artworkLoop = 0;
    this._baseActions = [PlayerAction.TOGGLE_PLAY, PlayerAction.PLAY, PlayerAction.PAUSE, PlayerAction.PREV_SONG, PlayerAction.NEXT_SONG];
    this._notification = Nuvola.notifications.getNamedNotification("mediaplayer", true, "x-gnome.music");
    Nuvola.core.connect("InitAppRunner", this);
    Nuvola.core.connect("InitWebWorker", this);
}

/**
 * Set info about currently playing track.
 *
 * If track info is same as in the previous call, this method does nothing.
 *
 * @param String|null track.title          track title
 * @param String|null track.artist         track artist
 * @param String|null track.album          track album
 * @param String|null track.artLocation    URL of album/track artwork
 * @param double|null track.rating         track rating from `0.0` to `1.0`. *This item is ignored prior API 3.1.*
 */
MediaPlayer.setTrack = function(track)
{
    var changed = Nuvola.objectDiff(this._track, track);

    if (!changed.length)
        return;

    this._track = track;

    if (!track.artLocation)
        this._artworkFile = null;

    if (Nuvola.inArray(changed, "artLocation") && track.artLocation)
    {
        this._artworkFile = null;
        var artworkId = this._artworkLoop++;
        if (this._artworkLoop > 9)
            this._artworkLoop = 0;
        Nuvola.browser.downloadFileAsync(track.artLocation, "player.artwork." + artworkId, this._onArtworkDownloaded.bind(this), changed);
        this._sendDevelInfo();
    }
    else
    {
        this._updateTrackInfo(changed);
    }
}

/**
 * Set current playback state
 *
 * If the current state is same as the previous one, this method does nothing.
 *
 * @param PlaybackState state    current playback state
 */
MediaPlayer.setPlaybackState = function(state)
{
    if (this._state !== state)
    {
        this._state = state;
        Nuvola.actions.setEnabled(PlayerAction.PLAYBACK_NOTIFICATION, this._state !== PlaybackState.UNKNOWN);
        this._setActions();
        this._updateTrackInfo(["state"]);
    }
}

/**
 * Set whether it is possible to go to the next track
 *
 * If the argument is same as in the previous call, this method does nothing.
 *
 * @param Boolean canGoNext    true if the "go to next track" button is active
 */
MediaPlayer.setCanGoNext = function(canGoNext)
{
    if (this._canGoNext !== canGoNext)
    {
        this._canGoNext = canGoNext;
        Nuvola.actions.setEnabled(PlayerAction.NEXT_SONG, !!canGoNext);
        Nuvola._sendMessageAsync("Nuvola.MediaPlayer.setFlag", "can-go-next", !!canGoNext);
        this._showNotification();
        this._sendDevelInfo();
    }
}

/**
 * Set whether it is possible to go to the previous track
 *
 * If the argument is same as in the previous call, this method does nothing.
 *
 * @param Boolean canGoPrev    true if the "go to previous track" button is active
 */
MediaPlayer.setCanGoPrev = function(canGoPrev)
{
    if (this._canGoPrev !== canGoPrev)
    {
        this._canGoPrev = canGoPrev;
        Nuvola.actions.setEnabled(PlayerAction.PREV_SONG, !!canGoPrev);
        Nuvola._sendMessageAsync("Nuvola.MediaPlayer.setFlag", "can-go-previous", !!canGoPrev);
        this._showNotification();
        this._sendDevelInfo();
    }
}

/**
 * Set whether it is possible to start playback
 *
 * If the argument is same as in the previous call, this method does nothing.
 *
 * @param Boolean canPlay    true if the "play" button is active
 */
MediaPlayer.setCanPlay = function(canPlay)
{
    if (this._canPlay !== canPlay)
    {
        this._canPlay = canPlay;
        Nuvola.actions.setEnabled(PlayerAction.PLAY, !!canPlay);
        Nuvola.actions.setEnabled(PlayerAction.TOGGLE_PLAY, !!(this._canPlay || this._canPause));
        Nuvola._sendMessageAsync("Nuvola.MediaPlayer.setFlag", "can-play", !!canPlay);
        this._showNotification();
        this._sendDevelInfo();
    }
}

/**
 * Set whether it is possible to pause playback
 *
 * If the argument is same as in the previous call, this method does nothing.
 *
 * @param Boolean canPause    true if the "pause" button is active
 */
MediaPlayer.setCanPause = function(canPause)
{
    if (this._canPause !== canPause)
    {
        this._canPause = canPause;
        Nuvola.actions.setEnabled(PlayerAction.PAUSE, !!canPause);
        Nuvola.actions.setEnabled(PlayerAction.STOP, !!canPause);
        Nuvola.actions.setEnabled(PlayerAction.TOGGLE_PLAY, !!(this._canPlay || this._canPause));
        Nuvola._sendMessageAsync("Nuvola.MediaPlayer.setFlag", "can-pause", !!canPause);
        Nuvola._sendMessageAsync("Nuvola.MediaPlayer.setFlag", "can-stop", !!canPause);
        this._showNotification();
        this._sendDevelInfo();
    }
}

/**
 * Add actions for media player capabilities
 *
 * For example: star rating, thumbs up/down, like/love/unlike.
 *
 * Actions that have been already added are ignored.
 *
 * @param "Array of String" actions    names of actions
 */
MediaPlayer.addExtraActions = function(actions)
{
    var update = false;
    for (var i = 0; i < actions.length; i++)
    {
        var action = actions[i];
        if (!Nuvola.inArray(this._extraActions, action))
        {
            this._extraActions.push(action);
            update = true;
        }
    }

    if (update)
        this._updateMenu();
}

MediaPlayer._onInitAppRunner = function(emitter)
{
    Nuvola.launcher.setActions(["quit"]);
    Nuvola.actions.addAction("playback", "win", PlayerAction.PLAY, "Play", null, "media-playback-start", null);
    Nuvola.actions.addAction("playback", "win", PlayerAction.PAUSE, "Pause", null, "media-playback-pause", null);
    Nuvola.actions.addAction("playback", "win", PlayerAction.TOGGLE_PLAY, "Toggle play/pause", null, null, null);
    Nuvola.actions.addAction("playback", "win", PlayerAction.STOP, "Stop", null, "media-playback-stop", null);
    Nuvola.actions.addAction("playback", "win", PlayerAction.PREV_SONG, "Previous song", null, "media-skip-backward", null);
    Nuvola.actions.addAction("playback", "win", PlayerAction.NEXT_SONG, "Next song", null, "media-skip-forward", null);
    Nuvola.actions.addAction("playback", "win", PlayerAction.PLAYBACK_NOTIFICATION, "Show playback notification", null, null, null);
    this._notification.setActions([PlayerAction.PREV_SONG, PlayerAction.PLAY, PlayerAction.PAUSE, PlayerAction.NEXT_SONG]);

    // Take into account the old BACKGROUND_PLAYBACK value
    Nuvola.config.setDefault(BACKGROUND_PLAYBACK, true);
    var defaultOption = RUN_IN_BACKGROUND_OPTIONS[Nuvola.config.get(BACKGROUND_PLAYBACK) ? 1 : 2][0]
    Nuvola.config.setDefault(RUN_IN_BACKGROUND, defaultOption);

    this._updateMenu();
    Nuvola.core.connect("PreferencesForm", this);
}

MediaPlayer._onInitWebWorker = function(emitter)
{
    Nuvola.mediaKeys.connect("MediaKeyPressed", this);
    Nuvola.actions.connect("ActionActivated", this);
    Nuvola.core.connect("QuitRequest", this);
    this._track = {
        "title": undefined,
        "artist": undefined,
        "album": undefined,
        "artLocation": undefined,
        "rating": undefined
    };
    this.setPlaybackState(PlaybackState.UNKNOWN);
    this._setActions();
}

MediaPlayer._onActionActivated = function(emitter, name, param)
{
    switch (name)
    {
    case PlayerAction.PLAYBACK_NOTIFICATION:
        this._notification.show();
        break;
    }
}

MediaPlayer._setActions = function()
{
    var actions = [this._state === PlaybackState.PLAYING ? PlayerAction.PAUSE : PlayerAction.PLAY, PlayerAction.PREV_SONG, PlayerAction.NEXT_SONG];
    actions = actions.concat(this._extraActions);
    actions.push("quit");
    Nuvola.launcher.setActions(actions);
}

MediaPlayer._sendDevelInfo = function()
{
    var rating = (Number) this._track.rating;
    if (rating < 0)
        rating = 0.0;
    else if (rating > 1)
        rating = 1;

    var info = {
        "title": this._track.title || null,
        "artist": this._track.artist || null,
        "album": this._track.album || null,
        "rating": rating,
        "artworkLocation": this._track.artLocation || null,
        "artworkFile": this._artworkFile || null,
        "playbackActions": this._baseActions.concat(this._extraActions),
        "state": ["unknown", "paused", "playing"][this._state],
    };
    Nuvola._sendMessageAsync("Nuvola.MediaPlayer.setTrackInfo", info);
}

MediaPlayer._onArtworkDownloaded = function(res, changed)
{
    if (!res.success)
    {
        this._artworkFile = null;
        console.log(Nuvola.format("Artwork download failed: {1} {2}.", res.statusCode, res.statusText));
    }
    else
    {
        this._artworkFile = res.filePath;
    }
    this._updateTrackInfo(changed);
}

MediaPlayer._updateTrackInfo = function(changed)
{
    this._sendDevelInfo();
    var track = this._track;

    if (track.title)
    {
        var title = track.title;
        var message;
        if (!track.artist && !track.album)
            message = "by unknown artist";
        else if(!track.artist)
            message = Nuvola.format("from {1}", track.album);
        else if(!track.album)
            message = Nuvola.format("by {1}", track.artist);
        else
            message = Nuvola.format("by {1} from {2}", track.artist, track.album);

        this._notification.update(title, message, this._artworkFile ? null : "nuvolaplayer", this._artworkFile);
        this._showNotification();

        var tooltip = track.artist ? Nuvola.format("{1} by {2}", track.title, track.artist) : track.title;
        Nuvola.launcher.setTooltip(tooltip);
    }
    else
    {
        Nuvola.launcher.setTooltip("Nuvola Player");
    }
}

MediaPlayer._showNotification = function()
{
    if (this._state === PlaybackState.PLAYING || Nuvola.notifications.isPersistenceSupported())
        this._notification.show();
}

MediaPlayer._updateMenu = function()
{
    Nuvola.menuBar.setMenu("playback", "_Control", this._baseActions.concat(this._extraActions));
}

MediaPlayer._onQuitRequest = function(emitter, result)
{
    var option = Nuvola.config.get(RUN_IN_BACKGROUND);
    if (option == RUN_IN_BACKGROUND_OPTIONS[0][0]
    || option == RUN_IN_BACKGROUND_OPTIONS[1][0] && this._state === PlaybackState.PLAYING)
    {
        result.approved = false;
    }
}

MediaPlayer._onPreferencesForm = function(object, values, entries)
{
    values[RUN_IN_BACKGROUND] = Nuvola.config.get(RUN_IN_BACKGROUND);
    entries.push(["label", _("Run in background when window is closed")]);
    for (var i = 0; i < RUN_IN_BACKGROUND_OPTIONS.length; i++)
    {
        var option = RUN_IN_BACKGROUND_OPTIONS[i];
        entries.push(["option", RUN_IN_BACKGROUND, option[0], option[1]]);
    }
}

MediaPlayer._onMediaKeyPressed = function(emitter, key)
{
    var A = Nuvola.actions;
    switch (key)
    {
    case MediaKey.PLAY:
    case MediaKey.PAUSE:
        A.activate(PlayerAction.TOGGLE_PLAY);
        break;
    case MediaKey.STOP:
        A.activate(PlayerAction.STOP);
        break;
    case MediaKey.NEXT:
        A.activate(PlayerAction.NEXT_SONG);
        break;
    case MediaKey.PREV:
        A.activate(PlayerAction.PREV_SONG);
        break;
    default:
        console.log(Nuvola.format("Unknown media key '{1}'.", key));
        break;
    }
}

// export public items
Nuvola.PlayerAction = PlayerAction;
Nuvola.PlaybackState = PlaybackState;
Nuvola.MediaPlayer = MediaPlayer;
<commit_msg>Fix syntax error from prev commit<commit_after>/*
 * Copyright 2014-2015 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("notification");
require("launcher");
require("actions");
require("mediakeys");
require("storage");
require("browser");
require("core");
require("gettext");

// Translations
var _ = Nuvola.Translate.gettext;
var C_ = Nuvola.Translate.pgettext;

/**
 * @enum Base media player @link{Actions|actions}
 */
var PlayerAction = {
    /**
     * Start playback
     */
    PLAY: "play",
    /**
     * Toggle playback (play/pause)
     */
    TOGGLE_PLAY: "toggle-play",
    /**
     * Pause playback
     */
    PAUSE: "pause",
    /**
     * Stop playback
     */
    STOP: "stop",
    /**
     * Skip to next track
     */
    PREV_SONG: "prev-song",
    /**
     * Skip to previous track
     */
    NEXT_SONG: "next-song",
    /**
     * Show playback notification
     */
    PLAYBACK_NOTIFICATION: "playback-notification",
}

/**
 * @enum Media player playback states
 */
var PlaybackState = {
    /**
     * Track is not playing nor paused.
     */
    UNKNOWN: 0,
    /**
     * Playback is paused.
     */
    PAUSED: 1,
    /**
     * Track is playing.
     */
    PLAYING: 2,
}

// New key
var RUN_IN_BACKGROUND = "player.run_in_background";
// Deprecated key - for backward compatibility
var BACKGROUND_PLAYBACK = "player.background_playback";

var RUN_IN_BACKGROUND_OPTIONS = [
    ["always", C_("Background playback", "Always")],
    ["playing", C_("Background playback", "When song is playing")],
    ["never", C_("Background playback", "Never")]
];

/**
 * Media player controller.
 */
var MediaPlayer = $prototype(null);

/**
 * Initializes media player
 */
MediaPlayer.$init = function()
{
    this._state = null;
    this._artworkFile = null;
    this._canGoPrev = null;
    this._canGoNext = null;
    this._canPlay = null;
    this._canPause = null;
    this._extraActions = [];
    this._artworkLoop = 0;
    this._baseActions = [PlayerAction.TOGGLE_PLAY, PlayerAction.PLAY, PlayerAction.PAUSE, PlayerAction.PREV_SONG, PlayerAction.NEXT_SONG];
    this._notification = Nuvola.notifications.getNamedNotification("mediaplayer", true, "x-gnome.music");
    Nuvola.core.connect("InitAppRunner", this);
    Nuvola.core.connect("InitWebWorker", this);
}

/**
 * Set info about currently playing track.
 *
 * If track info is same as in the previous call, this method does nothing.
 *
 * @param String|null track.title          track title
 * @param String|null track.artist         track artist
 * @param String|null track.album          track album
 * @param String|null track.artLocation    URL of album/track artwork
 * @param double|null track.rating         track rating from `0.0` to `1.0`. *This item is ignored prior API 3.1.*
 */
MediaPlayer.setTrack = function(track)
{
    var changed = Nuvola.objectDiff(this._track, track);

    if (!changed.length)
        return;

    this._track = track;

    if (!track.artLocation)
        this._artworkFile = null;

    if (Nuvola.inArray(changed, "artLocation") && track.artLocation)
    {
        this._artworkFile = null;
        var artworkId = this._artworkLoop++;
        if (this._artworkLoop > 9)
            this._artworkLoop = 0;
        Nuvola.browser.downloadFileAsync(track.artLocation, "player.artwork." + artworkId, this._onArtworkDownloaded.bind(this), changed);
        this._sendDevelInfo();
    }
    else
    {
        this._updateTrackInfo(changed);
    }
}

/**
 * Set current playback state
 *
 * If the current state is same as the previous one, this method does nothing.
 *
 * @param PlaybackState state    current playback state
 */
MediaPlayer.setPlaybackState = function(state)
{
    if (this._state !== state)
    {
        this._state = state;
        Nuvola.actions.setEnabled(PlayerAction.PLAYBACK_NOTIFICATION, this._state !== PlaybackState.UNKNOWN);
        this._setActions();
        this._updateTrackInfo(["state"]);
    }
}

/**
 * Set whether it is possible to go to the next track
 *
 * If the argument is same as in the previous call, this method does nothing.
 *
 * @param Boolean canGoNext    true if the "go to next track" button is active
 */
MediaPlayer.setCanGoNext = function(canGoNext)
{
    if (this._canGoNext !== canGoNext)
    {
        this._canGoNext = canGoNext;
        Nuvola.actions.setEnabled(PlayerAction.NEXT_SONG, !!canGoNext);
        Nuvola._sendMessageAsync("Nuvola.MediaPlayer.setFlag", "can-go-next", !!canGoNext);
        this._showNotification();
        this._sendDevelInfo();
    }
}

/**
 * Set whether it is possible to go to the previous track
 *
 * If the argument is same as in the previous call, this method does nothing.
 *
 * @param Boolean canGoPrev    true if the "go to previous track" button is active
 */
MediaPlayer.setCanGoPrev = function(canGoPrev)
{
    if (this._canGoPrev !== canGoPrev)
    {
        this._canGoPrev = canGoPrev;
        Nuvola.actions.setEnabled(PlayerAction.PREV_SONG, !!canGoPrev);
        Nuvola._sendMessageAsync("Nuvola.MediaPlayer.setFlag", "can-go-previous", !!canGoPrev);
        this._showNotification();
        this._sendDevelInfo();
    }
}

/**
 * Set whether it is possible to start playback
 *
 * If the argument is same as in the previous call, this method does nothing.
 *
 * @param Boolean canPlay    true if the "play" button is active
 */
MediaPlayer.setCanPlay = function(canPlay)
{
    if (this._canPlay !== canPlay)
    {
        this._canPlay = canPlay;
        Nuvola.actions.setEnabled(PlayerAction.PLAY, !!canPlay);
        Nuvola.actions.setEnabled(PlayerAction.TOGGLE_PLAY, !!(this._canPlay || this._canPause));
        Nuvola._sendMessageAsync("Nuvola.MediaPlayer.setFlag", "can-play", !!canPlay);
        this._showNotification();
        this._sendDevelInfo();
    }
}

/**
 * Set whether it is possible to pause playback
 *
 * If the argument is same as in the previous call, this method does nothing.
 *
 * @param Boolean canPause    true if the "pause" button is active
 */
MediaPlayer.setCanPause = function(canPause)
{
    if (this._canPause !== canPause)
    {
        this._canPause = canPause;
        Nuvola.actions.setEnabled(PlayerAction.PAUSE, !!canPause);
        Nuvola.actions.setEnabled(PlayerAction.STOP, !!canPause);
        Nuvola.actions.setEnabled(PlayerAction.TOGGLE_PLAY, !!(this._canPlay || this._canPause));
        Nuvola._sendMessageAsync("Nuvola.MediaPlayer.setFlag", "can-pause", !!canPause);
        Nuvola._sendMessageAsync("Nuvola.MediaPlayer.setFlag", "can-stop", !!canPause);
        this._showNotification();
        this._sendDevelInfo();
    }
}

/**
 * Add actions for media player capabilities
 *
 * For example: star rating, thumbs up/down, like/love/unlike.
 *
 * Actions that have been already added are ignored.
 *
 * @param "Array of String" actions    names of actions
 */
MediaPlayer.addExtraActions = function(actions)
{
    var update = false;
    for (var i = 0; i < actions.length; i++)
    {
        var action = actions[i];
        if (!Nuvola.inArray(this._extraActions, action))
        {
            this._extraActions.push(action);
            update = true;
        }
    }

    if (update)
        this._updateMenu();
}

MediaPlayer._onInitAppRunner = function(emitter)
{
    Nuvola.launcher.setActions(["quit"]);
    Nuvola.actions.addAction("playback", "win", PlayerAction.PLAY, "Play", null, "media-playback-start", null);
    Nuvola.actions.addAction("playback", "win", PlayerAction.PAUSE, "Pause", null, "media-playback-pause", null);
    Nuvola.actions.addAction("playback", "win", PlayerAction.TOGGLE_PLAY, "Toggle play/pause", null, null, null);
    Nuvola.actions.addAction("playback", "win", PlayerAction.STOP, "Stop", null, "media-playback-stop", null);
    Nuvola.actions.addAction("playback", "win", PlayerAction.PREV_SONG, "Previous song", null, "media-skip-backward", null);
    Nuvola.actions.addAction("playback", "win", PlayerAction.NEXT_SONG, "Next song", null, "media-skip-forward", null);
    Nuvola.actions.addAction("playback", "win", PlayerAction.PLAYBACK_NOTIFICATION, "Show playback notification", null, null, null);
    this._notification.setActions([PlayerAction.PREV_SONG, PlayerAction.PLAY, PlayerAction.PAUSE, PlayerAction.NEXT_SONG]);

    // Take into account the old BACKGROUND_PLAYBACK value
    Nuvola.config.setDefault(BACKGROUND_PLAYBACK, true);
    var defaultOption = RUN_IN_BACKGROUND_OPTIONS[Nuvola.config.get(BACKGROUND_PLAYBACK) ? 1 : 2][0]
    Nuvola.config.setDefault(RUN_IN_BACKGROUND, defaultOption);

    this._updateMenu();
    Nuvola.core.connect("PreferencesForm", this);
}

MediaPlayer._onInitWebWorker = function(emitter)
{
    Nuvola.mediaKeys.connect("MediaKeyPressed", this);
    Nuvola.actions.connect("ActionActivated", this);
    Nuvola.core.connect("QuitRequest", this);
    this._track = {
        "title": undefined,
        "artist": undefined,
        "album": undefined,
        "artLocation": undefined,
        "rating": undefined
    };
    this.setPlaybackState(PlaybackState.UNKNOWN);
    this._setActions();
}

MediaPlayer._onActionActivated = function(emitter, name, param)
{
    switch (name)
    {
    case PlayerAction.PLAYBACK_NOTIFICATION:
        this._notification.show();
        break;
    }
}

MediaPlayer._setActions = function()
{
    var actions = [this._state === PlaybackState.PLAYING ? PlayerAction.PAUSE : PlayerAction.PLAY, PlayerAction.PREV_SONG, PlayerAction.NEXT_SONG];
    actions = actions.concat(this._extraActions);
    actions.push("quit");
    Nuvola.launcher.setActions(actions);
}

MediaPlayer._sendDevelInfo = function()
{
    var rating = 1 * this._track.rating;
    if (rating < 0)
        rating = 0.0;
    else if (rating > 1)
        rating = 1;

    var info = {
        "title": this._track.title || null,
        "artist": this._track.artist || null,
        "album": this._track.album || null,
        "rating": rating,
        "artworkLocation": this._track.artLocation || null,
        "artworkFile": this._artworkFile || null,
        "playbackActions": this._baseActions.concat(this._extraActions),
        "state": ["unknown", "paused", "playing"][this._state],
    };
    Nuvola._sendMessageAsync("Nuvola.MediaPlayer.setTrackInfo", info);
}

MediaPlayer._onArtworkDownloaded = function(res, changed)
{
    if (!res.success)
    {
        this._artworkFile = null;
        console.log(Nuvola.format("Artwork download failed: {1} {2}.", res.statusCode, res.statusText));
    }
    else
    {
        this._artworkFile = res.filePath;
    }
    this._updateTrackInfo(changed);
}

MediaPlayer._updateTrackInfo = function(changed)
{
    this._sendDevelInfo();
    var track = this._track;

    if (track.title)
    {
        var title = track.title;
        var message;
        if (!track.artist && !track.album)
            message = "by unknown artist";
        else if(!track.artist)
            message = Nuvola.format("from {1}", track.album);
        else if(!track.album)
            message = Nuvola.format("by {1}", track.artist);
        else
            message = Nuvola.format("by {1} from {2}", track.artist, track.album);

        this._notification.update(title, message, this._artworkFile ? null : "nuvolaplayer", this._artworkFile);
        this._showNotification();

        var tooltip = track.artist ? Nuvola.format("{1} by {2}", track.title, track.artist) : track.title;
        Nuvola.launcher.setTooltip(tooltip);
    }
    else
    {
        Nuvola.launcher.setTooltip("Nuvola Player");
    }
}

MediaPlayer._showNotification = function()
{
    if (this._state === PlaybackState.PLAYING || Nuvola.notifications.isPersistenceSupported())
        this._notification.show();
}

MediaPlayer._updateMenu = function()
{
    Nuvola.menuBar.setMenu("playback", "_Control", this._baseActions.concat(this._extraActions));
}

MediaPlayer._onQuitRequest = function(emitter, result)
{
    var option = Nuvola.config.get(RUN_IN_BACKGROUND);
    if (option == RUN_IN_BACKGROUND_OPTIONS[0][0]
    || option == RUN_IN_BACKGROUND_OPTIONS[1][0] && this._state === PlaybackState.PLAYING)
    {
        result.approved = false;
    }
}

MediaPlayer._onPreferencesForm = function(object, values, entries)
{
    values[RUN_IN_BACKGROUND] = Nuvola.config.get(RUN_IN_BACKGROUND);
    entries.push(["label", _("Run in background when window is closed")]);
    for (var i = 0; i < RUN_IN_BACKGROUND_OPTIONS.length; i++)
    {
        var option = RUN_IN_BACKGROUND_OPTIONS[i];
        entries.push(["option", RUN_IN_BACKGROUND, option[0], option[1]]);
    }
}

MediaPlayer._onMediaKeyPressed = function(emitter, key)
{
    var A = Nuvola.actions;
    switch (key)
    {
    case MediaKey.PLAY:
    case MediaKey.PAUSE:
        A.activate(PlayerAction.TOGGLE_PLAY);
        break;
    case MediaKey.STOP:
        A.activate(PlayerAction.STOP);
        break;
    case MediaKey.NEXT:
        A.activate(PlayerAction.NEXT_SONG);
        break;
    case MediaKey.PREV:
        A.activate(PlayerAction.PREV_SONG);
        break;
    default:
        console.log(Nuvola.format("Unknown media key '{1}'.", key));
        break;
    }
}

// export public items
Nuvola.PlayerAction = PlayerAction;
Nuvola.PlaybackState = PlaybackState;
Nuvola.MediaPlayer = MediaPlayer;
<|endoftext|>


<commit_before>/**
 * User: Marius
 * Date: 14.02.2015
 * Time: 10:11
 */
const HOST = "countdown.ksc.nasa.gov";
const KSC_PORT = 11142;
const KSC_PORT_VAFB = 11144;

const KSC_PATTERN = 10;
const KSC_VERSION = 1;
const KSC_KEY_LEN = 10;

const INTERVAL_DELAY = 2000;

var KSC = (function ()
{

    var log = require("./logging");
    var dgram = require("dgram");
    var entries = require("./ksc-entries");

    function KSC(type)
    {
        if (typeof type === "undefined")
        {
            type = "ksc";
        }

        switch (type)
        {
            case "vafb":
                this.port = KSC_PORT_VAFB;
                break;
            default:
                this.port = KSC_PORT;
                break;
        }

        this.socket = null;
    }

    function endOnNull(string)
    {
        var pos = string.indexOf("\0");

        if (pos < 0)
        {
            return string;
        }

        return string.substring(0, pos);
    }

    KSC.prototype.sendNextMessage = function ()
    {
        var message = new Buffer("\000");

        log.info("Sending message...");
        this.socket.send(message, 0, message.length, this.port, HOST, function (err)
        {
            if (err)
            {
                log.error("Failed to send: " + err);
            }
        });
    };

    KSC.prototype.start = function ()
    {
        log.info("Starting listener for " + HOST + ":" + this.port);

        var that = this;

        this.socket = dgram.createSocket("udp4");
        this.socket.on("message", function (msg, rinfo)
        {
            log.info("Received message...");
            var parsed = that.processBuffer(msg);

            if (parsed != null)
            {
                that.dataCallback(parsed);
            }
        });

        this.intervalCancel = setInterval(function ()
                                          {
                                              that.sendNextMessage();
                                          }, INTERVAL_DELAY);
        this.sendNextMessage();
    };

    KSC.prototype.stop = function ()
    {
        clearInterval(this.intervalCancel);

        this.socket.close();
    };

    KSC.prototype.onData = function (callback)
    {
        this.dataCallback = callback;
    };

    KSC.prototype.processBuffer = function (buffer)
    {
        // Data format:
        // 0: pattern
        // 1: version
        // 2: number of changes
        // 3 - changes * KEY_LEN: the changed keys
        // rest: the values of the keys

        if (buffer.length < 3)
        {
            log.warn("Message was too small (" + buffer.length + " bytes)...");
            return null;
        }

        var pattern = buffer.readUInt8(0);
        var version = buffer.readUInt8(1);
        var changes = buffer.readUInt8(2);

        if (pattern != KSC_PATTERN)
        {
            log.warn("KSC Pattern doesn't match!");
            return null;
        }

        if (version != KSC_VERSION)
        {
            log.warn("KSC version doesn't match!");
            return null;
        }

        if (changes > entries.length)
        {
            log.warn("KSC delivered too many keys!");
            return null;
        }
        log.info("Processing data...");

        var key_offs = 3;
        var val_offs = key_offs + changes * KSC_KEY_LEN;

        var output = {};

        for (var i = 0; i < changes; ++i, key_offs += KSC_KEY_LEN)
        {
            var currentKey = buffer.toString("ascii", key_offs, key_offs + KSC_KEY_LEN);
            for (var j = 0; j < entries.length; ++j)
            {
                if (entries[j].key == currentKey)
                {
                    output[entries[j].key] = endOnNull(buffer.toString("ascii", val_offs, val_offs + entries[j].len));
                    val_offs += entries[j].len;

                    break;
                }
            }
        }

        return output;
    };

    return KSC;
})();

module.exports = KSC;
<commit_msg>Add generation timestamp<commit_after>/**
 * User: Marius
 * Date: 14.02.2015
 * Time: 10:11
 */
const HOST = "countdown.ksc.nasa.gov";
const KSC_PORT = 11142;
const KSC_PORT_VAFB = 11144;

const KSC_PATTERN = 10;
const KSC_VERSION = 1;
const KSC_KEY_LEN = 10;

const INTERVAL_DELAY = 2000;

var KSC = (function ()
{

    var log = require("./logging");
    var dgram = require("dgram");
    var entries = require("./ksc-entries");

    function KSC(type)
    {
        if (typeof type === "undefined")
        {
            type = "ksc";
        }

        switch (type)
        {
            case "vafb":
                this.port = KSC_PORT_VAFB;
                break;
            default:
                this.port = KSC_PORT;
                break;
        }

        this.socket = null;
    }

    function endOnNull(string)
    {
        var pos = string.indexOf("\0");

        if (pos < 0)
        {
            return string;
        }

        return string.substring(0, pos);
    }

    KSC.prototype.sendNextMessage = function ()
    {
        var message = new Buffer("\000");

        log.info("Sending message...");
        this.socket.send(message, 0, message.length, this.port, HOST, function (err)
        {
            if (err)
            {
                log.error("Failed to send: " + err);
            }
        });
    };

    KSC.prototype.start = function ()
    {
        log.info("Starting listener for " + HOST + ":" + this.port);

        var that = this;

        this.socket = dgram.createSocket("udp4");
        this.socket.on("message", function (msg, rinfo)
        {
            log.info("Received message...");
            var parsed = that.processBuffer(msg);

            if (parsed != null)
            {
                that.dataCallback(parsed);
            }
        });

        this.intervalCancel = setInterval(function ()
                                          {
                                              that.sendNextMessage();
                                          }, INTERVAL_DELAY);
        this.sendNextMessage();
    };

    KSC.prototype.stop = function ()
    {
        clearInterval(this.intervalCancel);

        this.socket.close();
    };

    KSC.prototype.onData = function (callback)
    {
        this.dataCallback = callback;
    };

    KSC.prototype.processBuffer = function (buffer)
    {
        // Data format:
        // 0: pattern
        // 1: version
        // 2: number of changes
        // 3 - changes * KEY_LEN: the changed keys
        // rest: the values of the keys

        if (buffer.length < 3)
        {
            log.warn("Message was too small (" + buffer.length + " bytes)...");
            return null;
        }

        var pattern = buffer.readUInt8(0);
        var version = buffer.readUInt8(1);
        var changes = buffer.readUInt8(2);

        if (pattern != KSC_PATTERN)
        {
            log.warn("KSC Pattern doesn't match!");
            return null;
        }

        if (version != KSC_VERSION)
        {
            log.warn("KSC version doesn't match!");
            return null;
        }

        if (changes > entries.length)
        {
            log.warn("KSC delivered too many keys!");
            return null;
        }
        log.info("Processing data...");

        var key_offs = 3;
        var val_offs = key_offs + changes * KSC_KEY_LEN;

        var output = {
            generated: Math.floor(Date.now() / 1000),
            raw: {}
        };

        for (var i = 0; i < changes; ++i, key_offs += KSC_KEY_LEN)
        {
            var currentKey = buffer.toString("ascii", key_offs, key_offs + KSC_KEY_LEN);
            for (var j = 0; j < entries.length; ++j)
            {
                if (entries[j].key == currentKey)
                {
                    output.raw[entries[j].key] = endOnNull(buffer.toString("ascii", val_offs, val_offs + entries[j].len));
                    val_offs += entries[j].len;

                    break;
                }
            }
        }

        return output;
    };

    return KSC;
})();

module.exports = KSC;
<|endoftext|>


<commit_before>'use strict';

class tab_maneger {
    constructor() {
        //config
        var Config = require('electron-config');
        this.config = new Config();
        //tab information
        this.tab_homepageurl = this.config.get("url") || "https://www.google.co.jp";
        this.tab_count = 0;
        this.tab_active = 0;
        this.tab_views = [];
        //Add destination
        this.add_tabs = document.getElementById("tabs");
        this.add_view = document.getElementById("view-container");
    }
    //function
    add(element) {
        this.tab_views.push(element);
        this.tab_count += 1;
    }
    take() {
        var remove_element = document.getElementById("tab" + this.tab_count);
        remove_element.parentNode.removeChild(remove_element.nextElementSibling);
        remove_element.parentNode.removeChild(remove_element);
        remove_element = document.getElementById("view" + this.tab_count);
        remove_element.parentNode.removeChild(remove_element);
        this.tab_count -= 1;
    }
    add_element(tab=null, view=null) {
        this.add_tabs.appendChild(tab == null ? this.tab_model : tab);
        this.add_view.appendChild(view == null ? this.webview_model : view);
    }
    //make
    get tab_model() {
        var fragment = document.createDocumentFragment();
        fragment.appendChild(this.input_model);
        fragment.appendChild(this.label_model);
        return fragment;
    }
    get input_model() {
        var clone = document.createElement("input");
        clone.className = "hide";
        clone.type = "radio";
        clone.name = "tab";
        clone.id = "tab" + (this.tab_count + 1);
        clone.setAttribute("onclick", "ChangeTab("+this.tab_count+")");
        return clone;
    }
    get label_model() {
        var clone = document.createElement("label");
        clone.setAttribute("for", "tab" + (this.tab_count + 1));
        clone.textContent = "Tab" + (this.tab_count + 1);
        return clone;
    }
    get webview_model() {
        var clone = document.createElement("webview");
        clone.id = "view" + (this.tab_count + 1);
        clone.src = this.tab_homepageurl;
        return clone;
    }
    //management
    get count() {
        return this.tab_count;
    }
    get active() {
        return this.tab_views[this.tab_active];
    }
    set active(num) {
        this.tab_active = num;
    }
    get url() {
        return this.active.getURL()
    }
    set url(url) {
        this.active.loadURL(url);
    }
    get title() {
        return this.active.getTitle()
    }
    //tab function
    back() {
        if(this.active.canGoBack())
            this.active.goBack();
        else
            console.log("Can't go back");
    }
    forword() {
        if(this.active.canGoForward())
            this.active.goForward();
        else
            console.log("Can't go forword");
    }
    reload() {
        this.active.reload();
    }
    homepage() {
        this.url = this.tab_homepageurl;
    }
    setting() {

    }
    mute() {
        if(this.active.isAudioMuted())
            this.active.setAudioMuted(false);
        else
            this.active.setAudioMuted(true);
    }
    changetab(num) {
        this.active.classList.add("hide");
        this.active = num;
        this.active.classList.remove("hide");
    }
}<commit_msg>Add function catch event(Beta)<commit_after>'use strict';

class tab_maneger {
    constructor() {
        //config
        var Config = require('electron-config');
        this.config = new Config();
        //tab information
        this.tab_homepageurl = this.config.get("url") || "https://www.google.co.jp";
        this.tab_count = 0;
        this.tab_active = 0;
        this.tab_views = [];
        //Add destination
        this.add_tabs = document.getElementById("tabs");
        this.add_view = document.getElementById("view-container");
    }
    //function
    add(element) {
        this.tab_views.push(element);
        this.tab_count += 1;
    }
    take() {
        var remove_element = document.getElementById("tab" + this.tab_count);
        remove_element.parentNode.removeChild(remove_element.nextElementSibling);
        remove_element.parentNode.removeChild(remove_element);
        remove_element = document.getElementById("view" + this.tab_count);
        remove_element.parentNode.removeChild(remove_element);
        this.tab_count -= 1;
    }
    add_element(tab=null, view=null) {
        this.add_tabs.appendChild(tab == null ? this.tab_model : tab);
        this.add_view.appendChild(view == null ? this.webview_model : view);
    }
    add_event_simple(element, events) {//fix
        /*
        "load-commit", "did-finish-load", "did-fail-load", "did-frame-finish-load", "did-start-loading", "did-stop-loading", "did-get-response-details", "did-get-redirect-request", "dom-ready", "page-title-updated", "page-favicon-updated", "enter-html-full-screen", "leave-html-full-screen", "console-message", "found-in-page", "new-window", "will-navigate", "did-navigate", "did-navigate-in-page", "close", "ipc-message", "crashed", "gpu-crashed", "plugin-crashed", "destroyed", "media-started-playing", "media-paused", "did-change-theme-color", "update-target-url", "devtools-opened", "devtools-closed", "devtools-focused"
        */
        for(var count in events) {
            element.addEventListener(events[count], (e) => {
                console.log(e)
            })
        }
    }
    add_event_complexity(element) {//fix

    }
    //make
    get tab_model() {
        var fragment = document.createDocumentFragment();
        fragment.appendChild(this.input_model);
        fragment.appendChild(this.label_model);
        return fragment;
    }
    get input_model() {
        var clone = document.createElement("input");
        clone.className = "hide";
        clone.type = "radio";
        clone.name = "tab";
        clone.id = "tab" + (this.tab_count + 1);
        clone.setAttribute("onclick", "ChangeTab("+this.tab_count+")");
        return clone;
    }
    get label_model() {
        var clone = document.createElement("label");
        clone.setAttribute("for", "tab" + (this.tab_count + 1));
        clone.textContent = "Tab" + (this.tab_count + 1);
        return clone;
    }
    get webview_model() {
        var clone = document.createElement("webview");
        clone.id = "view" + (this.tab_count + 1);
        clone.src = this.tab_homepageurl;
        return clone;
    }
    //management
    get count() {
        return this.tab_count;
    }
    get active() {
        return this.tab_views[this.tab_active];
    }
    set active(num) {
        this.tab_active = num;
    }
    get url() {
        return this.active.getURL()
    }
    set url(url) {
        this.active.loadURL(url);
    }
    get title() {
        return this.active.getTitle()
    }
    //tab function
    back() {
        if(this.active.canGoBack())
            this.active.goBack();
        else
            console.log("Can't go back");
    }
    forword() {
        if(this.active.canGoForward())
            this.active.goForward();
        else
            console.log("Can't go forword");
    }
    reload() {
        this.active.reload();
    }
    homepage() {
        this.url = this.tab_homepageurl;
    }
    setting() {

    }
    mute() {
        if(this.active.isAudioMuted())
            this.active.setAudioMuted(false);
        else
            this.active.setAudioMuted(true);
    }
    changetab(num) {
        this.active.classList.add("hide");
        this.active = num;
        this.active.classList.remove("hide");
    }
}<|endoftext|>


<commit_before>//Math
function snap(shape){
  var tmp = shape.points[0];
  translate(shape,-(tmp.x+size/2)%size+size/2,-(tmp.y+size/2)%size+size/2);
}
function translate(shape,dx,dy){
  for(var i = 0; i < shape.points.length;i++){
    shape.points[i].x += dx;
    shape.points[i].y += dy;
  }
}
function reflectX(shape){
  var mirror = cg(shape);
  mirror.x -= (mirror.x + size/2)%size - size/2;
  for(var i = 0; i < shape.points.length;i++){
    shape.points[i].x = 2*mirror.x - shape.points[i].x;
  }
}
function reflectY(shape){
  var mirror = cg(shape);
  mirror.y -= (mirror.y + size/2)%size - size/2;
  for(var i = 0; i < shape.points.length;i++){
    shape.points[i].y = 2*mirror.y - shape.points[i].y;
  }
}
function cg(shape){
  var x = 0, y = 0;
  for(var i = 0; i < shape.points.length;i++){
    x += shape.points[i].x;
    y += shape.points[i].y;
  }

  return {x:x/shape.points.length,y:y/shape.points.length};
}
function pointInside(point,shape){
  var intersections = 0,a={m:0,b:point.y},b,tmp;
  shape.push(shape[0]); // make it loop
  for(var i = 1; i < shape.length;i++){
    if(point.y <= shape[i-1].y ^ point.y >= shape[i].y ){
      continue;
    }
    b = lineFromPoints(shape[i-1],shape[i]);
    tmp = intersection(a,b);
    if(tmp === true && (point.x <= shape[i-1].x || point.x <= shape[i].x)){ //colinear
      intersections++;
      continue;
    }
    if(tmp === undefined || tmp > point.x ){ //never intersects or wrong side
      continue;
    }
    if(!(tmp <= shape[i-1].x ^ tmp >= shape[i].x )|| tmp === shape[i].x || tmp === shape[i-1].x){ //intersection within boundaries
      intersections++;
    }
  }
  shape.pop();
  return intersections % 2; //even-odd test
}
//intesection between lines (in point slope form) a, b
function intersection(a,b){
  //vertical line intersection
  if(a.x !== undefined && b.x !== undefined){ //both are vertical
    if(a.x === b.x){
      return a.x;
    }else{
      return;
    }
  }else if(a.x !== undefined || b.x !== undefined){ //only one is
    return a.x || b.x || 0;
  }

  if(a.m === b.m){ //parrallel lines
    if(a.b === b.b){
      return true;
    }else{
      return;
    }
  }
  return -(a.b - b.b)/(a.m - b.m); //solve y = m_1 * x + b_1 = m_2 * x + b_2
}

//define line ab in point slope form (vertical line has x=k)
function lineFromPoints(a,b){
  if(a.x===b.x){
    return {x:a.x};
  }
  var out = {m:0,b:0};
  out.m = (b.y-a.y)/(b.x - a.x);
  out.b = a.y-out.m*a.x;
  return out;
}

function Shape(xs,ys){
  this.points = [];
  for(var i = 0; i < xs.length && i < ys.length;i++){
    this.points.push({x:xs[i]*size,y:ys[i]*size});
  }
  this.color = randomColor();
}

//Graphics
var hex = "0123456789ABCDEF";
function HSVtoRGB(h, s, v) { //http://stackoverflow.com/questions/17242144/javascript-convert-hsb-hsv-color-to-rgb-accurately with modifications
    var r, g, b, i, f, p, q, t;
    i = Math.floor(h * 6);
    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    t = v * (1 - (1 - f) * s);
    switch (i % 6) {
        case 0: r = v, g = t, b = p; break;
        case 1: r = q, g = v, b = p; break;
        case 2: r = p, g = v, b = t; break;
        case 3: r = p, g = q, b = v; break;
        case 4: r = t, g = p, b = v; break;
        case 5: r = v, g = p, b = q; break;
    }
    r = r*255|0;
    g = g*255|0;
    b = b*255|0;
    return "#"+hex[r>>4%16]+hex[r%16]+hex[g>>4%16]+hex[r%16]+hex[b>>4%16]+hex[r%16];
}

var h = Math.random(), gold = 0.618033988749895;
function randomColor(){
  h = (h+gold)%1;
  return HSVtoRGB(h,0.6,0.95);
}

<commit_msg>added rotation<commit_after>//Math
function snap(shape){
  var tmp = shape.points[0];
  translate(shape,-(tmp.x+size/2)%size+size/2,-(tmp.y+size/2)%size+size/2);
}
function translate(shape,dx,dy){
  for(var i = 0; i < shape.points.length;i++){
    shape.points[i].x += dx;
    shape.points[i].y += dy;
  }
}

function rotate(shape,direction){
  var center = cg(shape), t;
  center.x -= (center.x + size/2)%size - size/2; //snap to grid
  center.y -= (center.y + size/2)%size - size/2;
  if(direction < 0){
    direction = direction % 4 + 4;
  }
  switch (direction % 4){
    case 3:
      for(var i = 0; i < shape.points.length;i++){
        t =  center.y + center.x - shape.points[i].x;
        shape.points[i].x = center.x - center.y + shape.points[i].y;
        shape.points[i].y = t;
      }
      break;
    case 2:
      for(i = 0; i < shape.points.length;i++){
        shape.points[i].y = 2*center.y - shape.points[i].y;
        shape.points[i].x = 2*center.x - shape.points[i].x;
      }
      break;
    case 1:
      for(i = 0; i < shape.points.length;i++){
        t =  center.y - center.x + shape.points[i].x;
        shape.points[i].x = center.x + center.y - shape.points[i].y;
        shape.points[i].y = t;
      }
      break;
    default:
  }
}
function reflectX(shape){
  var mirror = cg(shape);
  mirror.x -= (mirror.x + size/2)%size - size/2; //snap to grid
  for(var i = 0; i < shape.points.length;i++){
    shape.points[i].x = 2*mirror.x - shape.points[i].x;
  }
}
function reflectY(shape){
  var mirror = cg(shape);
  mirror.y -= (mirror.y + size/2)%size - size/2; //snap to grid
  for(var i = 0; i < shape.points.length;i++){
    shape.points[i].y = 2*mirror.y - shape.points[i].y;
  }
}
function cg(shape){
  var x = 0, y = 0;
  for(var i = 0; i < shape.points.length;i++){
    x += shape.points[i].x;
    y += shape.points[i].y;
  }

  return {x:x/shape.points.length,y:y/shape.points.length};
}
function pointInside(point,shape){
  var intersections = 0,a={m:0,b:point.y},b,tmp;
  shape.push(shape[0]); // make it loop
  for(var i = 1; i < shape.length;i++){
    if(point.y <= shape[i-1].y ^ point.y >= shape[i].y ){
      continue;
    }
    b = lineFromPoints(shape[i-1],shape[i]);
    tmp = intersection(a,b);
    if(tmp === true && (point.x <= shape[i-1].x || point.x <= shape[i].x)){ //colinear
      intersections++;
      continue;
    }
    if(tmp === undefined || tmp > point.x ){ //never intersects or wrong side
      continue;
    }
    if(!(tmp <= shape[i-1].x ^ tmp >= shape[i].x )|| tmp === shape[i].x || tmp === shape[i-1].x){ //intersection within boundaries
      intersections++;
    }
  }
  shape.pop();
  return intersections % 2; //even-odd test
}
//intesection between lines (in point slope form) a, b
function intersection(a,b){
  //vertical line intersection
  if(a.x !== undefined && b.x !== undefined){ //both are vertical
    if(a.x === b.x){
      return a.x;
    }else{
      return;
    }
  }else if(a.x !== undefined || b.x !== undefined){ //only one is
    return a.x || b.x || 0;
  }

  if(a.m === b.m){ //parrallel lines
    if(a.b === b.b){
      return true;
    }else{
      return;
    }
  }
  return -(a.b - b.b)/(a.m - b.m); //solve y = m_1 * x + b_1 = m_2 * x + b_2
}

//define line ab in point slope form (vertical line has x=k)
function lineFromPoints(a,b){
  if(a.x===b.x){
    return {x:a.x};
  }
  var out = {m:0,b:0};
  out.m = (b.y-a.y)/(b.x - a.x);
  out.b = a.y-out.m*a.x;
  return out;
}

function Shape(xs,ys){
  this.points = [];
  for(var i = 0; i < xs.length && i < ys.length;i++){
    this.points.push({x:xs[i]*size,y:ys[i]*size});
  }
  this.color = randomColor();
}

//Graphics
var hex = "0123456789ABCDEF";
function HSVtoRGB(h, s, v) { //http://stackoverflow.com/questions/17242144/javascript-convert-hsb-hsv-color-to-rgb-accurately with modifications
    var r, g, b, i, f, p, q, t;
    i = Math.floor(h * 6);
    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    t = v * (1 - (1 - f) * s);
    switch (i % 6) {
        case 0: r = v, g = t, b = p; break;
        case 1: r = q, g = v, b = p; break;
        case 2: r = p, g = v, b = t; break;
        case 3: r = p, g = q, b = v; break;
        case 4: r = t, g = p, b = v; break;
        case 5: r = v, g = p, b = q; break;
    }
    r = r*255|0;
    g = g*255|0;
    b = b*255|0;
    return "#"+hex[r>>4%16]+hex[r%16]+hex[g>>4%16]+hex[r%16]+hex[b>>4%16]+hex[r%16];
}

var h = Math.random(), gold = 0.618033988749895;
function randomColor(){
  h = (h+gold)%1;
  return HSVtoRGB(h,0.6,0.95);
}

<|endoftext|>


<commit_before>// Miscellaneous helpers
//

// log4js is optional.
function getLogger(label) {
  var log;
  try {
    log = require('log4js')().getLogger(scrub_creds(label || 'audit_couchdb'));
    log.setLevel('info');
  } catch(e) {
    log = { "trace": function() {}
          , "debug": function() {}
          , "info" : console.log
          , "warn" : console.log
          , "error": console.log
          , "fatal": console.log
          }
  }

  // Scrub credentials.
  ; ['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(function(level) {
    var inner = log[level];
    log[level] = function log_scrubbed() {
      var args = Array.prototype.slice.apply(arguments);
      args[0] = scrub_creds(args[0]);
      return inner.apply(this, args);
    }
  })

  return log;
}

function scrub_creds(url) {
  if(typeof url === 'string')
    url = url.replace(/(https?:\/\/)[^:]+:[^@]+@(.*)$/, '$1$2'); // Scrub username and password
  return url;
}

function join_and_fix_slashes() {
  return Array.prototype.map.apply(arguments, [function trimmed(arg) {
    return arg.replace(/^\/+/, "").replace(/\/+$/, "");
  }]).join('/');
}

module.exports = { "getLogger"  : getLogger
                 , "join"       : join_and_fix_slashes
                 };
<commit_msg>Support setLevel() with a stub<commit_after>// Miscellaneous helpers
//

// log4js is optional.
function getLogger(label) {
  var log;
  try {
    log = require('log4js')().getLogger(scrub_creds(label || 'audit_couchdb'));
    log.setLevel('info');
  } catch(e) {
    log = { "trace": function() {}
          , "debug": function() {}
          , "info" : console.log
          , "warn" : console.log
          , "error": console.log
          , "fatal": console.log
          }
    log.setLevel = function noop() {};
  }

  // Scrub credentials.
  ; ['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(function(level) {
    var inner = log[level];
    log[level] = function log_scrubbed() {
      var args = Array.prototype.slice.apply(arguments);
      args[0] = scrub_creds(args[0]);
      return inner.apply(this, args);
    }
  })

  return log;
}

function scrub_creds(url) {
  if(typeof url === 'string')
    url = url.replace(/(https?:\/\/)[^:]+:[^@]+@(.*)$/, '$1$2'); // Scrub username and password
  return url;
}

function join_and_fix_slashes() {
  return Array.prototype.map.apply(arguments, [function trimmed(arg) {
    return arg.replace(/^\/+/, "").replace(/\/+$/, "");
  }]).join('/');
}

module.exports = { "getLogger"  : getLogger
                 , "join"       : join_and_fix_slashes
                 };
<|endoftext|>


<commit_before>"use strict";

let SerialPort = require("serialport"),
    util       = require("util"),
    events     = require('events');

function Meridian(devicetype) {
    if (devicetype == "TN51") {
        // Meridian Technical Note TN51.2
    } else if (devicetype == "218") {
        // Meridian 218 Zone Controller
    } else {
        throw new Error("unsupported device type " + devicetype + " -- Unfortunately, there are many protocols for Meridian control.");
    }
    this.devicetype = devicetype;
    this.seq = 0;
}

util.inherits(Meridian, events.EventEmitter);

let _processw = function() {
    if (!this._port) return;
    if (this._woutstanding) return;
    if (this._qw.length == 0) return;

    this._woutstanding = true;
    console.log("[Meridian] writing:", this._qw[0]);

    this._port.write(this._qw[0] + "\r",
                    (err) => {
                        if (err) return;
                        this._qw.shift();
                        this._woutstanding = false;
                        setTimeout(() => { _processw.call(this); }, 150);
                    });
}

let _tn51 = function(val, cb) {
    this._qw.push(val + "\r");
    _processw.call(this);
};

Meridian.prototype.volume_up = function() {
    if (this.devicetype == "TN51") {
        _tn51.call(this, "VP");
    } else {
        throw new Error("device type " + this.devicetype + " do not support volume_up"); }
};
Meridian.prototype.volume_down = function(val) {
    if (this.devicetype == "TN51") {
        _tn51.call(this, "VM");
    } else {
        throw new Error("device type " + this.devicetype + " do not support volume_down");
    }
};
Meridian.prototype.set_volume = function(val) {
    if (this.devicetype == "TN51") {
	if (this.properties.volume == val) return;
	if (this.volumetimer) clearTimeout(this.volumetimer);
        this.volumetimer = setTimeout(() => {
		_tn51.call(this, "VN" + ("00" + Number(val).toString()).slice(-2));
	}, 50)
    } else {
        throw new Error("device type " + this.devicetype + " do not support set_volume");
    }
};
Meridian.prototype.standby = function(val) {
    if (this.devicetype == "TN51") {
        _tn51.call(this, "SB");
    } else {
        throw new Error("device type " + this.devicetype + " do not support standby");
    }
};
Meridian.prototype.set_source = function(val) {
    if (this.devicetype == "TN51") {
        _tn51.call(this, val.slice(0,2));
    } else {
        throw new Error("device type " + this.devicetype + " do not support set_source");
    }
};
Meridian.prototype.mute = function() {
    if (this.devicetype == "TN51") {
        _tn51.call(this, "MU");
    } else {
        throw new Error("device type " + this.devicetype + " do not support mute"); }
};

Meridian.prototype.init = function(port, opts, closecb) {
    let self = this;

    this._qw = [];
    this._woutstanding = false;

    this.properties = { volume: opts.volume || 1, source: opts.source || 'SB' };

    this.initializing = true;

    if (this.devicetype == "TN51") {
        this._port = new SerialPort(port, {
            baudRate: opts.baud || 9600,
            parser:   SerialPort.parsers.readline("\r")
        });

        this._port.on('data', data => {
	    data = data.trim();
	    console.log('[Meridian] received:', data);

	    if (/^V\. *([0-9][0-9]*) *$/.test(data)) {
	       let val = Number(data.trim().replace(/^V\. *([0-9][0-9]*) *$/, "$1"));
	       if (this.properties.volume != val) {
		   this.properties.volume = val;
	           this.emit('volume', val);
	       }

	    } else if (/^Standby$/.test(data)) {
	        let val = "SB";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Muted$/.test(data)) {
	        let val = "MU";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^CD$/.test(data)) {
	        let val = "CD";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Aux$/.test(data)) {
	        let val = "AX";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^SLS$/.test(data)) {
	        let val = "MS";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^CD$/.test(data)) {
	        let val = "CD";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Radio$/.test(data)) {
	        let val = "RD";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^TV$/.test(data)) {
	        let val = "TV";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Tape$/.test(data)) {
	        let val = "TA";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Disc$/.test(data)) {
	        let val = "DC";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Cable$/.test(data)) {
	        let val = "CB";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Sat$/.test(data)) {
	        let val = "SA";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^iPod$/.test(data)) {
	        let val = "IP";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Game$/.test(data)) {
	        let val = "GA";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^V1$/.test(data)) {
	        let val = "V1";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^V2$/.test(data)) {
	        let val = "V2";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^USB$/.test(data)) {
	        let val = "US";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Mixer$/.test(data)) {
	        let val = "MX";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else {
	        console.log("[Meridian] Unknown data received:", data);
	    }

	    if (this.initializing) {
		this.initializing = false;
		this.emit('connected');
            }
        });
    } else {
        throw new Error("unsupported device type " + devicetype + " -- Someone forgot to write some code here.");
    }

    let timer = setTimeout(() => {
	if (this.initializing) {
            this.initializing = false;
	    this.emit('connected');
	}
    }, 3000);
    this._port.on('open', err => {
        this.emit('preconnected');

        if (this.devicetype == "TN51") {
            _tn51.call(this, this.properties.source);
            _tn51.call(this, "VN" + ("00" + Number(this.properties.volume).toString()).slice(-2));
        }
    });

    this._port.on('close',      ()  => { this._port.close(() => { this._port = undefined; if (closecb) { var cb2 = closecb; closecb = undefined; cb2('close');      } }) });
    this._port.on('error',      err => { this._port.close(() => { this._port = undefined; if (closecb) { var cb2 = closecb; closecb = undefined; cb2('error');      } }) });
    this._port.on('disconnect', ()  => { this._port.close(() => { this._port = undefined; if (closecb) { var cb2 = closecb; closecb = undefined; cb2('disconnect'); } }) });
};

Meridian.prototype.start = function(opts) {
    this.seq++;

    let closecb = (why) => {
        this.emit('disconnected');
        if (why != 'close') {
            var seq = ++this.seq;
            setTimeout(() => {
                if (seq != this.seq) return;
                this.start(opts.port, opts);
            }, 1000);
        }
    };

    if (this._port) {
        this._port.close(() => {
            this.init(opts.port, opts, closecb);
        });
    } else {
        this.init(opts.port, opts, closecb);
    }
};

Meridian.prototype.stop = function() {
    this.seq++;
    if (this._port)
        this._port.close(() => {});
};

exports = module.exports = Meridian;

<commit_msg>new api<commit_after>"use strict";

let SerialPort = require("serialport"),
    util       = require("util"),
    events     = require('events');

function Meridian(devicetype) {
    if (devicetype == "TN51") {
        // Meridian Technical Note TN51.2
    } else if (devicetype == "218") {
        // Meridian 218 Zone Controller
    } else {
        throw new Error("unsupported device type " + devicetype + " -- Unfortunately, there are many protocols for Meridian control.");
    }
    this.devicetype = devicetype;
    this.seq = 0;
}

util.inherits(Meridian, events.EventEmitter);

let _processw = function() {
    if (!this._port) return;
    if (this._woutstanding) return;
    if (this._qw.length == 0) return;

    this._woutstanding = true;
    console.log("[Meridian] writing:", this._qw[0]);

    this._port.write(this._qw[0] + "\r",
                    (err) => {
                        if (err) return;
                        this._qw.shift();
                        this._woutstanding = false;
                        setTimeout(() => { _processw.call(this); }, 150);
                    });
}

let _tn51 = function(val, cb) {
    this._qw.push(val + "\r");
    _processw.call(this);
};

Meridian.prototype.volume_up = function() {
    if (this.devicetype == "TN51") {
        _tn51.call(this, "VP");
    } else {
        throw new Error("device type " + this.devicetype + " do not support volume_up"); }
};
Meridian.prototype.volume_down = function(val) {
    if (this.devicetype == "TN51") {
        _tn51.call(this, "VM");
    } else {
        throw new Error("device type " + this.devicetype + " do not support volume_down");
    }
};
Meridian.prototype.set_volume = function(val) {
    if (this.devicetype == "TN51") {
	if (this.properties.volume == val) return;
	if (this.volumetimer) clearTimeout(this.volumetimer);
        this.volumetimer = setTimeout(() => {
		_tn51.call(this, "VN" + ("00" + Number(val).toString()).slice(-2));
	}, 50)
    } else {
        throw new Error("device type " + this.devicetype + " do not support set_volume");
    }
};
Meridian.prototype.standby = function(val) {
    if (this.devicetype == "TN51") {
        _tn51.call(this, "SB");
    } else {
        throw new Error("device type " + this.devicetype + " do not support standby");
    }
};
Meridian.prototype.set_source = function(val) {
    if (this.devicetype == "TN51") {
        _tn51.call(this, val.slice(0,2));
    } else {
        throw new Error("device type " + this.devicetype + " do not support set_source");
    }
};
Meridian.prototype.mute = function() {
    if (this.devicetype == "TN51") {
        _tn51.call(this, "MU");
    } else {
        throw new Error("device type " + this.devicetype + " do not support mute"); }
};

Meridian.prototype.init = function(opts, closecb) {
    let self = this;

    this._qw = [];
    this._woutstanding = false;

    this.properties = { volume: opts.volume || 1, source: opts.source || 'SB' };

    this.initializing = true;

    if (this.devicetype == "TN51") {
        this._port = new SerialPort(opts.port, {
            baudRate: opts.baud || 9600,
            parser:   SerialPort.parsers.readline("\r")
        });

        this._port.on('data', data => {
	    data = data.trim();
	    console.log('[Meridian] received:', data);

	    if (/^V\. *([0-9][0-9]*) *$/.test(data)) {
	       let val = Number(data.trim().replace(/^V\. *([0-9][0-9]*) *$/, "$1"));
	       if (this.properties.volume != val) {
		   this.properties.volume = val;
	           this.emit('volume', val);
	       }

	    } else if (/^Standby$/.test(data)) {
	        let val = "SB";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Muted$/.test(data)) {
	        let val = "MU";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^CD$/.test(data)) {
	        let val = "CD";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Aux$/.test(data)) {
	        let val = "AX";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^SLS$/.test(data)) {
	        let val = "MS";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^CD$/.test(data)) {
	        let val = "CD";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Radio$/.test(data)) {
	        let val = "RD";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^TV$/.test(data)) {
	        let val = "TV";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Tape$/.test(data)) {
	        let val = "TA";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Disc$/.test(data)) {
	        let val = "DC";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Cable$/.test(data)) {
	        let val = "CB";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Sat$/.test(data)) {
	        let val = "SA";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^iPod$/.test(data)) {
	        let val = "IP";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Game$/.test(data)) {
	        let val = "GA";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^V1$/.test(data)) {
	        let val = "V1";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^V2$/.test(data)) {
	        let val = "V2";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^USB$/.test(data)) {
	        let val = "US";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else if (/^Mixer$/.test(data)) {
	        let val = "MX";
	        if (this.properties.source != val) { this.properties.source = val; this.emit('source', val); }

	    } else {
	        console.log("[Meridian] Unknown data received:", data);
	    }

	    if (this.initializing) {
		this.initializing = false;
		this.emit('connected');
            }
        });
    } else {
        throw new Error("unsupported device type " + devicetype + " -- Someone forgot to write some code here.");
    }

    let timer = setTimeout(() => {
	if (this.initializing) {
            this.initializing = false;
	    this.emit('connected');
	}
    }, 3000);
    this._port.on('open', err => {
        this.emit('preconnected');

        if (this.devicetype == "TN51") {
            _tn51.call(this, this.properties.source);
            _tn51.call(this, "VN" + ("00" + Number(this.properties.volume).toString()).slice(-2));
        }
    });

    this._port.on('close',      ()  => { this._port.close(() => { this._port = undefined; if (closecb) { var cb2 = closecb; closecb = undefined; cb2('close');      } }) });
    this._port.on('error',      err => { this._port.close(() => { this._port = undefined; if (closecb) { var cb2 = closecb; closecb = undefined; cb2('error');      } }) });
    this._port.on('disconnect', ()  => { this._port.close(() => { this._port = undefined; if (closecb) { var cb2 = closecb; closecb = undefined; cb2('disconnect'); } }) });
};

Meridian.prototype.start = function(opts) {
    this.seq++;

    let closecb = (why) => {
        this.emit('disconnected');
        if (why != 'close') {
            var seq = ++this.seq;
            setTimeout(() => {
                if (seq != this.seq) return;
                this.start(opts);
            }, 1000);
        }
    };

    if (this._port) {
        this._port.close(() => {
            this.init(opts, closecb);
        });
    } else {
        this.init(opts, closecb);
    }
};

Meridian.prototype.stop = function() {
    this.seq++;
    if (this._port)
        this._port.close(() => {});
};

exports = module.exports = Meridian;

<|endoftext|>


<commit_before>var _ = require("lodash");
var Bacon = require("baconjs");

var Logger = require("../logger.js");

var Application = module.exports;

Application.getInstanceType = function(api, type) {
  var s_types = api.products.instances.get().send();

  return s_types.flatMapLatest(function(types) {
    var instanceType = _.find(types, function(instanceType) {
      return instanceType.type == type;
    });

    return instanceType ? Bacon.once(instanceType) : new Bacon.Error(type + " type does not exist.");
  });
};

Application.create = function(api, name, instanceType, region) {
  Logger.debug("Create the application…");
  return api.owner().applications.post().send(JSON.stringify({
    "deploy": "git",
    "description": name,
    "instanceType": instanceType.type,
    "instanceVersion": instanceType.version,
    "maxFlavor": "S",
    "maxInstances": 1,
    "minFlavor": "S",
    "minInstances": 1,
    "name": name,
    "zone": region
  }));
};

Application.get = function(api, appId) {
  Logger.debug("Get information for the app: " + appId);
  return api.owner().applications._.get().withParams([appId]).send();
};
<commit_msg>Support apps in organisations for Application.get<commit_after>var _ = require("lodash");
var Bacon = require("baconjs");

var Logger = require("../logger.js");

var Application = module.exports;

Application.getInstanceType = function(api, type) {
  var s_types = api.products.instances.get().send();

  return s_types.flatMapLatest(function(types) {
    var instanceType = _.find(types, function(instanceType) {
      return instanceType.type == type;
    });

    return instanceType ? Bacon.once(instanceType) : new Bacon.Error(type + " type does not exist.");
  });
};

Application.create = function(api, name, instanceType, region) {
  Logger.debug("Create the application…");
  return api.owner().applications.post().send(JSON.stringify({
    "deploy": "git",
    "description": name,
    "instanceType": instanceType.type,
    "instanceVersion": instanceType.version,
    "maxFlavor": "S",
    "maxInstances": 1,
    "minFlavor": "S",
    "minInstances": 1,
    "name": name,
    "zone": region
  }));
};

Application.get = function(api, appId, orgaId) {
  Logger.debug("Get information for the app: " + appId);

  if(orgaId) {
    return api.organisations._.get().withParams([orgaId]).send()
      .map(function(organisation) {
        return _.find(organisation.apps, function(app) {
          return app.id === appId;
        });
      });
  } else {
    return api.owner().applications._.get().withParams([appId]).send();
  }
};
<|endoftext|>