<commit_before>import Phaser from 'Phaser';
import state from 'states/state';
import { each, any, invoke, includes } from 'lodash';
import { hit_world, hit_puck, reset_puck } from 'collision';
import { inactive, playing, choosing_letter, choosing_name, logging_in } from 'player-state-checkers';
import input from 'input';
import leaderboard from 'leaderboard';
import players from 'players';
import * as conf from 'conf';
import * as scores from 'scores';
import * as move from 'commands/move-paddle';
import * as scorecards from 'scorecards';

let puck;
let paddles = {};
let player_codes = ['n', 's', 'e', 'w'];
let paused = false;

function check_out_of_bounds(game, puck) {
    if (puck.body.position.y < game.world.bounds.top) {
        return 'n';
    }
    else if (puck.body.position.y + puck.body.height > game.world.bounds.bottom) {
        return 's';
    }
    else if (puck.body.position.x + puck.body.width > game.world.bounds.right) {
        return 'e';
    }
    else if (puck.body.position.x < game.world.bounds.left) {
        return 'w';
    }
    else {
        return;
    }
}

function set_body_to_sprite_size(sprite, rotate90) {
    if (rotate90) {
        sprite.body.setSize(sprite.height, sprite.width);
    }
    else {
        sprite.body.setSize(sprite.width, sprite.height);
    }
}

function update_bg_color(game) {
    game.stage.backgroundColor = conf.BG_COLOR_CURRENT.toHexString();
}

function log_in_if_start_pressed(player) {
    if (inactive(players[player]) && input.start_pressed(players[player].pad)) {
        players[player].choose_letter.execute();
    }
}

function log_in_if_start_clicked(player) {
    if (inactive(players[player])) {
        players[player].choose_letter.execute();
    }
}

function navigate_ui_if_logging_in(player) {
    if (choosing_letter(players[player])) {
        if (input.up_once(players[player].pad)) {
            leaderboard.select_prev_letter(player);
        }
        if (input.down_once(players[player].pad)) {
            leaderboard.select_next_letter(player);
        }
        if (input.left_once(players[player].pad) || input.b(players[player].pad)) {
            // player presses left or B, go back to inactive
            players[player].choose_letter.undo();
        }
        if (input.right_once(players[player].pad) || input.a(players[player].pad)) {
            // player presses right or A, choose this letter
            players[player].choose_letter.done();
        }
    }
    else if (choosing_name(players[player])) {
        if (input.up_once(players[player].pad)) {
            leaderboard.select_prev_player(player);
        }
        if (input.down_once(players[player].pad)) {
            leaderboard.select_next_player(player);
        }
        if (input.left_once(players[player].pad) || input.b(players[player].pad)) {
            // player presses left or B, go back to inactive
            players[player].choose_name.undo();
        }
        if (input.right_once(players[player].pad) || input.a(players[player].pad)) {
            // player presses right or A, choose this player
            players[player].choose_name.done();
        }
    }
}

function execute_powerup_if_b(player) {
    if (playing(players[player]) && input.b(players[player].pad)) {
        players[player].execute_powerup();
    }
}

function rotate_powerup_if_a(player) {
    if (playing(players[player]) && input.a(players[player].pad)) {
        players[player].rotate_powerups();
    }
}

function move_paddle(player) {
    // move the player with gamepad if player is active.  else let AI decide
    // how to move.

    let pl = players[player];
    let pad = paddles[player];

    if (playing(pl)) {
        // handle gamepad controls
        if (input[pl.neg](pl.pad)) {
            move[pl.neg].execute(pl, pad);
        }
        if (input[pl.pos](pl.pad)) {
            move[pl.pos].execute(pl, pad);
        }

        // handle keyboard controls
        if (input.left_kb() || input.up_kb()) {
            move[pl.neg].execute(pl, pad);
        }

        if (input.right_kb() || input.down_kb()) {
            move[pl.pos].execute(pl, pad);
        }
    }
    else {

        // AI IS HERE!
        // find the distance between the puck and the paddle, but only on the
        // axis of movement
        let on_axis        = pl.axis;
        let on_axis_d      = puck.body.center[on_axis]  - pad.body.center[on_axis];
        let motive_surface = Math.max(pad.body.height, pad.body.width) / (1/conf.AI_LAZINESS);
        let move_speed     = Math.min(1, Math.abs(on_axis_d / motive_surface));

        move[on_axis_d > 0 ? pl.pos : pl.neg].execute(pl, pad, move_speed);
    }
}

function press_start_click_handler() {
    if (!any(players, 'playing')) {
        let player = this.getAttribute('data-player');
        log_in_if_start_clicked(player);
    }
}

function pause_click_handler() {
    paused = !paused;

    if (!paused) {
        this.style.opacity = 0;
        reset_puck(puck);
    } else {
        this.style.opacity = 1;
    }
}

function score_timer() {
    let updateInterval = conf.SCORING_MS_PER_POINT;

    function tick() {
        for (var key in players) {
            if (playing(players[key])) {
                scores[key].add_score();
            }
        }
    }

    setInterval(tick, updateInterval);
}

export default class play_state extends state {
    constructor() {
        super('play');
    }
    create(game) {

        scorecards.create(players);

        game.physics.startSystem(Phaser.Physics.ARCADE);

        scores.reset_all();

        puck = game.add.sprite( game.world.centerX, game.world.centerY, 'puck');

        // dangit, players need references to puck, to pass into powerups that affect the puck
        // I shoulda just made everything globa, for a game this size. :)
        players.n.puck = puck;
        players.s.puck = puck;
        players.e.puck = puck;
        players.w.puck = puck;
        // end of dangit

        paddles.n = game.add.sprite( game.world.centerX, conf.PADDLE_PLACEMENT_WORLD_PADDING, 'paddle-blue');
        paddles.s = game.add.sprite( game.world.centerX, game.world.height - conf.PADDLE_PLACEMENT_WORLD_PADDING - 20, 'paddle-green');
        paddles.e = game.add.sprite( game.world.width - conf.PADDLE_PLACEMENT_WORLD_PADDING, game.world.centerY, 'paddle-yellow');
        paddles.w = game.add.sprite( conf.PADDLE_PLACEMENT_WORLD_PADDING + 20, game.world.centerY, 'paddle-red');

        paddles.n.addChild(game.make.sprite(0, 0, 'paddle-blue'));

        paddles.w.angle = 90;
        paddles.e.angle = 90;

        game.physics.enable([puck, paddles.n, paddles.s, paddles.e, paddles.w], Phaser.Physics.ARCADE);

        puck.name = 'PUCK';
        puck.body.setSize(20, 20, puck.height/2 - 10, puck.width/2 - 10);

        reset_puck(puck);

        paddles.n.name = 'n';
        paddles.s.name = 's';
        paddles.e.name = 'e';
        paddles.w.name = 'w';
        paddles.n.body.immovable = true;
        paddles.s.body.immovable = true;
        paddles.e.body.immovable = true;
        paddles.w.body.immovable = true;
        paddles.n.body.collideWorldBounds = true;
        paddles.s.body.collideWorldBounds = true;
        paddles.e.body.collideWorldBounds = true;
        paddles.w.body.collideWorldBounds = true;
        set_body_to_sprite_size(paddles.n);
        set_body_to_sprite_size(paddles.s);
        set_body_to_sprite_size(paddles.e, true);
        set_body_to_sprite_size(paddles.w, true);

        // not sure why this offset is needed, but it lines up the hitboxes with
        // the sprites.
        paddles.w.body.offset.x = -20;
        paddles.e.body.offset.x = -20;

        // pad_n.body.offset.y = +20;
        // pad_s.body.offset.y = +20;

        // puck.body.collideWorldBounds = true;
        puck.body.bounce.setTo(1, 1);

        update_bg_color(game);

        // add points to players periodically
        score_timer();

        let press_start_elements = document.querySelectorAll('.press-start');
        invoke(press_start_elements, 'addEventListener', 'click', press_start_click_handler, false);

        let pause_button = document.querySelector('#pause-btn');
        pause_button.addEventListener('click', pause_click_handler, false);
    }
    update(game) {

        game.physics.arcade.collide(puck, [paddles.n, paddles.s, paddles.e, paddles.w], hit_puck, null, this);

        paddles.n.body.velocity.setMagnitude(0);
        paddles.s.body.velocity.setMagnitude(0);
        paddles.e.body.velocity.setMagnitude(0);
        paddles.w.body.velocity.setMagnitude(0);

        update_bg_color(game);

        let oob = check_out_of_bounds(game, puck);
        if (oob) {
            hit_world(puck, oob);
        }

        /*
         * this is weird, let's not forget about this. this should be
         * handled in the pause_click_handler but for some reason the puck
         * is not being placed back in the center of the game
         */
        if (paused) {
            reset_puck(puck, false);
        }

        // check for players pressing start to join the game

        each(player_codes, log_in_if_start_pressed);

        // check for gamepad input during login

        each(player_codes, navigate_ui_if_logging_in);

        // map input to commands

        each(player_codes, move_paddle);
        each(player_codes, execute_powerup_if_b);
        each(player_codes, rotate_powerup_if_a);

        // update the player status scorecards

        scorecards.update(players);
    }

    render(game) {
        if (conf.DEBUG) {
            game.debug.body(puck);
            game.debug.body(paddles.n);
            game.debug.body(paddles.s);
            game.debug.body(paddles.e);
            game.debug.body(paddles.w);
        }
    }
}
<commit_msg>updated scoring timer<commit_after>import Phaser from 'Phaser';
import state from 'states/state';
import { each, any, invoke, includes } from 'lodash';
import { hit_world, hit_puck, reset_puck } from 'collision';
import { inactive, playing, choosing_letter, choosing_name, logging_in } from 'player-state-checkers';
import input from 'input';
import leaderboard from 'leaderboard';
import players from 'players';
import * as conf from 'conf';
import * as scores from 'scores';
import * as move from 'commands/move-paddle';
import * as scorecards from 'scorecards';

let puck;
let paddles = {};
let player_codes = ['n', 's', 'e', 'w'];
let paused = false;

function check_out_of_bounds(game, puck) {
    if (puck.body.position.y < game.world.bounds.top) {
        return 'n';
    }
    else if (puck.body.position.y + puck.body.height > game.world.bounds.bottom) {
        return 's';
    }
    else if (puck.body.position.x + puck.body.width > game.world.bounds.right) {
        return 'e';
    }
    else if (puck.body.position.x < game.world.bounds.left) {
        return 'w';
    }
    else {
        return;
    }
}

function set_body_to_sprite_size(sprite, rotate90) {
    if (rotate90) {
        sprite.body.setSize(sprite.height, sprite.width);
    }
    else {
        sprite.body.setSize(sprite.width, sprite.height);
    }
}

function update_bg_color(game) {
    game.stage.backgroundColor = conf.BG_COLOR_CURRENT.toHexString();
}

function log_in_if_start_pressed(player) {
    if (inactive(players[player]) && input.start_pressed(players[player].pad)) {
        players[player].choose_letter.execute();
    }
}

function log_in_if_start_clicked(player) {
    if (inactive(players[player])) {
        players[player].choose_letter.execute();
    }
}

function navigate_ui_if_logging_in(player) {
    if (choosing_letter(players[player])) {
        if (input.up_once(players[player].pad)) {
            leaderboard.select_prev_letter(player);
        }
        if (input.down_once(players[player].pad)) {
            leaderboard.select_next_letter(player);
        }
        if (input.left_once(players[player].pad) || input.b(players[player].pad)) {
            // player presses left or B, go back to inactive
            players[player].choose_letter.undo();
        }
        if (input.right_once(players[player].pad) || input.a(players[player].pad)) {
            // player presses right or A, choose this letter
            players[player].choose_letter.done();
        }
    }
    else if (choosing_name(players[player])) {
        if (input.up_once(players[player].pad)) {
            leaderboard.select_prev_player(player);
        }
        if (input.down_once(players[player].pad)) {
            leaderboard.select_next_player(player);
        }
        if (input.left_once(players[player].pad) || input.b(players[player].pad)) {
            // player presses left or B, go back to inactive
            players[player].choose_name.undo();
        }
        if (input.right_once(players[player].pad) || input.a(players[player].pad)) {
            // player presses right or A, choose this player
            players[player].choose_name.done();
        }
    }
}

function execute_powerup_if_b(player) {
    if (playing(players[player]) && input.b(players[player].pad)) {
        players[player].execute_powerup();
    }
}

function rotate_powerup_if_a(player) {
    if (playing(players[player]) && input.a(players[player].pad)) {
        players[player].rotate_powerups();
    }
}

function move_paddle(player) {
    // move the player with gamepad if player is active.  else let AI decide
    // how to move.

    let pl = players[player];
    let pad = paddles[player];

    if (playing(pl)) {
        // handle gamepad controls
        if (input[pl.neg](pl.pad)) {
            move[pl.neg].execute(pl, pad);
        }
        if (input[pl.pos](pl.pad)) {
            move[pl.pos].execute(pl, pad);
        }

        // handle keyboard controls
        if (input.left_kb() || input.up_kb()) {
            move[pl.neg].execute(pl, pad);
        }

        if (input.right_kb() || input.down_kb()) {
            move[pl.pos].execute(pl, pad);
        }
    }
    else {

        // AI IS HERE!
        // find the distance between the puck and the paddle, but only on the
        // axis of movement
        let on_axis        = pl.axis;
        let on_axis_d      = puck.body.center[on_axis]  - pad.body.center[on_axis];
        let motive_surface = Math.max(pad.body.height, pad.body.width) / (1/conf.AI_LAZINESS);
        let move_speed     = Math.min(1, Math.abs(on_axis_d / motive_surface));

        move[on_axis_d > 0 ? pl.pos : pl.neg].execute(pl, pad, move_speed);
    }
}

function press_start_click_handler() {
    if (!any(players, 'playing')) {
        let player = this.getAttribute('data-player');
        log_in_if_start_clicked(player);
    }
}

function pause_click_handler() {
    paused = !paused;

    if (!paused) {
        this.style.opacity = 0;
        reset_puck(puck);
    } else {
        this.style.opacity = 1;
    }
}

function tick() {
    for (var key in players) {
        if (playing(players[key])) {
            scores[key].add_score();
        }
    }
}

export default class play_state extends state {
    constructor() {
        super('play');
    }
    create(game) {

        scorecards.create(players);

        game.physics.startSystem(Phaser.Physics.ARCADE);

        scores.reset_all();

        puck = game.add.sprite( game.world.centerX, game.world.centerY, 'puck');

        // dangit, players need references to puck, to pass into powerups that affect the puck
        // I shoulda just made everything globa, for a game this size. :)
        players.n.puck = puck;
        players.s.puck = puck;
        players.e.puck = puck;
        players.w.puck = puck;
        // end of dangit

        paddles.n = game.add.sprite( game.world.centerX, conf.PADDLE_PLACEMENT_WORLD_PADDING, 'paddle-blue');
        paddles.s = game.add.sprite( game.world.centerX, game.world.height - conf.PADDLE_PLACEMENT_WORLD_PADDING - 20, 'paddle-green');
        paddles.e = game.add.sprite( game.world.width - conf.PADDLE_PLACEMENT_WORLD_PADDING, game.world.centerY, 'paddle-yellow');
        paddles.w = game.add.sprite( conf.PADDLE_PLACEMENT_WORLD_PADDING + 20, game.world.centerY, 'paddle-red');

        paddles.n.addChild(game.make.sprite(0, 0, 'paddle-blue'));

        paddles.w.angle = 90;
        paddles.e.angle = 90;

        game.physics.enable([puck, paddles.n, paddles.s, paddles.e, paddles.w], Phaser.Physics.ARCADE);

        puck.name = 'PUCK';
        puck.body.setSize(20, 20, puck.height/2 - 10, puck.width/2 - 10);

        reset_puck(puck);

        paddles.n.name = 'n';
        paddles.s.name = 's';
        paddles.e.name = 'e';
        paddles.w.name = 'w';
        paddles.n.body.immovable = true;
        paddles.s.body.immovable = true;
        paddles.e.body.immovable = true;
        paddles.w.body.immovable = true;
        paddles.n.body.collideWorldBounds = true;
        paddles.s.body.collideWorldBounds = true;
        paddles.e.body.collideWorldBounds = true;
        paddles.w.body.collideWorldBounds = true;
        set_body_to_sprite_size(paddles.n);
        set_body_to_sprite_size(paddles.s);
        set_body_to_sprite_size(paddles.e, true);
        set_body_to_sprite_size(paddles.w, true);

        // not sure why this offset is needed, but it lines up the hitboxes with
        // the sprites.
        paddles.w.body.offset.x = -20;
        paddles.e.body.offset.x = -20;

        // pad_n.body.offset.y = +20;
        // pad_s.body.offset.y = +20;

        // puck.body.collideWorldBounds = true;
        puck.body.bounce.setTo(1, 1);

        update_bg_color(game);

        // add points to players periodically
        game.time.events.loop(Phaser.Timer.SECOND * 3, tick, this);

        let press_start_elements = document.querySelectorAll('.press-start');
        invoke(press_start_elements, 'addEventListener', 'click', press_start_click_handler, false);

        let pause_button = document.querySelector('#pause-btn');
        pause_button.addEventListener('click', pause_click_handler, false);
    }
    update(game) {

        game.physics.arcade.collide(puck, [paddles.n, paddles.s, paddles.e, paddles.w], hit_puck, null, this);

        paddles.n.body.velocity.setMagnitude(0);
        paddles.s.body.velocity.setMagnitude(0);
        paddles.e.body.velocity.setMagnitude(0);
        paddles.w.body.velocity.setMagnitude(0);

        update_bg_color(game);

        let oob = check_out_of_bounds(game, puck);
        if (oob) {
            hit_world(puck, oob);
        }

        /*
         * this is weird, let's not forget about this. this should be
         * handled in the pause_click_handler but for some reason the puck
         * is not being placed back in the center of the game
         */
        if (paused) {
            reset_puck(puck, false);
        }

        // check for players pressing start to join the game

        each(player_codes, log_in_if_start_pressed);

        // check for gamepad input during login

        each(player_codes, navigate_ui_if_logging_in);

        // map input to commands

        each(player_codes, move_paddle);
        each(player_codes, execute_powerup_if_b);
        each(player_codes, rotate_powerup_if_a);

        // update the player status scorecards

        scorecards.update(players);
    }

    render(game) {
        if (conf.DEBUG) {
            game.debug.body(puck);
            game.debug.body(paddles.n);
            game.debug.body(paddles.s);
            game.debug.body(paddles.e);
            game.debug.body(paddles.w);
        }
    }
}
<|endoftext|>


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

import {LitElement, html} from "lit";
import UtilsNew from "../../core/utilsNew.js";
import LitUtils from "../commons/utils/lit-utils.js";
import NotificationUtils from "../commons/utils/notification-utils.js";

export default class OpencgaJobDetailLog extends LitElement {

    constructor() {
        super();

        this._init();
    }

    createRenderRoot() {
        return this;
    }

    static get properties() {
        return {
            opencgaSession: {
                type: Object
            },
            job: {
                type: Object
            },
            active: {
                type: Boolean
            },
            config: {
                type: Object
            }
        };
    }

    _init() {
        this._prefix = "sf-" + UtilsNew.randomString(6);

        this.content = null;
        this._config = {...this.getDefaultConfig(), ...this.config};
    }

    connectedCallback() {
        super.connectedCallback();
    }

    async updated(changedProperties) {
        if (changedProperties.has("job")) {
            this.jobId = this.job.id;
            if (this.active) {
                this.fetchContent(this.job, {command: this._config.command, type: this._config.type});
            }
        }

        if (changedProperties.has("active")) {
            this.content = null;
            this.requestUpdate();
            await this.updateComplete;
            // todo this should call fetchContent iff the job has changed
            // console.log("new job = old job ", this.active && this.jobId === this.job.id)
            if (this.active) {
                this.fetchContent(this.job, {command: this._config.command, type: this._config.type});
            } else {
                this.clearReload();
            }
        }
    }

    setCommand(command) {
        this._config.command = command;
        this.clearReload();
        this.fetchContent(this.job, {command: this._config.command, type: this._config.type});
        this.setReloadInterval();
    }

    setType(type) {
        this._config.type = type;
        this.clearReload();
        this.fetchContent(this.job, {command: this._config.command, type: this._config.type});
        this.setReloadInterval();
    }

    // setInterval makes sense only in case of Tail log
    setReloadInterval() {
        if (this.active && this._config.command === "tail" && this.job.internal.status.name === "RUNNING") {
            // console.log("setting interval");
            this.requestUpdate();
            this.interval = setInterval(() => {
                // this.content += "\n" + UtilsNew.randomString(6);
                if ($(".jobs-details-log", this).is(":visible")) {
                    // tail call is actually head (after the first tail call)
                    this.fetchContent(this.job, {command: "head", offset: this.contentOffset}, true);
                } else {
                    this.clearReload();
                }
                this.requestUpdate();
            }, 10000);
        }
    }

    clearReload() {
        this.contentOffset = 0;
        clearInterval(this.interval);
        this.requestUpdate();
    }

    async fetchContent(job, params = {}, append = false) {
        const statusWithoutLogs = ["PENDING", "ABORTED", "RUNNING", "QUEUED"];
        if (!append) {
            this.content = "";
        }
        this.loading = true;
        this.requestUpdate();
        await this.updateComplete;

        const command = params.command || this._config.command;
        // const offset = params.offset || 0;
        // console.log("request ", "command", command, "params", params, "offset", offset, "append", append);
        if (!statusWithoutLogs?.includes(job?.internal?.status?.id?.toUpperCase())) {
            this.opencgaSession.opencgaClient.jobs()[command + "Log"](job.id, {
                study: this.opencgaSession.study.fqn,
                lines: this._config.lines,
                type: this._config.type,
                offset: params.offset || 0,
                ...params
            }).then(restResponse => {
                const result = restResponse.getResult(0);
                if (result.content) {
                // if command=tail this is the first tail call (the subsequents will be head)
                    if (command === "tail") {
                        this.contentOffset = result.offset;
                    }
                    // append is true only in case of tail command (it has been kept as separate param to quickly have one-shot Tail call button (not live), just in case)
                    if (append) {
                        if (this.contentOffset !== result.offset) {
                            this.content = this.content + result.content;
                            this.contentOffset = result.offset;
                        }
                    } else {
                        this.content = result.content + "\n";
                    }
                } else {
                // this.content = "No content";
                }
            }).catch(response => {
                this.content = "An error occurred while fetching log.\n";
                NotificationUtils.dispatch(this, NotificationUtils.NOTIFY_RESPONSE, response);
            }).finally(() => {
                this.loading = false;
                this.requestUpdate();
            });
        } else {
            this.content = `Job is ${job?.internal?.status?.id}\n`;
            this.loading = false;
            this.requestUpdate();
        }
    }

    onScroll(e) {
        // TODO custom infinite scroll
        // console.log(e)
    }

    getDefaultConfig() {
        return {
            command: "head",
            type: "stderr",
            lines: 50
        };
    }

    render() {
        return html`

                    <button type="button" class="btn btn-default btn-small ripple ${this._config.command === "head" ? "active" : ""}" @click="${() => this.setCommand("head")}">
                         Head
                    </button>
                    <button type="button" class="btn btn-default btn-small ripple ${this._config.command === "tail" ? "active" : ""}" @click="${() => this.setCommand("tail")}">
                         Tail
                    </button>

                    <button type="button" class="btn btn-default btn-small ripple ${this._config.type === "stderr" ? "active" : ""}" @click="${() => this.setType("stderr")}">
                         Stderr
                    </button>
                    <button type="button" class="btn btn-default btn-small ripple ${this._config.type === "stdout" ? "active" : ""}" @click="${() => this.setType("stdout")}">
                         Stout
                    </button>

${this.content}\n${this.loading || (this.content && this._config.command === "tail") ? html`<div class="cursor"></div>` : ""}

        `;
    }

}

customElements.define("opencga-job-detail-log", OpencgaJobDetailLog);
<commit_msg>wc: fix typo in job-detail-log component #TASK-1360<commit_after>/**
 * Copyright 2015-2019 OpenCB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {LitElement, html} from "lit";
import UtilsNew from "../../core/utilsNew.js";
import LitUtils from "../commons/utils/lit-utils.js";
import NotificationUtils from "../commons/utils/notification-utils.js";

export default class OpencgaJobDetailLog extends LitElement {

    constructor() {
        super();

        this._init();
    }

    createRenderRoot() {
        return this;
    }

    static get properties() {
        return {
            opencgaSession: {
                type: Object
            },
            job: {
                type: Object
            },
            active: {
                type: Boolean
            },
            config: {
                type: Object
            }
        };
    }

    _init() {
        this._prefix = "sf-" + UtilsNew.randomString(6);

        this.content = null;
        this._config = {...this.getDefaultConfig(), ...this.config};
    }

    connectedCallback() {
        super.connectedCallback();
    }

    async updated(changedProperties) {
        if (changedProperties.has("job")) {
            this.jobId = this.job.id;
            if (this.active) {
                this.fetchContent(this.job, {command: this._config.command, type: this._config.type});
            }
        }

        if (changedProperties.has("active")) {
            this.content = null;
            this.requestUpdate();
            await this.updateComplete;
            // todo this should call fetchContent iff the job has changed
            // console.log("new job = old job ", this.active && this.jobId === this.job.id)
            if (this.active) {
                this.fetchContent(this.job, {command: this._config.command, type: this._config.type});
            } else {
                this.clearReload();
            }
        }
    }

    setCommand(command) {
        this._config.command = command;
        this.clearReload();
        this.fetchContent(this.job, {command: this._config.command, type: this._config.type});
        this.setReloadInterval();
    }

    setType(type) {
        this._config.type = type;
        this.clearReload();
        this.fetchContent(this.job, {command: this._config.command, type: this._config.type});
        this.setReloadInterval();
    }

    // setInterval makes sense only in case of Tail log
    setReloadInterval() {
        if (this.active && this._config.command === "tail" && this.job.internal.status.name === "RUNNING") {
            // console.log("setting interval");
            this.requestUpdate();
            this.interval = setInterval(() => {
                // this.content += "\n" + UtilsNew.randomString(6);
                if ($(".jobs-details-log", this).is(":visible")) {
                    // tail call is actually head (after the first tail call)
                    this.fetchContent(this.job, {command: "head", offset: this.contentOffset}, true);
                } else {
                    this.clearReload();
                }
                this.requestUpdate();
            }, 10000);
        }
    }

    clearReload() {
        this.contentOffset = 0;
        clearInterval(this.interval);
        this.requestUpdate();
    }

    async fetchContent(job, params = {}, append = false) {
        const statusWithoutLogs = ["PENDING", "ABORTED", "RUNNING", "QUEUED"];
        if (!append) {
            this.content = "";
        }
        this.loading = true;
        this.requestUpdate();
        await this.updateComplete;

        const command = params.command || this._config.command;
        // const offset = params.offset || 0;
        // console.log("request ", "command", command, "params", params, "offset", offset, "append", append);
        if (!statusWithoutLogs?.includes(job?.internal?.status?.id?.toUpperCase())) {
            this.opencgaSession.opencgaClient.jobs()[command + "Log"](job.id, {
                study: this.opencgaSession.study.fqn,
                lines: this._config.lines,
                type: this._config.type,
                offset: params.offset || 0,
                ...params
            }).then(restResponse => {
                const result = restResponse.getResult(0);
                if (result.content) {
                // if command=tail this is the first tail call (the subsequents will be head)
                    if (command === "tail") {
                        this.contentOffset = result.offset;
                    }
                    // append is true only in case of tail command (it has been kept as separate param to quickly have one-shot Tail call button (not live), just in case)
                    if (append) {
                        if (this.contentOffset !== result.offset) {
                            this.content = this.content + result.content;
                            this.contentOffset = result.offset;
                        }
                    } else {
                        this.content = result.content + "\n";
                    }
                } else {
                // this.content = "No content";
                }
            }).catch(response => {
                this.content = "An error occurred while fetching log.\n";
                NotificationUtils.dispatch(this, NotificationUtils.NOTIFY_RESPONSE, response);
            }).finally(() => {
                this.loading = false;
                this.requestUpdate();
            });
        } else {
            this.content = `Job is ${job?.internal?.status?.id}\n`;
            this.loading = false;
            this.requestUpdate();
        }
    }

    onScroll(e) {
        // TODO custom infinite scroll
        // console.log(e)
    }

    getDefaultConfig() {
        return {
            command: "head",
            type: "stderr",
            lines: 50
        };
    }

    render() {
        return html`

                    <button type="button" class="btn btn-default btn-small ripple ${this._config.command === "head" ? "active" : ""}" @click="${() => this.setCommand("head")}">
                         Head
                    </button>
                    <button type="button" class="btn btn-default btn-small ripple ${this._config.command === "tail" ? "active" : ""}" @click="${() => this.setCommand("tail")}">
                         Tail
                    </button>

                    <button type="button" class="btn btn-default btn-small ripple ${this._config.type === "stderr" ? "active" : ""}" @click="${() => this.setType("stderr")}">
                         Stderr
                    </button>
                    <button type="button" class="btn btn-default btn-small ripple ${this._config.type === "stdout" ? "active" : ""}" @click="${() => this.setType("stdout")}">
                         Stdout
                    </button>

${this.content}\n${this.loading || (this.content && this._config.command === "tail") ? html`<div class="cursor"></div>` : ""}

        `;
    }

}

customElements.define("opencga-job-detail-log", OpencgaJobDetailLog);
<|endoftext|>


<commit_before>import Vue from 'vue';
import Vuex from 'vuex';
import SecureLS from 'secure-ls';

import addDays from 'date-fns/add_days';
import getHours from 'date-fns/get_hours';
import isSaturday from 'date-fns/is_saturday';
import isSunday from 'date-fns/is_sunday';
import getMonth from 'date-fns/get_month';
import getYear from 'date-fns/get_year';
import getDate from 'date-fns/get_date';

Vue.use(Vuex);

const defaultState = {
    dnevnikData: null,
    offlineDnevnik: null,
    dnevnikLoad: true,
    statsData: null,
    statsLoad: true,
    feedData: null,
    feedLoad: true,
    userData: null,
    isLoggedIn: false,
    apiKey: null
};

const ls = new SecureLS({ encodingType: 'aes' });

const store = new Vuex.Store({
    state: defaultState,
    getters: {
        dnevnikData: (state) => state.dnevnikData,
        offlineDnevnik: (state) => state.offlineDnevnik,
        statsData: (state) => state.statsData,
        feedData: (state) => state.feedData,
        userData: (state) => state.userData,
        isLoggedIn: (state) => state.isLoggedIn,
        dnevnikLoad: (state) => state.dnevnikLoad,
        statsLoad: (state) => state.statsLoad,
        feedLoad: (state) => state.feedLoad
    },
    mutations: {
        fetchData() {
            let localState = {};

            ls.getAllKeys().forEach((key) => {
                localState[key] = ls.get(key);
            });

            if (localState.isLoggedIn) {
                let promiseArray = [];

                promiseArray.push(
                    new Promise(() => {
                        let dateCurrent = new Date;
                        let dateFormatted = null;

                        switch (true) {
                        case isSunday(dateCurrent):
                            dateFormatted = addDays(dateCurrent, 1);
                            break;

                        case isSaturday(new Date):
                            if (getHours(dateCurrent) < 14) {
                                dateFormatted = dateCurrent;

                            } else {
                                dateFormatted = addDays(dateCurrent, 2);
                            }
                            break;

                        default:
                            if (getHours(dateCurrent) < 15) {
                                dateFormatted = dateCurrent;

                            } else {
                                dateFormatted = addDays(dateCurrent, 1);
                            }
                            break;
                        }

                        let day = ('0' + getDate(dateFormatted)).slice(-2);
                        let month = ('0' + (getMonth(dateCurrent) + 1)).slice(-2);
                        let year = getYear(dateFormatted);

                        fetch(`https://api.dnevnik.ru/mobile/v2/schedule?startDate=${year}-${month}-${day}&endDate=${year}-${month}-${day}&personId=${localState.userData.personId}&groupId=${localState.userData.eduGroups[0].id_str}&access_token=${localState.apiKey}`, { credentials: 'same-origin' })
                            .then((response) => {
                                if (response.ok) {
                                    response.json().then((dnevnikJson) => {
                                        localState.offlineDnevnik, localState.dnevnikData = dnevnikJson;
                                        localState.dnevnikLoad = false;
                                    });

                                } else {
                                    localState.dnevnikData = localState.offlineDnevnik;
                                    localState.dnevnikLoad = false;
                                }

                            }, () => {
                                localState.dnevnikData = localState.offlineDnevnik;
                                localState.dnevnikLoad = false;
                            });
                    })
                );

                promiseArray.push(
                    fetch(`https://api.dnevnik.ru/mobile/v2/allMarks?personId=${localState.userData.personId}&groupId=${localState.userData.eduGroups[0].id_str}&access_token=${localState.apiKey}`, { credentials: 'same-origin' })
                        .then((response) => {
                            if (response.ok) {
                                response.json().then((statsJson) => {
                                    localState.statsData = statsJson;
                                    localState.statsLoad = false;
                                });

                            } else {
                                localState.statsLoad = false;
                            }

                        }, () => {
                            localState.statsLoad = false;
                        })
                );

                promiseArray.push(
                    new Promise(() => {
                        let dateCurrent = new Date;

                        let deauthChecker = (jsonData) => {
                            return ['invalidToken', 'apiRequestLimit'].some((elem) => {
                                return (jsonData.hasOwnProperty('type') && jsonData['type'] === elem);
                            });
                        };

                        let day = ('0' + getDate(dateCurrent)).slice(-2);
                        let month = ('0' + (getMonth(dateCurrent) + 1)).slice(-2);
                        let year = getYear(dateCurrent);

                        fetch(`https://api.dnevnik.ru/mobile/v2/feed/?date=${year}-${month}-${day}&limit=1&personId=${localState.userData.personId}&groupId=${localState.userData.eduGroups[0].id_str}&access_token=${localState.apiKey}`, { credentials: 'same-origin' })
                            .then((response) => {
                                if (response.ok) {
                                    response.json().then((feedJson) => {
                                        if (deauthChecker(feedJson)) {
                                            localState = defaultState;
                                            store.replaceState(defaultState);

                                        } else {
                                            localState.feedData = feedJson;
                                            localState.feedLoad = false;
                                        }
                                    });

                                } else {
                                    localState.feedLoad = false;
                                }

                            }, () => {
                                localState.feedLoad = false;
                            });
                    })
                );

                fetch(`https://api.dnevnik.ru/v1/users/me/context?access_token=${localState.apiKey}`, {
                    credentials: 'same-origin'
                }).then((response) => {
                    if (response.ok) {
                        response.json().then((userData) => {
                            localState.userData = userData;
                        });
                    }
                }).then(() => {
                    Promise.all(promiseArray);
                });

                for (let [key, value] of Object.entries(localState)) {
                    if (!['statsLoad', 'dnevnikLoad', 'feedLoad'].includes(key)) {
                        ls.set(key, value);

                    } else {
                        ls.set(key, true);
                    }
                }

                store.replaceState(localState);
            }
        },
        viewDnevnik(state, amount) {
            if (state.isLoggedIn) {
                let dateCurrent = new Date;
                let dateFormatted = null;

                switch (true) {
                case isSunday(dateCurrent):
                    dateFormatted = addDays(addDays(dateCurrent, 1), amount);
                    break;

                case isSaturday(new Date):
                    if (getHours(dateCurrent) < 14) {
                        dateFormatted = addDays(dateCurrent, amount);

                    } else {
                        dateFormatted = addDays(addDays(dateCurrent, 2), amount);
                    }
                    break;

                default:
                    if (getHours(dateCurrent) < 15) {
                        dateFormatted = addDays(dateCurrent, amount);

                    } else {
                        dateFormatted = addDays(addDays(dateCurrent, 1), amount);
                    }
                    break;
                }

                let day = ('0' + getDate(dateFormatted)).slice(-2);
                let month = ('0' + (getMonth(dateCurrent) + 1)).slice(-2);
                let year = getYear(dateFormatted);

                fetch(`https://api.dnevnik.ru/mobile/v2/schedule?startDate=${year}-${month}-${day}&endDate=${year}-${month}-${day}&personId=${state.userData.personId}&groupId=${state.userData.eduGroups[0].id_str}&access_token=${state.apiKey}`, { credentials: 'same-origin' }).then((response) => {
                    if (response.ok) {
                        response.json().then((dnevnikJson) => {
                            state.dnevnikData = dnevnikJson;
                            state.dnevnikLoad = false;
                        });

                    } else {
                        state.dnevnikData = state.offlineDnevnik;
                        state.dnevnikLoad = false;
                    }

                }, () => {
                    state.dnevnikData = state.offlineDnevnik;
                    state.dnevnikLoad = false;
                });
            }
        },
        setDefaultState() {
            for (let [key, value] of Object.entries(defaultState)) {
                ls.set(key, value);
            }
        },
        resetLoginState() {
            if (navigator.onLine) {
                for (let [key, value] of Object.entries(defaultState)) {
                    ls.set(key, value);
                }

                store.replaceState(defaultState);
            }
        }
    }
});

export default store;
<commit_msg>Offline fixes<commit_after>import Vue from 'vue';
import Vuex from 'vuex';
import SecureLS from 'secure-ls';

import addDays from 'date-fns/add_days';
import getHours from 'date-fns/get_hours';
import isSaturday from 'date-fns/is_saturday';
import isSunday from 'date-fns/is_sunday';
import getMonth from 'date-fns/get_month';
import getYear from 'date-fns/get_year';
import getDate from 'date-fns/get_date';

Vue.use(Vuex);

const defaultState = {
    dnevnikData: null,
    offlineDnevnik: null,
    dnevnikLoad: true,
    statsData: null,
    statsLoad: true,
    feedData: null,
    feedLoad: true,
    userData: null,
    isLoggedIn: false,
    apiKey: null
};

const ls = new SecureLS({ encodingType: 'aes' });

const store = new Vuex.Store({
    state: defaultState,
    getters: {
        dnevnikData: (state) => state.dnevnikData,
        offlineDnevnik: (state) => state.offlineDnevnik,
        statsData: (state) => state.statsData,
        feedData: (state) => state.feedData,
        userData: (state) => state.userData,
        isLoggedIn: (state) => state.isLoggedIn,
        dnevnikLoad: (state) => state.dnevnikLoad,
        statsLoad: (state) => state.statsLoad,
        feedLoad: (state) => state.feedLoad
    },
    mutations: {
        fetchData() {
            let localState = defaultState;

            ls.getAllKeys().forEach((key) => {
                localState[key] = ls.get(key);
            });

            if (localState.isLoggedIn) {
                let promiseArray = [];

                promiseArray.push(
                    new Promise(() => {
                        let dateCurrent = new Date;
                        let dateFormatted = null;

                        switch (true) {
                        case isSunday(dateCurrent):
                            dateFormatted = addDays(dateCurrent, 1);
                            break;

                        case isSaturday(new Date):
                            if (getHours(dateCurrent) < 14) {
                                dateFormatted = dateCurrent;

                            } else {
                                dateFormatted = addDays(dateCurrent, 2);
                            }
                            break;

                        default:
                            if (getHours(dateCurrent) < 15) {
                                dateFormatted = dateCurrent;

                            } else {
                                dateFormatted = addDays(dateCurrent, 1);
                            }
                            break;
                        }

                        let day = ('0' + getDate(dateFormatted)).slice(-2);
                        let month = ('0' + (getMonth(dateCurrent) + 1)).slice(-2);
                        let year = getYear(dateFormatted);

                        fetch(`https://api.dnevnik.ru/mobile/v2/schedule?startDate=${year}-${month}-${day}&endDate=${year}-${month}-${day}&personId=${localState.userData.personId}&groupId=${localState.userData.eduGroups[0].id_str}&access_token=${localState.apiKey}`, { credentials: 'same-origin' })
                            .then((response) => {
                                if (response.ok) {
                                    response.json().then((dnevnikJson) => {
                                        localState['offlineDnevnik'] = dnevnikJson;
                                        ls.set('offlineDnevnik', dnevnikJson);

                                        localState['dnevnikData'] = dnevnikJson;
                                        localState['dnevnikLoad'] = false;
                                    });

                                } else {
                                    localState['dnevnikData'] = localState['offlineDnevnik'];
                                    localState['dnevnikLoad'] = false;
                                }

                            }, () => {
                                localState['dnevnikData'] = localState['offlineDnevnik'];
                                localState['dnevnikLoad'] = false;
                            });
                    })
                );

                promiseArray.push(
                    fetch(`https://api.dnevnik.ru/mobile/v2/allMarks?personId=${localState.userData.personId}&groupId=${localState.userData.eduGroups[0].id_str}&access_token=${localState.apiKey}`, { credentials: 'same-origin' })
                        .then((response) => {
                            if (response.ok) {
                                response.json().then((statsJson) => {
                                    localState['statsData'] = statsJson;
                                    ls.set('statsData', statsJson);

                                    localState['statsLoad'] = false;
                                });

                            } else {
                                localState['statsLoad'] = false;
                            }

                        }, () => {
                            localState['statsLoad'] = false;
                        })
                );

                promiseArray.push(
                    new Promise(() => {
                        let dateCurrent = new Date;

                        let deauthChecker = (jsonData) => {
                            return ['invalidToken', 'apiRequestLimit'].some((elem) => {
                                return (jsonData.hasOwnProperty('type') && jsonData['type'] === elem);
                            });
                        };

                        let day = ('0' + getDate(dateCurrent)).slice(-2);
                        let month = ('0' + (getMonth(dateCurrent) + 1)).slice(-2);
                        let year = getYear(dateCurrent);

                        fetch(`https://api.dnevnik.ru/mobile/v2/feed/?date=${year}-${month}-${day}&limit=1&personId=${localState.userData.personId}&groupId=${localState.userData.eduGroups[0].id_str}&access_token=${localState.apiKey}`, { credentials: 'same-origin' })
                            .then((response) => {
                                if (response.ok) {
                                    response.json().then((feedJson) => {
                                        if (deauthChecker(feedJson)) {
                                            localState = defaultState;
                                            ls.clear();

                                        } else {
                                            localState['feedData'] = feedJson;
                                            ls.set('feedData', feedJson);

                                            localState['feedLoad'] = false;
                                        }
                                    });

                                } else {
                                    localState['feedLoad'] = false;
                                }

                            }, () => {
                                localState['feedLoad'] = false;
                            });
                    })
                );

                fetch(`https://api.dnevnik.ru/v1/users/me/context?access_token=${localState.apiKey}`, {
                    credentials: 'same-origin'
                }).then((response) => {
                    if (response.ok) {
                        response.json().then((userData) => {
                            localState['userData'] = userData;
                            ls.set('userData', userData);
                        });
                    }
                });

                Promise.all(promiseArray).then(() => {
                    store.replaceState(localState);
                });
            }
        },
        viewDnevnik(state, amount) {
            if (state.isLoggedIn) {
                let dateCurrent = new Date;
                let dateFormatted = null;

                switch (true) {
                case isSunday(dateCurrent):
                    dateFormatted = addDays(addDays(dateCurrent, 1), amount);
                    break;

                case isSaturday(new Date):
                    if (getHours(dateCurrent) < 14) {
                        dateFormatted = addDays(dateCurrent, amount);

                    } else {
                        dateFormatted = addDays(addDays(dateCurrent, 2), amount);
                    }
                    break;

                default:
                    if (getHours(dateCurrent) < 15) {
                        dateFormatted = addDays(dateCurrent, amount);

                    } else {
                        dateFormatted = addDays(addDays(dateCurrent, 1), amount);
                    }
                    break;
                }

                let day = ('0' + getDate(dateFormatted)).slice(-2);
                let month = ('0' + (getMonth(dateCurrent) + 1)).slice(-2);
                let year = getYear(dateFormatted);

                fetch(`https://api.dnevnik.ru/mobile/v2/schedule?startDate=${year}-${month}-${day}&endDate=${year}-${month}-${day}&personId=${state.userData.personId}&groupId=${state.userData.eduGroups[0].id_str}&access_token=${state.apiKey}`, { credentials: 'same-origin' }).then((response) => {
                    if (response.ok) {
                        response.json().then((dnevnikJson) => {
                            state.dnevnikData = dnevnikJson;
                            state.dnevnikLoad = false;
                        });

                    } else {
                        state.dnevnikData = state.offlineDnevnik;
                        state.dnevnikLoad = false;
                    }

                }, () => {
                    state.dnevnikData = state.offlineDnevnik;
                    state.dnevnikLoad = false;
                });
            }
        },
        setDefaultState() {
            for (let [key, value] of Object.entries(defaultState)) {
                ls.set(key, value);
            }
        },
        resetLoginState() {
            if (navigator.onLine) {
                ls.clear();
                store.replaceState({});
            }
        }
    }
});

export default store;
<|endoftext|>


<commit_before>/**
 * @file Stream piping
 */
/*#ifndef(UMD)*/
"use strict";
/*global _gpfStreamQueryReadable*/ // Get an IReadableStream or fail if not implemented
/*global _gpfStreamQueryWritable*/ // Get an IWritableStream or fail if not implemented
/*global _gpfInterfaceQuery*/ // gpf.interfaces.query
/*global _gpfIFlushableStream*/ // gpf.interfaces.IFlushableStream
/*exported _gpfStreamPipe*/ // gpf.stream.pipe
/*#endif*/

/**
 * Creates a pipe sequence chain between an intermediate stream and a writable (flushable?) destination
 *
 * @param {Object} intermediate Must at least implements IReadableStream interface.
 * If it implements the IFlushableStream interface, it is assumed that it retains data
 * until it receives the Flush. Meaning, the read won't complete until the flush call.
 * If it does not implement the IFlushableStream, the read may end before the whole sequence
 * has finished. It means that the next write should trigger a new read and flush must be simulated at
 * least to pass it to the
 * @param destination
 * @private
 */
/*
function _gpfStreamPipeToFlushableWrite (intermediate, destination) {

    var iReadableStream = _gpfStreamQueryReadable(intermediate),
        iWritableStream = _gpfStreamQueryWritable(destination),
        iFlushableOutStream = _gpfInterfaceQuery(_gpfIFlushableStream, destination);

}
*/

/**
 * Pipe streams.
 *
 * @param {gpf.interfaces.IReadableStream} source Source stream
 * @param {...gpf.interfaces.IWritableStream} destination Writable streams
 * @return {Promise} Resolved when reading (and subsequent writings) are done
 */
function _gpfStreamPipe (source, destination) {
    var iReadableStream = _gpfStreamQueryReadable(source),
        iWritableStream = _gpfStreamQueryWritable(destination);
    try {
        return iReadableStream.read(iWritableStream)
            .then(function () {
                var iFlushableStream = _gpfInterfaceQuery(_gpfIFlushableStream, iWritableStream);
                if (iFlushableStream) {
                    return iFlushableStream.flush();
                }
            });
    } catch (e) {
        return Promise.reject(e);
    }
}

/** @gpf:sameas _gpfStreamPipe */
gpf.stream.pipe = _gpfStreamPipe;
<commit_msg>WIP (#202)<commit_after>/**
 * @file Stream piping
 */
/*#ifndef(UMD)*/
"use strict";
/*global _gpfStreamQueryReadable*/ // Get an IReadableStream or fail if not implemented
/*global _gpfStreamQueryWritable*/ // Get an IWritableStream or fail if not implemented
/*global _gpfInterfaceQuery*/ // gpf.interfaces.query
/*global _gpfIFlushableStream*/ // gpf.interfaces.IFlushableStream
/*exported _gpfStreamPipe*/ // gpf.stream.pipe
/*#endif*/

/**
 * Create a flushable & writable stream by combining the intermediate stream with the writable destination
 *
 * @param {Object} intermediate Must implements IReadableStream interface.
 * If it implements the IFlushableStream interface, it is assumed that it retains data
 * until it receives the Flush. Meaning, the read won't complete until the flush call.
 * If it does not implement the IFlushableStream, the read may end before the whole sequence
 * has finished. It means that the next write should trigger a new read and flush must be simulated at
 * least to pass it to the destination
 * @param {Object} destination Must implements IWritableStream interface.
 * If it implements the IFlushableStream, it will be called when the intermediate completes.
 *
 * @return {Object} Implementing IWritableStream and IFlushableStream
 */
function _gpfStreamPipeToFlushableWrite (intermediate, destination) {
    var iReadableIntermediate = _gpfStreamQueryReadable(intermediate),
        iWritableIntermdiate = _gpfStreamQueryWritable(intermediate),
        iFlushableIntermediate = _gpfInterfaceQuery(_gpfIFlushableStream, intermediate),
        iWritableDestination = _gpfStreamQueryWritable(destination),
        iFlushableDestination = _gpfInterfaceQuery(_gpfIFlushableStream, destination);

    function _flushDestination () {
        if (iFlushableDestination) {
            return iFlushableDestination.flush();
        }
        return Promise.resolve();
    }

    iReadableIntermediate.read(iWritableDestination);
    return {

        flush: function () {
            if (iFlushableIntermediate) {
                return iFlushableIntermediate.flush()
                    .then(_flushDestination);
            }
            return _flushDestination();
        },

        write: function (data) {
            return iWritableIntermdiate.write(data);
        }

    };

}

/**
 * Pipe streams.
 *
 * @param {gpf.interfaces.IReadableStream} source Source stream
 * @param {...gpf.interfaces.IWritableStream} destination Writable streams
 * @return {Promise} Resolved when reading (and subsequent writings) are done
 */
function _gpfStreamPipe (source, destination) {
    var iReadableStream = _gpfStreamQueryReadable(source),
        iWritableStream = _gpfStreamQueryWritable(destination);
    try {
        return iReadableStream.read(iWritableStream)
            .then(function () {
                var iFlushableStream = _gpfInterfaceQuery(_gpfIFlushableStream, iWritableStream);
                if (iFlushableStream) {
                    return iFlushableStream.flush();
                }
            });
    } catch (e) {
        return Promise.reject(e);
    }
}

/** @gpf:sameas _gpfStreamPipe */
gpf.stream.pipe = _gpfStreamPipe;
<|endoftext|>


<commit_before>import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../../actions/transactions';
import AddTransaction from '../Modals/AddTransaction.js'
import EditTransaction from '../Modals/EditTransaction.js'
import TextField from 'material-ui/TextField';
import Toggle from 'material-ui/Toggle';

import {
  Table,
  TableBody,
  TableFooter,
  TableHeader,
  TableHeaderColumn,
  TableRow,
  TableRowColumn,
} from 'material-ui/Table';

import styles from './styles.scss';

function mapStateToProps(state) {
  return {
    data: state.transactions.data,
    token: state.auth.token,
    loaded: state.transactions.loaded,
    isFetching: state.transactions.isFetching,
    selectedAccount: state.accounts.selectedAccount,
  };
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(actionCreators, dispatch);
}

@connect(mapStateToProps, mapDispatchToProps)
export default class TransactionsList extends React.Component {
  state = {
    fixedHeader: true,
    fixedFooter: true,
    stripedRows: true,
    showRowHover: false,
    selectable: false,
    multiSelectable: false,
    enableSelectAll: false,
    deselectOnClickaway: false,
    showCheckboxes: false,
    height: '400px',
  };

  fetchData(account_id=null) {
    if (account_id === null) {
      account_id = this.props.selectedAccount
    }
    if (account_id !== null)
    {
      const token = this.props.token
      this.props.fetchTransactionsData(token, account_id)
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.selectedAccount !== this.props.selectedAccount) {
      this.fetchData(nextProps.selectedAccount)
    }
  }

  componentDidUpdate(prevProps, prevState) {
    this.scrollToBottom()
  }

  handleToggle = (event, toggled) => {
    this.setState({
      [event.target.name]: toggled,
    });
  };

  handleChange = (event) => {
    this.setState({height: event.target.value});
  };

  scrollToBottom() {
    if (this.refs.table !== undefined) {
      this.refs.table.refs.tableDiv.scrollTop = this.refs.table.refs.tableDiv.scrollHeight
    }
  }

  createTransaction( transaction ) {
    const token = this.props.token;
    this.props.createTransaction(token, transaction);
    this.fetchData();
    this.props.updateBalance();
  }

  editTransaction( transaction ) {
    const token = this.props.token;
    this.props.editTransaction(token, transaction);
    this.fetchData();
    this.props.updateBalance();
  }

  deleteTransaction( id ) {
    const token = this.props.token;
    this.props.deleteTransaction(token, id);
    this.fetchData();
    this.props.updateBalance();
  }

  renderTransactionsList( transactions ) {
    transactions.sort((a,b) => {
      return new Date(a.date) - new Date(b.date);
    });
    const rows = transactions.map((row, index) => {
      let credit = parseFloat(row.amount) < 0 ? '' : Number(row.amount).toFixed(2)
      let debit = parseFloat(row.amount) < 0 ? Number(row.amount).toFixed(2) : ''
      return (
        <TableRow key={index}>
          <TableHeaderColumn className={styles.smallColumn}>{row.date.toString()}</TableHeaderColumn>
          <TableHeaderColumn>{row.label}</TableHeaderColumn>
          <TableHeaderColumn className={styles.smallColumn}>{debit}</TableHeaderColumn>
          <TableHeaderColumn className={styles.smallColumn}>{credit}</TableHeaderColumn>
          <TableHeaderColumn className={styles.smallColumn}>
            <EditTransaction
              fields={row}
              editTransaction={this.editTransaction.bind(this)}
              deleteTransaction={this.deleteTransaction.bind(this)}
            />
          </TableHeaderColumn>
        </TableRow>
      )
    })
    return rows
  }

  render() {
    if (this.props.selectedAccount === undefined || this.props.selectedAccount === null) {
      return (

Select an account or create one.

      )
    }

    return (

      {!this.props.loaded
        ?
Loading data...

        :

            <TableHeader
              displaySelectAll={this.state.showCheckboxes}
              adjustForCheckbox={this.state.showCheckboxes}
              enableSelectAll={this.state.enableSelectAll}
            >
              <TableRow>
                <TableHeaderColumn className={styles.smallColumn}>Date</TableHeaderColumn>
                <TableHeaderColumn>Label</TableHeaderColumn>
                <TableHeaderColumn className={styles.smallColumn}>Debit</TableHeaderColumn>
                <TableHeaderColumn className={styles.smallColumn}>Credit</TableHeaderColumn>
                <TableHeaderColumn className={styles.smallColumn}></TableHeaderColumn>
              </TableRow>
            </TableHeader>
            <TableBody
              displayRowCheckbox={this.state.showCheckboxes}
              deselectOnClickaway={this.state.deselectOnClickaway}
              showRowHover={this.state.showRowHover}
              stripedRows={this.state.stripedRows}
              style={{height: '100%'}}
            >
              {this.renderTransactionsList(this.props.data)}
            </TableBody>
            <TableFooter
              adjustForCheckbox={this.state.showCheckboxes}
            >
              <TableRow>
                <TableRowColumn colSpan="5">
                  <AddTransaction
                    selectedAccount={this.props.selectedAccount}
                    createTransaction={this.createTransaction.bind(this)} />
                </TableRowColumn>
              </TableRow>
            </TableFooter>

      }

    );
  }
}

TransactionsList.propTypes = {
  fetchTransactionsData: React.PropTypes.func,
  loaded: React.PropTypes.bool,
  data: React.PropTypes.any,
  token: React.PropTypes.string,
  selectedAccount: React.PropTypes.number,
};
<commit_msg>Use async/await to fetchData after transactions update success<commit_after>import React from 'react';
import "babel-polyfill";
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../../actions/transactions';
import AddTransaction from '../Modals/AddTransaction.js'
import EditTransaction from '../Modals/EditTransaction.js'
import TextField from 'material-ui/TextField';
import Toggle from 'material-ui/Toggle';

import {
  Table,
  TableBody,
  TableFooter,
  TableHeader,
  TableHeaderColumn,
  TableRow,
  TableRowColumn,
} from 'material-ui/Table';

import styles from './styles.scss';

function mapStateToProps(state) {
  return {
    data: state.transactions.data,
    token: state.auth.token,
    loaded: state.transactions.loaded,
    isFetching: state.transactions.isFetching,
    selectedAccount: state.accounts.selectedAccount,
  };
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(actionCreators, dispatch);
}

@connect(mapStateToProps, mapDispatchToProps)
export default class TransactionsList extends React.Component {
  state = {
    fixedHeader: true,
    fixedFooter: true,
    stripedRows: true,
    showRowHover: false,
    selectable: false,
    multiSelectable: false,
    enableSelectAll: false,
    deselectOnClickaway: false,
    showCheckboxes: false,
    height: '400px',
  };

  fetchData(account_id=null) {
    if (account_id === null) {
      account_id = this.props.selectedAccount
    }
    if (account_id !== null)
    {
      const token = this.props.token
      this.props.fetchTransactionsData(token, account_id)
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.selectedAccount !== this.props.selectedAccount) {
      this.fetchData(nextProps.selectedAccount)
    }
  }

  componentDidUpdate(prevProps, prevState) {
    this.scrollToBottom()
  }

  handleToggle = (event, toggled) => {
    this.setState({
      [event.target.name]: toggled,
    });
  };

  handleChange = (event) => {
    this.setState({height: event.target.value});
  };

  scrollToBottom() {
    if (this.refs.table !== undefined) {
      this.refs.table.refs.tableDiv.scrollTop = this.refs.table.refs.tableDiv.scrollHeight
    }
  }

  createTransaction( transaction ) {
    const token = this.props.token;
    let call = async () =>
      await (await this.props.createTransaction(token, transaction));
    call()
      .then(() => {
        this.fetchData();
        this.props.updateBalance();
      })
  }

  editTransaction( transaction ) {
    const token = this.props.token;
    let call = async () =>
      await (await this.props.editTransaction(token, transaction));
    call()
      .then(() => {
        this.fetchData();
        this.props.updateBalance();
      })
  }

  deleteTransaction( id ) {
    const token = this.props.token;
    let call = async () =>
      await (await this.props.deleteTransaction(token, id));
    call()
      .then(() => {
        this.fetchData();
        this.props.updateBalance();
      })
  }

  renderTransactionsList( transactions ) {
    transactions.sort((a,b) => {
      return new Date(a.date) - new Date(b.date);
    });
    const rows = transactions.map((row, index) => {
      let credit = parseFloat(row.amount) < 0 ? '' : Number(row.amount).toFixed(2)
      let debit = parseFloat(row.amount) < 0 ? Number(row.amount).toFixed(2) : ''
      return (
        <TableRow key={index}>
          <TableHeaderColumn className={styles.smallColumn}>{row.date.toString()}</TableHeaderColumn>
          <TableHeaderColumn>{row.label}</TableHeaderColumn>
          <TableHeaderColumn className={styles.smallColumn}>{debit}</TableHeaderColumn>
          <TableHeaderColumn className={styles.smallColumn}>{credit}</TableHeaderColumn>
          <TableHeaderColumn className={styles.smallColumn}>
            <EditTransaction
              fields={row}
              editTransaction={this.editTransaction.bind(this)}
              deleteTransaction={this.deleteTransaction.bind(this)}
            />
          </TableHeaderColumn>
        </TableRow>
      )
    })
    return rows
  }

  render() {
    if (this.props.selectedAccount === undefined || this.props.selectedAccount === null) {
      return (

Select an account or create one.

      )
    }

    return (

      {!this.props.loaded
        ?
Loading data...

        :

            <TableHeader
              displaySelectAll={this.state.showCheckboxes}
              adjustForCheckbox={this.state.showCheckboxes}
              enableSelectAll={this.state.enableSelectAll}
            >
              <TableRow>
                <TableHeaderColumn className={styles.smallColumn}>Date</TableHeaderColumn>
                <TableHeaderColumn>Label</TableHeaderColumn>
                <TableHeaderColumn className={styles.smallColumn}>Debit</TableHeaderColumn>
                <TableHeaderColumn className={styles.smallColumn}>Credit</TableHeaderColumn>
                <TableHeaderColumn className={styles.smallColumn}></TableHeaderColumn>
              </TableRow>
            </TableHeader>
            <TableBody
              displayRowCheckbox={this.state.showCheckboxes}
              deselectOnClickaway={this.state.deselectOnClickaway}
              showRowHover={this.state.showRowHover}
              stripedRows={this.state.stripedRows}
              style={{height: '100%'}}
            >
              {this.renderTransactionsList(this.props.data)}
            </TableBody>
            <TableFooter
              adjustForCheckbox={this.state.showCheckboxes}
            >
              <TableRow>
                <TableRowColumn colSpan="5">
                  <AddTransaction
                    selectedAccount={this.props.selectedAccount}
                    createTransaction={this.createTransaction.bind(this)} />
                </TableRowColumn>
              </TableRow>
            </TableFooter>

      }

    );
  }
}

TransactionsList.propTypes = {
  fetchTransactionsData: React.PropTypes.func,
  loaded: React.PropTypes.bool,
  data: React.PropTypes.any,
  token: React.PropTypes.string,
  selectedAccount: React.PropTypes.number,
};
<|endoftext|>


<commit_before>(function(){

    xtag.register('widget-DAQhisto', {
        extends: 'div',
        lifecycle: {
            created: function() {
                var histos = ['spectrum', 'time', 'pulse', 'waveform'],
                    i;

                    for(i=0; i<histos.length; i++){
                        this[histos[i]] = document.createElement('div')
                        this[histos[i]].setAttribute('id', histos[i])
                        this[histos[i]].setAttribute('class', 'DAQhisto')
                        this.appendChild(this[histos[i]]);
                    }

                    //draw dygraphs
                    this.spectrumPlot = new Dygraph(document.getElementById('spectrum'), [[1,2,3]], {
                        title: 'test',
                        xlabel: 'ns',
                        ylabel: 'mV',

                        width: this.offsetWidth*0.4,
                        height: this.offsetWidth*0.2,
                        sigFigs: 2,
                        strokeWidth: 4,
                        yAxisLabelWidth: 75,
                        xAxisHeight: 30,
                        highlightCircleSize: 6,
                        showLabelsOnHighlight: false,
                        stepPlot: true,
                        includeZero: true,
                        colors: ['#F1C40F', '#2ECC71', '#E74C3C', '#ECF0F1', '#1ABC9C', '#E67E22', '#9B59B6']
                    });

                    this.timePlot = new Dygraph(document.getElementById('time'), 'ns,mV,\n', {
                        title: 'test',
                        xlabel: 'ns',
                        ylabel: 'mV',
                        width: this.offsetWidth*0.4,
                        height: this.offsetWidth*0.2,
                        sigFigs: 2,
                        strokeWidth: 4,
                        yAxisLabelWidth: 75,
                        xAxisHeight: 30,
                        highlightCircleSize: 6,
                        showLabelsOnHighlight: false,
                        stepPlot: true,
                        includeZero: true,
                        colors: ['#F1C40F', '#2ECC71', '#E74C3C', '#ECF0F1', '#1ABC9C', '#E67E22', '#9B59B6']
                    });

                    this.pulsePlot = new Dygraph(document.getElementById('pulse'), 'ns,mV,\n', {
                        title: 'test',
                        xlabel: 'ns',
                        ylabel: 'mV',
                        width: this.offsetWidth*0.4,
                        height: this.offsetWidth*0.2,
                        sigFigs: 2,
                        strokeWidth: 4,
                        yAxisLabelWidth: 75,
                        xAxisHeight: 30,
                        highlightCircleSize: 6,
                        showLabelsOnHighlight: false,
                        stepPlot: true,
                        includeZero: true,
                        colors: ['#F1C40F', '#2ECC71', '#E74C3C', '#ECF0F1', '#1ABC9C', '#E67E22', '#9B59B6']
                    });

                    this.wavefromPlot = new Dygraph(document.getElementById('waveform'), 'ns,mV,\n', {
                        title: 'test',
                        xlabel: 'ns',
                        ylabel: 'mV',
                        width: this.offsetWidth*0.4,
                        height: this.offsetWidth*0.2,
                        sigFigs: 2,
                        strokeWidth: 4,
                        yAxisLabelWidth: 75,
                        xAxisHeight: 30,
                        highlightCircleSize: 6,
                        showLabelsOnHighlight: false,
                        stepPlot: true,
                        includeZero: true,
                        colors: ['#F1C40F', '#2ECC71', '#E74C3C', '#ECF0F1', '#1ABC9C', '#E67E22', '#9B59B6']
                    });

            },
            inserted: function() {},
            removed: function() {},
            attributeChanged: function() {}
        },
        events: {

        },
        accessors: {

        },
        methods: {

            'update': function(){
                var i,
                    x = [], y = []

                for(i=0; i<4096; i++){
                    x.push(i)
                    dummy.push(Math.random());
                }

            }

        }
    });

})();<commit_msg>daq plots<commit_after>(function(){

    xtag.register('widget-DAQhisto', {
        extends: 'div',
        lifecycle: {
            created: function() {
                var histos = ['spectrum', 'time', 'pulse', 'waveform'],
                    i;

                    for(i=0; i<histos.length; i++){
                        this[histos[i]] = document.createElement('div')
                        this[histos[i]].setAttribute('id', histos[i])
                        this[histos[i]].setAttribute('class', 'DAQhisto')
                        this.appendChild(this[histos[i]]);
                    }

                    //draw dygraphs
                    this.spectrumPlot = new Dygraph(document.getElementById('spectrum'), [[1,2],[3,4],[5,6]], {
                        title: 'test',
                        xlabel: 'ns',
                        ylabel: 'mV',

                        width: this.offsetWidth*0.4,
                        height: this.offsetWidth*0.2,
                        sigFigs: 2,
                        strokeWidth: 4,
                        yAxisLabelWidth: 75,
                        xAxisHeight: 30,
                        highlightCircleSize: 6,
                        showLabelsOnHighlight: false,
                        stepPlot: true,
                        includeZero: true,
                        colors: ['#F1C40F', '#2ECC71', '#E74C3C', '#ECF0F1', '#1ABC9C', '#E67E22', '#9B59B6']
                    });

                    this.timePlot = new Dygraph(document.getElementById('time'), 'ns,mV,\n', {
                        title: 'test',
                        xlabel: 'ns',
                        ylabel: 'mV',
                        width: this.offsetWidth*0.4,
                        height: this.offsetWidth*0.2,
                        sigFigs: 2,
                        strokeWidth: 4,
                        yAxisLabelWidth: 75,
                        xAxisHeight: 30,
                        highlightCircleSize: 6,
                        showLabelsOnHighlight: false,
                        stepPlot: true,
                        includeZero: true,
                        colors: ['#F1C40F', '#2ECC71', '#E74C3C', '#ECF0F1', '#1ABC9C', '#E67E22', '#9B59B6']
                    });

                    this.pulsePlot = new Dygraph(document.getElementById('pulse'), 'ns,mV,\n', {
                        title: 'test',
                        xlabel: 'ns',
                        ylabel: 'mV',
                        width: this.offsetWidth*0.4,
                        height: this.offsetWidth*0.2,
                        sigFigs: 2,
                        strokeWidth: 4,
                        yAxisLabelWidth: 75,
                        xAxisHeight: 30,
                        highlightCircleSize: 6,
                        showLabelsOnHighlight: false,
                        stepPlot: true,
                        includeZero: true,
                        colors: ['#F1C40F', '#2ECC71', '#E74C3C', '#ECF0F1', '#1ABC9C', '#E67E22', '#9B59B6']
                    });

                    this.wavefromPlot = new Dygraph(document.getElementById('waveform'), 'ns,mV,\n', {
                        title: 'test',
                        xlabel: 'ns',
                        ylabel: 'mV',
                        width: this.offsetWidth*0.4,
                        height: this.offsetWidth*0.2,
                        sigFigs: 2,
                        strokeWidth: 4,
                        yAxisLabelWidth: 75,
                        xAxisHeight: 30,
                        highlightCircleSize: 6,
                        showLabelsOnHighlight: false,
                        stepPlot: true,
                        includeZero: true,
                        colors: ['#F1C40F', '#2ECC71', '#E74C3C', '#ECF0F1', '#1ABC9C', '#E67E22', '#9B59B6']
                    });

            },
            inserted: function() {},
            removed: function() {},
            attributeChanged: function() {}
        },
        events: {

        },
        accessors: {

        },
        methods: {

            'update': function(){
                var i,
                    x = [], y = []

                for(i=0; i<4096; i++){
                    x.push(i)
                    dummy.push(Math.random());
                }

            }

        }
    });

})();<|endoftext|>


<commit_before>import _ from 'lodash';
import debug from 'debug';
import { getOptions } from './swarm-options';

const log = debug('mup:swarm-utils');

export function hostsToServer(config, hosts) {
  const servers = config.servers;
  const result = [];

  Object.keys(servers).forEach(key => {
    const server = servers[key];

    if (hosts.indexOf(server.host) > -1) {
      result.push(key);
    }
  });

  return result;
}

export function currentManagers(config, serverInfo) {
  const hosts = [];

  // TODO: handle managers from multiple clusters.

  Object.keys(serverInfo).forEach(key => {
    const server = serverInfo[key];

    if (
      server.swarm &&
      server.swarm.LocalNodeState !== 'inactive' &&
      server.swarm.Cluster
    ) {
      hosts.push(key);
    }
  });

  const result = hostsToServer(config, hosts);

  log('current managers', result);

  return result;
}

export function desiredManagers(config, serverInfo) {
  const { managers } = getOptions(config);
  let additionalManagers = 0;

  log('requrested managers', managers);

  if (managers.length === 0 || managers.length === 2 || managers.length === 4) {
    additionalManagers = 1;
  }

  log('additional managers', additionalManagers);

  // TODO: handle more than 5 managers
  // TODO: handle when there are 2 or 4 servers
  // TODO: When there are at least 3 servers,
  // there should be at least 3 managers

  if (additionalManagers > 0) {
    const current = currentManagers(config, serverInfo);
    const diff = _.difference(current, managers);
    const managersToAdd = diff.splice(0, additionalManagers);

    log('managers to add', managersToAdd);
    additionalManagers -= managersToAdd.length;
    managers.push(...managersToAdd);
  }

  if (additionalManagers > 0) {
    const diff = _.difference(Object.keys(config.servers), managers);
    const managersToAdd = diff.splice(0, additionalManagers);
    log('random servers to add', managersToAdd);
    managers.push(...managersToAdd);
  }

  log('desired managers', managers);

  return managers;
}

export function findNodes(config, serverInfo) {
  const nodes = [];
  const managers = currentManagers(config, serverInfo);

  if (managers.length === 0) {
    return nodes;
  }

  // TODO: handle nodes that aren't listed in the config.server
  // TODO: handle multiple clusters

  const manager = config.servers[managers[0]].host;
  const ids = Object.keys(serverInfo).reduce((result, host) => {
    if (serverInfo[host].swarm) {
      const id = serverInfo[host].swarm.NodeID;
      result[id] = host;
    }

    return result;
  }, {});

  const nodeHosts = serverInfo[manager].swarmNodes.map(node => ids[node.ID]);

  return hostsToServer(config, nodeHosts);
}
<commit_msg>Improve deciding number of managers<commit_after>import _ from 'lodash';
import debug from 'debug';
import { getOptions } from './swarm-options';

const log = debug('mup:swarm-utils');

export function hostsToServer(config, hosts) {
  const servers = config.servers;
  const result = [];

  Object.keys(servers).forEach(key => {
    const server = servers[key];

    if (hosts.indexOf(server.host) > -1) {
      result.push(key);
    }
  });

  return result;
}

export function currentManagers(config, serverInfo) {
  const hosts = [];

  // TODO: handle managers from multiple clusters.

  Object.keys(serverInfo).forEach(key => {
    const server = serverInfo[key];

    if (
      server.swarm &&
      server.swarm.LocalNodeState !== 'inactive' &&
      server.swarm.Cluster
    ) {
      hosts.push(key);
    }
  });

  const result = hostsToServer(config, hosts);

  log('current managers', result);

  return result;
}

export function desiredManagers(config, serverInfo) {
  const { managers } = getOptions(config);
  const servers = Object.keys(config.servers);
  let additionalManagers = 0;

  log('requrested managers', managers);

  // Try to get an odd number of managers
  if (managers.length % 2 === 0 && managers.length < servers.length) {
    additionalManagers = 1;
  }

  // When there are enough servers, make sure there are
  // at least 3 managers, since it can then handle one manager
  // going down
  if (servers.length >= 3 && managers.length < 3) {
    additionalManagers = 3 - managers.length;
  }

  log('additional managers', additionalManagers);

  if (additionalManagers > 0) {
    const current = currentManagers(config, serverInfo);
    const diff = _.difference(current, managers);
    const managersToAdd = diff.splice(0, additionalManagers);

    log('managers to add', managersToAdd);
    additionalManagers -= managersToAdd.length;
    managers.push(...managersToAdd);
  }

  if (additionalManagers > 0) {
    const diff = _.difference(Object.keys(config.servers), managers);
    const managersToAdd = diff.splice(0, additionalManagers);
    log('random servers to add', managersToAdd);
    managers.push(...managersToAdd);
  }

  log('desired managers', managers);

  return managers;
}

export function findNodes(config, serverInfo) {
  const nodes = [];
  const managers = currentManagers(config, serverInfo);

  if (managers.length === 0) {
    return nodes;
  }

  // TODO: handle nodes that aren't listed in the config.server
  // TODO: handle multiple clusters

  const manager = config.servers[managers[0]].host;
  const ids = Object.keys(serverInfo).reduce((result, host) => {
    if (serverInfo[host].swarm) {
      const id = serverInfo[host].swarm.NodeID;
      result[id] = host;
    }

    return result;
  }, {});

  const nodeHosts = serverInfo[manager].swarmNodes.map(node => ids[node.ID]);

  return hostsToServer(config, nodeHosts);
}
<|endoftext|>


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

/**
 * @fileoverview Externs file for OpenSocial shindig libraries.
 * Note this may not be comprehensive.
 *
 * @see http://incubator.apache.org/shindig/shindig-1.1.x/shindig-features/jsdoc
 * @externs
 */

// Root namespace for shindig related functionality.
var shindig = {};

/**
 * Implements a safer random() method that is seeded from
 * screen width/height and (presumably random/unguessable) mouse
 * movement, in an effort to create a better seed for random().
 * @return {number} The random number.
 */
shindig.random = function() {};

/**
 * Implements a SHA1 hasher.
 * @return {Object} SHA1 hasher object.
 */
shindig.sha1 = function() {};
<commit_msg><commit_after>/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Externs file for OpenSocial shindig libraries.
 * Note this may not be comprehensive.
 *
 * @see http://incubator.apache.org/shindig/shindig-1.1.x/shindig-features/jsdoc
 * @externs
 */

// Root namespace for shindig related functionality.
var shindig = {};

/**
 * Implements a safer random() method that is seeded from
 * screen width/height and (presumably random/unguessable) mouse
 * movement, in an effort to create a better seed for random().
 * @return {number} The random number.
 */
shindig.random = function() {};

/**
 * Implements a SHA1 hasher.
 * @return {{reset: function(), update:
 * function(Array.<number>|string,number=), digest:
 * function():Array.<number>, digestString: function():string}} SHA1
 * hasher object. Methods of the returned object are: reset() which
 * resets the internal accumulator, update(bytes, opt_length) which
 * adds a byte array or string to the internal accumulator (or
 * optionally just a prefix of length opt_length), digest() which
 * returns a finalized hash as an array of 20 byte values, and
 * digestString() which returns a hex-encoded string containing a
 * finalized hash.
 */
shindig.sha1 = function() {};
<|endoftext|>


<commit_before>//
// Port
//
BiwaScheme.Port = BiwaScheme.Class.create({
  initialize: function(is_in, is_out){
    this.is_open = true;
    this.is_binary = false; //??
    this.is_input = is_in;
    this.is_output = is_out;
  },
  close: function(){
    // close port
    this.is_open = false;
  },
  inspect: function(){
    return "#<Port>";
  },
  to_write: function(){
    return "#<Port>";
  }
});
BiwaScheme.Port.BrowserInput = BiwaScheme.Class.extend(new BiwaScheme.Port(true, false), {
  initialize: function(){
  },
  get_string: function(after){
    var form = document.createElement("div");
    form.html("<input id='webscheme-read-line' type='text'><input id='webscheme-read-line-submit' type='button' value='ok'>");
    $('bs-console').appendChild(form);

    return new BiwaScheme.Pause(function(pause){
      Event.observe($('webscheme-read-line-submit'), 'click', function(){
        var input = $('webscheme-read-line').value;
        form.parentNode.removeChild(form);
        puts(input);
        pause.resume(after(input));
      });
    });
  }
})
BiwaScheme.Port.DefaultOutput = BiwaScheme.Class.extend(new BiwaScheme.Port(false, true), {
  initialize: function(){
  },
  put_string: function(str){
    puts(str, true);
  }
})

//
// string ports (srfi-6)
//
BiwaScheme.Port.StringOutput = BiwaScheme.Class.extend(new BiwaScheme.Port(false, true), {
  initialize: function(){
    this.buffer = [];
  },
  put_string: function(str){
    this.buffer.push(str);
  },
  output_string: function(str){
    return this.buffer.join("");
  }
});
BiwaScheme.Port.StringInput = BiwaScheme.Class.extend(new BiwaScheme.Port(true, false), {
  initialize: function(str){
    this.str = str;
  },
  get_string: function(after){
    return after(this.str);
  }
});
BiwaScheme.Port.current_input  = new BiwaScheme.Port.BrowserInput();
BiwaScheme.Port.current_output = new BiwaScheme.Port.DefaultOutput();
BiwaScheme.Port.current_error  = new BiwaScheme.Port.DefaultOutput();
<commit_msg>Fixed BrowserInput.<commit_after>//
// Port
//
BiwaScheme.Port = BiwaScheme.Class.create({
  initialize: function(is_in, is_out){
    this.is_open = true;
    this.is_binary = false; //??
    this.is_input = is_in;
    this.is_output = is_out;
  },
  close: function(){
    // close port
    this.is_open = false;
  },
  inspect: function(){
    return "#<Port>";
  },
  to_write: function(){
    return "#<Port>";
  }
});
BiwaScheme.Port.BrowserInput = BiwaScheme.Class.extend(new BiwaScheme.Port(true, false), {
  initialize: function(){
  },
  get_string: function(after){
    var form = $("<form/>");
    form.html("<input id='webscheme-read-line' type='text'><input id='webscheme-read-line-submit' type='button' value='ok'>");
    $("#bs-console").append(form);

    return new BiwaScheme.Pause(function(pause){
      $("#webscheme-read-line-submit").click(function(){
        var input = $("#webscheme-read-line").val();
        form.remove();
        puts(input);
        pause.resume(after(input));
      });
    });
  }
})
BiwaScheme.Port.DefaultOutput = BiwaScheme.Class.extend(new BiwaScheme.Port(false, true), {
  initialize: function(){
  },
  put_string: function(str){
    puts(str, true);
  }
})

//
// string ports (srfi-6)
//
BiwaScheme.Port.StringOutput = BiwaScheme.Class.extend(new BiwaScheme.Port(false, true), {
  initialize: function(){
    this.buffer = [];
  },
  put_string: function(str){
    this.buffer.push(str);
  },
  output_string: function(str){
    return this.buffer.join("");
  }
});
BiwaScheme.Port.StringInput = BiwaScheme.Class.extend(new BiwaScheme.Port(true, false), {
  initialize: function(str){
    this.str = str;
  },
  get_string: function(after){
    return after(this.str);
  }
});
BiwaScheme.Port.current_input  = new BiwaScheme.Port.BrowserInput();
BiwaScheme.Port.current_output = new BiwaScheme.Port.DefaultOutput();
BiwaScheme.Port.current_error  = new BiwaScheme.Port.DefaultOutput();
<|endoftext|>