<commit_before>#!/usr/bin/env node

var fs = require("fs");
var spawn = require('child_process').spawn;
var packageJSON = require("./package.json");
var readline = require('readline');

function execute(){
  var commands = Array.prototype.slice.call(arguments);
  var cb = commands.pop();

  if (commands.length === 0) {
    return cb();
  }

  var args = commands.shift();
  var command = args.shift();
  var cmd = spawn(command, args);

  cmd.stdout.pipe(process.stdout);
  cmd.stderr.pipe(process.stderr);
  cmd.on("exit", function(code){
    if (code !== 0) {
      return cb(new Error("Failed to execute " + command + " " + args.join(" ")));
    }
    commands.push(cb);
    execute.apply(null, commands);
  });
}

var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

console.log("Current version is", packageJSON.version);
rl.question("New version> ", function(version) {
  packageJSON.version = version;
  fs.writeFileSync("./package.json", JSON.stringify(packageJSON, null, "  "));

  execute(
    [ "npm", "publish" ],
    [ "git", "commit", "package.json", "-m", "New release " + version ],
    [ "git", "tag", "-a", "v" + version, "-m", "Release " + version ],
    [ "git", "push", "--tags", "origin"],
    [ "git", "push", "origin", "master"],
    function(err){
      if (err) throw err;
      rl.close();
      console.log("DONE");
    }
  );

});
<commit_msg>Clean up<commit_after>#!/usr/bin/env node

var fs = require("fs");
var spawn = require('child_process').spawn;
var readline = require('readline');

function execute(){
  var commands = Array.prototype.slice.call(arguments);
  var cb = commands.pop();

  if (commands.length === 0) {
    return cb();
  }

  var args = commands.shift();
  var command = args.shift();
  var cmd = spawn(command, args);

  cmd.stdout.pipe(process.stdout);
  cmd.stderr.pipe(process.stderr);
  cmd.on("exit", function(code){
    if (code !== 0) {
      return cb(new Error("Failed to execute " + command + " " + args.join(" ")));
    }
    commands.push(cb);
    execute.apply(null, commands);
  });
}

var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

var packageJSON = require("./package.json");
console.log("Current version is", packageJSON.version);

rl.question("New version> ", function(version) {
  packageJSON.version = version;
  fs.writeFileSync("./package.json", JSON.stringify(packageJSON, null, "  "));

  execute(
    [ "npm", "publish" ],
    [ "git", "commit", "package.json", "-m", "New release " + version ],
    [ "git", "tag", "-a", "v" + version, "-m", "Release " + version ],
    [ "git", "push", "--tags", "origin"],
    [ "git", "push", "origin", "master"],
    function(err){
      if (err) throw err;
      rl.close();
      console.log("DONE");
    }
  );

});
<|endoftext|>


<commit_before>/* globals WallabagApi */
if (typeof (browser) === 'undefined' && typeof (chrome) === 'object') {
    browser = chrome;
}

const icon = {
    'good': 'img/wallabagger-green.svg',
    'wip': 'img/wallabagger-yellow.svg',
    'bad': 'img/wallabagger-red.svg'

};

const GetApi = () => {
    const api = new WallabagApi();
    return api.load()
        .then(data => {
            if (api.needNewAppToken()) {
                return api.GetAppToken()
                    .then(r => api);
            }
            return api;
        })
        .catch(error => {
            throw error;
        });
};

let browserActionIconDefault = browser.runtime.getManifest().browser_action.default_icon;

browser.contextMenus.create({
    id: 'wallabagger-add-link',
    title: 'Wallabag it!',
    contexts: ['link', 'page']
});

browser.contextMenus.create({
    type: 'separator',
    contexts: ['browser_action']
});

browser.contextMenus.create({
    id: 'Unread-articles',
    title: 'Unread articles',
    contexts: ['browser_action']
});

browser.contextMenus.create({
    id: 'Favorite-articles',
    title: 'Starred articles',
    contexts: ['browser_action']
});

browser.contextMenus.create({
    id: 'Archived-articles',
    title: 'Archived articles',
    contexts: ['browser_action']
});

browser.contextMenus.create({
    id: 'All-articles',
    title: 'All articles',
    contexts: ['browser_action']
});

browser.contextMenus.create({
    id: 'Tag-list',
    title: 'Tag list',
    contexts: ['browser_action']
});

function savePageToWallabag (url) {
    browser.browserAction.setIcon({ path: icon.wip });

    GetApi().then(api => api.SavePage(url))
        .then(r => {
            browser.browserAction.setIcon({ path: icon.good });
            setTimeout(function () { browser.browserAction.setIcon({ path: browserActionIconDefault }); }, 5000);
        })
        .catch(e => {
            browser.browserAction.setIcon({ path: icon.bad });
            setTimeout(function () { browser.browserAction.setIcon({ path: browserActionIconDefault }); }, 5000);
        });
};

browser.contextMenus.onClicked.addListener(function (info) {
    switch (info.menuItemId) {
        case 'wallabagger-add-link':
            const url = typeof (info.linkUrl) === 'string' ? info.linkUrl : info.pageUrl;
            savePageToWallabag(url);
            break;
        case 'Unread-articles':
            GotoWallabag('unread');
            break;
        case 'Favorite-articles':
            GotoWallabag('starred');
            break;
        case 'Archived-articles':
            GotoWallabag('archive');
            break;
        case 'All-articles':
            GotoWallabag('all');
            break;
        case 'Tag-list':
            GotoWallabag('tag');
            break;
    }
});

const GotoWallabag = (part) =>
    GetApi().then(api => chrome.tabs.create({ url: `${api.data.Url}/${part}/list` }));

browser.commands.onCommand.addListener(function (command) {
    if (command === 'wallabag-it') {
        browser.tabs.query({ 'active': true }, function (tabs) {
            if (tabs[0] != null) {
                savePageToWallabag(tabs[0].url);
            }
        });
    }
});

GetApi().then(api => {
    if (api.data.AllowExistCheck) {
        browser.tabs.onActivated.addListener(function (activeInfo) {
            browser.browserAction.setIcon({ path: browserActionIconDefault });
            const { tabId } = activeInfo;
            browser.tabs.get(tabId, function (tab) {
                checkExist(tab.url);
            });
        });

        browser.tabs.onCreated.addListener(function (tab) {
            browser.browserAction.setIcon({ path: browserActionIconDefault });
        });

        browser.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
            if (changeInfo.status === 'loading' && tab.active) {
                requestExists(tab.url);
            }
        });

        chrome.runtime.onMessage.addListener(
        function (request, sender, sendResponse) {
            const {type, url} = request;
            switch (type) {
                case 'begin' :
                    browser.browserAction.setIcon({ path: icon.wip });
                    break;
                case 'success' :
                    browser.browserAction.setIcon({ path: icon.good });
                    saveExistFlag(url, true);
                    break;
                case 'error' :
                    browser.browserAction.setIcon({ path: icon.bad });
                    setTimeout(function () { browser.browserAction.setIcon({ path: browserActionIconDefault }); }, 5000);
                    break;
            }
        });
    };
});

const checkExist = (url) => {
    if (isServicePage(url)) { return; }
    existWasChecked(url)
    .then(wasChecked => {
        if (wasChecked) {
            getExistFlag(url)
            .then(exists => {
                if (exists) {
                    browser.browserAction.setIcon({ path: icon.good });
                }
            });
        } else {
            requestExists(url);
        }
    });
};

const requestExists = (url) =>
    GetApi()
    .then(api => api.EntryExists(url))
    .then(data => {
        let icon = browserActionIconDefault;
        if (data.exists) {
            icon = icon.good;
        }
        browser.browserAction.setIcon({ path: icon });
        saveExistFlag(url, data.exists);
    });

const saveExistFlag = (url, exists) => {
    browser.storage.local.set({[btoa(url)]: JSON.stringify(exists)});
};

const getExistFlag = (url) =>
    new Promise((resolve, reject) => {
        browser.storage.local.get(btoa(url), function (item) {
            resolve(JSON.parse(item[btoa(url)]));
        });
    });

const existWasChecked = (url) =>
    new Promise((resolve, reject) => {
        browser.storage.local.get(null, function (items) {
            resolve(btoa(url) in items);
        });
    });

// const slash = (url) => url.match(/(\w+)\.html?$/) ? url : url.replace(/\/?$/, '/');

const isServicePage = (url) => /^(chrome|about|browser):(.*)/.test(url);
<commit_msg>Use of browser instead of chrome<commit_after>/* globals WallabagApi */
if (typeof (browser) === 'undefined' && typeof (chrome) === 'object') {
    browser = chrome;
}

const icon = {
    'good': 'img/wallabagger-green.svg',
    'wip': 'img/wallabagger-yellow.svg',
    'bad': 'img/wallabagger-red.svg'

};

const GetApi = () => {
    const api = new WallabagApi();
    return api.load()
        .then(data => {
            if (api.needNewAppToken()) {
                return api.GetAppToken()
                    .then(r => api);
            }
            return api;
        })
        .catch(error => {
            throw error;
        });
};

let browserActionIconDefault = browser.runtime.getManifest().browser_action.default_icon;

browser.contextMenus.create({
    id: 'wallabagger-add-link',
    title: 'Wallabag it!',
    contexts: ['link', 'page']
});

browser.contextMenus.create({
    type: 'separator',
    contexts: ['browser_action']
});

browser.contextMenus.create({
    id: 'Unread-articles',
    title: 'Unread articles',
    contexts: ['browser_action']
});

browser.contextMenus.create({
    id: 'Favorite-articles',
    title: 'Starred articles',
    contexts: ['browser_action']
});

browser.contextMenus.create({
    id: 'Archived-articles',
    title: 'Archived articles',
    contexts: ['browser_action']
});

browser.contextMenus.create({
    id: 'All-articles',
    title: 'All articles',
    contexts: ['browser_action']
});

browser.contextMenus.create({
    id: 'Tag-list',
    title: 'Tag list',
    contexts: ['browser_action']
});

function savePageToWallabag (url) {
    browser.browserAction.setIcon({ path: icon.wip });

    GetApi().then(api => api.SavePage(url))
        .then(r => {
            browser.browserAction.setIcon({ path: icon.good });
            setTimeout(function () { browser.browserAction.setIcon({ path: browserActionIconDefault }); }, 5000);
        })
        .catch(e => {
            browser.browserAction.setIcon({ path: icon.bad });
            setTimeout(function () { browser.browserAction.setIcon({ path: browserActionIconDefault }); }, 5000);
        });
};

browser.contextMenus.onClicked.addListener(function (info) {
    switch (info.menuItemId) {
        case 'wallabagger-add-link':
            const url = typeof (info.linkUrl) === 'string' ? info.linkUrl : info.pageUrl;
            savePageToWallabag(url);
            break;
        case 'Unread-articles':
            GotoWallabag('unread');
            break;
        case 'Favorite-articles':
            GotoWallabag('starred');
            break;
        case 'Archived-articles':
            GotoWallabag('archive');
            break;
        case 'All-articles':
            GotoWallabag('all');
            break;
        case 'Tag-list':
            GotoWallabag('tag');
            break;
    }
});

const GotoWallabag = (part) =>
    GetApi().then(api => browser.tabs.create({ url: `${api.data.Url}/${part}/list` }));

browser.commands.onCommand.addListener(function (command) {
    if (command === 'wallabag-it') {
        browser.tabs.query({ 'active': true }, function (tabs) {
            if (tabs[0] != null) {
                savePageToWallabag(tabs[0].url);
            }
        });
    }
});

GetApi().then(api => {
    if (api.data.AllowExistCheck) {
        browser.tabs.onActivated.addListener(function (activeInfo) {
            browser.browserAction.setIcon({ path: browserActionIconDefault });
            const { tabId } = activeInfo;
            browser.tabs.get(tabId, function (tab) {
                checkExist(tab.url);
            });
        });

        browser.tabs.onCreated.addListener(function (tab) {
            browser.browserAction.setIcon({ path: browserActionIconDefault });
        });

        browser.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
            if (changeInfo.status === 'loading' && tab.active) {
                requestExists(tab.url);
            }
        });

        browser.runtime.onMessage.addListener(function (request, sender, sendResponse) {
            const {type, url} = request;
            switch (type) {
                case 'begin' :
                    browser.browserAction.setIcon({ path: icon.wip });
                    break;
                case 'success' :
                    browser.browserAction.setIcon({ path: icon.good });
                    saveExistFlag(url, true);
                    break;
                case 'error' :
                    browser.browserAction.setIcon({ path: icon.bad });
                    setTimeout(function () { browser.browserAction.setIcon({ path: browserActionIconDefault }); }, 5000);
                    break;
            }
        });
    };
});

const checkExist = (url) => {
    if (isServicePage(url)) { return; }
    existWasChecked(url)
    .then(wasChecked => {
        if (wasChecked) {
            getExistFlag(url)
            .then(exists => {
                if (exists) {
                    browser.browserAction.setIcon({ path: icon.good });
                }
            });
        } else {
            requestExists(url);
        }
    });
};

const requestExists = (url) =>
    GetApi()
    .then(api => api.EntryExists(url))
    .then(data => {
        let icon = browserActionIconDefault;
        if (data.exists) {
            icon = icon.good;
        }
        browser.browserAction.setIcon({ path: icon });
        saveExistFlag(url, data.exists);
    });

const saveExistFlag = (url, exists) => {
    browser.storage.local.set({[btoa(url)]: JSON.stringify(exists)});
};

const getExistFlag = (url) =>
    new Promise((resolve, reject) => {
        browser.storage.local.get(btoa(url), function (item) {
            resolve(JSON.parse(item[btoa(url)]));
        });
    });

const existWasChecked = (url) =>
    new Promise((resolve, reject) => {
        browser.storage.local.get(null, function (items) {
            resolve(btoa(url) in items);
        });
    });

// const slash = (url) => url.match(/(\w+)\.html?$/) ? url : url.replace(/\/?$/, '/');

const isServicePage = (url) => /^(chrome|about|browser):(.*)/.test(url);
<|endoftext|>


<commit_before>// Global variables
var topLayerIdx = 0;
var layerList = new Array();
var stage = undefined;
var connection = new WebSocket('ws://localhost:8080');
var canvas = undefined;

var generadedShapeIdIdx = 0;
var debugTween;
var debugAnnimatingShapes = [];
var debugCanClose = true;

// Event handlers
function connectionDataReceived(event) {

    // Reset mousedrag
    mouseDragFound = false;

    // Clear screen
    layerList[topLayerIdx].destroyChildren();

    var dataObject = jQuery.parseJSON(event.data);

    var shape = parseShapeData(dataObject);

    // Disable mousedrag if event is no longer attached to the shape
    if(!mouseDragFound && mouseDragId!=undefined) {
        mouseDragEndEventHandler(mouseDragId,undefined);
    }

    // Draw on current layer
    layerList[topLayerIdx].add(shape);
    layerList[topLayerIdx].batchDraw();
}

function connectionError(error) {

    printDebugMessage("WebSocket Error " + error);
}

function parseShapeData(data) {

    var shape = shapeFromData(data);
    enableEventHandlers(shape, data);

    return shape;
}

/*
 * Sending Input events
 */

function enableEventHandlers(shape, message) {
    if(message.eventData != undefined && message.eventData != null) {
        if(message.eventData.listen.indexOf("mouseclick") != -1) {
            shape.on('click', clickEventHandler.bind(undefined, message.eventData.eventId));
        }
        if(message.eventData.listen.indexOf("mousedown") != -1) {
            shape.on('mousedown', mouseDownEventHandler.bind(undefined, message.eventData.eventId));
        }
        if(message.eventData.listen.indexOf("mouseup") != -1) {
            shape.on('mouseup', mouseUpEventHandler.bind(undefined, message.eventData.eventId));
        }
        if(message.eventData.listen.indexOf("mouseover") != -1) {
            shape.on('mouseover', mouseOverEventHandler.bind(undefined, message.eventData.eventId));
        }
        if(message.eventData.listen.indexOf("mousemove") != -1) {
            shape.on('mousemove', mouseMoveEventHandler.bind(undefined, message.eventData.eventId));
        }
        if(message.eventData.listen.indexOf("mouseout") != -1) {
            shape.on('mouseout', mouseOutEventHandler.bind(undefined, message.eventData.eventId));
        }
        if(message.eventData.listen.indexOf("mousedrag") != -1) {
            mouseDragFound = mouseDragFound || message.eventData["eventId"]==mouseDragId; // Used to disable mousedrag when no longer this event is requested
            shape.on('mousedown', mouseDragStartEventHandler.bind(undefined, message.eventData.eventId));
        }
    }
}

function clickEventHandler(id, event) { mouseEvent("mouseclick", id, event); }
function mouseDownEventHandler(id, event) { mouseEvent("mousedown", id, event); }
function mouseUpEventHandler(id, event) { mouseEvent("mouseup", id, event); }
function mouseOverEventHandler(id, event) { mouseEvent("mouseover", id, event); }
function mouseOutEventHandler(id, event) { mouseEvent("mouseout", id, event); }
function mouseMoveEventHandler(id, event) { mouseEvent("mousemove", id, event); }
var mouseDragId = undefined;
var mouseDragEndHandler = undefined;
var mouseDragHandler = undefined;
var mouseDragFound = true;
var prevMousePosX = 0;
var prevMousePosY = 0;
function mouseDragStartEventHandler(id, event) {
    mouseDragEndHandler = mouseDragEndEventHandler.bind(undefined, id);
    mouseDragHandler = mouseDragEventHandler.bind(undefined, id);
    mouseDragId = id; // Used to disable mousedrag when mousedrag is no longer attached to the shape
    // Compensate for the position of the canvas
    var canvasPos = $("#canvas").position();
    prevMousePosX = event.pageX-canvasPos.left+450;
    prevMousePosY = event.pageY-canvasPos.top+300;

    canvas.on('mouseout', mouseDragEndHandler);
    canvas.on('mouseup', mouseDragEndHandler);
    canvas.on('mousemove', mouseDragHandler);
}
function mouseDragEndEventHandler(id, event) {
    canvas.off('mouseout', mouseDragEndHandler);
    canvas.off('mouseup', mouseDragEndHandler);
    canvas.off('mousemove', mouseDragHandler);
    mouseDragId = undefined;
    mouseDragEndHandler = undefined;
    mouseDragHandler = undefined;
}
function mouseDragEventHandler(id, event) {
    // Compensate for the position of the canvas
    var canvasPos = $("#canvas").position();
    var x1 = prevMousePosX; // Fix me
    var x2 = event.pageX-canvasPos.left+450;
    prevMousePosX = x2;
    var y1 = prevMousePosY; // Fix me
    var y2 = event.pageY-canvasPos.top+300;
    prevMousePosY = y2;

    printDebugMessage("Event mousedrag on " + id + " from (x1:"+x1+" y1:"+y1+") to (x2:"+x2+" y2:"+y2+")" ,0);

    connection.send(JSON.stringify({
        "event":"mousedrag",
        "data":{
            "id1": id,
            "x1": x1,
            "y1": y1,
            "id2": id,
            "x2": x2,
            "y2": y2
        }
    }));
}
function mouseEvent(eventName, id, event) {
    // Compensate for the position of the canvas
    var canvasPos = $("#canvas").position();
    var x = event.pageX-canvasPos.left+450; // Fix me
    var y = event.pageY-canvasPos.top+300; // Fix me

    printDebugMessage("Event "+eventName+" on " + id + " (x:"+x+" y:"+y+")",0);

    connection.send(JSON.stringify({
        "event":eventName,
        "data":{
            "id": id,
            "x": x,
            "y": y
        }
    }));
}

function sendKeyEvent(eventName, event) {

    var key = normalizeKeyCode(event);
    var ctrl = event.ctrlKey || event.metaKey;
    var alt = event.altKey;
    var shift = event.shiftKey;

    printDebugMessage("KeyEvent "+key+" "+eventName+" (ctrl:"+ctrl+" alt:"+alt+" shift:"+shift+")",0);

    event.preventDefault(); // Prevent browser default behavior to block ctrl-c for example

    connection.send(JSON.stringify({
        "event":eventName,
        "data":{
            "key": key,
            "control": ctrl,
            "alt": alt,
            "shift": shift
        }
    }));
}

function sendScrollEvent(deltaX,deltaY) {

    printDebugMessage("ScrollEvent (deltaX:"+deltaX+" deltaY:"+deltaY+")",0);

    connection.send(JSON.stringify({
        "event":"scroll",
        "data":{
            "xdelta": deltaX,
            "ydelta": deltaY
        }
    }));
}

/*
 * Drawing shapes
 */

function shapeFromData(message) {

    var shape = null;
    var debugMessage = "";
    var data = message.data;

    // both fill and stroke are in the form {"r": 255, "g": 255, "b": 255, ("a": 1.0)?}
    if(data["fill"]){
        data["fill"] = rgbaDictToColor(data["fill"]);
    }

    if(data["stroke"]){
        data["stroke"] = rgbaDictToColor(data["stroke"]);
    }
    // Debug message
    if(!data["id"]) {
        data["id"] = "sid" + generadedShapeIdIdx;
        generadedShapeIdIdx++;
    }

    debugMessage = "Drawing " + message.type + " (" + data["id"] + ") ";

    // Init shape based on type
    switch (message.type) {
        case "line":
            shape = new Kinetic.Line(data);
            debugMessage += "with points: " + data.points;
            break;
        case "polygon":
            data.draggable = true;
            shape = new Kinetic.Polygon(data);
            break;
        case "circle":
            shape = new Kinetic.Circle(data);
            debugMessage += "width x:"+data.x+" y:"+data.y+" and radius:"+data.radius;
            break;
        case "rect":
            shape = new Kinetic.Rect(data);
            debugMessage += "width x:"+data.x+" y:"+data.y+" width:"+data.width+" height:"+data.height;
            break;
        case "text":
            shape = new Kinetic.Text(data);
            debugMessage += "width x:"+data.x+" y:"+data.y+" text:"+data.text;
            break;
        case "container":

            data.clip = [0, 0, data.width, data.height];
            shape = new Kinetic.Group(data);
            message.children.forEach(function(child) {
                shape.add(parseShapeData(child));
            });
            break;
        default:
            debugMessage = null;
            printDebugMessage("Unrecognized JSON message received from server.",2);
    }

    if(debugMessage)
        printDebugMessage(debugMessage,0);

    return shape;
}

function rgbaDictToColor(dict){
    var res = "";
    if(dict["a"]){
        res = "rgba({0},{1},{2},{3})".format(dict["r"], dict["g"], dict["b"], dict["a"]);
    } else {
        res = "rgb({0},{1},{2})".format(dict["r"], dict["g"], dict["b"]);
    }
    return res;
}

/*
 * Visible debugger
 *
 * Type 0 = message
 * Type 1 = warning
 * Type 2 = error
 */

function printDebugMessage(message, type) {

    var now = new Date(),
        now = now.getHours()+':'+now.getMinutes()+':'+now.getSeconds();

    if(type == 1)
    {
        console.warn(message);
    }
    else if(type == 2)
    {
        console.error(message);
    }
    else
    {
        console.log(message);
    }

    $("#debug").prepend("
["+now+"] "+message+"
")
}

/*
 * Canvas setup
 */

function initCanvas(container, width, height) {
    // Only init canvas when there is a container
    // Container is provided for testing purposes and extesibility
    if(container.exists()) {
        container.css( "width", width+"px" );
        container.css( "height", height+"px" );
        container.css( "margin-top", "-"+height/2+"px" );
        container.css( "margin-left", "-"+width/2+"px" );

        stage = new Kinetic.Stage({
            container: container.attr('id'),
            width: width, // Default width and height
            height: height // Can be changed with the resize canvas function
        });

        // Create new layer to draw on
        newDefaultLayer();

    }
}
function initWrapper(wrapper, width, height) {

    wrapper.css( "min-width", width+"px" );
    wrapper.css( "height", $( window ).height()+"px" );
}

function newDefaultLayer() {

    topLayerIdx++;
    layerList[topLayerIdx] = new Kinetic.Layer();
    stage.add(layerList[topLayerIdx]);
}

/*
 * On document ready
 */
$(document).ready(function() {

    var width = 900; // defined here because the container also needs these proportions
    var height = 600;

    // Init canvas
    initCanvas($('#canvas'),width,height);
    initWrapper($('#wrapper'),width,height);

    canvas = $("#canvas canvas");

    // Start listening for scroll events using mousewheel.js
    canvas.mousewheel(function(event) {
        sendScrollEvent(event.deltaX,event.deltaY);
        return false; // Prevent browser default behavior
    });

    // For flashing certain shapes while debugging
    $("#debug").delegate( ".debugSelector", "click", function() {
        debugCanClose = false;
        var sid = $(this).data("sid"); // The shape id in an extended data attribute

        var shape = stage.find('#' + sid)[0];
        var alreadyAnnimatingIdx = $.inArray(shape, debugAnnimatingShapes);

        if(alreadyAnnimatingIdx == -1)
        {
            debugAnnimatingShapes.push(shape);

            var fill = shape.getFill();

            shape.setFill("red");
            layerList[topLayerIdx].draw();

            var interval = setInterval(function(){

                shape.setFill(fill);
                layerList[topLayerIdx].draw();

                clearInterval(interval);
                debugAnnimatingShapes.splice( alreadyAnnimatingIdx ,1 );
                debugCanClose = true;
            },250);
        }

    });

    // For hiding the debug console
    $("#debug").click(function(){
        if(debugCanClose) {
            if($(this).width() == 20)
            {
                $(this).animate({
                    width: 350
                },300);
            }
            else
            {
                $(this).animate({
                    width: 20
                },300);
            }
        }
    });

    // When the connection is open, send some data to the server
    connection.onopen = function () {
        printDebugMessage("Connection opened",0);
    };
    // Log errors
    connection.onerror = connectionError;
    // Callback for recieving data
    connection.onmessage = connectionDataReceived;

    // Begin to listen for keys
    window.addEventListener('keydown', sendKeyEvent.bind(this,'keydown'));

    window.addEventListener('keyup', sendKeyEvent.bind(this,'keyup'));
});
<commit_msg>Prevents bubbling of event handlers<commit_after>// Global variables
var topLayerIdx = 0;
var layerList = new Array();
var stage = undefined;
var connection = new WebSocket('ws://localhost:8080');
var canvas = undefined;

var generadedShapeIdIdx = 0;
var debugTween;
var debugAnnimatingShapes = [];
var debugCanClose = true;

// Event handlers
function connectionDataReceived(event) {

    // Reset mousedrag
    mouseDragFound = false;

    // Clear screen
    layerList[topLayerIdx].destroyChildren();

    var dataObject = jQuery.parseJSON(event.data);

    var shape = parseShapeData(dataObject);

    // Disable mousedrag if event is no longer attached to the shape
    if(!mouseDragFound && mouseDragId!=undefined) {
        mouseDragEndEventHandler(mouseDragId,undefined);
    }

    // Draw on current layer
    layerList[topLayerIdx].add(shape);
    layerList[topLayerIdx].batchDraw();
}

function connectionError(error) {

    printDebugMessage("WebSocket Error " + error);
}

function parseShapeData(data) {

    var shape = shapeFromData(data);
    enableEventHandlers(shape, data);

    return shape;
}

/*
 * Sending Input events
 */

function enableEventHandlers(shape, message) {
    if(message.eventData != undefined && message.eventData != null) {
        if(message.eventData.listen.indexOf("mouseclick") != -1) {
            shape.on('click', clickEventHandler.bind(undefined, message.eventData.eventId));
        }
        if(message.eventData.listen.indexOf("mousedown") != -1) {
            shape.on('mousedown', mouseDownEventHandler.bind(undefined, message.eventData.eventId));
        }
        if(message.eventData.listen.indexOf("mouseup") != -1) {
            shape.on('mouseup', mouseUpEventHandler.bind(undefined, message.eventData.eventId));
        }
        if(message.eventData.listen.indexOf("mouseover") != -1) {
            shape.on('mouseover', mouseOverEventHandler.bind(undefined, message.eventData.eventId));
        }
        if(message.eventData.listen.indexOf("mousemove") != -1) {
            shape.on('mousemove', mouseMoveEventHandler.bind(undefined, message.eventData.eventId));
        }
        if(message.eventData.listen.indexOf("mouseout") != -1) {
            shape.on('mouseout', mouseOutEventHandler.bind(undefined, message.eventData.eventId));
        }
        if(message.eventData.listen.indexOf("mousedrag") != -1) {
            mouseDragFound = mouseDragFound || message.eventData["eventId"]==mouseDragId; // Used to disable mousedrag when no longer this event is requested
            shape.on('mousedown', mouseDragStartEventHandler.bind(undefined, message.eventData.eventId));
        }
    }
}

function clickEventHandler(id, event) { mouseEvent("mouseclick", id, event); }
function mouseDownEventHandler(id, event) { mouseEvent("mousedown", id, event); }
function mouseUpEventHandler(id, event) { mouseEvent("mouseup", id, event); }
function mouseOverEventHandler(id, event) { mouseEvent("mouseover", id, event); }
function mouseOutEventHandler(id, event) { mouseEvent("mouseout", id, event); }
function mouseMoveEventHandler(id, event) { mouseEvent("mousemove", id, event); }
var mouseDragId = undefined;
var mouseDragEndHandler = undefined;
var mouseDragHandler = undefined;
var mouseDragFound = true;
var prevMousePosX = 0;
var prevMousePosY = 0;
function mouseDragStartEventHandler(id, event) {
    mouseDragEndHandler = mouseDragEndEventHandler.bind(undefined, id);
    mouseDragHandler = mouseDragEventHandler.bind(undefined, id);
    mouseDragId = id; // Used to disable mousedrag when mousedrag is no longer attached to the shape
    // Compensate for the position of the canvas
    var canvasPos = $("#canvas").position();
    prevMousePosX = event.pageX-canvasPos.left+450;
    prevMousePosY = event.pageY-canvasPos.top+300;

    canvas.on('mouseout', mouseDragEndHandler);
    canvas.on('mouseup', mouseDragEndHandler);
    canvas.on('mousemove', mouseDragHandler);
    // Cancel event bubbling
    event.cancelBubble = true;
}
function mouseDragEndEventHandler(id, event) {
    canvas.off('mouseout', mouseDragEndHandler);
    canvas.off('mouseup', mouseDragEndHandler);
    canvas.off('mousemove', mouseDragHandler);
    mouseDragId = undefined;
    mouseDragEndHandler = undefined;
    mouseDragHandler = undefined;
    // Cancel event bubbling
    event.cancelBubble = true;
}
function mouseDragEventHandler(id, event) {
    // Compensate for the position of the canvas
    var canvasPos = $("#canvas").position();
    var x1 = prevMousePosX; // Fix me
    var x2 = event.pageX-canvasPos.left+450;
    prevMousePosX = x2;
    var y1 = prevMousePosY; // Fix me
    var y2 = event.pageY-canvasPos.top+300;
    prevMousePosY = y2;

    printDebugMessage("Event mousedrag on " + id + " from (x1:"+x1+" y1:"+y1+") to (x2:"+x2+" y2:"+y2+")" ,0);

    connection.send(JSON.stringify({
        "event":"mousedrag",
        "data":{
            "id1": id,
            "x1": x1,
            "y1": y1,
            "id2": id,
            "x2": x2,
            "y2": y2
        }
    }));
    // Cancel event bubbling
    event.cancelBubble = true;
}
function mouseEvent(eventName, id, event) {
    // Compensate for the position of the canvas
    var canvasPos = $("#canvas").position();
    var x = event.pageX-canvasPos.left+450; // Fix me
    var y = event.pageY-canvasPos.top+300; // Fix me

    printDebugMessage("Event "+eventName+" on " + id + " (x:"+x+" y:"+y+")",0);

    connection.send(JSON.stringify({
        "event":eventName,
        "data":{
            "id": id,
            "x": x,
            "y": y
        }
    }));
    // Cancel event bubbling
    event.cancelBubble = true;
}

function sendKeyEvent(eventName, event) {

    var key = normalizeKeyCode(event);
    var ctrl = event.ctrlKey || event.metaKey;
    var alt = event.altKey;
    var shift = event.shiftKey;

    printDebugMessage("KeyEvent "+key+" "+eventName+" (ctrl:"+ctrl+" alt:"+alt+" shift:"+shift+")",0);

    event.preventDefault(); // Prevent browser default behavior to block ctrl-c for example

    connection.send(JSON.stringify({
        "event":eventName,
        "data":{
            "key": key,
            "control": ctrl,
            "alt": alt,
            "shift": shift
        }
    }));
}

function sendScrollEvent(deltaX,deltaY) {

    printDebugMessage("ScrollEvent (deltaX:"+deltaX+" deltaY:"+deltaY+")",0);

    connection.send(JSON.stringify({
        "event":"scroll",
        "data":{
            "xdelta": deltaX,
            "ydelta": deltaY
        }
    }));
}

/*
 * Drawing shapes
 */

function shapeFromData(message) {

    var shape = null;
    var debugMessage = "";
    var data = message.data;

    // both fill and stroke are in the form {"r": 255, "g": 255, "b": 255, ("a": 1.0)?}
    if(data["fill"]){
        data["fill"] = rgbaDictToColor(data["fill"]);
    }

    if(data["stroke"]){
        data["stroke"] = rgbaDictToColor(data["stroke"]);
    }
    // Debug message
    if(!data["id"]) {
        data["id"] = "sid" + generadedShapeIdIdx;
        generadedShapeIdIdx++;
    }

    debugMessage = "Drawing " + message.type + " (" + data["id"] + ") ";

    // Init shape based on type
    switch (message.type) {
        case "line":
            shape = new Kinetic.Line(data);
            debugMessage += "with points: " + data.points;
            break;
        case "polygon":
            shape = new Kinetic.Polygon(data);
            break;
        case "circle":
            shape = new Kinetic.Circle(data);
            debugMessage += "width x:"+data.x+" y:"+data.y+" and radius:"+data.radius;
            break;
        case "rect":
            shape = new Kinetic.Rect(data);
            debugMessage += "width x:"+data.x+" y:"+data.y+" width:"+data.width+" height:"+data.height;
            break;
        case "text":
            shape = new Kinetic.Text(data);
            debugMessage += "width x:"+data.x+" y:"+data.y+" text:"+data.text;
            break;
        case "container":

            data.clip = [0, 0, data.width, data.height];
            shape = new Kinetic.Group(data);
            message.children.forEach(function(child) {
                shape.add(parseShapeData(child));
            });
            break;
        default:
            debugMessage = null;
            printDebugMessage("Unrecognized JSON message received from server.",2);
    }

    if(debugMessage)
        printDebugMessage(debugMessage,0);

    return shape;
}

function rgbaDictToColor(dict){
    var res = "";
    if(dict["a"]){
        res = "rgba({0},{1},{2},{3})".format(dict["r"], dict["g"], dict["b"], dict["a"]);
    } else {
        res = "rgb({0},{1},{2})".format(dict["r"], dict["g"], dict["b"]);
    }
    return res;
}

/*
 * Visible debugger
 *
 * Type 0 = message
 * Type 1 = warning
 * Type 2 = error
 */

function printDebugMessage(message, type) {

    var now = new Date(),
        now = now.getHours()+':'+now.getMinutes()+':'+now.getSeconds();

    if(type == 1)
    {
        console.warn(message);
    }
    else if(type == 2)
    {
        console.error(message);
    }
    else
    {
        console.log(message);
    }

    $("#debug").prepend("
["+now+"] "+message+"
")
}

/*
 * Canvas setup
 */

function initCanvas(container, width, height) {
    // Only init canvas when there is a container
    // Container is provided for testing purposes and extesibility
    if(container.exists()) {
        container.css( "width", width+"px" );
        container.css( "height", height+"px" );
        container.css( "margin-top", "-"+height/2+"px" );
        container.css( "margin-left", "-"+width/2+"px" );

        stage = new Kinetic.Stage({
            container: container.attr('id'),
            width: width, // Default width and height
            height: height // Can be changed with the resize canvas function
        });

        // Create new layer to draw on
        newDefaultLayer();

    }
}
function initWrapper(wrapper, width, height) {

    wrapper.css( "min-width", width+"px" );
    wrapper.css( "height", $( window ).height()+"px" );
}

function newDefaultLayer() {

    topLayerIdx++;
    layerList[topLayerIdx] = new Kinetic.Layer();
    stage.add(layerList[topLayerIdx]);
}

/*
 * On document ready
 */
$(document).ready(function() {

    var width = 900; // defined here because the container also needs these proportions
    var height = 600;

    // Init canvas
    initCanvas($('#canvas'),width,height);
    initWrapper($('#wrapper'),width,height);

    canvas = $("#canvas canvas");

    // Start listening for scroll events using mousewheel.js
    canvas.mousewheel(function(event) {
        sendScrollEvent(event.deltaX,event.deltaY);
        return false; // Prevent browser default behavior
    });

    // For flashing certain shapes while debugging
    $("#debug").delegate( ".debugSelector", "click", function() {
        debugCanClose = false;
        var sid = $(this).data("sid"); // The shape id in an extended data attribute

        var shape = stage.find('#' + sid)[0];
        var alreadyAnnimatingIdx = $.inArray(shape, debugAnnimatingShapes);

        if(alreadyAnnimatingIdx == -1)
        {
            debugAnnimatingShapes.push(shape);

            var fill = shape.getFill();

            shape.setFill("red");
            layerList[topLayerIdx].draw();

            var interval = setInterval(function(){

                shape.setFill(fill);
                layerList[topLayerIdx].draw();

                clearInterval(interval);
                debugAnnimatingShapes.splice( alreadyAnnimatingIdx ,1 );
                debugCanClose = true;
            },250);
        }

    });

    // For hiding the debug console
    $("#debug").click(function(){
        if(debugCanClose) {
            if($(this).width() == 20)
            {
                $(this).animate({
                    width: 350
                },300);
            }
            else
            {
                $(this).animate({
                    width: 20
                },300);
            }
        }
    });

    // When the connection is open, send some data to the server
    connection.onopen = function () {
        printDebugMessage("Connection opened",0);
    };
    // Log errors
    connection.onerror = connectionError;
    // Callback for recieving data
    connection.onmessage = connectionDataReceived;

    // Begin to listen for keys
    window.addEventListener('keydown', sendKeyEvent.bind(this,'keydown'));

    window.addEventListener('keyup', sendKeyEvent.bind(this,'keyup'));
});
<|endoftext|>


<commit_before>module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    clean: ['jsc', 'css'],

    bower: {
      install: {
          options: {
              targetDir: './libs',
              install: true,
              copy: false,
              quiet: true
          }
      },
      prune: {
          options: {
              targetDir: './libs',
              copy: false,
              offline: true,
              quiet: true
          }
      }
    },

    exec: {
        buildOpenlayers: {
            command: 'python build.py -c none full ../OpenLayers.debug.js',
            stdout: false,
            cwd: 'libs/openlayers/build'
        },
        buildCytoscape: {
            command: 'make minify',
            stdout: false,
            cwd: 'libs/cytoscape.js'
        },
        buildPathFinding: {
            command: 'npm install -q && make',
            stdout: false,
            cwd: 'libs/PathFinding.js'
        }
    },

    less: {
        options: {
            paths: ['less'],
            sourceMap: true,
            sourceMapFilename: 'css/lumify.css.map',
            sourceMapURL: 'lumify.css.map',
            sourceMapRootpath: '/',
            dumpLineNumbers: 'all'
        },
        development: {
            files: {
                'css/lumify.css': 'less/lumify.less'
            }
        },
        production: {
            files: {
                'css/lumify.css': 'less/lumify.less'
            },
            options: {
                compress: true
            }
        }
    },

    requirejs: {
        options: {
            mainConfigFile: 'js/require.config.js',
            dir: 'jsc',
            baseUrl: 'js',
            preserveLicenseComments: false,
            removeCombined: false,
            /*
            modules: [
                { name: 'lumify' },
                { name: 'app' },
                { name: 'appFullscreenDetails' },
                { name: 'detail/artifact/artifact' },
                { name: 'detail/entity/entity' }
            ]
                */
        },
        development: {
            options: {
                logLevel: 2,
                optimize: 'none',
                keepBuildDir: true,
            }
        },
        production: {
            options: {
                logLevel: 0,
                optimize: 'uglify2',
                generateSourceMaps: true,
            }
        }
    },

    jshint: {
        options: {
            jshintrc: true
        },
        development: {
            src: ['js/**/*\.js']
        },
        ci: {
            src: ['js/**/*\.js'],
            options: {
                reporter: 'checkstyle',
                reporterOutput: 'build/jshint-checkstyle.xml'
            }
        }
    },

    jscs: {
        options: {
            config: '.jscs.json'
        },
        development: {
            src: [
                'js/**/*.js',
                'test/spec/**/*.js',
                '!js/**/three-plugins/*.js',
                '!js/graph/3d/3djs/3djs/graph/layout/force-directed.js',
                '!js/plugin-development/**/*.js',
                '!js/require.config.js'
            ],
        },
        ci: {
            src: [
                'js/**/*.js',
                'test/spec/**/*.js',
                '!js/**/three-plugins/*.js',
                '!js/graph/3d/3djs/3djs/graph/layout/force-directed.js',
                '!js/plugin-development/**/*.js',
                '!js/require.config.js'
            ],
            options: {
                force: true,
                reporter: 'checkstyle',
                reporterOutput: 'build/jscs-checkstyle.xml'
            }
        }
    },

    plato: {
        ci: {
            files: {
                'build/plato': ['js/**/*.js'],
            },
            options: {
                jshint: {
                    browser: true,
                    '-W033': true,
                    '-W040': true
                }
            }
        },
    },

    watch: {
        options: {
            dateFormat: function(time) {
                grunt.log.ok('The watch finished in ' + (time / 1000).toFixed(2) + 's. Waiting...');
            },
            spawn: false,
            interrupt: false
        },
        css: {
            files: ['less/**/*.less', 'libs/**/*.css', 'libs/**/*.less'],
            tasks: ['less:development', 'notify:css']
        },
        scripts: {
            files: [
                'js/**/*.js',
                'js/**/*.ejs',
                'js/**/*.hbs',
                'js/**/*.vsh',
                'js/**/*.fsh'
            ],
            tasks: ['requirejs:development', 'notify:js'],
            options: {
                livereload: {
                    port: 35729,
                    key: grunt.file.read('test/localhost.key'),
                    cert: grunt.file.read('test/localhost.cert')
                }
            }
        },
        lint: {
            files: ['js/**/*.js'],
            tasks: ['jshint:development']
        },
        jscs: {
            files: [ 'js/**/*.js' ],
            tasks: ['jscs:development']
        }
    },

    notify: {
        js: {
            options: {
                title: 'Lumify',
                message: 'RequireJS finished',
            }
        },
        css: {
            options: {
                title: 'Lumify',
                message: 'Less finished',
            }
        }
    },

    mochaSelenium: {
        options: {
            screenshotAfterEach: true,
            screenshotDir: 'test/reports',
            reporter: 'spec', // doc for html
            viewport: { width: 900, height: 700 },
            timeout: 30e3,
            slow: 10e3,
            implicitWaitTimeout: 100,
            asyncScriptTimeout: 5000,
            usePromises: true,
            useChaining: true,
            ignoreLeaks: false
        },
        firefox: { src: ['test/functional/spec/**/*.js' ], options: { browserName: 'firefox' } },
        chrome:  { src: ['test/functional/spec/**/*.js' ], options: { browserName: 'chrome' } }
    },

    karma: {
        unit: {
            configFile: 'karma.conf.js',
            runnerPort: 9999,
            singleRun: true,
            //browsers: ['PhantomJS']
        }
    }
  });

  grunt.loadNpmTasks('grunt-bower-task');
  grunt.loadNpmTasks('grunt-exec');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-less');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-requirejs');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-notify');
  grunt.loadNpmTasks('grunt-mocha-selenium');
  grunt.loadNpmTasks('grunt-karma');
  grunt.loadNpmTasks('grunt-jscs-checker');
  grunt.loadNpmTasks('grunt-plato');

  // Speed up jscs and jshint by only checking changed files
  // ensure we still ignore files though
  var initialJscsSrc = grunt.config('jscs.development.src'),
      initialHintSrc = grunt.config('jshint.development.src');
  grunt.event.on('watch', function(action, filepath) {
      var matchingHint = grunt.file.match(initialHintSrc, filepath),
          matchingJscs = grunt.file.match(initialJscsSrc, filepath);

      grunt.config('jshint.development.src', matchingHint);
      grunt.config('jscs.development.src', matchingJscs);
  });

  grunt.registerTask('deps', 'Install Webapp Dependencies',
     ['bower:install', 'bower:prune', 'exec']);

  grunt.registerTask('test:functional:chrome', 'Run JavaScript Functional Tests in Chrome',
     ['mochaSelenium:chrome']);
  grunt.registerTask('test:functional:firefox', 'Run JavaScript Functional Tests in Firefox',
     ['mochaSelenium:firefox']);
  grunt.registerTask('test:functional', 'Run JavaScript Functional Tests',
     ['test:functional:chrome', 'test:functional:firefox']);

  grunt.registerTask('test:unit', 'Run JavaScript Unit Tests',
     ['karma']);
  grunt.registerTask('test:style', 'Run JavaScript CodeStyle reports',
     ['jshint:ci', 'plato:ci', 'jscs:ci']);
  grunt.registerTask('style:development', 'Run JavaScript CodeStyle reports',
    ['jshint:development', 'jscs:development']);

  grunt.registerTask('development', 'Build js/less for development',
     ['less:development', 'requirejs:development']);
  grunt.registerTask('production', 'Build js/less for production',
     ['less:production', 'requirejs:production']);

  grunt.registerTask('default', ['clean', 'development', 'style:development', 'watch']);
};
<commit_msg>Ignore plugin development libraries<commit_after>module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    clean: ['jsc', 'css'],

    bower: {
      install: {
          options: {
              targetDir: './libs',
              install: true,
              copy: false,
              quiet: true
          }
      },
      prune: {
          options: {
              targetDir: './libs',
              copy: false,
              offline: true,
              quiet: true
          }
      }
    },

    exec: {
        buildOpenlayers: {
            command: 'python build.py -c none full ../OpenLayers.debug.js',
            stdout: false,
            cwd: 'libs/openlayers/build'
        },
        buildCytoscape: {
            command: 'make minify',
            stdout: false,
            cwd: 'libs/cytoscape.js'
        },
        buildPathFinding: {
            command: 'npm install -q && make',
            stdout: false,
            cwd: 'libs/PathFinding.js'
        }
    },

    less: {
        options: {
            paths: ['less'],
            sourceMap: true,
            sourceMapFilename: 'css/lumify.css.map',
            sourceMapURL: 'lumify.css.map',
            sourceMapRootpath: '/',
            dumpLineNumbers: 'all'
        },
        development: {
            files: {
                'css/lumify.css': 'less/lumify.less'
            }
        },
        production: {
            files: {
                'css/lumify.css': 'less/lumify.less'
            },
            options: {
                compress: true
            }
        }
    },

    requirejs: {
        options: {
            mainConfigFile: 'js/require.config.js',
            dir: 'jsc',
            baseUrl: 'js',
            preserveLicenseComments: false,
            removeCombined: false,
            /*
            modules: [
                { name: 'lumify' },
                { name: 'app' },
                { name: 'appFullscreenDetails' },
                { name: 'detail/artifact/artifact' },
                { name: 'detail/entity/entity' }
            ]
                */
        },
        development: {
            options: {
                logLevel: 2,
                optimize: 'none',
                keepBuildDir: true,
            }
        },
        production: {
            options: {
                logLevel: 0,
                optimize: 'uglify2',
                generateSourceMaps: true,
            }
        }
    },

    jshint: {
        options: {
            jshintrc: true
        },
        development: {
            src: [
                'js/**/*\.js',
                '!js/plugin-development/**/libs/**/*.js'
            ]
        },
        ci: {
            src: [
                'js/**/*\.js',
                '!js/plugin-development/**/libs/**/*.js'
            ],
            options: {
                reporter: 'checkstyle',
                reporterOutput: 'build/jshint-checkstyle.xml'
            }
        }
    },

    jscs: {
        options: {
            config: '.jscs.json'
        },
        development: {
            src: [
                'js/**/*.js',
                'test/spec/**/*.js',
                '!js/**/three-plugins/*.js',
                '!js/graph/3d/3djs/3djs/graph/layout/force-directed.js',
                '!js/plugin-development/**/*.js',
                '!js/require.config.js'
            ],
        },
        ci: {
            src: [
                'js/**/*.js',
                'test/spec/**/*.js',
                '!js/**/three-plugins/*.js',
                '!js/graph/3d/3djs/3djs/graph/layout/force-directed.js',
                '!js/plugin-development/**/*.js',
                '!js/require.config.js'
            ],
            options: {
                force: true,
                reporter: 'checkstyle',
                reporterOutput: 'build/jscs-checkstyle.xml'
            }
        }
    },

    plato: {
        ci: {
            files: {
                'build/plato': ['js/**/*.js'],
            },
            options: {
                jshint: {
                    browser: true,
                    '-W033': true,
                    '-W040': true
                }
            }
        },
    },

    watch: {
        options: {
            dateFormat: function(time) {
                grunt.log.ok('The watch finished in ' + (time / 1000).toFixed(2) + 's. Waiting...');
            },
            spawn: false,
            interrupt: false
        },
        css: {
            files: ['less/**/*.less', 'libs/**/*.css', 'libs/**/*.less'],
            tasks: ['less:development', 'notify:css']
        },
        scripts: {
            files: [
                'js/**/*.js',
                'js/**/*.ejs',
                'js/**/*.hbs',
                'js/**/*.vsh',
                'js/**/*.fsh'
            ],
            tasks: ['requirejs:development', 'notify:js'],
            options: {
                livereload: {
                    port: 35729,
                    key: grunt.file.read('test/localhost.key'),
                    cert: grunt.file.read('test/localhost.cert')
                }
            }
        },
        lint: {
            files: ['js/**/*.js'],
            tasks: ['jshint:development']
        },
        jscs: {
            files: [ 'js/**/*.js' ],
            tasks: ['jscs:development']
        }
    },

    notify: {
        js: {
            options: {
                title: 'Lumify',
                message: 'RequireJS finished',
            }
        },
        css: {
            options: {
                title: 'Lumify',
                message: 'Less finished',
            }
        }
    },

    mochaSelenium: {
        options: {
            screenshotAfterEach: true,
            screenshotDir: 'test/reports',
            reporter: 'spec', // doc for html
            viewport: { width: 900, height: 700 },
            timeout: 30e3,
            slow: 10e3,
            implicitWaitTimeout: 100,
            asyncScriptTimeout: 5000,
            usePromises: true,
            useChaining: true,
            ignoreLeaks: false
        },
        firefox: { src: ['test/functional/spec/**/*.js' ], options: { browserName: 'firefox' } },
        chrome:  { src: ['test/functional/spec/**/*.js' ], options: { browserName: 'chrome' } }
    },

    karma: {
        unit: {
            configFile: 'karma.conf.js',
            runnerPort: 9999,
            singleRun: true,
            //browsers: ['PhantomJS']
        }
    }
  });

  grunt.loadNpmTasks('grunt-bower-task');
  grunt.loadNpmTasks('grunt-exec');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-less');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-requirejs');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-notify');
  grunt.loadNpmTasks('grunt-mocha-selenium');
  grunt.loadNpmTasks('grunt-karma');
  grunt.loadNpmTasks('grunt-jscs-checker');
  grunt.loadNpmTasks('grunt-plato');

  // Speed up jscs and jshint by only checking changed files
  // ensure we still ignore files though
  var initialJscsSrc = grunt.config('jscs.development.src'),
      initialHintSrc = grunt.config('jshint.development.src');
  grunt.event.on('watch', function(action, filepath) {
      var matchingHint = grunt.file.match(initialHintSrc, filepath),
          matchingJscs = grunt.file.match(initialJscsSrc, filepath);

      grunt.config('jshint.development.src', matchingHint);
      grunt.config('jscs.development.src', matchingJscs);
  });

  grunt.registerTask('deps', 'Install Webapp Dependencies',
     ['bower:install', 'bower:prune', 'exec']);

  grunt.registerTask('test:functional:chrome', 'Run JavaScript Functional Tests in Chrome',
     ['mochaSelenium:chrome']);
  grunt.registerTask('test:functional:firefox', 'Run JavaScript Functional Tests in Firefox',
     ['mochaSelenium:firefox']);
  grunt.registerTask('test:functional', 'Run JavaScript Functional Tests',
     ['test:functional:chrome', 'test:functional:firefox']);

  grunt.registerTask('test:unit', 'Run JavaScript Unit Tests',
     ['karma']);
  grunt.registerTask('test:style', 'Run JavaScript CodeStyle reports',
     ['jshint:ci', 'plato:ci', 'jscs:ci']);
  grunt.registerTask('style:development', 'Run JavaScript CodeStyle reports',
    ['jshint:development', 'jscs:development']);

  grunt.registerTask('development', 'Build js/less for development',
     ['less:development', 'requirejs:development']);
  grunt.registerTask('production', 'Build js/less for production',
     ['less:production', 'requirejs:production']);

  grunt.registerTask('default', ['clean', 'development', 'style:development', 'watch']);
};
<|endoftext|>


<commit_before>$(function () {
  $("#grid").igGrid({
    autoGenerateColumns: false,
    width: "100%",
    columns: [
      { headerText: "Product ID", key: "ProductID", dataType: "number", width: "15%" },
      { headerText: "Product Name", key: "Name", dataType: "string", width: "40%" },
      { headerText: "Product Number", key: "ProductNumber", dataType: "string", width: "30%" },
      { headerText: "Make Flag", key: "MakeFlag", dataType: "bool", width: "15%" }
    ],
    dataSource: adventureWorks,
    features: [
      {
        name: 'Paging',
        type: "local",
        pageSize: 10
      }
    ]
  });
});
<commit_msg>Update demo.js<commit_after>$(function () {
  console.log("creating grid");
  $("#grid").igGrid({
    autoGenerateColumns: false,
    width: "100%",
    columns: [
      { headerText: "Product ID", key: "ProductID", dataType: "number", width: "15%" },
      { headerText: "Product Name", key: "Name", dataType: "string", width: "40%" },
      { headerText: "Product Number", key: "ProductNumber", dataType: "string", width: "30%" },
      { headerText: "Make Flag", key: "MakeFlag", dataType: "bool", width: "15%" }
    ],
    dataSource: adventureWorks,
    features: [
      {
        name: 'Paging',
        type: "local",
        pageSize: 10
      }
    ]
  });
});
<|endoftext|>


<commit_before>'use strict';

// declare modules
angular.module('Authentication', []);
angular.module('Home', []);

angular.module('BasicHttpAuthExample', [
    'Authentication',
    'Home',
    'ngRoute',
    'ngCookies'
])

.config(['$routeProvider', function ($routeProvider) {

    $routeProvider
        .when('/login', {
            controller: 'LoginController',
            templateUrl: 'modules/authentication/views/login.html',
            hideMenus: true
        })

        .when('/', {
            controller: 'HomeController',
            templateUrl: 'modules/home/views/index.htm'
        })

        .otherwise({ redirectTo: '/login' });
}])

.run(['$rootScope', '$location', '$cookieStore', '$http',
    function ($rootScope, $location, $cookieStore, $http) {
        // keep user logged in after page refresh
        $rootScope.globals = $cookieStore.get('globals') || {};
        if ($rootScope.globals.currentUser) {
            $http.defaults.headers.common['Authorization'] = 'Basic ' + $rootScope.globals.currentUser.authdata; // jshint ignore:line
        }

        $rootScope.$on('$locationChangeStart', function (event, next, current) {
            // redirect to login page if not logged in
            if ($location.path() !== '/login' && !$rootScope.globals.currentUser) {
                $location.path('/login');
            }
        });
    }]);
<commit_msg>Update app.js<commit_after>'use strict';

// declare modules
angular.module('Authentication', []);
angular.module('Home', []);

angular.module('BasicHttpAuthExample', [
    'Authentication',
    'Home',
    'ngRoute',
    'ngCookies'
])

.config(['$routeProvider', function ($routeProvider) {

    $routeProvider
        .when('/login', {
            controller: 'LoginController',
            templateUrl: 'modules/authentication/views/login.html',
            hideMenus: true
        })

        .when('/', {
            controller: 'HomeController',
            templateUrl: 'modules/home/views/home.html'
        })

        .otherwise({ redirectTo: '/login' });
}])

.run(['$rootScope', '$location', '$cookieStore', '$http',
    function ($rootScope, $location, $cookieStore, $http) {
        // keep user logged in after page refresh
        $rootScope.globals = $cookieStore.get('globals') || {};
        if ($rootScope.globals.currentUser) {
            $http.defaults.headers.common['Authorization'] = 'Basic ' + $rootScope.globals.currentUser.authdata; // jshint ignore:line
        }

        $rootScope.$on('$locationChangeStart', function (event, next, current) {
            // redirect to login page if not logged in
            if ($location.path() !== '/login' && !$rootScope.globals.currentUser) {
                $location.path('/login');
            }
        });
    }]);
<|endoftext|>


<commit_before>'use strict';
var server = require('../../server');

describe('GET /', function() {

  beforeEach(function() {
    browser.get('/');
    disableAnimation();
  });

  it('should return index page with greeting', function() {
    expect(element(by.id('greeting')).getText()).toEqual('hello, world!');
  });

  it('should update tagline when call button clicked', function() {
    element(by.id('call')).click();
    expect(element(by.id('tagline')).getText()).toEqual('Did you call me?');
  });

  it('should update tagline when AJAX call button clicked', function() {
    element(by.id('ajaxcall')).click();
    expect(element(by.id('tagline')).getText()).toEqual('Say hello from server! Your query: Did you call me?');
  });

  function disableAnimation() {
    $('body').allowAnimations(false);
    browser.executeScript("document.body.className += ' notransition';");
  }

});

<commit_msg>[#114206889] Refactor e2e specs<commit_after>'use strict';

describe('GET /', function() {

  beforeEach(function() {
    browser.get('/');
    disableAnimation();
  });

  it('should return index page with greeting', function() {
    expect($('#greeting').getText()).toBe('hello, world!');
  });

  it('should update tagline when call button clicked', function() {
    $('#call').click();
    expect($('#tagline').getText()).toBe('Did you call me?');
  });

  it('should update tagline when AJAX call button clicked', function() {
    $('#ajaxcall').click();
    expect($('#tagline').getText()).toBe('Say hello from server! Your query: Did you call me?');
  });

  function disableAnimation() {
    $('body').allowAnimations(false);
    browser.executeScript("document.body.className += ' notransition';");
  }

});

<|endoftext|>


<commit_before>var child = require('child_process');
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require("fs"));

module.exports = function(arg, generate, done) {
  var package = require(process.env.PWD + '/package.json');
  if (!package.hasOwnProperty('dependencies')) {
    package.dependencies = {}
  }
  package.dependencies['ml-event'] = "*";
  package.dependencies['ml-timer'] = "*";
  package.dependencies['ml-pinmux'] = "*";
  package.SDKpath = "./src/src";
  package.scripts.build = "ml parse:js && ml build:js && ml build:init && ml build:bin && ml burn ./out/mt7687_iot_sdk_xip.bin";
  package.scripts.installEnv = "ml install:sdk && ml install:gcc && ml install:jerry && ml install:ml";
  fs.writeFileAsync(process.env.PWD + '/package.json', JSON.stringify(package));
  return child.exec('cp ' + __dirname.replace('/commands', '/featureConfig.json') + ' ' + process.env.PWD + '/featureConfig.json && mkdir sdk');
}<commit_msg> change __dirname to path.join method<commit_after>var child = require('child_process');
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require("fs"));
var path = require('path');
module.exports = function(arg, generate, done) {
  var package = require(process.env.PWD + '/package.json');
  if (!package.hasOwnProperty('dependencies')) {
    package.dependencies = {}
  }
  package.dependencies['ml-event'] = "*";
  package.dependencies['ml-timer'] = "*";
  package.dependencies['ml-pinmux'] = "*";
  package.SDKpath = "./src/src";
  package.scripts.build = "ml parse:js && ml build:js && ml build:init && ml build:bin && ml burn ./out/mt7687_iot_sdk_xip.bin";
  package.scripts.installEnv = "ml install:sdk && ml install:gcc && ml install:jerry && ml install:ml";
  fs.writeFileAsync(process.env.PWD + '/package.json', JSON.stringify(package));
  return child.exec('cp ' + path.join(__dirname, '../featureConfig.json') + ' ' + process.env.PWD + '/featureConfig.json && mkdir sdk');
}<|endoftext|>


<commit_before>(function (global, factory) {
    if (typeof exports === 'object') {
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        define(factory);
    } else {
        global.weknowhow = global.weknowhow || {};
        global.weknowhow.magicpen = factory();
    }
}(this, function () {
    var global = this;
    function getKeys(obj) {
        var result = [];

        for (var i in obj) {
            if (obj.hasOwnProperty(i)) {
                result.push(i);
            }
        }

        return result;
    }

    function forEach(arr, callback, that) {
        for (var i = 0, n = arr.length; i < n; i += 1)
            callback.call(that, arr[i], i, arr);
    }

    function map(arr, callback, that) {
        var result = [];
        for (var i = 0, n = arr.length; i < n; i += 1)
            result.push(callback.call(that, arr[i], i, arr));
        return result;
    }

    function filter(arr, callback, that) {
        var result = [];
        for (var i = 0, n = arr.length; i < n; i += 1)
            if (callback.call(that, arr[i], i, arr))
                result.push(arr[i]);
        return result;
    }

    function extend(target) {
        var sources = Array.prototype.slice.call(arguments, 1);
        forEach(sources, function (source) {
            forEach(getKeys(source), function (key) {
                target[key] = source[key];
            });
        });
        return target;
    }

    var requireStyles = ['lines', 'text', 'block'];

    function Serializer(styles) {
        forEach(requireStyles, function (style) {
            if (!styles[style]) {
                throw new Error("Required style '" + style + "' is missing");
            }
        });
        this.styles = styles;
    }

    Serializer.prototype.serialize = function (lines) {
        return this.serializeLines(lines);
    };

    Serializer.prototype.serializeLines = function (lines) {
        return this.styles.lines(map(lines, function (line) {
            return this.serializeLineContent(line);
        }, this));
    };

    Serializer.prototype.serializeLineContent = function (content) {
        return map(content, this.serializeEntry, this);
    };

    function isOutputEntry(obj) {
        return typeof obj === 'object' && 'style' in obj && 'args' in obj;
    }

    Serializer.prototype.serializeEntry = function (entry) {
        if (entry.style in this.styles) {
            return this.styles[entry.style].apply(this.styles, entry.args);
        } else {
            throw new Error('Unknown style: "' + entry.style + '"');
        }
    };

    function createSerializer(styles) {
        function CustomSerializer() {
        }
        CustomSerializer.prototype = new Serializer(styles);
        return CustomSerializer;
    }

    function duplicateText(content, times) {
        var result = '';
        for (var i = 0; i < times; i += 1) {
            result += content;
        }
        return result;
    }

    // copied from https://github.com/sindresorhus/ansi-regex
    // License https://raw.githubusercontent.com/sindresorhus/ansi-regex/master/license
    var ansiRegex = /\u001b\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]/g;

    var TextSerializer = createSerializer({
        text: function (content) {
            return content;
        },
        lines: function (lines) {
            function serializeLine(line) {
                var serializedLines = [''];

                forEach(line, function (inlineBlock, blockIndex) {
                    var blockLines = map(String(inlineBlock).split('\n'), function (serializedBlockLine) {
                        return {
                            content: serializedBlockLine,
                            length: serializedBlockLine.replace(ansiRegex, '').length
                        };
                    });
                    var longestBlockLine = 0;
                    forEach(blockLines, function (blockLine) {
                        longestBlockLine = Math.max(longestBlockLine, blockLine.length);
                    });

                    var blockStartIndex = serializedLines[0].replace(ansiRegex, '').length;
                    serializedLines[0] += blockLines[0].content;
                    if (blockLines.length > 1 && blockIndex < line.length - 1) {
                        serializedLines[0] += duplicateText(' ', longestBlockLine - blockLines[0].length);
                    }

                    forEach(blockLines.slice(1), function (blockLine, index) {
                        var lineIndex = index + 1;
                        serializedLines[lineIndex] = serializedLines[lineIndex] || '';
                        var padding = duplicateText(' ', blockStartIndex - serializedLines[lineIndex].replace(ansiRegex, '').length);
                        serializedLines[lineIndex] += padding + blockLine.content;
                    });
                });

                return serializedLines.join('\n');
            }

            return map(lines, serializeLine).join('\n');
        },
        block: function (content) {
            return content;
        }
    });

    var ansiStyles = (function () {
        // Copied from https://github.com/sindresorhus/ansi-styles/
        // License: raw.githubusercontent.com/sindresorhus/ansi-styles/master/license
        var styles = {};

        var codes = {
            bold: [1, 22], // 21 isn't widely supported and 22 does the same thing
            dim: [2, 22],
            italic: [3, 23],
            underline: [4, 24],
            inverse: [7, 27],
            hidden: [8, 28],
            strikeThrough: [9, 29],

            black: [30, 39],
            red: [31, 39],
            green: [32, 39],
            yellow: [33, 39],
            blue: [34, 39],
            magenta: [35, 39],
            cyan: [36, 39],
            white: [37, 39],
            gray: [90, 39],

            bgBlack: [40, 49],
            bgRed: [41, 49],
            bgGreen: [42, 49],
            bgYellow: [43, 49],
            bgBlue: [44, 49],
            bgMagenta: [45, 49],
            bgCyan: [46, 49],
            bgWhite: [47, 49]
        };

        Object.keys(codes).forEach(function (key) {
            var val = codes[key];
            var style = styles[key] = {};
            style.open = '\x1B[' + val[0] + 'm';
            style.close = '\x1B[' + val[1] + 'm';
        });

        return styles;
    }());

    var AnsiSerializer = createSerializer(extend({}, TextSerializer.prototype.styles, {
        text: function (content) {
            if (arguments.length > 1) {
                var stylesString = Array.prototype.slice.call(arguments, 1).join(',');
                var styles = stylesString.split(/\s*,\s*/);
                forEach(styles, function (style) {
                    if (ansiStyles[style]) {
                        content = ansiStyles[style].open + content + ansiStyles[style].close;
                    }
                });
            }

            return content;
        }
    }));

    var htmlStyles = {
        bold: 'font-weight: bold',
        dim: 'opacity: 0.7',
        italic: 'font-style: italic',
        underline: 'text-decoration: underline',
        inverse: '-webkit-filter: invert(%100); filter: invert(100%)',
        hidden: 'visibility: hidden',
        strikeThrough: 'text-decoration: line-through',

        black: 'color: black',
        red: 'color: red',
        green: 'color: green',
        yellow: 'color: yellow',
        blue: 'color: blue',
        magenta: 'color: magenta',
        cyan: 'color: cyan',
        white: 'color: white',
        gray: 'color: gray',

        bgBlack: 'background-color: black',
        bgRed: 'background-color: red',
        bgGreen: 'background-color: green',
        bgYellow: 'background-color: yellow',
        bgBlue: 'background-color: blue',
        bgMagenta: 'background-color: magenta',
        bgCyan: 'background-color: cyan',
        bgWhite: 'background-color: white'
    };

    var HtmlSerializer = createSerializer({
        text: function (content) {
            content = String(content)
                .replace(/&/g, '&')
                .replace(/ /g, ' ')
                .replace(/</g, '<')
                .replace(/>/g, '>')
                .replace(/"/g, '"');

            if (arguments.length > 1) {
                var stylesString = Array.prototype.slice.call(arguments, 1).join(',');
                var styles = filter(stylesString.split(/\s*,\s*/), function (styleName) {
                    return htmlStyles[styleName];
                });

                content = '' + content + '';
            }
            return content;
        },
        lines: function (lines) {
            return '\n' +
                map(lines, function (line) {
                    return '
' + line.join('') + '
';
                }).join('\n') + '\n' +
                '';
        },
        block: function (content) {
            return '
' + content + '
';
        }
    });

    function MagicPen(mode) {
        if (this === global) {
            return new MagicPen(mode);
        }

        this.indentationLevel = 0;
        this.output = [];
        this.styles = {};

        this.mode = mode || 'text';
        this.serializer = new MagicPen.serializers[this.mode]();
    }

    MagicPen.prototype.newline = MagicPen.prototype.nl = function (count) {
        if (typeof count === 'undefined') {
            count = 1;
        }

        for (var i = 0; i < count; i += 1) {
            this.output.push([]);
        }
        return this;
    };

    MagicPen.serializers = {
        text: TextSerializer,
        ansi: AnsiSerializer,
        html: HtmlSerializer
    };

    MagicPen.prototype.write = function () {
        if (arguments.length === 1 && isOutputEntry(arguments[0])) {
            var options = arguments[0];
            if (this.styles[options.style]) {
                this.styles[options.style].apply(this, options.args);
                return this;
            }
            this.output[0] = this.output[0] || [];
            this.output[this.output.length - 1].push(options);
            return this;
        } else {
            var args = Array.prototype.slice.call(arguments, 1);
            return this.write({ style: arguments[0], args: args });
        }
    };

    MagicPen.prototype.indentLines = function () {
        this.indentationLevel += 1;
        return this;
    };

    MagicPen.prototype.indent = MagicPen.prototype.i = function () {
        for (var i = 0; i < this.indentationLevel; i += 1) {
            this.space(2);
        }
        return this;
    };

    MagicPen.prototype.outdentLines = function () {
        this.indentationLevel = Math.max(0, this.indentationLevel - 1);
        return this;
    };

    MagicPen.prototype.addStyle = function (style, handler) {
        if (this[style]) {
            throw new Error('"' + style + '" style is already defined');
        }
        this.styles[style] = handler;
        this[style] = function () {
            handler.apply(this, arguments);
            return this;
        };
        return this;
    };

    MagicPen.prototype.toString = function () {
        return this.serializer.serialize(this.output);
    };

    MagicPen.prototype.ensurePenWithSameMode = function (pen) {
        if (!(pen instanceof MagicPen) || pen.mode !== this.mode) {
            throw new Error('Expected an instance of a MagicPen in ' + this.mode + ' mode');
        }
    };

    MagicPen.prototype.text = function () {
        var args = Array.prototype.slice.call(arguments);
        return this.write({ style: 'text', args: args });
    };

    MagicPen.prototype.block = function (pen) {
        this.ensurePenWithSameMode(pen);
        return this.write('block', pen.toString());
    };

    MagicPen.prototype.append = function (pen) {
        this.ensurePenWithSameMode(pen);
        if (pen.output.length === 0) {
            return this;
        }

        this.output[0] = this.output[0] || [];
        this.output[0].push.apply(this.output[0], pen.output[0]);

        this.output.push.apply(this.output, pen.output.slice(1));

        return this;
    };

    MagicPen.prototype.prependLinesWith = function (pen) {
        this.ensurePenWithSameMode(pen);
        if (pen.output.length === 0 || this.output.length === 0) {
            return this;
        }

        if (pen.output.length > 1) {
            throw new Error('PrependLinesWith only supports a pen with single line content');
        }

        var outputToPrepend = [].concat(pen.output[0]);

        this.output = map(this.output, function (line) {
            return outputToPrepend.concat(line);
        });

        return this;
    };

    MagicPen.prototype.space = MagicPen.prototype.sp = function (count) {
        if (count === 0) {
            return this;
        }

        if (typeof count === 'undefined') {
            count = 1;
        }

        this.text(duplicateText(' ', count));
        return this;
    };

    forEach([
        'bold', 'dim', 'italic', 'underline', 'inverse', 'hidden',
        'strikeThrough', 'black', 'red', 'green', 'yellow', 'blue',
        'magenta', 'cyan', 'white', 'gray', 'bgBlack', 'bgRed',
        'bgGreen', 'bgYellow', 'bgBlue', 'bgMagenta', 'bgCyan',
        'bgWhite'
    ], function (textStyle) {
        MagicPen.prototype[textStyle] = function (content) {
            return this.text.call(this, content, textStyle);
        };
    });

    MagicPen.prototype.clone = function () {
        function MagicPenClone() {}
        MagicPenClone.prototype = this;
        var clonedPen = new MagicPenClone();
        clonedPen.serializer = new MagicPen.serializers[this.mode]();
        clonedPen.styles = extend({}, this.styles);
        clonedPen.indentationLevel = 0;
        clonedPen.output = [];
        return clonedPen;
    };

    return MagicPen;
}));
<commit_msg>Handle empty output when appending<commit_after>(function (global, factory) {
    if (typeof exports === 'object') {
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        define(factory);
    } else {
        global.weknowhow = global.weknowhow || {};
        global.weknowhow.magicpen = factory();
    }
}(this, function () {
    var global = this;
    function getKeys(obj) {
        var result = [];

        for (var i in obj) {
            if (obj.hasOwnProperty(i)) {
                result.push(i);
            }
        }

        return result;
    }

    function forEach(arr, callback, that) {
        for (var i = 0, n = arr.length; i < n; i += 1)
            callback.call(that, arr[i], i, arr);
    }

    function map(arr, callback, that) {
        var result = [];
        for (var i = 0, n = arr.length; i < n; i += 1)
            result.push(callback.call(that, arr[i], i, arr));
        return result;
    }

    function filter(arr, callback, that) {
        var result = [];
        for (var i = 0, n = arr.length; i < n; i += 1)
            if (callback.call(that, arr[i], i, arr))
                result.push(arr[i]);
        return result;
    }

    function extend(target) {
        var sources = Array.prototype.slice.call(arguments, 1);
        forEach(sources, function (source) {
            forEach(getKeys(source), function (key) {
                target[key] = source[key];
            });
        });
        return target;
    }

    var requireStyles = ['lines', 'text', 'block'];

    function Serializer(styles) {
        forEach(requireStyles, function (style) {
            if (!styles[style]) {
                throw new Error("Required style '" + style + "' is missing");
            }
        });
        this.styles = styles;
    }

    Serializer.prototype.serialize = function (lines) {
        return this.serializeLines(lines);
    };

    Serializer.prototype.serializeLines = function (lines) {
        return this.styles.lines(map(lines, function (line) {
            return this.serializeLineContent(line);
        }, this));
    };

    Serializer.prototype.serializeLineContent = function (content) {
        return map(content, this.serializeEntry, this);
    };

    function isOutputEntry(obj) {
        return typeof obj === 'object' && 'style' in obj && 'args' in obj;
    }

    Serializer.prototype.serializeEntry = function (entry) {
        if (entry.style in this.styles) {
            return this.styles[entry.style].apply(this.styles, entry.args);
        } else {
            throw new Error('Unknown style: "' + entry.style + '"');
        }
    };

    function createSerializer(styles) {
        function CustomSerializer() {
        }
        CustomSerializer.prototype = new Serializer(styles);
        return CustomSerializer;
    }

    function duplicateText(content, times) {
        var result = '';
        for (var i = 0; i < times; i += 1) {
            result += content;
        }
        return result;
    }

    // copied from https://github.com/sindresorhus/ansi-regex
    // License https://raw.githubusercontent.com/sindresorhus/ansi-regex/master/license
    var ansiRegex = /\u001b\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]/g;

    var TextSerializer = createSerializer({
        text: function (content) {
            return content;
        },
        lines: function (lines) {
            function serializeLine(line) {
                var serializedLines = [''];

                forEach(line, function (inlineBlock, blockIndex) {
                    var blockLines = map(String(inlineBlock).split('\n'), function (serializedBlockLine) {
                        return {
                            content: serializedBlockLine,
                            length: serializedBlockLine.replace(ansiRegex, '').length
                        };
                    });
                    var longestBlockLine = 0;
                    forEach(blockLines, function (blockLine) {
                        longestBlockLine = Math.max(longestBlockLine, blockLine.length);
                    });

                    var blockStartIndex = serializedLines[0].replace(ansiRegex, '').length;
                    serializedLines[0] += blockLines[0].content;
                    if (blockLines.length > 1 && blockIndex < line.length - 1) {
                        serializedLines[0] += duplicateText(' ', longestBlockLine - blockLines[0].length);
                    }

                    forEach(blockLines.slice(1), function (blockLine, index) {
                        var lineIndex = index + 1;
                        serializedLines[lineIndex] = serializedLines[lineIndex] || '';
                        var padding = duplicateText(' ', blockStartIndex - serializedLines[lineIndex].replace(ansiRegex, '').length);
                        serializedLines[lineIndex] += padding + blockLine.content;
                    });
                });

                return serializedLines.join('\n');
            }

            return map(lines, serializeLine).join('\n');
        },
        block: function (content) {
            return content;
        }
    });

    var ansiStyles = (function () {
        // Copied from https://github.com/sindresorhus/ansi-styles/
        // License: raw.githubusercontent.com/sindresorhus/ansi-styles/master/license
        var styles = {};

        var codes = {
            bold: [1, 22], // 21 isn't widely supported and 22 does the same thing
            dim: [2, 22],
            italic: [3, 23],
            underline: [4, 24],
            inverse: [7, 27],
            hidden: [8, 28],
            strikeThrough: [9, 29],

            black: [30, 39],
            red: [31, 39],
            green: [32, 39],
            yellow: [33, 39],
            blue: [34, 39],
            magenta: [35, 39],
            cyan: [36, 39],
            white: [37, 39],
            gray: [90, 39],

            bgBlack: [40, 49],
            bgRed: [41, 49],
            bgGreen: [42, 49],
            bgYellow: [43, 49],
            bgBlue: [44, 49],
            bgMagenta: [45, 49],
            bgCyan: [46, 49],
            bgWhite: [47, 49]
        };

        Object.keys(codes).forEach(function (key) {
            var val = codes[key];
            var style = styles[key] = {};
            style.open = '\x1B[' + val[0] + 'm';
            style.close = '\x1B[' + val[1] + 'm';
        });

        return styles;
    }());

    var AnsiSerializer = createSerializer(extend({}, TextSerializer.prototype.styles, {
        text: function (content) {
            if (arguments.length > 1) {
                var stylesString = Array.prototype.slice.call(arguments, 1).join(',');
                var styles = stylesString.split(/\s*,\s*/);
                forEach(styles, function (style) {
                    if (ansiStyles[style]) {
                        content = ansiStyles[style].open + content + ansiStyles[style].close;
                    }
                });
            }

            return content;
        }
    }));

    var htmlStyles = {
        bold: 'font-weight: bold',
        dim: 'opacity: 0.7',
        italic: 'font-style: italic',
        underline: 'text-decoration: underline',
        inverse: '-webkit-filter: invert(%100); filter: invert(100%)',
        hidden: 'visibility: hidden',
        strikeThrough: 'text-decoration: line-through',

        black: 'color: black',
        red: 'color: red',
        green: 'color: green',
        yellow: 'color: yellow',
        blue: 'color: blue',
        magenta: 'color: magenta',
        cyan: 'color: cyan',
        white: 'color: white',
        gray: 'color: gray',

        bgBlack: 'background-color: black',
        bgRed: 'background-color: red',
        bgGreen: 'background-color: green',
        bgYellow: 'background-color: yellow',
        bgBlue: 'background-color: blue',
        bgMagenta: 'background-color: magenta',
        bgCyan: 'background-color: cyan',
        bgWhite: 'background-color: white'
    };

    var HtmlSerializer = createSerializer({
        text: function (content) {
            content = String(content)
                .replace(/&/g, '&')
                .replace(/ /g, ' ')
                .replace(/</g, '<')
                .replace(/>/g, '>')
                .replace(/"/g, '"');

            if (arguments.length > 1) {
                var stylesString = Array.prototype.slice.call(arguments, 1).join(',');
                var styles = filter(stylesString.split(/\s*,\s*/), function (styleName) {
                    return htmlStyles[styleName];
                });

                content = '' + content + '';
            }
            return content;
        },
        lines: function (lines) {
            return '\n' +
                map(lines, function (line) {
                    return '
' + line.join('') + '
';
                }).join('\n') + '\n' +
                '';
        },
        block: function (content) {
            return '
' + content + '
';
        }
    });

    function MagicPen(mode) {
        if (this === global) {
            return new MagicPen(mode);
        }

        this.indentationLevel = 0;
        this.output = [];
        this.styles = {};

        this.mode = mode || 'text';
        this.serializer = new MagicPen.serializers[this.mode]();
    }

    MagicPen.prototype.newline = MagicPen.prototype.nl = function (count) {
        if (typeof count === 'undefined') {
            count = 1;
        }

        if (count === 0) {
            return this;
        }

        this.output[0] = this.output[0] || [];

        for (var i = 0; i < count; i += 1) {
            this.output.push([]);
        }
        return this;
    };

    MagicPen.serializers = {
        text: TextSerializer,
        ansi: AnsiSerializer,
        html: HtmlSerializer
    };

    MagicPen.prototype.write = function () {
        if (arguments.length === 1 && isOutputEntry(arguments[0])) {
            var options = arguments[0];
            if (this.styles[options.style]) {
                this.styles[options.style].apply(this, options.args);
                return this;
            }
            this.output[0] = this.output[0] || [];
            this.output[this.output.length - 1].push(options);
            return this;
        } else {
            var args = Array.prototype.slice.call(arguments, 1);
            return this.write({ style: arguments[0], args: args });
        }
    };

    MagicPen.prototype.indentLines = function () {
        this.indentationLevel += 1;
        return this;
    };

    MagicPen.prototype.indent = MagicPen.prototype.i = function () {
        for (var i = 0; i < this.indentationLevel; i += 1) {
            this.space(2);
        }
        return this;
    };

    MagicPen.prototype.outdentLines = function () {
        this.indentationLevel = Math.max(0, this.indentationLevel - 1);
        return this;
    };

    MagicPen.prototype.addStyle = function (style, handler) {
        if (this[style]) {
            throw new Error('"' + style + '" style is already defined');
        }
        this.styles[style] = handler;
        this[style] = function () {
            handler.apply(this, arguments);
            return this;
        };
        return this;
    };

    MagicPen.prototype.toString = function () {
        return this.serializer.serialize(this.output);
    };

    MagicPen.prototype.ensurePenWithSameMode = function (pen) {
        if (!(pen instanceof MagicPen) || pen.mode !== this.mode) {
            throw new Error('Expected an instance of a MagicPen in ' + this.mode + ' mode');
        }
    };

    MagicPen.prototype.text = function () {
        var args = Array.prototype.slice.call(arguments);
        return this.write({ style: 'text', args: args });
    };

    MagicPen.prototype.block = function (pen) {
        this.ensurePenWithSameMode(pen);
        return this.write('block', pen.toString());
    };

    MagicPen.prototype.append = function (pen) {
        this.ensurePenWithSameMode(pen);
        if (pen.output.length === 0) {
            return this;
        }

        this.output[0] = this.output[0] || [];
        this.output[0].push.apply(this.output[0], pen.output[0]);

        this.output.push.apply(this.output, pen.output.slice(1));

        return this;
    };

    MagicPen.prototype.prependLinesWith = function (pen) {
        this.ensurePenWithSameMode(pen);
        if (pen.output.length === 0 || this.output.length === 0) {
            return this;
        }

        if (pen.output.length > 1) {
            throw new Error('PrependLinesWith only supports a pen with single line content');
        }

        var outputToPrepend = [].concat(pen.output[0]);

        this.output = map(this.output, function (line) {
            return outputToPrepend.concat(line);
        });

        return this;
    };

    MagicPen.prototype.space = MagicPen.prototype.sp = function (count) {
        if (count === 0) {
            return this;
        }

        if (typeof count === 'undefined') {
            count = 1;
        }

        this.text(duplicateText(' ', count));
        return this;
    };

    forEach([
        'bold', 'dim', 'italic', 'underline', 'inverse', 'hidden',
        'strikeThrough', 'black', 'red', 'green', 'yellow', 'blue',
        'magenta', 'cyan', 'white', 'gray', 'bgBlack', 'bgRed',
        'bgGreen', 'bgYellow', 'bgBlue', 'bgMagenta', 'bgCyan',
        'bgWhite'
    ], function (textStyle) {
        MagicPen.prototype[textStyle] = function (content) {
            return this.text.call(this, content, textStyle);
        };
    });

    MagicPen.prototype.clone = function () {
        function MagicPenClone() {}
        MagicPenClone.prototype = this;
        var clonedPen = new MagicPenClone();
        clonedPen.serializer = new MagicPen.serializers[this.mode]();
        clonedPen.styles = extend({}, this.styles);
        clonedPen.indentationLevel = 0;
        clonedPen.output = [];
        return clonedPen;
    };

    return MagicPen;
}));
<|endoftext|>