<commit_before>// USE node.js 7.6 or above

var tls = require('tls');
var Docker = require('dockerode');
//var Queue = require('./queue.js').Queue;
var stream = require('stream');
var fs = require('fs');
//var events = require('events');
//var eventEmitter = require('events').EventEmitter;

var log = function(message){
    console.log('[' + new Date().toISOString() + '] ' + message);
}

/* Use ENV VAR as configuration
{
    "default": {"labels":["myname"], "token":"TOKEN_1"},
    "filters": [
        { "labels":["myname", "tag1"], "filter":["tag1=a", "tag2=1"], "token":"TOKEN_2"},
        { "labels":["myname", "tag2"], "filter":["tag1=b", "tag2=1"], "token":"TOKEN_3"},
    ]
}
*/
var config = JSON.parse(process.env['DOCKER_LE_CONFIG']); //ENV VAR

var containerPool = {}; // keep a connection pool of docker api

var leSocket = null; // socket object to Logentries
var dockerEvtSocket = null; // docker event listener socket

var docker = new Docker({socketPath: '/var/run/docker.sock'}); // unix socket

var LOG_TIME = 5000; // query docker api time interval
var EVENT_TIME = 5000;
var logTimer = null;
var eventTimer = null;

/* STARTS */
try{
    connectLE();
}catch(err){
    console.log(err);
}

// run GC every hour
setInterval(function(){
    if (global.gc){
        global.gc();
        log("[GC] Done!");
        return;
    }
    log("[GC] no GC, run with --expose-gc ?");

}, 3600000);

/*
*
* Change docker log buffer to json object:
* {
*     time: "unix time in ISO String"
*     log: "log content"
* }
*
* */
function dockerLogToObj(chunk){
    try{
        var s = chunk.toString();
        return {
            time: new Date(s.substr(0, 30)).toISOString(),
            log: s.substr(31).trim()
        }
    }catch(err){
    }
}

/*
*
*  1. keep connected with Logentries TCP api
*  2. keep log the missed containers (in case docker event api disconnected)
*  3. keep connected with docker event api
*
* */
function connectLE(){
    try{
        if (leSocket.ready) {
            return;
        }
    }catch(err){}

    log('Connecting Logentries...')
    leSocket = tls.connect(443, "data.logentries.com", {}, function () {
        if (leSocket.authorized) {
            leSocket.setEncoding('utf8');
            leSocket.setNoDelay();

            leSocket.ready = true;
            log("Logentries connected!");

            logContainers();
            listenDockerEvent();

            // keep log skipped containers
            clearInterval(logTimer);
            logTimer = setInterval(logContainers, LOG_TIME);

            // reconnect the docker event API
            clearInterval(eventTimer);
            eventTimer = setInterval(listenDockerEvent, EVENT_TIME);
        }else{
            log("Logentries failed!");
        }

    });

    // Just reconnect!!!
    leSocket.on('end', function(){
        log('Logentries connection closed! reconnect...');
        leSocket = null;
        connectLE
    });

    leSocket.on('close', function(){
        log('Logentries connection closed! reconnect...');
        leSocket = null;
        connectLE
    });

}

function le_write(token, message){
    if (!leSocket.ready) return;
    leSocket.write(token + ' ' + message + '\n');
}

async function logContainers(id, since){
    // get all containers
    var containers = await getContainers();
    for (var i=0; i < containers.length; i++){
        var c = containers[i];

        if (id && since && c['Id'] === id){
            _logContainer(c, since);
        }
        _logContainer(c);
    }

};

function _logContainer(c, since){
    // stop if already exist in connection pool
    if (containerPool[c.Id]){
        return;
    }

    var labels = hashTohash(c.Labels); // hash map for label checking
    var logset = []; // logset token list
    // match any possible label match
    for (var j=0; j < config.filters.length; j++){
        var filter = config.filters[j]
        // if matched, add to the logset
        if (matchLabel(filter.filter, labels)){

            var labelsToLogs = {};
            // include some labels to the log
            for (var k=0; k < filter.labels.length; k++){
                var key = filter.labels[k];
                if (c.Labels[key]){
                    labelsToLogs[key] = c.Labels[key]
                }
            }
            logset.push({ token:filter.token, labels:labelsToLogs});
        }
    }
    // if nothing match, go to the default logset if any
    if (logset.length === 0 && config.default ){
        var labelsToLogs = {};
        for (var j=0; j < config.default.labels.length; j++){
            var key = config.default.labels[j];
            if (c.Labels[key]){
                labelsToLogs[key] = c.Labels[key]
            }
        }

        logset.push({ token: config.default.token, labels: labelsToLogs});
    }

    listenDockerLog({id: c.Id, logset:logset, since: since});

}

function hashTohash(h){
    var keys = Object.keys(h);
    var o = {};
    for(var i=0; i < keys.length; i++){
        o[keys[i]+'='+h[keys[i]]] = true;
    }
    return o;

}

function matchLabel(f, l){
    for (var i=0; i < f.length; i++){
        if (!l[f[i]]) return false;
    }
    return true;
}

async function getContainers(){
    try {
        var res = await docker.listContainers({});
    }catch(err){
        log(err)
        return []
    }
    return res;
}

function listenDockerLog(info){
    // stop if exists
    if (containerPool[info.id]){
        return;
    }

    var container = docker.getContainer(info.id);
    var logStream = new stream.PassThrough();
    containerPool[info.id] = logStream; // store socket object to the pool
    logStream.info = info; // store info for reference
    // TODO: may also need to close the "stream" ?
    logStream.on('data', function(chunk){
        var l = dockerLogToObj(chunk);
        // add the Labels to the real log object
        for (var i=0; i < this.info.logset.length; i++){
            try{
                var logset = this.info.logset[i];
                // fire the log!
                le_write(logset.token, JSON.stringify(Object.assign(l, logset.labels)));
            }catch(err){
            }
        }
    });

    if (!info['since']){
        info['since'] = Math.round(new Date().getTime()/1000);
    }

    container.logs({
        follow: true,
        stdout: true,
        stderr: true,
        timestamps: true,
        // unixtime in SEC, very on99!
        since: info['since'],
    }, function(err, stream){
        if(err) {
            log(err);
            return;
        }

        container.modem.demuxStream(stream, logStream, logStream);

        stream.on('error', function(err){
            log('[ERROR] Container stream error! ' + logStream.info.id);
            log(err);
        });

        stream.on('end', function(){
            log('Container stream ended! "' + logStream.info.id );
            delete containerPool[logStream.info.id]; // remove socket object from the pool
            logStream.end('!stop!');
        });

        stream.on('close', function(){
            log('Container stream closed! "' + logStream.info.id );
            log('Container "' + logStream.info.id +'" stopped!');
            delete containerPool[logStream.info.id];
            logStream.end('!stop!');
        });

    });
}

/* Docker events sample
{
    "status": "start",
    "id": "451368a754f26702c12dbc44cfc7ac7096f775c57415522bb57005b0881de834",
    "from": "quay.io/onesky/dummy-log",
    "Type": "container",
    "Action": "start",
    "Actor": {
        "ID": "451368a754f26702c12dbc44cfc7ac7096f775c57415522bb57005b0881de834",
        "Attributes": {
            "image": "quay.io/onesky/dummy-log",
            "name": "optimistic_spence"
        }
    },
    "time": 1489729976,
    "timeNano": 1489729976229354000
}

{
    "status": "die",
    "id": "9c84c4fba102a75ad1e501b78fa80338e32fc39d356d7421f23e49d80cc0212b",
    "from": "quay.io/onesky/dummy-log",
    "Type": "container",
    "Action": "die",
    "Actor": {
        "ID": "9c84c4fba102a75ad1e501b78fa80338e32fc39d356d7421f23e49d80cc0212b",
        "Attributes": {
            "exitCode": "0",
            "image": "quay.io/onesky/dummy-log",
            "name": "nervous_northcutt"
        }
    },
    "time": 1489730101,
    "timeNano": 1489730101166362600
}

*/
function listenDockerEvent(){
    try{
        if (dockerEvtSocket.ready) {
            return;
        }
    }catch(err){}

    docker.getEvents({},function(err, res){
        if (err){
            console.log(err);
            return;
        }
        dockerEvtSocket = res;
        dockerEvtSocket.ready = true;
        res.on('data', function(data){

            var event = JSON.parse(data.toString());
            /*
             * Only 2 events will be considered:
             * 1. container -> start (just log that container)
             * 2. container -> die (log only)
             */
            if (event['Type'] !== 'container') {
                return;
            }

            if (event['status'] === 'start') {
                log('[DOCKER_EVENT] ' + event['id']  + ' '+  event['status'] );
                logContainers(event['id'], event['time']);
                return;
            }

            if (event['status'] === 'die') {
                log('[DOCKER_EVENT] ' + event['id']  + ' '+  event['status'] );
                return;
            }
        })

        res.on('error', function(){
            log('[ERROR] Listen Docker Event error.');

        })
        res.on('end', function(){
            log('[ERROR] Listen Docker Event connection end.');
            dockerEvtSocket = null;
            listenDockerEvent();
        })
        res.on('close', function(){
            log('[ERROR] Listen Docker Event connection closed.');
            dockerEvtSocket = null;
            listenDockerEvent();
        })

        log('Listening Docker Events...');
    })

}

<commit_msg>more log message<commit_after>// USE node.js 7.6 or above

var tls = require('tls');
var Docker = require('dockerode');
//var Queue = require('./queue.js').Queue;
var stream = require('stream');
var fs = require('fs');
//var events = require('events');
//var eventEmitter = require('events').EventEmitter;

var log = function(message){
    console.log('[' + new Date().toISOString() + '] ' + message);
}

/* Use ENV VAR as configuration
{
    "default": {"labels":["myname"], "token":"TOKEN_1"},
    "filters": [
        { "labels":["myname", "tag1"], "filter":["tag1=a", "tag2=1"], "token":"TOKEN_2"},
        { "labels":["myname", "tag2"], "filter":["tag1=b", "tag2=1"], "token":"TOKEN_3"},
    ]
}
*/
var config = JSON.parse(process.env['DOCKER_LE_CONFIG']); //ENV VAR

var containerPool = {}; // keep a connection pool of docker api

var leSocket = null; // socket object to Logentries
var dockerEvtSocket = null; // docker event listener socket

var docker = new Docker({socketPath: '/var/run/docker.sock'}); // unix socket

var LOG_TIME = 5000; // query docker api time interval
var EVENT_TIME = 5000;
var logTimer = null;
var eventTimer = null;

/* STARTS */
try{
    connectLE();
}catch(err){
    console.log(err);
}

// run GC every hour
setInterval(function(){
    if (global.gc){
        global.gc();
        log("[GC] Done!");
        return;
    }
    log("[GC] no GC, run with --expose-gc ?");

}, 3600000);

/*
*
* Change docker log buffer to json object:
* {
*     time: "unix time in ISO String"
*     log: "log content"
* }
*
* */
function dockerLogToObj(chunk){
    try{
        var s = chunk.toString();
        return {
            time: new Date(s.substr(0, 30)).toISOString(),
            log: s.substr(31).trim()
        }
    }catch(err){
    }
}

/*
*
*  1. keep connected with Logentries TCP api
*  2. keep log the missed containers (in case docker event api disconnected)
*  3. keep connected with docker event api
*
* */
function connectLE(){
    try{
        if (leSocket.ready) {
            return;
        }
    }catch(err){}

    log('Connecting Logentries...')
    leSocket = tls.connect(443, "data.logentries.com", {}, function () {
        if (leSocket.authorized) {
            leSocket.setEncoding('utf8');
            leSocket.setNoDelay();

            leSocket.ready = true;
            log("Logentries connected!");

            logContainers();
            listenDockerEvent();

            // keep log skipped containers
            clearInterval(logTimer);
            logTimer = setInterval(logContainers, LOG_TIME);

            // reconnect the docker event API
            clearInterval(eventTimer);
            eventTimer = setInterval(listenDockerEvent, EVENT_TIME);
        }else{
            log("Logentries failed!");
        }

    });

    // Just reconnect!!!
    leSocket.on('error', function(){
        log('Logentries connection error!');
    });

    leSocket.on('close', function(){
        log('Logentries connection closed!');
    });

    leSocket.on('end', function(){
        log('Logentries connection ended! reconnect...');
        leSocket = null;
        connectLE
    });

}

function le_write(token, message){
    if (!leSocket.ready) return;
    leSocket.write(token + ' ' + message + '\n');
}

async function logContainers(id, since){
    // get all containers
    var containers = await getContainers();
    for (var i=0; i < containers.length; i++){
        var c = containers[i];

        if (id && since && c['Id'] === id){
            _logContainer(c, since);
        }
        _logContainer(c);
    }

};

function _logContainer(c, since){
    // stop if already exist in connection pool
    if (containerPool[c.Id]){
        return;
    }

    var labels = hashTohash(c.Labels); // hash map for label checking
    var logset = []; // logset token list
    // match any possible label match
    for (var j=0; j < config.filters.length; j++){
        var filter = config.filters[j]
        // if matched, add to the logset
        if (matchLabel(filter.filter, labels)){

            var labelsToLogs = {};
            // include some labels to the log
            for (var k=0; k < filter.labels.length; k++){
                var key = filter.labels[k];
                if (c.Labels[key]){
                    labelsToLogs[key] = c.Labels[key]
                }
            }
            logset.push({ token:filter.token, labels:labelsToLogs});
        }
    }
    // if nothing match, go to the default logset if any
    if (logset.length === 0 && config.default ){
        var labelsToLogs = {};
        for (var j=0; j < config.default.labels.length; j++){
            var key = config.default.labels[j];
            if (c.Labels[key]){
                labelsToLogs[key] = c.Labels[key]
            }
        }

        logset.push({ token: config.default.token, labels: labelsToLogs});
    }

    listenDockerLog({id: c.Id, logset:logset, since: since});

}

function hashTohash(h){
    var keys = Object.keys(h);
    var o = {};
    for(var i=0; i < keys.length; i++){
        o[keys[i]+'='+h[keys[i]]] = true;
    }
    return o;

}

function matchLabel(f, l){
    for (var i=0; i < f.length; i++){
        if (!l[f[i]]) return false;
    }
    return true;
}

async function getContainers(){
    try {
        var res = await docker.listContainers({});
    }catch(err){
        log(err)
        return []
    }
    return res;
}

function listenDockerLog(info){
    // stop if exists
    if (containerPool[info.id]){
        return;
    }

    var container = docker.getContainer(info.id);
    var logStream = new stream.PassThrough();
    containerPool[info.id] = logStream; // store socket object to the pool
    logStream.info = info; // store info for reference
    // TODO: may also need to close the "stream" ?
    logStream.on('data', function(chunk){
        var l = dockerLogToObj(chunk);
        // add the Labels to the real log object
        for (var i=0; i < this.info.logset.length; i++){
            try{
                var logset = this.info.logset[i];
                // fire the log!
                le_write(logset.token, JSON.stringify(Object.assign(l, logset.labels)));
            }catch(err){
                log('LOG_ERROR: ' + err);
            }
        }
    });

    logStream.on('end', function(){
        log('LogSteam ended! ' + logStream.info.id);
    })
    logStream.on('error', function(){
        log('LogSteam error! ' + logStream.info.id);
    })

    logStream.on('close', function(){
        log('LogSteam closed! ' + logStream.info.id);
    })

    if (!info['since']){
        info['since'] = Math.floor(new Date().getTime()/1000) - 1;
    }

    container.logs({
        follow: true,
        stdout: true,
        stderr: true,
        timestamps: true,
        // unixtime in SEC, very on99!
        since: info['since'],
    }, function(err, stream){
        if(err) {
            log(err);
            return;
        }

        container.modem.demuxStream(stream, logStream, logStream);
        log('Container log stream connected! ' + logStream.info.id);

        stream.on('error', function(err){
            log('Container stream error! ' + logStream.info.id);
            log(err);
        });

        stream.on('end', function(){
            log('Container stream ended! "' + logStream.info.id );
            delete containerPool[logStream.info.id]; // remove socket object from the pool
            logStream.end('!stop!');
        });

        stream.on('close', function(){
            log('Container stream closed! "' + logStream.info.id );
            log('Container "' + logStream.info.id +'" stopped!');
            delete containerPool[logStream.info.id];
            logStream.end('!stop!');
        });

    });
}

/* Docker events sample
{
    "status": "start",
    "id": "451368a754f26702c12dbc44cfc7ac7096f775c57415522bb57005b0881de834",
    "from": "quay.io/onesky/dummy-log",
    "Type": "container",
    "Action": "start",
    "Actor": {
        "ID": "451368a754f26702c12dbc44cfc7ac7096f775c57415522bb57005b0881de834",
        "Attributes": {
            "image": "quay.io/onesky/dummy-log",
            "name": "optimistic_spence"
        }
    },
    "time": 1489729976,
    "timeNano": 1489729976229354000
}

{
    "status": "die",
    "id": "9c84c4fba102a75ad1e501b78fa80338e32fc39d356d7421f23e49d80cc0212b",
    "from": "quay.io/onesky/dummy-log",
    "Type": "container",
    "Action": "die",
    "Actor": {
        "ID": "9c84c4fba102a75ad1e501b78fa80338e32fc39d356d7421f23e49d80cc0212b",
        "Attributes": {
            "exitCode": "0",
            "image": "quay.io/onesky/dummy-log",
            "name": "nervous_northcutt"
        }
    },
    "time": 1489730101,
    "timeNano": 1489730101166362600
}

*/
function listenDockerEvent(){
    try{
        if (dockerEvtSocket.ready) {
            return;
        }
    }catch(err){}

    docker.getEvents({},function(err, res){
        if (err){
            console.log(err);
            return;
        }
        dockerEvtSocket = res;
        dockerEvtSocket.ready = true;
        res.on('data', function(data){

            var event = JSON.parse(data.toString());
            /*
             * Only 2 events will be considered:
             * 1. container -> start (just log that container)
             * 2. container -> die (log only)
             */
            if (event['Type'] !== 'container') {
                return;
            }

            if (event['status'] === 'start') {
                log('[DOCKER_EVENT] ' + event['id']  + ' '+  event['status'] );
                logContainers(event['id'], event['time']);
                return;
            }

            if (event['status'] === 'die') {
                log('[DOCKER_EVENT] ' + event['id']  + ' '+  event['status'] );
                return;
            }
        })

        res.on('error', function(){
            log('[ERROR] Listen Docker Event error.');

        })
        res.on('end', function(){
            log('[ERROR] Listen Docker Event connection end.');
            dockerEvtSocket = null;
            listenDockerEvent();
        })
        res.on('close', function(){
            log('[ERROR] Listen Docker Event connection closed.');
            dockerEvtSocket = null;
            listenDockerEvent();
        })

        log('Listening Docker Events...');
    })

}

<|endoftext|>


<commit_before>// *************************************************************************
// Copyright (c) 2014-2017, SUSE LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of SUSE LLC nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// *************************************************************************
//
// stack.js -- stack enabling targets (dialogs) to be "push"ed and "pop"ped
//
// To initiate a new target (dialog), call stack.push(target)
//
// To return to the previous target (dialog), call stack.pop()
//
// Each target type (dmenu, dform, etc.) needs to have methods for saving
// and restoring the target state. These can range from very simple (dmenu)
// to complicated (dbrowser).
//
"use strict";

define ([
    "jquery",
    "lib",
    "prototypes",
    "target"
], function (
    $,
    lib,
    prototypes,
    target
) {

    var
        // object for storing the stack
        _stack = [],

        // pop a target and its state off the stack
        // ARG1 (optional) - object to be merged into stateObj
        // ARG2 (optional) - boolean, whether to call start() (default: true)
        pop = function (mo, start) {
            start = (start === false) ? false : true;
            console.log("Entering stack.pop() with stack", _stack);
            var stackObj,
                type;
            _stack.pop();
            if (_stack.length === 0) {
                console.log("Stack empty - logging out");
                target.pull('logout').start();
                return;
            }
            stackObj = _stack[_stack.length - 1];
            if (typeof mo === 'object') {
                console.log("pop() was passed an object", mo);
                $.extend(stackObj.state, mo);
            }
            stackObj.pushed = false;
            console.log("Popped " + stackObj.target.name);
            type = stackObj.target.type;
            if (start) {
                lib.clearResult();
                stackObj.target.start();
            }
        },

        popWithoutStart = function (mo) {
            pop(mo, false);
        },

        // push a target and its state onto the stack
        push = function (tgt, obj, opts) {
            console.log("Entering stack.push() with target", tgt, "object", obj, "and opts", opts);
            // console.log("and stack", _stack);
            var flag,
                xtarget;
            if (obj === undefined || obj === null) {
                obj = {};
            }
            if (typeof tgt === "string") {
                tgt = target.pull(tgt);
            }
            if (typeof tgt !== "object") {
                console.log("ERROR in stack.push() - found no target object");
                return;
            }
            if (typeof opts === "object") {
                flag = opts.hasOwnProperty('flag') ? opts.flag : false;
                xtarget = opts.hasOwnProperty('xtarget') ? opts.xtarget : null;
                console.log("In stack.push(), setting flag", flag, "and xtarget", xtarget);
            }
            if (tgt.pushable) {
                _stack.push({
                    "flag": flag,
                    "push": true,
                    "state": obj,
                    "target": tgt,
                    "xtarget": xtarget
                });
            }
            lib.clearResult();
            tgt.start(obj);
        },

        getFlag = function () {
            return _stack[_stack.length - 1].flag;
        },
        getPush = function () {
            return _stack[_stack.length - 1].push;
        },
        getState = function () {
            return _stack[_stack.length - 1].state;
        },
        getTarget = function () {
            return _stack[_stack.length - 1].target;
        },
        getXTarget = function () {
            return _stack[_stack.length - 1].xtarget;
        },

        resetStack = function () {
            // (re)initializes the stack to an empty array
            _stack = [];
        },

        setFlag = function () {
            _stack[_stack.length - 1].flag = true;
        },
        setPush = function (newPush) {
            _stack[_stack.length - 1].push = newPush;
        },
        setState = function (newState) {
            _stack[_stack.length - 1].state = newState;
        },
        setTarget = function (newTarget) {
            _stack[_stack.length - 1].target = newTarget;
        },
        setXTarget = function (newXTarget) {
            _stack[_stack.length - 1].xtarget = newXTarget;
        },

        unsetFlag = function () {
            _stack[_stack.length - 1].flag = false;
        },
        unsetPush = function () {
            _stack[_stack.length - 1].push = undefined;
        },
        unsetState = function () {
            _stack[_stack.length - 1].state = undefined;
        },
        unsetTarget = function () {
            _stack[_stack.length - 1].target = undefined;
        },
        unsetXTarget = function () {
            _stack[_stack.length - 1].xtarget = undefined;
        },

        // unwind stack until given target is reached
        // optional object to pass to start()
        unwindToTarget = function (tname, obj) {
            console.log("Unwinding the stack to target " + tname);
            var tgt;
            for (var i = _stack.length; i > 0; i--) {
                tgt = _stack[i - 1].target;
                if (tgt.name === tname) {
                   break;
                }
                popWithoutStart();
            }
            tgt.start(obj);
        },

        unwindToFlag = function () {
            console.log("Unwinding the stack to flag");
            var flag;
            for (var i = _stack.length; i > 0; i--) {
                flag = _stack[i - 1].flag;
                if (flag) {
                   break;
                }
                popWithoutStart();
            }
            _stack[_stack.length - 1].target.start();
        },

        // grep stack for a target name (exact match)
        grep = function (tname) {
            var retval = false;
            for (var i=0; i<_stack.length; i++) {
                if (_stack[i].target.name === tname) {
                    return true;
                }
            }
            return false;
        };

    return {
        "getFlag": getFlag,
        "getPush": getPush,
        "getState": getState,
        "getTarget": getTarget,
        "getXTarget": getXTarget,
        "grep": grep,
        "pop": pop,
        "popWithoutStart": popWithoutStart,
        "push": push,
        "resetStack": resetStack,
        "setFlag": setFlag,
        "setPush": setPush,
        "setState": setState,
        "setTarget": setTarget,
        "setXTarget": setXTarget,
        "unsetFlag": unsetFlag,
        "unsetPush": unsetPush,
        "unsetState": unsetState,
        "unsetTarget": unsetTarget,
        "unsetXTarget": unsetXTarget,
        "unwindToFlag": unwindToFlag,
        "unwindToTarget": unwindToTarget
    };

});
<commit_msg>stack: implement getStack method<commit_after>// *************************************************************************
// Copyright (c) 2014-2017, SUSE LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of SUSE LLC nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// *************************************************************************
//
// stack.js -- stack enabling targets (dialogs) to be "push"ed and "pop"ped
//
// To initiate a new target (dialog), call stack.push(target)
//
// To return to the previous target (dialog), call stack.pop()
//
// Each target type (dmenu, dform, etc.) needs to have methods for saving
// and restoring the target state. These can range from very simple (dmenu)
// to complicated (dbrowser).
//
"use strict";

define ([
    "jquery",
    "lib",
    "prototypes",
    "target"
], function (
    $,
    lib,
    prototypes,
    target
) {

    var
        // object for storing the stack
        _stack = [],

        // pop a target and its state off the stack
        // ARG1 (optional) - object to be merged into stateObj
        // ARG2 (optional) - boolean, whether to call start() (default: true)
        pop = function (mo, start) {
            start = (start === false) ? false : true;
            console.log("Entering stack.pop() with stack", _stack);
            var stackObj,
                type;
            _stack.pop();
            if (_stack.length === 0) {
                console.log("Stack empty - logging out");
                target.pull('logout').start();
                return;
            }
            stackObj = _stack[_stack.length - 1];
            if (typeof mo === 'object') {
                console.log("pop() was passed an object", mo);
                $.extend(stackObj.state, mo);
            }
            stackObj.pushed = false;
            console.log("Popped " + stackObj.target.name);
            type = stackObj.target.type;
            if (start) {
                lib.clearResult();
                stackObj.target.start();
            }
        },

        popWithoutStart = function (mo) {
            pop(mo, false);
        },

        // push a target and its state onto the stack
        push = function (tgt, obj, opts) {
            console.log("Entering stack.push() with target", tgt, "object", obj, "and opts", opts);
            // console.log("and stack", _stack);
            var flag,
                xtarget;
            if (obj === undefined || obj === null) {
                obj = {};
            }
            if (typeof tgt === "string") {
                tgt = target.pull(tgt);
            }
            if (typeof tgt !== "object") {
                console.log("ERROR in stack.push() - found no target object");
                return;
            }
            if (typeof opts === "object") {
                flag = ('flag' in opts) ? opts.flag : false;
                xtarget = ('xtarget' in opts) ? opts.xtarget : null;
                console.log("In stack.push(), setting flag", flag, "and xtarget", xtarget);
            }
            if (tgt.pushable) {
                _stack.push({
                    "flag": flag,
                    "push": true,
                    "state": obj,
                    "target": tgt,
                    "xtarget": xtarget
                });
            }
            lib.clearResult();
            tgt.start(obj);
        },

        getFlag = function () {
            return _stack[_stack.length - 1].flag;
        },
        getPush = function () {
            return _stack[_stack.length - 1].push;
        },
        getStack = function () {
            // returns the entire stack
            return _stack;
        },
        getState = function () {
            return _stack[_stack.length - 1].state;
        },
        getTarget = function () {
            return _stack[_stack.length - 1].target;
        },
        getXTarget = function () {
            return _stack[_stack.length - 1].xtarget;
        },

        resetStack = function () {
            // (re)initializes the stack to an empty array
            _stack = [];
        },

        setFlag = function () {
            _stack[_stack.length - 1].flag = true;
        },
        setPush = function (newPush) {
            _stack[_stack.length - 1].push = newPush;
        },
        setState = function (newState) {
            _stack[_stack.length - 1].state = newState;
        },
        setTarget = function (newTarget) {
            _stack[_stack.length - 1].target = newTarget;
        },
        setXTarget = function (newXTarget) {
            _stack[_stack.length - 1].xtarget = newXTarget;
        },

        unsetFlag = function () {
            _stack[_stack.length - 1].flag = false;
        },
        unsetPush = function () {
            _stack[_stack.length - 1].push = undefined;
        },
        unsetState = function () {
            _stack[_stack.length - 1].state = undefined;
        },
        unsetTarget = function () {
            _stack[_stack.length - 1].target = undefined;
        },
        unsetXTarget = function () {
            _stack[_stack.length - 1].xtarget = undefined;
        },

        // unwind stack until given target is reached
        // optional object to pass to start()
        unwindToTarget = function (tname, obj) {
            console.log("Unwinding the stack to target " + tname);
            var tgt;
            for (var i = _stack.length; i > 0; i--) {
                tgt = _stack[i - 1].target;
                if (tgt.name === tname) {
                   break;
                }
                popWithoutStart();
            }
            tgt.start(obj);
        },

        unwindToFlag = function () {
            console.log("Unwinding the stack to flag");
            var flag;
            for (var i = _stack.length; i > 0; i--) {
                flag = _stack[i - 1].flag;
                if (flag) {
                   break;
                }
                popWithoutStart();
            }
            _stack[_stack.length - 1].target.start();
        },

        // grep stack for a target name (exact match)
        grep = function (tname) {
            var retval = false;
            for (var i=0; i<_stack.length; i++) {
                if (_stack[i].target.name === tname) {
                    return true;
                }
            }
            return false;
        };

    return {
        "getFlag": getFlag,
        "getPush": getPush,
        "getStack": getStack,
        "getState": getState,
        "getTarget": getTarget,
        "getXTarget": getXTarget,
        "grep": grep,
        "pop": pop,
        "popWithoutStart": popWithoutStart,
        "push": push,
        "resetStack": resetStack,
        "setFlag": setFlag,
        "setPush": setPush,
        "setState": setState,
        "setTarget": setTarget,
        "setXTarget": setXTarget,
        "unsetFlag": unsetFlag,
        "unsetPush": unsetPush,
        "unsetState": unsetState,
        "unsetTarget": unsetTarget,
        "unsetXTarget": unsetXTarget,
        "unwindToFlag": unwindToFlag,
        "unwindToTarget": unwindToTarget
    };

});
<|endoftext|>


<commit_before>function ddg_spice_bbc(api_result) {
    var query = DDG.get_query(),
        broadcasts = api_result.schedule.day.broadcasts,
        programmes = [],
        now = new Date();

    if (query.indexOf("night") != -1)
        now.setHours(18);

    var date = api_result.schedule.day.date,
        fulldate = new Date(date),
        date_round = 1000 * 60 * 60 * 24,
        inPast = +fulldate < Math.floor(+now / date_round)*date_round;

    for (var i=0; i<broadcasts.length; i++) {
        var end = new Date(broadcasts[i].end);
        if (end > now || inPast)
            programmes.push(broadcasts[i]);
    }

    Spice.render({
        data           : api_result.schedule,
        header1        : api_result.schedule.service.title + (api_result.schedule.service.outlet ? " "+api_result.schedule.service.outlet.title : "") + " (TV Schedule for "+date+")",
        source_url     : "http://www.bbc.co.uk/"+api_result.schedule.service.key+"/programmes/schedules/"+(api_result.schedule.service.outlet ? api_result.schedule.service.outlet.key+"/" : "")+api_result.schedule.day.date.split("-").join("/"),
        source_name    : 'BBC',
        template_frame : "carousel",
        spice_name     : "bbc",
        template_options: {
            li_width        : 120,
            li_height       : 105,
            items           : programmes,
            template_item   : "bbc",
            template_detail : "bbc_details"
        },
        force_big_header : true
    });
}

/*
 * time
 *
 * Find the start and end of a programme and format appropriately
 */
Handlebars.registerHelper("time", function() {
    var to_str = function ddg_spice_bbc_to_str(num) {
        if (num == 0) {
             return "00";
        }
        var str = (num|0)+"";
        if (str.length < 2) {
            str = "0" + str;
        }
        return str;
    };
    var start = new Date(this.start);
    var end = new Date(this.end);
    return to_str(start.getHours()) + ":" + to_str(start.getMinutes()) + " - " + to_str(end.getHours()) + ":" + to_str(end.getMinutes());
});

/*
 * duration
 *
 * Find the duration of a programme and return it
 */
Handlebars.registerHelper("duration", function() {
    var pluralise = function(n) {
        return n > 1 ? "s" : "";
    };
    var dur = this.duration;
    var hours = Math.floor(dur / (60 * 60));
    dur -= hours * 60 * 60;
    var minutes = Math.floor(dur / 60);
    this.duration -= minutes * 60;
    if (hours > 0 && minutes > 0) {
        return hours + " hour"+pluralise(hours)+", "+minutes+" min"+pluralise(minutes);
    } else if (hours > 0 && minutes == 0) {
        return hours + " hour"+pluralise(hours);
    } else {
        return minutes+" min"+pluralise(minutes);
    }
});

// Find the series URL and return it, or if it is not part of a series return the normal url
Handlebars.registerHelper("programme_url", function() {
    return "http://www.bbc.co.uk/programmes/"+(this.programme.programme ? this.programme.programme : this.programme).pid;
});

// Find the programme image and return it
Handlebars.registerHelper("image", function() {
    return "http://ichef.bbci.co.uk/images/ic/272x153/" + (this.programme.image ? this.programme.image.pid :  "legacy/episode/"+this.programme.pid) + ".jpg";
});

// Check if original air date is before today
Handlebars.registerHelper("checkAirDate", function(options) {
    var d = new Date(this.programme.first_broadcast_date);
    var now = new Date();

    if (d < now) {
        return options.fn(this);
    } else {
        return false;
    }
});

// Find the programme's initial broadcast date/time and return it
Handlebars.registerHelper("initial_broadcast", function() {
    var aired = DDG.getDateFromString(this.programme.first_broadcast_date),
        days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
        months = [ 'January','February','March','April','May','June','July','August','September','October','November','December'];

    return days[aired.getDay()] + ", " + months[aired.getMonth()] + " " + aired.getDate() + ", " + aired.getFullYear()
});

// Remove all the episode info from the subtitle
Handlebars.registerHelper("pretty_subtitle", function() {
    var seriesInfo = /series [0-9]+/ig;
    var episodeInfo = /episode [0-9]+/ig;
    var junk = /, ?/ig;
    console.log(this);
    var subtitle = this.programme.display_titles.subtitle + "";
    subtitle = subtitle.replace(seriesInfo, "").replace(episodeInfo, "").replace(junk, "");
    subtitle = $.trim(subtitle);
    if (this.programme.type == "episode" && this.programme.position !== null) {
        subtitle = subtitle.replace(this.programme.programme.title, "");
        subtitle = "Episode "+this.programme.position+(subtitle.length > 0 ? " - " + subtitle : "");
    }
    if (this.programme.programme.type == "series" && this.programme.programme.position !== null) {
        subtitle = this.programme.programme.title+", "+subtitle;
    }
    return subtitle;
});<commit_msg>BBC - More JS cleanup<commit_after>function ddg_spice_bbc(api_result) {
    var query = DDG.get_query(),
        broadcasts = api_result.schedule.day.broadcasts,
        programmes = [],
        now = new Date();

    if (query.indexOf("night") != -1)
        now.setHours(18);

    var date = api_result.schedule.day.date,
        fulldate = new Date(date),
        date_round = 1000 * 60 * 60 * 24,
        inPast = +fulldate < Math.floor(+now / date_round)*date_round;

    for (var i=0; i<broadcasts.length; i++) {
        var end = new Date(broadcasts[i].end);
        if (end > now || inPast)
            programmes.push(broadcasts[i]);
    }

    Spice.render({
        data           : api_result.schedule,
        header1        : api_result.schedule.service.title + (api_result.schedule.service.outlet ? " "+api_result.schedule.service.outlet.title : "") + " (TV Schedule for "+date+")",
        source_url     : "http://www.bbc.co.uk/"+api_result.schedule.service.key+"/programmes/schedules/"+(api_result.schedule.service.outlet ? api_result.schedule.service.outlet.key+"/" : "")+api_result.schedule.day.date.split("-").join("/"),
        source_name    : 'BBC',
        template_frame : "carousel",
        spice_name     : "bbc",
        template_options: {
            li_width        : 120,
            li_height       : 105,
            items           : programmes,
            template_item   : "bbc",
            template_detail : "bbc_details"
        },
        force_big_header : true
    });
}

/*
 * time
 *
 * Find the start and end of a programme and format appropriately
 */
Handlebars.registerHelper("time", function() {
    function to_str (num) {
        if (num == 0) {
             return "00";
        }
        var str = (num|0)+"";
        if (str.length < 2) {
            str = "0" + str;
        }
        return str;
    };

    var start = new Date(this.start),
        end = new Date(this.end);

    return to_str(start.getHours()) + ":" + to_str(start.getMinutes()) + " - " + to_str(end.getHours()) + ":" + to_str(end.getMinutes());
});

/*
 * duration
 *
 * Find the duration of a programme and return it
 */
Handlebars.registerHelper("duration", function() {
    var pluralise = function(n) {
        return n > 1 ? "s" : "";
    };
    var dur = this.duration,
        hours = Math.floor(dur / (60 * 60));

    dur -= hours * 60 * 60;
    var minutes = Math.floor(dur / 60);
    this.duration -= minutes * 60;
    if (hours > 0 && minutes > 0) {
        return hours + " hour"+pluralise(hours)+", "+minutes+" min"+pluralise(minutes);
    } else if (hours > 0 && minutes == 0) {
        return hours + " hour"+pluralise(hours);
    } else {
        return minutes+" min"+pluralise(minutes);
    }
});

// Find the series URL and return it, or if it is not part of a series return the normal url
Handlebars.registerHelper("programme_url", function() {
    return "http://www.bbc.co.uk/programmes/"+(this.programme.programme ? this.programme.programme : this.programme).pid;
});

// Find the programme image and return it
Handlebars.registerHelper("image", function() {
    return "http://ichef.bbci.co.uk/images/ic/272x153/" + (this.programme.image ? this.programme.image.pid :  "legacy/episode/"+this.programme.pid) + ".jpg";
});

// Check if original air date is before today
Handlebars.registerHelper("checkAirDate", function(options) {
    var d = new Date(this.programme.first_broadcast_date),
        now = new Date();

    if (d < now) {
        return options.fn(this);
    } else {
        return false;
    }
});

// Find the programme's initial broadcast date/time and return it
Handlebars.registerHelper("initial_broadcast", function() {
    var aired = DDG.getDateFromString(this.programme.first_broadcast_date),
        days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
        months = [ 'January','February','March','April','May','June','July','August','September','October','November','December'];

    return days[aired.getDay()] + ", " + months[aired.getMonth()] + " " + aired.getDate() + ", " + aired.getFullYear()
});

// Remove all the episode info from the subtitle
Handlebars.registerHelper("pretty_subtitle", function() {
    var seriesInfo = /series [0-9]+/ig,
        episodeInfo = /episode [0-9]+/ig,
        junk = /, ?/ig,
        subtitle = this.programme.display_titles.subtitle + "";

    subtitle = subtitle.replace(seriesInfo, "").replace(episodeInfo, "").replace(junk, "");
    subtitle = $.trim(subtitle);

    if (this.programme.type == "episode" && this.programme.position !== null) {
        subtitle = subtitle.replace(this.programme.programme.title, "");
        subtitle = "Episode "+this.programme.position+(subtitle.length > 0 ? " - " + subtitle : "");
    }

    if (this.programme.programme.type == "series" && this.programme.programme.position !== null) {
        subtitle = this.programme.programme.title+", "+subtitle;
    }

    return subtitle;
});<|endoftext|>


<commit_before>var app = app || {};
app.connectionApi = app.connectionApi || {};

(function(app) {
    document.addEventListener("deviceready", function () {
        var states = null;

        function initStates() {
            states = {};
            states[Connection.UNKNOWN] = 'Unknown connection';
            states[Connection.ETHERNET] = 'Ethernet connection';
            states[Connection.WIFI] = 'WiFi connection';
            states[Connection.CELL_2G] = 'Cell 2G connection';
            states[Connection.CELL_3G] = 'Cell 3G connection';
            states[Connection.CELL_4G] = 'Cell 4G connection';
            states[Connection.CELL] = 'Cell generic connection';
            states[Connection.NONE] = 'No network connection';
        }

        if (!states) {
            initStates();
        }

        app.connectionApi = {
            hasConnection: function() {
                var connectionType = navigator.connection.type;

                if (!connectionType ||
                    [/*'unknown',*/ 'wifi', 'cell', 'none'].indexOf(connectionType.toLowerCase()) !== -1) {
                    return false;
                }

                return true;
            },
            getConnectionType: function() {
                 return networkState = navigator.connection.type;
            },
            getConnectionDescription: function() {
                var connectionType = navigator.connection.type;
                return states[connectionType]
            }
        }
    });
}(app));<commit_msg>Changes made by Dzhenko Penev (dzhenko@gmail.com).<commit_after>var app = app || {};
app.connectionApi = app.connectionApi || {};

(function(app) {
    document.addEventListener("deviceready", function () {
        var states = null;

        function initStates() {
            states = {};
            states[Connection.UNKNOWN] = 'Unknown connection';
            states[Connection.ETHERNET] = 'Ethernet connection';
            states[Connection.WIFI] = 'WiFi connection';
            states[Connection.CELL_2G] = 'Cell 2G connection';
            states[Connection.CELL_3G] = 'Cell 3G connection';
            states[Connection.CELL_4G] = 'Cell 4G connection';
            states[Connection.CELL] = 'Cell generic connection';
            states[Connection.NONE] = 'No network connection';
        }

        if (!states) {
            initStates();
        }

        app.connectionApi = {
            hasConnection: function() {
                var connectionType = navigator.connection.type;

                if (!connectionType ||
                    [/*'unknown',*/ 'wifi', 'cell', 'none'].indexOf(connectionType.toLowerCase()) !== -1) {
                    return false;
                }
                return true;
            },
            getConnectionType: function() {
                 return networkState = navigator.connection.type;
            },
            getConnectionDescription: function() {
                var connectionType = navigator.connection.type;
                return states[connectionType]
            }
        }
    });
}(app));<|endoftext|>


<commit_before>/**
	@license glitch.js v0.1 <http://www.github.com/sjhewitt/glitch.js>
	Released under MIT License

	Copyright (c) 2012 Simon Hewitt.
	http://www.twitter.com/sjhewitt
	*/
(function(){
	/*global $,html2canvas */

	/**
	 * Generates an integer between min and max
	 *
	 * @param  {Number} min The lower bound
	 * @param  {Number} max The upper bound
	 * @return {Number}     A random number
	 */
	function getRandInt(min, max) {
		return (Math.floor(Math.random() * (max - min) + min));
	}

	/**
	 * Apply the glitch effect to a canvas object
	 *
	 * @param  {HTMLCanvasElement} canvas  The canvas (or HTMLImageElement) to apply the glitch to
	 * @param  {number}            amount  The amount to glitch the canvas (default: 6)
	 * @return {HTMLCanvasElement}         A canvas containing a glitched version
	 *                                     of the original canvas
	 */
	var _glitch = function(canvas, amount) {
		var
			// cache the width and height of the canvas locally
			x, y, w = canvas.width, h = canvas.height,

			// _len is an iterator limit, initially storing the number of slices
			// to create
			i, _len = amount || 6,

			// pick a random amount to offset the color channel
			channelOffset = (getRandInt(-_len*2, _len*2) * w * + getRandInt(-_len, _len)) * 4,

			// the maximum amount to offset a chunk of the image is a function of its width
			maxOffset = _len * _len / 100 * w,

			// vars for the width and height of the chunk that gets offset
			chunkWidth, chunkHeight,

			// create a temporary canvas to hold the image we're working on
			tempCanvas = document.createElement("canvas"),
			tempCtx = tempCanvas.getContext("2d"),

			srcData, targetImageData, data;

		// set the dimensions of the working canvas
		tempCanvas.width = w;
		tempCanvas.height = h;

		// draw the initial image onto the working canvas
		tempCtx.drawImage(canvas, 0, 0, w, h);

		// store the data of the original image for use when offsetting a channel
		srcData = tempCtx.getImageData(0, 0, w, h).data;

		// randomly offset slices horizontally
		for (i = 0; i < _len; i++) {

			// pick a random y coordinate to slice at
			y = getRandInt(0, h);

			// pick a random height of the slice
			chunkHeight = Math.min(getRandInt(1, h / 4), h - y);

			// pick a random horizontal distance to offset the slice
			x = getRandInt(1, maxOffset);
			chunkWidth = w - x;

			// draw the first chunk
			tempCtx.drawImage(canvas,
				0, y, chunkWidth, chunkHeight,
				x, y, chunkWidth, chunkHeight);

			// draw the rest
			tempCtx.drawImage(canvas,
				chunkWidth, y, x, chunkHeight,
				0, y, x, chunkHeight);
		}

		// get hold of the ImageData for the working image
		targetImageData = tempCtx.getImageData(0, 0, w, h);

		// and get a local reference to the rgba data array
		data = targetImageData.data;

		// Copy a random color channel from the original image into
		// the working canvas, offsetting it by a random amount
		//
		// ImageData arrays are a single dimension array that contains
		// 4 values for each pixel.
		// so, by initializing `i` to a random number between 0 and 2,
		// and incrementing by 4 on each iteration, we can replace only
		// a single channel in the image
		for(i = getRandInt(0, 3), _len = srcData.length; i < _len; i += 4) {
			data[i+channelOffset] = srcData[i];
		}

		// Make the image brighter by doubling the rgb values
		for(i = 0; i < _len; i++) {
			data[i++] *= 2;
			data[i++] *= 2;
			data[i++] *= 2;
		}

		// TODO: The above loops are the most costly in this function, iterating
		// over all the pixels in the image twice.
		// It maybe possible to optimize this by combining both loops into one,
		// and only processing every other line, as alternate lines are replaced
		// with black in the 'scan lines' block belop

		// copy the tweaked ImageData back into the context
		tempCtx.putImageData(targetImageData, 0, 0);

		// add scan lines
		tempCtx.fillStyle = "rgb(0,0,0)";
		for (i = 0; i < h; i += 2) {
			tempCtx.fillRect (0, i, w, 1);
		}

		return tempCanvas;
	};

	/**
	 * Creates a canvas containing a glitched version of the element
	 * @param  {jQuery}   el       The element to glitch
	 * @param  {Object}   options  An object containing the complete callback,
	 *                             the amount to glitch the image, and any
	 *                             html2canvas options
	 */
	var glitch = function(el, options) {
		options = $.extend({
			// the amount to glitch the image
			amount: 6,
			// a callback that takes the glitched canvas as its only argument
			complete: $.noop
		}, options || {});

		// callback for when the element has been rendered
		options.onrendered = function(canvas) {
			options.complete(_glitch(canvas, options.amount));
		};

		// render the element onto a canvas
		html2canvas(el, options);
	};

	/**
	 * Replace `el` with `newEl` by overlaying a glitched version of `el`, then
	 * animating it out to reveal `newEl`
	 *
	 * @param  {jQuery} el     The original element that will be glitched
	 * @param  {jQuery} newEl  The element to show
	 * @param  {Object} options An object containing the options for the animation
	 *                          and any options for html2canvas
	 */
	var glitchReplace = function(el, newEl, options) {
		// set the default options
		options = $.extend({
			// the amount to glitch the image
			amount: 6,
			// A callback when the animation is complete
			complete: $.noop,
			// The delay after rendering the glitched element until starting the transition
			delay: 300,
			// The duration of the transition effect
			duration: 500,
			// The z-index to apply to the overlay. You might need to tweak this if
			// you have things that appear above the element, or are using high
			// z-indexes in your page
			zIndex: 1000,
			// the transition effect to use. This may be "fade" or "slide"
			effect: "fade",
			// The size of the top border. Set to 0 to disable, only used in slide mode
			borderSize: 2,
			// The color of the top border, only used in slide mode
			borderColor: "green"
		}, options || {});

		var _complete = options.complete;

		options.complete = function(canvas){
			// position the canvas absolutely within the container
			var $canvas = $(canvas).css("position", "absolute"),
				offset = el.offset(),
				// create a container element that contains the canvas and position it
				// over the element we're replacing
				container = $("<div>").css({
						"border-top": options.borderSize ? options.borderSize + "px solid " + options.borderColor : "none",
						position: "absolute",
						left: offset.left,
						top: offset.top - options.borderSize,
						width: canvas.width,
						height: canvas.height,
						overflow: "hidden",
						"z-index": options.zIndex
					})
					// add the canvas as a child to the container
					.html(canvas)
					// add the container to the dom
					.appendTo("body")
					// delay the animation a bit
					.delay(options.delay),

				// the default transition effect is to fade out
				animation = {
					opacity: 0
				},
				animateOptions = {
						duration: options.duration,
						complete: function(){
							// when the animation is done:
							// remove the container from the dom
							container.remove();

							// call the complete callback
							_complete();

							// and clear all references
							options = $canvas = container = null;
						}
					};

			if(options.effect === "slide") {
				// for the slide effect, we move move the container down
				animation = {
					top: offset.top + canvas.height,
					height: 0
				};

				// and on each step of the animation, we need to offset the top so it
				// remains in the same place on the screen
				animateOptions.step = function(now, fx){
					if(fx.prop === "top") {
						$canvas.css("top", fx.start - now);
					}
				};
			}

			// apply the animation
			container.animate(animation, animateOptions);

			// replace the original element with the new one
			newEl.show().insertAfter(el);
			el.detach();
			// el.replaceWith(newEl.show());

		};

		// create a glitched version of the start element
		glitch(el, options);
	};

	window.glitch = glitch;
	window.glitchReplace = glitchReplace;
})();
<commit_msg>changed replace/transition to be properties of main glitch function<commit_after>/**
	@license glitch.js v0.1 <http://www.github.com/sjhewitt/glitch.js>
	Released under MIT License

	Copyright (c) 2012 Simon Hewitt.
	http://www.twitter.com/sjhewitt
	*/
(function(){
	/*global $,html2canvas */

	/**
	 * Generates an integer between min and max
	 *
	 * @param  {Number} min The lower bound
	 * @param  {Number} max The upper bound
	 * @return {Number}     A random number
	 */
	function getRandInt(min, max) {
		return (Math.floor(Math.random() * (max - min) + min));
	}

	/**
	 * Apply the glitch effect to a canvas object
	 *
	 * @param  {HTMLCanvasElement} canvas  The canvas (or HTMLImageElement) to apply the glitch to
	 * @param  {number}            amount  The amount to glitch the canvas (default: 6)
	 * @return {HTMLCanvasElement}         A canvas containing a glitched version
	 *                                     of the original canvas
	 */
	var _glitch = function(canvas, amount) {
		var
			// cache the width and height of the canvas locally
			x, y, w = canvas.width, h = canvas.height,

			// _len is an iterator limit, initially storing the number of slices
			// to create
			i, _len = amount || 6,

			// pick a random amount to offset the color channel
			channelOffset = (getRandInt(-_len*2, _len*2) * w * + getRandInt(-_len, _len)) * 4,

			// the maximum amount to offset a chunk of the image is a function of its width
			maxOffset = _len * _len / 100 * w,

			// vars for the width and height of the chunk that gets offset
			chunkWidth, chunkHeight,

			// create a temporary canvas to hold the image we're working on
			tempCanvas = document.createElement("canvas"),
			tempCtx = tempCanvas.getContext("2d"),

			srcData, targetImageData, data;

		// set the dimensions of the working canvas
		tempCanvas.width = w;
		tempCanvas.height = h;

		// draw the initial image onto the working canvas
		tempCtx.drawImage(canvas, 0, 0, w, h);

		// store the data of the original image for use when offsetting a channel
		srcData = tempCtx.getImageData(0, 0, w, h).data;

		// randomly offset slices horizontally
		for (i = 0; i < _len; i++) {

			// pick a random y coordinate to slice at
			y = getRandInt(0, h);

			// pick a random height of the slice
			chunkHeight = Math.min(getRandInt(1, h / 4), h - y);

			// pick a random horizontal distance to offset the slice
			x = getRandInt(1, maxOffset);
			chunkWidth = w - x;

			// draw the first chunk
			tempCtx.drawImage(canvas,
				0, y, chunkWidth, chunkHeight,
				x, y, chunkWidth, chunkHeight);

			// draw the rest
			tempCtx.drawImage(canvas,
				chunkWidth, y, x, chunkHeight,
				0, y, x, chunkHeight);
		}

		// get hold of the ImageData for the working image
		targetImageData = tempCtx.getImageData(0, 0, w, h);

		// and get a local reference to the rgba data array
		data = targetImageData.data;

		// Copy a random color channel from the original image into
		// the working canvas, offsetting it by a random amount
		//
		// ImageData arrays are a single dimension array that contains
		// 4 values for each pixel.
		// so, by initializing `i` to a random number between 0 and 2,
		// and incrementing by 4 on each iteration, we can replace only
		// a single channel in the image
		for(i = getRandInt(0, 3), _len = srcData.length; i < _len; i += 4) {
			data[i+channelOffset] = srcData[i];
		}

		// Make the image brighter by doubling the rgb values
		for(i = 0; i < _len; i++) {
			data[i++] *= 2;
			data[i++] *= 2;
			data[i++] *= 2;
		}

		// TODO: The above loops are the most costly in this function, iterating
		// over all the pixels in the image twice.
		// It maybe possible to optimize this by combining both loops into one,
		// and only processing every other line, as alternate lines are replaced
		// with black in the 'scan lines' block belop

		// copy the tweaked ImageData back into the context
		tempCtx.putImageData(targetImageData, 0, 0);

		// add scan lines
		tempCtx.fillStyle = "rgb(0,0,0)";
		for (i = 0; i < h; i += 2) {
			tempCtx.fillRect (0, i, w, 1);
		}

		return tempCanvas;
	};

	/**
	 * Creates a canvas containing a glitched version of the element
	 * @param  {jQuery}   el       The element to glitch
	 * @param  {Object}   options  An object containing the complete callback,
	 *                             the amount to glitch the image, and any
	 *                             html2canvas options
	 */
	var glitch = function(el, options) {
		options = $.extend({
			// the amount to glitch the image
			amount: 6,
			// a callback that takes the glitched canvas as its only argument
			complete: $.noop
		}, options || {});

		// callback for when the element has been rendered
		options.onrendered = function(canvas) {
			options.complete(_glitch(canvas, options.amount));
		};

		// render the element onto a canvas
		html2canvas(el, options);
	};

	/**
	 * Replace el with a glitched version of it
	 * @param  {jQuery} el      The element to glitch
	 * @param  {Object} options An object containing the options for glitch
	 */
	glitch.replace = function(el, options) {
		// store a reference to the complete callback so we can use the same
		// options for the glitch function call
		var _complete = options.complete;
		options.complete = function(canvas) {
			el.after(canvas).detach();
			_complete();
		};

		glitch(el, options);
	};

	/**
	 * Replace `el` with `newEl` by overlaying a glitched version of `el`, then
	 * animating it out to reveal `newEl`
	 *
	 * @param  {jQuery} el     The original element that will be glitched
	 * @param  {jQuery} newEl  The element to show
	 * @param  {Object} options An object containing the options for the animation
	 *                          and any options for html2canvas
	 */
	glitch.transition = function(el, newEl, options) {
		// set the default options
		options = $.extend({
			// the amount to glitch the image
			amount: 6,
			// A callback when the animation is complete
			complete: $.noop,
			// The delay after rendering the glitched element until starting the transition
			delay: 300,
			// The duration of the transition effect
			duration: 500,
			// The z-index to apply to the overlay. You might need to tweak this if
			// you have things that appear above the element, or are using high
			// z-indexes in your page
			zIndex: 1000,
			// the transition effect to use. This may be "fade" or "slide"
			effect: "fade",
			// The size of the top border. Set to 0 to disable, only used in slide mode
			borderSize: 2,
			// The color of the top border, only used in slide mode
			borderColor: "green"
		}, options || {});

		// store a reference to the complete callback so we can use the same
		// options for the glitch function call
		var _complete = options.complete;

		// create a callback that will
		options.complete = function(canvas){
			// position the canvas absolutely within the container
			var $canvas = $(canvas).css("position", "absolute"),
				offset = el.offset(),
				// create a container element that contains the canvas and position it
				// over the element we're replacing
				container = $("<div>").css({
						"border-top": options.borderSize ? options.borderSize + "px solid " + options.borderColor : "none",
						position: "absolute",
						left: offset.left,
						top: offset.top - options.borderSize,
						width: canvas.width,
						height: canvas.height,
						overflow: "hidden",
						"z-index": options.zIndex
					})
					// add the canvas as a child to the container
					.html(canvas)
					// add the container to the dom
					.appendTo("body")
					// delay the animation a bit
					.delay(options.delay),

				// the default transition effect is to fade out
				animation = {
					opacity: 0
				},
				animateOptions = {
						duration: options.duration,
						complete: function(){
							// when the animation is done:
							// remove the container from the dom
							container.remove();

							// call the complete callback
							_complete();

							// and clear all references
							options = $canvas = container = null;
						}
					};

			if(options.effect === "slide") {
				// for the slide effect, we move move the container down
				animation = {
					top: offset.top + canvas.height,
					height: 0
				};

				// and on each step of the animation, we need to offset the top so it
				// remains in the same place on the screen
				animateOptions.step = function(now, fx){
					if(fx.prop === "top") {
						$canvas.css("top", fx.start - now);
					}
				};
			}

			// apply the animation
			container.animate(animation, animateOptions);

			// replace the original element with the new one
			// we use detatch so that the event handlers on the old
			// element are retained.
			newEl.show().insertAfter(el);
			el.detach();

		};

		// create a glitched version of the start element
		glitch(el, options);
	};

	window.glitch = glitch;
})();
<|endoftext|>


<commit_before>/*
 *  asf-message -- Message component
 *  Copyright (C) 2013 Stephane Carrez
 *  Written by Stephane Carrez (Stephane.Carrez@gmail.com)
 *
 *  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.
 */
(function($, undefined) {

    /**
     * The message component is a small popup element intended to display some message.
     */
    $.widget("ui.message", {
        options: {
            attachment: null,
            css: null
        },

        _create: function() {
            var self = this;

            this.element.bind('click', function(event) {
                return self.click(event);
            });

            this.element.addClass("asf-message");

            if (this.options.attachment) {
                this.attachment(this.options.attachment);
            }
        },

	    _setOption: function(key, value){
		    switch (key) {
			case "css":
			    this.options.css = value;
                this.element.addClass("asf-message " + value);
			    break;

            case "triggerHandler":
                self.options.triggerHandler = value;
                break;
			}
			$.Widget.prototype._setOption.apply(this, arguments);
	    },

        /**
         * Set the attachment node.
         *
         * The message popup is moved near the attachment node.
         * Display the message if it is hidden.
         */
        attachment: function(node) {
            this.options.attachment = $(node);

            this.element.show('fast');
            if (this.options.attachment) {
                var offset = $(this.options.attachment).offset();
                this.element.offset(offset);
            }
        },

        /**
         * Click action called when the user clicks on the message.
         * Hide the message.
         *
         * @param event the click event
         */
        click: function(event) {
            this.element.hide('fast');
        },

        /**
         * Schedule a close and removal of the message after a delay.
         */
        autoClose: function() {
            var self = this;

            this.element.delay(1000).fadeOut('slow', function() {
                self.element.remove();
            });
        }
    });

})( jQuery );<commit_msg>Add some documentation<commit_after>/*
 *  asf-message -- Message component
 *  Copyright (C) 2013 Stephane Carrez
 *  Written by Stephane Carrez (Stephane.Carrez@gmail.com)
 *
 *  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.
 */
/**
 * @namespace ui.message
     * @class ui.message
 */
(function($, undefined) {

    /**
     * @class ui.message
     * @extends ui.widget
     *
     * The message component is a small popup element intended to display some message.
     * The component onto which it is applied is turned into a popup element.  The popup
     * is closed automatically after a delay or if the user clicks on the popup element.
     */
    var proto = {
        options: {
	    /**
	     * @cfg {Object} attachment The attachment object.
	     */
            attachment: null,

	    /**
	     * @cfg {String} css The CSS to apply on the popup div.
	     */
            css: null
        },

        _create: function() {
            var self = this;

            this.element.bind('click', function(event) {
                return self.click(event);
            });

            this.element.addClass("asf-message");

            if (this.options.attachment) {
                this.attachment(this.options.attachment);
            }
        },

	    _setOption: function(key, value){
		    switch (key) {
			case "css":
			    this.options.css = value;
                this.element.addClass("asf-message " + value);
			    break;

            case "triggerHandler":
                self.options.triggerHandler = value;
                break;
			}
			$.Widget.prototype._setOption.apply(this, arguments);
	    },

        /**
         * Set the attachment node.
         *
         * The message popup is moved near the attachment node.
         * Display the message if it is hidden.
         */
        attachment: function(node) {
            this.options.attachment = $(node);

            this.element.show('fast');
            if (this.options.attachment) {
                var offset = $(this.options.attachment).offset();
                this.element.offset(offset);
            }
        },

        /**
         * Click action called when the user clicks on the message.
         * Hide the message.
         *
         * @param event the click event
         */
        click: function(event) {
            this.element.hide('fast');
        },

        /**
         * Schedule a close and removal of the message after a delay.
         */
        autoClose: function() {
            var self = this;

            this.element.delay(1000).fadeOut('slow', function() {
                self.element.remove();
            });
        }
    };

    $.widget("ui.message", proto);

})( jQuery );
<|endoftext|>


<commit_before>var extensionId = 'meajklokhjoflbimamdbhpdjlondmgpi',
    postal = new Postal(),
    firmata = {
        Board: function(port, callback) {
            chrome.runtime.sendMessage(extensionId, { command: 'connectBoard', args: [ port ] }, callback)
        }
    },
    require = function () {};

// Messaging between web client and plugin

function Postal() {};

// Command sender function factory
Postal.prototype.commandSender = function () {
    var myself = this,
        command = arguments[0],
        args = Array.from(arguments).slice(1),
        callback = typeof args[args.length - 1] === 'function' ? args.splice(args.length - 1) : null;

    return function () { myself.sendCommand(command, args, callback); };
};

Postal.prototype.sendCommand = function (command, args, callback) {
    chrome.runtime.sendMessage(extensionId, { command: command, args: args }, callback);
};

chrome.serial = {
    getDevices: function (callback) { postal.sendCommand('getDevices', null, callback) }
};
<commit_msg>Fixing chromium version to work with current and old connectors (extensions)<commit_after>var currentExtensionId = 'fhmkennllhhclkcjcdaegceilcefedle',
    oldExtensionId = 'meajklokhjoflbimamdbhpdjlondmgpi',
    extensionId = currentExtensionId,
    postal = new Postal(),
    firmata = {
        Board: function(port, callback) {
            chrome.runtime.sendMessage(extensionId, { command: 'connectBoard', args: [ port ] }, callback)
        }
    },
    require = function () {};

// Messaging between web client and plugin

function Postal() {};

// Command sender function factory
Postal.prototype.commandSender = function () {
    var myself = this,
        command = arguments[0],
        args = Array.from(arguments).slice(1),
        callback = typeof args[args.length - 1] === 'function' ? args.splice(args.length - 1) : null;

    return function () { myself.sendCommand(command, args, callback); };
};

Postal.prototype.sendCommand = function (command, args, callback) {
    chrome.runtime.sendMessage(extensionId, { command: command, args: args }, callback);
};

chrome.serial = {
    getDevices: function (callback) { postal.sendCommand('getDevices', null, callback) }
};

// Checking if Snap4Arduino Connector (current or old, with different 'extensionId') are present
chrome.runtime.sendMessage(extensionId, {command:'getDevices'}, function (devices) {
    if (chrome.runtime.lastError) {
        console.log("Current connector not found");
        extensionId = oldExtensionId;
        chrome.runtime.sendMessage(extensionId, {command:'getDevices'}, function (devices) {
            if (chrome.runtime.lastError) {
                console.log("Old connector has not been found either");
                extensionId = currentExtensionId;
            } else {
                console.log("Old connector found successfully");
            }
        });
    } else {
        console.log("Current connector found successfully");
    }
});
<|endoftext|>


<commit_before>import { extend, cached, noop } from 'shared/util'
import { activeInstance } from 'core/instance/lifecycle'

export default {
  create: enter,
  activate: enter,
  remove: leave
}

function enter (_, vnode) {
  const el = vnode.elm

  // call leave callback now
  if (el._leaveCb) {
    el._leaveCb.cancelled = true
    el._leaveCb()
  }

  const data = resolveTransition(vnode.data.transition)
  if (!data) {
    return
  }

  /* istanbul ignore if */
  if (el._enterCb) {
    return
  }

  const {
    enterClass,
    enterActiveClass,
    appearClass,
    appearActiveClass,
    beforeEnter,
    enter,
    afterEnter,
    enterCancelled,
    beforeAppear,
    appear,
    afterAppear,
    appearCancelled
  } = data

  let context = activeInstance
  let transitionNode = activeInstance.$vnode
  while (transitionNode && transitionNode.parent) {
    transitionNode = transitionNode.parent
    context = transitionNode.context
  }

  const isAppear = !context._isMounted || !vnode.isRootInsert

  if (isAppear && !appear && appear !== '') {
    return
  }

  const startClass = isAppear ? appearClass : enterClass
  const activeClass = isAppear ? appearActiveClass : enterActiveClass
  const beforeEnterHook = isAppear ? (beforeAppear || beforeEnter) : beforeEnter
  const enterHook = isAppear ? (typeof appear === 'function' ? appear : enter) : enter
  const afterEnterHook = isAppear ? (afterAppear || afterEnter) : afterEnter
  const enterCancelledHook = isAppear ? (appearCancelled || enterCancelled) : enterCancelled

  const userWantsControl =
    enterHook &&
    // enterHook may be a bound method which exposes
    // the length of original fn as _length
    (enterHook._length || enterHook.length) > 1

  const stylesheet = vnode.context.$options.style || {}
  const startState = stylesheet[startClass]
  const endState = stylesheet[activeClass]
  const expectsCSS = startState && endState

  const cb = el._enterCb = once(() => {
    if (cb.cancelled) {
      enterCancelledHook && enterCancelledHook(el)
    } else {
      afterEnterHook && afterEnterHook(el)
    }
    el._enterCb = null
  })

  // remove pending leave element on enter by injecting an insert hook
  // mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'insert', () => {

  // }, 'transition-insert')

  setTimeout(() => {
    const parent = el.parentNode
    const pendingNode = parent && parent._pending && parent._pending[vnode.key]
    if (pendingNode &&
        pendingNode.context === vnode.context &&
        pendingNode.tag === vnode.tag &&
        pendingNode.elm._leaveCb) {
      pendingNode.elm._leaveCb()
    }
    enterHook && enterHook(el, cb)

    if (endState) {
      const animation = vnode.context.$options.animation
      animation.transition(el.ref, {
        styles: endState,
        duration: 300,
        timingFunction: 'ease-in-out'
      }, userWantsControl ? noop : cb)
    } else if (!userWantsControl) {
      cb()
    }
    // if (expectsCSS) {
    //   animation.transition(el.ref, {
    //     styles: startState
    //   }, () => {
    //     animation.transition(el.ref, {
    //       styles: endState,
    //       duration: 300,
    //       timingFunction: 'ease-in-out'
    //     }, userWantsControl ? noop : cb)
    //   })
    // }
  }, 16)

  // start enter transition
  beforeEnterHook && beforeEnterHook(el)

  if (startState) {
    for (const key in startState) {
      el.setStyle(key, startState[key])
    }
  }

  if (!expectsCSS && !userWantsControl) {
    cb()
  }
}

function leave (vnode, rm) {
  const el = vnode.elm

  // call enter callback now
  if (el._enterCb) {
    el._enterCb.cancelled = true
    el._enterCb()
  }

  const data = resolveTransition(vnode.data.transition)
  if (!data) {
    return rm()
  }

  if (el._leaveCb) {
    return
  }

  const {
    leaveClass,
    leaveActiveClass,
    beforeLeave,
    leave,
    afterLeave,
    leaveCancelled,
    delayLeave
  } = data

  const userWantsControl =
    leave &&
    // leave hook may be a bound method which exposes
    // the length of original fn as _length
    (leave._length || leave.length) > 1

  const stylesheet = vnode.context.$options.style || {}
  const startState = stylesheet[leaveClass]
  const endState = stylesheet[leaveActiveClass]
  const expectsCSS = startState && endState

  const cb = el._leaveCb = once(() => {
    if (el.parentNode && el.parentNode._pending) {
      el.parentNode._pending[vnode.key] = null
    }
    if (cb.cancelled) {
      leaveCancelled && leaveCancelled(el)
    } else {
      rm()
      afterLeave && afterLeave(el)
    }
    el._leaveCb = null
  })

  if (delayLeave) {
    delayLeave(performLeave)
  } else {
    performLeave()
  }

  function performLeave () {
    const animation = vnode.context.$options.animation
    // the delayed leave may have already been cancelled
    if (cb.cancelled) {
      return
    }
    // record leaving element
    if (!vnode.data.show) {
      (el.parentNode._pending || (el.parentNode._pending = {}))[vnode.key] = vnode
    }
    beforeLeave && beforeLeave(el)

    if (startState) {
      animation.transition(el.ref, {
        styles: startState
      }, next)
    } else {
      next()
    }

    function next () {
      animation.transition(el.ref, {
        styles: endState,
        duration: 300,
        timingFunction: 'ease-in-out'
      }, userWantsControl ? noop : cb)
    }

    leave && leave(el, cb)
    if (!expectsCSS && !userWantsControl) {
      cb()
    }
  }
}

function resolveTransition (def) {
  if (!def) {
    return
  }
  /* istanbul ignore else */
  if (typeof def === 'object') {
    const res = {}
    if (def.css !== false) {
      extend(res, autoCssTransition(def.name || 'v'))
    }
    extend(res, def)
    return res
  } else if (typeof def === 'string') {
    return autoCssTransition(def)
  }
}

function once (fn) {
  let called = false
  return () => {
    if (!called) {
      called = true
      fn()
    }
  }
}

const autoCssTransition = cached(name => {
  return {
    enterClass: `${name}-enter`,
    leaveClass: `${name}-leave`,
    appearClass: `${name}-enter`,
    enterActiveClass: `${name}-enter-active`,
    leaveActiveClass: `${name}-leave-active`,
    appearActiveClass: `${name}-enter-active`
  }
})
<commit_msg>[weex] expose native module getter on sub Vue instances<commit_after>import { extend, cached, noop } from 'shared/util'
import { activeInstance } from 'core/instance/lifecycle'

export default {
  create: enter,
  activate: enter,
  remove: leave
}

function enter (_, vnode) {
  const el = vnode.elm

  // call leave callback now
  if (el._leaveCb) {
    el._leaveCb.cancelled = true
    el._leaveCb()
  }

  const data = resolveTransition(vnode.data.transition)
  if (!data) {
    return
  }

  /* istanbul ignore if */
  if (el._enterCb) {
    return
  }

  const {
    enterClass,
    enterActiveClass,
    appearClass,
    appearActiveClass,
    beforeEnter,
    enter,
    afterEnter,
    enterCancelled,
    beforeAppear,
    appear,
    afterAppear,
    appearCancelled
  } = data

  let context = activeInstance
  let transitionNode = activeInstance.$vnode
  while (transitionNode && transitionNode.parent) {
    transitionNode = transitionNode.parent
    context = transitionNode.context
  }

  const isAppear = !context._isMounted || !vnode.isRootInsert

  if (isAppear && !appear && appear !== '') {
    return
  }

  const startClass = isAppear ? appearClass : enterClass
  const activeClass = isAppear ? appearActiveClass : enterActiveClass
  const beforeEnterHook = isAppear ? (beforeAppear || beforeEnter) : beforeEnter
  const enterHook = isAppear ? (typeof appear === 'function' ? appear : enter) : enter
  const afterEnterHook = isAppear ? (afterAppear || afterEnter) : afterEnter
  const enterCancelledHook = isAppear ? (appearCancelled || enterCancelled) : enterCancelled

  const userWantsControl =
    enterHook &&
    // enterHook may be a bound method which exposes
    // the length of original fn as _length
    (enterHook._length || enterHook.length) > 1

  const stylesheet = vnode.context.$options.style || {}
  const startState = stylesheet[startClass]
  const endState = stylesheet[activeClass]
  const expectsCSS = startState && endState

  const cb = el._enterCb = once(() => {
    if (cb.cancelled) {
      enterCancelledHook && enterCancelledHook(el)
    } else {
      afterEnterHook && afterEnterHook(el)
    }
    el._enterCb = null
  })

  // We need to wait until the native element has been inserted, but currently
  // there's no API to do that. So we have to wait "one frame" - not entirely
  // sure if this is guaranteed to be enough (e.g. on slow devices?)
  setTimeout(() => {
    const parent = el.parentNode
    const pendingNode = parent && parent._pending && parent._pending[vnode.key]
    if (pendingNode &&
        pendingNode.context === vnode.context &&
        pendingNode.tag === vnode.tag &&
        pendingNode.elm._leaveCb) {
      pendingNode.elm._leaveCb()
    }
    enterHook && enterHook(el, cb)

    if (endState) {
      const animation = vnode.context._requireWeexModule('animation')
      animation.transition(el.ref, {
        styles: endState,
        duration: 300,
        timingFunction: 'ease-in-out'
      }, userWantsControl ? noop : cb)
    } else if (!userWantsControl) {
      cb()
    }
  }, 16)

  // start enter transition
  beforeEnterHook && beforeEnterHook(el)

  if (startState) {
    for (const key in startState) {
      el.setStyle(key, startState[key])
    }
  }

  if (!expectsCSS && !userWantsControl) {
    cb()
  }
}

function leave (vnode, rm) {
  const el = vnode.elm

  // call enter callback now
  if (el._enterCb) {
    el._enterCb.cancelled = true
    el._enterCb()
  }

  const data = resolveTransition(vnode.data.transition)
  if (!data) {
    return rm()
  }

  if (el._leaveCb) {
    return
  }

  const {
    leaveClass,
    leaveActiveClass,
    beforeLeave,
    leave,
    afterLeave,
    leaveCancelled,
    delayLeave
  } = data

  const userWantsControl =
    leave &&
    // leave hook may be a bound method which exposes
    // the length of original fn as _length
    (leave._length || leave.length) > 1

  const stylesheet = vnode.context.$options.style || {}
  const startState = stylesheet[leaveClass]
  const endState = stylesheet[leaveActiveClass]
  const expectsCSS = startState && endState

  const cb = el._leaveCb = once(() => {
    if (el.parentNode && el.parentNode._pending) {
      el.parentNode._pending[vnode.key] = null
    }
    if (cb.cancelled) {
      leaveCancelled && leaveCancelled(el)
    } else {
      rm()
      afterLeave && afterLeave(el)
    }
    el._leaveCb = null
  })

  if (delayLeave) {
    delayLeave(performLeave)
  } else {
    performLeave()
  }

  function performLeave () {
    const animation = vnode.context._requireWeexModule('animation')
    // the delayed leave may have already been cancelled
    if (cb.cancelled) {
      return
    }
    // record leaving element
    if (!vnode.data.show) {
      (el.parentNode._pending || (el.parentNode._pending = {}))[vnode.key] = vnode
    }
    beforeLeave && beforeLeave(el)

    if (startState) {
      animation.transition(el.ref, {
        styles: startState
      }, next)
    } else {
      next()
    }

    function next () {
      animation.transition(el.ref, {
        styles: endState,
        duration: 300,
        timingFunction: 'ease-in-out'
      }, userWantsControl ? noop : cb)
    }

    leave && leave(el, cb)
    if (!expectsCSS && !userWantsControl) {
      cb()
    }
  }
}

function resolveTransition (def) {
  if (!def) {
    return
  }
  /* istanbul ignore else */
  if (typeof def === 'object') {
    const res = {}
    if (def.css !== false) {
      extend(res, autoCssTransition(def.name || 'v'))
    }
    extend(res, def)
    return res
  } else if (typeof def === 'string') {
    return autoCssTransition(def)
  }
}

function once (fn) {
  let called = false
  return () => {
    if (!called) {
      called = true
      fn()
    }
  }
}

const autoCssTransition = cached(name => {
  return {
    enterClass: `${name}-enter`,
    leaveClass: `${name}-leave`,
    appearClass: `${name}-enter`,
    enterActiveClass: `${name}-enter-active`,
    leaveActiveClass: `${name}-leave-active`,
    appearActiveClass: `${name}-enter-active`
  }
})
<|endoftext|>


<commit_before>/*jshint multistr: true*/
(function (window, document, undefined) {
  'use strict';
  var DiveProfile;
  DiveProfile = function (cockpit) {
    console.log('Adding Compass to Artificial Horizon.');
    // Instance variables
    this.cockpit = cockpit;
    // Add required UI elements
    var calibrations = cockpit.extensionPoints.rovDiagnostics.find('#calibrations');
    calibrations.append('Switch Water Type');
    // Listen to navdata updates
    var self = this;
    cockpit.extensionPoints.rovDiagnostics.find('#toggle_watertype').click(function () {
      self.cockpit.emit('plugin.diveprofile.watertype.toggle');
    });

    cockpit.messaging.register({
      toSocket: ['plugin.diveprofile.watertype.toggle']
    });
  };
  window.Cockpit.plugins.push(DiveProfile);
}(window, document, undefined));
<commit_msg>Fix buttons<commit_after>/*jshint multistr: true*/
(function (window, document, undefined) {
  'use strict';
  var DiveProfile;
  DiveProfile = function (cockpit) {
    console.log('Adding Compass to Artificial Horizon.');
    // Instance variables
    this.cockpit = cockpit;
    // Add required UI elements
    var calibrations = cockpit.extensionPoints.rovDiagnostics.find('#calibrations');
    calibrations.append('Switch Water Type');
    // Listen to navdata updates
    var self = this;
    cockpit.extensionPoints.rovDiagnostics.find('#toggle_watertype').click(function () {
      self.cockpit.emit('plugin.diveprofile.watertype.toggle');
    });

    cockpit.messaging.register({
      toSocket: ['plugin.diveprofile.watertype.toggle']
    });
  };
  window.Cockpit.plugins.push(DiveProfile);
}(window, document, undefined));
<|endoftext|>


<commit_before>/**
 * @jsx React.DOM
 */
'use strict';

var React                     = require('react');
var utils                     = require('./utils');
var schema                    = require('./schema');
var ValidatedMixin            = require('./ValidatedMixin');
var getDefaultValueForSchema  = require('./getDefaultValueForSchema');
var validationM               = require('./validation');

var success = validationM.success;
var serialize = validationM.serialize;

/**
 * Mixin for the form element (form field, fieldset of repeating fieldset).
 */
var FormElementMixin = {

  mixins: [ValidatedMixin],

  propTypes: {
    name: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.number
    ])
  },

  contextTypes: {
    serializedValueLens: React.PropTypes.object,
    valueLens: React.PropTypes.object,
    validationLens: React.PropTypes.object,
    schema: React.PropTypes.object,
    onValueUpdate: React.PropTypes.func
  },

  /**
   * Return lens for the form element value or for the value passed as an
   * argument.
   *
   * @param {Any?} value
   * @returns {Lens}
   */
  serializedValueLens: function(value) {
    var lens = this.context.serializedValueLens;
    if (this.props.name !== undefined) {
      lens = lens.get(
        this.props.name,
        serialize(this.schema(), this.valueLens(value).val())
      );
    }
    return value ? lens.for(value) : lens;
  },

  valueLens: function(value) {
    var lens = this.context.valueLens;
    if (this.props.name !== undefined) {
      lens = lens.get(this.props.name, getDefaultValueForSchema(this.schema()));
    }
    return value ? lens.for(value) : lens;
  },

  /**
   * Return lens for the form element validation state or for the validation
   * state passed as an argument.
   *
   * @param {Validation?} validation
   * @returns {Lens}
   */
  validationLens: function(validation) {
    var lens = this.context.validationLens;
    if (this.props.name !== undefined) {
      lens = lens.get('children', {}).get(this.props.name, success);
    }
    return validation ? lens.for(validation) : lens;
  },

  /**
   * Return form element schema.
   *
   * @returns {Schema}
   */
  schema: function() {
    var node = this.context.schema;

    if (node && this.props.name !== undefined) {
      if (schema.isSchema(node)) {
        node = node.children[this.props.name];
      } else if (schema.isList(node)) {
        node = node.children;
      } else {
        utils.invariant(false, 'invalid field used for schema');
      }
    }

    return node;
  },

  /**
   * Called when the form value and validation state is being updated.
   *
   * This method intercepts updated value and validation state and perform its
   * own local validation and deserialization. Then passes everything up the
   * owner.
   *
   * @param {Any} value
   * @param {Validation} validation
   */
  onValueUpdate: function(value, validation, serializedValue) {
    var validationLens = this.validationLens(validation);
    var valueLens = this.valueLens(value);

    var local = this.validateOnly(
      valueLens.val(),
      validationLens.val().children
    );

    valueLens = valueLens.mod(local.value);
    validationLens = validationLens.update(local.validation);

    this.context.onValueUpdate(
      valueLens.root(),
      validationLens.root(),
      serializedValue
    );
  },

  /**
   * Update the serialized value for the current form element.
   *
   * @param {Any} serializedValue
   */
  updateValue: function(serializedValue) {
    this.onValueUpdate(
      this.valueLens().mod(serializedValue).root(),
      this.validationLens().root(),
      this.serializedValueLens().mod(serializedValue).root()
    );
  }

};

module.exports = FormElementMixin;
<commit_msg>Update value only in case it is valid<commit_after>/**
 * @jsx React.DOM
 */
'use strict';

var React                     = require('react');
var utils                     = require('./utils');
var schema                    = require('./schema');
var ValidatedMixin            = require('./ValidatedMixin');
var getDefaultValueForSchema  = require('./getDefaultValueForSchema');
var validationM               = require('./validation');

var success = validationM.success;
var serialize = validationM.serialize;
var isFailure = validationM.isFailure;

/**
 * Mixin for the form element (form field, fieldset of repeating fieldset).
 */
var FormElementMixin = {

  mixins: [ValidatedMixin],

  propTypes: {
    name: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.number
    ])
  },

  contextTypes: {
    serializedValueLens: React.PropTypes.object,
    valueLens: React.PropTypes.object,
    validationLens: React.PropTypes.object,
    schema: React.PropTypes.object,
    onValueUpdate: React.PropTypes.func
  },

  /**
   * Return lens for the form element value or for the value passed as an
   * argument.
   *
   * @param {Any?} value
   * @returns {Lens}
   */
  serializedValueLens: function(value) {
    var lens = this.context.serializedValueLens;
    if (this.props.name !== undefined) {
      lens = lens.get(
        this.props.name,
        serialize(this.schema(), this.valueLens().val())
      );
    }
    return value ? lens.for(value) : lens;
  },

  valueLens: function(value) {
    var lens = this.context.valueLens;
    if (this.props.name !== undefined) {
      lens = lens.get(this.props.name, getDefaultValueForSchema(this.schema()));
    }
    return value ? lens.for(value) : lens;
  },

  /**
   * Return lens for the form element validation state or for the validation
   * state passed as an argument.
   *
   * @param {Validation?} validation
   * @returns {Lens}
   */
  validationLens: function(validation) {
    var lens = this.context.validationLens;
    if (this.props.name !== undefined) {
      lens = lens.get('children', {}).get(this.props.name, success);
    }
    return validation ? lens.for(validation) : lens;
  },

  /**
   * Return form element schema.
   *
   * @returns {Schema}
   */
  schema: function() {
    var node = this.context.schema;

    if (node && this.props.name !== undefined) {
      if (schema.isSchema(node)) {
        node = node.children[this.props.name];
      } else if (schema.isList(node)) {
        node = node.children;
      } else {
        utils.invariant(false, 'invalid field used for schema');
      }
    }

    return node;
  },

  /**
   * Called when the form value and validation state is being updated.
   *
   * This method intercepts updated value and validation state and perform its
   * own local validation and deserialization. Then passes everything up the
   * owner.
   *
   * @param {Any} value
   * @param {Validation} validation
   */
  onValueUpdate: function(value, validation, serializedValue) {
    var validationLens = this.validationLens(validation);
    var valueLens = this.valueLens(value);

    var local = this.validateOnly(
      valueLens.val(),
      validationLens.val().children
    );

    validationLens = validationLens.update(local.validation);

    if (isFailure(validationLens.val())) {
      // revert to the previous value
      valueLens = this.valueLens();
    } else {
      valueLens = valueLens.mod(local.value);
    }

    this.context.onValueUpdate(
      valueLens.root(),
      validationLens.root(),
      serializedValue
    );
  },

  /**
   * Update the serialized value for the current form element.
   *
   * @param {Any} serializedValue
   */
  updateValue: function(serializedValue) {
    this.onValueUpdate(
      this.valueLens().mod(serializedValue).root(),
      this.validationLens().root(),
      this.serializedValueLens().mod(serializedValue).root()
    );
  }

};

module.exports = FormElementMixin;
<|endoftext|>