<commit_before>module.exports = {
  extends: 'standard',
  parser: 'babel-eslint',
  plugins: [
    'react',
    'objects'
  ],
  parserOptions: {
    ecmaVersion: 6,
    ecmaFeatures: {
      jsx: true
    },
    sourceType: 'module'
  },
  env: {
    browser: true,
    node: true,
    es6: true,
    mocha: true
  },
  globals: {
    React: true,
    ReactDOM: true
  },
  rules: {
    'no-case-declarations': 0,
    'no-unused-labels': 2,
    'no-self-assign': 2,
    'no-new-symbol': 2,
    'arrow-spacing': [2, {before: true, after: true}],
    'block-scoped-var': 2,
    'brace-style': [2, '1tbs', {allowSingleLine: true}],
    'camelcase': [1, {properties: 'always'}],
    'comma-dangle': [2, 'never'],
    'comma-spacing': [2, {before: false, after: true}],
    'comma-style': [2, 'last'],
    'consistent-return': 2,
    'curly': [2, 'multi-line'],
    'default-case': 2,
    'dot-notation': [2, {allowKeywords: true}],
    'eol-last': 2,
    'eqeqeq': 2,
    'func-names': 1,
    'guard-for-in': 2,
    'indent': [2, 2, { SwitchCase: 1 }],
    'init-declarations': [2, 'always'],
    'jsx-quotes': [2, 'prefer-double'],
    'key-spacing': [2],
    'max-len': [1, 80, 4],
    'new-cap': [2, {newIsCap: true}],
    'no-alert': 1,
    'no-caller': 2,
    'no-class-assign': 2,
    'no-cond-assign': [2, 'always'],
    'no-console': 1,
    'no-const-assign': 2,
    'no-constant-condition': 1,
    'no-debugger': 1,
    'no-dupe-keys': 2,
    'no-duplicate-case': 2,
    'no-else-return': 2,
    'no-empty': 2,
    'no-eq-null': 2,
    'no-eval': 2,
    'no-ex-assign': 2,
    'no-extend-native': 2,
    'no-extra-bind': 2,
    'no-extra-boolean-cast': 0,
    'no-extra-parens': 0,
    'no-extra-semi': 2,
    'no-fallthrough': 2,
    'no-floating-decimal': 2,
    'no-func-assign': 2,
    'no-implicit-coercion': 2,
    'no-implied-eval': 2,
    'no-inner-declarations': 2,
    'no-invalid-regexp': 2,
    'no-irregular-whitespace': 2,
    'no-lone-blocks': 2,
    'no-loop-func': 2,
    'no-multi-str': 2,
    'no-multiple-empty-lines': [2, {max: 2}],
    'no-native-reassign': 2,
    'no-nested-ternary': 2,
    'no-new-func': 2,
    'no-new-object': 2,
    'no-new-wrappers': 2,
    'no-new': 2,
    'no-obj-calls': 2,
    'no-octal-escape': 2,
    'no-octal': 2,
    'no-param-reassign': 2,
    'no-proto': 2,
    'no-redeclare': 2,
    'no-return-assign': 2,
    'no-script-url': 2,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-shadow-restricted-names': 2,
    'no-shadow': 2,
    'no-spaced-func': 2,
    'no-sparse-arrays': 2,
    'no-throw-literal': 2,
    'no-trailing-spaces': 2,
    'no-underscore-dangle': 0,
    'no-unreachable': 2,
    'no-unused-vars': [2, {vars: 'local', args: 'after-used'}],
    'no-use-before-define': 2,
    'no-var': 2,
    'no-with': 2,
    'one-var': [2, 'never'],
    'padded-blocks': [2, 'never'],
    'prefer-reflect': [2],
    'prefer-spread': 2,
    'quote-props': [2, 'consistent-as-needed'],
    'quotes': [2, 'single', 'avoid-escape'],
    'radix': 2,
    'semi-spacing': [2, {before: false, after: true}],
    'semi': [2, 'always'],
    'keyword-spacing': 2,
    'space-before-blocks': 2,
    'space-before-function-paren': [2, 'never'],
    'space-infix-ops': 2,
    'spaced-comment': 2,
    'strict': [2, 'never'],
    'use-isnan': 2,
    'vars-on-top': 2,
    'wrap-iife': [2, 'any'],
    'yoda': 2,
    'react/display-name': 0,
    'react/forbid-prop-types': 0,
    'react/jsx-boolean-value': 2,
    'react/jsx-closing-bracket-location': 2,
    'react/jsx-curly-spacing': [2, 'never'],
    'react/jsx-handler-names': 0,
    'react/jsx-indent-props': [2, 2],
    'react/jsx-indent': [2, 2],
    'react/jsx-key': 2,
    'react/jsx-max-props-per-line': 0,
    'react/jsx-no-bind': 2,
    'react/jsx-no-duplicate-props': 2,
    'react/jsx-no-literals': 2,
    'react/jsx-no-undef': 2,
    'react/jsx-pascal-case': 2,
    'react/jsx-sort-prop-types': 0,
    'react/jsx-sort-props': 0,
    'react/jsx-uses-react': 2,
    'react/jsx-uses-vars': 2,
    'react/no-danger': 0,
    'react/no-deprecated': 2,
    'react/no-did-mount-set-state': 2,
    'react/no-did-update-set-state': 2,
    'react/no-direct-mutation-state': 2,
    'react/no-is-mounted': 2,
    'react/no-multi-comp': 2,
    'react/no-set-state': 0,
    'react/no-string-refs': 2,
    'react/no-unknown-property': 2,
    'react/prefer-es6-class': 2,
    'react/prop-types': 2,
    'react/react-in-jsx-scope': 2,
    'react/require-extension': 0,
    'react/self-closing-comp': 2,
    'react/jsx-wrap-multilines': 2,
    'react/sort-comp': [2, {
      order: [
        'constructor',
        'lifecycle',
        'everything-else',
        'render'
      ],
      groups: {
        lifecycle: [
          'displayName',
          'propTypes',
          'defaultProps',
          'contextTypes',
          'state',
          'childContextTypes',
          'mixins',
          'statics',
          'getDefaultProps',
          'getInitialState',
          'getChildContext',
          'componentWillMount',
          'componentDidMount',
          'componentWillReceiveProps',
          'shouldComponentUpdate',
          'componentWillUpdate',
          'componentDidUpdate',
          'componentWillUnmount'
        ]
      }
    }]
  }
}
<commit_msg>Removed react/jsx-no-bind rule<commit_after>module.exports = {
  extends: 'standard',
  parser: 'babel-eslint',
  plugins: [
    'react',
    'objects'
  ],
  parserOptions: {
    ecmaVersion: 6,
    ecmaFeatures: {
      jsx: true
    },
    sourceType: 'module'
  },
  env: {
    browser: true,
    node: true,
    es6: true,
    mocha: true
  },
  globals: {
    React: true,
    ReactDOM: true
  },
  rules: {
    'no-case-declarations': 0,
    'no-unused-labels': 2,
    'no-self-assign': 2,
    'no-new-symbol': 2,
    'arrow-spacing': [2, {before: true, after: true}],
    'block-scoped-var': 2,
    'brace-style': [2, '1tbs', {allowSingleLine: true}],
    'camelcase': [1, {properties: 'always'}],
    'comma-dangle': [2, 'never'],
    'comma-spacing': [2, {before: false, after: true}],
    'comma-style': [2, 'last'],
    'consistent-return': 2,
    'curly': [2, 'multi-line'],
    'default-case': 2,
    'dot-notation': [2, {allowKeywords: true}],
    'eol-last': 2,
    'eqeqeq': 2,
    'func-names': 1,
    'guard-for-in': 2,
    'indent': [2, 2, { SwitchCase: 1 }],
    'init-declarations': [2, 'always'],
    'jsx-quotes': [2, 'prefer-double'],
    'key-spacing': [2],
    'max-len': [1, 80, 4],
    'new-cap': [2, {newIsCap: true}],
    'no-alert': 1,
    'no-caller': 2,
    'no-class-assign': 2,
    'no-cond-assign': [2, 'always'],
    'no-console': 1,
    'no-const-assign': 2,
    'no-constant-condition': 1,
    'no-debugger': 1,
    'no-dupe-keys': 2,
    'no-duplicate-case': 2,
    'no-else-return': 2,
    'no-empty': 2,
    'no-eq-null': 2,
    'no-eval': 2,
    'no-ex-assign': 2,
    'no-extend-native': 2,
    'no-extra-bind': 2,
    'no-extra-boolean-cast': 0,
    'no-extra-parens': 0,
    'no-extra-semi': 2,
    'no-fallthrough': 2,
    'no-floating-decimal': 2,
    'no-func-assign': 2,
    'no-implicit-coercion': 2,
    'no-implied-eval': 2,
    'no-inner-declarations': 2,
    'no-invalid-regexp': 2,
    'no-irregular-whitespace': 2,
    'no-lone-blocks': 2,
    'no-loop-func': 2,
    'no-multi-str': 2,
    'no-multiple-empty-lines': [2, {max: 2}],
    'no-native-reassign': 2,
    'no-nested-ternary': 2,
    'no-new-func': 2,
    'no-new-object': 2,
    'no-new-wrappers': 2,
    'no-new': 2,
    'no-obj-calls': 2,
    'no-octal-escape': 2,
    'no-octal': 2,
    'no-param-reassign': 2,
    'no-proto': 2,
    'no-redeclare': 2,
    'no-return-assign': 2,
    'no-script-url': 2,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-shadow-restricted-names': 2,
    'no-shadow': 2,
    'no-spaced-func': 2,
    'no-sparse-arrays': 2,
    'no-throw-literal': 2,
    'no-trailing-spaces': 2,
    'no-underscore-dangle': 0,
    'no-unreachable': 2,
    'no-unused-vars': [2, {vars: 'local', args: 'after-used'}],
    'no-use-before-define': 2,
    'no-var': 2,
    'no-with': 2,
    'one-var': [2, 'never'],
    'padded-blocks': [2, 'never'],
    'prefer-reflect': [2],
    'prefer-spread': 2,
    'quote-props': [2, 'consistent-as-needed'],
    'quotes': [2, 'single', 'avoid-escape'],
    'radix': 2,
    'semi-spacing': [2, {before: false, after: true}],
    'semi': [2, 'always'],
    'keyword-spacing': 2,
    'space-before-blocks': 2,
    'space-before-function-paren': [2, 'never'],
    'space-infix-ops': 2,
    'spaced-comment': 2,
    'strict': [2, 'never'],
    'use-isnan': 2,
    'vars-on-top': 2,
    'wrap-iife': [2, 'any'],
    'yoda': 2,
    'react/display-name': 0,
    'react/forbid-prop-types': 0,
    'react/jsx-boolean-value': 2,
    'react/jsx-closing-bracket-location': 2,
    'react/jsx-curly-spacing': [2, 'never'],
    'react/jsx-handler-names': 0,
    'react/jsx-indent-props': [2, 2],
    'react/jsx-indent': [2, 2],
    'react/jsx-key': 2,
    'react/jsx-max-props-per-line': 0,
    'react/jsx-no-bind': 0,
    'react/jsx-no-duplicate-props': 2,
    'react/jsx-no-literals': 2,
    'react/jsx-no-undef': 2,
    'react/jsx-pascal-case': 2,
    'react/jsx-sort-prop-types': 0,
    'react/jsx-sort-props': 0,
    'react/jsx-uses-react': 2,
    'react/jsx-uses-vars': 2,
    'react/no-danger': 0,
    'react/no-deprecated': 2,
    'react/no-did-mount-set-state': 2,
    'react/no-did-update-set-state': 2,
    'react/no-direct-mutation-state': 2,
    'react/no-is-mounted': 2,
    'react/no-multi-comp': 2,
    'react/no-set-state': 0,
    'react/no-string-refs': 2,
    'react/no-unknown-property': 2,
    'react/prefer-es6-class': 2,
    'react/prop-types': 2,
    'react/react-in-jsx-scope': 2,
    'react/require-extension': 0,
    'react/self-closing-comp': 2,
    'react/jsx-wrap-multilines': 2,
    'react/sort-comp': [2, {
      order: [
        'constructor',
        'lifecycle',
        'everything-else',
        'render'
      ],
      groups: {
        lifecycle: [
          'displayName',
          'propTypes',
          'defaultProps',
          'contextTypes',
          'state',
          'childContextTypes',
          'mixins',
          'statics',
          'getDefaultProps',
          'getInitialState',
          'getChildContext',
          'componentWillMount',
          'componentDidMount',
          'componentWillReceiveProps',
          'shouldComponentUpdate',
          'componentWillUpdate',
          'componentDidUpdate',
          'componentWillUnmount'
        ]
      }
    }]
  }
}
<|endoftext|>


<commit_before>//TODO wire protocol parser & handler

//define westfield namespace as wf
const wf = wf || {};

/**
 *
 * @param {Number} number
 * @constructor
 */
wf.Fixed = function (number) {
    //TODO
};

wf._Object = function (connection) {

    //--public functions--
    /**
     *
     */
    this.delete = function () {
        this._connection._objects.remove(this._id);
        this._connection._marshall(this._id, 0, []);//opcode 0 is reserved for deletion
    };

    /**
     *
     * @param {Number} errorCode an integer error code
     * @param {String} errorMsg the error message
     */
    this.postError = function (errorCode, errorMsg) {
        this._connection._marshall(this._id, 255, [//opcode 255 is reserved for error
            {
                value: errorCode,
                type: "i",
                size: 4,
                optional: false,
                marshallTo: function (dataView, offset) {
                    dataView.setInt32(offset, this.value, this._connection._littleEndian);
                }
            },
            {
                value: errorMsg,
                type: "s",
                size: 4 + errorMsg.length,
                optional: false,
                marshallTo: function (dataView, offset) {
                    dataView.setInt32(offset, 4, this._connection._littleEndian);
                    let writeOffset = offset + 4;
                    this.value.forEach(new function (char) {
                        dataView.setUint8(writeOffset, char);
                        writeOffset += 1;
                    });
                }
            }
        ]);
    };

    //--constructor--
    this._connection = connection;
};

wf.Connection = function (socketUrl) {

    //--properties--
    /**
     *
     * @type {Map}
     * @private
     */
    this._objects = new Map();
    /**
     *
     * @type {boolean}
     * @private
     */
    this._littleEndian = true;

    //--functions--
    /**
     *
     * @param {DataView} wireMsg
     * @returns {*}
     * @private
     */
    this._unmarshallArg = function (wireMsg) {
        const typeAscii = wireMsg.getUint8(wireMsg.offset);
        wireMsg.offset += 1;

        switch (String.fromCharCode(typeAscii)) {
            case "i"://{Number}
                const arg = wireMsg.getInt32(wireMsg.offset, this._littleEndian);
                wireMsg.offset += 4;
                return arg;
            case "f"://{wf.Fixed}
                const arg = wireMsg.getInt32(wireMsg.offset, this._littleEndian);
                wireMsg.offset += 4;
                return new wf.Fixed(arg);
            case "o"://existing object, subtype of {wf._Object}
                const id = wireMsg.getUint16(wireMsg.offset, this._littleEndian);
                wireMsg.offset += 2;
                return this._objects.get(id);
            case "n":///new object, subtype of {wf._Object}
                const id = wireMsg.getUint16(wireMsg.offset, this._littleEndian);
                wireMsg.offset += 2;
                const typeNameSize = wireMsg.getUint8(wireMsg.offset);
                wireMsg.offset += 1;
                const byteArray = new Uint8Array(wireMsg.buffer, wireMsg.offset, typeNameSize);
                wireMsg.offset += typeNameSize;

                const type = String.fromCharCode.apply(null, byteArray);
                const object = new wf[type](this);
                object._id = id;
                this._objects.set(object._id, object);

                return object;
            case "s"://{String}
                const stringSize = wireMsg.getInt32(wireMsg.offset, this._littleEndian);
                wireMsg.offset += 4;
                const byteArray = new Uint8Array(wireMsg.buffer, wireMsg.offset, stringSize);
                wireMsg.offset += stringSize;

                return String.fromCharCode.apply(null, byteArray);
            case "a"://{ArrayBuffer}
                //TODO
                break;
        }
    };

    /**
     *
     * @param {ArrayBuffer} message
     * @private
     */
    this._unmarshall = function (message) {
        //example wire message
        //[00 03] [01] [6e 00 07 03 66 6f 6f] [69 00 00 04 00] [61 00 00 00 03 ef fa 7e]
        //translates to:
        //3 (=id),1 (=opcode),n (=new object id) 7 (id value) 3 (object name lenght) foo (object type), i (integer) 1024 (integer value), a (array) 3 (array size) [0xef 0xfa 0x7e] (array value)

        //TODO convert blob to message;
        //TODO interpret message => find object & invoke it's function
        const wireMsg = new DataView(message);
        wireMsg.offset = 0;

        const id = wireMsg.getUint16(wireMsg.offset, this._littleEndian);
        wireMsg.offset += 2;

        const opcode = wireMsg.getUint8(wireMsg.offset);
        wireMsg.offset += 1;

        const args = [];
        let argi = 0;

        while (wireMsg.offset < message.byteLength) {
            args[argi] = this._unmarshallArg(wireMsg);
            argi++;
        }

        const obj = this._objects.get(id);
        return {
            obj: obj,
            opcode: opcode,
            args: args
        };
    };

    this._onSocketOpen = function (event) {
        //TODO send back-end minimal required browser info (we start with screen size)
        //TODO the first request shall be a json informing the host of our properties.
    };

    this._onSocketClose = function (event) {

    };

    this._onSocketError = function (event) {

    };

    this._onSocketMessage = function (event) {
        //TODO as a first response we expect the hosts' wire message endianess
        //TODO the first response shall be a json informing us of the host's properties.
        //all subsequent message will be in the binary wire format.

        const message = this._unmarshall(event);
        message.obj[message.opcode](message.args);
    };

    this._setupSocket = function (socket) {
        socket.onopen = this._onSocketOpen;
        socket.onclose = this._onSocketClose;
        socket.onerror = this._onSocketError;
        socket.onmessage = this._onSocketMessage;

        return socket;
    };

    /**
     * Marshall a js function call into a binary message and send it to the remote host.
     *
     * @param {Number} id the object._id
     * @param {Number} opcode the wire protocol opcode of the function
     * @param {Array} argsArray An array of private argument literals
     * @private
     */
    this._marshall = function (id, opcode, argsArray) {
        //determine required wire message length
        let size = 2 + 1;  //id+opcode
        argsArray.forEach(function (arg) {
            if (arg.optional) {
                size += 1; //add one for the optional (=?) ascii char
            }
            size += 1; //add one for the ascii char
            size += arg.size; //add size of the actual argument values
        });

        const wireMsgBuffer = new ArrayBuffer(size);
        const wireMsgView = new DataView(wireMsgBuffer);
        let offset = 0;

        //write actual wire message
        wireMsgView.setUint16(offset, id, this._littleEndian);//id
        offset += 2;
        wireMsgView.setUint8(offset, opcode);//opcode
        offset += 1;

        argsArray.forEach(function (arg) {
            if (arg.optional) {
                wireMsgView.setUint8(offset, "?".codePointAt(0)); //optional ascii char
                offset += 1;
            }
            wireMsgView.setUint8(offset, arg.type.codePointAt(0)); //argument type ascii char
            offset += 1;
            arg.marshallTo(wireMsgView, offset); //write actual argument value to buffer
            offset += arg.size;
        });

        socket.send(wireMsgBuffer);
    };

    /**
     * Close the connection to the remote host. All objects will be deleted before the connection is closed.
     */
    this.close = function () {
        this._objects.values().slice().forEach(function (object) {
            object.delete();
        });
        socket.close();
    };

    /**
     * Register an object and give it a new id.
     * @param {Object} object
     * @private
     */
    this._registerObject = function (object) {
        //find unused id.
        let id = 0x10000; //host constructed object ids will be below this range.
        while (this._objects.has(id)) {
            id++;
        }
        object._id = id;
        this._objects.set(object._id, object);
    };

    //--constructor--
    const socket = this._setupSocket(new WebSocket(socketUrl, "westfield"));
    //registry will be defined by the protocol generator
    this.registry = new wf.Registry();
    this._registerObject(this.registry);
};
<commit_msg>experiment with unmarshall function<commit_after>//TODO wire protocol parser & handler

//define westfield namespace as wf
const wf = wf || {};

//-- js argument to wire format literals: integer (number), float (number), object (wf._Object), new object (wf._Object), string (string), array (ArrayBuffer) --
/**
 *
 * @param {Number} arg
 * @returns {{value: number, type: string, size: number, optional: boolean, marshallTo: marshallTo}}
 * @private
 */
wf._int = function (arg) {
    return {
        value: arg,
        type: "i",
        size: 4,
        optional: false,
        /**
         *
         * @param {DataView} dataView
         * @param {Number} offset
         */
        marshallTo: function (dataView, offset) {
            dataView.setInt32(offset, this.value);
        }
    };
};

/**
 *
 * @param {Number} arg
 * @returns {{value: number, type: string, size: number, optional: boolean, marshallTo: marshallTo}}
 * @private
 */
wf._intOptional = function (arg) {
    return {
        value: arg,
        type: "i",
        size: 4,
        optional: true,
        /**
         *
         * @param {DataView} dataView
         * @param {Number} offset
         */
        marshallTo: function (dataView, offset) {
            if (arg == null) {
                dataView.setInt32(offset, 0);
            } else {
                dataView.setInt32(offset, this.value);
            }
        }
    }
};

/**
 *
 * @param {Number} arg
 * @returns {{value: number, type: string, size: number, optional: boolean, marshallTo: marshallTo}}
 * @private
 */
wf._float = function (arg) {
    return {
        value: arg,
        type: "f",
        size: 4,
        optional: false,
        /**
         *
         * @param {DataView} dataView
         * @param {Number} offset
         */
        marshallTo: function (dataView, offset) {
            dataView.setFloat32(offset, this.value);
        }
    };
};

/**
 *
 * @param {Number} arg
 * @returns {{value: number, type: string, size: number, optional: boolean, marshallTo: marshallTo}}
 * @private
 */
wf._floatOptional = function (arg) {
    return {
        value: arg,
        type: "f",
        size: 4,
        optional: true,
        /**
         *
         * @param {DataView} dataView
         * @param {Number} offset
         */
        marshallTo: function (dataView, offset) {
            if (arg == null) {
                dataView.setFloat32(offset, 0);
            }
            else {
                dataView.setFloat32(offset, this.value);
            }
        }
    };
};

/**
 *
 * @param {wf._Object} arg
 * @returns {{value: wf._Object, type: string, size: number, optional: boolean, marshallTo: marshallTo}}
 * @private
 */
wf._object = function (arg) {
    return {
        value: arg,
        type: "o",
        size: 2,
        optional: false,
        /**
         *
         * @param {DataView} dataView
         * @param {Number} offset
         */
        marshallTo: function (dataView, offset) {
            dataView.setUint16(offset, this.value._id);
        }
    };
};

/**
 *
 * @param {wf._Object} arg
 * @returns {{value: wf._Object, type: string, size: number, optional: boolean, marshallTo: marshallTo}}
 * @private
 */
wf._objectOptional = function (arg) {
    return {
        value: arg,
        type: "o",
        size: 2,
        optional: true,
        /**
         *
         * @param {DataView} dataView
         * @param {Number} offset
         */
        marshallTo: function (dataView, offset) {
            if (arg == null) {
                dataView.setUint16(offset, 0);
            } else {
                dataView.setUint16(offset, this.value._id);
            }
        }
    };
};

/**
 *
 * @param {wf._Object} arg
 * @returns {{value: wf._Object, type: string, size: number, optional: boolean, marshallTo: marshallTo}}
 * @private
 */
wf._newObject = function (arg) {
    return {
        value: arg,
        type: "n",
        size: 2 + 1 + (typeof arg).length,//id+length+name
        optional: false,
        /**
         *
         * @param {DataView} dataView
         * @param {Number} offset
         */
        marshallTo: function (dataView, offset) {
            dataView.setUint16(offset, this.value._id);
            let writeOffset = offset + 2;
            const objType = (typeof this.value);
            dataView.setUint8(writeOffset, objType.length);
            writeOffset += 1;
            objType.forEach(new function (char) {
                dataView.setUint8(writeOffset, char.codePointAt(0));
                writeOffset += 1;
            });
        }
    };
};

/**
 *
 * @param {wf._Object} arg
 * @returns {{value: wf._Object, type: string, size: number, optional: boolean, marshallTo: marshallTo}}
 * @private
 */
wf._newObjectOptional = function (arg) {
    return {
        value: arg,
        type: "n",
        size: 2 + (function () {
            if (arg == null) {
                return 0;
            } else {
                return 1 + (typeof arg).length;
            }
        })(),//id+length+name
        optional: true,
        /**
         *
         * @param {DataView} dataView
         * @param {Number} offset
         */
        marshallTo: function (dataView, offset) {
            if (this.value == null) {
                dataView.setUint16(offset, 0);
            } else {
                dataView.setUint16(offset, this.value._id);
                let writeOffset = offset + 2;
                const objType = (typeof this.value);
                dataView.setUint8(writeOffset, objType.length);
                writeOffset += 1;
                objType.forEach(new function (char) {
                    dataView.setUint8(writeOffset, char.codePointAt(0));
                    writeOffset += 1;
                });
            }
        }
    };
};

/**
 *
 * @param {String} arg
 * @returns {{value: String, type: string, size: number, optional: boolean, marshallTo: marshallTo}}
 * @private
 */
wf._string = function (arg) {
    return {
        value: arg,
        type: "s",
        size: 4 + arg.length,
        optional: false,
        /**
         *
         * @param {DataView} dataView
         * @param {Number} offset
         */
        marshallTo: function (dataView, offset) {
            dataView.setInt32(offset, this.value.length);
            let writeOffset = offset + 4;
            this.value.forEach(new function (char) {
                dataView.setUint8(writeOffset, char.codePointAt(0));
                writeOffset += 1;
            });
        }
    };
};

/**
 *
 * @param {String} arg
 * @returns {{value: String, type: string, size: number, optional: boolean, marshallTo: marshallTo}}
 * @private
 */
wf._stringOptional = function (arg) {
    return {
        value: arg,
        type: "s",
        size: 4 + (function () {
            if (arg == null) {
                return 0;
            } else {
                return arg.length;
            }
        })(),
        optional: false,
        /**
         *
         * @param {DataView} dataView
         * @param {Number} offset
         */
        marshallTo: function (dataView, offset) {
            if (this.value == null) {
                dataView.setInt32(offset, 0);
            } else {
                dataView.setInt32(offset, this.value.length);
                let writeOffset = offset + 4;
                this.value.forEach(new function (char) {
                    dataView.setUint8(writeOffset, char.codePointAt(0));
                    writeOffset += 1;
                });
            }
        }
    };
};

/**
 *
 * @param {TypedArray} arg
 * @returns {{value: TypedArray, type: string, size: number, optional: boolean, marshallTo: marshallTo}}
 * @private
 */
wf._array = function (arg) {
    return {
        value: arg,
        type: "a",
        size: 4 + arg.buffer.byteLength,
        optional: false,
        /**
         *
         * @param {DataView} dataView
         * @param {Number} offset
         */
        marshallTo: function (dataView, offset) {
            dataView.setInt32(offset, this.value.buffer.byteLength);
            let writeOffset = offset + 4;
            new Uint8Array(this.value.buffer, 0, this.value.buffer.byteLength).forEach(new function (byte) {
                dataView.setUint8(writeOffset, byte);
                writeOffset += 1;
            });
        }
    };
};

/**
 *
 * @param {TypedArray} arg
 * @returns {{value: ArrayBuffer, type: string, size: number, optional: boolean, marshallTo: marshallTo}}
 * @private
 */
wf._arrayOptional = function (arg) {
    return {
        value: arg,
        type: "a",
        size: 4 + (function () {
            if (arg == null) {
                return 0;
            } else {
                return arg.buffer.byteLength;
            }
        })(),
        optional: true,
        /**
         *
         * @param {DataView} dataView
         * @param {Number} offset
         */
        marshallTo: function (dataView, offset) {
            if (this.value == null) {
                dataView.setInt32(offset, 0);
            } else {
                dataView.setInt32(offset, this.value.buffer.byteLength);
                let writeOffset = offset + 4;
                new Uint8Array(this.value.buffer, 0, this.value.buffer.byteLength).forEach(new function (byte) {
                    dataView.setUint8(writeOffset, byte);
                    writeOffset += 1;
                });
            }
        }
    };
};

/**
 *
 * @param {wf.Connection} connection
 * @private
 */
wf._Object = function (connection) {

    //--functions--
    /**
     * Deletes this object from the local pool of objects and notify the remote about this object's deletion.
     */
    this.delete = function () {
        this._connection._objects.remove(this._id);
        this._connection._marshall(this._id, 0, []);//opcode 0 is reserved for deletion
        delete this._id;
    };

    /**
     * Post an error to the remote.
     *
     * @param {Number} errorCode an integer error code
     * @param {String} errorMsg the error message
     */
    this.postError = function (errorCode, errorMsg) {
        this._connection._marshall(this._id, 255, [wf._int(errorCode), wf._string(errorMsg)]);//opcode 255 is reserved for error
    };

    //--constructor--
    this._connection = connection;
};

wf.Connection = function (socketUrl) {

    //--properties--
    /**
     * Pool of objects that live on this connection.
     * Key: Number, Value: a subtype of wf._Object
     * wf._Object._id == Key
     *
     * @type {Map}
     * @private
     */
    this._objects = new Map();

    //--functions--
    /**
     *
     * @param {DataView} wireMsg
     * @returns {*}
     * @private
     */
    this._unmarshallArg = function (wireMsg) {
        let typeAscii = wireMsg.getUint8(wireMsg.offset);
        wireMsg.offset += 1;

        const optional = String.fromCharCode(typeAscii) == "?";
        if (optional) {
            typeAscii = wireMsg.getUint8(wireMsg.offset);
            wireMsg.offset += 1;
        }

        let arg = null;
        switch (String.fromCharCode(typeAscii)) {
            case "i"://integer {Number}
                arg = wireMsg.getInt32(wireMsg.offset);
                wireMsg.offset += 4;
                break;
            case "f"://float {Number}
                arg = wireMsg.getFloat32(wireMsg.offset);
                wireMsg.offset += 4;
                break;
            case "o"://existing object, subtype of {wf._Object}
                const id = wireMsg.getUint16(wireMsg.offset);
                wireMsg.offset += 2;
                if (optional && id == 0) {
                    arg = null;
                } else {
                    arg = this._objects.get(id);
                }
                break;
            case "n":///new object, subtype of {wf._Object}
                const id = wireMsg.getUint16(wireMsg.offset);
                wireMsg.offset += 2;
                if (optional && id == 0) {
                    arg = null;
                } else {
                    const typeNameSize = wireMsg.getUint8(wireMsg.offset);
                    wireMsg.offset += 1;
                    const byteArray = new Uint8Array(wireMsg.buffer, wireMsg.offset, typeNameSize);
                    wireMsg.offset += typeNameSize;

                    const type = String.fromCharCode.apply(null, byteArray);
                    const newObject = new wf[type](this);
                    newObject._id = id;
                    this._objects.set(newObject._id, newObject);
                    arg = newObject;
                }
                break;
            case "s"://{String}
                const stringSize = wireMsg.getInt32(wireMsg.offset);
                wireMsg.offset += 4;
                if (optional && stringSize == 0) {
                    arg = null;
                }
                else {
                    const byteArray = new Uint8Array(wireMsg.buffer, wireMsg.offset, stringSize);
                    wireMsg.offset += stringSize;
                    arg = String.fromCharCode.apply(null, byteArray);
                }
                break;
            case "a"://{Uint8Array}
                const arraySize = wireMsg.getInt32(wireMsg.offset);
                wireMsg.offset += 4;
                if (optional && arraySize == 0) {
                    arg = null;
                } else {
                    const byteArray = new Uint8Array(wireMsg.buffer, wireMsg.offset, arraySize);
                    wireMsg.offset += arraySize;
                    arg = byteArray;
                }
                break;
        }

        if (arg == null) {
            //TODO throw unmarshalling exception
        }

        return arg;
    };

    /**
     *
     * @param {ArrayBuffer} message
     * @private
     */
    this._unmarshall = function (message) {
        //example wire message
        //[00 03] [01] [6e 00 07 03 66 6f 6f] [69 00 00 04 00] [61 00 00 00 03 ef fa 7e]
        //translates to:
        //3 (=id),1 (=opcode),n (=new object id) 7 (id value) 3 (object name lenght) foo (object type), i (integer) 1024 (integer value), a (array) 3 (array size) [0xef 0xfa 0x7e] (array value)

        //TODO convert blob to message;
        //TODO interpret message => find object & invoke it's function
        const wireMsg = new DataView(message);
        wireMsg.offset = 0;

        const id = wireMsg.getUint16(wireMsg.offset);
        wireMsg.offset += 2;

        const opcode = wireMsg.getUint8(wireMsg.offset);
        wireMsg.offset += 1;

        const args = [];
        let argi = 0;

        while (wireMsg.offset < message.byteLength) {
            args[argi] = this._unmarshallArg(wireMsg);
            argi++;
        }

        const obj = this._objects.get(id);
        return {
            obj: obj,
            opcode: opcode,
            args: args
        };
    };

    this._onSocketOpen = function (event) {
        //TODO send back-end minimal required browser info (we start with screen size)
        //TODO the first request shall be a json informing the host of our properties.
        //all subsequent message will be in the binary wire format.
    };

    this._onSocketClose = function (event) {

    };

    this._onSocketError = function (event) {

    };

    this._onSocketMessage = function (event) {
        //TODO the first response shall be a json informing us of the host's properties.
        //all subsequent message will be in the binary wire format.
        const message = this._unmarshall(event);
        message.obj[message.opcode](message.args);
    };

    this._setupSocket = function (socket) {
        socket.onopen = this._onSocketOpen;
        socket.onclose = this._onSocketClose;
        socket.onerror = this._onSocketError;
        socket.onmessage = this._onSocketMessage;

        return socket;
    };

    /**
     * Marshall a js function call into a binary message and send it to the remote host.
     *
     * @param {Number} id the object._id
     * @param {Number} opcode the wire protocol opcode of the function
     * @param {Array} argsArray An array of private argument literals
     * @private
     */
    this._marshall = function (id, opcode, argsArray) {
        //determine required wire message length
        let size = 2 + 1;  //id+opcode
        argsArray.forEach(function (arg) {
            if (arg.optional) {
                size += 1; //add one for the optional (=?) ascii char
            }
            size += 1; //add one for the ascii char
            size += arg.size; //add size of the actual argument values
        });

        const wireMsgBuffer = new ArrayBuffer(size);
        const wireMsgView = new DataView(wireMsgBuffer);
        let offset = 0;

        //write actual wire message
        wireMsgView.setUint16(offset, id);//id
        offset += 2;
        wireMsgView.setUint8(offset, opcode);//opcode
        offset += 1;

        argsArray.forEach(function (arg) {
            if (arg.optional) {
                wireMsgView.setUint8(offset, "?".codePointAt(0)); //optional ascii char
                offset += 1;
            }
            wireMsgView.setUint8(offset, arg.type.codePointAt(0)); //argument type ascii char
            offset += 1;
            arg.marshallTo(wireMsgView, offset); //write actual argument value to buffer
            offset += arg.size;
        });

        socket.send(wireMsgBuffer);
    };

    /**
     * Close the connection to the remote host. All objects will be deleted before the connection is closed.
     */
    this.close = function () {
        this._objects.values().slice().forEach(function (object) {
            object.delete();
        });
        socket.close();
    };

    /**
     * Register an object and give it a new id.
     * @param {Object} object
     * @private
     */
    this._registerObject = function (object) {
        //find unused id.
        let id = 0x10000; //host constructed object ids will be below this range.
        while (this._objects.has(id)) {
            id++;
        }
        object._id = id;
        this._objects.set(object._id, object);
    };

    //--constructor--
    const socket = this._setupSocket(new WebSocket(socketUrl, "westfield"));
    //registry will be defined by the protocol generator
    this.registry = new wf.Registry();
    this._registerObject(this.registry);
};
<|endoftext|>


<commit_before>/*** MQTT Z-Way module *******************************************
 Version: 0.0.1
 -----------------------------------------------------------------------------
 Author: David Uebelacker <david@uebelacker.ch>
 Buffer from: https://github.com/toots/buffer-browserify
 MQTT from: https://github.com/leizongmin/MQTTClient
 ******************************************************************************/

function MQTT(id, controller) {
    MQTT.super_.call(this, id, controller);
}

inherits(MQTT, AutomationModule);

_module = MQTT;

MQTT.prototype.init = function (config) {

    const BRIDGE_URL = 'http://127.0.0.1:34254';
    const DIRECT_DEVICES = [
        'toggleButton',
        'battery',
        'sensorBinary',
        'sensorMultilevel'
    ];

    MQTT.super_.prototype.init.call(this, config);
    console.log('MQTT: starting...');

    var self = this;
    self.config = config;
    self.status = {};

    this.log = function (message) {
        console.log('MQTT: ' + message);
    };

    this.verbose = function (message) {
        if (self.config.verbose) {
            console.log('MQTT: ' + message);
        }
    };

    this.findRoom = function (id) {
        var locations = self.controller.locations;
        if (locations) {
            for (var i = 0; i < locations.length; i++) {
                if (locations[i].id == id) {
                    return locations[i];
                }
            }
        }
        return null;
    };

    this.createTopic = function (device) {
        var room = self.findRoom(device.get('location'));
        var topic = self.config.topic_prefix;
        topic += '/';
        topic += room.title;
        topic += '/';
        topic += device.get('metrics:title');
        return topic;
    };

    this.deviceUpdate = function (device) {
        var topic = self.createTopic(device);
        var value = device.get('metrics:level');
        if (!device.get('permanently_hidden')) {
            if (self.status[topic] === undefined
                || value != self.status[topic].value
                || (DIRECT_DEVICES.indexOf(device.get('deviceType')) >= 0
                    && self.status[topic].timestamp + 2000 < self.timestamp())) {
                self.status[topic] = {timestamp: self.timestamp(), value: value};
                self.verbose('publishing: ' + topic + ': ' + value);
                self.sendUpdateToBridge(topic, value.toString().trim(), {retain: self.config.retain});
            }
        }
    };

    this.timestamp = function() {
        return Date.now();
    };

    this.sendConfigToBridge = function () {
        request = {
            url: BRIDGE_URL + '/config',
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            data: JSON.stringify(self.config)
        };
        self.verbose('SENDING: ' + JSON.stringify(request));
        self.verbose('RESPONSE: ' + JSON.stringify(http.request(request)));
    };

    this.sendUpdateToBridge = function (topic, value) {
        request = {
            url: BRIDGE_URL + '/update',
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            data: JSON.stringify({topic: topic, value: value})
        };

        self.verbose('SENDING: ' + JSON.stringify(request));
        self.verbose('RESPONSE: ' + JSON.stringify(http.request(request)));
    };

    if (!self.config.host || !self.config.port) {
        this.log('Host or port not configured! will not start!')
    } else {
        this.sendConfigToBridge();
        this.controller.devices.on('change:metrics:level', self.deviceUpdate);
    }
};

<commit_msg>correct references to config variables<commit_after>/*** MQTT Z-Way module *******************************************
 Version: 0.0.1
 -----------------------------------------------------------------------------
 Author: David Uebelacker <david@uebelacker.ch>
 Buffer from: https://github.com/toots/buffer-browserify
 MQTT from: https://github.com/leizongmin/MQTTClient
 ******************************************************************************/

function MQTT(id, controller) {
    MQTT.super_.call(this, id, controller);
}

inherits(MQTT, AutomationModule);

_module = MQTT;

MQTT.prototype.init = function (config) {

    const BRIDGE_URL = 'http://127.0.0.1:34254';
    const DIRECT_DEVICES = [
        'toggleButton',
        'battery',
        'sensorBinary',
        'sensorMultilevel'
    ];

    MQTT.super_.prototype.init.call(this, config);
    console.log('MQTT: starting...');

    var self = this;
    self.config = config;
    self.status = {};

    this.log = function (message) {
        console.log('MQTT: ' + message);
    };

    this.verbose = function (message) {
        if (self.config.verbose) {
            console.log('MQTT: ' + message);
        }
    };

    this.findRoom = function (id) {
        var locations = self.controller.locations;
        if (locations) {
            for (var i = 0; i < locations.length; i++) {
                if (locations[i].id == id) {
                    return locations[i];
                }
            }
        }
        return null;
    };

    this.createTopic = function (device) {
        var room = self.findRoom(device.get('location'));
        var topic = self.config.mqtt_topic_prefix;
        topic += '/';
        topic += room.title;
        topic += '/';
        topic += device.get('metrics:title');
        return topic;
    };

    this.deviceUpdate = function (device) {
        var topic = self.createTopic(device);
        var value = device.get('metrics:level');
        if (!device.get('permanently_hidden')) {
            if (self.status[topic] === undefined
                || value != self.status[topic].value
                || (DIRECT_DEVICES.indexOf(device.get('deviceType')) >= 0
                    && self.status[topic].timestamp + 2000 < self.timestamp())) {
                self.status[topic] = {timestamp: self.timestamp(), value: value};
                self.verbose('publishing: ' + topic + ': ' + value);
                self.sendUpdateToBridge(topic, value.toString().trim(), {retain: self.config.retain});
            }
        }
    };

    this.timestamp = function() {
        return Date.now();
    };

    this.sendConfigToBridge = function () {
        request = {
            url: BRIDGE_URL + '/config',
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            data: JSON.stringify(self.config)
        };
        self.verbose('SENDING: ' + JSON.stringify(request));
        self.verbose('RESPONSE: ' + JSON.stringify(http.request(request)));
    };

    this.sendUpdateToBridge = function (topic, value) {
        request = {
            url: BRIDGE_URL + '/update',
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            data: JSON.stringify({topic: topic, value: value})
        };

        self.verbose('SENDING: ' + JSON.stringify(request));
        self.verbose('RESPONSE: ' + JSON.stringify(http.request(request)));
    };

    if (!self.config.mqtt_host || !self.config.mqtt_port) {
        this.log('Host or port not configured! will not start!')
    } else {
        this.sendConfigToBridge();
        this.controller.devices.on('change:metrics:level', self.deviceUpdate);
    }
};

<|endoftext|>


<commit_before>/**
 * Takes a {@link Polygon} feature and returns a {@link FeatureCollection} of {@link Point} features at all self-intersections.
 *
 * @module turf/kinks
 * @param {Polygon} polygon a Polygon feature
 * @returns {FeatureCollection} a FeatureCollection of {@link Point} features representing self-intersections
 * @example
 * var poly = {
 *   "type": "Feature",
 *   "properties": {},
 *   "geometry": {
 *     "type": "Polygon",
 *     "coordinates": [[
 *       [-12.034835, 8.901183],
 *       [-12.060413, 8.899826],
 *       [-12.03638, 8.873199],
 *       [-12.059383, 8.871418],
 *       [-12.034835, 8.901183]
 *     ]]
 *   }
 * };
 *
 * var kinks = turf.kinks(poly);
 *
 * var resultFeatures = kinks.intersections.features.concat(poly);
 * var result = {
 *   "type": "FeatureCollection",
 *   "features": resultFeatures
 * };
 *
 * //=result
 */

var polygon = require('turf-polygon');
var point = require('turf-point');
var fc = require('turf-featurecollection');

module.exports = function(polyIn) {
  var poly;
  var results = {intersections: fc([]), fixed: null};
  if (polyIn.type === 'Feature') {
    poly = polyIn.geometry;
  } else {
    poly = polyIn;
  }
  var intersectionHash = {};
  poly.coordinates.forEach(function(ring1){
    poly.coordinates.forEach(function(ring2){
      for(var i = 0; i < ring1.length-1; i++) {
        for(var k = 0; k < ring2.length-1; k++) {
          var intersection = lineIntersects(ring1[i][0],ring1[i][1],ring1[i+1][0],ring1[i+1][1],
            ring2[k][0],ring2[k][1],ring2[k+1][0],ring2[k+1][1]);
          if(intersection) {
            results.intersections.features.push(point([intersection[0], intersection[1]]));
          }
        }
      }
    })
  })
  return results;
}

// modified from http://jsfiddle.net/justin_c_rounds/Gd2S2/light/
function lineIntersects(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {
  // if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
  var denominator, a, b, numerator1, numerator2, result = {
    x: null,
    y: null,
    onLine1: false,
    onLine2: false
  };
  denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY));
  if (denominator == 0) {
    if(result.x != null && result.y != null) {
      return result;
    } else {
      return false;
    }
  }
  a = line1StartY - line2StartY;
  b = line1StartX - line2StartX;
  numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b);
  numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b);
  a = numerator1 / denominator;
  b = numerator2 / denominator;

  // if we cast these lines infinitely in both directions, they intersect here:
  result.x = line1StartX + (a * (line1EndX - line1StartX));
  result.y = line1StartY + (a * (line1EndY - line1StartY));

  // if line1 is a segment and line2 is infinite, they intersect if:
  if (a > 0 && a < 1) {
    result.onLine1 = true;
  }
  // if line2 is a segment and line1 is infinite, they intersect if:
  if (b > 0 && b < 1) {
    result.onLine2 = true;
  }
  // if line1 and line2 are segments, they intersect if both of the above are true
  if(result.onLine1 && result.onLine2){
    return [result.x, result.y];
  }
  else {
    return false;
  }
}
<commit_msg>Add category<commit_after>/**
 * Takes a {@link Polygon} feature and returns a {@link FeatureCollection} of {@link Point} features at all self-intersections.
 *
 * @module turf/kinks
 * @category misc
 * @param {Polygon} polygon a Polygon feature
 * @returns {FeatureCollection} a FeatureCollection of {@link Point} features representing self-intersections
 * @example
 * var poly = {
 *   "type": "Feature",
 *   "properties": {},
 *   "geometry": {
 *     "type": "Polygon",
 *     "coordinates": [[
 *       [-12.034835, 8.901183],
 *       [-12.060413, 8.899826],
 *       [-12.03638, 8.873199],
 *       [-12.059383, 8.871418],
 *       [-12.034835, 8.901183]
 *     ]]
 *   }
 * };
 *
 * var kinks = turf.kinks(poly);
 *
 * var resultFeatures = kinks.intersections.features.concat(poly);
 * var result = {
 *   "type": "FeatureCollection",
 *   "features": resultFeatures
 * };
 *
 * //=result
 */

var polygon = require('turf-polygon');
var point = require('turf-point');
var fc = require('turf-featurecollection');

module.exports = function(polyIn) {
  var poly;
  var results = {intersections: fc([]), fixed: null};
  if (polyIn.type === 'Feature') {
    poly = polyIn.geometry;
  } else {
    poly = polyIn;
  }
  var intersectionHash = {};
  poly.coordinates.forEach(function(ring1){
    poly.coordinates.forEach(function(ring2){
      for(var i = 0; i < ring1.length-1; i++) {
        for(var k = 0; k < ring2.length-1; k++) {
          var intersection = lineIntersects(ring1[i][0],ring1[i][1],ring1[i+1][0],ring1[i+1][1],
            ring2[k][0],ring2[k][1],ring2[k+1][0],ring2[k+1][1]);
          if(intersection) {
            results.intersections.features.push(point([intersection[0], intersection[1]]));
          }
        }
      }
    })
  })
  return results;
}

// modified from http://jsfiddle.net/justin_c_rounds/Gd2S2/light/
function lineIntersects(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {
  // if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
  var denominator, a, b, numerator1, numerator2, result = {
    x: null,
    y: null,
    onLine1: false,
    onLine2: false
  };
  denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY));
  if (denominator == 0) {
    if(result.x != null && result.y != null) {
      return result;
    } else {
      return false;
    }
  }
  a = line1StartY - line2StartY;
  b = line1StartX - line2StartX;
  numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b);
  numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b);
  a = numerator1 / denominator;
  b = numerator2 / denominator;

  // if we cast these lines infinitely in both directions, they intersect here:
  result.x = line1StartX + (a * (line1EndX - line1StartX));
  result.y = line1StartY + (a * (line1EndY - line1StartY));

  // if line1 is a segment and line2 is infinite, they intersect if:
  if (a > 0 && a < 1) {
    result.onLine1 = true;
  }
  // if line2 is a segment and line1 is infinite, they intersect if:
  if (b > 0 && b < 1) {
    result.onLine2 = true;
  }
  // if line1 and line2 are segments, they intersect if both of the above are true
  if(result.onLine1 && result.onLine2){
    return [result.x, result.y];
  }
  else {
    return false;
  }
}
<|endoftext|>


<commit_before>'use strict';

var output_types = {
		'INPUT_CSS': 'input',
		'CRITICAL_CSS': 'critical',
		'REST_CSS': 'rest'
	},
	path = require('path'),
	fs = require('fs'),
	merge = require('merge'),
	postcss = require('postcss'),
	userOptions = null,
	criticalActive = false,
	defaults = {
		'startTag': 'critical:start',
		'endTag': 'critical:end',
		'blockTag': 'critical',
		'suffix': '-critical',
		'output': output_types.INPUT_CSS,
		'save': false,
		'modules': null,
		'separator': ':'
	};

function CriticalSplit(newOptions) {
	newOptions = newOptions || {};

	return function(originalCss, result) {
		if (applyUserOptions(newOptions)) {
			performTransform(originalCss, result);
		};
	};
}

function performTransform(inputCss, result) {
	var originalCss = inputCss.clone(true),
		criticalCss = postcss.root(),
		absolutePath = null,
		directoryPath = null,
		nonCriticalFilename = null,
		criticalFilename = null;

	getAllCriticals(originalCss, criticalCss);

	cleanUp(originalCss);
	cleanUp(criticalCss);

	if (userOptions.save === true) {
		absolutePath = originalCss.source.input.file,
		directoryPath = path.dirname(absolutePath),
		nonCriticalFilename = path.basename(absolutePath),
		criticalFilename = createCriticalFilename(nonCriticalFilename);

		saveCssFile(path.join(directoryPath, nonCriticalFilename), originalCss);
		saveCssFile(path.join(directoryPath, criticalFilename), criticalCss);
	}

	switch(userOptions.output) {
		case output_types.INPUT_CSS:
			result.root = inputCss;
			break;
		case output_types.CRITICAL_CSS:
			result.root = criticalCss;
			break;
		case output_types.REST_CSS:
			result.root = originalCss;
			break;
	}
}

function saveCssFile(filepath, cssRoot) {
	if (cssRoot.nodes.length > 0) {
		fs.writeFileSync(filepath, cssRoot.toResult());
	}
}

function cleanUp(cssRoot) {

	var handleBlock = function(block) {
		if (block.nodes && block.nodes.length === 0) {
			block.remove();
		}
	};

	cssRoot.walkRules(handleBlock);
	cssRoot.walkAtRules(handleBlock);

	cssRoot.raws.semicolon = true;
}

function applyUserOptions(newOptions) {
	var errorMessage ='',
		result = true;

	userOptions = merge(defaults, newOptions);

	if (userOptions.startTag === userOptions.endTag) {
		errorMessage += '\n\n';
		errorMessage += 'ERROR :: PostCSS Plugin: Critical Split\n';
		errorMessage += '.Critical CSS start and end tag must not be the same. \n';
		errorMessage += 'Please adapt your options. \n\n';
		errorMessage += '------ Current Options ----- \n\n';
		errorMessage += JSON.stringify(userOptions, null, 2) + '\n\n';
		errorMessage += '---------- End -------------\n';

		console.error(errorMessage);
		result = false;
	}

	if (typeof userOptions.modules === 'string'){
		userOptions.modules = [userOptions.modules];
	} else if (userOptions.modules instanceof Array === false) {
		userOptions.modules = defaults.modules;
	}

	if (typeof userOptions.separator !== 'string'){
		userOptions.separator = defaults.separator;
	}

	return result;
}

function createCriticalFilename(filename) {
	var position = filename.lastIndexOf('.css'),
		result = '';

	result = filename.substring(0, position);
	result += userOptions.suffix;
	result += '.css';

	return result;
}

function clearLevel(level) {
	level.walk(function(nestedRule) {
		nestedRule.remove();
	});
	level.raws.semicolon = true;
}

function getAllCriticals(originalCss, criticalCss) {
	var currentLevel = null;

	originalCss.walk(function(line) {
		var temp = null;

		line.parent.raws.semicolon = true;

		if (line.type === 'comment' && line.text === userOptions.blockTag) {
			appendFullBlock(criticalCss,line);
			line.remove(); // remove tagging comment
		} else if (line.type === 'comment' && isStartTag(line.text)) {
			criticalActive = true;
			line.remove(); // remove tagging comment
		} else if (line.type === 'comment' && line.text === userOptions.endTag) {
			criticalActive = false
			currentLevel = null;
			line.remove(); // remove tagging comment
		} else if (criticalActive === true && (line.type === 'atrule' && line.name === 'keyframes')) { //keyframes shouldn't be split
			appendDeclaration(criticalCss, line);
		} else if (criticalActive === true && (line.type === 'decl' && hasParentAtRule(line, 'keyframes') === true)) {
			// ignore this rule...
		} else if (criticalActive === true && (line.type === 'atrule' && line.name === 'font-face')){
			appendEmptyRule(criticalCss, line);
		} else if (criticalActive === true && (line.type === 'decl' || line.type === 'comment')) {
			appendDeclaration(criticalCss, line);
			line.remove(); // remove line from originalCss as it is now alive in criticalCss
		}
	});

	originalCss.raws.semicolon = true;
}

function isStartTag(currentText) {
	var result = false,
		modules = userOptions.modules,
		currentModule = null,
		currentModuleStartTag = '',
		i = 0;

	if (currentText === userOptions.startTag) {
		result = true;
	} else if (modules !== null) {
		for (i = 0; i < modules.length; i++) {
			currentModule = modules[i];
			currentModuleStartTag = userOptions.startTag + userOptions.separator + currentModule;

			if (currentText === currentModuleStartTag) {
				result = true;
				break;
			}
		}
	}

	return result;
}

function getBlockFromTriggerTag(line) {
	var result = null;

	if (line.parent.type !== 'root') {
		result = line.parent;
	}

	return result;
}

function appendFullBlock(criticalCss, line) {
	var currentLevel = null,
		parents = null,
		block =  getBlockFromTriggerTag(line);

	if (block !== null) {
		parents = getParents(line);
		currentLevel = prepareSelectors(criticalCss, parents);

		if (currentLevel.type === 'rule') {
			block.walk(function(line) {
				if (!(line.type === 'comment' && line.text === userOptions.blockTag)){
					// we don't want to add the blockTag comment back; skip that
					currentLevel.append(line);
					line.remove();
					currentLevel.raws.semicolon = true;
				}
			});
		}
	}
}

function appendDeclaration(criticalCss, line) {
	var parents = getParents(line),
		currentLevel = prepareSelectors(criticalCss, parents);

	currentLevel.append(line);
	currentLevel.raws.semicolon = true;
}

function appendEmptyRule(criticalCss, line) {
	var rule = line.clone();

	rule.removeAll();
	appendDeclaration(criticalCss, rule);
}

function prepareSelectors(criticalCss, selectorLevels) {
	var currentLevel = null;

	currentLevel = findSelector(criticalCss, selectorLevels);

	if (currentLevel === null) {
		currentLevel = createSelectorLevels(criticalCss, selectorLevels);
		currentLevel.raws.semicolon = true;
	}

	return currentLevel;
}

function createSelectorLevels(criticalCss, selectorLevels) {
	var i = null,
		currentLevel = null,
		temp = null;

	currentLevel = criticalCss;

	for (i = 0; i < selectorLevels.length; i++) {
		temp = selectorLevels[i];
		currentLevel.append(temp);
		currentLevel = temp;
		currentLevel.raws.semicolon = true;
		temp = null;
	}

	return currentLevel;
}

function findSelector(criticalCss, selectorLevels) {
	var result = null,
		currentLevel = null,
		temp = null,
		i = null;

	currentLevel = criticalCss;

	for (i = 0; i < selectorLevels.length; i++) {
		temp = selectorLevels[i];
		currentLevel = currentLevel.last;

		if (typeof currentLevel === 'undefined' || areTheSame(temp, currentLevel) === false) {
			currentLevel = null;
			break;
		}
	}

	result = currentLevel;

	return result;
}

function areTheSame(a, b) {
	var tempA = null,
		tempB = null,
		result = false;

	if (a.type === b.type) {
		tempA = a.clone().removeAll()
		tempB = b.clone().removeAll();

		if (tempA.toString() === tempB.toString()) {
			result =  true;
		}
	}

	return result;
}

function hasParentAtRule(line, name) {
	var result = false,
		parents = getParents(line),
		i = 0,
		currentParent = null;

	for (i; i < parents.length; i++) {
		currentParent = parents[i]

		if (currentParent.type === 'atrule' && currentParent.name === name) {
			result = true;
		}
	}

	return result;
}

function getParents(line) {
	 var parents = [],
		currentParent = null,
		temp = null;

	currentParent = line.parent;

	while (typeof currentParent !== 'undefined' && currentParent.type !== 'root') {
		temp = currentParent.clone();
		clearLevel(temp);
		parents.push(temp);
		temp = null;
		currentParent = currentParent.parent;
	}

	parents = parents.reverse();

	return parents;
}

module.exports = postcss.plugin('postcss-critical-split', CriticalSplit);
module.exports.output_types = output_types;

<commit_msg>added blocktag support for modules<commit_after>'use strict';

var output_types = {
		'INPUT_CSS': 'input',
		'CRITICAL_CSS': 'critical',
		'REST_CSS': 'rest'
	},
	path = require('path'),
	fs = require('fs'),
	merge = require('merge'),
	postcss = require('postcss'),
	userOptions = null,
	criticalActive = false,
	defaults = {
		'startTag': 'critical:start',
		'endTag': 'critical:end',
		'blockTag': 'critical',
		'suffix': '-critical',
		'output': output_types.INPUT_CSS,
		'save': false,
		'modules': null,
		'separator': ':'
	};

function CriticalSplit(newOptions) {
	newOptions = newOptions || {};

	return function(originalCss, result) {
		if (applyUserOptions(newOptions)) {
			performTransform(originalCss, result);
		};
	};
}

function performTransform(inputCss, result) {
	var originalCss = inputCss.clone(true),
		criticalCss = postcss.root(),
		absolutePath = null,
		directoryPath = null,
		nonCriticalFilename = null,
		criticalFilename = null;

	getAllCriticals(originalCss, criticalCss);

	cleanUp(originalCss);
	cleanUp(criticalCss);

	if (userOptions.save === true) {
		absolutePath = originalCss.source.input.file,
		directoryPath = path.dirname(absolutePath),
		nonCriticalFilename = path.basename(absolutePath),
		criticalFilename = createCriticalFilename(nonCriticalFilename);

		saveCssFile(path.join(directoryPath, nonCriticalFilename), originalCss);
		saveCssFile(path.join(directoryPath, criticalFilename), criticalCss);
	}

	switch(userOptions.output) {
		case output_types.INPUT_CSS:
			result.root = inputCss;
			break;
		case output_types.CRITICAL_CSS:
			result.root = criticalCss;
			break;
		case output_types.REST_CSS:
			result.root = originalCss;
			break;
	}
}

function saveCssFile(filepath, cssRoot) {
	if (cssRoot.nodes.length > 0) {
		fs.writeFileSync(filepath, cssRoot.toResult());
	}
}

function cleanUp(cssRoot) {

	var handleBlock = function(block) {
		if (block.nodes && block.nodes.length === 0) {
			block.remove();
		}
	};

	cssRoot.walkRules(handleBlock);
	cssRoot.walkAtRules(handleBlock);

	cssRoot.raws.semicolon = true;
}

function applyUserOptions(newOptions) {
	var errorMessage ='',
		result = true;

	userOptions = merge(defaults, newOptions);

	if (userOptions.startTag === userOptions.endTag) {
		errorMessage += '\n\n';
		errorMessage += 'ERROR :: PostCSS Plugin: Critical Split\n';
		errorMessage += '.Critical CSS start and end tag must not be the same. \n';
		errorMessage += 'Please adapt your options. \n\n';
		errorMessage += '------ Current Options ----- \n\n';
		errorMessage += JSON.stringify(userOptions, null, 2) + '\n\n';
		errorMessage += '---------- End -------------\n';

		console.error(errorMessage);
		result = false;
	}

	if (typeof userOptions.modules === 'string'){
		userOptions.modules = [userOptions.modules];
	} else if (userOptions.modules instanceof Array === false) {
		userOptions.modules = defaults.modules;
	}

	if (typeof userOptions.separator !== 'string'){
		userOptions.separator = defaults.separator;
	}

	return result;
}

function createCriticalFilename(filename) {
	var position = filename.lastIndexOf('.css'),
		result = '';

	result = filename.substring(0, position);
	result += userOptions.suffix;
	result += '.css';

	return result;
}

function clearLevel(level) {
	level.walk(function(nestedRule) {
		nestedRule.remove();
	});
	level.raws.semicolon = true;
}

function getAllCriticals(originalCss, criticalCss) {
	var currentLevel = null;

	originalCss.walk(function(line) {
		var temp = null;

		line.parent.raws.semicolon = true;

		if (line.type === 'comment' && isBlockTag(line.text)) {
			appendFullBlock(criticalCss,line);
			line.remove(); // remove tagging comment
		} else if (line.type === 'comment' && isStartTag(line.text)) {
			criticalActive = true;
			line.remove(); // remove tagging comment
		} else if (line.type === 'comment' && line.text === userOptions.endTag) {
			criticalActive = false
			currentLevel = null;
			line.remove(); // remove tagging comment
		} else if (criticalActive === true && (line.type === 'atrule' && line.name === 'keyframes')) { //keyframes shouldn't be split
			appendDeclaration(criticalCss, line);
		} else if (criticalActive === true && (line.type === 'decl' && hasParentAtRule(line, 'keyframes') === true)) {
			// ignore this rule...
		} else if (criticalActive === true && (line.type === 'atrule' && line.name === 'font-face')){
			appendEmptyRule(criticalCss, line);
		} else if (criticalActive === true && (line.type === 'decl' || line.type === 'comment')) {
			appendDeclaration(criticalCss, line);
			line.remove(); // remove line from originalCss as it is now alive in criticalCss
		}
	});

	originalCss.raws.semicolon = true;
}

function isMarkedTag(currentText, marker) {
	var result = false,
		modules = userOptions.modules,
		currentModule = null,
		currentModuleStartTag = '',
		i = 0;

	if (currentText === userOptions.blockTag) {
		result = true;
	} else if (modules !== null) {
		for (i = 0; i < modules.length; i++) {
			currentModule = modules[i];
			currentModuleStartTag = marker + userOptions.separator + currentModule;

			if (currentText === currentModuleStartTag) {
				result = true;
				break;
			}
		}
	}

	return result;
}

function isBlockTag(currentText) {
	return isMarkedTag(currentText, userOptions.blockTag);
}

function isStartTag(currentText) {
	return isMarkedTag(currentText, userOptions.startTag);
}

function getBlockFromTriggerTag(line) {
	var result = null;

	if (line.parent.type !== 'root') {
		result = line.parent;
	}

	return result;
}

function appendFullBlock(criticalCss, line) {
	var currentLevel = null,
		parents = null,
		block =  getBlockFromTriggerTag(line);

	if (block !== null) {
		parents = getParents(line);
		currentLevel = prepareSelectors(criticalCss, parents);

		if (currentLevel.type === 'rule') {
			block.walk(function(line) {
				if (!(line.type === 'comment' && line.text === userOptions.blockTag)){
					// we don't want to add the blockTag comment back; skip that
					currentLevel.append(line);
					line.remove();
					currentLevel.raws.semicolon = true;
				}
			});
		}
	}
}

function appendDeclaration(criticalCss, line) {
	var parents = getParents(line),
		currentLevel = prepareSelectors(criticalCss, parents);

	currentLevel.append(line);
	currentLevel.raws.semicolon = true;
}

function appendEmptyRule(criticalCss, line) {
	var rule = line.clone();

	rule.removeAll();
	appendDeclaration(criticalCss, rule);
}

function prepareSelectors(criticalCss, selectorLevels) {
	var currentLevel = null;

	currentLevel = findSelector(criticalCss, selectorLevels);

	if (currentLevel === null) {
		currentLevel = createSelectorLevels(criticalCss, selectorLevels);
		currentLevel.raws.semicolon = true;
	}

	return currentLevel;
}

function createSelectorLevels(criticalCss, selectorLevels) {
	var i = null,
		currentLevel = null,
		temp = null;

	currentLevel = criticalCss;

	for (i = 0; i < selectorLevels.length; i++) {
		temp = selectorLevels[i];
		currentLevel.append(temp);
		currentLevel = temp;
		currentLevel.raws.semicolon = true;
		temp = null;
	}

	return currentLevel;
}

function findSelector(criticalCss, selectorLevels) {
	var result = null,
		currentLevel = null,
		temp = null,
		i = null;

	currentLevel = criticalCss;

	for (i = 0; i < selectorLevels.length; i++) {
		temp = selectorLevels[i];
		currentLevel = currentLevel.last;

		if (typeof currentLevel === 'undefined' || areTheSame(temp, currentLevel) === false) {
			currentLevel = null;
			break;
		}
	}

	result = currentLevel;

	return result;
}

function areTheSame(a, b) {
	var tempA = null,
		tempB = null,
		result = false;

	if (a.type === b.type) {
		tempA = a.clone().removeAll()
		tempB = b.clone().removeAll();

		if (tempA.toString() === tempB.toString()) {
			result =  true;
		}
	}

	return result;
}

function hasParentAtRule(line, name) {
	var result = false,
		parents = getParents(line),
		i = 0,
		currentParent = null;

	for (i; i < parents.length; i++) {
		currentParent = parents[i]

		if (currentParent.type === 'atrule' && currentParent.name === name) {
			result = true;
		}
	}

	return result;
}

function getParents(line) {
	 var parents = [],
		currentParent = null,
		temp = null;

	currentParent = line.parent;

	while (typeof currentParent !== 'undefined' && currentParent.type !== 'root') {
		temp = currentParent.clone();
		clearLevel(temp);
		parents.push(temp);
		temp = null;
		currentParent = currentParent.parent;
	}

	parents = parents.reverse();

	return parents;
}

module.exports = postcss.plugin('postcss-critical-split', CriticalSplit);
module.exports.output_types = output_types;

<|endoftext|>


<commit_before>module.exports = {
  'env': {
    'browser': true,
    'es6': true,
    'node': true
  },

  'rules': {
    'block-scoped-var': 0,
    'brace-style': [2, '1tbs', { 'allowSingleLine': false }],
    'camelcase': [2, { 'properties': 'never' }],
    'comma-dangle': 0,
    'comma-spacing': [2, { 'before': false, 'after': true }],
    'comma-style': [2, 'last'],
    'complexity': [1, 10],
    'computed-property-spacing': [2, 'never'],
    'consistent-return': 0,
    'curly': 2,
    'dot-notation': 2,
    'eqeqeq': 2,
    'guard-for-in': 2,
    'indent': [2, 2, { 'SwitchCase': 1 }],
    'key-spacing': 0,
    'max-len': [1, 80, 2],
    'max-nested-callbacks': [2, 3],
    'max-params': [1, 4],
    'new-cap': [2, { 'newIsCap': true, 'capIsNew': false }],
    'new-parens': 2,
    'no-alert': 2,
    'no-array-constructor': 2,
    'no-bitwise': 2,
    'no-caller': 2,
    'no-catch-shadow': 0, // Off because we don't support IE8 and older
    'no-console': 2,
    'no-dupe-args': 2,
    'no-dupe-keys': 2,
    'no-duplicate-case': 0,
    'no-empty-label': 2,
    'no-eq-null': 2,
    'no-eval': 2,
    'no-extend-native': 2,
    'no-extra-bind': 2,
    'no-extra-parens': [2, 'functions'],
    'no-floating-decimal': 2,
    'no-implied-eval': 2,
    'no-irregular-whitespace': 2,
    'no-iterator': 2,
    'no-label-var': 2,
    'no-labels': 2,
    'no-lone-blocks': 2,
    'no-lonely-if': 2,
    'no-loop-func': 2,
    'no-multi-spaces': 0,
    'no-multi-str': 2,
    'no-multiple-empty-lines': 2,
    'no-native-reassign': 2,
    'no-nested-ternary': 2,
    'no-new': 2,
    'no-new-func': 2,
    'no-new-object': 2,
    'no-new-wrappers': 2,
    'no-octal-escape': 2,
    'no-process-exit': 2,
    'no-proto': 2,
    'no-regex-spaces': 2,
    'no-return-assign': 2,
    'no-script-url': 2,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-shadow': 2,
    'no-shadow-restricted-names': 2,
    'no-spaced-func': 2,
    'no-throw-literal': 2,
    'no-trailing-spaces': 2,
    'no-undef-init': 2,
    'no-underscore-dangle': 0,
    'no-unused-expressions': 2,
    'no-use-before-define': 2,
    'no-var': 2,
    'no-void': 2,
    'no-with': 2,
    'object-shorthand': [2, 'methods'],
    'operator-assignment': [2, 'always'],
    'operator-linebreak': [2, 'after'],
    'padded-blocks': [2, 'never'],
    'prefer-const': 2,
    'quotes': [2, 'single', 'avoid-escape'],
    'radix': 2,
    'semi': [2, 'always'],
    'semi-spacing': [2, { 'before': false, 'after': true }],
    'space-after-keywords': [2, 'always'],
    'space-before-blocks': [2, 'always'],
    'space-before-function-paren': [2, 'never'],
    'space-in-parens': [2, 'never'],
    'space-infix-ops': 2,
    'space-return-throw-case': 2,
    'space-unary-ops': 2,
    'spaced-comment': [2, 'always'],
    'strict': [2, 'never'],
    'use-isnan': 2,
    'valid-jsdoc': [2, {
      'prefer': { 'returns': 'return' },
      'requireParamDescription': false,
      'requireReturn': false,
      'requireReturnDescription': false,
    }],
    'valid-typeof': 2,
    'wrap-iife': [2, 'outside'],
    'yoda': 2,
  }
};
<commit_msg>Enable all recommended rules<commit_after>module.exports = {
  'env': {
    'browser': true,
    'es6': true,
    'node': true
  },

  'rules': {
    'block-scoped-var': 0,
    'brace-style': [2, '1tbs', { 'allowSingleLine': false }],
    'camelcase': [2, { 'properties': 'never' }],
    'comma-dangle': 0,
    'comma-spacing': [2, { 'before': false, 'after': true }],
    'comma-style': [2, 'last'],
    'complexity': [1, 10],
    'computed-property-spacing': [2, 'never'],
    'consistent-return': 0,
    'curly': 2,
    'dot-notation': 2,
    'eqeqeq': 2,
    'guard-for-in': 2,
    'indent': [2, 2, { 'SwitchCase': 1 }],
    'key-spacing': 0,
    'max-len': [1, 80, 2],
    'max-nested-callbacks': [2, 3],
    'max-params': [1, 4],
    'new-cap': [2, { 'newIsCap': true, 'capIsNew': false }],
    'new-parens': 2,
    'no-alert': 2,
    'no-array-constructor': 2,
    'no-bitwise': 2,
    'no-caller': 2,
    'no-catch-shadow': 0, // Off because we don't support IE8 and older
    'no-cond-assign': 2,
    'no-console': 2,
    'no-constant-condition': 2,
    'no-control-regex': 2,
    'no-debugger': 2,
    'no-delete-var': 2,
    'no-dupe-args': 2,
    'no-dupe-keys': 2,
    'no-duplicate-case': 0,
    'no-empty': 2,
    'no-empty-character-class': 2,
    'no-empty-label': 2,
    'no-eq-null': 2,
    'no-eval': 2,
    'no-ex-assign': 2,
    'no-extend-native': 2,
    'no-extra-bind': 2,
    'no-extra-boolean-cast': 2,
    'no-extra-parens': [2, 'functions'],
    'no-extra-semi': 2,
    'no-fallthrough': 2,
    'no-floating-decimal': 2,
    'no-func-assign': 2,
    'no-implied-eval': 2,
    'no-inner-declarations': 2,
    'no-invalid-regexp': 2,
    'no-irregular-whitespace': 2,
    'no-iterator': 2,
    'no-label-var': 2,
    'no-labels': 2,
    'no-lone-blocks': 2,
    'no-lonely-if': 2,
    'no-loop-func': 2,
    'no-mixed-spaces-and-tabs': 2,
    'no-multi-spaces': 0,
    'no-multi-str': 2,
    'no-multiple-empty-lines': 2,
    'no-native-reassign': 2,
    'no-negated-in-lhs': 2,
    'no-nested-ternary': 2,
    'no-new': 2,
    'no-new-func': 2,
    'no-new-object': 2,
    'no-new-wrappers': 2,
    'no-obj-calls': 2,
    'no-octal': 2,
    'no-octal-escape': 2,
    'no-process-exit': 2,
    'no-proto': 2,
    'no-redeclare': 2,
    'no-regex-spaces': 2,
    'no-return-assign': 2,
    'no-script-url': 2,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-shadow': 2,
    'no-shadow-restricted-names': 2,
    'no-spaced-func': 2,
    'no-sparse-arrays': 2,
    'no-throw-literal': 2,
    'no-trailing-spaces': 2,
    'no-undef': 2,
    'no-undef-init': 2,
    'no-underscore-dangle': 0,
    'no-unreachable': 2,
    'no-unused-expressions': 2,
    'no-unused-vars': 2,
    'no-use-before-define': 2,
    'no-var': 2,
    'no-void': 2,
    'no-with': 2,
    'object-shorthand': [2, 'methods'],
    'operator-assignment': [2, 'always'],
    'operator-linebreak': [2, 'after'],
    'padded-blocks': [2, 'never'],
    'prefer-const': 2,
    'quotes': [2, 'single', 'avoid-escape'],
    'radix': 2,
    'semi': [2, 'always'],
    'semi-spacing': [2, { 'before': false, 'after': true }],
    'space-after-keywords': [2, 'always'],
    'space-before-blocks': [2, 'always'],
    'space-before-function-paren': [2, 'never'],
    'space-in-parens': [2, 'never'],
    'space-infix-ops': 2,
    'space-return-throw-case': 2,
    'space-unary-ops': 2,
    'spaced-comment': [2, 'always'],
    'strict': [2, 'never'],
    'use-isnan': 2,
    'valid-typeof': 2,
    'valid-jsdoc': [2, {
      'prefer': { 'returns': 'return' },
      'requireParamDescription': false,
      'requireReturn': false,
      'requireReturnDescription': false,
    }],
    'valid-typeof': 2,
    'wrap-iife': [2, 'outside'],
    'yoda': 2,
  }
};
<|endoftext|>


<commit_before>var githubhook = require('githubhook');
var ellipsize = require('ellipsize');
var Telegram = require('node-telegram-bot-api');
var path = require('path');
var os = require('os');
var fs = require('fs');

var configPath = path.join(os.homedir(), '.telegit', 'config.js')
try {
    var config = require(configPath);
} catch (e) {
    console.log('Exception caught while loading config file:');
    console.log(e);

    console.log('\nPlease make sure the config file exists in ' + configPath);
    console.log('\nSee https://github.com/FruitieX/telegit/blob/master/config.example.js');
    console.log('for an example config file.');
    process.exit(1);
}

var START_STR = 'Ok, I will be sending you updates from the following GitHub repo: ';
START_STR += config.git.reponame;

var STOP_STR = 'Ok, I will no longer be sending you GitHub updates.';

var github = githubhook(config.git);

var tg = new Telegram(config.telegram.token, {polling: true});

var tgChats = [];

var chatsPath = path.join(os.homedir(), '.telegit', 'chats.json')
try {
    console.log('Attempting to restore group chat IDs from ' + chatsPath + '...');
    tgChats = require(chatsPath);
    console.log('Successfully restored group chat IDs.');
} catch (e) {
    console.log('Error while reading from ' + chatsPath + ':');
    console.log(e);

    console.log('\nNot restoring group chat IDs. You MUST greet the bot with the /gitstart');
    console.log('command in each group chat where you want it to send any github events.');
}

var writeTgChats = function() {
    fs.writeFileSync(chatsPath, JSON.stringify(tgChats));
};

tg.on('message', function(msg) {
    var chatId = msg.chat.id;
    if (msg.text === '/gitstart') {
        if (tgChats.indexOf(chatId) === -1) {
            tgChats.push(chatId);
            writeTgChats();
            tg.sendMessage(chatId, START_STR);
        }
    } else if (msg.text === '/gitstop') {
        var chatIndex = tgChats.indexOf(chatId);
        if (chatIndex !== -1) {
            tgChats.splice(chatIndex, 1);
            writeTgChats();
            tg.sendMessage(chatId, STOP_STR);
        }
    }
});

var sendTg = function(msg) {
    console.log('Sending to Telegram: ' + msg);

    tgChats.forEach(function(chatId) {
        tg.sendMessage(chatId, msg, {
            disable_web_page_preview: true,
            parse_mode: 'Markdown'
        });
    });
};

github.on('push:' + config.git.reponame, function(ref, data) {
    // don't care about branch deletes
    if (!data.commits.length) {
        return;
    }

    var s = '[' + data.after.substr(0, 8) + '](' + data.compare + ')';

    s += ': ' + data.pusher.name + ', (' + data.pusher.email + ')';
    s += ' pushed ' + data.commits.length;
    s += ' ' + (data.commits.length === 1 ? 'commit' : 'commits') + ' to ' + ref;

    if (data.commits.length === 1) {
        s += ': "' + data.commits[0].message + '"';
    }

    sendTg(s);
});

github.on('pull_request:' + config.git.reponame, function(ref, data) {
    var s = '[Pull request #';

    s += data.number;
    s += '](' + data.pull_request.html_url + ')';

    s += ' (' + data.pull_request.title + ')';

    s += ' ' + data.action;
    s += ' by ' + data.sender.login;

    sendTg(s);
});

github.on('issues:' + config.git.reponame, function(ref, data) {
    var s = '[Issue #';

    s += data.issue.number;
    s += '](' + data.issue.html_url + ')';

    s += ' (' + data.issue.title + ')';
    s += ' ' + data.action;

    if (data.action === 'unassigned') {
        s += ' from ' + data.assignee.login;
    } else if (data.action === 'assigned') {
        s += ' to ' + data.assignee.login;
    }

    s += ' by ' + data.sender.login;

    sendTg(s);
});

github.on('issue_comment:' + config.git.reponame, function(ref, data) {
    var s = data.sender.login + ' commented on [';
    s += data.issue.pull_request ? 'pull request' : 'issue';
    s += ' #';
    s += data.issue.number;
    s += '](' + data.comment.html_url + ')';

    s += ' (' + data.issue.title + '): ';

    s += ellipsize(data.comment.body, 120);

    sendTg(s);
});

github.listen();
<commit_msg>Escape markdown control characters in any strings from GitHub API<commit_after>var githubhook = require('githubhook');
var ellipsize = require('ellipsize');
var Telegram = require('node-telegram-bot-api');
var path = require('path');
var os = require('os');
var fs = require('fs');

var configPath = path.join(os.homedir(), '.telegit', 'config.js')
try {
    var config = require(configPath);
} catch (e) {
    console.log('Exception caught while loading config file:');
    console.log(e);

    console.log('\nPlease make sure the config file exists in ' + configPath);
    console.log('\nSee https://github.com/FruitieX/telegit/blob/master/config.example.js');
    console.log('for an example config file.');
    process.exit(1);
}

var START_STR = 'Ok, I will be sending you updates from the following GitHub repo: ';
START_STR += config.git.reponame;

var STOP_STR = 'Ok, I will no longer be sending you GitHub updates.';

var github = githubhook(config.git);

var tg = new Telegram(config.telegram.token, {polling: true});

var tgChats = [];

var chatsPath = path.join(os.homedir(), '.telegit', 'chats.json')
try {
    console.log('Attempting to restore group chat IDs from ' + chatsPath + '...');
    tgChats = require(chatsPath);
    console.log('Successfully restored group chat IDs.');
} catch (e) {
    console.log('Error while reading from ' + chatsPath + ':');
    console.log(e);

    console.log('\nNot restoring group chat IDs. You MUST greet the bot with the /gitstart');
    console.log('command in each group chat where you want it to send any github events.');
}

var writeTgChats = function() {
    fs.writeFileSync(chatsPath, JSON.stringify(tgChats));
};

tg.on('message', function(msg) {
    var chatId = msg.chat.id;
    if (msg.text === '/gitstart') {
        if (tgChats.indexOf(chatId) === -1) {
            tgChats.push(chatId);
            writeTgChats();
            tg.sendMessage(chatId, START_STR);
        }
    } else if (msg.text === '/gitstop') {
        var chatIndex = tgChats.indexOf(chatId);
        if (chatIndex !== -1) {
            tgChats.splice(chatIndex, 1);
            writeTgChats();
            tg.sendMessage(chatId, STOP_STR);
        }
    }
});

var escapeMd = function(text) {
    // Telegram Bot API supports *_[]()` markdown control characters
    var specialChars = new RegExp('([\\\\`*_()\\[\\]]){1}', 'g');

    return text.replace(specialChars, function(match) {
        return '\\' + match;
    });
};

var sendTg = function(msg) {
    console.log('Sending to Telegram: ' + msg);

    tgChats.forEach(function(chatId) {
        tg.sendMessage(chatId, msg, {
            disable_web_page_preview: true,
            parse_mode: 'Markdown'
        });
    });
};

github.on('push:' + config.git.reponame, function(ref, data) {
    // don't care about branch deletes
    if (!data.commits.length) {
        return;
    }

    var s = '[' + escapeMd(data.after.substr(0, 8)) + '](' + escapeMd(data.compare) + ')';

    s += ': ' + escapeMd(data.pusher.name) + ', (' + escapeMd(data.pusher.email) + ')';
    s += ' pushed ' + data.commits.length;
    s += ' ' + (data.commits.length === 1 ? 'commit' : 'commits') + ' to ' + ref;

    if (data.commits.length === 1) {
        s += ': "' + escapeMd(data.commits[0].message) + '"';
    }

    sendTg(s);
});

github.on('pull_request:' + config.git.reponame, function(ref, data) {
    var s = '[Pull request #';

    s += escapeMd(data.number);
    s += '](' + escapeMd(data.pull_request.html_url) + ')';

    s += ' (' + escapeMd(data.pull_request.title) + ')';

    s += ' ' + escapeMd(data.action);
    s += ' by ' + escapeMd(data.sender.login);

    sendTg(s);
});

github.on('issues:' + config.git.reponame, function(ref, data) {
    var s = '[Issue #';

    s += escapeMd(data.issue.number);
    s += '](' + escapeMd(data.issue.html_url) + ')';

    s += ' (' + escapeMd(data.issue.title) + ')';
    s += ' ' + escapeMd(data.action);

    if (data.action === 'unassigned') {
        s += ' from ' + escapeMd(data.assignee.login);
    } else if (data.action === 'assigned') {
        s += ' to ' + escapeMd(data.assignee.login);
    }

    s += ' by ' + escapeMd(data.sender.login);

    sendTg(s);
});

github.on('issue_comment:' + config.git.reponame, function(ref, data) {
    var s = escapeMd(data.sender.login) + ' commented on [';
    s += escapeMd(data.issue.pull_request) ? 'pull request' : 'issue';
    s += ' #';
    s += escapeMd(data.issue.number);
    s += '](' + escapeMd(data.comment.html_url) + ')';

    s += ' (' + escapeMd(data.issue.title) + '): ';

    s += escapeMd(ellipsize(data.comment.body, 120));

    sendTg(s);
});

github.listen();
<|endoftext|>