<commit_before>// json5.js
// Modern JSON. See README.md for details.
//
// This file is modeled directly off of Douglas Crockford's json_parse.js:
// https://github.com/douglascrockford/JSON-js/blob/master/json_parse.js

var JSON5 = (typeof exports === 'object' ? exports : {});

JSON5.parse = (function () {
    "use strict";

// This is a function that can parse a JSON5 text, producing a JavaScript
// data structure. It is a simple, recursive descent parser. It does not use
// eval or regular expressions, so it can be used as a model for implementing
// a JSON5 parser in other languages.

// We are defining the function inside of another function to avoid creating
// global variables.

    var at,     // The index of the current character
        ch,     // The current character
        escapee = {
            "'":  "'",
            '"':  '"',
            '\\': '\\',
            '/':  '/',
            '\n': '',       // Replace newlines in strings w/ empty string
            b:    '\b',
            f:    '\f',
            n:    '\n',
            r:    '\r',
            t:    '\t'
        },
        text,

        error = function (m) {

// Call error when something is wrong.

            throw {
                name:    'SyntaxError',
                message: m,
                at:      at,
                text:    text
            };
        },

        next = function (c) {

// If a c parameter is provided, verify that it matches the current character.

            if (c && c !== ch) {
                error("Expected '" + c + "' instead of '" + ch + "'");
            }

// Get the next character. When there are no more characters,
// return the empty string.

            ch = text.charAt(at);
            at += 1;
            return ch;
        },

        identifier = function () {

// Parse an identifier. Normally, reserved words are disallowed here, but we
// only use this for unquoted object keys, where reserved words are allowed,
// so we don't check for those here. References:
// - http://es5.github.com/#x7.6
// - https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Variables
// - http://docstore.mik.ua/orelly/webprog/jscript/ch02_07.htm
// TODO Identifiers can have Unicode "letters" in them; add support for those.

            var key = ch;

            // Identifiers must start with a letter, _ or $.
            if (!ch.match(/^[a-zA-Z_$]$/)) {
                error("Bad identifier");
            }

            // Subsequent characters can contain digits.
            while (next() && ch.match(/^[\w$]$/)) {
                key += ch;
            }

            return key;
        },

        number = function () {

// Parse a number value.

            var number,
                string = '';

            if (ch === '-') {
                string = '-';
                next('-');
            }
            while (ch >= '0' && ch <= '9') {
                string += ch;
                next();
            }
            if (ch === '.') {
                string += '.';
                while (next() && ch >= '0' && ch <= '9') {
                    string += ch;
                }
            }
            if (ch === 'e' || ch === 'E') {
                string += ch;
                next();
                if (ch === '-' || ch === '+') {
                    string += ch;
                    next();
                }
                while (ch >= '0' && ch <= '9') {
                    string += ch;
                    next();
                }
            }
            number = +string;
            if (!isFinite(number)) {
                error("Bad number");
            } else {
                return number;
            }
        },

        string = function () {

// Parse a string value.

            var hex,
                i,
                string = '',
                delim,      // double quote or single quote
                uffff;

// When parsing for string values, we must look for " and \ characters.

            if (ch === '"' || ch === "'") {
                delim = ch;
                while (next()) {
                    if (ch === delim) {
                        next();
                        return string;
                    } else if (ch === '\\') {
                        next();
                        if (ch === 'u') {
                            uffff = 0;
                            for (i = 0; i < 4; i += 1) {
                                hex = parseInt(next(), 16);
                                if (!isFinite(hex)) {
                                    break;
                                }
                                uffff = uffff * 16 + hex;
                            }
                            string += String.fromCharCode(uffff);
                        } else if (typeof escapee[ch] === 'string') {
                            string += escapee[ch];
                        } else {
                            break;
                        }
                    } else {
                        string += ch;
                    }
                }
            }
            error("Bad string");
        },

        inlineComment = function () {

// Skip inline comments, assuming this is one. The current character should be
// the second / character in the // pair that begins this inline comment.
// When parsing inline comments, we look for a newline or the end of the text.

            if (ch !== '/') {
                error("Not an inline comment");
            }

            do {
                next();
                if (ch === '\n') {
                    next('\n');
                    return;
                }
            } while (ch);
        },

        blockComment = function () {

// Skip block comments, assuming this is one. The current character should be
// the * character in the /* pair that begins this block comment.
// When parsing block comments, we look for an ending */ pair of characters,
// but we also watch for the end of text before the comment is terminated.

            if (ch !== '*') {
                error("Not a block comment");
            }

            do {
                next();
                while (ch === '*') {
                    next('*');
                    if (ch === '/') {
                        next('/');
                        return;
                    }
                }
            } while (ch);

            error("Unterminated block comment");
        },

        comment = function () {

// Skip comments, both inline and block-level, assuming this is one. Comments
// always begin with a / character.

            if (ch !== '/') {
                error("Not a comment");
            }

            next('/');

            if (ch === '/') {
                inlineComment();
            } else if (ch === '*') {
                blockComment();
            } else {
                error("Unrecognized comment");
            }
        },

        white = function () {

// Skip whitespace and comments.
// Note that we're detecting comments by only a single / character.
// This works since regular expressions are not valid JSON(5), but this will
// break if there are other valid values that begin with a / character!

            while (ch) {
                if (ch === '/') {
                    comment();
                } else if (ch <= ' ') {
                    next();
                } else {
                    return;
                }
            }
        },

        word = function () {

// true, false, or null.

            switch (ch) {
            case 't':
                next('t');
                next('r');
                next('u');
                next('e');
                return true;
            case 'f':
                next('f');
                next('a');
                next('l');
                next('s');
                next('e');
                return false;
            case 'n':
                next('n');
                next('u');
                next('l');
                next('l');
                return null;
            }
            error("Unexpected '" + ch + "'");
        },

        value,  // Place holder for the value function.

        array = function () {

// Parse an array value.

            var array = [];

            if (ch === '[') {
                next('[');
                white();
                while (ch) {
                    if (ch === ']') {
                        next(']');
                        return array;   // Potentially empty array
                    }
                    // Omitted values are allowed and detected by the presence
                    // of a trailing comma. Whether the value was omitted or
                    // not, the current character after this block should be
                    // the character after the value.
                    if (ch === ',') {
                        // Pushing an undefined value isn't quite equivalent
                        // to what ES5 does in practice, but we can emulate it
                        // by incrementing the array's length.
                        array.length += 1;
                        // Don't go next; the comma is the character after the
                        // omitted (undefined) value.
                    } else {
                        array.push(value());
                        // The various value methods call next(); the current
                        // character here is now the one after the value.
                    }
                    white();
                    // If there's no comma after this value, this needs to
                    // be the end of the array.
                    if (ch !== ',') {
                        next(']');
                        return array;
                    }
                    next(',');
                    white();
                }
            }
            error("Bad array");
        },

        object = function () {

// Parse an object value.
// TODO Update to support unquoted keys.

            var key,
                object = {};

            if (ch === '{') {
                next('{');
                white();
                while (ch) {
                    if (ch === '}') {
                        next('}');
                        return object;   // Potentially empty object
                    }

                    // Keys can be unquoted. If they are, they need to be
                    // valid JS identifiers.
                    if (ch === '"' || ch === "'") {
                        key = string();
                    } else {
                        key = identifier();
                    }

                    white();
                    next(':');
                    if (Object.hasOwnProperty.call(object, key)) {
                        error('Duplicate key "' + key + '"');
                    }
                    object[key] = value();
                    white();
                    // If there's no comma after this pair, this needs to be
                    // the end of the object.
                    if (ch !== ',') {
                        next('}');
                        return object;
                    }
                    next(',');
                    white();
                }
            }
            error("Bad object");
        };

    value = function () {

// Parse a JSON value. It could be an object, an array, a string, a number,
// or a word.

        white();
        switch (ch) {
        case '{':
            return object();
        case '[':
            return array();
        case '"':
        case "'":
            return string();
        case '-':
            return number();
        default:
            return ch >= '0' && ch <= '9' ? number() : word();
        }
    };

// Return the json_parse function. It will have access to all of the above
// functions and variables.

    return function (source, reviver) {
        var result;

        text = source;
        at = 0;
        ch = ' ';
        result = value();
        white();
        if (ch) {
            error("Syntax error");
        }

// If there is a reviver function, we recursively walk the new structure,
// passing each name/value pair to the reviver function for possible
// transformation, starting with a temporary root object that holds the result
// in an empty key. If there is not a reviver function, we simply return the
// result.

        return typeof reviver === 'function' ? (function walk(holder, key) {
            var k, v, value = holder[key];
            if (value && typeof value === 'object') {
                for (k in value) {
                    if (Object.prototype.hasOwnProperty.call(value, k)) {
                        v = walk(value, k);
                        if (v !== undefined) {
                            value[k] = v;
                        } else {
                            delete value[k];
                        }
                    }
                }
            }
            return reviver.call(holder, key, value);
        }({'': result}, '')) : result;
    };
}());

JSON5.stringify = function (obj, replacer, space) {
    // Since regular JSON is a strict subset of JSON5, we'll always output as
    // regular JSON to foster better interoperability. TODO Should we not?
    return JSON.stringify.apply(JSON, arguments);
};
<commit_msg>Fixed misleading comment.<commit_after>// json5.js
// Modern JSON. See README.md for details.
//
// This file is modeled directly off of Douglas Crockford's json_parse.js:
// https://github.com/douglascrockford/JSON-js/blob/master/json_parse.js

var JSON5 = (typeof exports === 'object' ? exports : {});

JSON5.parse = (function () {
    "use strict";

// This is a function that can parse a JSON5 text, producing a JavaScript
// data structure. It is a simple, recursive descent parser. It does not use
// eval or regular expressions, so it can be used as a model for implementing
// a JSON5 parser in other languages.

// We are defining the function inside of another function to avoid creating
// global variables.

    var at,     // The index of the current character
        ch,     // The current character
        escapee = {
            "'":  "'",
            '"':  '"',
            '\\': '\\',
            '/':  '/',
            '\n': '',       // Replace escaped newlines in strings w/ empty string
            b:    '\b',
            f:    '\f',
            n:    '\n',
            r:    '\r',
            t:    '\t'
        },
        text,

        error = function (m) {

// Call error when something is wrong.

            throw {
                name:    'SyntaxError',
                message: m,
                at:      at,
                text:    text
            };
        },

        next = function (c) {

// If a c parameter is provided, verify that it matches the current character.

            if (c && c !== ch) {
                error("Expected '" + c + "' instead of '" + ch + "'");
            }

// Get the next character. When there are no more characters,
// return the empty string.

            ch = text.charAt(at);
            at += 1;
            return ch;
        },

        identifier = function () {

// Parse an identifier. Normally, reserved words are disallowed here, but we
// only use this for unquoted object keys, where reserved words are allowed,
// so we don't check for those here. References:
// - http://es5.github.com/#x7.6
// - https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Variables
// - http://docstore.mik.ua/orelly/webprog/jscript/ch02_07.htm
// TODO Identifiers can have Unicode "letters" in them; add support for those.

            var key = ch;

            // Identifiers must start with a letter, _ or $.
            if (!ch.match(/^[a-zA-Z_$]$/)) {
                error("Bad identifier");
            }

            // Subsequent characters can contain digits.
            while (next() && ch.match(/^[\w$]$/)) {
                key += ch;
            }

            return key;
        },

        number = function () {

// Parse a number value.

            var number,
                string = '';

            if (ch === '-') {
                string = '-';
                next('-');
            }
            while (ch >= '0' && ch <= '9') {
                string += ch;
                next();
            }
            if (ch === '.') {
                string += '.';
                while (next() && ch >= '0' && ch <= '9') {
                    string += ch;
                }
            }
            if (ch === 'e' || ch === 'E') {
                string += ch;
                next();
                if (ch === '-' || ch === '+') {
                    string += ch;
                    next();
                }
                while (ch >= '0' && ch <= '9') {
                    string += ch;
                    next();
                }
            }
            number = +string;
            if (!isFinite(number)) {
                error("Bad number");
            } else {
                return number;
            }
        },

        string = function () {

// Parse a string value.

            var hex,
                i,
                string = '',
                delim,      // double quote or single quote
                uffff;

// When parsing for string values, we must look for " and \ characters.

            if (ch === '"' || ch === "'") {
                delim = ch;
                while (next()) {
                    if (ch === delim) {
                        next();
                        return string;
                    } else if (ch === '\\') {
                        next();
                        if (ch === 'u') {
                            uffff = 0;
                            for (i = 0; i < 4; i += 1) {
                                hex = parseInt(next(), 16);
                                if (!isFinite(hex)) {
                                    break;
                                }
                                uffff = uffff * 16 + hex;
                            }
                            string += String.fromCharCode(uffff);
                        } else if (typeof escapee[ch] === 'string') {
                            string += escapee[ch];
                        } else {
                            break;
                        }
                    } else {
                        string += ch;
                    }
                }
            }
            error("Bad string");
        },

        inlineComment = function () {

// Skip inline comments, assuming this is one. The current character should be
// the second / character in the // pair that begins this inline comment.
// When parsing inline comments, we look for a newline or the end of the text.

            if (ch !== '/') {
                error("Not an inline comment");
            }

            do {
                next();
                if (ch === '\n') {
                    next('\n');
                    return;
                }
            } while (ch);
        },

        blockComment = function () {

// Skip block comments, assuming this is one. The current character should be
// the * character in the /* pair that begins this block comment.
// When parsing block comments, we look for an ending */ pair of characters,
// but we also watch for the end of text before the comment is terminated.

            if (ch !== '*') {
                error("Not a block comment");
            }

            do {
                next();
                while (ch === '*') {
                    next('*');
                    if (ch === '/') {
                        next('/');
                        return;
                    }
                }
            } while (ch);

            error("Unterminated block comment");
        },

        comment = function () {

// Skip comments, both inline and block-level, assuming this is one. Comments
// always begin with a / character.

            if (ch !== '/') {
                error("Not a comment");
            }

            next('/');

            if (ch === '/') {
                inlineComment();
            } else if (ch === '*') {
                blockComment();
            } else {
                error("Unrecognized comment");
            }
        },

        white = function () {

// Skip whitespace and comments.
// Note that we're detecting comments by only a single / character.
// This works since regular expressions are not valid JSON(5), but this will
// break if there are other valid values that begin with a / character!

            while (ch) {
                if (ch === '/') {
                    comment();
                } else if (ch <= ' ') {
                    next();
                } else {
                    return;
                }
            }
        },

        word = function () {

// true, false, or null.

            switch (ch) {
            case 't':
                next('t');
                next('r');
                next('u');
                next('e');
                return true;
            case 'f':
                next('f');
                next('a');
                next('l');
                next('s');
                next('e');
                return false;
            case 'n':
                next('n');
                next('u');
                next('l');
                next('l');
                return null;
            }
            error("Unexpected '" + ch + "'");
        },

        value,  // Place holder for the value function.

        array = function () {

// Parse an array value.

            var array = [];

            if (ch === '[') {
                next('[');
                white();
                while (ch) {
                    if (ch === ']') {
                        next(']');
                        return array;   // Potentially empty array
                    }
                    // Omitted values are allowed and detected by the presence
                    // of a trailing comma. Whether the value was omitted or
                    // not, the current character after this block should be
                    // the character after the value.
                    if (ch === ',') {
                        // Pushing an undefined value isn't quite equivalent
                        // to what ES5 does in practice, but we can emulate it
                        // by incrementing the array's length.
                        array.length += 1;
                        // Don't go next; the comma is the character after the
                        // omitted (undefined) value.
                    } else {
                        array.push(value());
                        // The various value methods call next(); the current
                        // character here is now the one after the value.
                    }
                    white();
                    // If there's no comma after this value, this needs to
                    // be the end of the array.
                    if (ch !== ',') {
                        next(']');
                        return array;
                    }
                    next(',');
                    white();
                }
            }
            error("Bad array");
        },

        object = function () {

// Parse an object value.
// TODO Update to support unquoted keys.

            var key,
                object = {};

            if (ch === '{') {
                next('{');
                white();
                while (ch) {
                    if (ch === '}') {
                        next('}');
                        return object;   // Potentially empty object
                    }

                    // Keys can be unquoted. If they are, they need to be
                    // valid JS identifiers.
                    if (ch === '"' || ch === "'") {
                        key = string();
                    } else {
                        key = identifier();
                    }

                    white();
                    next(':');
                    if (Object.hasOwnProperty.call(object, key)) {
                        error('Duplicate key "' + key + '"');
                    }
                    object[key] = value();
                    white();
                    // If there's no comma after this pair, this needs to be
                    // the end of the object.
                    if (ch !== ',') {
                        next('}');
                        return object;
                    }
                    next(',');
                    white();
                }
            }
            error("Bad object");
        };

    value = function () {

// Parse a JSON value. It could be an object, an array, a string, a number,
// or a word.

        white();
        switch (ch) {
        case '{':
            return object();
        case '[':
            return array();
        case '"':
        case "'":
            return string();
        case '-':
            return number();
        default:
            return ch >= '0' && ch <= '9' ? number() : word();
        }
    };

// Return the json_parse function. It will have access to all of the above
// functions and variables.

    return function (source, reviver) {
        var result;

        text = source;
        at = 0;
        ch = ' ';
        result = value();
        white();
        if (ch) {
            error("Syntax error");
        }

// If there is a reviver function, we recursively walk the new structure,
// passing each name/value pair to the reviver function for possible
// transformation, starting with a temporary root object that holds the result
// in an empty key. If there is not a reviver function, we simply return the
// result.

        return typeof reviver === 'function' ? (function walk(holder, key) {
            var k, v, value = holder[key];
            if (value && typeof value === 'object') {
                for (k in value) {
                    if (Object.prototype.hasOwnProperty.call(value, k)) {
                        v = walk(value, k);
                        if (v !== undefined) {
                            value[k] = v;
                        } else {
                            delete value[k];
                        }
                    }
                }
            }
            return reviver.call(holder, key, value);
        }({'': result}, '')) : result;
    };
}());

JSON5.stringify = function (obj, replacer, space) {
    // Since regular JSON is a strict subset of JSON5, we'll always output as
    // regular JSON to foster better interoperability. TODO Should we not?
    return JSON.stringify.apply(JSON, arguments);
};
<|endoftext|>


<commit_before>var markdown = require('markdown').markdown,
    yargs = require('yargs');

// TODO: get filename as arguments from terminal for publish.
// TODO: read markdown file.
// TODO: convert it to html.
// TODO: embed into the page template.
// TODO: create html file in static folder.
// TODO: update the home page list.
<commit_msg>Basic prompt and code structure<commit_after>#!/usr/bin/env node

var markdown = require('markdown').markdown,
    argv = require('yargs').argv;

var Large = {
  folder : {
    markdown : [process.cwd(), 'post/'].join('/'),
    html : ''
  },

  // TODO: read markdown file.
  _readMarkdown : function(filename)  {
    console.log(this.folder.markdown);
    console.log(filename);
  },

  // TODO: convert it to html.
  // TODO: embed into the page template.
  // TODO: create html file in static folder.
  // TODO: update the home page list.
  // TODO: publish the article.
  publish : function(filename)  {
    this._readMarkdown(filename);
  }
};

if(argv.p)  {
  Large.publish(argv.p);
}
<|endoftext|>


<commit_before>/*The file to interface with the beam API */

//run 'npm i -S beam-client-node' to get these two things
const BeamClient = require('beam-client-node');
const BeamSocket = require('beam-client-node/lib/ws');
const config = require('./config.json');
const spawn = require('child_process').spawn;

const debug = config.debug;
var moderationModule = config.moderationOn;
const commandModule = config.commandsOn;

let userInfo;
let channelId = -1;

const client = new BeamClient();

//OAuth gives permision for all following actions
client.use('oauth',{
  tokens:{
    access: config.OAuth,
    expires: Date.now() + (365 * 24 *60 *60 *1000)
  },
});

//get following channel and join its chat
client.request('GET', 'users/current')
.then(response => {
    if(debug) console.log(response.body);
    userInfo = response.body;
    return client.request('GET','users/'+userInfo.id+'/follows');
})
.then(response =>{
  //TODO modify to join all followed channels
  if(debug){
    return client.chat.join(userInfo.channel.id);
  }else{
    if(response.body[0]){
      channelId = response.body[0].id;
      console.log("Joining",response.body[0].name);
      return client.chat.join(channelId);
    }else{
      return client.chat.join(userInfo.channel.id);
    }
  }
})
.then(response => {
    const body = response.body;
    if(debug) console.log(body);
    if(!body.roles.includes('Mod') && !body.roles.includes('Owner')){
      console.log("Cannot moderate this chat");
      moderationModule = false;
    }
    if(!debug && channelId !== -1){
      return createChatSocket(userInfo.id, channelId, body.endpoints, body.authkey);
    }else{
      return createChatSocket(userInfo.id, userInfo.channel.id, body.endpoints, body.authkey);
    }
})
.catch(error => {
      console.log("Something went wrong:", error);
});

/**
 * Creates a beam chat socket and sets up listeners to various chat events.
 * @param {number} userId The user to authenticate as
 * @param {number} channelId The channel id to join
 * @param {any} endpoints An endpoints array from a beam.chat.join call.
 * @param {any} authkey An authentication key from a beam.chat.join call.
 * @returns {Promise.<>}
 */
function createChatSocket (userId, channelId, endpoints, authkey) {
    const socket = new BeamSocket(endpoints).boot();

    socket.auth(channelId, userId, authkey)
    .then(() => {
        if(debug) console.log('You are now authenticated!');
        // Send a chat message
        console.log("Joined chatroom");
        return socket.call('msg', ['BOOTING UP...']);
    })
    .catch(error => {
        console.log('Oh no! An error occurred!', error);
    });

    // Listen to chat messages, note that you will also receive your own!
    socket.on('ChatMessage', data => {
        if(debug) console.log('We got a ChatMessage packet!');
        if(debug) console.log(data);
        if(debug) console.log(data.message); // lets take a closer look
        var roles = data.user_roles;
        if(moderationModule && !roles.includes('Mod') && !roles.includes('Owner')){
          moderate(socket,data);
        }
        if(commandModule){
          commands(socket,data,roles);
        }
    });

    // Listen to socket errors, you'll need to handle these!
    socket.on('error', error => {
        console.error('Socket error', error);
    });
}

function moderate(socket,messageData){
  //This is how to send the data to be processed by the python
  var moderator = spawn('python3', ['hammer.py']);

  if(debug) console.log("In moderate function");
  var msgArr = messageToString(messageData.message.message);
  var msg = msgArr[0];
  var hasLink = msgArr[1]; //TODO make link handler
  var toPython = messageData.user_name + " " + msg;
  if(debug) console.log("MODERATE: Input to python:", toPython);
  moderator.stdin.write(toPython);
  moderator.stdin.end();

  moderator.stdout.on('data', function(data){
    var pythonOut = data.toString().trim();
    if(debug) console.log("MODERATE: From python:",pythonOut);
    var action = pythonOut.split(" ")[0];
    var user = pythonOut.split(" ")[1];
    var indexOfSpace1 = pythonOut.indexOf(" ");
    var indexOfSpace2 = pythonOut.indexOf(" ",indexOfSpace1+1);
    var response = pythonOut.substr(indexOfSpace2);
    if(indexOfSpace2 === -1){
      response = "";
    }
    if(debug){
      console.log("\tMODERATE: User is:", user);
      console.log("\tMODERATE: Action to take:",action);
      console.log("\tMODERATE: Response is: ","@"+user+": "+response);
    }
    //Parse the actions and take action if needed
    if(action === "timeout"){
      timeout(socket,user,response,msg);
    }else if(action === "ban"){
      ban(socket,user,response,msg);
    }else if(action === "purge"){
      purge(socket,user,response,msg);
    }else if(action === "nothing"){
      if(debug){console.log("No action to take");}
      //THIS SPACE INTENTIONALLY LEFT BLANK
    }
  });

  moderator.stdout.on('end', function(){
    if(debug) console.log("Finished moderate parse");
  });
}

function ban(socket,user,response,msg){ //TODO check that the call completed successfully
  if(debug){
    console.log("Need to ban",user);
  }else{
    socket.call('timeout',[user,config.banDuration]); //need to timeout for 1 to unban
    if(!config.silentBans) socket.call('msg',["@"+user+": "+response]);
    console.log("Banning",user,"for message:",msg);
  }
}

function purge(socket,user,response,msg){ //TODO check that the call completed successfully
  if(debug){
    console.log("Need to purge",user);
  }else{
    socket.call('purge',[user]);
    if(!config.silentBans) socket.call('msg',["@"+user+": "+response]);
    console.log("Purging",user,"for message:",msg);
  }
}

//To unban, use a duration of 1 with this function
function timeout(socket,user,response,msg,duration){ //TODO check that the call completed successfully
  if(debug){
    console.log("Need to timeout",user);
  }else{
    if(duration){
      socket.call('timeout',[user,duration]);
    }else{
      socket.call('timeout',[user,config.timeoutDuration]);
    }
    if(!config.silentBans) socket.call('msg',["@"+user+": "+response]);
    console.log("Timing out",user,"for message:",msg);
  }
}

function unban(socket,user){
  timeout(socket,user,"You have been unbanned", "<TO UNBAN>",1);
}

function messageToString(array){
  var result = "";
  var hasLink = false;
  for(var index in array){
    var element = array[index];
    result += element.text;
    if(element.type === "link"){
      hasLink = true;
    }
  }
  if(debug) console.log("messageToString is:", result);
  return [result, hasLink];
}

function commands(socket,messageData,roles){
  if(debug) console.log("In commands function");
  var msg = messageToString(messageData.message.message)[0];
  var role = "Normal";
  var username = messageData.user_name;

  //Send command to python script
  if(msg.charAt(0) != '!'){
    return;
  }else{
    role = parseRoles(roles);
    var handleCmds = spawn('python3', ['centurion.py']);
    var toPython = username + " " + role + " " + msg;
    if(debug) console.log("COMMANDS: Input to python:", toPython);
    handleCmds.stdin.write(toPython);
    handleCmds.stdin.end();

    handleCmds.stdout.on('data', function(data){
      var pythonOut = data.toString().trim();
      if(debug) console.log("COMMANDS: From python:",pythonOut);
      var action = pythonOut.split(" ")[0];
      var user = pythonOut.split(" ")[1];
      var indexOfSpace1 = pythonOut.indexOf(" ");
      var indexOfSpace2 = pythonOut.indexOf(" ",indexOfSpace1+1);
      var response = pythonOut.substr(indexOfSpace2);
      if(indexOfSpace2 === -1){
        response = "";
      }
      if(debug){
        console.log("\tCOMMANDS: User is:", user);
        console.log("\tCOMMANDS: Action to take:",action);
        console.log("\tCOMMANDS: Response is: ","@"+user+": "+response);
      }
      //Parse the actions and take action if needed
      if(action === "timeout"){
        timeout(socket,user,response,msg);
      }else if(action === "ban"){
        ban(socket,user,response,msg);
      }else if(action === "purge"){
        purge(socket,user,response,msg);
      }else if(action === "respond"){
        socket.call('msg', [response]);
      }else if(action === "unban"){
        unban(socket,user);
      }else if(action === "nothing"){
        if(debug){console.log("No action to take");}
        //THIS SPACE INTENTIONALLY LEFT BLANK
      }
    });

    handleCmds.stdout.on('end', function(){
      if(debug) console.log("Finished moderate parse");
    });
  }

}

function parseRoles(roleArr){
  var result = "Normal";
  for(var index in roleArr){
    var role = roleArr[index];
    //TODO parse the remaining Mixer roles'
    if(role === "Owner"){
      result = "Caster";
    }else if(role === "Mod" && result !== "Caster"){
      result = "Mod";
    }else if(role == "User" && result !== "Caster" && result !== "Mod"){
      result = "Normal";
    }
  }
  return result;
}
<commit_msg>updated debug text again<commit_after>/*The file to interface with the beam API */

//run 'npm i -S beam-client-node' to get these two things
const BeamClient = require('beam-client-node');
const BeamSocket = require('beam-client-node/lib/ws');
const config = require('./config.json');
const spawn = require('child_process').spawn;

const debug = config.debug;
var moderationModule = config.moderationOn;
const commandModule = config.commandsOn;

let userInfo;
let channelId = -1;

const client = new BeamClient();

//OAuth gives permision for all following actions
client.use('oauth',{
  tokens:{
    access: config.OAuth,
    expires: Date.now() + (365 * 24 *60 *60 *1000)
  },
});

//get following channel and join its chat
client.request('GET', 'users/current')
.then(response => {
    if(debug) console.log(response.body);
    userInfo = response.body;
    return client.request('GET','users/'+userInfo.id+'/follows');
})
.then(response =>{
  //TODO modify to join all followed channels
  if(debug){
    return client.chat.join(userInfo.channel.id);
  }else{
    if(response.body[0]){
      channelId = response.body[0].id;
      console.log("Joining",response.body[0].name);
      return client.chat.join(channelId);
    }else{
      return client.chat.join(userInfo.channel.id);
    }
  }
})
.then(response => {
    const body = response.body;
    if(debug) console.log(body);
    if(!body.roles.includes('Mod') && !body.roles.includes('Owner')){
      console.log("Cannot moderate this chat");
      moderationModule = false;
    }
    if(!debug && channelId !== -1){
      return createChatSocket(userInfo.id, channelId, body.endpoints, body.authkey);
    }else{
      return createChatSocket(userInfo.id, userInfo.channel.id, body.endpoints, body.authkey);
    }
})
.catch(error => {
      console.log("Something went wrong:", error);
});

/**
 * Creates a beam chat socket and sets up listeners to various chat events.
 * @param {number} userId The user to authenticate as
 * @param {number} channelId The channel id to join
 * @param {any} endpoints An endpoints array from a beam.chat.join call.
 * @param {any} authkey An authentication key from a beam.chat.join call.
 * @returns {Promise.<>}
 */
function createChatSocket (userId, channelId, endpoints, authkey) {
    const socket = new BeamSocket(endpoints).boot();

    socket.auth(channelId, userId, authkey)
    .then(() => {
        if(debug) console.log('You are now authenticated!');
        // Send a chat message
        console.log("Joined chatroom");
        return socket.call('msg', ['BOOTING UP...']);
    })
    .catch(error => {
        console.log('Oh no! An error occurred!', error);
    });

    // Listen to chat messages, note that you will also receive your own!
    socket.on('ChatMessage', data => {
        if(debug) console.log('We got a ChatMessage packet!');
        if(debug) console.log(data);
        if(debug) console.log(data.message); // lets take a closer look
        var roles = data.user_roles;
        if(moderationModule && !roles.includes('Mod') && !roles.includes('Owner')){
          moderate(socket,data);
        }
        if(commandModule){
          commands(socket,data,roles);
        }
    });

    // Listen to socket errors, you'll need to handle these!
    socket.on('error', error => {
        console.error('Socket error', error);
    });
}

function moderate(socket,messageData){
  //This is how to send the data to be processed by the python
  var moderator = spawn('python3', ['hammer.py']);

  if(debug) console.log("MODERATE: In moderate function");
  var msgArr = messageToString(messageData.message.message);
  var msg = msgArr[0];
  var hasLink = msgArr[1]; //TODO make link handler
  var toPython = messageData.user_name + " " + msg;
  if(debug) console.log("\tMODERATE: Input to python:", toPython);
  moderator.stdin.write(toPython);
  moderator.stdin.end();

  moderator.stdout.on('data', function(data){
    var pythonOut = data.toString().trim();
    if(debug) console.log("\tMODERATE: From python:",pythonOut);
    var action = pythonOut.split(" ")[0];
    var user = pythonOut.split(" ")[1];
    var indexOfSpace1 = pythonOut.indexOf(" ");
    var indexOfSpace2 = pythonOut.indexOf(" ",indexOfSpace1+1);
    var response = pythonOut.substr(indexOfSpace2);
    if(indexOfSpace2 === -1){
      response = "";
    }
    if(debug){
      console.log("\tMODERATE: User is:", user);
      console.log("\tMODERATE: Action to take:",action);
      console.log("\tMODERATE: Response is: ","@"+user+": "+response);
    }
    //Parse the actions and take action if needed
    if(action === "timeout"){
      timeout(socket,user,response,msg);
    }else if(action === "ban"){
      ban(socket,user,response,msg);
    }else if(action === "purge"){
      purge(socket,user,response,msg);
    }else if(action === "nothing"){
      if(debug){console.log("No action to take");}
      //THIS SPACE INTENTIONALLY LEFT BLANK
    }
  });

  moderator.stdout.on('end', function(){
    if(debug) console.log("Finished moderate parse");
  });
}

function ban(socket,user,response,msg){ //TODO check that the call completed successfully
  if(debug){
    console.log("Need to ban",user);
  }else{
    socket.call('timeout',[user,config.banDuration]); //need to timeout for 1 to unban
    if(!config.silentBans) socket.call('msg',["@"+user+": "+response]);
    console.log("Banning",user,"for message:",msg);
  }
}

function purge(socket,user,response,msg){ //TODO check that the call completed successfully
  if(debug){
    console.log("Need to purge",user);
  }else{
    socket.call('purge',[user]);
    if(!config.silentBans) socket.call('msg',["@"+user+": "+response]);
    console.log("Purging",user,"for message:",msg);
  }
}

//To unban, use a duration of 1 with this function
function timeout(socket,user,response,msg,duration){ //TODO check that the call completed successfully
  if(debug){
    console.log("Need to timeout",user);
  }else{
    if(duration){
      socket.call('timeout',[user,duration]);
    }else{
      socket.call('timeout',[user,config.timeoutDuration]);
    }
    if(!config.silentBans) socket.call('msg',["@"+user+": "+response]);
    console.log("Timing out",user,"for message:",msg);
  }
}

function unban(socket,user){
  timeout(socket,user,"You have been unbanned", "<TO UNBAN>",1);
}

function messageToString(array){
  var result = "";
  var hasLink = false;
  for(var index in array){
    var element = array[index];
    result += element.text;
    if(element.type === "link"){
      hasLink = true;
    }
  }
  if(debug) console.log("\tMESSAGE2STRING: messageToString is:", result);
  return [result, hasLink];
}

function commands(socket,messageData,roles){
  if(debug) console.log("COMMANDS: In commands function");
  var msg = messageToString(messageData.message.message)[0];
  var role = "Normal";
  var username = messageData.user_name;

  //Send command to python script
  if(msg.charAt(0) != '!'){
    return;
  }else{
    role = parseRoles(roles);
    var handleCmds = spawn('python3', ['centurion.py']);
    var toPython = username + " " + role + " " + msg;
    if(debug) console.log("\tCOMMANDS: Input to python:", toPython);
    handleCmds.stdin.write(toPython);
    handleCmds.stdin.end();

    handleCmds.stdout.on('data', function(data){
      var pythonOut = data.toString().trim();
      if(debug) console.log("\tCOMMANDS: From python:",pythonOut);
      var action = pythonOut.split(" ")[0];
      var user = pythonOut.split(" ")[1];
      var indexOfSpace1 = pythonOut.indexOf(" ");
      var indexOfSpace2 = pythonOut.indexOf(" ",indexOfSpace1+1);
      var response = pythonOut.substr(indexOfSpace2);
      if(indexOfSpace2 === -1){
        response = "";
      }
      if(debug){
        console.log("\tCOMMANDS: User is:", user);
        console.log("\tCOMMANDS: Action to take:",action);
        console.log("\tCOMMANDS: Response is: ","@"+user+": "+response);
      }
      //Parse the actions and take action if needed
      if(action === "timeout"){
        timeout(socket,user,response,msg);
      }else if(action === "ban"){
        ban(socket,user,response,msg);
      }else if(action === "purge"){
        purge(socket,user,response,msg);
      }else if(action === "respond"){
        socket.call('msg', [response]);
      }else if(action === "unban"){
        unban(socket,user);
      }else if(action === "nothing"){
        if(debug){console.log("No action to take");}
        //THIS SPACE INTENTIONALLY LEFT BLANK
      }
    });

    handleCmds.stdout.on('end', function(){
      if(debug) console.log("Finished moderate parse");
    });
  }

}

function parseRoles(roleArr){
  var result = "Normal";
  for(var index in roleArr){
    var role = roleArr[index];
    //TODO parse the remaining Mixer roles'
    if(role === "Owner"){
      result = "Caster";
    }else if(role === "Mod" && result !== "Caster"){
      result = "Mod";
    }else if(role == "User" && result !== "Caster" && result !== "Mod"){
      result = "Normal";
    }
  }
  return result;
}
<|endoftext|>


<commit_before>jQuery(document).ready(function() {
    jQuery(".post-type-archive-learn .learnPosts").each(function() {
        var index = jQuery(this).index();
        //alert(index);
        var myArray = [];
        jQuery(this).find(".categories a").each(function() {
            strClass = jQuery(this).text();
            strClass = strClass.replace(/[^a-z0-9\s]/gi, '').replace(/[_\s]/g, '');
            if (jQuery(this).hasClass('selcountry')) {
                strClass = 'country_' + strClass;
            }
            myArray.push(strClass);
        });
        function addClassToDiv(elem) {
            jQuery(".post-type-archive-learn .learnPosts").eq(index).addClass(elem);
        }
        myArray.forEach(addClassToDiv);
    });
    //display results or hide results
    function noResultDisplay() {
        visLearn = jQuery(".post-type-archive-learn .learnPosts:visible").size();
        if (visLearn > 0) {
            jQuery(".post-type-archive-learn #noResults").css("display", "none")
        } else {
            jQuery(".post-type-archive-learn #noResults").css("display", "block")
        }
    }
    jQuery('select').prop('selectedIndex', 0);
    jQuery(".post-type-archive-learn #country").change(function() {
        jQuery('.post-type-archive-learn #implementation_partner').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #project_type').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #technology').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #publication_type').prop('selectedIndex', 0);
        var strSelect = "";
        strSelect = jQuery(".post-type-archive-learn #country option:selected").text();
        strSelect = strSelect.replace(/[^a-z0-9\s]/gi, '').replace(/[\s]/g, '_'); //strip whitespaces, add underscore
        if (strSelect == "Country") {
            jQuery(".post-type-archive-learn .learnPosts").show("slow");
        } else {
            strSelectClass = ".country_" + strSelect;
            jQuery(".post-type-archive-learn .learnPosts").hide();
            jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry").hide();
            jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .zpt").hide();
            //jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry"+ strSelectClass).show();
            //jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry.Global").show();
            //jQuery(strSelectClass).show("slow");
            jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry" + strSelectClass).each(function() {
                jQuery(this).show();
                //jQuery(this).find(".selcountry.country_Global").show();
                parentClasses = jQuery(this).parent().parent().parent().parent().attr('class');
                //alert(parentClasses);
                jQuery(this).parent().parent().parent().parent().show();
            });
        }
        noResultDisplay();
    });
    jQuery(".post-type-archive-learn #implementation_partner").change(function() {
        jQuery('.post-type-archive-learn #country').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #project_type').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #technology').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #publication_type').prop('selectedIndex', 0);
        var strSelect = "";
        strSelect = jQuery(".post-type-archive-learn #implementation_partner option:selected").text();
        strSelect = strSelect.replace(/[^a-z0-9\s]/gi, '').replace(/[_\s]/g, '');
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry").show();
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .zpt").show();
        if (strSelect == "Organization") {
            jQuery(".post-type-archive-learn .learnPosts").show("slow");
        } else {
            strSelectClass = "." + strSelect;
            jQuery(".post-type-archive-learn .learnPosts").hide();
            jQuery(strSelectClass).show("slow");
        }
        noResultDisplay();
    });
    jQuery(".post-type-archive-learn #project_type").change(function() {
        jQuery('.post-type-archive-learn #country').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #implementation_partner').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #technology').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #publication_type').prop('selectedIndex', 0);
        var strSelect = "";
        strSelect = jQuery(".post-type-archive-learn #project_type option:selected").text();
        strSelect = strSelect.replace(/[^a-z0-9\s]/gi, '').replace(/[_\s]/g, '');
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry").show();
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .zpt").show();
        if (strSelect == "Topic") {
            jQuery(".post-type-archive-learn .learnPosts").show("slow");
        } else {
            strSelectClass = "." + strSelect;
            jQuery(".post-type-archive-learn .learnPosts").hide();
            jQuery(strSelectClass).show("slow");
        }
        noResultDisplay();
    });
    jQuery(".post-type-archive-learn #technology").change(function() {
        jQuery('.post-type-archive-learn #country').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #project_type').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #implementation_partner').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #publication_type').prop('selectedIndex', 0);
        var strSelect = "";
        strSelect = jQuery(".post-type-archive-learn #technology option:selected").text();
        strSelect = strSelect.replace(/[^a-z0-9\s]/gi, '').replace(/[\s]/g, '_'); //strip whitespaces, add underscore
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry").show();
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .zpt").show();
        if (strSelect == "Technology") {
            jQuery(".post-type-archive-learn .learnPosts").show("slow");
        } else {
            strSelectClass = "." + strSelect;
            jQuery(".post-type-archive-learn .learnPosts").hide();
            jQuery(strSelectClass).show("slow");
        }
        noResultDisplay();
    });
    jQuery(".post-type-archive-learn #publication_type").change(function() {
        jQuery('.post-type-archive-learn #country').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #project_type').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #implementation_partner').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #technology').prop('selectedIndex', 0);
        var strSelect = "";
        strSelect = jQuery(".post-type-archive-learn #publication_type option:selected").text();
        strSelect = strSelect.replace(/[^a-z0-9\s]/gi, '').replace(/[_\s]/g, '');
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry").show();
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .zpt").show();
        if (strSelect == "PublicationType") {
            jQuery(".post-type-archive-learn .learnPosts").show("slow");
        } else {
            strSelectClass = "." + strSelect;
            jQuery(".post-type-archive-learn .learnPosts").hide();
            jQuery(strSelectClass).show("slow");
        }
        noResultDisplay();
    });
});
<commit_msg>replaced var name and functions, restore #tech rgx<commit_after>jQuery(document).ready(function() {
    jQuery(".post-type-archive-learn .learnPosts").each(function() {
        //var index = jQuery(this).index();
        //alert(index);
        //var myArray = [];
        var categoryClasses = [];
        jQuery(this).find(".categories a").each(function() {
            strClass = jQuery(this).text();
            strClass = strClass.replace(/[^a-z0-9\s]/gi, '').replace(/[_\s]/g, '');
            if (jQuery(this).hasClass('selcountry')) {
                strClass = 'country_' + strClass;
            }
            //myArray.push(strClass);
            categoryClasses.push(strClass);
        });
        /*function addClassToDiv(elem) {
            //jQuery(".post-type-archive-learn .learnPosts").eq(index).addClass(elem);
          jQuery(this).addClass(elem);
        }
        myArray.forEach(addClassToDiv);*/
        jQuery(this).addClass(categoryClasses.join(' '));
    });
    //display results or hide results
    function noResultDisplay() {
        visLearn = jQuery(".post-type-archive-learn .learnPosts:visible").size();
        if (visLearn > 0) {
            jQuery(".post-type-archive-learn #noResults").css("display", "none")
        } else {
            jQuery(".post-type-archive-learn #noResults").css("display", "block")
        }
    }
    jQuery('select').prop('selectedIndex', 0);
    jQuery(".post-type-archive-learn #country").change(function() {
        jQuery('.post-type-archive-learn #implementation_partner').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #project_type').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #technology').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #publication_type').prop('selectedIndex', 0);
        var strSelect = "";
        strSelect = jQuery(".post-type-archive-learn #country option:selected").text();
        strSelect = strSelect.replace(/[^a-z0-9\s]/gi, '').replace(/[\s]/g, '_');
        if (strSelect == "Country") {
            jQuery(".post-type-archive-learn .learnPosts").show("slow");
        } else {
            strSelectClass = ".country_" + strSelect;
            jQuery(".post-type-archive-learn .learnPosts").hide();
            jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry").hide();
            jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .zpt").hide();
            //jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry"+ strSelectClass).show();
            //jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry.Global").show();
            //jQuery(strSelectClass).show("slow");
            jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry" + strSelectClass).each(function() {
                jQuery(this).show();
                //jQuery(this).find(".selcountry.country_Global").show();
                parentClasses = jQuery(this).parent().parent().parent().parent().attr('class');
                //alert(parentClasses);
                jQuery(this).parent().parent().parent().parent().show();
            });
        }
        noResultDisplay();
    });
    jQuery(".post-type-archive-learn #implementation_partner").change(function() {
        jQuery('.post-type-archive-learn #country').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #project_type').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #technology').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #publication_type').prop('selectedIndex', 0);
        var strSelect = "";
        strSelect = jQuery(".post-type-archive-learn #implementation_partner option:selected").text();
        strSelect = strSelect.replace(/[^a-z0-9\s]/gi, '').replace(/[_\s]/g, '');
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry").show();
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .zpt").show();
        if (strSelect == "Organization") {
            jQuery(".post-type-archive-learn .learnPosts").show("slow");
        } else {
            strSelectClass = "." + strSelect;
            jQuery(".post-type-archive-learn .learnPosts").hide();
            jQuery(strSelectClass).show("slow");
        }
        noResultDisplay();
    });
    jQuery(".post-type-archive-learn #project_type").change(function() {
        jQuery('.post-type-archive-learn #country').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #implementation_partner').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #technology').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #publication_type').prop('selectedIndex', 0);
        var strSelect = "";
        strSelect = jQuery(".post-type-archive-learn #project_type option:selected").text();
        strSelect = strSelect.replace(/[^a-z0-9\s]/gi, '').replace(/[_\s]/g, '');
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry").show();
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .zpt").show();
        if (strSelect == "Topic") {
            jQuery(".post-type-archive-learn .learnPosts").show("slow");
        } else {
            strSelectClass = "." + strSelect;
            jQuery(".post-type-archive-learn .learnPosts").hide();
            jQuery(strSelectClass).show("slow");
        }
        noResultDisplay();
    });
    jQuery(".post-type-archive-learn #technology").change(function() {
        jQuery('.post-type-archive-learn #country').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #project_type').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #implementation_partner').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #publication_type').prop('selectedIndex', 0);
        var strSelect = "";
        strSelect = jQuery(".post-type-archive-learn #technology option:selected").text();
        //strSelect = strSelect.replace(/[^a-z0-9\s]/gi, '').replace(/[\s]/g, '_');
        strSelect = strSelect.replace(/[^a-zA-Z0-9]/g, '')
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry").show();
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .zpt").show();
        if (strSelect == "Technology") {
            jQuery(".post-type-archive-learn .learnPosts").show("slow");
        } else {
            strSelectClass = "." + strSelect;
            jQuery(".post-type-archive-learn .learnPosts").hide();
            jQuery(strSelectClass).show("slow");
        }
        noResultDisplay();
    });
    jQuery(".post-type-archive-learn #publication_type").change(function() {
        jQuery('.post-type-archive-learn #country').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #project_type').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #implementation_partner').prop('selectedIndex', 0);
        jQuery('.post-type-archive-learn #technology').prop('selectedIndex', 0);
        var strSelect = "";
        strSelect = jQuery(".post-type-archive-learn #publication_type option:selected").text();
        strSelect = strSelect.replace(/[^a-z0-9\s]/gi, '').replace(/[_\s]/g, '');
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .selcountry").show();
        jQuery(".post-type-archive-learn .learnPosts .tag-cloud .categories.countries .zpt").show();
        if (strSelect == "PublicationType") {
            jQuery(".post-type-archive-learn .learnPosts").show("slow");
        } else {
            strSelectClass = "." + strSelect;
            jQuery(".post-type-archive-learn .learnPosts").hide();
            jQuery(strSelectClass).show("slow");
        }
        noResultDisplay();
    });
});
<|endoftext|>


<commit_before>var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");

function parseQuery() {
    var query = window.location.search.substring(1);
    var vars = query.split('&');
    var result = {};
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split('=');
        result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
    }
    return result;
}

module.exports = function (conf) {

    var self;
    var config;

    self = this;
    config = processConfig(conf);
    var query = parseQuery();

    Events.call(self, config);
    self.emit("ready", config);

    // this is the provider callback
    if (query.provider) {
        handleProviderCallback(query);
        return;
    }

    // show early the user info if this is available in the cookies
    //setUserInfoFrom("cookies");

    self.link("userInfo", function(err, data) {

        if (err) {
            alert(err);
            return;
        }

        /* =====================
         * The user is logged in
         * ===================== */
        if (data) {

            // let others know the user is logged in
            self.emit('userInfo', data);

            // and also we have to adjust the cookies/UI
            setUserInfoFrom(data);

            // show the logged in mode
            $(config.classes.login, self.dom).hide();
            $(config.classes.logout, self.dom).show();

            // add the logout handler
            $(self.dom).on("click", config.classes.logoutLink, function() {
                logout(function() {
                    window.location = config.successPage;
                });
                return false;
            });

            return;
        }

        /* =========================
         * The user is NOT logged in
         * ========================= */

        // delete the cookies (if any)
        for (var cookie in config.cookies) {
            $.removeCookie(cookie);
        }

        $(self.dom).on('click', config.classes.loginButton, function() {

            $("body").css("cursor", "wait");
            self.link('redirect', { data: { provider: $(this).attr('data-provider') } }, function(err, data) {
                if (err) {
                    console.log(err);
                    return;
                }
                window.location = data;
            });
            return false;
        });

        // show the correct UI parts
        $(config.classes.logout).hide();
        $(config.classes.login).show();

        // The page requires auth => redirects the
        // user automatically on the login link
        // TODO A better way?
        function verify() {
            var redirect = config.auth.login.redirect
            if (requiresAuth() && redirect) {
                if (typeof redirect === "object") {
                    self.emit("redirect", redirect.page);
                    return;
                }
                window.location = redirect;
            }
        }

        verify();

        $(window).on("hashchange", function() {
            verify();
        });
    });

    /*
     * This will be called when the login provider calls back on us.
     */
    function handleProviderCallback(query) {
        $(config.classes.login, self.dom).hide();

        query.cookies = config.cookies;

        // call login operation
        self.link("login", { data: query }, function(err, data) {
            if (err) {
                console.log(err);
                return;
            }

            window.location = config.successPage;
        });
    }

    // Logout
    function logout(callback) {

        for (var cookie in config.cookies) {
            $.removeCookie(cookie);
        }

        self.link("logout", callback);
        self.emit("loggedOut");
    }

        /*
            config:

            {
                loginPage:     ..., (default: "/bitbucket-login")
                successPage:   ..., (default: "/")
                redirect_uri:  ...,
                logoutLink:    ..., (default: "/logout")
                htmlAttributes: {
                    cookies: {
                        "userInfo": "data-user-cookie-info"
                    }
                }
                classes: {
                    loginButton: ..., (default: ".login-button")
                    login:     ..., (default: ".login")
                    logout:    ..., (default: ".logout")
                    logoutLink:..., (default: ".logout-btn")
                    notLogged: ..., (default: ".fail")
                    username:  ... (default: ".userName")
                },
                auth: {
                    login: {
                        redirect: ..., (default: true)
                    },
                    logout: {
                        redirect: ..., (default: false)
                    },
                    pages: [
                        "/examplePathName#withHash?andSomethingSearch"
                    ]
                }
            }
        */

    // Sets defaults, and correct empty objects
    function processConfig(config) {

        // General
        config.successPage = config.successPage || "/";
        config.logoutLink = config.logoutLink || "/logout";

        // Html Attributes
        // TODO Dinamic HTML Attributes, like for cookies?
        config.htmlAttributes = config.htmlAttributes || {};
        config.htmlAttributes.cookies = config.htmlAttributes.cookies || {};
        config.htmlAttributes.cookies.userInfo = config.htmlAttributes.cookies.userInfo || "data-user-cookie-info";

        // Classes
        config.classes = config.classes || {};
        config.classes.loginButton = config.classes.loginButton || ".login-button";
        config.classes.login = config.classes.login || ".login";
        config.classes.logout = config.classes.logout || ".logout";
        config.classes.logoutLink = config.classes.logoutLink || ".logout-btn";
        config.classes.userName = config.classes.userName || ".userName";

        // Auth
        config.auth = config.auth || {};
        config.auth.login = config.auth.login || {};
        config.auth.logout = config.auth.logout || {};
        config.auth.pages = config.auth.pages || [];

        return config;
    }

    // Gets callback code
    function getCode(url, str) {
        return url.substring(url.indexOf(str) + str.length);
    }

    // Verify if the user is on a page that require authentification
    function requiresAuth() {

        var location = window.location;
        var currentPage = location.pathname + location.hash + location.search;

        // Return true if the page is on the list
        if (config.auth.pages.indexOf(currentPage) !== -1) {
            return true;
        }

        // default: false
        return false;
    }

    // Set user info from "cookies" or from an object
    function setUserInfoFrom(input) {

        $(self.dom).find('[data-key]').each(function() {

            var key = $(this).attr('data-key');
            var value = input[key];

            if (key === "*") {
                value = input;
            }

            if (typeof value === "object") {
                value = JSON.stringify(value, null, 4);
            }

            $(this).html(value);
        });

    return;

        for (var key in config.cookies) {
            // TODO Dinamic HTML attributes?

            // Get attributes
            var attributesValues = [];

            var userInfoAttr = config.htmlAttributes.cookies.userInfo;

            if (attributesValues.indexOf(key) !== -1) {

                var userInfoValue;

                // TODO Get cookies as object
                if (input === "cookies") {
                    userInfoValue = $.cookie(key);
                }
                else
                if (typeof input === "object") {
                    userInfoValue =  input[config.cookies[key]];
                }

                if (userInfoValue) {
                    loggedPrev = true;
                    $("*[" + userInfoAttr + "=" + key + "]", self.dom).text(userInfoValue);
                }
            }
        }

        // Show the logout class if the user was
        // looged in previously (this means that there
        // is at least a cookie that is saved)
        if (loggedPrev) {
            $(config.classes.login).hide();
            $(config.classes.logout).show();
        }
        else {
            $(config.classes.logout).hide();
            $(config.classes.login).show();
        }
    }
}
<commit_msg>Comments, syntax, removed obsolete code<commit_after>// bind and events dependencies
var Bind = require("github/jillix/bind")
  , Events = require("github/jillix/events")
  ;

/**
 *  This function parses the query from url
 *
 */
function parseQuery() {
    var query = window.location.search.substring(1);
    var vars = query.split('&');
    var result = {};
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split('=');
        result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
    }
    return result;
}

/**
 *  Login Providers
 *  Mono login modules using external providers like Github, Bitbucket, etc
 *
 */
module.exports = function (conf) {

    // get self, config and query
    var self = this
      , config = processConfig (conf)
      , query = parseQuery ()
      ;

    // call events
    Events.call(self, config);

    // emit ready
    self.emit("ready", config);

    // this is the provider callback
    if (query.provider) {
        handleProviderCallback (query);
        return;
    }

    // show early the user info if this is available in the cookies
    //setUserInfoFrom("cookies");

    // get the userinfo
    self.link("userInfo", function(err, data) {

        // handle error
        if (err) {
            alert(err);
            return;
        }

        /* =====================
         * The user is logged in
         * ===================== */
        if (data) {

            // let others know the user is logged in
            self.emit('userInfo', data);

            // and also we have to adjust the cookies/UI
            setUserInfoFrom(data);

            // show the logged in mode
            $(config.classes.login, self.dom).hide();
            $(config.classes.logout, self.dom).show();

            // add the logout handler
            $(self.dom).on("click", config.classes.logoutLink, function() {

                // call logout operation
                logout(function() {
                    window.location = config.successPage;
                });
                return false;
            });

            return;
        }

        /* =========================
         * The user is NOT logged in
         * ========================= */

        // delete the cookies (if any)
        for (var cookie in config.cookies) {
            $.removeCookie(cookie);
        }

        // login handlers
        $(self.dom).on('click', config.classes.loginButton, function() {

            // change the cursor
            $("body").css("cursor", "wait");

            // send the provider
            self.link('redirect', { data: { provider: $(this).attr('data-provider') } }, function(err, data) {

                // handle error
                if (err) {
                    console.error (err);
                    return;
                }

                // success, redirect
                window.location = data;
            });
            return false;
        });

        // show the correct UI parts
        $(config.classes.logout).hide();
        $(config.classes.login).show();

        // The page requires auth => redirects the
        // user automatically on the login link
        // TODO A better way?
        function verify() {
            var redirect = config.auth.login.redirect
            if (requiresAuth() && redirect) {
                if (typeof redirect === "object") {
                    self.emit("redirect", redirect.page);
                    return;
                }
                window.location = redirect;
            }
        }

        verify();

        $(window).on("hashchange", function() {
            verify();
        });
    });

    /**
     * This will be called when the login provider calls back on us.
     *
     */
    function handleProviderCallback(query) {

        // hide login
        $(config.classes.login, self.dom).hide();

        // get cookies
        query.cookies = config.cookies;

        // call login operation
        self.link("login", { data: query }, function(err, data) {

            // handle error
            if (err) {
                console.error(err);
                return;
            }

            // redirect
            window.location = config.successPage;
        });
    }

    /**
     *  Logout operation
     *
     */
    function logout(callback) {

        // remove cookies
        for (var cookie in config.cookies) {
            $.removeCookie(cookie);
        }

        // call logout operation
        self.link("logout", callback);

        // emit logged out
        self.emit("loggedOut");
    }

    // Sets defaults, and correct empty objects
    function processConfig(config) {

        // General
        config.successPage = config.successPage || "/";
        config.logoutLink = config.logoutLink || "/logout";

        // Html Attributes
        // TODO Dinamic HTML Attributes, like for cookies?
        config.htmlAttributes = config.htmlAttributes || {};
        config.htmlAttributes.cookies = config.htmlAttributes.cookies || {};
        config.htmlAttributes.cookies.userInfo = config.htmlAttributes.cookies.userInfo || "data-user-cookie-info";

        // Classes
        config.classes = config.classes || {};
        config.classes.loginButton = config.classes.loginButton || ".login-button";
        config.classes.login = config.classes.login || ".login";
        config.classes.logout = config.classes.logout || ".logout";
        config.classes.logoutLink = config.classes.logoutLink || ".logout-btn";
        config.classes.userName = config.classes.userName || ".userName";

        // Auth
        config.auth = config.auth || {};
        config.auth.login = config.auth.login || {};
        config.auth.logout = config.auth.logout || {};
        config.auth.pages = config.auth.pages || [];

        return config;
    }

    /**
     *  Gets callback code
     *
     */
    function getCode(url, str) {
        return url.substring(url.indexOf(str) + str.length);
    }

    /**
     * Verify if the user is on a page that require authentification
     *
     */
    function requiresAuth() {

        var location = window.location;
        var currentPage = location.pathname + location.hash + location.search;

        // Return true if the page is on the list
        if (config.auth.pages.indexOf(currentPage) !== -1) {
            return true;
        }

        // default: false
        return false;
    }

    /**
     * Set user info from "cookies" or from an object
     *
     */
    function setUserInfoFrom(input) {

        // each data-key element
        $(self.dom).find('[data-key]').each(function() {

            // get its key and value
            var key = $(this).attr('data-key')
              , value = input[key]
              ;

            // "*" is special
            if (key === "*") {
                value = input;
            }

            // value is an object that must be stringified
            if (typeof value === "object") {
                value = JSON.stringify(value, null, 4);
            }

            // set the html for this element
            $(this).html(value);
        });
    }
}
<|endoftext|>


<commit_before>//Display error
function mayError(errObj){
    if (errObj.error){
        console.log(errObj)
        $("#error strong").text(errObj.error);
        $("#error em").text(errObj.details);
        $("#error").show()
        return 1;
    } else {
        return 0;
    }
}

//Blanks the cards
function newCard(){
    var blankCard = LZString.compressToBase64(JSON.stringify({
        classes: "card pony maleFemale unicorn s0",
        name: "",
        attrs: "",
        effect: "",
        flavour: "",
        copyright: "",
    }))
    updateFields({card:blankCard})
}

//Loads a card
function load(string){
    var data = JSON.parse(LZString.decompressFromBase64(string))
    $(".card").attr("class",data.classes)
    $(".card .nameInput").val(data.name)
    $(".card .attrs").val(data.attrs)
    $(".card .effect").val(data.effect)
    $(".card .flavour").val(data.flavour)
    $(".card .copyright").val(data.copyright)
}

//Saves a card
function save(){
    var newString = LZString.compressToBase64(JSON.stringify({
        classes: $(".card").attr("class"),
        name: $(".card .nameInput").val(),
        attrs: $(".card .attrs").val(),
        effect: $(".card .effect").val(),
        flavour: $(".card .flavour").val(),
        copyright: $(".card .copyright").val(),
    }))
    updateFields({card:newString})
}

function cardSetup(){
    //On card button clicks, remove other classes and add new ones.
    //Unless it is changeling, special case, just toggle.
    $(".card button").click(function(){
        if ($(this).attr("value") == "changeling"){
            $(".card").toggleClass($(this).attr("value"));
        } else {
            $(this).parent().children("button").each(function(){
                if ($(this).attr("value") != "changeling"){
                    $(".card").removeClass($(this).attr("value"));
                }
            })
            $(".card").addClass($(this).attr("value"));
        }
    })

    //On Window resize we use css transformation to scale the card to fix
    //Yes it seems horrible but the alternative was somehting even more horrible!
    $(window).resize(function(){
        var f = ($(".cardwrapper").width())/788;
        $(".card").css("transform","scale("+f+")")
        $(".cardwrapper").height(1088*f);
    });

    //Constant infomation for special escape code handling.
    var SPECIAL_REGEX = /\\(malefemale|unicorn|pegasus|earth|alicorn|goal|time|female|male|ship|replace|swap|draw|newgoal|search|copy|changeling)/g
    var SPECIAL_REPLACE = {
        "\\male":"\u2642",
        "\\female":"\u2640",
        "\\malefemale":"\u26A4",
        "\\ship":"\u2764",
        "\\earth":"\uE000",
        "\\unicorn":"\uE001",
        "\\pegasus":"\uE002",
        "\\alicorn":"\uE003",
        "\\time":"\uE004",
        "\\replace":"(Replace): While in your hand, you may discard a Pony card from the grid and play this card in its place. This power cannot be copied.",
        "\\swap":"(Swap): You may swap 2 Pony cards on the shipping grid.",
        "\\draw":"(Draw): You may draw a card from the Ship or Pony deck.",
        "\\newgoal":"(New Goal): You may discard a Goal and draw a new one to replace it.",
        "\\search":"(Search): You may search the Ship or Pony discard pile for a card of your choice and play it.",
        "\\copy":"(Copy): You may copy the power of any Pony card currently on the shipping grid, except for Changelings.",
        "\\changeling":"Gains the name, keywords and symbols of any single [race] of your choice until the end of the turn. If this card is moved to a new place on the grid, the current player must select a new disguise that will last until the end of their turn, even if other cards say its power would not activate."
    }

    //Replace special escape codes when an input is updated
    $(".card input[type=text], .card textarea").on("change",function(){
        var txt = $(this).val();
        txt = txt.replace(SPECIAL_REGEX,function(t){
            return SPECIAL_REPLACE[t];
        });
        $(this).val(txt)
    })

    //Replace and create tooltip hints
    $.each(SPECIAL_REPLACE,function(key,replace){
        $("dt[data-original-title='\\"+key+"']").attr("data-original-title",replace).tooltip();
    })

    //When a text editor is updated resize it's helper to clone back the height.
    //This is because CSS Really hates working vertically
    $(".card textarea").on("change keyup paste",function(){
        var t = $(this),
            o = $(".cardHelper ." + t.attr("class"));
        o.text(t.val());
        t.height(o.height());
    });

    //We also use a simular system for the name, but since we dont need manual
    //line breaks it gets easiers
    $(".card .nameInput").on("change keyup paste",function(){
        var t = $(this),
            o = $(".card .name");
        o.toggleClass("small",t[0].scrollWidth > t.width()+1)
        o.text(t.val());
    });

    //Update image
    $("#image").change(function(){
        $(".card .image").css("background-image","url('"+$(this).val()+"')")
    })

    //Save, New & Export buttons
    $("#save").click(save)
    $("#new").click(newCard)

    //Inital call setup functions
    $(window).resize();
    $(".card textarea").change();

    //Check the hash to see if we are loading something
    if(document.location.hash){
        hs = document.location.hash.substr(1).split(":")
        if (hs.length==2 && (hs[0] == "view" || hs[0] == "edit")){
            load(hs[0],hs[1]);
        } else {
            document.location.hash = ""
        }
    }
};
<commit_msg>added hook to state:adjust<commit_after>//Display error
function mayError(errObj){
    if (errObj.error){
        console.log(errObj)
        $("#error strong").text(errObj.error);
        $("#error em").text(errObj.details);
        $("#error").show()
        return 1;
    } else {
        return 0;
    }
}

//Blanks the cards
function newCard(){
    var blankCard = LZString.compressToBase64(JSON.stringify({
        classes: "card pony maleFemale unicorn s0",
        name: "",
        attrs: "",
        effect: "",
        flavour: "",
        copyright: "",
    }))
    updateFields({card:blankCard})
}

//Loads a card
function load(string){
    var data = JSON.parse(LZString.decompressFromBase64(string))
    $(".card").attr("class",data.classes)
    $(".card .nameInput").val(data.name)
    $(".card .attrs").val(data.attrs)
    $(".card .effect").val(data.effect)
    $(".card .flavour").val(data.flavour)
    $(".card .copyright").val(data.copyright)
}
$(document).on("state:adjust",function(event,get,oldGet){
    if (get.card != oldGet.card){
        load(get.card)
    }
})

//Saves a card
function save(){
    var newString = LZString.compressToBase64(JSON.stringify({
        classes: $(".card").attr("class"),
        name: $(".card .nameInput").val(),
        attrs: $(".card .attrs").val(),
        effect: $(".card .effect").val(),
        flavour: $(".card .flavour").val(),
        copyright: $(".card .copyright").val(),
    }))
    updateFields({card:newString},true)
}

function cardSetup(){
    //On card button clicks, remove other classes and add new ones.
    //Unless it is changeling, special case, just toggle.
    $(".card button").click(function(){
        if ($(this).attr("value") == "changeling"){
            $(".card").toggleClass($(this).attr("value"));
        } else {
            $(this).parent().children("button").each(function(){
                if ($(this).attr("value") != "changeling"){
                    $(".card").removeClass($(this).attr("value"));
                }
            })
            $(".card").addClass($(this).attr("value"));
        }
    })

    //On Window resize we use css transformation to scale the card to fix
    //Yes it seems horrible but the alternative was somehting even more horrible!
    $(window).resize(function(){
        var f = ($(".cardwrapper").width())/788;
        $(".card").css("transform","scale("+f+")")
        $(".cardwrapper").height(1088*f);
    });

    //Constant infomation for special escape code handling.
    var SPECIAL_REGEX = /\\(malefemale|unicorn|pegasus|earth|alicorn|goal|time|female|male|ship|replace|swap|draw|newgoal|search|copy|changeling)/g
    var SPECIAL_REPLACE = {
        "\\male":"\u2642",
        "\\female":"\u2640",
        "\\malefemale":"\u26A4",
        "\\ship":"\u2764",
        "\\earth":"\uE000",
        "\\unicorn":"\uE001",
        "\\pegasus":"\uE002",
        "\\alicorn":"\uE003",
        "\\time":"\uE004",
        "\\replace":"(Replace): While in your hand, you may discard a Pony card from the grid and play this card in its place. This power cannot be copied.",
        "\\swap":"(Swap): You may swap 2 Pony cards on the shipping grid.",
        "\\draw":"(Draw): You may draw a card from the Ship or Pony deck.",
        "\\newgoal":"(New Goal): You may discard a Goal and draw a new one to replace it.",
        "\\search":"(Search): You may search the Ship or Pony discard pile for a card of your choice and play it.",
        "\\copy":"(Copy): You may copy the power of any Pony card currently on the shipping grid, except for Changelings.",
        "\\changeling":"Gains the name, keywords and symbols of any single [race] of your choice until the end of the turn. If this card is moved to a new place on the grid, the current player must select a new disguise that will last until the end of their turn, even if other cards say its power would not activate."
    }

    //Replace special escape codes when an input is updated
    $(".card input[type=text], .card textarea").on("change",function(){
        var txt = $(this).val();
        txt = txt.replace(SPECIAL_REGEX,function(t){
            return SPECIAL_REPLACE[t];
        });
        $(this).val(txt)
    })

    //Replace and create tooltip hints
    $.each(SPECIAL_REPLACE,function(key,replace){
        $("dt[data-original-title='\\"+key+"']").attr("data-original-title",replace).tooltip();
    })

    //When a text editor is updated resize it's helper to clone back the height.
    //This is because CSS Really hates working vertically
    $(".card textarea").on("change keyup paste",function(){
        var t = $(this),
            o = $(".cardHelper ." + t.attr("class"));
        o.text(t.val());
        t.height(o.height());
    });

    //We also use a simular system for the name, but since we dont need manual
    //line breaks it gets easiers
    $(".card .nameInput").on("change keyup paste",function(){
        var t = $(this),
            o = $(".card .name");
        o.toggleClass("small",t[0].scrollWidth > t.width()+1)
        o.text(t.val());
    });

    //Update image
    $("#image").change(function(){
        $(".card .image").css("background-image","url('"+$(this).val()+"')")
    })

    //Save, New & Export buttons
    $("#save").click(save)
    $("#new").click(newCard)

    //Inital call setup functions
    $(window).resize();
    $(".card textarea").change();

    //Check the hash to see if we are loading something
    if(document.location.hash){
        hs = document.location.hash.substr(1).split(":")
        if (hs.length==2 && (hs[0] == "view" || hs[0] == "edit")){
            load(hs[0],hs[1]);
        } else {
            document.location.hash = ""
        }
    }
};
<|endoftext|>