<commit_before>// Requirements
const mqtt = require('mqtt')
var Redis = require('redis')
var async = require('async')
const _ = require('lodash')
var is_test_mode = process.env.TEST_MODE

if (is_test_mode == 'true') {
    is_test_mode = true
} else if (is_test_mode != true) {
    is_test_mode = false
}

const rules = require('./homeautomation-js-lib/rules.js')
const logging = require('./homeautomation-js-lib/logging.js')

require('./homeautomation-js-lib/devices.js')
require('./homeautomation-js-lib/mqtt_helpers.js')
require('./homeautomation-js-lib/redis_helpers.js')

const utilities = require('./lib/utilities.js')
const schedule = require('./lib/scheduled-jobs.js')
const evaluation = require('./lib/evaluation.js')
const metrics = require('./homeautomation-js-lib/stats.js')

const config_path = process.env.TRANSFORM_CONFIG_PATH

// Setup MQTT
global.client = mqtt.setupClient(function() {
    logging.info('MQTT Connected', {
        action: 'mqtt-connected'
    })
    global.client.subscribe('#')

}, function() {
    logging.error('Disconnected', {
        action: 'mqtt-disconnected'
    })
})

global.publishEvents = []

global.publish = function(rule_name, expression, valueOrExpression, topic, message) {
    logging.info('=> rule: ' + rule_name + '  publishing: ' + topic + ':' + message + ' (expression: ' + expression + ' | value: ' + valueOrExpression + ')')
    global.client.publish(topic, message)
}

global.devices_to_monitor = []

global.changeProcessor = function(rules, context, topic, message) {
    context[utilities.update_topic_for_expression(topic)] = message

    const ruleStartTime = new Date().getTime()
    logging.verbose(' rule processing start ', {
        action: 'rule-processing-start',
        start_time: ruleStartTime
    })

    var ruleProcessor = function(rule, rule_name, callback) {
        const disabled = rule.disabled

        if (disabled == true) return

        const watch = rule.watch

        if (!_.isNil(watch) && !_.isNil(watch.devices)) {
            if (watch.devices.indexOf(topic) !== -1) {

                logging.verbose('matched topic to rule', {
                    action: 'rule-match',
                    rule_name: rule_name,
                    topic: topic,
                    message: utilities.convertToNumberIfNeeded(message),
                    rule: rule,
                    context: context
                })

                evaluation.evalulateValue(topic, context, rule_name, rule)
            }
        }

        callback()
    }

    var configProcessor = function(config, callback) {
        async.eachOf(config, ruleProcessor)
        callback()
    }

    async.each(rules, configProcessor)

    logging.verbose(' rule processing done ', {
        action: 'rule-processing-done',
        processing_time: ((new Date().getTime()) - ruleStartTime)
    })
}

global.client.on('message', (topic, message) => {
    if (is_test_mode == true)
        return

    if (!global.devices_to_monitor.includes(topic))
        return

    message = utilities.convertToNumberIfNeeded(message)
    const redisStartTime = new Date().getTime()
    logging.verbose(' redis query', {
        action: 'redis-query-start',
        start_time: redisStartTime
    })

    global.redis.mget(global.devices_to_monitor, function(err, values) {
        const redisQueryTime = ((new Date().getTime()) - redisStartTime)
        logging.verbose(' redis query done', {
            action: 'redis-query-done',
            query_time: redisQueryTime
        })

        metrics.submit('redis_query_time', redisQueryTime)

        var context = {}

        for (var index = 0; index < global.devices_to_monitor.length; index++) {
            const key = global.devices_to_monitor[index]
            const value = values[index]
            const newKey = utilities.update_topic_for_expression(key)
            if (key === topic)
                context[newKey] = utilities.convertToNumberIfNeeded(message)
            else
                context[newKey] = utilities.convertToNumberIfNeeded(value)
        }

        try {
            var jsonFound = JSON.parse(message)
            if (!_.isNil(jsonFound)) {
                Object.keys(jsonFound).forEach(function(key) {
                    context[key] = jsonFound[key]
                })
            }
        } catch (err) {
            logging.debug('invalid json')
        }

        global.changeProcessor(rules.get_configs(), context, topic, message)
    })
})

global.redis = Redis.setupClient(function() {
    logging.info('redis connected ', {
        action: 'redis-connected'
    })
    if (is_test_mode == false) {
        logging.info('loading rules')
        rules.load_path(config_path)
    } else {
        logging.info('not - loading rules')

    }
})

rules.on('rules-loaded', () => {
    if (is_test_mode == true) {
        logging.info('test mode, not loading rules')
        return
    }
    logging.info('Loading rules')

    global.devices_to_monitor = []

    rules.ruleIterator(function(rule_name, rule) {
        const watch = rule.watch
        if (!_.isNil(watch)) {
            const devices = watch.devices
            if (!_.isNil(devices)) {
                devices.forEach(function(device) {
                    global.devices_to_monitor.push(device)
                }, this)
            }
        }

        const rules = rule.rules
        if (!_.isNil(rules)) {
            const expression = rules.expression
            logging.debug('expression :' + expression)
            if (!_.isNil(expression)) {

                var foundDevices = expression.match(/\/([a-z,0-9,\-,_,/])*/g)

                if (!_.isNil(foundDevices)) {
                    foundDevices.forEach(function(device) {
                        global.devices_to_monitor.push(device)
                    }, this)
                }
            }
        }

        const actions = rule.actions
        if (!_.isNil(actions)) {
            Object.keys(actions).forEach(function(action) {
                const action_value = actions[action]

                var foundDevices = action_value.match(/\/([a-z,0-9,\-,_,/])*/g)

                if (!_.isNil(foundDevices)) {
                    foundDevices.forEach(function(device) {
                        global.devices_to_monitor.push(device)
                    }, this)
                }
            }, this)
        }

    })

    global.devices_to_monitor = utilities.unique(global.devices_to_monitor)

    logging.info('rules loaded ', {
        action: 'rules-loaded',
        devices_to_monitor: global.devices_to_monitor
    })

    global.redis.mget(global.devices_to_monitor, function(err, values) {
        logging.debug('devices :' + JSON.stringify(global.devices_to_monitor))
        logging.debug('values :' + JSON.stringify(values))
    })

    schedule.scheduleJobs()
})

global.clearQueues = function() {
    evaluation.clearQueues()
}<commit_msg>Fixing tests to run in isolation<commit_after>// Requirements
const mqtt = require('mqtt')
var Redis = require('redis')
var async = require('async')
const _ = require('lodash')
var is_test_mode = process.env.TEST_MODE

if (is_test_mode == 'true') {
    is_test_mode = true
} else if (is_test_mode != true) {
    is_test_mode = false
}

const rules = require('./homeautomation-js-lib/rules.js')
const logging = require('./homeautomation-js-lib/logging.js')

require('./homeautomation-js-lib/devices.js')
require('./homeautomation-js-lib/mqtt_helpers.js')
require('./homeautomation-js-lib/redis_helpers.js')

const utilities = require('./lib/utilities.js')
const schedule = require('./lib/scheduled-jobs.js')
const evaluation = require('./lib/evaluation.js')
const metrics = require('./homeautomation-js-lib/stats.js')

const config_path = process.env.TRANSFORM_CONFIG_PATH

// Setup MQTT
if (is_test_mode === false) {
    global.client = mqtt.setupClient(function() {
        logging.info('MQTT Connected', {
            action: 'mqtt-connected'
        })
        global.client.subscribe('#')

    }, function() {
        logging.error('Disconnected', {
            action: 'mqtt-disconnected'
        })
    })
}
global.publishEvents = []

global.publish = function(rule_name, expression, valueOrExpression, topic, message) {
    logging.info('=> rule: ' + rule_name + '  publishing: ' + topic + ':' + message + ' (expression: ' + expression + ' | value: ' + valueOrExpression + ')')
    global.client.publish(topic, message)
}

global.devices_to_monitor = []

global.changeProcessor = function(rules, context, topic, message) {
    context[utilities.update_topic_for_expression(topic)] = message

    const ruleStartTime = new Date().getTime()
    logging.verbose(' rule processing start ', {
        action: 'rule-processing-start',
        start_time: ruleStartTime
    })

    var ruleProcessor = function(rule, rule_name, callback) {
        const disabled = rule.disabled

        if (disabled == true) return

        const watch = rule.watch

        if (!_.isNil(watch) && !_.isNil(watch.devices)) {
            if (watch.devices.indexOf(topic) !== -1) {

                logging.verbose('matched topic to rule', {
                    action: 'rule-match',
                    rule_name: rule_name,
                    topic: topic,
                    message: utilities.convertToNumberIfNeeded(message),
                    rule: rule,
                    context: context
                })

                evaluation.evalulateValue(topic, context, rule_name, rule)
            }
        }

        callback()
    }

    var configProcessor = function(config, callback) {
        async.eachOf(config, ruleProcessor)
        callback()
    }

    async.each(rules, configProcessor)

    logging.verbose(' rule processing done ', {
        action: 'rule-processing-done',
        processing_time: ((new Date().getTime()) - ruleStartTime)
    })
}

if (is_test_mode === false) {
    global.client.on('message', (topic, message) => {
        if (is_test_mode == true)
            return

        if (!global.devices_to_monitor.includes(topic))
            return

        message = utilities.convertToNumberIfNeeded(message)
        const redisStartTime = new Date().getTime()
        logging.verbose(' redis query', {
            action: 'redis-query-start',
            start_time: redisStartTime
        })

        global.redis.mget(global.devices_to_monitor, function(err, values) {
            const redisQueryTime = ((new Date().getTime()) - redisStartTime)
            logging.verbose(' redis query done', {
                action: 'redis-query-done',
                query_time: redisQueryTime
            })

            metrics.submit('redis_query_time', redisQueryTime)

            var context = {}

            for (var index = 0; index < global.devices_to_monitor.length; index++) {
                const key = global.devices_to_monitor[index]
                const value = values[index]
                const newKey = utilities.update_topic_for_expression(key)
                if (key === topic)
                    context[newKey] = utilities.convertToNumberIfNeeded(message)
                else
                    context[newKey] = utilities.convertToNumberIfNeeded(value)
            }

            try {
                var jsonFound = JSON.parse(message)
                if (!_.isNil(jsonFound)) {
                    Object.keys(jsonFound).forEach(function(key) {
                        context[key] = jsonFound[key]
                    })
                }
            } catch (err) {
                logging.debug('invalid json')
            }

            global.changeProcessor(rules.get_configs(), context, topic, message)
        })
    })
}
global.redis = Redis.setupClient(function() {
    logging.info('redis connected ', {
        action: 'redis-connected'
    })
    if (is_test_mode == false) {
        logging.info('loading rules')
        rules.load_path(config_path)
    } else {
        logging.info('not - loading rules')

    }
})

rules.on('rules-loaded', () => {
    if (is_test_mode == true) {
        logging.info('test mode, not loading rules')
        return
    }
    logging.info('Loading rules')

    global.devices_to_monitor = []

    rules.ruleIterator(function(rule_name, rule) {
        const watch = rule.watch
        if (!_.isNil(watch)) {
            const devices = watch.devices
            if (!_.isNil(devices)) {
                devices.forEach(function(device) {
                    global.devices_to_monitor.push(device)
                }, this)
            }
        }

        const rules = rule.rules
        if (!_.isNil(rules)) {
            const expression = rules.expression
            logging.debug('expression :' + expression)
            if (!_.isNil(expression)) {

                var foundDevices = expression.match(/\/([a-z,0-9,\-,_,/])*/g)

                if (!_.isNil(foundDevices)) {
                    foundDevices.forEach(function(device) {
                        global.devices_to_monitor.push(device)
                    }, this)
                }
            }
        }

        const actions = rule.actions
        if (!_.isNil(actions)) {
            Object.keys(actions).forEach(function(action) {
                const action_value = actions[action]

                var foundDevices = action_value.match(/\/([a-z,0-9,\-,_,/])*/g)

                if (!_.isNil(foundDevices)) {
                    foundDevices.forEach(function(device) {
                        global.devices_to_monitor.push(device)
                    }, this)
                }
            }, this)
        }

    })

    global.devices_to_monitor = utilities.unique(global.devices_to_monitor)

    logging.info('rules loaded ', {
        action: 'rules-loaded',
        devices_to_monitor: global.devices_to_monitor
    })

    global.redis.mget(global.devices_to_monitor, function(err, values) {
        logging.debug('devices :' + JSON.stringify(global.devices_to_monitor))
        logging.debug('values :' + JSON.stringify(values))
    })

    schedule.scheduleJobs()
})

global.clearQueues = function() {
    evaluation.clearQueues()
}<|endoftext|>


<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)
            ? window.console.log.bind(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 asyncGet(url, onSuccess, onFailure) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);  // asynchronous
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {  // response received and loaded
                if (xhr.status === 200) {
                    onSuccess(xhr.responseText, xhr.statusText);
                } else {
                    onFailure(xhr.statusText);
                }
            }
        };
        xhr.send(null);
    }

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

    function parseQueryParams(location) {
        var query = location.search.substr(1),
            pairs = query.split("&"),
            params = {};
        // Note: this implementation does not handle duplicate parameters
        // properly, but we don't need that here (yet)
        pairs.forEach(function (e) {
            var pair = e.split("="),
                name = decodeURIComponent(pair[0]),
                value = decodeURIComponent(pair[1] || "");
            params[name] = value;
        });
        return params;
    }
    */

    function generateRandomGuestName() {
        var id = Math.floor(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) {  /*, fetchParams*/
        var url = nainwakOrigin + '/jeu/' + name + '.php';
        // TODO: add a fetch method
        return Object.freeze({
            name: name,
            url: url,  // base URL without query parameters
            analyze: analyze
        });
    }

    /* pages analyzers */
    function analyzeDetect(window) {
        var document = window.document,
            c1 = document.getElementsByClassName('c1')[0];
        log('Processing detect...');
        //log(document);
        log(c1);
        //log(window.tabavat);
        //gavat(id, photo, nom, tag, barbe, classe, cote, distance, x, y, description, attaquer, gifler, estCible)
        //log(window.tabobjet);
        //gobjet(id, photo, nom, distance, x, y, poussiere, prendre)
    }

    /* available pages for the Spy objects */
    var pages = [
        // Détection
        createPage('detect', analyzeDetect)
        /*
        // 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)
        */
        // There's also /accueil/resume.php?IDS=...&errmsg=
    ];

    /* Spy factory */
    function createSpy(frame) {  /*user, channel*/
        //IDS = parseQueryParams(frame.location).IDS,
        var infoLoaded = function () {
                var window = frame.contentWindow,
                    location = window.location,
                    url = location.origin + location.pathname;
                pages.forEach(function (page) {
                    if (page.url === url) {
                        page.analyze(window);
                    }
                });
            },
            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,
            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) {
     // TODO: don't use Function.prototype.toString, may not work on every browser!
        var template = function () {
                var w = window,
                    l = w.location,
                    u = l.origin + l.pathname,
                    d = document,
                    b = d.body,
                    n = 'Nainwaklet',
                    id = 'nainwakletScript',
                    s = d.getElementById(id);

                if (u === '@gameUrl@') {
                    if (s) {
                        w[n].app.destroy();
                        w[n] = null;
                        b.removeChild(s);
                    } else {
                        s = d.createElement('script');
                        s.id = id;
                        s.type = 'text/javascript';
                        s.src = '@src@';
                        s.async = false;
                        s.setAttribute('data-channel', '@channel@');
                        b.appendChild(s);
                    }
                } else {
                    alert("Ne fonctionne que sur la page jeu de Nainwak");
                }
            },
            code = template.toString()
                .replace(/\s+/g, ' ')
                .replace('@gameUrl@', nainwakGameUrl)
                .replace('@src@', scriptUrl)
                .replace('@channel@', channel);

        return 'javascript:(' + code + '())';
    }

    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 = {
        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>pages is now an object with useful methods<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)
            ? window.console.log.bind(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 asyncGet(url, onSuccess, onFailure) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);  // asynchronous
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {  // response received and loaded
                if (xhr.status === 200) {
                    onSuccess(xhr.responseText, xhr.statusText);
                } else {
                    onFailure(xhr.statusText);
                }
            }
        };
        xhr.send(null);
    }

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

    function parseQueryParams(location) {
        var query = location.search.substr(1),
            pairs = query.split("&"),
            params = {};
        // Note: this implementation does not handle duplicate parameters
        // properly, but we don't need that here (yet)
        pairs.forEach(function (e) {
            var pair = e.split("="),
                name = decodeURIComponent(pair[0]),
                value = decodeURIComponent(pair[1] || "");
            params[name] = value;
        });
        return params;
    }
    */

    function generateRandomGuestName() {
        var id = Math.floor(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) {  /*, fetchParams*/
        var url = nainwakOrigin + '/jeu/' + name + '.php';
        // TODO: add a fetch method
        return Object.freeze({
            name: name,
            url: url,  // base URL without query parameters
            analyze: analyze
        });
    }

    /* pages analyzers */
    function analyzeDetect(contentWindow) {
        var document = contentWindow.document,
            c1 = document.getElementsByClassName('c1')[0];
        log('Processing detect...');
        //log(document);
        log(c1);
        //log(window.tabavat);
        //gavat(id, photo, nom, tag, barbe, classe, cote, distance, x, y, description, attaquer, gifler, estCible)
        //log(window.tabobjet);
        //gobjet(id, photo, nom, distance, x, y, poussiere, prendre)
    }

    /* available pages for the Spy objects */
    var pages = (function () {
        var list = [
                // Détection
                createPage('detect', analyzeDetect)
                /*
                // 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)
                */
                // There's also /accueil/resume.php?IDS=...&errmsg=
            ],
            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,
                    location = contentWindow.location,
                    url = location.origin + location.pathname,
                    page = pages.get(url);

                if (page) {
                    page.analyze(contentWindow);
                }
            },
            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,
            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) {
     // TODO: don't use Function.prototype.toString, may not work on every browser!
        var template = function () {
                var w = window,
                    l = w.location,
                    u = l.origin + l.pathname,
                    d = document,
                    b = d.body,
                    n = 'Nainwaklet',
                    id = 'nainwakletScript',
                    s = d.getElementById(id);

                if (u === '@gameUrl@') {
                    if (s) {
                        w[n].app.destroy();
                        w[n] = null;
                        b.removeChild(s);
                    } else {
                        s = d.createElement('script');
                        s.id = id;
                        s.type = 'text/javascript';
                        s.src = '@src@';
                        s.async = false;
                        s.setAttribute('data-channel', '@channel@');
                        b.appendChild(s);
                    }
                } else {
                    alert("Ne fonctionne que sur la page jeu de Nainwak");
                }
            },
            code = template.toString()
                .replace(/\s+/g, ' ')
                .replace('@gameUrl@', nainwakGameUrl)
                .replace('@src@', scriptUrl)
                .replace('@channel@', channel);

        return 'javascript:(' + code + '())';
    }

    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>'use strict';

const colorRE = /^((bright)?(black|white|red|yellow|green|cyan|blue|magenta))?(,(black|white|red|yellow|green|cyan|blue|magenta))?/;

/**
* HTML escaping
*/
let htmlEscape = str => str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');

/**
* Convert POSIX ERE to JS RegExp as a string
* @param {String} str A POSIX regular expression
* @returns {String} JS regular expression as a string that can be passed to the `RegExp` constructor
*/
let convertRegExp = str => {
    if (!str) {
        throw new Error('Empty regular expression');
    }
    // character classes as per http://www.regular-expressions.info/posixbrackets.html#class
    let replaced = str.replace(/\[:space:]/g, '\\s').replace(/\[:word:]/g, '\\w')
        .replace(/\[\[:[><]:\]\]|\\[><]/g, '\\b') // not accurate, but the best we can do
        .replace(/\[:alpha:]/g, 'a-zA-Z').replace(/\[:alnum:]/g, 'a-zA-Z0-9')
        .replace(/\[:lower:]/g, 'a-z').replace(/\[:upper:]/g, 'A-Z')
        .replace(/\[:ascii:]/g, '\\x00-\\x7F').replace(/\[:graph:]/g, '\\x21-\\x7E')
        .replace(/\[:blank:]/g, '\\h').replace(/\[:digit:]/g, '\\d')
        .replace(/\[:cntrl:]/g, '\\x00-\\x1F\\x7F').replace(/\[:print:]/g, '\\x20-\\x7E')
        .replace(/\[:xdigit:]/g, 'a-fA-F0-9').replace(/\[:punct:]/g, '\'!"#$%&()*+,\\-./:;<=>?@[\\]^_`{|}~\\\\');
    // Allow unescaped ] in character class like []…] or [^]…].
    // This is a bit tricky for things like [][], which must be written as [\][] in JS
    let parts = replaced.split(/(\[\^?\].*?\])/g);
    return parts.reduce((result, part, i) => {
        if (i % 2) {
            part = part.replace(/^(\[\^?)]/, '$1\\]')
        }
        return result + part;
    });
};

/**
* TODO:doc
*/
let getGlobalMatcher = (line, insensitive) => {
    let res = line.replace('start="', '').replace(/"$/, '').split(/"\s+end="/);
    let flag = insensitive ? 'gmi' : 'gm';
    return {
        start: new RegExp(convertRegExp(res[0]), flag),
        end: new RegExp(convertRegExp(res[1]), flag),
        source: res
    };
};

let getLineMatcher = (re, insensitive) => {
    return {
        re: new RegExp(convertRegExp(re), insensitive ? 'gi' : 'g'),
        source: re
    };
};

let getHighlighters = nanorc => {
    let lines = {color: [], other: []};
    nanorc.split('\n').map(l => l.trim()).forEach(line => {
        if (/^i?color\b/.test(line)) {
            lines.color.push(line);
        } else if (line.length && !/^#/.test(line)) {
            lines.other.push(line)
        }
    });
    lines.other.forEach(l => console.warn('ignoring line: ', l));
    let result = [];
    lines.color.forEach(line => {
        let insensitive = line[0] === 'i';
        line = line.replace(/i?color\s*/, '');
        if (!colorRE.test(line)) {
            console.warn('incomplete line: ', line);
            return;
        }
        let colors = line.match(colorRE)[0].split(',');
        let fg = colors[0] || 'default';
        let bg = colors[1] || 'default';
        line = line.replace(colorRE, '').trim();
        if (!/^".*"$|^start=".+"\s+end=".+"/.test(line)) {
            console.warn('incomplete matching definition: ', line);
            return;
        }
        line.split(/\s*(start=".*?" end=".*?")\s*/g).filter(part => part && part.length).forEach(part => {
            part = part.trim();
            try {
                if (/^start=/.test(part)) {
                    result.push({
                        fg,
                        bg,
                        global: true,
                        matcher: getGlobalMatcher(part, insensitive)
                    });
                } else {
                    part.replace(/^"|"$/g, '').split(/"\s+"/).forEach(re => {
                        result.push({
                            fg,
                            bg,
                            global: false,
                            matcher: getLineMatcher(re, insensitive)
                        });
                    });
                }
            } catch (e) {
                console.error('Could not convert matcher ', line, ': ', e.message);
                console.log(e.stack);
            }
        });
    });
    return result;
};

let analyzeGlobal = (code, highlighter, metas) => {
    let starts = [];
    let start;
    let lastIndex;
    while (start = highlighter.matcher.start.exec(code)) {
        starts.push({
            from: start.index,
            to: highlighter.matcher.start.lastIndex - 1
        });
        if (start.index === lastIndex) {
            throw new Error(highlighter.matcher.source[0] + ' matched the empty string');
        }
        lastIndex = start.index;
    }
    if (!starts.length) {
        return;
    }
    let ends = [];
    let end;
    lastIndex = -1;
    while (end = highlighter.matcher.end.exec(code)) {
        if (end.index > starts[0].to) {
            ends.push({
                from: end.index,
                to: highlighter.matcher.end.lastIndex - 1
            });
        }
        if (end.index === lastIndex) {
            throw new Error(highlighter.matcher.source[0] + ' matched the empty string');
        }
        lastIndex = end.index;
    }
    if (!ends.length) {
        return;
    }
    //console.log('****');
    //console.log('starts,ends:', starts, ends);
    let intervals = [];
    for (let start = starts.shift(); start && ends.length; start = starts.shift()) {
        //console.log('start', start);
        let end;
        do {
            end = ends.shift();
            //console.log('trying end', end);
            while (starts.length && starts[0].from < end.from) {
                //console.log('shifting!', starts[0]);
                starts.shift();
            }
        } while (end.from <= start.to && ends.length);
        //console.log('good!');
        intervals.push([start, end]);
    }
    let charcounts = code.split('\n').map(x => x.length + 1);
    intervals.forEach(interval => {
        let start = interval[0].from;
        let end = interval[1].to;
        let started = false;
        for (let line = 0; line < charcounts.length; line++) {
            //console.log(start, end);
            let length = charcounts[line];
            if (0 <= start && start < length) {
                started = true;
            }
            if (started) {
                metas[line] = metas[line] || [];
                let meta = {
                    start: Math.max(0, start),
                    fg: highlighter.fg,
                    bg: highlighter.bg
                };
                if (0 <= end && end < length) {
                    meta.end = end + 1;
                    metas[line].push(meta);
                } else {
                    meta.end = length - 1;
                    metas[line].push(meta);
                }
            }
            start -= length;
            end -= length;
            if (end < 0) {
                break;
            }
        }
        //console.log(metas);
    });
};

/**
* Adds highlighting metadata for the given highlighter to the given metas object
* @param {String} code
* @param {Object} highlighter
* @param {Object} metas A hash of metadata items, one per line. Will be modified in place.
*/
let addMetaData = (code, highlighter, metas) => {
    if (highlighter.global) {
        analyzeGlobal(code, highlighter, metas);
    } else {
        code.split('\n').forEach((line, i) => {
            let match;
            let lastIndex;
            while (match = highlighter.matcher.re.exec(line)) {
                metas[i] = metas[i] || [];
                metas[i].push({
                    start: match.index,
                    end: highlighter.matcher.re.lastIndex,
                    fg: highlighter.fg,
                    bg: highlighter.bg
                });
                if (match.index === lastIndex) {
                    throw new Error(highlighter.matcher.source + ' matched the empty string');
                }
                lastIndex = match.index;
            }
        });
    }
};

/**
* Get the colorized version of a given line and its respective metadata
* @param {string} line
* @param {Array} meta
* @returns {string} HTML
*/
let colorizeHTML = function (line, meta) {
    let colorful = {};
    meta.forEach(function (meta) {
        for (let i = meta.start; i < meta.end; i++) {
            colorful[i] = {fg: meta.fg, bg: meta.bg};
        }
    });
    let spans = [];
    let span = {text: '', fg: 'default', bg: 'default'};
    for (let i = 0; i < line.length; i++) {
        let fg, bg;
        if (colorful[i]) {
            fg = colorful[i].fg;
            bg = colorful[i].bg;
        } else {
            fg = 'default';
            bg = 'default';
        }
        if (fg === span.fg && bg === span.bg) {
            span.text += line[i];
        } else {
            spans.push(span);
            span = {
                text: line[i],
                fg,
                bg
            }
        }
    }
    spans.push(span);
    return spans.filter(s => s.text.length).map(s => `<span class="${s.fg} bg-${s.bg}">${htmlEscape(s.text)}</span>`).join('');
};

/**
* Create highlighted HTML from source code and a nanorc definition
* @param {String} code Code to be highlighted
* @param {String} nanorc A nanorc syntax definition
* @returns {String} HTML
*/
let highlight = function (code, nanorc) {
    // create metadata
    let highlighters = getHighlighters(nanorc);
    let metas = {};
    highlighters.forEach(highlighter => addMetaData(code, highlighter, metas));

    // colorize output line-by-line
    let result = code.split('\n').map((line, i) => {
        if (metas[i]) {
            return colorizeHTML(line, metas[i]);
        } else {
            return htmlEscape(line);
        }
    });

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

let codeEl = document.querySelector('#code');
let nanorcEl = document.querySelector('#nanorc');
let resultEl = document.querySelector('#result');

// sync output with input fields
let sync = (() => {
    let code, nanorc;
    return () => {
        let newCode = codeEl.value;
        let newNanorc = nanorcEl.value;
        if (code !== newCode || nanorc !== newNanorc) {
            code = newCode;
            nanorc = newNanorc;
            resultEl.innerHTML = highlight(code, nanorc).replace(/https?:\/\/([\][!#$&-;=?-_a-z~]|%[0-9a-fA-F]{2})+/g, url => `<a href="${url}">${url}</a>`);
        }
    };
})();

// start polling
setInterval(sync, 100);
<commit_msg>Fix translation of word boundaries<commit_after>'use strict';

const colorRE = /^((bright)?(black|white|red|yellow|green|cyan|blue|magenta))?(,(black|white|red|yellow|green|cyan|blue|magenta))?/;

/**
* HTML escaping
*/
let htmlEscape = str => str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');

/**
* Convert POSIX ERE to JS RegExp as a string
* @param {String} str A POSIX regular expression
* @returns {String} JS regular expression as a string that can be passed to the `RegExp` constructor
*/
let convertRegExp = str => {
    if (!str) {
        throw new Error('Empty regular expression');
    }
    // character classes as per http://www.regular-expressions.info/posixbrackets.html#class
    let replaced = str.replace(/\[:space:]/g, '\\s').replace(/\[:word:]/g, '\\w')
        .replace(/\[:alpha:]/g, 'a-zA-Z').replace(/\[:alnum:]/g, 'a-zA-Z0-9')
        .replace(/\[:lower:]/g, 'a-z').replace(/\[:upper:]/g, 'A-Z')
        .replace(/\[:ascii:]/g, '\\x00-\\x7F').replace(/\[:graph:]/g, '\\x21-\\x7E')
        .replace(/\[:blank:]/g, '\\h').replace(/\[:digit:]/g, '\\d')
        .replace(/\[:cntrl:]/g, '\\x00-\\x1F\\x7F').replace(/\[:print:]/g, '\\x20-\\x7E')
        .replace(/\[:xdigit:]/g, 'a-fA-F0-9').replace(/\[:punct:]/g, '\'!"#$%&()*+,\\-./:;<=>?@[\\]^_`{|}~\\\\')
        // word boundaries
        .replace(/\[\[:>:]]|\\>/g, '\\b(?=\\W|$)').replace(/\[\[:<:]]|\\</g, '\\b(?=\\w)');
    // Allow unescaped ] in character class like []…] or [^]…].
    // This is a bit tricky for things like [][], which must be written as [\][] in JS
    let parts = replaced.split(/(\[\^?\].*?\])/g);
    return parts.reduce((result, part, i) => {
        if (i % 2) {
            part = part.replace(/^(\[\^?)]/, '$1\\]')
        }
        return result + part;
    });
};

/**
* TODO:doc
*/
let getGlobalMatcher = (line, insensitive) => {
    let res = line.replace('start="', '').replace(/"$/, '').split(/"\s+end="/);
    let flag = insensitive ? 'gmi' : 'gm';
    return {
        start: new RegExp(convertRegExp(res[0]), flag),
        end: new RegExp(convertRegExp(res[1]), flag),
        source: res
    };
};

let getLineMatcher = (re, insensitive) => {
    return {
        re: new RegExp(convertRegExp(re), insensitive ? 'gi' : 'g'),
        source: re
    };
};

let getHighlighters = nanorc => {
    let lines = {color: [], other: []};
    nanorc.split('\n').map(l => l.trim()).forEach(line => {
        if (/^i?color\b/.test(line)) {
            lines.color.push(line);
        } else if (line.length && !/^#/.test(line)) {
            lines.other.push(line)
        }
    });
    lines.other.forEach(l => console.warn('ignoring line: ', l));
    let result = [];
    lines.color.forEach(line => {
        let insensitive = line[0] === 'i';
        line = line.replace(/i?color\s*/, '');
        if (!colorRE.test(line)) {
            console.warn('incomplete line: ', line);
            return;
        }
        let colors = line.match(colorRE)[0].split(',');
        let fg = colors[0] || 'default';
        let bg = colors[1] || 'default';
        line = line.replace(colorRE, '').trim();
        if (!/^".*"$|^start=".+"\s+end=".+"/.test(line)) {
            console.warn('incomplete matching definition: ', line);
            return;
        }
        line.split(/\s*(start=".*?" end=".*?")\s*/g).filter(part => part && part.length).forEach(part => {
            part = part.trim();
            try {
                if (/^start=/.test(part)) {
                    result.push({
                        fg,
                        bg,
                        global: true,
                        matcher: getGlobalMatcher(part, insensitive)
                    });
                } else {
                    part.replace(/^"|"$/g, '').split(/"\s+"/).forEach(re => {
                        result.push({
                            fg,
                            bg,
                            global: false,
                            matcher: getLineMatcher(re, insensitive)
                        });
                    });
                }
            } catch (e) {
                console.error('Could not convert matcher ', line, ': ', e.message);
                console.log(e.stack);
            }
        });
    });
    return result;
};

let analyzeGlobal = (code, highlighter, metas) => {
    let starts = [];
    let start;
    let lastIndex;
    while (start = highlighter.matcher.start.exec(code)) {
        starts.push({
            from: start.index,
            to: highlighter.matcher.start.lastIndex - 1
        });
        if (start.index === lastIndex) {
            throw new Error(highlighter.matcher.source[0] + ' matched the empty string');
        }
        lastIndex = start.index;
    }
    if (!starts.length) {
        return;
    }
    let ends = [];
    let end;
    lastIndex = -1;
    while (end = highlighter.matcher.end.exec(code)) {
        if (end.index > starts[0].to) {
            ends.push({
                from: end.index,
                to: highlighter.matcher.end.lastIndex - 1
            });
        }
        if (end.index === lastIndex) {
            throw new Error(highlighter.matcher.source[0] + ' matched the empty string');
        }
        lastIndex = end.index;
    }
    if (!ends.length) {
        return;
    }
    //console.log('****');
    //console.log('starts,ends:', starts, ends);
    let intervals = [];
    for (let start = starts.shift(); start && ends.length; start = starts.shift()) {
        //console.log('start', start);
        let end;
        do {
            end = ends.shift();
            //console.log('trying end', end);
            while (starts.length && starts[0].from < end.from) {
                //console.log('shifting!', starts[0]);
                starts.shift();
            }
        } while (end.from <= start.to && ends.length);
        //console.log('good!');
        intervals.push([start, end]);
    }
    let charcounts = code.split('\n').map(x => x.length + 1);
    intervals.forEach(interval => {
        let start = interval[0].from;
        let end = interval[1].to;
        let started = false;
        for (let line = 0; line < charcounts.length; line++) {
            //console.log(start, end);
            let length = charcounts[line];
            if (0 <= start && start < length) {
                started = true;
            }
            if (started) {
                metas[line] = metas[line] || [];
                let meta = {
                    start: Math.max(0, start),
                    fg: highlighter.fg,
                    bg: highlighter.bg
                };
                if (0 <= end && end < length) {
                    meta.end = end + 1;
                    metas[line].push(meta);
                } else {
                    meta.end = length - 1;
                    metas[line].push(meta);
                }
            }
            start -= length;
            end -= length;
            if (end < 0) {
                break;
            }
        }
        //console.log(metas);
    });
};

/**
* Adds highlighting metadata for the given highlighter to the given metas object
* @param {String} code
* @param {Object} highlighter
* @param {Object} metas A hash of metadata items, one per line. Will be modified in place.
*/
let addMetaData = (code, highlighter, metas) => {
    if (highlighter.global) {
        analyzeGlobal(code, highlighter, metas);
    } else {
        code.split('\n').forEach((line, i) => {
            let match;
            let lastIndex;
            while (match = highlighter.matcher.re.exec(line)) {
                metas[i] = metas[i] || [];
                metas[i].push({
                    start: match.index,
                    end: highlighter.matcher.re.lastIndex,
                    fg: highlighter.fg,
                    bg: highlighter.bg
                });
                if (match.index === lastIndex) {
                    throw new Error(highlighter.matcher.source + ' matched the empty string');
                }
                lastIndex = match.index;
            }
        });
    }
};

/**
* Get the colorized version of a given line and its respective metadata
* @param {string} line
* @param {Array} meta
* @returns {string} HTML
*/
let colorizeHTML = function (line, meta) {
    let colorful = {};
    meta.forEach(function (meta) {
        for (let i = meta.start; i < meta.end; i++) {
            colorful[i] = {fg: meta.fg, bg: meta.bg};
        }
    });
    let spans = [];
    let span = {text: '', fg: 'default', bg: 'default'};
    for (let i = 0; i < line.length; i++) {
        let fg, bg;
        if (colorful[i]) {
            fg = colorful[i].fg;
            bg = colorful[i].bg;
        } else {
            fg = 'default';
            bg = 'default';
        }
        if (fg === span.fg && bg === span.bg) {
            span.text += line[i];
        } else {
            spans.push(span);
            span = {
                text: line[i],
                fg,
                bg
            }
        }
    }
    spans.push(span);
    return spans.filter(s => s.text.length).map(s => `<span class="${s.fg} bg-${s.bg}">${htmlEscape(s.text)}</span>`).join('');
};

/**
* Create highlighted HTML from source code and a nanorc definition
* @param {String} code Code to be highlighted
* @param {String} nanorc A nanorc syntax definition
* @returns {String} HTML
*/
let highlight = function (code, nanorc) {
    // create metadata
    let highlighters = getHighlighters(nanorc);
    let metas = {};
    highlighters.forEach(highlighter => addMetaData(code, highlighter, metas));

    // colorize output line-by-line
    let result = code.split('\n').map((line, i) => {
        if (metas[i]) {
            return colorizeHTML(line, metas[i]);
        } else {
            return htmlEscape(line);
        }
    });

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

let codeEl = document.querySelector('#code');
let nanorcEl = document.querySelector('#nanorc');
let resultEl = document.querySelector('#result');

// sync output with input fields
let sync = (() => {
    let code, nanorc;
    return () => {
        let newCode = codeEl.value;
        let newNanorc = nanorcEl.value;
        if (code !== newCode || nanorc !== newNanorc) {
            code = newCode;
            nanorc = newNanorc;
            resultEl.innerHTML = highlight(code, nanorc).replace(/https?:\/\/([\][!#$&-;=?-_a-z~]|%[0-9a-fA-F]{2})+/g, url => `<a href="${url}">${url}</a>`);
        }
    };
})();

// start polling
setInterval(sync, 100);
<|endoftext|>


<commit_before>const models = require('../../../models');
const _ = require('lodash');
const log = require('../../../utils/logger');
const importDomain = require('./utils').importDomain;
const importCommunity = require('./utils').importCommunity;
const importGroup = require('./utils').importGroup;
const importPost = require('./utils').importPost;
const importPoint = require('./utils').importPoint;
const request = require('request');

const updateDomain = (domainId, done) => {
  log.info('updateDomain');

  models.Domain.unscoped().findOne({
    where: {
      id: domainId
    },
    attributes: ['id','name','default_locale'],
    order: [
      ['id', 'asc' ]
    ]
  }).then((domain) => {
    if (domain) {
      importDomain(domain, done);
    } else {
      done("Can't find domain for similarities import");
    }
  }).catch((error) => {
    done(error);
  });
};

const updateCommunity = (communityId, done) => {
  log.info('updateCommunity');

  models.Community.unscoped().findOne({
    where: {
      id: communityId
    },
    include: [
      {
        model: models.Domain,
        attributes: ['id','default_locale'],
        required: true
      }
    ],
    attributes: ['id','name','default_locale'],
    order: [
      ['id', 'asc' ]
    ]
  }).then((community) => {
    if (community) {
      importCommunity(community, done);
    } else {
      done("Can't find community for similarities import");
    }
  }).catch(function (error) {
    done(error);
  });
};

const updateGroup = (groupId, done) => {
  log.info('updateGroup');

  models.Group.unscoped().findOne({
    where: {
      id: groupId
    },
    include: [
      {
        model: models.Community,
        attributes: ['id','access','status','default_locale'],
        required: true,
        include: [
          {
            model: models.Domain,
            attributes: ['id','default_locale'],
            required: true
          }
        ]
      }
    ],
    attributes: ['id','name'],
    order: [
      ['id', 'asc' ]
    ]
  }).then((group) => {
    if (group) {
      importGroup(group, done);
    } else {
      done("Can't find group for similarities import");
    }
  }).catch(function (error) {
    done(error);
  });
};

const updatePost = (postId, done) => {
  log.info('updatePost');

  models.Post.unscoped().findOne(
    {
      where: {
        id: postId
      },
      include: [
        {
          model: models.Point,
          required: false,
          attributes: ['id','content'],
        },
        {
          model: models.Group,
          required: true,
          attributes: ['id','access','status','configuration'],
          include: [
            {
              attributes: ['id','formats'],
              model: models.Image, as: 'GroupLogoImages',
              required: false
            },
            {
              model: models.Community,
              attributes: ['id','access','status','default_locale'],
              required: true,
              include: [
                {
                  attributes: ['id','formats'],
                  model: models.Image, as: 'CommunityLogoImages',
                  required: false
                },
                {
                  model: models.Domain,
                  attributes: ['id','default_locale'],
                  required: true
                }
              ]
            }
          ]
        },
        {
          model: models.Image,
          required: false,
          as: 'PostHeaderImages',
          attributes: ['id','formats']
        },
        {
          model: models.Video,
          required: false,
          attributes: ['id','formats','updated_at','viewable','public_meta'],
          as: 'PostVideos',
          include: [
            {
              model: models.Image,
              as: 'VideoImages',
              attributes:["formats",'updated_at'],
              required: false
            },
          ]
        },
        {
          model: models.Audio,
          required: false,
          attributes: ['id','formats','updated_at','listenable'],
          as: 'PostAudios',
        }
      ],
      order: [
        ['id', 'desc' ],
        [ { model: models.Image, as: 'PostHeaderImages' } ,'updated_at', 'asc' ],
        [ { model: models.Group }, { model: models.Image, as: 'GroupLogoImages' } , 'created_at', 'desc' ],
        [ { model: models.Group }, { model: models.Community }, { model: models.Image, as: 'CommunityLogoImages' } , 'created_at', 'desc' ]
      ],
      attributes: ['id','name','description','group_id','category_id','status','deleted','language','created_at',
        'user_id','official_status','public_data','cover_media_type',
        'counter_endorsements_up','counter_endorsements_down','counter_points','counter_flags']
    }).then((post) => {
      if (post) {
        importPost(post, done);
      } else {
        done("Can't find post for similarities import");
      }
  }).catch(function (error) {
    done(error);
  });
};

const updatePoint = (pointId, done) => {
  log.info('updatePoint');

  models.Point.unscoped().findOne({
    where: {
      id: pointId
    },
    attributes: ['id', 'name', 'content', 'user_id', 'post_id', 'value', 'status', 'counter_quality_up', 'counter_quality_down', 'language', 'created_at'],
    order: [
      [models.PointRevision, 'created_at', 'asc'],
      [{model: models.Video, as: "PointVideos"}, 'updated_at', 'desc'],
      [{model: models.Audio, as: "PointAudios"}, 'updated_at', 'desc'],
    ],
    include: [
      {
        model: models.PointRevision,
        attributes: ['content', 'value', 'created_at'],
        required: false
      },
      {
        model: models.Video,
        required: false,
        attributes: ['id', 'formats'],
        as: 'PointVideos'
      },
      {
        model: models.Audio,
        required: false,
        attributes: ['id', 'formats'],
        as: 'PointAudios'
      },
      {
        model: models.Post,
        attributes: ['id', 'group_id','created_at','category_id','official_status','status'],
        required: true,
        include: [
          {
            model: models.Group,
            attributes: ['id','access','status','configuration'],
            required: true,
            include: [
              {
                model: models.Community,
                attributes: ['id','access','status','default_locale'],
                required: true,
                include: [
                  {
                    model: models.Domain,
                    attributes: ['id','default_locale'],
                    required: true
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }).then(function (point) {
    if (point) {
      importPoint(point, done);
    } else {
      done("Can't find point for similarities import");
    }
  }).catch(function (error) {
    done(error);
  });
};

const updateCollection = (workPackage, done) => {
  if (process.env.AC_ANALYTICS_KEY &&
      process.env.AC_ANALYTICS_CLUSTER_ID &&
      process.env.AC_ANALYTICS_BASE_URL) {
    if (workPackage.domainId) {
      updateDomain(workPackage.domainId, done)
    } else if (workPackage.communityId) {
      updateCommunity(workPackage.communityId, done)
    } else if (workPackage.groupId) {
      updateGroup(workPackage.groupId, done)
    } else if (workPackage.postId) {
      updatePost(workPackage.postId, done)
    } else if (workPackage.pointId) {
      updatePoint(workPackage.pointId, done)
    } else {
      done("Couldn't find any collection to update similarities", { workPackage });
    }
  } else {
    log.warn("Can't find AC_ANALYTICS_KEY, AC_ANALYTICS_CLUSTER_ID & AC_ANALYTICS_BASE_URL for the similarities engine");
    done();
  }
};

const getFromAnalyticsApi = (req, featureType, collectionType, collectionId, done) => {
  const redisKey = "cachev4:getAnalytics:"+featureType+":"+collectionType+":"+collectionId;
  req.redisClient.get(redisKey, (error, content) => {
    if (!error && content) {
      done(null, JSON.parse(content));
    } else {
      if (error) {
        log.error('Could not get pages for group from redis', {
          err: error,
          context: 'redis',
          userId: req.user ? req.user.id : null
        });
      }

      const options = {
        url: process.env["AC_ANALYTICS_BASE_URL"]+featureType+"/"+collectionType+"/"+process.env.AC_ANALYTICS_CLUSTER_ID+"/"+collectionId,
        headers: {
          'X-API-KEY': process.env["AC_ANALYTICS_KEY"]
        }
      };

      request.get(options, (error, content) => {
        if (content && content.statusCode!=200) {
          error = content.statusCode;
        } else {
          req.redisClient.setex(redisKey, process.env.SIMILARITIES_CACHE_TTL ? parseInt(process.env.SIMILARITIES_CACHE_TTL) : 60*60, JSON.stringify(content));
        }
        done(error, content);
      });
    }
  });
};

const triggerSimilaritiesTraining = (req, collectionType, collectionId, done) => {
  const options = {
    url: process.env["AC_ANALYTICS_BASE_URL"]+"trigger_similarities_training"+"/"+collectionType+"/"+process.env.AC_ANALYTICS_CLUSTER_ID+"/"+collectionId,
    headers: {
      'X-API-KEY': process.env["AC_ANALYTICS_KEY"]
    },
    json: {}
  };

  request.put(options, (error, content) => {
    if (content && content.statusCode!=200) {
      error = content.statusCode;
    }
    done(error, content);
  });
};

const getParsedSimilaritiesContent = (content) => {
  let results;
  if (content && content.body) {
    try {
      results = JSON.parse(content.body);
    } catch (error) {
      log.error("Can't parse JSON from similarities");
    }
  }
  return results;
};

const sendBackAnalyticsResultsOrError = (req,res,error,results) => {
  if (error) {
    log.error("Analytics Error", { id: req.params.id, url: req.url, userId: req.user ? req.user.id : null, errorStatus:  500, error });
    res.sendStatus(500);
  } else {
    res.send(results);
  }
};

module.exports = {
  updateCollection,
  getFromAnalyticsApi,
  triggerSimilaritiesTraining,
  sendBackAnalyticsResultsOrError,
  getParsedSimilaritiesContent
};<commit_msg>Work on analytics<commit_after>const models = require('../../../models');
const _ = require('lodash');
const log = require('../../../utils/logger');
const importDomain = require('./utils').importDomain;
const importCommunity = require('./utils').importCommunity;
const importGroup = require('./utils').importGroup;
const importPost = require('./utils').importPost;
const importPoint = require('./utils').importPoint;
const request = require('request');

const updateDomain = (domainId, done) => {
  log.info('updateDomain');

  models.Domain.unscoped().findOne({
    where: {
      id: domainId
    },
    attributes: ['id','name','default_locale'],
    order: [
      ['id', 'asc' ]
    ]
  }).then((domain) => {
    if (domain) {
      importDomain(domain, done);
    } else {
      done("Can't find domain for similarities import");
    }
  }).catch((error) => {
    done(error);
  });
};

const updateCommunity = (communityId, done) => {
  log.info('updateCommunity');

  models.Community.unscoped().findOne({
    where: {
      id: communityId
    },
    include: [
      {
        model: models.Domain,
        attributes: ['id','default_locale'],
        required: true
      }
    ],
    attributes: ['id','name','default_locale'],
    order: [
      ['id', 'asc' ]
    ]
  }).then((community) => {
    if (community) {
      importCommunity(community, done);
    } else {
      done("Can't find community for similarities import");
    }
  }).catch(function (error) {
    done(error);
  });
};

const updateGroup = (groupId, done) => {
  log.info('updateGroup');

  models.Group.unscoped().findOne({
    where: {
      id: groupId
    },
    include: [
      {
        model: models.Community,
        attributes: ['id','access','status','default_locale'],
        required: true,
        include: [
          {
            model: models.Domain,
            attributes: ['id','default_locale'],
            required: true
          }
        ]
      }
    ],
    attributes: ['id','name'],
    order: [
      ['id', 'asc' ]
    ]
  }).then((group) => {
    if (group) {
      importGroup(group, done);
    } else {
      done("Can't find group for similarities import");
    }
  }).catch(function (error) {
    done(error);
  });
};

const updatePost = (postId, done) => {
  log.info('updatePost');

  models.Post.unscoped().findOne(
    {
      where: {
        id: postId
      },
      include: [
        {
          model: models.Point,
          required: false,
          attributes: ['id','content'],
        },
        {
          model: models.Group,
          required: true,
          attributes: ['id','access','status','configuration'],
          include: [
            {
              attributes: ['id','formats'],
              model: models.Image, as: 'GroupLogoImages',
              required: false
            },
            {
              model: models.Community,
              attributes: ['id','access','status','default_locale'],
              required: true,
              include: [
                {
                  attributes: ['id','formats'],
                  model: models.Image, as: 'CommunityLogoImages',
                  required: false
                },
                {
                  model: models.Domain,
                  attributes: ['id','default_locale'],
                  required: true
                }
              ]
            }
          ]
        },
        {
          model: models.Image,
          required: false,
          as: 'PostHeaderImages',
          attributes: ['id','formats']
        },
        {
          model: models.Video,
          required: false,
          attributes: ['id','formats','updated_at','viewable','public_meta'],
          as: 'PostVideos',
          include: [
            {
              model: models.Image,
              as: 'VideoImages',
              attributes:["formats",'updated_at'],
              required: false
            },
          ]
        },
        {
          model: models.Audio,
          required: false,
          attributes: ['id','formats','updated_at','listenable'],
          as: 'PostAudios',
        }
      ],
      order: [
        ['id', 'desc' ],
        [ { model: models.Image, as: 'PostHeaderImages' } ,'updated_at', 'asc' ],
        [ { model: models.Group }, { model: models.Image, as: 'GroupLogoImages' } , 'created_at', 'desc' ],
        [ { model: models.Group }, { model: models.Community }, { model: models.Image, as: 'CommunityLogoImages' } , 'created_at', 'desc' ]
      ],
      attributes: ['id','name','description','group_id','category_id','status','deleted','language','created_at',
        'user_id','official_status','public_data','cover_media_type',
        'counter_endorsements_up','counter_endorsements_down','counter_points','counter_flags']
    }).then((post) => {
      if (post) {
        importPost(post, done);
      } else {
        done("Can't find post for similarities import");
      }
  }).catch(function (error) {
    done(error);
  });
};

const updatePoint = (pointId, done) => {
  log.info('updatePoint');

  models.Point.unscoped().findOne({
    where: {
      id: pointId
    },
    attributes: ['id', 'name', 'content', 'user_id', 'post_id', 'value', 'status', 'counter_quality_up', 'counter_quality_down', 'language', 'created_at'],
    order: [
      [models.PointRevision, 'created_at', 'asc'],
      [{model: models.Video, as: "PointVideos"}, 'updated_at', 'desc'],
      [{model: models.Audio, as: "PointAudios"}, 'updated_at', 'desc'],
    ],
    include: [
      {
        model: models.PointRevision,
        attributes: ['content', 'value', 'created_at'],
        required: false
      },
      {
        model: models.Video,
        required: false,
        attributes: ['id', 'formats'],
        as: 'PointVideos'
      },
      {
        model: models.Audio,
        required: false,
        attributes: ['id', 'formats'],
        as: 'PointAudios'
      },
      {
        model: models.Post,
        attributes: ['id', 'group_id','created_at','category_id','official_status','status'],
        required: true,
        include: [
          {
            model: models.Group,
            attributes: ['id','access','status','configuration'],
            required: true,
            include: [
              {
                model: models.Community,
                attributes: ['id','access','status','default_locale'],
                required: true,
                include: [
                  {
                    model: models.Domain,
                    attributes: ['id','default_locale'],
                    required: true
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }).then(function (point) {
    if (point) {
      importPoint(point, done);
    } else {
      done("Can't find point for similarities import");
    }
  }).catch(function (error) {
    done(error);
  });
};

const updateCollection = (workPackage, done) => {
  if (process.env.AC_ANALYTICS_KEY &&
      process.env.AC_ANALYTICS_CLUSTER_ID &&
      process.env.AC_ANALYTICS_BASE_URL) {
    if (workPackage.domainId) {
      updateDomain(workPackage.domainId, done)
    } else if (workPackage.communityId) {
      updateCommunity(workPackage.communityId, done)
    } else if (workPackage.groupId) {
      updateGroup(workPackage.groupId, done)
    } else if (workPackage.postId) {
      updatePost(workPackage.postId, done)
    } else if (workPackage.pointId) {
      updatePoint(workPackage.pointId, done)
    } else {
      done("Couldn't find any collection to update similarities", { workPackage });
    }
  } else {
    log.warn("Can't find AC_ANALYTICS_KEY, AC_ANALYTICS_CLUSTER_ID & AC_ANALYTICS_BASE_URL for the similarities engine");
    done();
  }
};

const getFromAnalyticsApi = (req, featureType, collectionType, collectionId, done) => {
  const redisKey = "cachev5:getAnalytics:"+featureType+":"+collectionType+":"+collectionId;
  req.redisClient.get(redisKey, (error, content) => {
    if (!error && content) {
      done(null, JSON.parse(content));
    } else {
      if (error) {
        log.error('Could not get pages for group from redis', {
          err: error,
          context: 'redis',
          userId: req.user ? req.user.id : null
        });
      }

      const options = {
        url: process.env["AC_ANALYTICS_BASE_URL"]+featureType+"/"+collectionType+"/"+process.env.AC_ANALYTICS_CLUSTER_ID+"/"+collectionId,
        headers: {
          'X-API-KEY': process.env["AC_ANALYTICS_KEY"]
        }
      };

      request.get(options, (error, content) => {
        if (content && content.statusCode!=200) {
          error = content.statusCode;
        } else {
          req.redisClient.setex(redisKey, process.env.SIMILARITIES_CACHE_TTL ? parseInt(process.env.SIMILARITIES_CACHE_TTL) : 60*60, JSON.stringify(content));
        }
        done(error, content);
      });
    }
  });
};

const triggerSimilaritiesTraining = (req, collectionType, collectionId, done) => {
  const options = {
    url: process.env["AC_ANALYTICS_BASE_URL"]+"trigger_similarities_training"+"/"+collectionType+"/"+process.env.AC_ANALYTICS_CLUSTER_ID+"/"+collectionId,
    headers: {
      'X-API-KEY': process.env["AC_ANALYTICS_KEY"]
    },
    json: {}
  };

  request.put(options, (error, content) => {
    if (content && content.statusCode!=200) {
      error = content.statusCode;
    }
    done(error, content);
  });
};

const getParsedSimilaritiesContent = (content) => {
  let results;
  if (content && content.body) {
    try {
      results = JSON.parse(content.body);
    } catch (error) {
      log.error("Can't parse JSON from similarities");
    }
  }
  return results;
};

const sendBackAnalyticsResultsOrError = (req,res,error,results) => {
  if (error) {
    log.error("Analytics Error", { id: req.params.id, url: req.url, userId: req.user ? req.user.id : null, errorStatus:  500, error });
    res.sendStatus(500);
  } else {
    res.send(results);
  }
};

module.exports = {
  updateCollection,
  getFromAnalyticsApi,
  triggerSimilaritiesTraining,
  sendBackAnalyticsResultsOrError,
  getParsedSimilaritiesContent
};<|endoftext|>


<commit_before>var file_prefix = 'Yam_';
var dfreq_dn = 2;

var	vaccineChoice = {};
vaccineChoice['B/Beijing/184/93'] = "1998-11-01";
vaccineChoice['B/Sichuan/379/99'] = "2001-09-25";
vaccineChoice['B/Shanghai/361/2002'] = "2004-09-25";
vaccineChoice['B/Florida/4/2006'] = "2008-09-25";
vaccineChoice['B/PHUKET/3073/2013'] = "2015-03-25";

var epiColorDomain = [0,1,2,3, 4,5,6,7,8,9,10];
var nonEpiColorDomain = [0,1,2,3,4,5,6,7,8,9,10,11];
var rbsColorDomain = [0,0.5, 1,1.5, 2];

var time_ticks = [2010, 2011, 2012, 2013, 2014, 2015];<commit_msg>Update Yam vaccines in auspice.<commit_after>var file_prefix = 'Yam_';
var dfreq_dn = 2;

var	vaccineChoice = {};
vaccineChoice['B/Beijing/184/93'] = "1998-11-01";
vaccineChoice['B/Sichuan/379/99'] = "2001-09-25";
vaccineChoice['B/Shanghai/361/2002'] = "2004-09-25";
vaccineChoice['B/Florida/4/2006'] = "2008-09-25";
vaccineChoice['B/Wisconsin/01/2010'] = "2012-02-25";
vaccineChoice['B/Massachusetts/02/2012'] = "2013-02-25";
vaccineChoice['B/PHUKET/3073/2013'] = "2014-09-25";

var epiColorDomain = [0,1,2,3, 4,5,6,7,8,9,10];
var nonEpiColorDomain = [0,1,2,3,4,5,6,7,8,9,10,11];
var rbsColorDomain = [0,0.5, 1,1.5, 2];

var time_ticks = [2010, 2011, 2012, 2013, 2014, 2015];<|endoftext|>


<commit_before>// IGNORE THESE
var config = {};
config.account = {};
// END OF IGNORE

// 0 = Shows small amount of logs | 1 = Shows default logs | 2 = Shows most logs
config.verbosityLevel = 1;

// Useragent the bot will use
config.userAgent = 'agario-feeder-bot';

// you can use the public server or run your own node server/server.js
config.feederServer = "ws://104.236.100.252:8081";
config.client_uuid = "YOUR_UUID"; // after you install the userscript it will show you a UUID in the corner

// Location of your proxy files with HTTP proxies (Sock proxies not supported yet!)
config.proxies = "proxy.txt";
config.botsPerIp = 1;
config.onboardingTimer = 5000;

// Maximum number of bots to load
config.maxBots = "128";

config.useRandomSkinName = true; // Set to true to use random name
config.useStaticName = "AgarioFeederBot"; // If above is false, us this skin name

// Set this to the server IP (Agar or Ogar server IP).
config.gameServerIp = "127.0.0.1:443"

// Edit this value so bots collect mass before coming towards the player
config.minimumMassBeforeFeed = 0;
config.enableSaveMoveTo = false;

config.useFacebookAuth = false; // Set to true for Facebook authentication
config.account.token = ""; // Auth token which is only valid for 2 hours - better use cookies

// You can also generate your own token through Facebook Data. (This will get overwritten if you use account.token)
config.account.c_user = ""; // Cookie from "c_user" at http://facebook.com
config.account.datr = "";// Cookie from "datr" at http://facebook.com
config.account.xs = ""; // Cookie from "xs" at http://facebook.com

// IGNORE THIS
module.exports = config;
// END OF IGNORE<commit_msg>Use config for serverPort<commit_after>// IGNORE THESE
var config = {};
config.account = {};
// END OF IGNORE

// 0 = Shows small amount of logs | 1 = Shows default logs | 2 = Shows most logs
config.verbosityLevel = 1;

// Useragent the bot will use
config.userAgent = 'agario-feeder-bot';

// you can use the public server or run your own node server/server.js
config.serverPort = 8081;
config.feederServer = "ws://104.236.100.252:8081";
config.client_uuid = "YOUR_UUID"; // after you install the userscript it will show you a UUID in the corner

// Location of your proxy files with HTTP proxies (Sock proxies not supported yet!)
config.proxies = "proxy.txt";
config.botsPerIp = 1;
config.onboardingTimer = 5000;

// Maximum number of bots to load
config.maxBots = "128";

config.useRandomSkinName = true; // Set to true to use random name
config.useStaticName = "AgarioFeederBot"; // If above is false, us this skin name

// Set this to the server IP (Agar or Ogar server IP).
config.gameServerIp = "127.0.0.1:443"

// Edit this value so bots collect mass before coming towards the player
config.minimumMassBeforeFeed = 0;
config.enableSaveMoveTo = false;

config.useFacebookAuth = false; // Set to true for Facebook authentication
config.account.token = ""; // Auth token which is only valid for 2 hours - better use cookies

// You can also generate your own token through Facebook Data. (This will get overwritten if you use account.token)
config.account.c_user = ""; // Cookie from "c_user" at http://facebook.com
config.account.datr = "";// Cookie from "datr" at http://facebook.com
config.account.xs = ""; // Cookie from "xs" at http://facebook.com

// IGNORE THIS
module.exports = config;
// END OF IGNORE
<|endoftext|>


<commit_before>const fs = require("fs");

function list_ids_in_directory(directory) {
  const files = fs.readdirSync(`../${directory}`).sort();
  let ids = [];
  for (const name of files) {
    if (fs.lstatSync(`../${directory}/${name}`).isDirectory()) {
      // Recurse into the directory.
      const inner_ids = list_ids_in_directory(`${directory}/${name}`);
      ids = ids.concat(inner_ids);
    } else {
      if (name.endsWith(".md")) {
        const slug = name.replace(/\.md$/, "");
        let id = `${directory}/${slug}`;
        if (id.match(/\/\d+-.+/)) {
          id = id.replace(/\/\d+-/, "/");
        }
        ids.push(id);
      }
    }
  }
  return ids;
}

// note: to handle errors where you don't want a markdown file in the sidebar, add it as a comment.
// this will fix errors like `Error: File not accounted for in sidebar: ...`
module.exports = {
  // users
  // architects
  // modelers
  // developers
  // operators

  overviewSidebar: {
    DataHub: [
      "README",
      // "docs/faq", // hide from sidebar: out of date
      "docs/features",
      "docs/roadmap",
      "docs/CONTRIBUTING",
      "docs/demo",
      "docs/saas",
      "releases",
    ],
    "Getting Started": [
      // Serves as user guides.
      "docs/quickstart",
      "docs/debugging",
      "metadata-ingestion/README",
    ],
    Architecture: [
      "docs/architecture/architecture",
      "docs/architecture/metadata-ingestion",
      //"docs/what/gma",
      "docs/architecture/metadata-serving",
      //"docs/what/gms",
    ],
    "Metadata Ingestion": [
      {
        Sources: list_ids_in_directory("metadata-ingestion/source_docs"),
      },
      {
        Sinks: list_ids_in_directory("metadata-ingestion/sink_docs"),
      },
    ],
    "Metadata Modeling": [
      "docs/modeling/metadata-model",
      "docs/modeling/extending-the-metadata-model",
      // TODO: change the titles of these, removing the "What is..." portion from the sidebar"
      // "docs/what/entity",
      // "docs/what/aspect",
      // "docs/what/urn",
      // "docs/what/relationship",
      // "docs/what/search-document",
      // "docs/what/snapshot",
      // "docs/what/delta",
      // "docs/what/mxe",
    ],
    "Developer Guides": [
      // TODO: the titles of these should not be in question form in the sidebar
      "docs/developers",
      "docs/docker/development",
      "metadata-ingestion/adding-source",
      "metadata-ingestion/s3-ingestion",
      //"metadata-ingestion/examples/transforms/README"
      "metadata-ingestion/transformers",
      //"docs/what/graph",
      //"docs/what/search-index",
      //"docs/how/add-new-aspect",
      //"docs/how/build-metadata-service",
      //"docs/how/graph-onboarding",
      //"docs/demo/graph-onboarding",
      "docs/how/auth/jaas",
      "docs/how/auth/sso/configure-oidc-react",
      "docs/how/auth/sso/configure-oidc-react-google",
      "docs/how/auth/sso/configure-oidc-react-okta",
      "docs/how/restore-indices",
      "docs/how/extract-container-logs",
      "docs/how/delete-metadata",
      "datahub-web-react/src/app/analytics/README",
      "metadata-ingestion/developing",
    ],
    Components: [
      "datahub-web-react/README",
      "datahub-frontend/README",
      "datahub-graphql-core/README",
      "gms/README",
      "datahub-gms-graphql-service/README",
      // "metadata-jobs/README",
      "metadata-jobs/mae-consumer-job/README",
      "metadata-jobs/mce-consumer-job/README",
    ],
    "Advanced Guides": [
      "docs/advanced/no-code-modeling",
      "docs/advanced/aspect-versioning",
      "docs/advanced/es-7-upgrade",
      "docs/advanced/high-cardinality",
      "docs/how/scsi-onboarding-guide",
      "docs/advanced/no-code-upgrade",
      "docs/how/migrating-graph-service-implementation",
      "docs/advanced/mcp-mcl",
      // WIP "docs/advanced/backfilling",
      // WIP "docs/advanced/derived-aspects",
      // WIP "docs/advanced/entity-hierarchy",
      // WIP "docs/advanced/partial-update",
      // WIP "docs/advanced/pdl-best-practices",
    ],
    Deployment: [
      "docs/how/kafka-config",
      "docker/README",
      "docs/deploy/kubernetes",
      "docker/datahub-upgrade/README",
      "docs/deploy/aws",
      "docs/deploy/gcp",
    ],
    Community: [
      "docs/slack",
      "docs/links",
      "docs/townhalls",
      "docs/townhall-history",
      "docs/CODE_OF_CONDUCT",
      "docs/rfc",
      {
        RFCs: list_ids_in_directory("docs/rfc/active"),
      },
    ],
  },
};
<commit_msg>feat(docs): link to ingestion quickstart under ingestion section (#3059)<commit_after>const fs = require("fs");

function list_ids_in_directory(directory) {
  const files = fs.readdirSync(`../${directory}`).sort();
  let ids = [];
  for (const name of files) {
    if (fs.lstatSync(`../${directory}/${name}`).isDirectory()) {
      // Recurse into the directory.
      const inner_ids = list_ids_in_directory(`${directory}/${name}`);
      ids = ids.concat(inner_ids);
    } else {
      if (name.endsWith(".md")) {
        const slug = name.replace(/\.md$/, "");
        let id = `${directory}/${slug}`;
        if (id.match(/\/\d+-.+/)) {
          id = id.replace(/\/\d+-/, "/");
        }
        ids.push(id);
      }
    }
  }
  return ids;
}

// note: to handle errors where you don't want a markdown file in the sidebar, add it as a comment.
// this will fix errors like `Error: File not accounted for in sidebar: ...`
module.exports = {
  // users
  // architects
  // modelers
  // developers
  // operators

  overviewSidebar: {
    DataHub: [
      "README",
      // "docs/faq", // hide from sidebar: out of date
      "docs/features",
      "docs/roadmap",
      "docs/CONTRIBUTING",
      "docs/demo",
      "docs/saas",
      "releases",
    ],
    "Getting Started": [
      // Serves as user guides.
      "docs/quickstart",
      "docs/debugging",
      "metadata-ingestion/README",
    ],
    Architecture: [
      "docs/architecture/architecture",
      "docs/architecture/metadata-ingestion",
      //"docs/what/gma",
      "docs/architecture/metadata-serving",
      //"docs/what/gms",
    ],
    "Ingestion Home": [
      "metadata-ingestion/README",
      {
        Sources: list_ids_in_directory("metadata-ingestion/source_docs"),
      },
      {
        Sinks: list_ids_in_directory("metadata-ingestion/sink_docs"),
      },
    ],
    "Metadata Modeling": [
      "docs/modeling/metadata-model",
      "docs/modeling/extending-the-metadata-model",
      // TODO: change the titles of these, removing the "What is..." portion from the sidebar"
      // "docs/what/entity",
      // "docs/what/aspect",
      // "docs/what/urn",
      // "docs/what/relationship",
      // "docs/what/search-document",
      // "docs/what/snapshot",
      // "docs/what/delta",
      // "docs/what/mxe",
    ],
    "Developer Guides": [
      // TODO: the titles of these should not be in question form in the sidebar
      "docs/developers",
      "docs/docker/development",
      "metadata-ingestion/adding-source",
      "metadata-ingestion/s3-ingestion",
      //"metadata-ingestion/examples/transforms/README"
      "metadata-ingestion/transformers",
      //"docs/what/graph",
      //"docs/what/search-index",
      //"docs/how/add-new-aspect",
      //"docs/how/build-metadata-service",
      //"docs/how/graph-onboarding",
      //"docs/demo/graph-onboarding",
      "docs/how/auth/jaas",
      "docs/how/auth/sso/configure-oidc-react",
      "docs/how/auth/sso/configure-oidc-react-google",
      "docs/how/auth/sso/configure-oidc-react-okta",
      "docs/how/restore-indices",
      "docs/how/extract-container-logs",
      "docs/how/delete-metadata",
      "datahub-web-react/src/app/analytics/README",
      "metadata-ingestion/developing",
    ],
    Components: [
      "datahub-web-react/README",
      "datahub-frontend/README",
      "datahub-graphql-core/README",
      "gms/README",
      "datahub-gms-graphql-service/README",
      // "metadata-jobs/README",
      "metadata-jobs/mae-consumer-job/README",
      "metadata-jobs/mce-consumer-job/README",
    ],
    "Advanced Guides": [
      "docs/advanced/no-code-modeling",
      "docs/advanced/aspect-versioning",
      "docs/advanced/es-7-upgrade",
      "docs/advanced/high-cardinality",
      "docs/how/scsi-onboarding-guide",
      "docs/advanced/no-code-upgrade",
      "docs/how/migrating-graph-service-implementation",
      "docs/advanced/mcp-mcl",
      // WIP "docs/advanced/backfilling",
      // WIP "docs/advanced/derived-aspects",
      // WIP "docs/advanced/entity-hierarchy",
      // WIP "docs/advanced/partial-update",
      // WIP "docs/advanced/pdl-best-practices",
    ],
    Deployment: [
      "docs/how/kafka-config",
      "docker/README",
      "docs/deploy/kubernetes",
      "docker/datahub-upgrade/README",
      "docs/deploy/aws",
      "docs/deploy/gcp",
    ],
    Community: [
      "docs/slack",
      "docs/links",
      "docs/townhalls",
      "docs/townhall-history",
      "docs/CODE_OF_CONDUCT",
      "docs/rfc",
      {
        RFCs: list_ids_in_directory("docs/rfc/active"),
      },
    ],
  },
};
<|endoftext|>