<commit_before>/*global
    window, document, alert, XMLHttpRequest
 */

/* Nainwaklet module */
var Nainwaklet = (function () {
    'use strict';

    var nainwakOrigin = 'http://www.nainwak.com',
        nainwakGameUrl = nainwakOrigin + '/jeu/index.php',
        script = document.scripts[document.scripts.length - 1],
        scriptUrl = script.src,
        scriptBaseUrl = scriptUrl.substring(0, scriptUrl.lastIndexOf('/') + 1),
        scriptChannel = script.getAttribute('data-channel'),
        log = (window.console)
            ? Function.prototype.bind.call(window.console.log, window.console)
            : function () {
                return;
            };

    function extend(target, source) {
        Object.keys(source).forEach(function (key) {
            if (target[key] === undefined) {
                target[key] = source[key];
            }
        });
        return target;
    }

    function httpGet(url, processResponse) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);  // asynchronous
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {  // response received and loaded
                processResponse({
                    status: xhr.status,
                    body: xhr.responseText
                });
            }
        };
        xhr.send(null);
    }

    function buildQueryParams(params) {
        var pairs = [];
        Object.keys(params).forEach(function (key) {
            var value = params[key],
                encodedKey = encodeURIComponent(key),
                encodedValue = encodeURIComponent(value);
            pairs.push(encodedKey + '=' + encodedValue);
        });
        return pairs.join('&');
    }

    /*
    function parseUrl(url) {
        var a = document.createElement('a');
        a.href = url;
        return a;
    }
    */

    function generateRandomGuestName() {
        var id = Math.round(Math.random() * 1000);
        return 'guest' + id;
    }

    /* User factory */
    function createUser(name, avatar) {
        var defaultAvatar = scriptBaseUrl + 'avatar.png';
        return Object.freeze({
            name: name || generateRandomGuestName(),
            avatar: avatar || defaultAvatar
        });
    }

    /* Page factory */
    function createPage(name, analyze, loadParams) {
        var url = nainwakOrigin + '/jeu/' + name + '.php';

        function getUrl(IDS) {
            var params = {
                IDS: IDS
            };
            if (loadParams) {
                extend(params, loadParams);
            }
            return url + '?' + buildQueryParams(params);
        }

        function load(IDS, processResult) {
            var loadUrl = getUrl(IDS);
            httpGet(loadUrl, function (response) {
                var result = null;
                if (response.status === 200) {
                    result = analyze(response.body);
                }
                processResult(result);
            });
        }

        return Object.freeze({
            name: name,
            url: url,  // base URL without query parameters
            analyze: analyze,
            load: load
        });
    }

    function forEachMatch(regex, string, processMatch) {
        var match;
        if (!regex.global) {
            return regex.exec(string);
        } else {
            while (true) {
                match = regex.exec(string);
                if (match === null) {
                    break;
                }
                processMatch(match);
            }
        }
    }

    /* detect page */
    var detect = (function () {
        function findLocalization(html) {
            // example:
            // Position (13,5) sur "Ronain Graou" |b95eb2f716c500db6|
            var regex = /Position\s\((\d+),(\d+)\)\ssur\s"([^"]*)"/i,
                match = regex.exec(html);
            if (match) {
                return {
                    position: [parseInt(match[1], 10), parseInt(match[2], 10)],
                    world: match[3]
                };
            }
        }

        function findDwarfs(html) {
            // example:
            // tabavat[1] = ["33966", "avatar_guilde/41ddb8ad2c2be408e27352accf1cc0b6559466bb.png", "Le PheniX", '[Gnouille] [#!]', "13794", "2", "Diablotin(e)", "0", "13", "5", ""Le PheniX est un oiseau qui symbolise l'immortalité et la résurrection." A quoi bon me tuer ?!?", "o", "", "0"];
            // (id, photo, nom, tag, barbe, classe, cote, distance, x, y, description, attaquer, gifler, estCible)
            var regex = /tabavat\[\d+\]\s=\s\[(.*)\];/ig,
                dwarfs = [];

            forEachMatch(regex, html, function (match) {
                // TODO: not implemented
                log(match);
                //createDwarfFromMatch(match[1]);
            });

            return dwarfs;
        }

        function findObjects(html) {
            // example:
            // tabobjet[1] = [3609504, "objets/oreiller.gif", "Oreiller", 1, 12, 5, "ARME", 1262774];
            // (id, photo, nom, distance, x, y, poussiere, prendre)
            var objects = [];
            // TODO: not implemented
            return objects;
        }

        function analyze(html) {
            log('Analyzing detect...');
            return {
                localization: findLocalization(html),
                dwarfs: findDwarfs(html),
                objects: findObjects(html)
            };
        }

        return createPage('detect', analyze, {});
    }());

    /* pages */
    var pages = (function () {
        var list = [
                detect
                /*
                // Evénements (tous types d'evts sur 10 jours)
                createPage('even', log, {duree: 240, type: 'ALL'}),
                // Fiche de perso
                createPage('perso', log),
                // Inventaire
                createPage('invent', log),
                // Encyclopédie
                createPage('encyclo', log)
                */
            ],
            get = function (nameOrUrl) {
                var results = list.filter(function (page) {
                    return (page.name === nameOrUrl) || (page.url === nameOrUrl);
                });

                if (results) {
                    return results[0];
                }
            };

        return Object.freeze({
            get: get,
            list: list
        });
    }());

    /* Spy factory */
    function createSpy(frame) {  /*user, channel*/
        //IDS = parseQueryParams(frame.location).IDS,
        var infoLoaded = function () {
                var contentWindow = frame.contentWindow,
                    doc = contentWindow.document,
                    docEl = doc.documentElement,
                    html = docEl.outerHTML,  // no doctype but we don't mind
                    location = contentWindow.location,
                    url = location.origin + location.pathname,
                    page = pages.get(url),
                    result;

                if (page) {
                    result = page.analyze(html);
                    log(result);
                }
            },
            enabled = false,
            enable = function (value) {
                var oldEnabled = enabled;

                // update the status (and convert to boolean, just in case)
                enabled = !!value;

                if (oldEnabled === enabled) {  // nothing to do
                    return;
                }

                // register or unregister the load event handler
                if (enabled) {
                    frame.addEventListener('load', infoLoaded, false);
                } else {
                    frame.removeEventListener('load', infoLoaded, false);
                }
            };

        // start enabled
        enable(true);

        return Object.freeze({
            get enabled() {
                return enabled;
            },
            set enabled(value) {
                enable(value);
            }
        });
    }

    /* Hub factory */
    function createHub(container) {  /*, user, channel*/
        var containerContent = null,  // original content of the container
            ui = (function () {  // create the hub UI
                var iframe = document.createElement('iframe'),
                    hubUrl = scriptBaseUrl + 'hub.html';
                iframe.setAttribute('class', 'nainwaklet-hub');
                iframe.setAttribute('src', hubUrl);
                iframe.style.width = '100%';
                iframe.style.border = 0;
                return iframe;
            }()),
            /*
            cssLink = (function () {
                var link = document.createElement('link'),
                    cssUrl = scriptUrl.replace('.js', '.css');
                link.setAttribute('rel', 'stylesheet');
                link.setAttribute('type', 'text/css');
                link.setAttribute('href', cssUrl);
                return link;
            }()),
            */
            enabled = false,
            enable = function (value) {
                var oldEnabled = enabled;

                // update the status (and convert to boolean, just in case)
                enabled = !!value;

                if (enabled === oldEnabled) {  // nothing to do
                    return;
                }

                if (enabled) {
                    //var doc = container.ownerDocument;
                    //    head = doc.getElementsByTagName('head')[0]

                    // add the CSS element to the head
                    //head.appendChild(cssLink);

                    // backup the initial content
                    containerContent = container.innerHTML;

                    // replace by our UI
                    container.innerHTML = '';
                    container.appendChild(ui);
                } else {
                    // restore the initial content
                    container.innerHTML = containerContent;
                    containerContent = null;

                    // remove the CSS element
                    //cssLink.parentNode.removeChild(cssLink);
                }
            };

        // start enabled
        enable(true);

        return Object.freeze({
            get enabled() {
                return enabled;
            },
            set enabled(value) {
                enable(value);
            }
        });
    }

    /* Application factory */
    function createApplication(conf) {
        var _conf = conf || {},
            user = _conf.user || createUser(),
            channel = _conf.channel || scriptChannel || 'default',
            container = _conf.container || window.document.body,
            spyFrame = _conf.spyFrame,  // should be the frame element
            hub,
            spy;

        if (container) {
            hub = createHub(container, user, channel);
        }

        if (spyFrame) {
            spy = createSpy(spyFrame, user, channel);
        }

        function destroy() {
            if (spy) {
                spy.enabled = false;
            }

            if (hub) {
                hub.enabled = false;
            }
        }

        return Object.freeze({
            user: user,
            hub: hub,
            spy: spy,
            destroy: destroy
        });
    }

    /* Check if we are on the Nainwak game page */
    function isOnNainwakGame() {
        var loc = window.location,
            pageUrl = loc.origin + loc.pathname;
        return pageUrl === nainwakGameUrl;
    }

    /* Get the Nainwak User info from the menu frame */
    function getNainwakUser() {
        var frame = window.frames.menu,
            doc = frame.document,
            title = doc.querySelector('.news-titre'),
            name = title.querySelector('td:last-child').innerHTML,
            avatar = title.querySelector('td:first-child img').src;
        return createUser(name, avatar);
    }

    function augmentApiOnNainwakGame(api) {
        if (isOnNainwakGame()) {
            // augment the API with an application object tailored for the Nainwak game
            api.app = createApplication({
                user: getNainwakUser(),
                container: window.frames.pub.document.body,
                spyFrame: window.frames.info.frameElement
            });
        }
    }

    /********************/
    /* Bookmarklet part */
    /********************/
    function getInjectionUrl(channel) {
        var lines = [
            'javascript:(function () {',
            '    var w = window,',
            '        l = w.location,',
            '        u = l.origin + l.pathname,',
            '        d = document,',
            '        b = d.body,',
            '        n = "Nainwaklet",',
            '        i = n + "Script",',
            '        s = d.getElementById(i);',
            '    if (u === "' + nainwakGameUrl + '") {',
            '        if (s) {',
            '            w[n].app.destroy();',
            '            w[n] = null;',
            '            b.removeChild(s);',
            '        } else {',
            '            s = d.createElement("script");',
            '            s.id = i;',
            '            s.type = "text/javascript";',
            '            s.src = "' + scriptUrl + '";',
            '            s.async = false;',
            '            s.setAttribute("data-channel", "' + channel + '");',
            '            b.appendChild(s);',
            '        }',
            '    } else {',
            '        alert("Ne fonctionne que sur la page jeu de Nainwak");',
            '    }',
            '}())'
        ];
        return lines.join('\n').replace(/\s+/g, ' ');
    }

    function initializeButtons(buttons) {
        var _buttons = buttons || document.querySelectorAll('.nainwaklet-button');
        Array.prototype.forEach.call(_buttons, function (button) {
            var channel = button.getAttribute('data-channel'),
                href = getInjectionUrl(channel);
            button.setAttribute("href", href);
        });
    }

    // module public API
    var api = {
        pages: pages,
        createUser: createUser,
        createApplication: createApplication,
        initializeButtons: initializeButtons
    };

    // augment the API if we are on the Nainwak game page
    augmentApiOnNainwakGame(api);

    return Object.freeze(api);
}());
<commit_msg>fixed the forEachMatch function<commit_after>/*global
    window, document, alert, XMLHttpRequest
 */

/* Nainwaklet module */
var Nainwaklet = (function () {
    'use strict';

    var nainwakOrigin = 'http://www.nainwak.com',
        nainwakGameUrl = nainwakOrigin + '/jeu/index.php',
        script = document.scripts[document.scripts.length - 1],
        scriptUrl = script.src,
        scriptBaseUrl = scriptUrl.substring(0, scriptUrl.lastIndexOf('/') + 1),
        scriptChannel = script.getAttribute('data-channel'),
        log = (window.console)
            ? Function.prototype.bind.call(window.console.log, window.console)
            : function () {
                return;
            };

    function extend(target, source) {
        Object.keys(source).forEach(function (key) {
            if (target[key] === undefined) {
                target[key] = source[key];
            }
        });
        return target;
    }

    function httpGet(url, processResponse) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);  // asynchronous
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {  // response received and loaded
                processResponse({
                    status: xhr.status,
                    body: xhr.responseText
                });
            }
        };
        xhr.send(null);
    }

    function buildQueryParams(params) {
        var pairs = [];
        Object.keys(params).forEach(function (key) {
            var value = params[key],
                encodedKey = encodeURIComponent(key),
                encodedValue = encodeURIComponent(value);
            pairs.push(encodedKey + '=' + encodedValue);
        });
        return pairs.join('&');
    }

    /*
    function parseUrl(url) {
        var a = document.createElement('a');
        a.href = url;
        return a;
    }
    */

    function generateRandomGuestName() {
        var id = Math.round(Math.random() * 1000);
        return 'guest' + id;
    }

    /* User factory */
    function createUser(name, avatar) {
        var defaultAvatar = scriptBaseUrl + 'avatar.png';
        return Object.freeze({
            name: name || generateRandomGuestName(),
            avatar: avatar || defaultAvatar
        });
    }

    /* Page factory */
    function createPage(name, analyze, loadParams) {
        var url = nainwakOrigin + '/jeu/' + name + '.php';

        function getUrl(IDS) {
            var params = {
                IDS: IDS
            };
            if (loadParams) {
                extend(params, loadParams);
            }
            return url + '?' + buildQueryParams(params);
        }

        function load(IDS, processResult) {
            var loadUrl = getUrl(IDS);
            httpGet(loadUrl, function (response) {
                var result = null;
                if (response.status === 200) {
                    result = analyze(response.body);
                }
                processResult(result);
            });
        }

        return Object.freeze({
            name: name,
            url: url,  // base URL without query parameters
            analyze: analyze,
            load: load
        });
    }

    function forEachMatch(regex, string, processMatch) {
        var match;

        if (!regex.global) {
            throw "The global flag should be set for this RegExp: " + regex;
        }

        while (true) {
            match = regex.exec(string);
            if (match === null) {
                break;
            }
            processMatch(match);
        }
    }

    /* detect page */
    var detect = (function () {
        function findLocalization(html) {
            // example:
            // Position (13,5) sur "Ronain Graou" |b95eb2f716c500db6|
            var regex = /Position\s\((\d+),(\d+)\)\ssur\s"([^"]*)"/i,
                match = regex.exec(html);
            if (match) {
                return {
                    position: [parseInt(match[1], 10), parseInt(match[2], 10)],
                    world: match[3]
                };
            }
        }

        function findDwarfs(html) {
            // example:
            // tabavat[1] = ["33966", "avatar_guilde/41ddb8ad2c2be408e27352accf1cc0b6559466bb.png", "Le PheniX", '[Gnouille] [#!]', "13794", "2", "Diablotin(e)", "0", "13", "5", ""Le PheniX est un oiseau qui symbolise l'immortalité et la résurrection." A quoi bon me tuer ?!?", "o", "", "0"];
            // (id, photo, nom, tag, barbe, classe, cote, distance, x, y, description, attaquer, gifler, estCible)
            var regex = /tabavat\[\d+\]\s=\s\[(.*)\];/ig,
                dwarfs = [];

            forEachMatch(regex, html, function (match) {
                // TODO: not implemented
                log(match);
                //createDwarfFromMatch(match[1]);
            });

            return dwarfs;
        }

        function findObjects(html) {
            // example:
            // tabobjet[1] = [3609504, "objets/oreiller.gif", "Oreiller", 1, 12, 5, "ARME", 1262774];
            // (id, photo, nom, distance, x, y, poussiere, prendre)
            var objects = [];
            // TODO: not implemented
            return objects;
        }

        function analyze(html) {
            log('Analyzing detect...');
            return {
                localization: findLocalization(html),
                dwarfs: findDwarfs(html),
                objects: findObjects(html)
            };
        }

        return createPage('detect', analyze, {});
    }());

    /* pages */
    var pages = (function () {
        var list = [
                detect
                /*
                // Evénements (tous types d'evts sur 10 jours)
                createPage('even', log, {duree: 240, type: 'ALL'}),
                // Fiche de perso
                createPage('perso', log),
                // Inventaire
                createPage('invent', log),
                // Encyclopédie
                createPage('encyclo', log)
                */
            ],
            get = function (nameOrUrl) {
                var results = list.filter(function (page) {
                    return (page.name === nameOrUrl) || (page.url === nameOrUrl);
                });

                if (results) {
                    return results[0];
                }
            };

        return Object.freeze({
            get: get,
            list: list
        });
    }());

    /* Spy factory */
    function createSpy(frame) {  /*user, channel*/
        //IDS = parseQueryParams(frame.location).IDS,
        var infoLoaded = function () {
                var contentWindow = frame.contentWindow,
                    doc = contentWindow.document,
                    docEl = doc.documentElement,
                    html = docEl.outerHTML,  // no doctype but we don't mind
                    location = contentWindow.location,
                    url = location.origin + location.pathname,
                    page = pages.get(url),
                    result;

                if (page) {
                    result = page.analyze(html);
                    log(result);
                }
            },
            enabled = false,
            enable = function (value) {
                var oldEnabled = enabled;

                // update the status (and convert to boolean, just in case)
                enabled = !!value;

                if (oldEnabled === enabled) {  // nothing to do
                    return;
                }

                // register or unregister the load event handler
                if (enabled) {
                    frame.addEventListener('load', infoLoaded, false);
                } else {
                    frame.removeEventListener('load', infoLoaded, false);
                }
            };

        // start enabled
        enable(true);

        return Object.freeze({
            get enabled() {
                return enabled;
            },
            set enabled(value) {
                enable(value);
            }
        });
    }

    /* Hub factory */
    function createHub(container) {  /*, user, channel*/
        var containerContent = null,  // original content of the container
            ui = (function () {  // create the hub UI
                var iframe = document.createElement('iframe'),
                    hubUrl = scriptBaseUrl + 'hub.html';
                iframe.setAttribute('class', 'nainwaklet-hub');
                iframe.setAttribute('src', hubUrl);
                iframe.style.width = '100%';
                iframe.style.border = 0;
                return iframe;
            }()),
            /*
            cssLink = (function () {
                var link = document.createElement('link'),
                    cssUrl = scriptUrl.replace('.js', '.css');
                link.setAttribute('rel', 'stylesheet');
                link.setAttribute('type', 'text/css');
                link.setAttribute('href', cssUrl);
                return link;
            }()),
            */
            enabled = false,
            enable = function (value) {
                var oldEnabled = enabled;

                // update the status (and convert to boolean, just in case)
                enabled = !!value;

                if (enabled === oldEnabled) {  // nothing to do
                    return;
                }

                if (enabled) {
                    //var doc = container.ownerDocument;
                    //    head = doc.getElementsByTagName('head')[0]

                    // add the CSS element to the head
                    //head.appendChild(cssLink);

                    // backup the initial content
                    containerContent = container.innerHTML;

                    // replace by our UI
                    container.innerHTML = '';
                    container.appendChild(ui);
                } else {
                    // restore the initial content
                    container.innerHTML = containerContent;
                    containerContent = null;

                    // remove the CSS element
                    //cssLink.parentNode.removeChild(cssLink);
                }
            };

        // start enabled
        enable(true);

        return Object.freeze({
            get enabled() {
                return enabled;
            },
            set enabled(value) {
                enable(value);
            }
        });
    }

    /* Application factory */
    function createApplication(conf) {
        var _conf = conf || {},
            user = _conf.user || createUser(),
            channel = _conf.channel || scriptChannel || 'default',
            container = _conf.container || window.document.body,
            spyFrame = _conf.spyFrame,  // should be the frame element
            hub,
            spy;

        if (container) {
            hub = createHub(container, user, channel);
        }

        if (spyFrame) {
            spy = createSpy(spyFrame, user, channel);
        }

        function destroy() {
            if (spy) {
                spy.enabled = false;
            }

            if (hub) {
                hub.enabled = false;
            }
        }

        return Object.freeze({
            user: user,
            hub: hub,
            spy: spy,
            destroy: destroy
        });
    }

    /* Check if we are on the Nainwak game page */
    function isOnNainwakGame() {
        var loc = window.location,
            pageUrl = loc.origin + loc.pathname;
        return pageUrl === nainwakGameUrl;
    }

    /* Get the Nainwak User info from the menu frame */
    function getNainwakUser() {
        var frame = window.frames.menu,
            doc = frame.document,
            title = doc.querySelector('.news-titre'),
            name = title.querySelector('td:last-child').innerHTML,
            avatar = title.querySelector('td:first-child img').src;
        return createUser(name, avatar);
    }

    function augmentApiOnNainwakGame(api) {
        if (isOnNainwakGame()) {
            // augment the API with an application object tailored for the Nainwak game
            api.app = createApplication({
                user: getNainwakUser(),
                container: window.frames.pub.document.body,
                spyFrame: window.frames.info.frameElement
            });
        }
    }

    /********************/
    /* Bookmarklet part */
    /********************/
    function getInjectionUrl(channel) {
        var lines = [
            'javascript:(function () {',
            '    var w = window,',
            '        l = w.location,',
            '        u = l.origin + l.pathname,',
            '        d = document,',
            '        b = d.body,',
            '        n = "Nainwaklet",',
            '        i = n + "Script",',
            '        s = d.getElementById(i);',
            '    if (u === "' + nainwakGameUrl + '") {',
            '        if (s) {',
            '            w[n].app.destroy();',
            '            w[n] = null;',
            '            b.removeChild(s);',
            '        } else {',
            '            s = d.createElement("script");',
            '            s.id = i;',
            '            s.type = "text/javascript";',
            '            s.src = "' + scriptUrl + '";',
            '            s.async = false;',
            '            s.setAttribute("data-channel", "' + channel + '");',
            '            b.appendChild(s);',
            '        }',
            '    } else {',
            '        alert("Ne fonctionne que sur la page jeu de Nainwak");',
            '    }',
            '}())'
        ];
        return lines.join('\n').replace(/\s+/g, ' ');
    }

    function initializeButtons(buttons) {
        var _buttons = buttons || document.querySelectorAll('.nainwaklet-button');
        Array.prototype.forEach.call(_buttons, function (button) {
            var channel = button.getAttribute('data-channel'),
                href = getInjectionUrl(channel);
            button.setAttribute("href", href);
        });
    }

    // module public API
    var api = {
        pages: pages,
        createUser: createUser,
        createApplication: createApplication,
        initializeButtons: initializeButtons
    };

    // augment the API if we are on the Nainwak game page
    augmentApiOnNainwakGame(api);

    return Object.freeze(api);
}());
<|endoftext|>


<commit_before>nelliel.ui.hideShowThread = function(element, command, content_id) {
    if(element == null && content_id == null) {
        return;
    }

    if(content_id == null) {
        content_id = nelliel.core.contentID(element.getAttribute("data-content-id"));
    }

    var post_container = document.getElementById("post-container-" + content_id.id_string);
    var thread_header_options = post_container.querySelector(".thread-header-options");

    if(element == null) {
        element = thread_header_options.querySelector(".toggle-thread");
    }

    var post_header_options = post_container.querySelector(".post-header-options");
    var expand_thread = thread_header_options.querySelector(".expand-thread");
    var reply_thread = thread_header_options.querySelector(".reply-thread");
    var content_container = post_container.querySelector(".content-container");
    var comment_container = post_container.querySelector(".comment-container");
    var thread_expand = document.getElementById("thread-expand-" + "cid_" + content_id.thread_id + "_0_0");

    nelliel.ui.toggleHidden(post_header_options);
    nelliel.ui.toggleHidden(expand_thread);
    nelliel.ui.toggleHidden(reply_thread);

    // Special case since OP is (presently) considered to be the thread
    if (!dataBin.hidden_posts.hasOwnProperty(content_id.id_string)) {
        nelliel.ui.hideShowPost(element, command, content_id);
    }

    nelliel.ui.toggleHidden(thread_expand);

    if (command === "hide-thread") {
        dataBin.hidden_threads[content_id.id_string] = Date.now();
    } else if (command === "show-thread") {
        delete dataBin.hidden_threads[content_id.id_string];
    } else {
        return;
    }

    nelliel.core.storeInLocalStorage(dataBin.hidden_threads_id, dataBin.hidden_threads);
    nelliel.ui.switchDataCommand(element, "hide-thread", "show-thread");
    nelliel.ui.swapContentAttribute(element, "data-alt-visual");
}

nelliel.ui.hideShowPost = function(element, command, content_id) {
    if(element == null && content_id == null) {
        return;
    }

    if(content_id == null) {
        content_id = nelliel.core.contentID(element.getAttribute("data-content-id"));
    }

    var post_container = document.getElementById("post-container-" + content_id.id_string);
    var post_header_options = post_container.querySelector(".post-header-options");

    if(element == null) {
        element = post_header_options.querySelector(".toggle-post");
    }

    var content_container = post_container.querySelector(".content-container");
    var comment_container = post_container.querySelector(".comment-container");
    nelliel.ui.toggleHidden(content_container);
    nelliel.ui.toggleHidden(comment_container);

    if (command == "hide-post") {
        dataBin.hidden_posts[content_id.id_string] = Date.now();
    } else if (command == "show-post") {
        delete dataBin.hidden_posts[content_id.id_string];
    } else {
        return
    }

    nelliel.core.storeInLocalStorage(dataBin.hidden_posts_id, dataBin.hidden_posts);
    nelliel.ui.switchDataCommand(element, "hide-post", "show-post");
    nelliel.ui.swapContentAttribute(element, "data-alt-visual");
}

nelliel.ui.hideShowFile = function(element, command, content_id) {
    if(element == null && content_id == null) {
        return;
    }

    if(content_id == null) {
        content_id = nelliel.core.contentID(element.getAttribute("data-content-id"));
    }

    var file_container = document.getElementById("file-container-" + content_id.id_string);

    if(element == null) {
        element = file_container.querySelector(".toggle-file");
    }

    var file_preview = file_container.querySelector(".file-preview");
    nelliel.ui.toggleHidden(file_preview);

    if (command == "hide-file") {
        dataBin.hidden_files[content_id.id_string] = Date.now();
    } else if (command == "show-file") {
        delete dataBin.hidden_files[content_id.id_string];
    } else {
        return;
    }

    nelliel.core.storeInLocalStorage(dataBin.hidden_files_id, dataBin.hidden_files);
    nelliel.ui.switchDataCommand(element, "hide-file", "show-file");
    nelliel.ui.swapContentAttribute(element, "data-alt-visual");
}

nelliel.ui.hideShowEmbed = function(element, command, content_id) {
    if(element == null && content_id == null) {
        return;
    }

    if(content_id == null) {
        content_id = nelliel.core.contentID(element.getAttribute("data-content-id"));
    }

    var embed_container = document.getElementById("embed-container-" + content_id.id_string);

    if(element == null) {
        element = embed_container.querySelector(".toggle-embed");
    }

    var embed_frame = embed_container.querySelector(".embed-frame");
    nelliel.ui.toggleHidden(embed_frame);

    if (command == "hide-embed") {
        dataBin.hidden_embeds[content_id.id_string] = Date.now();
    } else if (command == "show-embed") {
        delete dataBin.hidden_embeds[content_id.id_string];
    } else {
        return;
    }

    nelliel.core.storeInLocalStorage(dataBin.hidden_embeds_id, dataBin.hidden_embeds);
    nelliel.ui.switchDataCommand(element, "hide-embed", "show-embed");
    nelliel.ui.swapContentAttribute(element, "data-alt-visual");
}

nelliel.ui.applyHideContent = function() {
    var cids = [];

    for (var id in dataBin.hidden_threads) {
        nelliel.ui.hideShowThread(null, "apply", nelliel.core.contentID(id));
    }

    for (var id in dataBin.hidden_posts) {
        nelliel.ui.hideShowPost(null, "apply", nelliel.core.contentID(id));
    }

    for (var id in dataBin.hidden_files) {
        nelliel.ui.hideShowFile(null, "apply", nelliel.core.contentID(id));
    }

    for (var id in dataBin.hidden_embeds) {
        nelliel.ui.hideShowEmbed(null, "apply", nelliel.core.contentID(id));
    }
}

nelliel.ui.showHideFileMeta = function(element) {
    if(element === null) {
        return;
    }

    var content_id = nelliel.core.contentID(element.getAttribute("data-content-id"))
    var file_container = document.getElementById("file-container-" + content_id.id_string);
    var meta_element = file_container.querySelector(".file-meta");
    nelliel.ui.swapContentAttribute(element, "data-alt-visual");
    nelliel.ui.toggleHidden(meta_element);
    nelliel.ui.switchDataCommand(element, "show-file-meta", "hide-file-meta");
}

nelliel.ui.expandCollapseThread = function(element, command, dynamic = false) {
    if(element === null) {
        return;
    }

    var content_id = nelliel.core.contentID(element.getAttribute("data-content-id"));
    var target_element = document.getElementById("thread-expand-" + content_id.id_string);
    var split_command = command.split("-");

    if (!target_element) {
        return;
    }

    if (dynamic) {
        var url = "imgboard.php?module=render&action=view-thread&content-id=" + content_id.id_string + "&board-id=" + dataBin.board_id + "&thread=" + content_id.thread_id;

        if(dataBin.is_modmode) {
            url = url + "&modmode=true";
        }

        var command1 = "expand-thread-render";
        var command2 = "collapse-thread-render";
    } else {
        var url = "threads/" + content_id.thread_id + "/thread-" + content_id.thread_id + ".html";
        var command1 = "expand-thread";
        var command2 = "collapse-thread";
    }

    if(command === "expand-thread" || command === "expand-thread-render") {
        dataBin.collapsedThreads[content_id.id_string] = target_element.innerHTML;
        var request = new XMLHttpRequest();
        request.open('GET', url);
        request.responseType = "document";
        request.onload = function() {
            if (request.status === 200) {
                var expandHTML = request.response.getElementById("thread-expand-" + content_id.id_string).innerHTML;
                target_element.innerHTML = expandHTML;
            }
        };

        request.send();
    }

    if(command === "collapse-thread" || command === "collapse-thread-render") {
        target_element.innerHTML = dataBin.collapsedThreads[content_id.id_string];
    }

    nelliel.ui.swapContentAttribute(element, "data-alt-visual");
    nelliel.ui.switchDataCommand(element, command1, command2);
    nelliel.ui.applyHideContent();
}

nelliel.ui.highlightPost = function(content_id) {
    var post_container = document.getElementById("post-container-" + content_id.id_string);

    if (post_container !== null) {
        if (post_container.className.indexOf('post-hightlight') === -1) {
            post_container.className += " post-highlight";
        } else {
            post_container.className = post_container.className.replace(/\post-highlight\b/g, "");
        }
    }
}

nelliel.ui.inlineExpandReduce = function(element, command) {
    if(element === null) {
        return;
    }

    if (element.hasAttribute("data-other-dims")) {
        var new_location = element.getAttribute("data-other-loc");
        var old_location = element.getAttribute("src");
        var image_dims = element.getAttribute("data-other-dims");
        var width = image_dims.match(/w([0-9]+)/)[1];
        var height = image_dims.match(/h([0-9]+)/)[1];
        var old_width = element.getAttribute("width");
        var old_height = element.getAttribute("height");
        element.setAttribute("width", width);
        element.setAttribute("height", height);
        element.setAttribute("data-other-dims", 'w' + old_width + 'h' + old_height);
        element.setAttribute("src", new_location);
        element.setAttribute("data-other-loc", old_location);
        nelliel.ui.switchDataCommand(element, "inline-reduce", "inline-expand");
    }
}

nelliel.ui.showLinkedPost = function(element, event) {
    if(element === null) {
        return;
    }

    var href = element.getAttribute("href");
    var anchor_matches = href.match(/#t([0-9]+)p([0-9]+)/);
    var post_id = "cid_" + anchor_matches[1] + "_" + anchor_matches[2] + "_0";

    if (document.getElementById("post-cite-popup-" + post_id) !== null) {
        return;
    }

    var offsetY = window.pageYOffset || document.documentElement.scrollTop;
    var offsetX = window.pageXOffset || document.documentElement.scrollLeft;
    var popup_div = document.createElement("div");
    popup_div.id = "post-cite-popup-" + post_id;
    popup_div.setAttribute("class", "post-cite-popup");
    var element_rect = element.getBoundingClientRect();
    nelliel.ui.addBoundingClientRectProperties(element_rect);

    var request = new XMLHttpRequest();
    request.open('GET', element.getAttribute("href"));
    request.responseType = "document";

    request.onload = function() {
        if (request.status === 200) {
            var quoted_post = request.response.getElementById("post-container-" + post_id);
            quoted_post.className = quoted_post.className.replace(/\op-post\b/g, "reply-post");
            popup_div.appendChild(quoted_post);
            element.parentNode.insertBefore(popup_div, element);
            var popup_rect = quoted_post.getBoundingClientRect();
            nelliel.ui.addBoundingClientRectProperties(popup_rect);
            popup_div.style.left = (element_rect.right + offsetX + 5) + 'px';
            popup_div.style.top = ((element_rect.y + offsetY) - (popup_rect.height / 2)) + 'px';
        }
    };

    request.send();
}

nelliel.ui.hideLinkedPost = function(element, event) {
    if(element === null) {
        return;
    }

    var href = element.getAttribute("href");
    var anchor_matches = href.match(/#t([0-9]+)p([0-9]+)/);
    var post_id = "cid_" + anchor_matches[1] + "_" + anchor_matches[2] + "_0";
    var target_popup = document.getElementById("post-cite-popup-" + post_id);

    if (target_popup !== null) {
        target_popup.remove();
    }
}

nelliel.ui.citePost = function(element) {
    if(element === null) {
        return;
    }

    var content_id = nelliel.core.contentID(element.getAttribute("data-content-id"))
    var wordswordswords = document.getElementById("wordswordswords");
    wordswordswords.value = wordswordswords.value + '>>' + content_id.post_id + '\n';
}

nelliel.ui.toggleHidden = function(element) {
    if (element === null) {
        return;
    }

    if (element.className.search("hidden") === -1) {
        element.className += " hidden";
    } else {
        element.className = element.className.replace(/\bhidden\b/g, "");
    }
}

nelliel.ui.swapContentAttribute = function(element, attribute_name) {
    if(element === null) {
        return;
    }

    var inner = element.innerHTML;
    element.innerHTML = element.getAttribute(attribute_name);
    element.setAttribute(attribute_name, inner);
}

nelliel.ui.switchDataCommand = function(element, option_one, option_two) {
    if(element === null) {
        return;
    }

    var data_command = element.getAttribute("data-command");

    if (data_command.indexOf(option_one) > -1 ) {
        element.setAttribute("data-command", option_two);
    } else if (data_command.indexOf(option_two) > -1 ) {
        element.setAttribute("data-command", option_one);
    }
}

nelliel.ui.addBoundingClientRectProperties = function(bounding_rect) {
    bounding_rect.width = bounding_rect.width || bounding_rect.right - bounding_rect.left;
    bounding_rect.height = bounding_rect.height || bounding_rect.bottom - bounding_rect.top;
    bounding_rect.x = bounding_rect.x || bounding_rect.left + (bounding_rect.width / 2);
    bounding_rect.y = bounding_rect.y || bounding_rect.top + (bounding_rect.height / 2);
}
<commit_msg>Think the post and thread toggle is fixed for real this time<commit_after>nelliel.ui.hideShowThread = function(element, command, content_id) {
    if (element == null && content_id == null) {
        return;
    }

    var store_update = true;

    if (content_id == null) {
        content_id = nelliel.core.contentID(element.getAttribute("data-content-id"));
    }

    var post_container = document.getElementById("post-container-" + content_id.id_string);
    var thread_header_options = post_container.querySelector(".thread-header-options");

    if (element == null) {
        element = thread_header_options.querySelector(".toggle-thread");
    }

    var post_header_options = post_container.querySelector(".post-header-options");
    var expand_thread = thread_header_options.querySelector(".expand-thread");
    var reply_thread = thread_header_options.querySelector(".reply-thread");
    var content_container = post_container.querySelector(".content-container");
    var comment_container = post_container.querySelector(".comment-container");
    var thread_expand = document.getElementById("thread-expand-" + "cid_" + content_id.thread_id + "_0_0");

    nelliel.ui.toggleHidden(post_header_options);
    nelliel.ui.toggleHidden(expand_thread);
    nelliel.ui.toggleHidden(reply_thread);

    if (command === "hide-thread") {
        dataBin.hidden_threads[content_id.id_string] = Date.now();
    } else if (command === "show-thread") {
        delete dataBin.hidden_threads[content_id.id_string];
    } else if (command === "apply") {
        store_update = false;
    } else {
        return;
    }

    if (store_update) {
        nelliel.core.storeInLocalStorage(dataBin.hidden_threads_id, dataBin.hidden_threads);
    }

    // Special case since OP is (presently) considered to be the thread
    if (!dataBin.hidden_posts.hasOwnProperty(content_id.id_string)) {
        nelliel.ui.hideShowPost(null, null, content_id);
    }

    nelliel.ui.swapContentAttribute(element, "data-alt-visual");
    nelliel.ui.toggleHidden(thread_expand);
    nelliel.ui.switchDataCommand(element, "hide-thread", "show-thread");
}

nelliel.ui.hideShowPost = function(element, command, content_id) {
    if (element == null && content_id == null) {
        return;
    }

    var store_update = true;

    if (content_id == null) {
        content_id = nelliel.core.contentID(element.getAttribute("data-content-id"));
    }

    var post_container = document.getElementById("post-container-" + content_id.id_string);
    var post_header_options = post_container.querySelector(".post-header-options");

    if (element == null) {
        element = post_header_options.querySelector(".toggle-post");
    }

    var content_container = post_container.querySelector(".content-container");
    var comment_container = post_container.querySelector(".comment-container");
    nelliel.ui.toggleHidden(content_container);
    nelliel.ui.toggleHidden(comment_container);

    if (command == "hide-post") {
        dataBin.hidden_posts[content_id.id_string] = Date.now();
    } else if (command == "show-post") {
        delete dataBin.hidden_posts[content_id.id_string];
    } else if (command === "apply") {
        store_update = false;
    } else {
        return;
    }

    if (store_update) {
        nelliel.core.storeInLocalStorage(dataBin.hidden_posts_id, dataBin.hidden_posts);
    }

    nelliel.ui.switchDataCommand(element, "hide-post", "show-post");
    nelliel.ui.swapContentAttribute(element, "data-alt-visual");
}

nelliel.ui.hideShowFile = function(element, command, content_id) {
    if (element == null && content_id == null) {
        return;
    }

    if (content_id == null) {
        content_id = nelliel.core.contentID(element.getAttribute("data-content-id"));
    }

    var file_container = document.getElementById("file-container-" + content_id.id_string);

    if (element == null) {
        element = file_container.querySelector(".toggle-file");
    }

    var file_preview = file_container.querySelector(".file-preview");
    nelliel.ui.toggleHidden(file_preview);

    if (command == "hide-file") {
        dataBin.hidden_files[content_id.id_string] = Date.now();
    } else if (command == "show-file") {
        delete dataBin.hidden_files[content_id.id_string];
    } else {
        return;
    }

    nelliel.core.storeInLocalStorage(dataBin.hidden_files_id, dataBin.hidden_files);
    nelliel.ui.switchDataCommand(element, "hide-file", "show-file");
    nelliel.ui.swapContentAttribute(element, "data-alt-visual");
}

nelliel.ui.hideShowEmbed = function(element, command, content_id) {
    if (element == null && content_id == null) {
        return;
    }

    if (content_id == null) {
        content_id = nelliel.core.contentID(element.getAttribute("data-content-id"));
    }

    var embed_container = document.getElementById("embed-container-" + content_id.id_string);

    if (element == null) {
        element = embed_container.querySelector(".toggle-embed");
    }

    var embed_frame = embed_container.querySelector(".embed-frame");
    nelliel.ui.toggleHidden(embed_frame);

    if (command == "hide-embed") {
        dataBin.hidden_embeds[content_id.id_string] = Date.now();
    } else if (command == "show-embed") {
        delete dataBin.hidden_embeds[content_id.id_string];
    } else {
        return;
    }

    nelliel.core.storeInLocalStorage(dataBin.hidden_embeds_id, dataBin.hidden_embeds);
    nelliel.ui.switchDataCommand(element, "hide-embed", "show-embed");
    nelliel.ui.swapContentAttribute(element, "data-alt-visual");
}

nelliel.ui.applyHideContent = function() {
    var cids = [];

    for (var id in dataBin.hidden_threads) {
        nelliel.ui.hideShowThread(null, "apply", nelliel.core.contentID(id));
    }

    for (var id in dataBin.hidden_posts) {
        nelliel.ui.hideShowPost(null, "apply", nelliel.core.contentID(id));
    }

    for (var id in dataBin.hidden_files) {
        nelliel.ui.hideShowFile(null, "apply", nelliel.core.contentID(id));
    }

    for (var id in dataBin.hidden_embeds) {
        nelliel.ui.hideShowEmbed(null, "apply", nelliel.core.contentID(id));
    }
}

nelliel.ui.showHideFileMeta = function(element) {
    if (element === null) {
        return;
    }

    var content_id = nelliel.core.contentID(element.getAttribute("data-content-id"))
    var file_container = document.getElementById("file-container-" + content_id.id_string);
    var meta_element = file_container.querySelector(".file-meta");
    nelliel.ui.swapContentAttribute(element, "data-alt-visual");
    nelliel.ui.toggleHidden(meta_element);
    nelliel.ui.switchDataCommand(element, "show-file-meta", "hide-file-meta");
}

nelliel.ui.expandCollapseThread = function(element, command, dynamic = false) {
    if (element === null) {
        return;
    }

    var content_id = nelliel.core.contentID(element.getAttribute("data-content-id"));
    var target_element = document.getElementById("thread-expand-" + content_id.id_string);
    var split_command = command.split("-");

    if (!target_element) {
        return;
    }

    if (dynamic) {
        var url = "imgboard.php?module=render&action=view-thread&content-id=" + content_id.id_string + "&board-id=" + dataBin.board_id + "&thread=" + content_id.thread_id;

        if (dataBin.is_modmode) {
            url = url + "&modmode=true";
        }

        var command1 = "expand-thread-render";
        var command2 = "collapse-thread-render";
    } else {
        var url = "threads/" + content_id.thread_id + "/thread-" + content_id.thread_id + ".html";
        var command1 = "expand-thread";
        var command2 = "collapse-thread";
    }

    if (command === "expand-thread" || command === "expand-thread-render") {
        dataBin.collapsedThreads[content_id.id_string] = target_element.innerHTML;
        var request = new XMLHttpRequest();
        request.open('GET', url);
        request.responseType = "document";
        request.onload = function() {
            if (request.status === 200) {
                var expandHTML = request.response.getElementById("thread-expand-" + content_id.id_string).innerHTML;
                target_element.innerHTML = expandHTML;
            }
        };

        request.send();
    }

    if (command === "collapse-thread" || command === "collapse-thread-render") {
        target_element.innerHTML = dataBin.collapsedThreads[content_id.id_string];
    }

    nelliel.ui.swapContentAttribute(element, "data-alt-visual");
    nelliel.ui.switchDataCommand(element, command1, command2);
    nelliel.ui.applyHideContent();
}

nelliel.ui.highlightPost = function(content_id) {
    var post_container = document.getElementById("post-container-" + content_id.id_string);

    if (post_container !== null) {
        if (post_container.className.indexOf('post-hightlight') === -1) {
            post_container.className += " post-highlight";
        } else {
            post_container.className = post_container.className.replace(/\post-highlight\b/g, "");
        }
    }
}

nelliel.ui.inlineExpandReduce = function(element, command) {
    if (element === null) {
        return;
    }

    if (element.hasAttribute("data-other-dims")) {
        var new_location = element.getAttribute("data-other-loc");
        var old_location = element.getAttribute("src");
        var image_dims = element.getAttribute("data-other-dims");
        var width = image_dims.match(/w([0-9]+)/)[1];
        var height = image_dims.match(/h([0-9]+)/)[1];
        var old_width = element.getAttribute("width");
        var old_height = element.getAttribute("height");
        element.setAttribute("width", width);
        element.setAttribute("height", height);
        element.setAttribute("data-other-dims", 'w' + old_width + 'h' + old_height);
        element.setAttribute("src", new_location);
        element.setAttribute("data-other-loc", old_location);
        nelliel.ui.switchDataCommand(element, "inline-reduce", "inline-expand");
    }
}

nelliel.ui.showLinkedPost = function(element, event) {
    if (element === null) {
        return;
    }

    var href = element.getAttribute("href");
    var anchor_matches = href.match(/#t([0-9]+)p([0-9]+)/);
    var post_id = "cid_" + anchor_matches[1] + "_" + anchor_matches[2] + "_0";

    if (document.getElementById("post-cite-popup-" + post_id) !== null) {
        return;
    }

    var offsetY = window.pageYOffset || document.documentElement.scrollTop;
    var offsetX = window.pageXOffset || document.documentElement.scrollLeft;
    var popup_div = document.createElement("div");
    popup_div.id = "post-cite-popup-" + post_id;
    popup_div.setAttribute("class", "post-cite-popup");
    var element_rect = element.getBoundingClientRect();
    nelliel.ui.addBoundingClientRectProperties(element_rect);

    var request = new XMLHttpRequest();
    request.open('GET', element.getAttribute("href"));
    request.responseType = "document";

    request.onload = function() {
        if (request.status === 200) {
            var quoted_post = request.response.getElementById("post-container-" + post_id);
            quoted_post.className = quoted_post.className.replace(/\op-post\b/g, "reply-post");
            popup_div.appendChild(quoted_post);
            element.parentNode.insertBefore(popup_div, element);
            var popup_rect = quoted_post.getBoundingClientRect();
            nelliel.ui.addBoundingClientRectProperties(popup_rect);
            popup_div.style.left = (element_rect.right + offsetX + 5) + 'px';
            popup_div.style.top = ((element_rect.y + offsetY) - (popup_rect.height / 2)) + 'px';
        }
    };

    request.send();
}

nelliel.ui.hideLinkedPost = function(element, event) {
    if (element === null) {
        return;
    }

    var href = element.getAttribute("href");
    var anchor_matches = href.match(/#t([0-9]+)p([0-9]+)/);
    var post_id = "cid_" + anchor_matches[1] + "_" + anchor_matches[2] + "_0";
    var target_popup = document.getElementById("post-cite-popup-" + post_id);

    if (target_popup !== null) {
        target_popup.remove();
    }
}

nelliel.ui.citePost = function(element) {
    if (element === null) {
        return;
    }

    var content_id = nelliel.core.contentID(element.getAttribute("data-content-id"))
    var wordswordswords = document.getElementById("wordswordswords");
    wordswordswords.value = wordswordswords.value + '>>' + content_id.post_id + '\n';
}

nelliel.ui.toggleHidden = function(element) {
    if (element === null) {
        return;
    }

    if (element.className.search("hidden") === -1) {
        element.className += " hidden";
    } else {
        element.className = element.className.replace(/\bhidden\b/g, "");
    }
}

nelliel.ui.swapContentAttribute = function(element, attribute_name) {
    if (element === null) {
        return;
    }

    var inner = element.innerHTML;
    element.innerHTML = element.getAttribute(attribute_name);
    element.setAttribute(attribute_name, inner);
}

nelliel.ui.switchDataCommand = function(element, option_one, option_two) {
    if (element === null) {
        return;
    }

    var data_command = element.getAttribute("data-command");

    if (data_command.indexOf(option_one) > -1 ) {
        element.setAttribute("data-command", option_two);
    } else if (data_command.indexOf(option_two) > -1 ) {
        element.setAttribute("data-command", option_one);
    }
}

nelliel.ui.addBoundingClientRectProperties = function(bounding_rect) {
    bounding_rect.width = bounding_rect.width || bounding_rect.right - bounding_rect.left;
    bounding_rect.height = bounding_rect.height || bounding_rect.bottom - bounding_rect.top;
    bounding_rect.x = bounding_rect.x || bounding_rect.left + (bounding_rect.width / 2);
    bounding_rect.y = bounding_rect.y || bounding_rect.top + (bounding_rect.height / 2);
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
import _ from 'lodash';
import fs from 'fs';
import path from 'path';
import yargs from 'yargs';
import log from '../core/logger';
require('colors');

import PluginScanner from '../core/plugin_scanner';
import registry from '../core/registry';
import JsonFile from '../utils/json_file';
import * as utils from '../utils/util';

// Set up the logger with debug level if requested.
const debugFlags = ['-d', '--debug'];
const debug = _.some(debugFlags, (flag) => _.includes(process.argv, flag));
// Use require to workaround importing issue.
// See https://github.com/winstonjs/winston/issues/801
log.level = debug ? 'debug' : 'info';
log.debug(`Initialised log with level ${log.level.cyan}`);

// The path to the directory where global node modules are installed.
const GLOBAL_NODE_ROOT =
    path.join(process.config.variables.node_prefix, 'lib/node_modules');

// Follow links and expand parent dirs to find the package.json of this script.
const binDir = path.dirname(fs.realpathSync(process.argv[1]));
const packagePath = fs.realpathSync(`${binDir}/../../package.json`);
const version = new JsonFile(packagePath).read().version;

// Initialise yargs for command line parsing.
yargs
    .usage(`Usage: $0 ${'<command>'.cyan} ${'<action>'.magenta} [options]`)
    .version(version).alias('v', 'version')
// Merge configuration into the yargs options.
    .config('config', utils.loadConfig)
    .alias('c', 'config')
    .default('config', '~/.oi/config.json')
    .option('d', {
      'alias': 'debug',
      'default': false,
      'describe': 'Enable debug logging',
    })
    .global('d')
    .global('c')
    .option('completion');

// Scan for plugins that will register as yargs commands.
new PluginScanner({
  paths: GLOBAL_NODE_ROOT,
  host: {version, log},
}).loadPlugins((err, pluginModules) => {
  if (err) {
    log.info('Failed to load plugins');
  } else {
    // Register any loaded plugin modules.
    registry.register(pluginModules);
  }

  // Register the commands of each module in the registry with yargs.
  registry.registerAllCommands(yargs);

  // Generate the help after everything is registered.
  log.debug('Generating help');
  yargs
      .demandCommand(1, 'Must provide a command'.red)
      .strict()
      .fail(utils.fail)
      .help().alias('h', 'help')
      .completion();

  log.debug('Invoking yargs processing...');
  yargs.argv;
});

<commit_msg>Fix Node root<commit_after>#!/usr/bin/env node
import _ from 'lodash';
import fs from 'fs';
import path from 'path';
import yargs from 'yargs';
import log from '../core/logger';
require('colors');

import PluginScanner from '../core/plugin_scanner';
import registry from '../core/registry';
import JsonFile from '../utils/json_file';
import * as utils from '../utils/util';

// Set up the logger with debug level if requested.
const debugFlags = ['-d', '--debug'];
const debug = _.some(debugFlags, (flag) => _.includes(process.argv, flag));
// Use require to workaround importing issue.
// See https://github.com/winstonjs/winston/issues/801
log.level = debug ? 'debug' : 'info';
log.debug(`Initialised log with level ${log.level.cyan}`);

// The path to the directory where global node modules are installed.
const GLOBAL_NODE_ROOT =
    process.execPath.replace('bin/node', 'lib/node_modules');

// Follow links and expand parent dirs to find the package.json of this script.
const binDir = path.dirname(fs.realpathSync(process.argv[1]));
const packagePath = fs.realpathSync(`${binDir}/../../package.json`);
const version = new JsonFile(packagePath).read().version;

// Initialise yargs for command line parsing.
yargs
    .usage(`Usage: $0 ${'<command>'.cyan} ${'<action>'.magenta} [options]`)
    .version(version).alias('v', 'version')
// Merge configuration into the yargs options.
    .config('config', utils.loadConfig)
    .alias('c', 'config')
    .default('config', '~/.oi/config.json')
    .option('d', {
      'alias': 'debug',
      'default': false,
      'describe': 'Enable debug logging',
    })
    .global('d')
    .global('c')
    .option('completion');

// Scan for plugins that will register as yargs commands.
new PluginScanner({
  paths: GLOBAL_NODE_ROOT,
  host: {version, log},
}).loadPlugins((err, pluginModules) => {
  if (err) {
    log.info('Failed to load plugins');
  } else {
    // Register any loaded plugin modules.
    registry.register(pluginModules);
  }

  // Register the commands of each module in the registry with yargs.
  registry.registerAllCommands(yargs);

  // Generate the help after everything is registered.
  log.debug('Generating help');
  yargs
      .demandCommand(1, 'Must provide a command'.red)
      .strict()
      .fail(utils.fail)
      .help().alias('h', 'help')
      .completion();

  log.debug('Invoking yargs processing...');
  yargs.argv;
});
<|endoftext|>


<commit_before>/* global Promise */
var registerComponent = require('../core/register-component').registerComponent;
var srcLoader = require('../utils/src-loader');
var THREE = require('../../lib/three');
var utils = require('../vr-utils');

var CubeLoader = new THREE.CubeTextureLoader();
var texturePromises = {};

/**
 * Material component.
 *
 * @namespace material
 * @params {string} color - Diffuse color.
 * @params {string} envMap - To load a environment cubemap. Takes a selector
           to an element containing six img elements, or a comma-separated
           string of direct url()s.
 * @params {number} height - Height to render texture.
 * @params {number} metalness - Parameter for physical/standard material.
 * @params {number} opacity - [0-1].
 * @params {boolean} receiveLight - Determines whether the material is shaded.
 * @params {number} roughness - Parameter for physical/standard material.
 * @params {string} src - To load a texture. takes a selector to an img/video
           element or a direct url().
 * @params {boolean} transparent - Whether alpha channel of texture image is
           rendered transparent.
 * @params {number} width - Width to render texture.
 */
module.exports.Component = registerComponent('material', {
  defaults: {
    value: {
      color: '#FFF',
      envMap: '',
      height: 360,
      metalness: 0.0,
      opacity: 1.0,
      receiveLight: true,
      reflectivity: 1.0,
      roughness: 0.5,
      src: '',
      transparent: false,
      width: 640
    }
  },

  /**
   * Initialize material.
   */
  init: {
    value: function () {
      this.textureSrc = null;
      this.isLoadingEnvMap = false;
      this.el.object3D.material = this.updateOrCreateMaterial();
    }
  },

  /**
   * Update material.
   */
  update: {
    value: function () {
      this.el.object3D.material = this.updateOrCreateMaterial();
    }
  },

  /**
   * Update or create material.
   * Returned material type depends on receiveLight.
   *   receiveLight: false - MeshBasicMaterial.
   *   receiveLight: true - MeshStandardMaterial.
   *
   * @return {object} material
   */
  updateOrCreateMaterial: {
    value: function () {
      var data = this.data;
      var material;

      if (data.receiveLight) {
        // Physical material.
        material = this.updateOrCreateMaterialHelper({
          color: new THREE.Color(data.color),
          side: this.getSides(),
          opacity: data.opacity,
          transparent: data.opacity < 1,
          metalness: data.metalness,
          reflectivity: data.reflectivity,
          roughness: data.roughness
        }, 'MeshStandardMaterial');
        // Environment cubemaps.
        if (data.envMap && !this.isLoadingEnvMap) {
          this.loadEnvMap(material, data.envMap);
        } else {
          material.envMap = null;
          material.needsUpdate = true;
        }
      } else {
        material = this.updateOrCreateMaterialHelper({
          // Basic material.
          color: new THREE.Color(data.color),
          side: this.getSides(),
          opacity: data.opacity,
          transparent: data.transparent
        }, 'MeshBasicMaterial');
      }

      // Textures.
      var src = data.src;
      if (src) {
        if (src !== this.textureSrc) {
          // Texture added or changed.
          this.textureSrc = src;
          srcLoader.validateSrc(src, this.loadImage.bind(this),
                                this.loadVideo.bind(this));
        }
      } else {
        // Texture removed.
        material.map = null;
        material.needsUpdate = true;
      }

      return material;
    }
  },

  /**
   * Updates this.material using data, creates new material if this.material
   * doesn't yet exist.
   *
   * @params {object} data - Attributes to set on the material.
   * @params {string} type - Type of material to create (if necessary).
   * @returns {object} material - three.js material based on `type`.
   */
  updateOrCreateMaterialHelper: {
    value: function (data, type) {
      var material = this.material;
      var reuseMaterial = material && material.type === type;
      if (reuseMaterial) {
        Object.keys(data).forEach(function (key) {
          material[key] = data[key];
        });
      } else {
        material = new THREE[type](data);
      }
      this.material = material;
      return material;
    }
  },

  /**
   * Returns an integer for which new material face sides will be rendered.
   *
   * @returns {Integer} `THREE.DoubleSide` (`0`) or `THREE.FrontSide` (`2`)
   */
  getSides: {
    value: function () {
      var geometry = this.el.components.geometry;
      if (geometry && geometry.data.openEnded) {
        // For performance reasons, we special case open-ended cylinders
        // for rendering both faces.
        return THREE.DoubleSide;
      }
      return THREE.FrontSide;
    }
  },

  /**
   * Handle environment cubemap. Textures are cached in texturePromises.
   *
   * @param {object} material - three.js material.
   * @param {string} envMap - Query selector or comma-separated list of url()s.
   */
  loadEnvMap: {
    value: function (material, envMap) {
      var self = this;
      self.isLoadingEnvMap = true;
      if (texturePromises[envMap]) {
        // Another material is already loading this texture. Wait on promise.
        texturePromises[envMap].then(function (cube) {
          self.isLoadingEnvMap = false;
          material.envMap = cube;
          material.needsUpdate = true;
        });
      } else {
        // Material is first to load this texture. Load and resolve texture.
        texturePromises[envMap] = new Promise(function (resolve) {
          srcLoader.validateCubemapSrc(envMap, function loadEnvMap (urls) {
            CubeLoader.load(urls, function (cube) {
              // Texture loaded.
              self.isLoadingEnvMap = false;
              material.envMap = cube;
              resolve(cube);
            });
          });
        });
      }
    }
  },

  /**
   * Sets image texture on material as map.
   *
   * @params {string|object} src - An <img> element or url to an image file.
   */
  loadImage: {
    value: function (src) {
      var texture;
      var isEl = typeof src !== 'string';
      if (isEl) {
        texture = new THREE.Texture(src);
        texture.needsUpdate = true;
      } else {
        texture = THREE.ImageUtils.loadTexture(src);
      }
      this.material.needsUpdate = true;
      this.material.map = texture;
    }
  },

  /**
   * Creates a video element to be used as a texture.
   *
   * @params {string} src - the url pointing to the video file.
   */
  createVideoEl: {
    value: function (src) {
      var el = this.videoEl || document.createElement('video');
      function onError () {
        utils.warn('The url "$s" is not a valid image or video', src);
      }
      el.width = this.data.width;
      el.height = this.data.height;
      // Attach event listeners if brand new video element.
      if (el !== this.videoEl) {
        el.autoplay = true;
        el.loop = true;
        el.crossOrigin = true;
        el.addEventListener('error', onError, true);
        this.videoEl = el;
      }
      el.src = src;
      return el;
    }
  },

  /**
   * Sets video texture on material as map.
   *
   * @params {string|object} src - A <video> element or url to a video file.
   */
  loadVideo: {
    value: function (src) {
      // three.js video texture loader requires a <video>.
      var videoEl = typeof src !== 'string' ? src : this.createVideoEl(src);
      var texture = new THREE.VideoTexture(videoEl);
      texture.minFilter = THREE.LinearFilter;
      texture.needsUpdate = true;
      this.material.map = texture;
      this.material.needsUpdate = true;
    }
  }
});
<commit_msg>enable texture tiling in material<commit_after>/* global Promise */
var registerComponent = require('../core/register-component').registerComponent;
var srcLoader = require('../utils/src-loader');
var THREE = require('../../lib/three');
var utils = require('../vr-utils');

var CubeLoader = new THREE.CubeTextureLoader();
var texturePromises = {};

/**
 * Material component.
 *
 * @namespace material
 * @params {string} color - Diffuse color.
 * @params {string} envMap - To load a environment cubemap. Takes a selector
           to an element containing six img elements, or a comma-separated
           string of direct url()s.
 * @params {number} height - Height to render texture.
 * @params {number} metalness - Parameter for physical/standard material.
 * @params {number} opacity - [0-1].
 * @params {boolean} receiveLight - Determines whether the material is shaded.
 * @params {number} reflectivity - Parameter for physical/standard material.
 * @params {string} repeat - X and Y value for size of texture repeating
           (in UV units).
 * @params {number} roughness - Parameter for physical/standard material.
 * @params {string} src - To load a texture. takes a selector to an img/video
           element or a direct url().
 * @params {boolean} transparent - Whether to render transparent the alpha
           channel of a texture (e.g., .png).
 * @params {number} width - Width to render texture.
 */
module.exports.Component = registerComponent('material', {
  defaults: {
    value: {
      color: '#FFF',
      envMap: '',
      height: 360,
      metalness: 0.0,
      opacity: 1.0,
      receiveLight: true,
      reflectivity: 1.0,
      repeat: '',
      roughness: 0.5,
      src: '',
      transparent: false,
      width: 640
    }
  },

  /**
   * Initialize material.
   */
  init: {
    value: function () {
      this.textureSrc = null;
      this.isLoadingEnvMap = false;
      this.el.object3D.material = this.updateOrCreateMaterial();
    }
  },

  /**
   * Update material.
   */
  update: {
    value: function () {
      this.el.object3D.material = this.updateOrCreateMaterial();
    }
  },

  /**
   * Update or create material.
   * Returned material type depends on receiveLight.
   *   receiveLight: false - MeshBasicMaterial.
   *   receiveLight: true - MeshStandardMaterial.
   *
   * @return {object} material
   */
  updateOrCreateMaterial: {
    value: function () {
      var data = this.data;
      var material;

      if (data.receiveLight) {
        // Physical material.
        material = this.updateOrCreateMaterialHelper({
          color: new THREE.Color(data.color),
          side: this.getSides(),
          opacity: data.opacity,
          transparent: data.opacity < 1,
          metalness: data.metalness,
          reflectivity: data.reflectivity,
          roughness: data.roughness
        }, 'MeshStandardMaterial');
        // Environment cubemaps.
        if (data.envMap && !this.isLoadingEnvMap) {
          this.loadEnvMap(material, data.envMap);
        } else {
          material.envMap = null;
          material.needsUpdate = true;
        }
      } else {
        material = this.updateOrCreateMaterialHelper({
          // Basic material.
          color: new THREE.Color(data.color),
          side: this.getSides(),
          opacity: data.opacity,
          transparent: data.transparent
        }, 'MeshBasicMaterial');
      }

      // Textures.
      var src = data.src;
      if (src) {
        if (src !== this.textureSrc) {
          // Texture added or changed.
          this.textureSrc = src;
          srcLoader.validateSrc(src, this.loadImage.bind(this),
                                this.loadVideo.bind(this));
        }
      } else {
        // Texture removed.
        material.map = null;
        material.needsUpdate = true;
      }

      return material;
    }
  },

  /**
   * Updates this.material using data, creates new material if this.material
   * doesn't yet exist.
   *
   * @params {object} data - Attributes to set on the material.
   * @params {string} type - Type of material to create (if necessary).
   * @returns {object} material - three.js material based on `type`.
   */
  updateOrCreateMaterialHelper: {
    value: function (data, type) {
      var material = this.material;
      var reuseMaterial = material && material.type === type;
      if (reuseMaterial) {
        Object.keys(data).forEach(function (key) {
          material[key] = data[key];
        });
      } else {
        material = new THREE[type](data);
      }
      this.material = material;
      return material;
    }
  },

  /**
   * Returns an integer for which new material face sides will be rendered.
   *
   * @returns {Integer} `THREE.DoubleSide` (`0`) or `THREE.FrontSide` (`2`)
   */
  getSides: {
    value: function () {
      var geometry = this.el.components.geometry;
      if (geometry && geometry.data.openEnded) {
        // For performance reasons, we special case open-ended cylinders
        // for rendering both faces.
        return THREE.DoubleSide;
      }
      return THREE.FrontSide;
    }
  },

  /**
   * Handle environment cubemap. Textures are cached in texturePromises.
   *
   * @param {object} material - three.js material.
   * @param {string} envMap - Query selector or comma-separated list of url()s.
   */
  loadEnvMap: {
    value: function (material, envMap) {
      var self = this;
      self.isLoadingEnvMap = true;
      if (texturePromises[envMap]) {
        // Another material is already loading this texture. Wait on promise.
        texturePromises[envMap].then(function (cube) {
          self.isLoadingEnvMap = false;
          material.envMap = cube;
          material.needsUpdate = true;
        });
      } else {
        // Material is first to load this texture. Load and resolve texture.
        texturePromises[envMap] = new Promise(function (resolve) {
          srcLoader.validateCubemapSrc(envMap, function loadEnvMap (urls) {
            CubeLoader.load(urls, function (cube) {
              // Texture loaded.
              self.isLoadingEnvMap = false;
              material.envMap = cube;
              resolve(cube);
            });
          });
        });
      }
    }
  },

  /**
   * Sets image texture on material as map.
   *
   * @params {string|object} src - An <img> element or url to an image file.
   */
  loadImage: {
    value: function (src) {
      var repeat = this.data.repeat;
      var repeatXY;
      var texture;
      var isEl = typeof src !== 'string';
      if (isEl) {
        texture = new THREE.Texture(src);
        texture.needsUpdate = true;
      } else {
        texture = THREE.ImageUtils.loadTexture(src);
      }
      if (repeat) {
        repeatXY = repeat.split(' ');
        if (repeatXY.length === 2) {
          texture.wrapS = THREE.RepeatWrapping;
          texture.wrapT = THREE.RepeatWrapping;
          texture.repeat.set(parseInt(repeatXY[0], 10),
                             parseInt(repeatXY[1], 10));
        }
      }
      this.material.needsUpdate = true;
      this.material.map = texture;
    }
  },

  /**
   * Creates a video element to be used as a texture.
   *
   * @params {string} src - the url pointing to the video file.
   */
  createVideoEl: {
    value: function (src) {
      var el = this.videoEl || document.createElement('video');
      function onError () {
        utils.warn('The url "$s" is not a valid image or video', src);
      }
      el.width = this.data.width;
      el.height = this.data.height;
      // Attach event listeners if brand new video element.
      if (el !== this.videoEl) {
        el.autoplay = true;
        el.loop = true;
        el.crossOrigin = true;
        el.addEventListener('error', onError, true);
        this.videoEl = el;
      }
      el.src = src;
      return el;
    }
  },

  /**
   * Sets video texture on material as map.
   *
   * @params {string|object} src - A <video> element or url to a video file.
   */
  loadVideo: {
    value: function (src) {
      // three.js video texture loader requires a <video>.
      var videoEl = typeof src !== 'string' ? src : this.createVideoEl(src);
      var texture = new THREE.VideoTexture(videoEl);
      texture.minFilter = THREE.LinearFilter;
      texture.needsUpdate = true;
      this.material.map = texture;
      this.material.needsUpdate = true;
    }
  }
});
<|endoftext|>


<commit_before>var Device = require('./Device')

module.exports =  _.merge(_.cloneDeep(Device), {
  attributes: {
    status: function () {
      return this.isyDevice().getCurrentLightState() ? 'on' : 'off'
    },

    level: function () {
      return this.isyDevice().getCurrentLightDimState()
    },

    getStatus: function () {
      return { status: this.status(), level: this.level() }
    },

    refreshStatus: function () {
      this.sendSmartThingsUpdate()
      return { command: 'refresh_status' }
    },

    turnOn: function () {
      this.isyDevice().sendLightCommand(true, success => {})
      return { command: 'turn_on' }
    },

    turnOff: function () {
      this.isyDevice().sendLightCommand(false, success => {})
      return { command: 'turn_off' }
    },

    setLevel: function (level) {
      this.isyDevice().sendLightDimCommand(level, success => {})
      return { command: 'set_level', level: level }
    },

    brighten: function () {
      this.isyDevice().sendLightDimCommand(Math.min(this.level() + 5, 100), success => {})
      return { command: 'brighten' }
    },

    dim: function () {
      this.isyDevice().sendLightDimCommand(Math.max(this.level() - 5, 1), success => {})
      return { command: 'dim' }
    }
  }
})
<commit_msg>Improved dimming<commit_after>var Device = require('./Device')

module.exports =  _.merge(_.cloneDeep(Device), {
  attributes: {
    status: function () {
      return this.isyDevice().getCurrentLightState() ? 'on' : 'off'
    },

    level: function () {
      return this.isyDevice().getCurrentLightDimState()
    },

    getStatus: function () {
      return { status: this.status(), level: this.level() }
    },

    refreshStatus: function () {
      this.sendSmartThingsUpdate()
      return { command: 'refresh_status' }
    },

    turnOn: function () {
      this.isyDevice().sendLightCommand(true, success => {})
      return { command: 'turn_on' }
    },

    turnOff: function () {
      this.isyDevice().sendLightCommand(false, success => {})
      return { command: 'turn_off' }
    },

    setLevel: function (level) {
      if (level <  1) {
        level = 0
      }
      if (level > 100) {
        level = 100
      }
      if (level === 0) {
        this.isyDevice().sendLightCommand(false, success => {})
      } else {
        this.isyDevice().sendLightDimCommand(level, success => {})
      }
      return { command: 'set_level', level: level }
    },

    brighten: function () {
      var level = Math.min(this.level() + 5, 100
      this.isyDevice().sendLightDimCommand(level), success => {})
      return { command: 'brighten' }
    },

    dim: function () {
      var level = Math.max(this.level() - 5, 1
      this.isyDevice().sendLightDimCommand(level), success => {})
      return { command: 'dim' }
    }
  }
})
<|endoftext|>


<commit_before>/* jslint node: true */
'use strict';

var ansi		= require('./ansi_term.js');
var colorCodes	= require('./color_codes.js');
var theme		= require('./theme.js');
var moduleUtil	= require('./module_util.js');
var Config		= require('./config.js').config;

var packageJson = require('../package.json');

var assert		= require('assert');
var util		= require('util');

exports.connectEntry	= connectEntry;

function ansiQueryTermSizeIfNeeded(client, cb) {
	if(client.term.termHeight > 0 || client.term.termWidth > 0) {
		cb(true);
		return;
	}

	var done = function(res) {
		client.removeListener('cursor position report', cprListener);
		clearTimeout(giveUpTimer);
		cb(res);
	};

	var cprListener = function(pos) {
		//
		//	If we've already found out, disregard
		//
		if(client.term.termHeight > 0 || client.term.termWidth > 0) {
			done(true);
			return;
		}

		assert(2 === pos.length);
		var h = pos[0];
		var w = pos[1];

		//
		//	Netrunner for example gives us 1x1 here. Not really useful. Ignore
		//	values that seem obviously bad.
		//
		if(h < 10 || w < 10) {
			client.log.warn(
				{ height : h, width : w },
				'Ignoring ANSI CPR screen size query response due to very small values');
			done(false);
			return;
		}

		client.term.termHeight	= h;
		client.term.termWidth	= w;

		client.log.debug(
			{
				termWidth	: client.term.termWidth,
				termHeight	: client.term.termHeight,
				source		: 'ANSI CPR'
			},
			'Window size updated'
			);

		done(true);
	};

	client.once('cursor position report', cprListener);

	//	give up after 2s
	var giveUpTimer = setTimeout(function onTimeout() {
		done(false);
	}, 2000);

	//	This causes
	client.term.write(ansi.queryScreenSize());
}

function prepareTerminal(term) {
	term.write(ansi.normal());
	term.write(ansi.disableVT100LineWrapping());
	//	:TODO: set xterm stuff -- see x84/others
}

function displayBanner(term) {
	//	:TODO: add URL(s) to banner
	term.write(colorCodes.pipeToAnsi(util.format(
		'|33Conected to |32EN|33|01i|00|32|22GMA|32|01½|00 |33BBS version|31|01 %s\n' +
		'|00|33Copyright (c) 2014-2015 Bryan Ashby\n' +
		'|00', packageJson.version)));
}

function connectEntry(client) {
	var term = client.term;

	/*
	theme.displayThemeArt({client : client, name : 'DM-ENIG.ANS'}, function onArt() {
		return;
	});
*/
	/*
	var iconv = require('iconv-lite');
	var art1 = require('fs').readFileSync('/home/nuskooler/dev/enigma-bbs/mods/art/DM-ENIG.ANS');
	console.log(typeof art1);
	art1 = iconv.decode(art1, 'cp437');
	console.log(typeof art1)
	term.output.write(art1);
	//term.output.write(require('iconv-lite').encode(art1, 'CP437'));
	return;
	*/

	//	:TODO: Enthral for example queries cursor position & checks if it worked. This might be good
	//	:TODO: How to detect e.g. if show/hide cursor can work? Probably can if CPR is avail

	//
	//	Some terminal clients can be detected using a nonstandard ANSI DSR
	//
	term.rawWrite(ansi.queryDeviceAttributes(0));

	//	:TODO: PuTTY will apparently respond with "PuTTY" if a CTRL-E is sent to it

	//
	//	If we don't yet know the client term width/height,
	//	try with a nonstandard ANSI DSR type request.
	//
	ansiQueryTermSizeIfNeeded(client, function ansiCprResult(result) {

		if(!result) {
			//
			//	We still don't have something good for term height/width.
			//	Default to DOS size 80x25.
			//
			//	:TODO: Netrunner is currenting hitting this and it feels wrong. Why is NAWS/ENV/CPR all failing???
			client.log.warn('Failed to negotiate term size; Defaulting to 80x25!');

			term.termHeight	= 25;
			term.termWidth	= 80;
		}

		prepareTerminal(term);

		//
		//	Always show a ENiGMA½ banner
		//
		displayBanner(term);

		setTimeout(function onTimeout() {
			client.gotoMenuModule( { name : Config.firstMenu } );
		}, 500);
	});
}

<commit_msg>* Add l33t.codes URL to connect banner<commit_after>/* jslint node: true */
'use strict';

var ansi		= require('./ansi_term.js');
var colorCodes	= require('./color_codes.js');
var theme		= require('./theme.js');
var moduleUtil	= require('./module_util.js');
var Config		= require('./config.js').config;

var packageJson = require('../package.json');

var assert		= require('assert');
var util		= require('util');

exports.connectEntry	= connectEntry;

function ansiQueryTermSizeIfNeeded(client, cb) {
	if(client.term.termHeight > 0 || client.term.termWidth > 0) {
		cb(true);
		return;
	}

	var done = function(res) {
		client.removeListener('cursor position report', cprListener);
		clearTimeout(giveUpTimer);
		cb(res);
	};

	var cprListener = function(pos) {
		//
		//	If we've already found out, disregard
		//
		if(client.term.termHeight > 0 || client.term.termWidth > 0) {
			done(true);
			return;
		}

		assert(2 === pos.length);
		var h = pos[0];
		var w = pos[1];

		//
		//	Netrunner for example gives us 1x1 here. Not really useful. Ignore
		//	values that seem obviously bad.
		//
		if(h < 10 || w < 10) {
			client.log.warn(
				{ height : h, width : w },
				'Ignoring ANSI CPR screen size query response due to very small values');
			done(false);
			return;
		}

		client.term.termHeight	= h;
		client.term.termWidth	= w;

		client.log.debug(
			{
				termWidth	: client.term.termWidth,
				termHeight	: client.term.termHeight,
				source		: 'ANSI CPR'
			},
			'Window size updated'
			);

		done(true);
	};

	client.once('cursor position report', cprListener);

	//	give up after 2s
	var giveUpTimer = setTimeout(function onTimeout() {
		done(false);
	}, 2000);

	//	This causes
	client.term.write(ansi.queryScreenSize());
}

function prepareTerminal(term) {
	term.write(ansi.normal());
	term.write(ansi.disableVT100LineWrapping());
	//	:TODO: set xterm stuff -- see x84/others
}

function displayBanner(term) {
	//	:TODO: add URL(s) to banner
	term.write(colorCodes.pipeToAnsi(util.format(
		'|33Conected to |32EN|33|01i|00|32|22GMA|32|01½|00 |33BBS version|31|01 %s\n'	+
		'|00|33Copyright (c) 2014-2015 Bryan Ashby |33|01- |31|01http://l33t.codes/\n'	+
		'|00', packageJson.version)));
}

function connectEntry(client) {
	var term = client.term;

	/*
	theme.displayThemeArt({client : client, name : 'DM-ENIG.ANS'}, function onArt() {
		return;
	});
*/
	/*
	var iconv = require('iconv-lite');
	var art1 = require('fs').readFileSync('/home/nuskooler/dev/enigma-bbs/mods/art/DM-ENIG.ANS');
	console.log(typeof art1);
	art1 = iconv.decode(art1, 'cp437');
	console.log(typeof art1)
	term.output.write(art1);
	//term.output.write(require('iconv-lite').encode(art1, 'CP437'));
	return;
	*/

	//	:TODO: Enthral for example queries cursor position & checks if it worked. This might be good
	//	:TODO: How to detect e.g. if show/hide cursor can work? Probably can if CPR is avail

	//
	//	Some terminal clients can be detected using a nonstandard ANSI DSR
	//
	term.rawWrite(ansi.queryDeviceAttributes(0));

	//	:TODO: PuTTY will apparently respond with "PuTTY" if a CTRL-E is sent to it

	//
	//	If we don't yet know the client term width/height,
	//	try with a nonstandard ANSI DSR type request.
	//
	ansiQueryTermSizeIfNeeded(client, function ansiCprResult(result) {

		if(!result) {
			//
			//	We still don't have something good for term height/width.
			//	Default to DOS size 80x25.
			//
			//	:TODO: Netrunner is currenting hitting this and it feels wrong. Why is NAWS/ENV/CPR all failing???
			client.log.warn('Failed to negotiate term size; Defaulting to 80x25!');

			term.termHeight	= 25;
			term.termWidth	= 80;
		}

		prepareTerminal(term);

		//
		//	Always show a ENiGMA½ banner
		//
		displayBanner(term);

		setTimeout(function onTimeout() {
			client.gotoMenuModule( { name : Config.firstMenu } );
		}, 500);
	});
}

<|endoftext|>