<commit_before>/*global $, SyntaxHighlighter, alert, clearTimeout, console, document, setTimeout, trym2, updateOrientation, window */

var trym2 = {
    offset: 0,
    waitingtime: 2500, // in ms.  Each time we poll for data and don't receive it, we wait longer.
    maxwaitingtime: 60 * 1000 * 10, // 10 minutes
    minwaitingtime: 100,
    lessonNr: 1,
    maxLesson: 1,
    timerobject: 0
};

trym2.scrollDown = function (area) {
    var mySize = $(area).val().length;
    $(area).scrollTop(mySize);
    return false;
    // Return false to cancel the default link action
};

/* get selected text, or current line, in the textarea #M2In */
trym2.getSelected = function (inputField) {
    var str = $(inputField).val(),
        start = $(inputField)[0].selectionStart,
        end = $(inputField)[0].selectionEnd,
        endPos;
    if (start === end) {
        // grab the current line
        start = 1 + str.lastIndexOf("\n", end - 1);
        endPos = str.indexOf("\n", start);
        if (endPos !== -1) {
            end = endPos;
        } else {
            end = str.length;
        }
    }
    return str.slice(start, end) + "\n";
};

trym2.checkForNewData = function () {
    $.post("getResults.php", 'offset=' + trym2.offset, function (data) {
        if (data !== "") {
            $("#M2Out").val($("#M2Out").val() + data);
            trym2.scrollDown("#M2Out");
            trym2.offset = trym2.offset + data.length;
            trym2.waitingtime = trym2.minwaitingtime;
        } else {
            trym2.waitingtime = 2 * trym2.waitingtime;
            if (trym2.waitingtime > trym2.maxwaitingtime) {
                trym2.waitingtime = trym2.maxwaitingtime;
            }
        }
        $("#waittime").text("waiting time: " + trym2.waitingtime);
        trym2.timerobject = setTimeout(trym2.checkForNewData, trym2.waitingtime);
    });
};

// return false on error
trym2.sendToM2 = function (myCommand) {
    clearTimeout(trym2.timerobject);
    trym2.waitingtime = trym2.minwaitingtime;
    $("#waittime").text("waiting time: " + trym2.waitingtime);
    trym2.timerobject = setTimeout(trym2.checkForNewData, trym2.waitingtime);
    $.post("sockets/M2Client.php",
        {
            cmd: myCommand
        },
        function (data) {
            console.log("Here is the data: " + data);
            if (data !== "0") {
                $("#M2Out").val($("#M2Out").val() + "Something Broke! HELP!");
                return false;
            }
        });
    return true;
};

trym2.sendOnEnterCallback = function (inputfield) {
    return function (e) {
        if (e.which === 13 && e.shiftKey) {
            e.preventDefault();
            // do not make a line break or remove selected text when sending
            trym2.sendToM2(">>SENDCOMMANDS<<\n" + trym2.getSelected(inputfield));
        }
    };
};

trym2.loadLesson = function (ell) {
    if (ell === 0) {
        $("#lesson").hide();
        $("#inputarea").show();
        $("#send").show();
        $("#pageIndex").hide();
    } else {
        $("#inputarea").hide();
        var lessonContent = $('[lessonid="' + ell + '"]').html();
        $("#send").hide();
        $("#pageIndex").text("Lesson " + trym2.lessonNr + "/" + trym2.maxLesson).show();
        $("#lesson").html(lessonContent).show();
    }
};

trym2.switchLesson = function (incr) {
    trym2.lessonNr = trym2.lessonNr + incr;
    if (trym2.lessonNr >= 0 && trym2.lessonNr <= trym2.maxLesson) {
        trym2.loadLesson(trym2.lessonNr);
    } else {
        trym2.lessonNr = trym2.lessonNr - incr;
        //alert("lesson with " + trym2.lessonNr + "." + incr + " not available");
    }
};

trym2.resetCallback = function () {
    if (!trym2.sendToM2(">>RESET<<")) {
        $("#M2Out").val($("#M2Out").val() + "Something Broke! HELP!");
    }
    $("#M2Out").val("");
};

trym2.sendCallback = function (inputField) {
    return function () {
        var str = trym2.getSelected(inputField);
        trym2.sendToM2(">>SENDCOMMANDS<<\n" + str);
        return false;
    };
};

trym2.helpScreen = function () {
    console.log("Display Help.");
    $("#help-dialog").dialog('open');
};

// input: filename with tutorial content
// return a list of lessons title, each wrapped in a div and link,
//
" + title + "

// attach a lesson ID
trym2.getLessonTitles = function (tutorialFile, callback) {
    var titles = "";
    $("#menuTutorial").load(tutorialFile, function () {
        var i = 1;
        $("#menuTutorial h4").each(function () {
            var title = $(this).text();
            titles = titles + "
" + title + "
";
            i = i + 1;
            //console.log("Title in m2.js: " + title);
        });
        //console.log("All titles: " + titles);
        callback(titles);
    });
};

$(document).ready(function () {
    $('.submenuItem').live("click", function () {
        var i = 1,
            lessonId = $(this).attr('lessonid');
        console.log("You clicked a submenuItem: " + $(this).html());
        trym2.lessonNr = parseInt(lessonId.match(/\d/g), 10);
        $("#tutorial").html($("#menuTutorial").html());
        $("#tutorial h4").each(function () {
            $(this).parent().attr('lessonid', i); // add an ID to every lesson div
            i = i + 1;
        });
        trym2.maxLesson = $('#tutorial .lesson').children().length;
        trym2.loadLesson(trym2.lessonNr);
    });

    $('#help-dialog').dialog({
        height: 340,
        width: 460,
        modal: true,
        autoOpen: false
    });
    $('#help').click(trym2.helpScreen);

    SyntaxHighlighter.all();

    trym2.checkForNewData(trym2.offset);

    $('#M2In').keypress(trym2.sendOnEnterCallback('#M2In'));
    $("#send").click(trym2.sendCallback('#M2In'));
    $("#reset").click(trym2.resetCallback);

    $("code").live("click", function () {
        $(this).effect("highlight", {color: 'red'}, 800);
        var code = $(this).html();
        $("#M2In").val($("#M2In").val() + "\n" + code);
        trym2.scrollDown("#M2In");
        trym2.sendToM2(">>SENDCOMMANDS<<\n" + code);
    });

    $("#inputarea").hide();
    $("#send").hide();
    $("#pageIndex").hide();

    $("#tutorial").html("

Get started by selecting a tutorial from the menu on the upper right corner or by using the Macaulay2 console. Have fun!

");
    trym2.loadLesson(trym2.lessonNr);
    trym2.maxLesson = $('.lesson').children().length;

    $("#next").click(function () {
        trym2.switchLesson(1);
    });
    $("#previous").click(function () {
        trym2.switchLesson(-1);
    });

    // swipe changed to swipeXXX to remove functionality for testing
    $(function () {
        $("#leftwindow").bind("swipeXXX", function (event, info) {
            if (info.direction === "left") {
                trym2.switchLesson(1);
            } else if (info.direction === "right") {
                trym2.switchLesson(-1);
            } else {
                alert("swiped: huh?");
            }
        });
    });

    $(function () {
        $("#extruderTop").buildMbExtruder({
            position: "top",
            width: 350,
            extruderOpacity: 1,
            onExtOpen: function () {},
            onExtContentLoad: function () {},
            onExtClose: function () {}
        });
    });

    //updateOrientation();
});

function updateOrientation() {
    var orient = "";
    switch (window.orientation) {
    case 0:
    case 180:
        orient = "show_portrait";
        break;
    case -90:
    case 90:
        orient = "show_landscape";
        break;
    default:
        orient = "show_landscape";
    }
    $("body").attr("class", orient);
    $("#rightwindow").attr('class', orient);
    $("#leftwindow").attr('class', orient);
}
/* Info on selected text:
 * jquery plugin: jquery-fieldselection.js
 * example use: http://laboratorium.0xab.cd/jquery/fieldselection/0.1.0/test.html
 * DOM: selectionStart, selectionEnd
 *  stackoverflow: search for selectionStart
 */
<commit_msg>fix bug with maxLesson, last and next button work again<commit_after>/*global $, SyntaxHighlighter, alert, clearTimeout, console, document, setTimeout, trym2, updateOrientation, window */

var trym2 = {
    offset: 0,
    waitingtime: 2500, // in ms.  Each time we poll for data and don't receive it, we wait longer.
    maxwaitingtime: 60 * 1000 * 10, // 10 minutes
    minwaitingtime: 100,
    lessonNr: 1,
    maxLesson: 1,
    timerobject: 0
};

trym2.scrollDown = function (area) {
    var mySize = $(area).val().length;
    $(area).scrollTop(mySize);
    return false;
    // Return false to cancel the default link action
};

/* get selected text, or current line, in the textarea #M2In */
trym2.getSelected = function (inputField) {
    var str = $(inputField).val(),
        start = $(inputField)[0].selectionStart,
        end = $(inputField)[0].selectionEnd,
        endPos;
    if (start === end) {
        // grab the current line
        start = 1 + str.lastIndexOf("\n", end - 1);
        endPos = str.indexOf("\n", start);
        if (endPos !== -1) {
            end = endPos;
        } else {
            end = str.length;
        }
    }
    return str.slice(start, end) + "\n";
};

trym2.checkForNewData = function () {
    $.post("getResults.php", 'offset=' + trym2.offset, function (data) {
        if (data !== "") {
            $("#M2Out").val($("#M2Out").val() + data);
            trym2.scrollDown("#M2Out");
            trym2.offset = trym2.offset + data.length;
            trym2.waitingtime = trym2.minwaitingtime;
        } else {
            trym2.waitingtime = 2 * trym2.waitingtime;
            if (trym2.waitingtime > trym2.maxwaitingtime) {
                trym2.waitingtime = trym2.maxwaitingtime;
            }
        }
        $("#waittime").text("waiting time: " + trym2.waitingtime);
        trym2.timerobject = setTimeout(trym2.checkForNewData, trym2.waitingtime);
    });
};

// return false on error
trym2.sendToM2 = function (myCommand) {
    clearTimeout(trym2.timerobject);
    trym2.waitingtime = trym2.minwaitingtime;
    $("#waittime").text("waiting time: " + trym2.waitingtime);
    trym2.timerobject = setTimeout(trym2.checkForNewData, trym2.waitingtime);
    $.post("sockets/M2Client.php",
        {
            cmd: myCommand
        },
        function (data) {
            console.log("Here is the data: " + data);
            if (data !== "0") {
                $("#M2Out").val($("#M2Out").val() + "Something Broke! HELP!");
                return false;
            }
        });
    return true;
};

trym2.sendOnEnterCallback = function (inputfield) {
    return function (e) {
        if (e.which === 13 && e.shiftKey) {
            e.preventDefault();
            // do not make a line break or remove selected text when sending
            trym2.sendToM2(">>SENDCOMMANDS<<\n" + trym2.getSelected(inputfield));
        }
    };
};

trym2.loadLesson = function (ell) {
    if (ell === 0) {
        $("#lesson").hide();
        $("#inputarea").show();
        $("#send").show();
        $("#pageIndex").hide();
    } else {
        $("#inputarea").hide();
        var lessonContent = $('[lessonid="' + ell + '"]').html();
        $("#send").hide();
        $("#pageIndex").text("Lesson " + trym2.lessonNr + "/" + trym2.maxLesson).show();
        $("#lesson").html(lessonContent).show();
    }
};

trym2.switchLesson = function (incr) {
    //console.log("Current lessonNr " + trym2.lessonNr);
    //console.log("maxlesson " + trym2.maxLesson);
    trym2.lessonNr = trym2.lessonNr + incr;
    if (trym2.lessonNr >= 0 && trym2.lessonNr <= trym2.maxLesson) {
        //console.log("Switch lesson");
        trym2.loadLesson(trym2.lessonNr);
    } else {
        trym2.lessonNr = trym2.lessonNr - incr;
        //alert("lesson with " + trym2.lessonNr + "." + incr + " not available");
    }
};

trym2.resetCallback = function () {
    if (!trym2.sendToM2(">>RESET<<")) {
        $("#M2Out").val($("#M2Out").val() + "Something Broke! HELP!");
    }
    $("#M2Out").val("");
};

trym2.sendCallback = function (inputField) {
    return function () {
        var str = trym2.getSelected(inputField);
        trym2.sendToM2(">>SENDCOMMANDS<<\n" + str);
        return false;
    };
};

trym2.helpScreen = function () {
    console.log("Display Help.");
    $("#help-dialog").dialog('open');
};

// input: filename with tutorial content
// return a list of lessons title, each wrapped in a div and link,
//
" + title + "

// attach a lesson ID
trym2.getLessonTitles = function (tutorialFile, callback) {
    var titles = "";
    $("#menuTutorial").load(tutorialFile, function () {
        var i = 1;
        $("#menuTutorial h4").each(function () {
            var title = $(this).text();
            titles = titles + "
" + title + "
";
            i = i + 1;
            //console.log("Title in m2.js: " + title);
        });
        //console.log("All titles: " + titles);
        callback(titles);
    });
};

$(document).ready(function () {
    $('.submenuItem').live("click", function () {
        var i = 1,
            lessonId = $(this).attr('lessonid');
        //console.log("You clicked a submenuItem: " + $(this).html());
        trym2.lessonNr = parseInt(lessonId.match(/\d/g), 10);
        $("#tutorial").html($("#menuTutorial").html());
        $("#tutorial h4").each(function () {
            $(this).parent().attr('lessonid', i); // add an ID to every lesson div
            i = i + 1;
        });
        trym2.maxLesson = i-1;
        trym2.loadLesson(trym2.lessonNr);
    });

    $('#help-dialog').dialog({
        height: 340,
        width: 460,
        modal: true,
        autoOpen: false
    });
    $('#help').click(trym2.helpScreen);

    SyntaxHighlighter.all();

    trym2.checkForNewData(trym2.offset);

    $('#M2In').keypress(trym2.sendOnEnterCallback('#M2In'));
    $("#send").click(trym2.sendCallback('#M2In'));
    $("#reset").click(trym2.resetCallback);

    $("code").live("click", function () {
        $(this).effect("highlight", {color: 'red'}, 800);
        var code = $(this).html();
        $("#M2In").val($("#M2In").val() + "\n" + code);
        trym2.scrollDown("#M2In");
        trym2.sendToM2(">>SENDCOMMANDS<<\n" + code);
    });

    $("#inputarea").hide();
    $("#send").hide();
    $("#pageIndex").hide();

    $("#tutorial").html("

Get started by selecting a tutorial from the menu on the upper right corner or by using the Macaulay2 console. Have fun!

");
    trym2.loadLesson(trym2.lessonNr);
    trym2.maxLesson = $('#tutorial').children().length;
    console.log("maxLesson: " + trym2.maxLesson);

    $("#next").click(function () {
        trym2.switchLesson(1);
    });
    $("#previous").click(function () {
        trym2.switchLesson(-1);
    });

    // swipe changed to swipeXXX to remove functionality for testing
    $(function () {
        $("#leftwindow").bind("swipeXXX", function (event, info) {
            if (info.direction === "left") {
                trym2.switchLesson(1);
            } else if (info.direction === "right") {
                trym2.switchLesson(-1);
            } else {
                alert("swiped: huh?");
            }
        });
    });

    $(function () {
        $("#extruderTop").buildMbExtruder({
            position: "top",
            width: 350,
            extruderOpacity: 1,
            onExtOpen: function () {},
            onExtContentLoad: function () {},
            onExtClose: function () {}
        });
    });

    //updateOrientation();
});

function updateOrientation() {
    var orient = "";
    switch (window.orientation) {
    case 0:
    case 180:
        orient = "show_portrait";
        break;
    case -90:
    case 90:
        orient = "show_landscape";
        break;
    default:
        orient = "show_landscape";
    }
    $("body").attr("class", orient);
    $("#rightwindow").attr('class', orient);
    $("#leftwindow").attr('class', orient);
}
/* Info on selected text:
 * jquery plugin: jquery-fieldselection.js
 * example use: http://laboratorium.0xab.cd/jquery/fieldselection/0.1.0/test.html
 * DOM: selectionStart, selectionEnd
 *  stackoverflow: search for selectionStart
 */
<|endoftext|>


<commit_before>/* jshint -W097 */// jshint strict:false
/*jslint node: true */
'use strict';

const adapterName = require('./package.json').name.split('.').pop();
const utils 	  = require('./lib/utils'); // Get common adapter utils
const tools 	  = require(utils.controllerDir + '/lib/tools.js');
const SocketIO    = require('./lib/socket');
const Web         = require('./lib/web');

let socket      = null;
let webServer   = null;

let objects     = {};
let states      = {};
let secret      = 'Zgfr56gFe87jJOM'; // Will be generated by first start
let adapter;

function startAdapter(options) {
    options = options || {};
	Object.assign(options, {
	    name:           adapterName, // adapter name
	    dirname:        __dirname,   // say own position
	    logTransporter: true,        // receive the logs
	    systemConfig:   true,
	    install:        callback => typeof callback === 'function' && callback()
	});

    adapter = new utils.Adapter(options);

    adapter.on('objectChange', (id, obj) => {
        if (obj) {
            //console.log('objectChange: ' + id);
            objects[id] = obj;

            if (id === 'system.repositories') {
                writeUpdateInfo();
            }
        } else {
            //console.log('objectDeleted: ' + id);
            if (objects[id]) {
                delete objects[id];
            }
        }

        // TODO Build in some threshold of messages
        if (socket) {
            socket.objectChange(id, obj);
        }
    });

    adapter.on('stateChange', (id, state) => {
        if (!state) {
            if (states[id]) {
                delete states[id];
            }
        } else {
            states[id] = state;
        }
        if (socket) {
            socket.stateChange(id, state);
        }
    });

    adapter.on('ready', () => {
        adapter.getForeignObject('system.config', (err, obj) => {
            if (!err && obj) {
                obj.native = obj.native || {};
                if (!obj.native.secret) {
                    require('crypto').randomBytes(24, (ex, buf) => {
                        adapter.config.secret = buf.toString('hex');
                        adapter.extendForeignObject('system.config', {native: {secret: adapter.config.secret}});
                        main();
                    });
                } else {
                    adapter.config.secret = obj.native.secret;
                    main();
                }
            } else {
                adapter.config.secret = secret;
                adapter.logger.error('Cannot find object system.config');
            }
        });
    });

    adapter.on('message', obj => {
        if (!obj || !obj.message) {
            return false;
        }

        if (socket) {
            socket.sendCommand(obj);
        }

        return true;
    });

    adapter.on('unload', callback => {
        if (socket) {
            // unsubscribe all
            socket.unsubscribeAll();
        }

        try {
            adapter.log.info('terminating http' + (adapter.config.secure ? 's' : '') + ' server on port ' + adapter.config.port);
            webServer.close();
            callback();
        } catch (e) {
            callback();
        }
    });

// obj = {message: msg, severity: level, from: this.namespace, ts: (new Date()).getTime()}
    adapter.on('log', obj => socket && socket.sendLog(obj));

    return adapter;
}

function createUpdateInfo() {
    // create connected object and state
    let updatesNumberObj = objects[adapter.namespace + '.info.updatesNumber'];

    if (!updatesNumberObj || !updatesNumberObj.common || updatesNumberObj.common.type !== 'number') {
        let obj = {
            _id:  'info.updatesNumber',
            type: 'state',
            common: {
                role:  'indicator.updates',
                name:  'Number of adapters to update',
                type:  'number',
                read:  true,
                write: false,
                def:   0
            },
            native: {}
        };

        adapter.setObject(obj._id, obj);
    }

    let updatesListObj = objects[adapter.namespace + '.info.updatesList'];

    if (!updatesListObj || !updatesListObj.common || updatesListObj.common.type !== 'string') {
        let obj = {
            _id:  'info.updatesList',
            type: 'state',
            common: {
                role:  'indicator.updates',
                name:  'List of adapters to update',
                type:  'string',
                read:  true,
                write: false,
                def:   ''
            },
            native: {}
        };

        adapter.setObject(obj._id, obj);
    }

    let newUpdatesObj = objects[adapter.namespace + '.info.newUpdates'];

    if (!newUpdatesObj || !newUpdatesObj.common || newUpdatesObj.common.type !== 'boolean') {
        let obj = {
            _id:  'info.newUpdates',
            type: 'state',
            common: {
                role:  'indicator.updates',
                name:  'Indicator if new adapter updates are available',
                type:  'boolean',
                read:  true,
                write: false,
                def:   false
            },
            native: {}
        };

        adapter.setObject(obj._id, obj);
    }

    let updatesJsonObj = objects[adapter.namespace + '.info.updatesJson'];

    if (!updatesJsonObj || !updatesJsonObj.common || updatesJsonObj.common.type !== 'string') {
        let obj = {
            _id:  'info.updatesJson',
            type: 'state',
            common: {
                role:  'indicator.updates',
                name:  'JSON string with adapter update information',
                type:  'string',
                read:  true,
                write: false,
                def:   '{}'
            },
            native: {}
        };

        adapter.setObject(obj._id, obj);
    }

    let lastUpdateCheckObj = objects[adapter.namespace + '.info.lastUpdateCheck'];

    if (!lastUpdateCheckObj || !lastUpdateCheckObj.common || lastUpdateCheckObj.common.type !== 'string') {
        let obj = {
            _id:  'info.lastUpdateCheck',
            type: 'state',
            common: {
                role:  'value.datetime',
                name:  'Timestamp of last update check',
                type:  'string',
                read:  true,
                write: false,
                def:   '{}'
            },
            native: {}
        };

        adapter.setObject(obj._id, obj);
    }
}

// Helper methods
function upToDate(a, b) {
    a = a.split('.');
    b = b.split('.');
    a[0] = parseInt(a[0], 10);
    b[0] = parseInt(b[0], 10);
    if (a[0] > b[0]) {
        return false;
    } else if (a[0] < b[0]) {
        return true;
    } else if (a[0] === b[0]) {
        a[1] = parseInt(a[1], 10);
        b[1] = parseInt(b[1], 10);
        if (a[1] > b[1]) {
            return false;
        } else if (a[1] < b[1]) {
            return true;
        } else if (a[1] === b[1]) {
            a[2] = parseInt(a[2], 10);
            b[2] = parseInt(b[2], 10);
            return a[2] <= b[2];
        }
    } else {
        return true;
    }
}

function writeUpdateInfo(sources) {
    if (!sources) {
        let obj = objects['system.repositories'];
        if (!objects['system.config'] || !objects['system.config'].common) {
            adapter.log.warn('Repository cannot be read. Invalid "system.config" object.');
            return;
        }

        const activeRepo = objects['system.config'].common.activeRepo;

        if (obj && obj.native && obj.native.repositories && obj.native.repositories[activeRepo] &&
            obj.native.repositories[activeRepo].json) {
            sources = obj.native.repositories[activeRepo].json;
        } else {
            adapter.setState('info.updatesNumber', 0, true);
            adapter.setState('info.updatesList',  '', true);
            adapter.setState('info.newUpdates', false, true);
            adapter.setState('info.updatesJson', '{}', true);
            let updateTime = new Date();
            adapter.setState('info.lastUpdateCheck', new Date(updateTime - updateTime.getTimezoneOffset() * 60000).toISOString(), true);
            if (obj && obj.native && obj.native.repositories && obj.native.repositories[activeRepo]) {
                adapter.log.warn('Repository cannot be read');
            } else {
                adapter.log.warn('No repository source configured');
            }
            return;
        }
    }

    let installed = tools.getInstalledInfo();
    let list  = [];
    let updatesJson = {};
    let newUpdateIndicator = false;
    adapter.getState('info.updatesJson', (err, state) => {
        let oldUpdates;
        if (state && state.val) oldUpdates = JSON.parse(state.val) || {};
        else oldUpdates = {};
        for (let name in sources) {
            if (!sources.hasOwnProperty(name)) continue;
            if (installed[name] && installed[name].version && sources[name].version) {
                if (sources[name].version !== installed[name].version &&
                    !upToDate(sources[name].version, installed[name].version)) {
                    // Check if updates are new or already known to user
                    if (!oldUpdates || !oldUpdates[name] || oldUpdates[name].availableVersion !== sources[name].version) {
                        newUpdateIndicator = true;
                    } // endIf
                    updatesJson[name] = {
                        availableVersion: sources[name].version,
                        installedVersion: installed[name].version
                    };
                    // remove first part of the name
                    const n = name.indexOf('.');
                    list.push(n === -1 ? name : name.substring(n + 1));
                }
            }
        }
        adapter.setState('info.updatesNumber', list.length, true);
        adapter.setState('info.updatesList', list.join(', '), true);
        adapter.setState('info.newUpdates', newUpdateIndicator, true);
        adapter.setState('info.updatesJson', JSON.stringify(updatesJson), true);
        let updateTime = new Date();
        adapter.setState('info.lastUpdateCheck', new Date(updateTime - updateTime.getTimezoneOffset() * 60000).toISOString(), true);
    });

}

// to do => remove it later, when all repositories patched.
function patchRepos(callback) {
    return callback && callback();
    // do not patch any more. Delete it later 2018.04.23
    /*
    adapter.getForeignObject('system.repositories', (err, obj) => {
        let changed = false;
        if (obj && obj.native && obj.native.repositories) {
            // default link should point to stable
            if (!obj.native.repositories.default || obj.native.repositories.default.link !== 'http://download.iobroker.net/sources-dist.json') {
                changed = true;
                obj.native.repositories.default = {
                    link: 'http://download.iobroker.net/sources-dist.json'
                };
            }
            // latest link should point to latest
            if (!obj.native.repositories.latest) {
                obj.native.repositories.latest = {
                    link: 'http://download.iobroker.net/sources-dist-latest.json'
                };
                changed = true;
            }

            // change URL of raw sources from ioBroker.js-controller to ioBroker.repositories
            for (let r in obj.native.repositories) {
                if (obj.native.repositories.hasOwnProperty(r) &&
                    obj.native.repositories[r].link === 'https://raw.githubusercontent.com/ioBroker/ioBroker.js-controller/master/conf/sources-dist.json') {
                    obj.native.repositories[r].link = 'https://raw.githubusercontent.com/ioBroker/ioBroker.repositories/master/sources-dist.json';
                    changed = true;
                }
            }
        }
        if (changed) {
            adapter.setForeignObject(obj._id, obj, function () {
                callback && callback();
            });
        } else {
            callback && callback();
        }
    });*/
}
function initSocket(server, store) {
    socket = new SocketIO(server, adapter.config, adapter, objects, states, store);
    socket.subscribe(null, 'objectChange', '*');
}

function main() {
    // adapter.subscribeForeignStates('*');
    // adapter.subscribeForeignObjects('*');

    adapter.config.defaultUser = adapter.config.defaultUser || 'admin';
    if (!adapter.config.defaultUser.match(/^system\.user\./)) {
        adapter.config.defaultUser = 'system.user.' + adapter.config.defaultUser;
    }

    if (adapter.config.secure) {
        // Load certificates
        adapter.getCertificates((err, certificates, leConfig) => {
            adapter.config.certificates = certificates;
            adapter.config.leConfig     = leConfig;

            getData(() => webServer = new Web(adapter.config, adapter, initSocket));
        });
    } else {
        getData(() => webServer = new Web(adapter.config, adapter, initSocket));
    }

    patchRepos(() => {
        // By default update repository every 24 hours
        if (adapter.config.autoUpdate === undefined) {
            adapter.config.autoUpdate = 24;
        }
        adapter.config.autoUpdate = parseInt(adapter.config.autoUpdate, 10) || 0;
        if (adapter.config.autoUpdate) {
            setInterval(() => updateRegister(), adapter.config.autoUpdate * 3600000);
            updateRegister();
        }
    });
}

function getData(callback) {
    adapter.log.info('requesting all states');
    let tasks = 0;
    tasks++;
    adapter.getForeignStates('*', (err, res) => {
        adapter.log.info('received all states');
        states = res;
        if (!--tasks && callback) callback();
    });
    adapter.log.info('requesting all objects');
    tasks++;
    adapter.objects.getObjectList({include_docs: true}, (err, res) => {
        adapter.log.info('received all objects');
        res = res.rows;
        objects = {};
        let tmpPath = '';
        for (let i = 0; i < res.length; i++) {
            objects[res[i].doc._id] = res[i].doc;
            if (res[i].doc.type === 'instance' && res[i].doc.common && res[i].doc.common.tmpPath) {
                if (tmpPath) {
                    adapter.log.warn('tmpPath has multiple definitions!!');
                }
                tmpPath = res[i].doc.common.tmpPath;
            }
        }

        // Some adapters want access on specified tmp directory
        if (tmpPath) {
            adapter.config.tmpPath = tmpPath;
            adapter.config.tmpPathAllow = true;
        }

        createUpdateInfo();
        writeUpdateInfo();
        if (!--tasks && callback) callback();
    });
}

// read repository information from active repository
function updateRegister() {
    adapter.log.info('Request actual repository...');
    adapter.getForeignObject('system.config', (err, data) => {
        if (data && data.common) {

            adapter.sendToHost(adapter.host, 'getRepository', {
                repo:   data.common.activeRepo,
                update: true
            }, _repository => {
                if (_repository === 'permissionError') {
                    adapter.log.error('May not read "getRepository"');
                } else {
                    adapter.log.info('Repository received successfully.');

                    if (socket) {
                        socket.repoUpdated();
                    }
                }
            });
        }
    });
}

// If started as allInOne mode => return function to create instance
if (typeof module !== undefined && module.parent) {
    module.exports = startAdapter;
} else {
    // or start the instance directly
    startAdapter();
}<commit_msg>fix check for compact mode<commit_after>/* jshint -W097 */// jshint strict:false
/*jslint node: true */
'use strict';

const adapterName = require('./package.json').name.split('.').pop();
const utils 	  = require('./lib/utils'); // Get common adapter utils
const tools 	  = require(utils.controllerDir + '/lib/tools.js');
const SocketIO    = require('./lib/socket');
const Web         = require('./lib/web');

let socket      = null;
let webServer   = null;

let objects     = {};
let states      = {};
let secret      = 'Zgfr56gFe87jJOM'; // Will be generated by first start
let adapter;

function startAdapter(options) {
    options = options || {};
	Object.assign(options, {
	    name:           adapterName, // adapter name
	    dirname:        __dirname,   // say own position
	    logTransporter: true,        // receive the logs
	    systemConfig:   true,
	    install:        callback => typeof callback === 'function' && callback()
	});

    adapter = new utils.Adapter(options);

    adapter.on('objectChange', (id, obj) => {
        if (obj) {
            //console.log('objectChange: ' + id);
            objects[id] = obj;

            if (id === 'system.repositories') {
                writeUpdateInfo();
            }
        } else {
            //console.log('objectDeleted: ' + id);
            if (objects[id]) {
                delete objects[id];
            }
        }

        // TODO Build in some threshold of messages
        if (socket) {
            socket.objectChange(id, obj);
        }
    });

    adapter.on('stateChange', (id, state) => {
        if (!state) {
            if (states[id]) {
                delete states[id];
            }
        } else {
            states[id] = state;
        }
        if (socket) {
            socket.stateChange(id, state);
        }
    });

    adapter.on('ready', () => {
        adapter.getForeignObject('system.config', (err, obj) => {
            if (!err && obj) {
                obj.native = obj.native || {};
                if (!obj.native.secret) {
                    require('crypto').randomBytes(24, (ex, buf) => {
                        adapter.config.secret = buf.toString('hex');
                        adapter.extendForeignObject('system.config', {native: {secret: adapter.config.secret}});
                        main();
                    });
                } else {
                    adapter.config.secret = obj.native.secret;
                    main();
                }
            } else {
                adapter.config.secret = secret;
                adapter.logger.error('Cannot find object system.config');
            }
        });
    });

    adapter.on('message', obj => {
        if (!obj || !obj.message) {
            return false;
        }

        if (socket) {
            socket.sendCommand(obj);
        }

        return true;
    });

    adapter.on('unload', callback => {
        if (socket) {
            // unsubscribe all
            socket.unsubscribeAll();
        }

        try {
            adapter.log.info('terminating http' + (adapter.config.secure ? 's' : '') + ' server on port ' + adapter.config.port);
            webServer.close();
            callback();
        } catch (e) {
            callback();
        }
    });

// obj = {message: msg, severity: level, from: this.namespace, ts: (new Date()).getTime()}
    adapter.on('log', obj => socket && socket.sendLog(obj));

    return adapter;
}

function createUpdateInfo() {
    // create connected object and state
    let updatesNumberObj = objects[adapter.namespace + '.info.updatesNumber'];

    if (!updatesNumberObj || !updatesNumberObj.common || updatesNumberObj.common.type !== 'number') {
        let obj = {
            _id:  'info.updatesNumber',
            type: 'state',
            common: {
                role:  'indicator.updates',
                name:  'Number of adapters to update',
                type:  'number',
                read:  true,
                write: false,
                def:   0
            },
            native: {}
        };

        adapter.setObject(obj._id, obj);
    }

    let updatesListObj = objects[adapter.namespace + '.info.updatesList'];

    if (!updatesListObj || !updatesListObj.common || updatesListObj.common.type !== 'string') {
        let obj = {
            _id:  'info.updatesList',
            type: 'state',
            common: {
                role:  'indicator.updates',
                name:  'List of adapters to update',
                type:  'string',
                read:  true,
                write: false,
                def:   ''
            },
            native: {}
        };

        adapter.setObject(obj._id, obj);
    }

    let newUpdatesObj = objects[adapter.namespace + '.info.newUpdates'];

    if (!newUpdatesObj || !newUpdatesObj.common || newUpdatesObj.common.type !== 'boolean') {
        let obj = {
            _id:  'info.newUpdates',
            type: 'state',
            common: {
                role:  'indicator.updates',
                name:  'Indicator if new adapter updates are available',
                type:  'boolean',
                read:  true,
                write: false,
                def:   false
            },
            native: {}
        };

        adapter.setObject(obj._id, obj);
    }

    let updatesJsonObj = objects[adapter.namespace + '.info.updatesJson'];

    if (!updatesJsonObj || !updatesJsonObj.common || updatesJsonObj.common.type !== 'string') {
        let obj = {
            _id:  'info.updatesJson',
            type: 'state',
            common: {
                role:  'indicator.updates',
                name:  'JSON string with adapter update information',
                type:  'string',
                read:  true,
                write: false,
                def:   '{}'
            },
            native: {}
        };

        adapter.setObject(obj._id, obj);
    }

    let lastUpdateCheckObj = objects[adapter.namespace + '.info.lastUpdateCheck'];

    if (!lastUpdateCheckObj || !lastUpdateCheckObj.common || lastUpdateCheckObj.common.type !== 'string') {
        let obj = {
            _id:  'info.lastUpdateCheck',
            type: 'state',
            common: {
                role:  'value.datetime',
                name:  'Timestamp of last update check',
                type:  'string',
                read:  true,
                write: false,
                def:   '{}'
            },
            native: {}
        };

        adapter.setObject(obj._id, obj);
    }
}

// Helper methods
function upToDate(a, b) {
    a = a.split('.');
    b = b.split('.');
    a[0] = parseInt(a[0], 10);
    b[0] = parseInt(b[0], 10);
    if (a[0] > b[0]) {
        return false;
    } else if (a[0] < b[0]) {
        return true;
    } else if (a[0] === b[0]) {
        a[1] = parseInt(a[1], 10);
        b[1] = parseInt(b[1], 10);
        if (a[1] > b[1]) {
            return false;
        } else if (a[1] < b[1]) {
            return true;
        } else if (a[1] === b[1]) {
            a[2] = parseInt(a[2], 10);
            b[2] = parseInt(b[2], 10);
            return a[2] <= b[2];
        }
    } else {
        return true;
    }
}

function writeUpdateInfo(sources) {
    if (!sources) {
        let obj = objects['system.repositories'];
        if (!objects['system.config'] || !objects['system.config'].common) {
            adapter.log.warn('Repository cannot be read. Invalid "system.config" object.');
            return;
        }

        const activeRepo = objects['system.config'].common.activeRepo;

        if (obj && obj.native && obj.native.repositories && obj.native.repositories[activeRepo] &&
            obj.native.repositories[activeRepo].json) {
            sources = obj.native.repositories[activeRepo].json;
        } else {
            adapter.setState('info.updatesNumber', 0, true);
            adapter.setState('info.updatesList',  '', true);
            adapter.setState('info.newUpdates', false, true);
            adapter.setState('info.updatesJson', '{}', true);
            let updateTime = new Date();
            adapter.setState('info.lastUpdateCheck', new Date(updateTime - updateTime.getTimezoneOffset() * 60000).toISOString(), true);
            if (obj && obj.native && obj.native.repositories && obj.native.repositories[activeRepo]) {
                adapter.log.warn('Repository cannot be read');
            } else {
                adapter.log.warn('No repository source configured');
            }
            return;
        }
    }

    let installed = tools.getInstalledInfo();
    let list  = [];
    let updatesJson = {};
    let newUpdateIndicator = false;
    adapter.getState('info.updatesJson', (err, state) => {
        let oldUpdates;
        if (state && state.val) oldUpdates = JSON.parse(state.val) || {};
        else oldUpdates = {};
        for (let name in sources) {
            if (!sources.hasOwnProperty(name)) continue;
            if (installed[name] && installed[name].version && sources[name].version) {
                if (sources[name].version !== installed[name].version &&
                    !upToDate(sources[name].version, installed[name].version)) {
                    // Check if updates are new or already known to user
                    if (!oldUpdates || !oldUpdates[name] || oldUpdates[name].availableVersion !== sources[name].version) {
                        newUpdateIndicator = true;
                    } // endIf
                    updatesJson[name] = {
                        availableVersion: sources[name].version,
                        installedVersion: installed[name].version
                    };
                    // remove first part of the name
                    const n = name.indexOf('.');
                    list.push(n === -1 ? name : name.substring(n + 1));
                }
            }
        }
        adapter.setState('info.updatesNumber', list.length, true);
        adapter.setState('info.updatesList', list.join(', '), true);
        adapter.setState('info.newUpdates', newUpdateIndicator, true);
        adapter.setState('info.updatesJson', JSON.stringify(updatesJson), true);
        let updateTime = new Date();
        adapter.setState('info.lastUpdateCheck', new Date(updateTime - updateTime.getTimezoneOffset() * 60000).toISOString(), true);
    });

}

// to do => remove it later, when all repositories patched.
function patchRepos(callback) {
    return callback && callback();
    // do not patch any more. Delete it later 2018.04.23
    /*
    adapter.getForeignObject('system.repositories', (err, obj) => {
        let changed = false;
        if (obj && obj.native && obj.native.repositories) {
            // default link should point to stable
            if (!obj.native.repositories.default || obj.native.repositories.default.link !== 'http://download.iobroker.net/sources-dist.json') {
                changed = true;
                obj.native.repositories.default = {
                    link: 'http://download.iobroker.net/sources-dist.json'
                };
            }
            // latest link should point to latest
            if (!obj.native.repositories.latest) {
                obj.native.repositories.latest = {
                    link: 'http://download.iobroker.net/sources-dist-latest.json'
                };
                changed = true;
            }

            // change URL of raw sources from ioBroker.js-controller to ioBroker.repositories
            for (let r in obj.native.repositories) {
                if (obj.native.repositories.hasOwnProperty(r) &&
                    obj.native.repositories[r].link === 'https://raw.githubusercontent.com/ioBroker/ioBroker.js-controller/master/conf/sources-dist.json') {
                    obj.native.repositories[r].link = 'https://raw.githubusercontent.com/ioBroker/ioBroker.repositories/master/sources-dist.json';
                    changed = true;
                }
            }
        }
        if (changed) {
            adapter.setForeignObject(obj._id, obj, function () {
                callback && callback();
            });
        } else {
            callback && callback();
        }
    });*/
}
function initSocket(server, store) {
    socket = new SocketIO(server, adapter.config, adapter, objects, states, store);
    socket.subscribe(null, 'objectChange', '*');
}

function main() {
    // adapter.subscribeForeignStates('*');
    // adapter.subscribeForeignObjects('*');

    adapter.config.defaultUser = adapter.config.defaultUser || 'admin';
    if (!adapter.config.defaultUser.match(/^system\.user\./)) {
        adapter.config.defaultUser = 'system.user.' + adapter.config.defaultUser;
    }

    if (adapter.config.secure) {
        // Load certificates
        adapter.getCertificates((err, certificates, leConfig) => {
            adapter.config.certificates = certificates;
            adapter.config.leConfig     = leConfig;

            getData(() => webServer = new Web(adapter.config, adapter, initSocket));
        });
    } else {
        getData(() => webServer = new Web(adapter.config, adapter, initSocket));
    }

    patchRepos(() => {
        // By default update repository every 24 hours
        if (adapter.config.autoUpdate === undefined) {
            adapter.config.autoUpdate = 24;
        }
        adapter.config.autoUpdate = parseInt(adapter.config.autoUpdate, 10) || 0;
        if (adapter.config.autoUpdate) {
            setInterval(() => updateRegister(), adapter.config.autoUpdate * 3600000);
            updateRegister();
        }
    });
}

function getData(callback) {
    adapter.log.info('requesting all states');
    let tasks = 0;
    tasks++;
    adapter.getForeignStates('*', (err, res) => {
        adapter.log.info('received all states');
        states = res;
        if (!--tasks && callback) callback();
    });
    adapter.log.info('requesting all objects');
    tasks++;
    adapter.objects.getObjectList({include_docs: true}, (err, res) => {
        adapter.log.info('received all objects');
        res = res.rows;
        objects = {};
        let tmpPath = '';
        for (let i = 0; i < res.length; i++) {
            objects[res[i].doc._id] = res[i].doc;
            if (res[i].doc.type === 'instance' && res[i].doc.common && res[i].doc.common.tmpPath) {
                if (tmpPath) {
                    adapter.log.warn('tmpPath has multiple definitions!!');
                }
                tmpPath = res[i].doc.common.tmpPath;
            }
        }

        // Some adapters want access on specified tmp directory
        if (tmpPath) {
            adapter.config.tmpPath = tmpPath;
            adapter.config.tmpPathAllow = true;
        }

        createUpdateInfo();
        writeUpdateInfo();
        if (!--tasks && callback) callback();
    });
}

// read repository information from active repository
function updateRegister() {
    adapter.log.info('Request actual repository...');
    adapter.getForeignObject('system.config', (err, data) => {
        if (data && data.common) {

            adapter.sendToHost(adapter.host, 'getRepository', {
                repo:   data.common.activeRepo,
                update: true
            }, _repository => {
                if (_repository === 'permissionError') {
                    adapter.log.error('May not read "getRepository"');
                } else {
                    adapter.log.info('Repository received successfully.');

                    if (socket) {
                        socket.repoUpdated();
                    }
                }
            });
        }
    });
}

// If started as allInOne mode => return function to create instance
if (module && module.parent) {
    module.exports = startAdapter;
} else {
    // or start the instance directly
    startAdapter();
}
<|endoftext|>


<commit_before>(function () {
    'use strict';

    const classes = {
        hidden: 'gcf-hidden-line',
        sideways: 'gcf-sideways',
        collapser: 'gcf-collapser',
        ellipsis: 'gcf-ellipsis',
        blockStart: 'gcf-block-start',
        previouslyCollapsed: 'gcf-stay-hidden',
    };

    // Clear old classes and attributes from previous page loads
    document.querySelectorAll('.' + classes.collapser).forEach((arrow) => {
        arrow.parentElement.removeChild(arrow);
    });

    document.querySelectorAll('.' + classes.ellipsis).forEach((el) => {
        el.parentElement.removeChild(el);
    });

    document.querySelectorAll(`[${classes.previouslyCollapsed}]`).forEach((el) => {
        el.removeAttribute(`[${classes.previouslyCollapsed}]`);
    });

    const codeLines = [...document.querySelectorAll('table.js-file-line-container tr .blob-code-inner')];
    const codeLinesText = codeLines.map((l) => l.textContent);

    const _arrow =
        '<svg version="1.1" width="7px" fill="#969896" xmlns="http://www.w3.org/2000/svg" ' +
        'xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">' +
        '<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>' +
        '<g><path d="M579.5,879.8c-43.7,75.7-115.3,75.7-159,0L28.7,201.1c-43.7-75.7-8-137.7,79.5-137.7h783.7c87.5,0,123.2,62,79.5,137.7L579.5,879.8z"></path></g>' +
        '</svg>';

    class Element {
        constructor(name) {
            this.element = document.createElement(name);
        }
        addClass(className) {
            this.element.classList.add(className);
            return this;
        }
        setId(id) {
            this.element.id = id;
            return this;
        }
        setHTML(str) {
            this.element.innerHTML = str;
            return this;
        }
    }

    const arrowFactory = (id) => {
        return new Element('span').addClass(classes.collapser).setId(id).setHTML(_arrow).element;
    };

    const ellipsisFactory = (id) => {
        return new Element('span').addClass('pl-smi').addClass(classes.ellipsis).setId(id).setHTML('...').element;
    };

    const spaceMap = new Map();
    const pairs = new Map();
    const stack = [];
    const blockStarts = [];
    const countLeadingWhitespace = (arr) => {
        const getWhitespaceIndex = (i) => {
            if (arr[i] !== ' ' && arr[i] !== '\t') {
                return i;
            }
            i++;
            return getWhitespaceIndex(i);
        };
        return getWhitespaceIndex(0);
    };

    const last = (arr) => arr[arr.length - 1];
    const getPreviousSpaces = (map, lineNum) => {
        let prev = map.get(lineNum - 1);
        return prev === -1 ? getPreviousSpaces(map, lineNum - 1) : { lineNum: lineNum - 1, count: prev };
    };

    for (let lineNum = 0; lineNum < codeLinesText.length; lineNum++) {
        let line = codeLinesText[lineNum];
        let count = line.trim().length ? countLeadingWhitespace(line.split('')) : -1;
        spaceMap.set(lineNum, count);

        function tryPair() {
            let top = last(stack);
            if (count !== -1 && count <= spaceMap.get(top)) {
                pairs.set(top, lineNum);
                // codeLines[top].setAttribute(classes.blockStart, true);
                const arrow = arrowFactory(`gcf-${top + 1}`);
                codeLines[top].prepend(arrow);
                blockStarts.push(codeLines[top]);
                stack.pop();
                return tryPair();
            }
        }
        tryPair();

        let prevSpaces = getPreviousSpaces(spaceMap, lineNum);
        if (count > prevSpaces.count) {
            stack.push(prevSpaces.lineNum);
        }
    }

    const toggleCode = (action, start, end) => {
        if (action === 'hide') {
            const sliced = codeLines.slice(start, end);
            sliced.forEach((elem) => {
                const tr = elem.parentElement;

                // If a line was already hidden, there was an inner block that
                // was previously collapsed. Setting this attribute will
                // protect the inner block from being expanded
                // when this current outer block is expanded
                if (tr.classList.contains(classes.hidden)) {
                    tr.setAttribute(classes.previouslyCollapsed, true);
                }
                tr.classList.add(classes.hidden);
            });
            codeLines[start - 1].appendChild(ellipsisFactory(`ellipsis-${start - 1}`));
        } else if (action === 'show') {
            const sliced = codeLines.slice(start, end);
            const topLine = codeLines[start - 1];

            sliced.forEach((elem) => {
                const tr = elem.parentElement;
                if (!tr.getAttribute(classes.previouslyCollapsed)) {
                    tr.classList.remove(classes.hidden);
                } else {
                    tr.removeAttribute(classes.previouslyCollapsed);
                }
            });
            topLine.removeChild(topLine.lastChild);
        }
    };

    const arrows = document.querySelectorAll('.' + classes.collapser);
    function arrowListener(e) {
        e.preventDefault();
        let svg = e.currentTarget;
        let td = e.currentTarget.parentElement;
        let id = td.getAttribute('id');
        let index = parseInt(id.slice(2)) - 1;
        if (svg.classList.contains(classes.sideways)) {
            svg.classList.remove(classes.sideways);
            toggleCode('show', index + 1, pairs.get(index));
        } else {
            svg.classList.add(classes.sideways);
            toggleCode('hide', index + 1, pairs.get(index));
        }
    }

    arrows.forEach((c) => {
        c.addEventListener('click', arrowListener);
    });

    function ellipsisListener(e) {
        if (!e.target.parentElement) return;
        if (e.target.classList.contains(classes.ellipsis)) {
            let td = e.target.parentElement;
            let svg = td.querySelector('.' + classes.sideways);
            let id = e.target.parentElement.getAttribute('id');
            let index = parseInt(id.slice(2)) - 1;
            svg.classList.remove(classes.sideways);
            toggleCode('show', index + 1, pairs.get(index));
        }
    }

    blockStarts.forEach((line) => {
        line.addEventListener('click', ellipsisListener);
    });
})();
<commit_msg>bugfix<commit_after>(function () {
    'use strict';

    const classes = {
        hidden: 'gcf-hidden-line',
        sideways: 'gcf-sideways',
        collapser: 'gcf-collapser',
        ellipsis: 'gcf-ellipsis',
        blockStart: 'gcf-block-start',
        previouslyCollapsed: 'gcf-stay-hidden',
    };

    // Clear old classes and attributes from previous page loads
    document.querySelectorAll('.' + classes.collapser).forEach((arrow) => {
        arrow.parentElement.removeChild(arrow);
    });

    document.querySelectorAll('.' + classes.ellipsis).forEach((el) => {
        el.parentElement.removeChild(el);
    });

    document.querySelectorAll('.' + classes.hidden).forEach((el) => {
        el.classList.remove(classes.hidden);
    });

    document.querySelectorAll(`[${classes.previouslyCollapsed}]`).forEach((el) => {
        el.removeAttribute(classes.previouslyCollapsed);
    });

    const codeLines = [...document.querySelectorAll('table.js-file-line-container tr .blob-code-inner')];
    const codeLinesText = codeLines.map((l) => l.textContent);

    const _arrow =
        '<svg version="1.1" width="7px" fill="#969896" xmlns="http://www.w3.org/2000/svg" ' +
        'xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">' +
        '<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>' +
        '<g><path d="M579.5,879.8c-43.7,75.7-115.3,75.7-159,0L28.7,201.1c-43.7-75.7-8-137.7,79.5-137.7h783.7c87.5,0,123.2,62,79.5,137.7L579.5,879.8z"></path></g>' +
        '</svg>';

    class Element {
        constructor(name) {
            this.element = document.createElement(name);
        }
        addClass(className) {
            this.element.classList.add(className);
            return this;
        }
        setId(id) {
            this.element.id = id;
            return this;
        }
        setHTML(str) {
            this.element.innerHTML = str;
            return this;
        }
    }

    const arrowFactory = (id) => {
        return new Element('span').addClass(classes.collapser).setId(id).setHTML(_arrow).element;
    };

    const ellipsisFactory = (id) => {
        return new Element('span').addClass(classes.ellipsis).setId(id).setHTML('...').element;
    };

    const spaceMap = new Map();
    const pairs = new Map();
    const stack = [];
    const blockStarts = [];
    const countLeadingWhitespace = (arr) => {
        const getWhitespaceIndex = (i) => {
            if (arr[i] !== ' ' && arr[i] !== '\t') {
                return i;
            }
            i++;
            return getWhitespaceIndex(i);
        };
        return getWhitespaceIndex(0);
    };

    const last = (arr) => arr[arr.length - 1];
    const getPreviousSpaces = (map, lineNum) => {
        let prev = map.get(lineNum - 1);
        return prev === -1 ? getPreviousSpaces(map, lineNum - 1) : { lineNum: lineNum - 1, count: prev };
    };

    for (let lineNum = 0; lineNum < codeLinesText.length; lineNum++) {
        let line = codeLinesText[lineNum];
        let count = line.trim().length ? countLeadingWhitespace(line.split('')) : -1;
        spaceMap.set(lineNum, count);

        function tryPair() {
            let top = last(stack);
            if (count !== -1 && count <= spaceMap.get(top)) {
                pairs.set(top, lineNum);
                // codeLines[top].setAttribute(classes.blockStart, true);
                const arrow = arrowFactory(`gcf-${top + 1}`);
                codeLines[top].prepend(arrow);
                blockStarts.push(codeLines[top]);
                stack.pop();
                return tryPair();
            }
        }
        tryPair();

        let prevSpaces = getPreviousSpaces(spaceMap, lineNum);
        if (count > prevSpaces.count) {
            stack.push(prevSpaces.lineNum);
        }
    }

    const toggleCode = (action, start, end) => {
        if (action === 'hide') {
            const sliced = codeLines.slice(start, end);
            sliced.forEach((elem) => {
                const tr = elem.parentElement;

                // If a line was already hidden, there was an inner block that
                // was previously collapsed. Setting this attribute will
                // protect the inner block from being expanded
                // when this current outer block is expanded
                if (tr.classList.contains(classes.hidden)) {
                    tr.setAttribute(classes.previouslyCollapsed, true);
                }
                tr.classList.add(classes.hidden);
            });
            codeLines[start - 1].appendChild(ellipsisFactory(`ellipsis-${start - 1}`));
        } else if (action === 'show') {
            const sliced = codeLines.slice(start, end);
            const topLine = codeLines[start - 1];

            sliced.forEach((elem) => {
                const tr = elem.parentElement;
                if (!tr.getAttribute(classes.previouslyCollapsed)) {
                    tr.classList.remove(classes.hidden);
                } else {
                    tr.removeAttribute(classes.previouslyCollapsed);
                }
            });
            topLine.removeChild(topLine.lastChild);
        }
    };

    const arrows = document.querySelectorAll('.' + classes.collapser);
    function arrowListener(e) {
        e.preventDefault();
        let svg = e.currentTarget;
        let td = e.currentTarget.parentElement;
        let id = td.getAttribute('id');
        let index = parseInt(id.slice(2)) - 1;
        if (svg.classList.contains(classes.sideways)) {
            svg.classList.remove(classes.sideways);
            toggleCode('show', index + 1, pairs.get(index));
        } else {
            svg.classList.add(classes.sideways);
            toggleCode('hide', index + 1, pairs.get(index));
        }
    }

    arrows.forEach((c) => {
        c.addEventListener('click', arrowListener);
    });

    function ellipsisListener(e) {
        if (!e.target.parentElement) return;
        if (e.target.classList.contains(classes.ellipsis)) {
            let td = e.target.parentElement;
            let svg = td.querySelector('.' + classes.sideways);
            let id = e.target.parentElement.getAttribute('id');
            let index = parseInt(id.slice(2)) - 1;
            svg.classList.remove(classes.sideways);
            toggleCode('show', index + 1, pairs.get(index));
        }
    }

    blockStarts.forEach((line) => {
        line.addEventListener('click', ellipsisListener);
    });
})();
<|endoftext|>


<commit_before>module.exports = function (RED) {
   function HarmonySendCommand (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)
    node.activity = n.activity
    node.label = n.label
    node.command = n.command
    node.harmony_type = n.harmony_type
    node.repeat = Number.parseInt(n.repeat) || 1
    node.releasetimestamp = Number.parseInt(n.releasetimestamp) || 50
    node.presstimestamp = Number.parseInt(n.presstimestamp) || 0
    node.delay = Number.parseInt(n.delay) || 0
    if (!node.server) return

    var action = decodeURI(node.command)

    node.on('input', function (msg) {
      try {
        msg.payload = JSON.parse(msg.payload)
      } catch (err) {
        console.log('Error: ' + err)
      }

      if (!node.command || !node.server) {
        node.send({payload: false})
      } else {
        for (var i = 0; i < node.repeat; i++) {
          node.server.harmony.request('holdAction', 'action=' + action + ':status=press' + ':timestamp='+node.presstimestamp)
            .catch(function (err) {
              node.send({payload: false})
              if (err) throw err
            }).then(function (response) {
              setTimeout(function(){ node.server.harmony.request('holdAction', 'action=' + action + ':status=release' + ':timestamp='+node.releasetimestamp)
                 .catch(function (err) {
                  node.send({payload: false})
                  console.log('Error: ' + err)
              })
            }, node.delay);

            })
        }
        node.send({payload: true})
      }
    })
  }
  RED.nodes.registerType('H command', HarmonySendCommand)

  function HarmonySendPress (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)
    node.activity = n.activity
    node.label = n.label
    node.command = n.command
    node.harmony_type = n.harmony_type
    node.repeat = Number.parseInt(n.repeat) || 1
    node.timestamp = Number.parseInt(n.timestamp) || 0

    if (!node.server) return

    var action = decodeURI(node.command)

    node.on('input', function (msg) {
      try {
        msg.payload = JSON.parse(msg.payload)
      } catch (err) {
        console.log('Error: ' + err)
      }

      if (!node.command || !node.server) {
        node.send({payload: false})
      } else {
        for (var i = 0; i < node.repeat; i++) {
          node.server.harmony.request('holdAction', 'action=' + action + ':status=press' + ':timestamp='+node.timestamp)
            .catch(function (err) {
              node.send({payload: false})
              if (err) throw err
            }).then(function (response) {

            })
        }
        node.send({payload: true})
      }
    })
  }
  RED.nodes.registerType('H press', HarmonySendPress)

  function HarmonySendRelease (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)
    node.activity = n.activity
    node.label = n.label
    node.command = n.command
    node.harmony_type = n.harmony_type
    node.repeat = Number.parseInt(n.repeat) || 1
    node.timestamp = Number.parseInt(n.timestamp) || 0

    if (!node.server) return

    var action = decodeURI(node.command)

    node.on('input', function (msg) {
      try {
        msg.payload = JSON.parse(msg.payload)
      } catch (err) {
        console.log('Error: ' + err)
      }

      if (!node.command || !node.server) {
        node.send({payload: false})
      } else {
        for (var i = 0; i < node.repeat; i++) {
          node.server.harmony.request('holdAction', 'action=' + action + ':status=release' + ':timestamp='+node.timestamp)
            .catch(function (err) {
              node.send({payload: false})
              if (err) throw err
            }).then(function (response) {

            })
        }
        node.send({payload: true})
      }
    })
  }
  RED.nodes.registerType('H release', HarmonySendRelease)

  function HarmonySendHold (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)
    node.activity = n.activity
    node.label = n.label
    node.command = n.command
    node.harmony_type = n.harmony_type
    node.repeat = Number.parseInt(n.repeat) || 1
    node.timestamp = Number.parseInt(n.timestamp) || 0

    if (!node.server) return

    var action = decodeURI(node.command)

    node.on('input', function (msg) {
      try {
        msg.payload = JSON.parse(msg.payload)
      } catch (err) {
        console.log('Error: ' + err)
      }

      if (!node.command || !node.server) {
        node.send({payload: false})
      } else {
        for (var i = 0; i < node.repeat; i++) {
          node.server.harmony.request('holdAction', 'action=' + action + ':status=hold' + ':timestamp='+node.timestamp)
            .catch(function (err) {
              node.send({payload: false})
              if (err) throw err
            }).then(function (response) {

            })
        }
        node.send({payload: true})
      }
    })
  }
  RED.nodes.registerType('H hold', HarmonySendHold)

  function HarmonyActivity (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)
    node.activity = n.activity
    node.label = n.label

    if (!node.server) return

    node.on('input', function (msg) {
      try {
        msg.payload = JSON.parse(msg.payload)
      } catch (err) {
        console.log('Error: ' + err)
      }
      node.server.harmony.startActivity(node.activity)
        .catch(function (err) {
          node.send({payload: false})
          console.log('Error: ' + err)
        }).then(function (response) {
          node.send({payload: true})
        })
    })
  }
  RED.nodes.registerType('H activity', HarmonyActivity)

  function HarmonyGetActivity (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)

    if (!node.server) return

    node.on('input', function (msg) {
      try {
        msg.payload = JSON.parse(msg.payload)
      } catch (err) {
        console.log('Error: ' + err)
      }
      node.server.harmony.getCurrentActivity()
        .catch(function (err) {
          node.send({payload: false})
          console.log('Error: ' + err)
        }).then(function (response) {
          //console.log('Harmony: ' + response)
          node.send({payload: response})
        })
    })
  }

  RED.nodes.registerType('H GetActivity', HarmonyGetActivity)

  function HarmonyObserve (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)
    node.label = n.label

    if (!node.server) return

    setTimeout(function () {
      try {
        node.server.harmonyEventEmitter.on('stateDigest', function (digest) {
          try {
            node.send({
              payload: {
                activityId: digest.activityId,
                activityStatus: digest.activityStatus
              }
            })
          } catch (err) {
            console.log('Error: ' + err)
          }
        })
      } catch (err) {
        console.log('Error: ' + err)
      }
    }, 2000)
  }
  RED.nodes.registerType('H observe', HarmonyObserve)
}
<commit_msg>add better error handling and set a global to what was just done<commit_after>module.exports = function (RED) {
   function HarmonySendCommand (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)
    node.activity = n.activity
    node.label = n.label
    node.command = n.command
    node.harmony_type = n.harmony_type
    node.repeat = Number.parseInt(n.repeat) || 1
    node.releasetimestamp = Number.parseInt(n.releasetimestamp) || 50
    node.presstimestamp = Number.parseInt(n.presstimestamp) || 0
    node.delay = Number.parseInt(n.delay) || 0

    if (!node.server) {
      node.warn("HarmonySendCommand: no server");
      return
    }
    var action = decodeURI(node.command)

    node.on('input', function (msg) {
      var globalContext = this.context().global;

      if (!node.command ) {
        node.warn("HarmonySendCommand: missing command")
        node.send({payload: false})
      } else {
        globalContext.set("HarmonyFunction","Command");
        globalContext.set("HarmonyAction",action);
        for (var i = 0; i < node.repeat; i++) {
          node.server.harmony.request('holdAction', 'action=' + action + ':status=press' + ':timestamp='+node.presstimestamp)
            .catch(function (err) {
              node.send({payload: false})
              if (err) throw err
            }).then(function (response) {
              setTimeout(function(){ node.server.harmony.request('holdAction', 'action=' + action + ':status=release' + ':timestamp='+node.releasetimestamp)
                 .catch(function (err) {
                  node.send({payload: false})
                  console.log('Error: ' + err)
              })
            }, node.delay);

            })
        }
        node.send({payload: true})
      }
    })
  }
  RED.nodes.registerType('H command', HarmonySendCommand)

  function HarmonySendPress (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)
    node.activity = n.activity
    node.label = n.label
    node.command = n.command
    node.harmony_type = n.harmony_type
    node.repeat = Number.parseInt(n.repeat) || 1
    node.timestamp = Number.parseInt(n.timestamp) || 0

    if (!node.server) {
      node.warn("HarmonySendPress: no server");
      return
    }

    var action = decodeURI(node.command)

    node.on('input', function (msg) {
      var globalContext = this.context().global;
      if (!node.command || !node.server) {
        node.send({payload: false})
      } else {
        globalContext.set("HarmonyFunction","Press");
        globalContext.set("HarmonyAction",action);
        for (var i = 0; i < node.repeat; i++) {
          node.server.harmony.request('holdAction', 'action=' + action + ':status=press' + ':timestamp='+node.timestamp)
            .catch(function (err) {
              node.send({payload: false})
              if (err) throw err
            }).then(function (response) {

            })
        }
        node.send({payload: true})
      }
    })
  }
  RED.nodes.registerType('H press', HarmonySendPress)

  function HarmonySendRelease (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)
    node.activity = n.activity
    node.label = n.label
    node.command = n.command
    node.harmony_type = n.harmony_type
    node.repeat = Number.parseInt(n.repeat) || 1
    node.timestamp = Number.parseInt(n.timestamp) || 0

    if (!node.server) {
      node.warn("HarmonySendRelease: no server");
      return
    }

    var action = decodeURI(node.command)

    node.on('input', function (msg) {

      if (!node.command || !node.server) {
        node.send({payload: false})
      } else {
        var globalContext = this.context().global;
        globalContext.set("HarmonyFunction","Release");
        globalContext.set("HarmonyAction",action);
        for (var i = 0; i < node.repeat; i++) {
          node.server.harmony.request('holdAction', 'action=' + action + ':status=release' + ':timestamp='+node.timestamp)
            .catch(function (err) {
              node.send({payload: false})
              if (err) throw err
            }).then(function (response) {

            })
        }
        node.send({payload: true})
      }
    })
  }
  RED.nodes.registerType('H release', HarmonySendRelease)

  function HarmonySendHold (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)
    node.activity = n.activity
    node.label = n.label
    node.command = n.command
    node.harmony_type = n.harmony_type
    node.repeat = Number.parseInt(n.repeat) || 1
    node.timestamp = Number.parseInt(n.timestamp) || 0

    if (!node.server) {
      node.warn("HarmonyHold: no server");
      return
    }
    var action = decodeURI(node.command)

    node.on('input', function (msg) {

      if (!node.command || !node.server) {
        node.send({payload: false})
      } else {
        var globalContext = this.context().global;
        globalContext.set("HarmonyFunction","Hold");
        globalContext.set("HarmonyAction",action);
        for (var i = 0; i < node.repeat; i++) {
          node.server.harmony.request('holdAction', 'action=' + action + ':status=hold' + ':timestamp='+node.timestamp)
            .catch(function (err) {
              node.send({payload: false})
              if (err) throw err
            }).then(function (response) {

            })
        }
        node.send({payload: true})
      }
    })
  }
  RED.nodes.registerType('H hold', HarmonySendHold)

  function HarmonyActivity (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)
    node.activity = n.activity
    node.label = n.label

    if (!node.server) {
      node.warn("HarmonyActivity: no server");
      return
    }
    node.on('input', function (msg) {

      node.server.harmony.startActivity(node.activity)
        .catch(function (err) {
          node.send({payload: false})
          console.log('Error: ' + err)
        }).then(function (response) {
          node.send({payload: true})
        })
    })
  }
  RED.nodes.registerType('H activity', HarmonyActivity)

  function HarmonyGetActivity (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)

    if (!node.server) {
      node.warn("HarmonyGetActivity: no server");
      return
    }
    node.on('input', function (msg) {

      node.server.harmony.getCurrentActivity()
        .catch(function (err) {
          node.send({payload: false})
          console.log('Error: ' + err)
        }).then(function (response) {
          //console.log('Harmony: ' + response)
          node.send({payload: response})
        })
    })
  }

  RED.nodes.registerType('H GetActivity', HarmonyGetActivity)

  function HarmonyObserve (n) {
    RED.nodes.createNode(this, n)
    var node = this

    node.server = RED.nodes.getNode(n.server)
    node.label = n.label

    if (!node.server) {
      node.warn("HarmonyObserve: no server");
      return
    }

    setTimeout(function () {
      try {
        node.server.harmonyEventEmitter.on('stateDigest', function (digest) {
          try {
            node.send({
              payload: {
                activityId: digest.activityId,
                activityStatus: digest.activityStatus
              }
            })
          } catch (err) {
            console.log('Error: ' + err)
          }
        })
      } catch (err) {
        console.log('Error: ' + err)
      }
    }, 2000)
  }
  RED.nodes.registerType('H observe', HarmonyObserve)
}
<|endoftext|>


<commit_before>;(function(exports) {
  // creates lines and circles and starts the animation
  function start(canvas) {
    // make new circle every so often
    var circles = [];
    setInterval(function() {
      circles.push(makeCircle(canvas));
    }, 400);

    // make grid of lines
    var lines = [];
    for (var i = 1; i < 6; i++) {
      for (var j = 1; j < 6; j++) {
        lines.push(makeLine(i * 80, j * 80, i * j));
      }
    }
    lines.splice(10, 2); // throw away top center lines

    // start update/draw loop
    function tick() {
      update(circles, lines, canvas);
      draw(circles, lines, canvas);
      requestAnimationFrame(tick);
    };

    tick();
  };
  exports.start = start; // make start function available to HTML page

  // rotates the lines, moves and bounces the circles
  function update(circles, lines, canvas) {
    for (var i = circles.length - 1; i >= 0; i--) {
      for (var j = 0; j < lines.length; j++) {
        bounceCircle(circles[i], lines[j]);
      }

      moveCircle(circles[i]);
      if (!isCircleOnCanvas(circles[i], canvas)) {
        circles.splice(i, 1); // remove circles that have left screen
      }
    }

    for (var i = 0; i < lines.length; i++) {
      lines[i].angle += lines[i].rotateSpeed;
    }
  };

  function draw(circles, lines, canvas) {
    var ctx = canvas.getContext('2d');

    // fill screen with white
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // draw circles
    for (var i = 0; i < circles.length; i++) {
      drawCircle(circles[i], ctx);
    }

    // draw lines
    for (var i = 0; i < lines.length; i++) {
      drawLine(lines[i], ctx);
    }
  };

  function makeCircle(canvas) {
    var radius = 7;
    return {
      center: {
        x: canvas.width / 2,
        y: -radius,
      },
      velocity: { x: 0, y: 0 },
      radius: radius
    };
  };

  function makeLine(x, y) {
    return {
      center: { x: x, y: y },
      span: 40,
      angle: Math.random() * 360,
      rotateSpeed: 0.5
    };
  };

  function distance(point1, point2) {
    var x = Math.abs(point1.x - point2.x);
    var y = Math.abs(point1.y - point2.y);
    return Math.sqrt((x * x) + (y * y));
  };

  function magnitude(vector) {
    return Math.sqrt(vector.x * vector.x + vector.y* vector.y);
  };

  function unitVector(vector) {
    return {
      x: vector.x / magnitude(vector),
      y: vector.y / magnitude(vector)
    };
  };

  function dotProduct(vector1, vector2) {
    return vector1.x * vector2.x + vector1.y * vector2.y;
  };

  // returns the points at the two ends of the passed line
  function lineEnds(line) {
    var angleRadians = line.angle * 0.01745;
    var lineVector = unitVector({
      x: Math.cos(angleRadians) * 0 - Math.sin(angleRadians) * -1,
      y: Math.sin(angleRadians) * 0 + Math.cos(angleRadians) * -1
    });

    return [{
      x: line.center.x + lineVector.x * line.span / 2,
      y: line.center.y + lineVector.y * line.span / 2
    }, {
      x: line.center.x - lineVector.x * line.span / 2,
      y: line.center.y - lineVector.y * line.span / 2
    }];
  };

  function drawLine(line, ctx) {
    var end1 = lineEnds(line)[0];
    var end2 = lineEnds(line)[1];

    ctx.beginPath();
    ctx.lineWidth = 2;
    ctx.moveTo(end1.x, end1.y);
    ctx.lineTo(end2.x, end2.y);
    ctx.closePath();

    ctx.strokeStyle = "black";
    ctx.stroke();
  };

  function drawCircle(circle, ctx) {
    ctx.beginPath();
    ctx.arc(circle.center.x, circle.center.y, circle.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = "black";
    ctx.fill();
  };

  // returns point on passed line closest to passed circle
  function pointOnLineClosestToCircle(circle, line) {
    var lineEnd1 = lineEnds(line)[0];
    var lineEnd2 = lineEnds(line)[1];

    // vector representing line surface
    var lineVector = {
      x: lineEnd2.x - lineEnd1.x,
      y: lineEnd2.y - lineEnd1.y
    };
    var lineUnitVector = unitVector(lineVector);

    var lineEnd1ToCircle = {
      x: circle.center.x - lineEnd1.x,
      y: circle.center.y - lineEnd1.y
    };

    // project vector between line end and circle along
    // line to get distance between end and point
    // on line closest to circle
    var projection = dotProduct(lineEnd1ToCircle, lineUnitVector);

    if (projection <= 0) {
      return lineEnd1; // off end of line - end is closest point
    } else if (projection >= magnitude(lineVector)) {
      return lineEnd2; // ditto
    } else {
      // part way along line - return that point
      return {
        x: lineEnd1.x + lineUnitVector.x * projection,
        y: lineEnd1.y + lineUnitVector.y * projection
      };
    }
  };

  function isCircleIntersectingLine(circle, line) {
    var closest = pointOnLineClosestToCircle(circle, line);
    var circleToLineDistance = distance(circle.center, closest);
    return circleToLineDistance < circle.radius;
  };

  // bounces circle off line
  function bounceCircle(circle, line) {
    if (!isCircleIntersectingLine(circle, line)) {
      return; // line not touching circle - no bounce
    }

    // get normal of surface to bounce circle off
    var closest = pointOnLineClosestToCircle(circle, line);
    var normal = unitVector({
      x: circle.center.x - closest.x,
      y: circle.center.y - closest.y
    });

    // set new circle velocity by reflecting old velocity in
    // the normal to the surface the circle is bouncing off
    var dot = dotProduct(circle.velocity, normal);
    circle.velocity.x = circle.velocity.x - 2 * dot * normal.x;
    circle.velocity.y = circle.velocity.y - 2 * dot * normal.y;

    // move circle until outside line
    while (isCircleIntersectingLine(circle, line)) {
      moveCircle(circle);
    }
  };

  // move passed circle based on its current speed
  function moveCircle(circle) {
    circle.velocity.y = circle.velocity.y + 2;
    circle.center.x = circle.center.x + circle.velocity.x / 30;
    circle.center.y = circle.center.y + circle.velocity.y / 30;
  };

  function isCircleOnCanvas(circle, canvas) {
    return circle.center.x > -circle.radius &&
      circle.center.x < canvas.width + circle.radius &&
      circle.center.y > -circle.radius &&
      circle.center.y < canvas.height + circle.radius;
  };
})(this);

// concepts will learn:
// - detecting line circle collisions
// - line normals
// - bouncing off arbitrarily angled surfaces
<commit_msg>Collect state into one var<commit_after>;(function(exports) {
  // creates lines and circles and starts the animation
  function start(canvas) {
    var world = {
      circles: [],
      lines: [],
      dimensions: { x: canvas.width, y: canvas.height }
    };

    // make new circle every so often
    setInterval(function() {
      world.circles.push(makeCircle(world.dimensions.x / 2));
    }, 400);

    // make grid of lines
    for (var i = 1; i < 6; i++) {
      for (var j = 1; j < 6; j++) {
        world.lines.push(makeLine(i * 80, j * 80, i * j));
      }
    }
    world.lines.splice(10, 2); // throw away top center lines

    // start update/draw loop
    function tick() {
      update(world);
      draw(world, canvas);
      requestAnimationFrame(tick);
    };

    tick();
  };
  exports.start = start; // make start function available to HTML page

  // rotates the lines, moves and bounces the circles
  function update(world) {
    for (var i = world.circles.length - 1; i >= 0; i--) {
      for (var j = 0; j < world.lines.length; j++) {
        bounceCircle(world.circles[i], world.lines[j]);
      }

      moveCircle(world.circles[i]);
      if (!isCircleInWorld(world.circles[i], world.dimensions)) {
        world.circles.splice(i, 1); // remove circles that have left screen
      }
    }

    for (var i = 0; i < world.lines.length; i++) {
      world.lines[i].angle += world.lines[i].rotateSpeed;
    }
  };

  function draw(world, canvas) {
    var ctx = canvas.getContext('2d');

    // fill screen with white
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, world.dimensions.x, world.dimensions.y);

    // draw circles
    for (var i = 0; i < world.circles.length; i++) {
      drawCircle(world.circles[i], ctx);
    }

    // draw lines
    for (var i = 0; i < world.lines.length; i++) {
      drawLine(world.lines[i], ctx);
    }
  };

  function makeCircle(x) {
    return {
      center: { x: x, y: -7 },
      velocity: { x: 0, y: 0 },
      radius: 7
    };
  };

  function makeLine(x, y) {
    return {
      center: { x: x, y: y },
      span: 40,
      angle: Math.random() * 360,
      rotateSpeed: 0.5
    };
  };

  function distance(point1, point2) {
    var x = Math.abs(point1.x - point2.x);
    var y = Math.abs(point1.y - point2.y);
    return Math.sqrt((x * x) + (y * y));
  };

  function magnitude(vector) {
    return Math.sqrt(vector.x * vector.x + vector.y* vector.y);
  };

  function unitVector(vector) {
    return {
      x: vector.x / magnitude(vector),
      y: vector.y / magnitude(vector)
    };
  };

  function dotProduct(vector1, vector2) {
    return vector1.x * vector2.x + vector1.y * vector2.y;
  };

  // returns the points at the two ends of the passed line
  function lineEnds(line) {
    var angleRadians = line.angle * 0.01745;
    var lineVector = unitVector({
      x: Math.cos(angleRadians) * 0 - Math.sin(angleRadians) * -1,
      y: Math.sin(angleRadians) * 0 + Math.cos(angleRadians) * -1
    });

    return [{
      x: line.center.x + lineVector.x * line.span / 2,
      y: line.center.y + lineVector.y * line.span / 2
    }, {
      x: line.center.x - lineVector.x * line.span / 2,
      y: line.center.y - lineVector.y * line.span / 2
    }];
  };

  function drawLine(line, ctx) {
    var end1 = lineEnds(line)[0];
    var end2 = lineEnds(line)[1];

    ctx.beginPath();
    ctx.lineWidth = 2;
    ctx.moveTo(end1.x, end1.y);
    ctx.lineTo(end2.x, end2.y);
    ctx.closePath();

    ctx.strokeStyle = "black";
    ctx.stroke();
  };

  function drawCircle(circle, ctx) {
    ctx.beginPath();
    ctx.arc(circle.center.x, circle.center.y, circle.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = "black";
    ctx.fill();
  };

  // returns point on passed line closest to passed circle
  function pointOnLineClosestToCircle(circle, line) {
    var lineEnd1 = lineEnds(line)[0];
    var lineEnd2 = lineEnds(line)[1];

    // vector representing line surface
    var lineVector = {
      x: lineEnd2.x - lineEnd1.x,
      y: lineEnd2.y - lineEnd1.y
    };
    var lineUnitVector = unitVector(lineVector);

    var lineEnd1ToCircle = {
      x: circle.center.x - lineEnd1.x,
      y: circle.center.y - lineEnd1.y
    };

    // project vector between line end and circle along
    // line to get distance between end and point
    // on line closest to circle
    var projection = dotProduct(lineEnd1ToCircle, lineUnitVector);

    if (projection <= 0) {
      return lineEnd1; // off end of line - end is closest point
    } else if (projection >= magnitude(lineVector)) {
      return lineEnd2; // ditto
    } else {
      // part way along line - return that point
      return {
        x: lineEnd1.x + lineUnitVector.x * projection,
        y: lineEnd1.y + lineUnitVector.y * projection
      };
    }
  };

  function isCircleIntersectingLine(circle, line) {
    var closest = pointOnLineClosestToCircle(circle, line);
    var circleToLineDistance = distance(circle.center, closest);
    return circleToLineDistance < circle.radius;
  };

  // bounces circle off line
  function bounceCircle(circle, line) {
    if (!isCircleIntersectingLine(circle, line)) {
      return; // line not touching circle - no bounce
    }

    // get normal of surface to bounce circle off
    var closest = pointOnLineClosestToCircle(circle, line);
    var normal = unitVector({
      x: circle.center.x - closest.x,
      y: circle.center.y - closest.y
    });

    // set new circle velocity by reflecting old velocity in
    // the normal to the surface the circle is bouncing off
    var dot = dotProduct(circle.velocity, normal);
    circle.velocity.x = circle.velocity.x - 2 * dot * normal.x;
    circle.velocity.y = circle.velocity.y - 2 * dot * normal.y;

    // move circle until outside line
    while (isCircleIntersectingLine(circle, line)) {
      moveCircle(circle);
    }
  };

  // move passed circle based on its current speed
  function moveCircle(circle) {
    circle.velocity.y = circle.velocity.y + 2;
    circle.center.x = circle.center.x + circle.velocity.x / 30;
    circle.center.y = circle.center.y + circle.velocity.y / 30;
  };

  function isCircleInWorld(circle, worldDimensions) {
    return circle.center.x > -circle.radius &&
      circle.center.x < worldDimensions.x + circle.radius &&
      circle.center.y > -circle.radius &&
      circle.center.y < worldDimensions.y + circle.radius;
  };
})(this);

// concepts will learn:
// - detecting line circle collisions
// - line normals
// - bouncing off arbitrarily angled surfaces
<|endoftext|>


<commit_before>const Client       = require('../../../base/client');
const BinarySocket = require('../../../base/socket');
const Dialog       = require('../../../common/attach_dom/dialog');
const ClientBase   = require('../../../../_common/base/client_base');
const localize     = require('../../../../_common/localize').localize;
const State        = require('../../../../_common/storage').State;

const Settings = (() => {
    const onLoad = () => {
        BinarySocket.wait('get_account_status').then(() => {
            $('.real').setVisibility(!Client.get('is_virtual'));

            const status = State.getResponse('get_account_status.status') || [];

            if (!/social_signup/.test(status)) {
                $('#change_password').setVisibility(1);
            }

            if (ClientBase.isAuthenticationAllowed()) {
                $('#authenticate a')
                    .attr('href', '#')
                    .on('click',  () => {
                        Dialog.alert({
                            id               : 'authorize_svg_error',
                            localized_message: localize('You do not need to authenticate your account at this time.[_1]We will inform you when your account needs to be authenticated.', '
'),
                            localized_title  : localize('No authentication required'),
                            ok_text          : localize('Back to trading'),
                        });
                    });
            }

            // Professional Client menu should only be shown to maltainvest accounts.
            if ((Client.get('landing_company_shortcode') === 'maltainvest')) {
                const is_professional_client    = status.indexOf('professional') !== -1;
                const is_requested_professional = /professional_requested/.test(status);
                let localized_text = '';
                if (is_professional_client) {
                    localized_text = localize('You are categorised as a professional client.');
                } else if (is_requested_professional) {
                    localized_text = localize('Your application to be treated as a professional client is being processed.');
                } else { // is retail client
                    localized_text = localize('You are categorised as a retail client. Apply to be treated as a professional trader.');
                }
                $('#professional_client').setVisibility(1).find('p').text(localized_text);
            }

            $('#settings_container').setVisibility(1);
        });
    };

    return {
        onLoad,
    };
})();

module.exports = Settings;
<commit_msg>not allowed check<commit_after>const Client       = require('../../../base/client');
const BinarySocket = require('../../../base/socket');
const Dialog       = require('../../../common/attach_dom/dialog');
const ClientBase   = require('../../../../_common/base/client_base');
const localize     = require('../../../../_common/localize').localize;
const State        = require('../../../../_common/storage').State;

const Settings = (() => {
    const onLoad = () => {
        BinarySocket.wait('get_account_status').then(() => {
            $('.real').setVisibility(!Client.get('is_virtual'));

            const status = State.getResponse('get_account_status.status') || [];

            if (!/social_signup/.test(status)) {
                $('#change_password').setVisibility(1);
            }

            if (!ClientBase.isAuthenticationAllowed()) {
                $('#authenticate a')
                    .attr('href', '#')
                    .on('click',  () => {
                        Dialog.alert({
                            id               : 'authorize_svg_error',
                            localized_message: localize('You do not need to authenticate your account at this time.[_1]We will inform you when your account needs to be authenticated.', '
'),
                            localized_title  : localize('No authentication required'),
                            ok_text          : localize('Back to trading'),
                        });
                    });
            }

            // Professional Client menu should only be shown to maltainvest accounts.
            if ((Client.get('landing_company_shortcode') === 'maltainvest')) {
                const is_professional_client    = status.indexOf('professional') !== -1;
                const is_requested_professional = /professional_requested/.test(status);
                let localized_text = '';
                if (is_professional_client) {
                    localized_text = localize('You are categorised as a professional client.');
                } else if (is_requested_professional) {
                    localized_text = localize('Your application to be treated as a professional client is being processed.');
                } else { // is retail client
                    localized_text = localize('You are categorised as a retail client. Apply to be treated as a professional trader.');
                }
                $('#professional_client').setVisibility(1).find('p').text(localized_text);
            }

            $('#settings_container').setVisibility(1);
        });
    };

    return {
        onLoad,
    };
})();

module.exports = Settings;
<|endoftext|>


<commit_before>const electron = require('electron')
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow

const path = require('path')
const url = require('url')

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600})

  // and load the index.html of the app.
  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
  }))

  // Open the DevTools.
    //mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<commit_msg>Delete main.js<commit_after><|endoftext|>