<commit_before>"use strict";

var util = require('./util.js')("lora-packet parser");

exports.fromWire = function(contents) {
    var lp = new LoraPacket();
    lp._fromWire(contents);

    // return null object on failure
    if (lp._packet !== null) {
        return lp;
    } else {
        return null;
    }
};

exports.fromFields = function(userFields) {
    var lp = new LoraPacket();
    lp._fromFields(userFields);

    // return null object on failure
    if (lp._packet !== null) {
        return lp;
    } else {
        return null;
    }
};

var constants = {
    // decoding MType description & direction
    MTYPE_JOIN_REQUEST: 0,
    MTYPE_JOIN_ACCEPT: 1,
    MTYPE_UNCONFIRMED_DATA_UP: 2,
    MTYPE_UNCONFIRMED_DATA_DOWN: 3,
    MTYPE_CONFIRMED_DATA_UP: 4,
    MTYPE_CONFIRMED_DATA_DOWN: 5,

    MTYPE_DESCRIPTIONS: [
        'Join Request',
        'Join Accept',
        'Unconfirmed Data Up',
        'Unconfirmed Data Down',
        'Confirmed Data Up',
        'Confirmed Data Down',
        'RFU',
        'Proprietary'
    ],

    MTYPE_DIRECTIONS: [
        null,
        null,
        'up',
        'down',
        'up',
        'down',
        null,
        null
    ],

    FCTRL_ADR: 0x80,
    FCTRL_ADRACKREQ: 0x40,
    FCTRL_ACK: 0x20,
    FCTRL_FPENDING: 0x10
};

exports.constants = function() {
    //  return Object.assign({}, constants);
    return constants;
};

function LoraPacket(packetContents) {
    if (!(this instanceof LoraPacket)) return new LoraPacket(packetContents);
    var that = this;

    this.getPacket = function() {
        return that._packet;
    };

    this._getMType = function() {
        return (that._packet.MHDR.readUInt8(0) & 0xff) >> 5;
    };

    this.isDataMessage = function() {
        switch (that._getMType()) {
            case constants.MTYPE_UNCONFIRMED_DATA_UP:
            case constants.MTYPE_UNCONFIRMED_DATA_DOWN:
            case constants.MTYPE_CONFIRMED_DATA_UP:
            case constants.MTYPE_CONFIRMED_DATA_DOWN:
                return true;
            default:
                return false;
        }
    };

    //    this.isJoinMessage = function () {
    //        return that.isJoinRequestMessage() || that.isJoinAcceptMessage();
    //    };
    this.isJoinRequestMessage = function() {
        return (that._getMType() == constants.MTYPE_JOIN_REQUEST);
    };
    this.isJoinAcceptMessage = function() {
        return (that._getMType() == constants.MTYPE_JOIN_ACCEPT);
    };

    this._fromWire = function(contents) {
        if (contents instanceof Buffer) {
            _initialiseFromWireformat(contents);

        }
        // TODO also allow initialisation from byte array
        else {
            that._packet = null;
        }
    };

    // parse incoming packet
    function _initialiseFromWireformat(contents) {
        that._packet = {};
        var p = that._packet;

        // duplicate buffer to guard against modification
        var incoming = new Buffer(contents);

        p.PHYPayload = incoming;

        p.MHDR = incoming.slice(0, 1);

        //        var type = p.MHDR._getMType;
        if (that.isJoinRequestMessage()) {
            // NB little-endian buffers!
            p.AppEUI = util.reverse(incoming.slice(1, 1 + 8));
            p.DevEUI = util.reverse(incoming.slice(9, 9 + 8));
            p.DevNonce = util.reverse(incoming.slice(17, 17 + 2));
        } else if (that.isJoinAcceptMessage()) {
            p.AppNonce = util.reverse(incoming.slice(1, 1 + 3));
            p.NetID = util.reverse(incoming.slice(4, 4 + 3));
            p.DevAddr = util.reverse(incoming.slice(7, 7 + 4));
            var DLSettings = incoming.readInt8(11);
            var RxDelay = incoming.readInt8(12);
        } else if (that.isDataMessage()) {
            p.MACPayload = incoming.slice(1, incoming.length - 4);
            p.MIC = incoming.slice(incoming.length - 4);

            p.FCtrl = p.MACPayload.slice(4, 5);
            var FCtrl = p.FCtrl.readInt8(0);
            var FOptsLen = FCtrl & 0x0f;
            p.FOpts = p.MACPayload.slice(6, 6 + FOptsLen);
            var FHDR_length = 7 + FOptsLen;
            p.FHDR = p.MACPayload.slice(0, 0 + FHDR_length); // TODO silence linter
            p.DevAddr = util.reverse(p.FHDR.slice(0, 4)); // NB little-endian buffers!

            p.FCnt = util.reverse(p.FHDR.slice(5, 7)); // NB little-endian buffers!

            // what's left? Either FPort+FRMPayload or nothing.
            if (FHDR_length == p.MACPayload.length) {
                p.FPort = new Buffer(0);
                p.FRMPayload = new Buffer(0);
            } else {
                p.FPort = p.MACPayload.slice(FHDR_length, FHDR_length + 1);
                p.FRMPayload = p.MACPayload.slice(FHDR_length + 1);
            }
        }
    }

    this._fromFields = function(userFields) {
        if (_isPlausible(userFields)) {
            _initialiseFromFields(userFields);
        } else {
            that._packet = null;
        }
    };

    function _isPlausible(userFields) {
        if (!util.isDefined(userFields)) {
            return false;
        } else if (
            util.isDefined(userFields.payload) && util.isDefined(userFields.DevAddr)
        ) {
            return true;
        } else {
            // not enough to construct packet
            return false;
        }
    }

    // populate anew from whatever the client gives us
    //  with defaults for the rest
    function _initialiseFromFields(userFields) {
        var p = {}; // temp packet we use during construction
        that._packet = null;

        // required fields
        if (util.isBufferLength(userFields.DevAddr, 4)) {
            p.DevAddr = new Buffer(userFields.DevAddr);
        } else {
            return null;
        }

        // required fields:
        if (util.isDefined(userFields.payload)) {
            if (util.isString(userFields.payload)) {
                // construct from string
                p.FRMPayload = new Buffer(userFields.payload);
            } else if (util.isBuffer(userFields.payload)) {
                // construct from buffer
                p.FRMPayload = new Buffer(userFields.payload);
            } else if (userFields.payload instanceof Uint8Array) {
                // TODO: construct from Uint8Array
                p.FRMPayload = new Buffer("TODO");
            } else {
                return null; // payload is required in suitable format
            }
        } else {
            return null; // payload is required
        }

        if (util.isDefined(userFields.MType)) {
            if (util.isNumber(userFields.MType)) {
                p.MHDR = new Buffer(1);
                p.MHDR.writeUInt8(userFields.MType << 5, 0);
            } else if (util.isString(userFields.MType)) {
                var mhdr_idx = constants.MTYPE_DESCRIPTIONS.indexOf(userFields.MType);
                if (mhdr_idx >= 0) {
                    p.MHDR = new Buffer(1);
                    p.MHDR.writeUInt8(mhdr_idx << 5, 0);
                }
            } else {
                return null; // bogus
            }
        }

        if (util.isDefined(userFields.FCnt)) {
            if (util.isBufferLength(userFields.FCnt, 2)) {
                p.FCnt = new Buffer(userFields.FCnt);
            } else if (util.isNumber(userFields.FCnt)) {
                p.FCnt = new Buffer(2);
                p.FCnt.writeInt16BE(userFields.FCnt, 0); // NB bigendian
            }
        }

        if (util.isDefined(userFields.FOpts)) {
            if (util.isString(userFields.FOpts)) {
                p.FOpts = new Buffer(userFields.FOpts, 'hex');
            } else if (util.isBuffer(userFields.FOpts)) {
                p.FOpts = new Buffer(userFields.FOpts);
            }
            if (15 < p.FOpts.length) {
                return null; // Too many options fo piggybacked
            }
        } else {
            p.FOpts = new Buffer('', 'hex');
        }

        if (util.isDefined(userFields.FCtrl)) {
            var fctrl = 0;
            if (userFields.FCtrl.ADR) {
                fctrl |= constants.FCTRL_ADR;
            }
            if (userFields.FCtrl.ADRACKReq) {
                fctrl |= constants.FCTRL_ADRACKREQ;
            }
            if (userFields.FCtrl.ACK) {
                fctrl |= constants.FCTRL_ACK;
            }
            if (userFields.FCtrl.FPending) {
                fctrl |= constants.FCTRL_FPENDING;
            }
            fctrl |= (p.FOpts.length & 0x0f);
            p.FCtrl = new Buffer(1);
            p.FCtrl.writeUInt8(fctrl, 0);
        }

        if (util.isDefined(userFields.FPort)) {
            if (util.isNumber(userFields.FPort)) {
                p.FPort = new Buffer(1);
                p.FPort.writeUInt8(userFields.FPort, 0);
            } else {
                return null; // bogus
            }
        }

        // defaults
        if (!util.isDefined(p.MHDR)) {
            p.MHDR = new Buffer(1);
            p.MHDR.writeUInt8(constants.MTYPE_UNCONFIRMED_DATA_UP << 5, 0);
        }

        if (!util.isDefined(p.FPort)) {
            p.FPort = new Buffer('01', 'hex');
        }
        if (!util.isDefined(p.FCnt)) {
            p.FCnt = new Buffer('0001', 'hex');
        }
        if (!util.isDefined(p.FCtrl)) {
            p.FCtrl = new Buffer('00', 'hex');
        }

        // this MIC is bogus for now, but correct length
        if (!util.isDefined(p.MIC)) {
            p.MIC = new Buffer('EEEEEEEE', 'hex');
        }

        that._packet = p;
        that.mergeGroupFields();
    }

    // packet creation (or re-creation)
    // - merge individual fields to create the higher-level ones
    //  (e.g PHYPayload = MHDR | MACPayload  | MIC)
    // BUT note that DevAddr & FCnt are stored big-endian
    this.mergeGroupFields = function() {
        var p = that._packet;
        p.FHDR = Buffer.concat([util.reverse(p.DevAddr), p.FCtrl, util.reverse(p.FCnt), p.FOpts]);
        p.MACPayload = Buffer.concat([p.FHDR, p.FPort, p.FRMPayload]);
        p.PHYPayload = Buffer.concat([p.MHDR, p.MACPayload, p.MIC]);
    };

    // provide MType as a string
    this.getMType = function() {
        return constants.MTYPE_DESCRIPTIONS[that._getMType()];
    };

    // provide Direction as a string
    this.getDir = function() {
        return constants.MTYPE_DIRECTIONS[that._getMType()];
    };

    // provide FPort as a number
    this.getFPort = function() {
        return that._packet.FPort.readUInt8(0);
    };

    // provide FCnt as a number
    this.getFCnt = function() {
        return that._packet.FCnt.readUInt16BE(0);
    };

    // provide FCtrl.ACK as a flag
    this.getFCtrlACK = function() {
        return !!(that._packet.FCtrl.readUInt8(0) & constants.FCTRL_ACK);
    };

    // provide FCtrl.ADR as a flag
    this.getFCtrlADR = function() {
        return !!(that._packet.FCtrl.readUInt8(0) & constants.FCTRL_ADR);
    };

    // provide FCtrl.FPending as a flag
    this.getFCtrlFPending = function() {
        return !!(that._packet.FCtrl.readUInt8(0) & constants.FCTRL_FPENDING);
    };

    this.getPHYPayload = function() {
        return that._packet.PHYPayload;
    };

    this.getBuffers = function() {
        return that._packet;
    };

    /*    this.setPlaintextPayload = function(payload) {
     that._plaintextPayload = payload;
     };*/

    /*    this.getPlaintextPayload = function() {
     return that._plaintextPayload;
     };*/

    this.toString = function() {
        var p = that._packet;

        var msg = "";

        if (that.isJoinRequestMessage()) {
            msg += "      AppEUI = " + asHexString(p.AppEUI) + "\n";
            msg += "      DevEUI = " + asHexString(p.DevEUI) + "\n";
            msg += "    DevNonce = " + asHexString(p.DevNonce) + "\n";
        } else if (that.isJoinAcceptMessage()) {
            msg += "    AppNonce = " + asHexString(p.AppNonce) + "\n";
            msg += "       NetID = " + asHexString(p.NetID) + "\n";
            msg += "     DevAddr = " + asHexString(p.DevAddr) + "\n";
            // TODO & the rest
        } else if (that.isDataMessage()) {
            msg += "  PHYPayload = " + asHexString(p.PHYPayload).toUpperCase() + "\n";
            msg += "\n";
            msg += "( PHYPayload = MHDR[1] | MACPayload[..] | MIC[4] )\n";
            msg += "        MHDR = " + asHexString(p.MHDR) + "\n";
            msg += "  MACPayload = " + asHexString(p.MACPayload) + "\n";
            msg += "         MIC = " + asHexString(p.MIC) + "\n";
            msg += "\n";
            msg += "( MACPayload = FHDR | FPort | FRMPayload )\n";
            msg += "        FHDR = " + asHexString(p.FHDR) + "\n";
            msg += "       FPort = " + asHexString(p.FPort) + "\n";
            msg += "  FRMPayload = " + asHexString(p.FRMPayload) + "\n";
            msg += "\n";
            msg += "      ( FHDR = DevAddr[4] | FCtrl[1] | FCnt[2] | FOpts[0..15] )\n";
            msg += "     DevAddr = " + asHexString(p.DevAddr) + " (Big Endian)\n";
            msg += "       FCtrl = " + asHexString(p.FCtrl) + "\n"; //TODO as binary?
            msg += "        FCnt = " + asHexString(p.FCnt) + " (Big Endian)\n";
            msg += "       FOpts = " + asHexString(p.FOpts) + "\n";
            msg += "\n";
            msg += "Message Type = " + that.getMType() + "\n";
            msg += "   Direction = " + that.getDir() + "\n";
            msg += "        FCnt = " + that.getFCnt() + "\n";
            msg += "   FCtrl.ACK = " + that.getFCtrlACK() + "\n";
            msg += "   FCtrl.ADR = " + that.getFCtrlADR() + "\n";
        }
        return msg;
    };
}

function asHexString(buf) {
    return buf.toString('hex').toUpperCase();
}<commit_msg>debug add message type<commit_after>"use strict";

var util = require('./util.js')("lora-packet parser");

exports.fromWire = function(contents) {
    var lp = new LoraPacket();
    lp._fromWire(contents);

    // return null object on failure
    if (lp._packet !== null) {
        return lp;
    } else {
        return null;
    }
};

exports.fromFields = function(userFields) {
    var lp = new LoraPacket();
    lp._fromFields(userFields);

    // return null object on failure
    if (lp._packet !== null) {
        return lp;
    } else {
        return null;
    }
};

var constants = {
    // decoding MType description & direction
    MTYPE_JOIN_REQUEST: 0,
    MTYPE_JOIN_ACCEPT: 1,
    MTYPE_UNCONFIRMED_DATA_UP: 2,
    MTYPE_UNCONFIRMED_DATA_DOWN: 3,
    MTYPE_CONFIRMED_DATA_UP: 4,
    MTYPE_CONFIRMED_DATA_DOWN: 5,

    MTYPE_DESCRIPTIONS: [
        'Join Request',
        'Join Accept',
        'Unconfirmed Data Up',
        'Unconfirmed Data Down',
        'Confirmed Data Up',
        'Confirmed Data Down',
        'RFU',
        'Proprietary'
    ],

    MTYPE_DIRECTIONS: [
        null,
        null,
        'up',
        'down',
        'up',
        'down',
        null,
        null
    ],

    FCTRL_ADR: 0x80,
    FCTRL_ADRACKREQ: 0x40,
    FCTRL_ACK: 0x20,
    FCTRL_FPENDING: 0x10
};

exports.constants = function() {
    //  return Object.assign({}, constants);
    return constants;
};

function LoraPacket(packetContents) {
    if (!(this instanceof LoraPacket)) return new LoraPacket(packetContents);
    var that = this;

    this.getPacket = function() {
        return that._packet;
    };

    this._getMType = function() {
        return (that._packet.MHDR.readUInt8(0) & 0xff) >> 5;
    };

    this.isDataMessage = function() {
        switch (that._getMType()) {
            case constants.MTYPE_UNCONFIRMED_DATA_UP:
            case constants.MTYPE_UNCONFIRMED_DATA_DOWN:
            case constants.MTYPE_CONFIRMED_DATA_UP:
            case constants.MTYPE_CONFIRMED_DATA_DOWN:
                return true;
            default:
                return false;
        }
    };

    //    this.isJoinMessage = function () {
    //        return that.isJoinRequestMessage() || that.isJoinAcceptMessage();
    //    };
    this.isJoinRequestMessage = function() {
        return (that._getMType() == constants.MTYPE_JOIN_REQUEST);
    };
    this.isJoinAcceptMessage = function() {
        return (that._getMType() == constants.MTYPE_JOIN_ACCEPT);
    };

    this._fromWire = function(contents) {
        if (contents instanceof Buffer) {
            _initialiseFromWireformat(contents);

        }
        // TODO also allow initialisation from byte array
        else {
            that._packet = null;
        }
    };

    // parse incoming packet
    function _initialiseFromWireformat(contents) {
        that._packet = {};
        var p = that._packet;

        // duplicate buffer to guard against modification
        var incoming = new Buffer(contents);

        p.PHYPayload = incoming;

        p.MHDR = incoming.slice(0, 1);

        //        var type = p.MHDR._getMType;
        if (that.isJoinRequestMessage()) {
            // NB little-endian buffers!
            p.AppEUI = util.reverse(incoming.slice(1, 1 + 8));
            p.DevEUI = util.reverse(incoming.slice(9, 9 + 8));
            p.DevNonce = util.reverse(incoming.slice(17, 17 + 2));
        } else if (that.isJoinAcceptMessage()) {
            p.AppNonce = util.reverse(incoming.slice(1, 1 + 3));
            p.NetID = util.reverse(incoming.slice(4, 4 + 3));
            p.DevAddr = util.reverse(incoming.slice(7, 7 + 4));
            var DLSettings = incoming.readInt8(11);
            var RxDelay = incoming.readInt8(12);
        } else if (that.isDataMessage()) {
            p.MACPayload = incoming.slice(1, incoming.length - 4);
            p.MIC = incoming.slice(incoming.length - 4);

            p.FCtrl = p.MACPayload.slice(4, 5);
            var FCtrl = p.FCtrl.readInt8(0);
            var FOptsLen = FCtrl & 0x0f;
            p.FOpts = p.MACPayload.slice(6, 6 + FOptsLen);
            var FHDR_length = 7 + FOptsLen;
            p.FHDR = p.MACPayload.slice(0, 0 + FHDR_length); // TODO silence linter
            p.DevAddr = util.reverse(p.FHDR.slice(0, 4)); // NB little-endian buffers!

            p.FCnt = util.reverse(p.FHDR.slice(5, 7)); // NB little-endian buffers!

            // what's left? Either FPort+FRMPayload or nothing.
            if (FHDR_length == p.MACPayload.length) {
                p.FPort = new Buffer(0);
                p.FRMPayload = new Buffer(0);
            } else {
                p.FPort = p.MACPayload.slice(FHDR_length, FHDR_length + 1);
                p.FRMPayload = p.MACPayload.slice(FHDR_length + 1);
            }
        }
    }

    this._fromFields = function(userFields) {
        if (_isPlausible(userFields)) {
            _initialiseFromFields(userFields);
        } else {
            that._packet = null;
        }
    };

    function _isPlausible(userFields) {
        if (!util.isDefined(userFields)) {
            return false;
        } else if (
            util.isDefined(userFields.payload) && util.isDefined(userFields.DevAddr)
        ) {
            return true;
        } else {
            // not enough to construct packet
            return false;
        }
    }

    // populate anew from whatever the client gives us
    //  with defaults for the rest
    function _initialiseFromFields(userFields) {
        var p = {}; // temp packet we use during construction
        that._packet = null;

        // required fields
        if (util.isBufferLength(userFields.DevAddr, 4)) {
            p.DevAddr = new Buffer(userFields.DevAddr);
        } else {
            return null;
        }

        // required fields:
        if (util.isDefined(userFields.payload)) {
            if (util.isString(userFields.payload)) {
                // construct from string
                p.FRMPayload = new Buffer(userFields.payload);
            } else if (util.isBuffer(userFields.payload)) {
                // construct from buffer
                p.FRMPayload = new Buffer(userFields.payload);
            } else if (userFields.payload instanceof Uint8Array) {
                // TODO: construct from Uint8Array
                p.FRMPayload = new Buffer("TODO");
            } else {
                return null; // payload is required in suitable format
            }
        } else {
            return null; // payload is required
        }

        if (util.isDefined(userFields.MType)) {
            if (util.isNumber(userFields.MType)) {
                p.MHDR = new Buffer(1);
                p.MHDR.writeUInt8(userFields.MType << 5, 0);
            } else if (util.isString(userFields.MType)) {
                var mhdr_idx = constants.MTYPE_DESCRIPTIONS.indexOf(userFields.MType);
                if (mhdr_idx >= 0) {
                    p.MHDR = new Buffer(1);
                    p.MHDR.writeUInt8(mhdr_idx << 5, 0);
                }
            } else {
                return null; // bogus
            }
        }

        if (util.isDefined(userFields.FCnt)) {
            if (util.isBufferLength(userFields.FCnt, 2)) {
                p.FCnt = new Buffer(userFields.FCnt);
            } else if (util.isNumber(userFields.FCnt)) {
                p.FCnt = new Buffer(2);
                p.FCnt.writeInt16BE(userFields.FCnt, 0); // NB bigendian
            }
        }

        if (util.isDefined(userFields.FOpts)) {
            if (util.isString(userFields.FOpts)) {
                p.FOpts = new Buffer(userFields.FOpts, 'hex');
            } else if (util.isBuffer(userFields.FOpts)) {
                p.FOpts = new Buffer(userFields.FOpts);
            }
            if (15 < p.FOpts.length) {
                return null; // Too many options fo piggybacked
            }
        } else {
            p.FOpts = new Buffer('', 'hex');
        }

        if (util.isDefined(userFields.FCtrl)) {
            var fctrl = 0;
            if (userFields.FCtrl.ADR) {
                fctrl |= constants.FCTRL_ADR;
            }
            if (userFields.FCtrl.ADRACKReq) {
                fctrl |= constants.FCTRL_ADRACKREQ;
            }
            if (userFields.FCtrl.ACK) {
                fctrl |= constants.FCTRL_ACK;
            }
            if (userFields.FCtrl.FPending) {
                fctrl |= constants.FCTRL_FPENDING;
            }
            fctrl |= (p.FOpts.length & 0x0f);
            p.FCtrl = new Buffer(1);
            p.FCtrl.writeUInt8(fctrl, 0);
        }

        if (util.isDefined(userFields.FPort)) {
            if (util.isNumber(userFields.FPort)) {
                p.FPort = new Buffer(1);
                p.FPort.writeUInt8(userFields.FPort, 0);
            } else {
                return null; // bogus
            }
        }

        // defaults
        if (!util.isDefined(p.MHDR)) {
            p.MHDR = new Buffer(1);
            p.MHDR.writeUInt8(constants.MTYPE_UNCONFIRMED_DATA_UP << 5, 0);
        }

        if (!util.isDefined(p.FPort)) {
            p.FPort = new Buffer('01', 'hex');
        }
        if (!util.isDefined(p.FCnt)) {
            p.FCnt = new Buffer('0001', 'hex');
        }
        if (!util.isDefined(p.FCtrl)) {
            p.FCtrl = new Buffer('00', 'hex');
        }

        // this MIC is bogus for now, but correct length
        if (!util.isDefined(p.MIC)) {
            p.MIC = new Buffer('EEEEEEEE', 'hex');
        }

        that._packet = p;
        that.mergeGroupFields();
    }

    // packet creation (or re-creation)
    // - merge individual fields to create the higher-level ones
    //  (e.g PHYPayload = MHDR | MACPayload  | MIC)
    // BUT note that DevAddr & FCnt are stored big-endian
    this.mergeGroupFields = function() {
        var p = that._packet;
        p.FHDR = Buffer.concat([util.reverse(p.DevAddr), p.FCtrl, util.reverse(p.FCnt), p.FOpts]);
        p.MACPayload = Buffer.concat([p.FHDR, p.FPort, p.FRMPayload]);
        p.PHYPayload = Buffer.concat([p.MHDR, p.MACPayload, p.MIC]);
    };

    // provide MType as a string
    this.getMType = function() {
        return constants.MTYPE_DESCRIPTIONS[that._getMType()];
    };

    // provide Direction as a string
    this.getDir = function() {
        return constants.MTYPE_DIRECTIONS[that._getMType()];
    };

    // provide FPort as a number
    this.getFPort = function() {
        return that._packet.FPort.readUInt8(0);
    };

    // provide FCnt as a number
    this.getFCnt = function() {
        return that._packet.FCnt.readUInt16BE(0);
    };

    // provide FCtrl.ACK as a flag
    this.getFCtrlACK = function() {
        return !!(that._packet.FCtrl.readUInt8(0) & constants.FCTRL_ACK);
    };

    // provide FCtrl.ADR as a flag
    this.getFCtrlADR = function() {
        return !!(that._packet.FCtrl.readUInt8(0) & constants.FCTRL_ADR);
    };

    // provide FCtrl.FPending as a flag
    this.getFCtrlFPending = function() {
        return !!(that._packet.FCtrl.readUInt8(0) & constants.FCTRL_FPENDING);
    };

    this.getPHYPayload = function() {
        return that._packet.PHYPayload;
    };

    this.getBuffers = function() {
        return that._packet;
    };

    /*    this.setPlaintextPayload = function(payload) {
     that._plaintextPayload = payload;
     };*/

    /*    this.getPlaintextPayload = function() {
     return that._plaintextPayload;
     };*/

    this.toString = function() {
        var p = that._packet;

        var msg = "";

        if (that.isJoinRequestMessage()) {
            msg += "Message Type = Join Request" + "\n";
            msg += "      AppEUI = " + asHexString(p.AppEUI) + "\n";
            msg += "      DevEUI = " + asHexString(p.DevEUI) + "\n";
            msg += "    DevNonce = " + asHexString(p.DevNonce) + "\n";
        } else if (that.isJoinAcceptMessage()) {
            msg += "Message Type = Join Accept" + "\n";
            msg += "    AppNonce = " + asHexString(p.AppNonce) + "\n";
            msg += "       NetID = " + asHexString(p.NetID) + "\n";
            msg += "     DevAddr = " + asHexString(p.DevAddr) + "\n";
            // TODO & the rest
        } else if (that.isDataMessage()) {
            msg += "Message Type = Data" + "\n";
            msg += "  PHYPayload = " + asHexString(p.PHYPayload).toUpperCase() + "\n";
            msg += "\n";
            msg += "( PHYPayload = MHDR[1] | MACPayload[..] | MIC[4] )\n";
            msg += "        MHDR = " + asHexString(p.MHDR) + "\n";
            msg += "  MACPayload = " + asHexString(p.MACPayload) + "\n";
            msg += "         MIC = " + asHexString(p.MIC) + "\n";
            msg += "\n";
            msg += "( MACPayload = FHDR | FPort | FRMPayload )\n";
            msg += "        FHDR = " + asHexString(p.FHDR) + "\n";
            msg += "       FPort = " + asHexString(p.FPort) + "\n";
            msg += "  FRMPayload = " + asHexString(p.FRMPayload) + "\n";
            msg += "\n";
            msg += "      ( FHDR = DevAddr[4] | FCtrl[1] | FCnt[2] | FOpts[0..15] )\n";
            msg += "     DevAddr = " + asHexString(p.DevAddr) + " (Big Endian)\n";
            msg += "       FCtrl = " + asHexString(p.FCtrl) + "\n"; //TODO as binary?
            msg += "        FCnt = " + asHexString(p.FCnt) + " (Big Endian)\n";
            msg += "       FOpts = " + asHexString(p.FOpts) + "\n";
            msg += "\n";
            msg += "Message Type = " + that.getMType() + "\n";
            msg += "   Direction = " + that.getDir() + "\n";
            msg += "        FCnt = " + that.getFCnt() + "\n";
            msg += "   FCtrl.ACK = " + that.getFCtrlACK() + "\n";
            msg += "   FCtrl.ADR = " + that.getFCtrlADR() + "\n";
        }
        return msg;
    };
}

function asHexString(buf) {
    return buf.toString('hex').toUpperCase();
}<|endoftext|>


<commit_before>var path = require("path")
var assert = require("assert")

process.env.npm_config_prefix = process.cwd()
delete process.env.npm_config_global
delete process.env.npm_config_depth

var npm = process.env.npm_execpath

require("child_process").execFile(process.execPath, [npm, "ls", "--json"], {
    env: process.env, cwd: process.cwd() },
    function (err, stdout, stderr) {

  if (err) throw err

  var actual = JSON.parse(stdout).dependencies
  var expected = require("./npm-ls.json")
  console.error(JSON.stringify(actual, null, 2))
  console.error(JSON.stringify(expected, null, 2))

  assert.deepEqual(actual, expected)
})
<commit_msg>test: Resolved url doesn't matter for peerDeps test<commit_after>var path = require("path")
var assert = require("assert")

process.env.npm_config_prefix = process.cwd()
delete process.env.npm_config_global
delete process.env.npm_config_depth

var npm = process.env.npm_execpath

require("child_process").execFile(process.execPath, [npm, "ls", "--json"], {
    env: process.env, cwd: process.cwd() },
    function (err, stdout, stderr) {

  if (err) throw err

  var actual = JSON.parse(stdout).dependencies
  var expected = require("./npm-ls.json")

  // resolved url doesn't matter
  delete actual.dict.resolved
  delete expected.dict.resolved

  console.error(JSON.stringify(actual, null, 2))
  console.error(JSON.stringify(expected, null, 2))

  assert.deepEqual(actual, expected)
})
<|endoftext|>


<commit_before>'use strict';

var url = require('url');
var _ = require('lodash');
var Backbone = global.Backbone || (global.Backbone = require('backbone'));
require('backbone-relational');
require('backbone-validation');

// <https://github.com/balderdashy/waterline-docs/blob/master/models.md#data-types-and-attribute-properties>
var instanceMethods = {
  defaults: function () {
    return _.cloneDeep(this._defaults);
  },
  validation: function () {
    return _.cloneDeep(this._validation);
  }
};

/**
 * Parse a schema into Backbone Relational models.
 * @param schema - list of backbone model definitions ordered by inheritance depth
 * @param [ns] - existing namespace
 */
exports.parse = function (schema, _ns) {
  return _.transform(_.sortBy(schema, 'index'), function (ns, model) {
    var superModel = _.isString(model.extend) ? ns[model.extend] : Backbone.RelationalModel;
    var superCollection = _.isString(model.extend) ? ns[model.extend + 'Collection'] : Backbone.Collection;

    ns[model.name] = superModel.extend(_.extend(model, _ns[model.name], instanceMethods));
    ns[model.name + 'Collection'] = superCollection.extend(_.extend({
      model: ns[model.name],
      url: url.resolve(_ns.url.protocol + '//' + _ns.url.host, model.urlRoot),
    }, _ns[model.name + 'Collection']));
  });
};
<commit_msg>enable credentials<commit_after>'use strict';

var url = require('url');
var _ = require('lodash');
var Backbone = global.Backbone || (global.Backbone = require('backbone'));
require('backbone-relational');
require('backbone-validation');

// <https://github.com/balderdashy/waterline-docs/blob/master/models.md#data-types-and-attribute-properties>
var instanceMethods = {
  defaults: function () {
    return _.cloneDeep(this._defaults);
  },
  validation: function () {
    return _.cloneDeep(this._validation);
  }
};

Backbone._sync = Backbone.sync;
Backbone.sync = function (method, model, options) {
  return Backbone._sync.call(this, method, model, _.extend({
    xhrFields: {
      withCredentials: true
    }
  }, options));
};

/**
 * Parse a schema into Backbone Relational models.
 * @param schema - list of backbone model definitions ordered by inheritance depth
 * @param [ns] - existing namespace
 */
exports.parse = function (schema, _ns) {
  _ns.url || (_ns.url = { });
  return _.transform(_.sortBy(schema, 'index'), function (ns, model) {
    var superModel = _.isString(model.extend) ? ns[model.extend] : Backbone.RelationalModel;
    var superCollection = _.isString(model.extend) ? ns[model.extend + 'Collection'] : Backbone.Collection;

    ns[model.name] = superModel.extend(_.extend(model, _ns[model.name], instanceMethods));
    ns[model.name + 'Collection'] = superCollection.extend(_.extend({
      model: ns[model.name],
      url: _ns.url.host ? url.resolve(_ns.url.protocol + '//' + _ns.url.host, model.urlRoot) : model.urlRoot,
    }, _ns[model.name + 'Collection']));
  });
};
<|endoftext|>


<commit_before>//jshint esnext:true
var Message = require('../models/message');

function Parser (msg){
    if (!(this instanceof Parser)) {
        return new Parser(msg);
    }
    this.foundAny = false;
    this.foundTriggers = [];
    this.msg = msg;
    this.triggers = [
        new RegExp("Emory","g")
    ]; // = app.registeredTriggers();
    this.findTriggers();
    this.saveMessage();
}

Parser.prototype.findTriggers = function (){
    this.triggers.map(function(e){
        if (this.msg.text.match(e)){
            this.foundTriggers.push(e);
            this.foundAny = true;
        }
    }.bind(this));
};

Parser.prototype.saveMessage = function (){
    var message;

    if (!this.foundAny){
        message = new Message(this.msg);
        message.save(function(err, message){
            if (err){
                console.log(err);
            } else {
                console.log('new message', message);
                this.message = message;
            }
        }.bind(this));
    }
};
<commit_msg>parser<commit_after>//jshint esnext:true
var Message = require('../models/message');

module.exports = Parser;

function Parser (msg){
    if (!(this instanceof Parser)) {
        return new Parser(msg);
    }
    this.foundAny = false;
    this.foundTriggers = [];
    this.msg = msg;
    this.triggers = [
        new RegExp("Emory","g")
    ]; // = app.registeredTriggers();
    this.findTriggers();
    this.saveMessage();
}

Parser.prototype.findTriggers = function (){
    this.triggers.map(function(e){
        if (this.msg.text.match(e)){
            this.foundTriggers.push(e);
            this.foundAny = true;
        }
    }.bind(this));
};

Parser.prototype.saveMessage = function (){
    var message;

    if (!this.foundAny){
        message = new Message(this.msg);
        message.save(function(err, message){
            if (err){
                console.log(err);
            } else {
                console.log('new message', message);
                this.message = message;
            }
        }.bind(this));
    }
};
<|endoftext|>


<commit_before>function form_iframe_get(action, callback){
	var time = new Date().getTime()
	,c_iframe = document.createElement('iframe')
	,c_form = document.createElement('form');
	c_iframe.className = 'hidden';
	c_iframe.name = 'request_iframe_'+time;
	c_form.className = 'hidden';
	c_form.id = 'request_form_'+time;
	c_form.method='get';
	c_form.target=c_iframe.name;
	c_form.action=action;

	document.body.appendChild(c_iframe);
	document.body.appendChild(c_form);
	c_form.submit();

	var loadedDo = function(){
		callback(c_iframe.contentWindow.document.body.innerHTML);
		c_form.parentNode.removeChild(c_form);
		c_iframe.parentNode.removeChild(c_iframe);
		adjust_right();
	};

	if(c_iframe.attachEvent){
		c_iframe.attachEvent("onload", function(){
			loadedDo();
		});
	} else {
		c_iframe.onload = function(){
			loadedDo();
		};
	}
}

function adjust_right(){
	var left_w = document.getElementsByClassName('left')[0].offsetWidth
	,body_w = document.body.clientWidth, right = document.getElementsByClassName('right')[0];
	right.style.width = body_w - left_w - 20 + "px";
	lmlSmartFloat();
}

function eid(i){
	return document.getElementById(i);
}

function on(o,e,c){
	if(window.addEventListener){
		o.addEventListener(e, c, false);
	}else if(window.attachEvent){
		o.attachEvent('on'+e, c);
	}
}

function show_info(v, f, w, s){
	var a = $("<div/>")
	.attr({"style":"border:1px solid green;background:#fff;z-index:10000;-webkit-box-shadow:0 0 8px 10px #e9d9e9;"
		+"-moz-box-shadow:0 0 8px 10px #e9d9e9;box-shadow:0 0 8px 10px #e9d9e9;"
		+"position:fixed;_position:absolute;_bottom:auto;padding:5px;top:-42px;left:50%;text-align:center;line-height:30px;border-radius:5px;"})
	.css({"opacity":.5})
	.html(v),f=f||200,s=s||1000,w=w||1000;
	$(document.body).append(
		a.animate({"top":"100px","opacity":1}, f)
		.delay(w)
		.animate({"top":"-42px","opacity":.5}, s)
		.queue(function(){a.remove()}));
	a.css({'margin-left': -a.width()/2+'px'});
};

function create_tab(title, content){
	var tags_title = $('.tabs_title');
	var tags_content = $('.tabs_content');
	var tag_title = $('a[data-tab='+title+']', tags_title);

	$(tags_title).children('a').removeClass('bbw');
	$(tags_content).children('div').addClass('hidden');

	if(tag_title.length==0){
		$('<a/>').attr('data-tab', title).html(title).addClass('bbw').appendTo(tags_title);
		$('<div/>').attr('data-tab', title).html(content).appendTo(tags_content);
	}else{
		$('a[data-tab='+title+']', tags_title).addClass('bbw');
		$('div[data-tab='+title+']', tags_content).removeClass('hidden').html(content);
	}
	adjust_right();
}

function modify_tab_title(origin_title, new_title){
	var tags_title = $('.tabs_title');
	var tags_content = $('.tabs_content');
	$(tags_title).children('a[data-tab='+origin_title+']').attr({"data-tab":new_title}).html(new_title);
	$(tags_content).children('div[data-tab='+origin_title+']').attr({"data-tab":new_title});
}

function get(u,c){
	$('#topbar').animate({"width":"15%","opacity":"0.5"});
	$.ajax({
		"method":"GET",
		"url":u
	}).done(c)
	.done(function(){
		$('#topbar').stop(true,true).animate({"width":"100%"}).animate({"opacity":"0"}).css({"width":"0%"});
	});
}

function post(u,d,c){
	$('#topbar').animate({"width":"15%","opacity":"0.5"});
	$.ajax({
		"method":"POST",
		"url":u,
		"data":d
	}).done(c)
	.done(function(){
		$('#topbar').stop(true,true).animate({"width":"100%"}).animate({"opacity":"0"}).css({"width":"0%"});
	});
}

lml.registerOnResize(adjust_right);

(function(){
	function getElementOffsetTop(element){
		var actualTop = element.offsetTop
			,offsetParentElement = element.offsetParent;
		if( offsetParentElement == null && element.parentNode ){
			/* when style display is none */
			var parentNode = element.parentNode;
			while( offsetParentElement == null ){
				offsetParentElement = parentNode.offsetParent;
				parentNode = parentNode.parentNode;
				if( !parentNode ){
					break;
				}
			}
		}
		while ( offsetParentElement !== null /* document.body */ ){
			actualTop += (offsetParentElement.offsetTop+offsetParentElement.clientTop);
			offsetParentElement = offsetParentElement.offsetParent;
		}
		return actualTop;
	}

	function getEleStyle(ele, cssName){
		var style = window.getComputedStyle ?
			window.getComputedStyle(ele, "") :
			ele.currentStyle;
		return style[cssName];
	}

	function getPageScrollOffset() {
		var doc = document, w = window;
		var x, y, docEl;

		if ( typeof w.pageYOffset === 'number' ) {
			x = w.pageXOffset;
			y = w.pageYOffset;
		} else {
			docEl = (doc.compatMode && doc.compatMode === 'CSS1Compat') ?
				doc.documentElement : doc.body;
			x = docEl.scrollLeft;
			y = docEl.scrollTop;
		}
		return {x : x, y : y};
	}

	function getWindowViewport() {
		var doc = document, w = window;
		var docEl = (doc.compatMode && doc.compatMode === 'CSS1Compat') ?
				doc.documentElement: doc.body;

		var width = docEl.clientWidth;
		var height = docEl.clientHeight;

		if ( w.innerWidth && width > w.innerWidth ) {
			width = w.innerWidth;
			height = w.innerHeight;
		}

		return {width : width, height : height};
	}

	var left = document.getElementsByClassName('left')[0];
	var right = document.getElementsByClassName('right')[0];
	var rightOriginOffsetTop = parseInt(getElementOffsetTop(right));
	var leftHeight, rightHeight;

	left.originMarginTop = left.style.marginTop.match(/\d+/);
	right.originMarginTop = right.style.marginTop.match(/\d+/);

	var rightFloat = function(){
		leftHeight = left.offsetHeight;
		rightHeight = right.offsetHeight;
		if(leftHeight < rightHeight){
			var temp = right;
			right = left;
			left = temp;
			leftHeight = left.offsetHeight;
			rightHeight = right.offsetHeight;
			left.style.marginTop = (left.originMarginTop || 0) + 'px';
		}

		if(left.originMarginTop){
			left.style.marginTop = left.originMarginTop + 'px';
		}

		var viewportHeight = parseInt(getWindowViewport().height);
		var pageScrollTop = parseInt(getPageScrollOffset().y);
		var leftOffsetTop = parseInt(getElementOffsetTop(left));
		var rightBoxOffsetTop = parseInt(getElementOffsetTop(right));
		var rightBoxMarginTop = parseInt(right.style.marginTop.match(/\d+/));
		var maxMarginTopVal = rightHeight + rightBoxOffsetTop - leftOffsetTop - leftHeight;

		if(maxMarginTopVal > 0){
			var startBack = rightBoxOffsetTop - pageScrollTop;
			if(startBack > 0){
				right.style.marginTop = (pageScrollTop - rightOriginOffsetTop) + 'px';
			}
			return;
		}

		var startFloatDownVal = pageScrollTop + viewportHeight
			- rightOriginOffsetTop - rightHeight;

		if(rightHeight<viewportHeight){
			startFloatDownVal = pageScrollTop - rightOriginOffsetTop;
		}

		if(startFloatDownVal >= 0){
			if(rightBoxMarginTop >= startFloatDownVal){
				if(rightBoxOffsetTop > pageScrollTop && rightBoxOffsetTop > rightOriginOffsetTop){
					var temp = pageScrollTop - rightOriginOffsetTop;
					right.style.marginTop = (temp>0?temp:0) +'px';
				}
				return;
			}
			var actualVal = leftHeight + leftOffsetTop - rightHeight - rightOriginOffsetTop;
			var pos = startFloatDownVal >= actualVal ? actualVal : startFloatDownVal;
			right.style.marginTop =  pos + 'px';
		}

		if(rightBoxOffsetTop > pageScrollTop && rightBoxOffsetTop > rightOriginOffsetTop){
			var temp = pageScrollTop - rightOriginOffsetTop;
			right.style.marginTop = (temp>0?temp:0) +'px';
		}
	};

	if( window.addEventListener ){
		document.addEventListener( 'scroll', rightFloat, false );
	}else if( window.attachEvent ){
		window.attachEvent("onscroll", rightFloat);
	}
	window.lmlSmartFloat = rightFloat;
})();

<commit_msg>to process animate<commit_after>function form_iframe_get(action, callback){
	var time = new Date().getTime()
	,c_iframe = document.createElement('iframe')
	,c_form = document.createElement('form');
	c_iframe.className = 'hidden';
	c_iframe.name = 'request_iframe_'+time;
	c_form.className = 'hidden';
	c_form.id = 'request_form_'+time;
	c_form.method='get';
	c_form.target=c_iframe.name;
	c_form.action=action;

	document.body.appendChild(c_iframe);
	document.body.appendChild(c_form);
	c_form.submit();

	var loadedDo = function(){
		callback(c_iframe.contentWindow.document.body.innerHTML);
		c_form.parentNode.removeChild(c_form);
		c_iframe.parentNode.removeChild(c_iframe);
		adjust_right();
	};

	if(c_iframe.attachEvent){
		c_iframe.attachEvent("onload", function(){
			loadedDo();
		});
	} else {
		c_iframe.onload = function(){
			loadedDo();
		};
	}
}

function adjust_right(){
	var left_w = document.getElementsByClassName('left')[0].offsetWidth
	,body_w = document.body.clientWidth, right = document.getElementsByClassName('right')[0];
	right.style.width = body_w - left_w - 20 + "px";
	lmlSmartFloat();
}

function eid(i){
	return document.getElementById(i);
}

function on(o,e,c){
	if(window.addEventListener){
		o.addEventListener(e, c, false);
	}else if(window.attachEvent){
		o.attachEvent('on'+e, c);
	}
}

function show_info(v, f, w, s){
	var a = $("<div/>")
	.attr({"style":"border:1px solid green;background:#fff;z-index:10000;-webkit-box-shadow:0 0 8px 10px #e9d9e9;"
		+"-moz-box-shadow:0 0 8px 10px #e9d9e9;box-shadow:0 0 8px 10px #e9d9e9;"
		+"position:fixed;_position:absolute;_bottom:auto;padding:5px;top:-42px;left:50%;text-align:center;line-height:30px;border-radius:5px;"})
	.css({"opacity":.5})
	.html(v),f=f||200,s=s||1000,w=w||1000;
	$(document.body).append(
		a.animate({"top":"100px","opacity":1}, f)
		.delay(w)
		.animate({"top":"-42px","opacity":.5}, s)
		.queue(function(){a.remove()}));
	a.css({'margin-left': -a.width()/2+'px'});
};

function create_tab(title, content){
	var tags_title = $('.tabs_title');
	var tags_content = $('.tabs_content');
	var tag_title = $('a[data-tab='+title+']', tags_title);

	$(tags_title).children('a').removeClass('bbw');
	$(tags_content).children('div').addClass('hidden');

	if(tag_title.length==0){
		$('<a/>').attr('data-tab', title).html(title).addClass('bbw').appendTo(tags_title);
		$('<div/>').attr('data-tab', title).html(content).appendTo(tags_content);
	}else{
		$('a[data-tab='+title+']', tags_title).addClass('bbw');
		$('div[data-tab='+title+']', tags_content).removeClass('hidden').html(content);
	}
	adjust_right();
}

function modify_tab_title(origin_title, new_title){
	var tags_title = $('.tabs_title');
	var tags_content = $('.tabs_content');
	$(tags_title).children('a[data-tab='+origin_title+']').attr({"data-tab":new_title}).html(new_title);
	$(tags_content).children('div[data-tab='+origin_title+']').attr({"data-tab":new_title});
}

function get(u,c){
	$('#topbar').stop(true,true)
	.animate({"width":"15%","opacity":"0.5"})
	.animate({"width":"30%","opacity":"0.8"})
	.animate({"width":"80%","opacity":"1"}, {"duration":"10000"});
	$.ajax({
		"method":"GET",
		"url":u
	}).done(c)
	.done(function(){
		$('#topbar').stop(true,true)
		.animate({"width":"100%"}).animate({"opacity":"0"}).css({"width":"0%"});
	});
}

function post(u,d,c){
	$('#topbar').stop(true,true)
	.animate({"width":"15%","opacity":"0.5"})
	.animate({"width":"30%","opacity":"0.8"})
	.animate({"width":"80%","opacity":"1"}, {"duration":"10000"});
	$.ajax({
		"method":"POST",
		"url":u,
		"data":d
	}).done(c)
	.done(function(){
		$('#topbar').stop(true,true)
		.animate({"width":"100%"}).animate({"opacity":"0"}).css({"width":"0%"});
	});
}

lml.registerOnResize(adjust_right);

(function(){
	function getElementOffsetTop(element){
		var actualTop = element.offsetTop
			,offsetParentElement = element.offsetParent;
		if( offsetParentElement == null && element.parentNode ){
			/* when style display is none */
			var parentNode = element.parentNode;
			while( offsetParentElement == null ){
				offsetParentElement = parentNode.offsetParent;
				parentNode = parentNode.parentNode;
				if( !parentNode ){
					break;
				}
			}
		}
		while ( offsetParentElement !== null /* document.body */ ){
			actualTop += (offsetParentElement.offsetTop+offsetParentElement.clientTop);
			offsetParentElement = offsetParentElement.offsetParent;
		}
		return actualTop;
	}

	function getEleStyle(ele, cssName){
		var style = window.getComputedStyle ?
			window.getComputedStyle(ele, "") :
			ele.currentStyle;
		return style[cssName];
	}

	function getPageScrollOffset() {
		var doc = document, w = window;
		var x, y, docEl;

		if ( typeof w.pageYOffset === 'number' ) {
			x = w.pageXOffset;
			y = w.pageYOffset;
		} else {
			docEl = (doc.compatMode && doc.compatMode === 'CSS1Compat') ?
				doc.documentElement : doc.body;
			x = docEl.scrollLeft;
			y = docEl.scrollTop;
		}
		return {x : x, y : y};
	}

	function getWindowViewport() {
		var doc = document, w = window;
		var docEl = (doc.compatMode && doc.compatMode === 'CSS1Compat') ?
				doc.documentElement: doc.body;

		var width = docEl.clientWidth;
		var height = docEl.clientHeight;

		if ( w.innerWidth && width > w.innerWidth ) {
			width = w.innerWidth;
			height = w.innerHeight;
		}

		return {width : width, height : height};
	}

	var left = document.getElementsByClassName('left')[0];
	var right = document.getElementsByClassName('right')[0];
	var rightOriginOffsetTop = parseInt(getElementOffsetTop(right));
	var leftHeight, rightHeight;

	left.originMarginTop = left.style.marginTop.match(/\d+/);
	right.originMarginTop = right.style.marginTop.match(/\d+/);

	var rightFloat = function(){
		leftHeight = left.offsetHeight;
		rightHeight = right.offsetHeight;
		if(leftHeight < rightHeight){
			var temp = right;
			right = left;
			left = temp;
			leftHeight = left.offsetHeight;
			rightHeight = right.offsetHeight;
			left.style.marginTop = (left.originMarginTop || 0) + 'px';
		}

		if(left.originMarginTop){
			left.style.marginTop = left.originMarginTop + 'px';
		}

		var viewportHeight = parseInt(getWindowViewport().height);
		var pageScrollTop = parseInt(getPageScrollOffset().y);
		var leftOffsetTop = parseInt(getElementOffsetTop(left));
		var rightBoxOffsetTop = parseInt(getElementOffsetTop(right));
		var rightBoxMarginTop = parseInt(right.style.marginTop.match(/\d+/));
		var maxMarginTopVal = rightHeight + rightBoxOffsetTop - leftOffsetTop - leftHeight;

		if(maxMarginTopVal > 0){
			var startBack = rightBoxOffsetTop - pageScrollTop;
			if(startBack > 0){
				right.style.marginTop = (pageScrollTop - rightOriginOffsetTop) + 'px';
			}
			return;
		}

		var startFloatDownVal = pageScrollTop + viewportHeight
			- rightOriginOffsetTop - rightHeight;

		if(rightHeight<viewportHeight){
			startFloatDownVal = pageScrollTop - rightOriginOffsetTop;
		}

		if(startFloatDownVal >= 0){
			if(rightBoxMarginTop >= startFloatDownVal){
				if(rightBoxOffsetTop > pageScrollTop && rightBoxOffsetTop > rightOriginOffsetTop){
					var temp = pageScrollTop - rightOriginOffsetTop;
					right.style.marginTop = (temp>0?temp:0) +'px';
				}
				return;
			}
			var actualVal = leftHeight + leftOffsetTop - rightHeight - rightOriginOffsetTop;
			var pos = startFloatDownVal >= actualVal ? actualVal : startFloatDownVal;
			right.style.marginTop =  pos + 'px';
		}

		if(rightBoxOffsetTop > pageScrollTop && rightBoxOffsetTop > rightOriginOffsetTop){
			var temp = pageScrollTop - rightOriginOffsetTop;
			right.style.marginTop = (temp>0?temp:0) +'px';
		}
	};

	if( window.addEventListener ){
		document.addEventListener( 'scroll', rightFloat, false );
	}else if( window.attachEvent ){
		window.attachEvent("onscroll", rightFloat);
	}
	window.lmlSmartFloat = rightFloat;
})();

<|endoftext|>


<commit_before>/*
 * decaffeinate suggestions:
 * DS101: Remove unnecessary use of Array.from
 * DS102: Remove unnecessary code created because of implicit returns
 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
 */
const parse = function(trace) {

  let data;
  let dir = function(ident) {

      if (ident === "=>") {
        return "request";
      }
      if (ident === "<=") {
        return "response";
      }
    };

  // better regex, please, start here:
  // http://scriptular.com/#%5E(%3F%3A%5Ba-z0-9%5D%7B4%7D%3A)%20((%3F%3A%5Ba-z0-9%5D%7B2%7D%20)%7B1%2C16%7D)%7C%7C%7C%7C%7C%7C%7C%7C%5B%2200a0%3A%2011%2022%2033%2044%2055%2066%2077%2088%2099%2010%2011%2012%2013%2014%2015%2016%2017%20%20%7B.%20%20%5C%22headers%5C%22%3A%20%7B%22%5D

  const traceLines = trace.split("\n");
  const dataPattern = /^(?:[a-z0-9]{4}:) ((?:[a-z0-9]{2} ){1,16})/;
  const dirPattern = /^(=>|<=)/;

  // find ASCI bytes in raw lines

  // will contain array of arrays with direction and data
  // e.g [['<=', "47 45 54 20 2f 73 68 6f 70 70 69 6e 67 2d 63 61"]]
  const asciiHexSets = [];
  let lastDir = "";

  for (let line of Array.from(traceLines)) {
    const dirMatch = dirPattern.exec(line);
    if (dirMatch !== null) {
      lastDir = dirMatch[0].trim();
    }

    const dataMatch = dataPattern.exec(line);
    if (dataMatch !== null) {
      data = dataMatch[1].trim();
      asciiHexSets.push([lastDir, data]);
    }
  }

  // split lines by spaces and make array of ASCII hex bytes
  const asciiHexBuffer = {request: [], response: []};
  for (let set of Array.from(asciiHexSets)) {
    data = set[1];
    for (let byte of Array.from(data.split(" "))) {
      asciiHexBuffer[dir(set[0])].push(byte);
    }
  }

  //convert ASCII hex to ASCII integers codes
  const asciiIntBuffer = {request: [], response: []};
  for (dir in asciiHexBuffer) {
    const hexs = asciiHexBuffer[dir];
    for (let hex of Array.from(hexs)) {
      asciiIntBuffer[dir].push(parseInt(`0x${hex}`));
    }
  }

  //convert ACII codes to charactes
  const stringBuffer = {request: [], response: []};
  for (dir in asciiIntBuffer) {
    const codes = asciiIntBuffer[dir];
    for (let code of Array.from(codes)) {
      stringBuffer[dir].push(String.fromCharCode(code));
    }
  }

  const output = {};
  output['request'] = stringBuffer['request'].join("");
  output['response'] = stringBuffer['response'].join("");
  return output;
};

const parseToString = function(trace) {
  const message = parse(trace);
  let output = "";

  const request = [];
  const requestLines = message['request'].split("\r\n");
  for (var line of Array.from(requestLines)) {
    request.push(`> ${line}`);
  }
  output += request.join("\r\n");
  output += "\n";
  output += "\r\n";
  const response = [];
  const responseLines = message['response'].split("\r\n");
  for (line of Array.from(responseLines)) {
    response.push(`< ${line}`);
  }
  output += response.join("\r\n");
  output += "\n";
  return output;
};

const parseBackRequestAndResponseFromString = function(string) {
  const output = {};

  const request = [];
  const stringLines = string.split('\r\n');
  for (var line of Array.from(stringLines)) {
    if (/^> /.test(line)) { request.push(line.replace(/^> /, '')); }
  }

  //removing trailing LF
  output['request'] = request.join('\r\n').replace(/\n$/, '');

  const response = [];
  for (line of Array.from(stringLines)) {
    if (/^< /.test(line)) { response.push(line.replace(/^< /, '')); }
  }

  //removing trailing LF
  output['response'] = response.join('\r\n').replace(/\n$/, '');

  return output;
};

module.exports.parseBackRequestAndResponseFromString = parseBackRequestAndResponseFromString;
module.exports.parseBack = parseBackRequestAndResponseFromString;
module.exports.parseToString = parseToString;
module.exports.parse = parse;
<commit_msg>eslint autofixes for parser.js<commit_after>/*
 * decaffeinate suggestions:
 * DS101: Remove unnecessary use of Array.from
 * DS102: Remove unnecessary code created because of implicit returns
 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
 */
const parse = function (trace) {
  let data;
  let dir = function (ident) {
    if (ident === '=>') {
      return 'request';
    }
    if (ident === '<=') {
      return 'response';
    }
  };

  // better regex, please, start here:
  // http://scriptular.com/#%5E(%3F%3A%5Ba-z0-9%5D%7B4%7D%3A)%20((%3F%3A%5Ba-z0-9%5D%7B2%7D%20)%7B1%2C16%7D)%7C%7C%7C%7C%7C%7C%7C%7C%5B%2200a0%3A%2011%2022%2033%2044%2055%2066%2077%2088%2099%2010%2011%2012%2013%2014%2015%2016%2017%20%20%7B.%20%20%5C%22headers%5C%22%3A%20%7B%22%5D

  const traceLines = trace.split('\n');
  const dataPattern = /^(?:[a-z0-9]{4}:) ((?:[a-z0-9]{2} ){1,16})/;
  const dirPattern = /^(=>|<=)/;

  // find ASCI bytes in raw lines

  // will contain array of arrays with direction and data
  // e.g [['<=', "47 45 54 20 2f 73 68 6f 70 70 69 6e 67 2d 63 61"]]
  const asciiHexSets = [];
  let lastDir = '';

  for (const line of Array.from(traceLines)) {
    const dirMatch = dirPattern.exec(line);
    if (dirMatch !== null) {
      lastDir = dirMatch[0].trim();
    }

    const dataMatch = dataPattern.exec(line);
    if (dataMatch !== null) {
      data = dataMatch[1].trim();
      asciiHexSets.push([lastDir, data]);
    }
  }

  // split lines by spaces and make array of ASCII hex bytes
  const asciiHexBuffer = { request: [], response: [] };
  for (const set of Array.from(asciiHexSets)) {
    data = set[1];
    for (const byte of Array.from(data.split(' '))) {
      asciiHexBuffer[dir(set[0])].push(byte);
    }
  }

  // convert ASCII hex to ASCII integers codes
  const asciiIntBuffer = { request: [], response: [] };
  for (dir in asciiHexBuffer) {
    const hexs = asciiHexBuffer[dir];
    for (const hex of Array.from(hexs)) {
      asciiIntBuffer[dir].push(parseInt(`0x${hex}`));
    }
  }

  // convert ACII codes to charactes
  const stringBuffer = { request: [], response: [] };
  for (dir in asciiIntBuffer) {
    const codes = asciiIntBuffer[dir];
    for (const code of Array.from(codes)) {
      stringBuffer[dir].push(String.fromCharCode(code));
    }
  }

  const output = {};
  output.request = stringBuffer.request.join('');
  output.response = stringBuffer.response.join('');
  return output;
};

const parseToString = function (trace) {
  const message = parse(trace);
  let output = '';

  const request = [];
  const requestLines = message.request.split('\r\n');
  for (var line of Array.from(requestLines)) {
    request.push(`> ${line}`);
  }
  output += request.join('\r\n');
  output += '\n';
  output += '\r\n';
  const response = [];
  const responseLines = message.response.split('\r\n');
  for (line of Array.from(responseLines)) {
    response.push(`< ${line}`);
  }
  output += response.join('\r\n');
  output += '\n';
  return output;
};

const parseBackRequestAndResponseFromString = function (string) {
  const output = {};

  const request = [];
  const stringLines = string.split('\r\n');
  for (var line of Array.from(stringLines)) {
    if (/^> /.test(line)) { request.push(line.replace(/^> /, '')); }
  }

  // removing trailing LF
  output.request = request.join('\r\n').replace(/\n$/, '');

  const response = [];
  for (line of Array.from(stringLines)) {
    if (/^< /.test(line)) { response.push(line.replace(/^< /, '')); }
  }

  // removing trailing LF
  output.response = response.join('\r\n').replace(/\n$/, '');

  return output;
};

module.exports.parseBackRequestAndResponseFromString = parseBackRequestAndResponseFromString;
module.exports.parseBack = parseBackRequestAndResponseFromString;
module.exports.parseToString = parseToString;
module.exports.parse = parse;
<|endoftext|>


<commit_before>'use strict';

var CHAR = Object.freeze({
    ASSIGNMENT: '=',
    BRACE_OPEN: '{',
    BRACE_CLOSE: '}',
    BRACKET_OPEN: '[',
    BRACKET_CLOSE: ']',
    COLON: ':',
    COMMA: ',',
    EMPTY: '',
    EQUALITY: '===',
    NEWLINE: '\n',
    QUESTION: '?',
    QUOTE: '\'',
    SEMICOLON: ';',
    SPACE: ' ',
    TAB: '    ',
    UNDERSCORE: '_'
});

var EXPRESSION = Object.freeze({
    ANON_F_OPEN: '(function () {',
    ANON_F_CLOSE: '})()',
    RETURN: 'return',
    UNDEFINED: 'undefined',
    VAR: 'var'
});

var GRAMMAR = Object.freeze({
    COLOR: 'paint',
    COMMENT: '#',
    CREATE_OPEN: 'create',
    CREATE_CLOSE: 'leave',
    FEEDBACK: 'feedback',
    NAME: 'name',
    NOTE: 'note',
    PROGRAM: 'program',
    USAGE: 'usage'
});

var PROPERTIES = [GRAMMAR.FEEDBACK, GRAMMAR.NAME, GRAMMAR.NOTE, GRAMMAR.PROGRAM, GRAMMAR.USAGE];

var CHILD_COLLECTION = 'children';

var indentLevel;

var pad = function (expression, afterOnly) {
    return (afterOnly ? CHAR.EMPTY : CHAR.SPACE) + expression + CHAR.SPACE;
};

var createIndent = function () {
    var indent = '';

    for (var i = 0; i < indentLevel; i++) {
        indent += CHAR.TAB;
    }

    return indent;
};

var createVesselName = function (keys) {
    keys.shift();

    return keys.join(CHAR.UNDERSCORE);
};

var createPropertyName = function (vessel, property) {
    return vessel + CHAR.UNDERSCORE + property;
};

var createPropertyValue = function (keys) {
    keys.shift();

    return keys.join(' ');
};

var parentVessel;

var getParentVessel = function () {
    return parentVessel[parentVessel.length - 1];
};

var currentVessel;
var has;
var children;

var Parser = function () {
    this.parse = function (line) {
        line = line.replace(/[^A-Z0-9#\,\.\s]/gi, '');

        var keys = line.match(/'[^']+'|\S+/g);
        var statement = '';

        if (keys === null) {
            return line + CHAR.NEWLINE;
        }

        if (keys[0] === GRAMMAR.COMMENT) {
            return CHAR.EMPTY;
        }

        if (keys[0] === GRAMMAR.CREATE_OPEN) {
            var vesselName = createVesselName(keys);

            if (currentVessel) {
                parentVessel.push(currentVessel);
            }

            currentVessel = vesselName;

            if (!has[currentVessel]) {
                has[currentVessel] = {};
            }

            has[currentVessel].name = true;

            if (getParentVessel()) {
                if (typeof children[getParentVessel()] === EXPRESSION.UNDEFINED) {
                    children[getParentVessel()] = [];
                }

                children[getParentVessel()].push(currentVessel);
            }

            if (indentLevel === 0) {
                statement += createIndent() + EXPRESSION.ANON_F_OPEN + CHAR.NEWLINE;
            } else {
                statement += createIndent() + pad(EXPRESSION.VAR, true) + vesselName + pad(CHAR.ASSIGNMENT) + EXPRESSION.ANON_F_OPEN + CHAR.NEWLINE;
            }

            indentLevel++;

            statement += createIndent() + pad(EXPRESSION.VAR, true) + createPropertyName(currentVessel, GRAMMAR.NAME) + pad(CHAR.ASSIGNMENT) + CHAR.QUOTE + vesselName + CHAR.QUOTE + CHAR.SEMICOLON + CHAR.NEWLINE;
        }

        if (keys[0] === GRAMMAR.CREATE_CLOSE) {

            statement += createIndent() + pad(EXPRESSION.RETURN, true) + CHAR.BRACE_OPEN + CHAR.NEWLINE;

            indentLevel++;

            for (var i in PROPERTIES) {
                if (has[currentVessel] && has[currentVessel][PROPERTIES[i]]) {
                    statement += createIndent() + PROPERTIES[i] + pad(CHAR.COLON, true) + currentVessel + CHAR.UNDERSCORE + PROPERTIES[i] + CHAR.COMMA + CHAR.NEWLINE;
                }
            }

            statement += createIndent() + CHILD_COLLECTION + pad(CHAR.COLON, true) + CHAR.BRACKET_OPEN + CHAR.NEWLINE;

            indentLevel++;

            for (var i in children[currentVessel]) {
                statement += createIndent() + children[currentVessel][i] + CHAR.COMMA + CHAR.NEWLINE;
            }

            currentVessel = parentVessel.pop();

            indentLevel--;

            statement += createIndent() + CHAR.BRACKET_CLOSE + CHAR.NEWLINE

            indentLevel--;

            statement += createIndent() + CHAR.BRACE_CLOSE + CHAR.SEMICOLON + CHAR.NEWLINE;

            indentLevel--;

            statement += createIndent() + EXPRESSION.ANON_F_CLOSE + CHAR.SEMICOLON + CHAR.NEWLINE;
        }

        if (keys[0] === GRAMMAR.FEEDBACK || keys[0] === GRAMMAR.NOTE || keys[0] === GRAMMAR.PROGRAM || keys[0] === GRAMMAR.USAGE) {
            var propertyName = keys[0];
            var propertyValue = createPropertyValue(keys);

            has[currentVessel][propertyName] = true;

            statement += createIndent() + pad(EXPRESSION.VAR, true) + createPropertyName(currentVessel, propertyName) + pad(CHAR.ASSIGNMENT) + CHAR.QUOTE + propertyValue + CHAR.QUOTE + CHAR.SEMICOLON + CHAR.NEWLINE;
        }

        return statement;
    };

    this.reset = function () {
        indentLevel = 0;
        parentVessel = [];
        currentVessel = null;
        has = {};
        children = {};
    };

    this.reset();
};

module.exports = Parser;
<commit_msg>Prefer empty value to nonexistent one<commit_after>'use strict';

var CHAR = Object.freeze({
    ASSIGNMENT: '=',
    BRACE_OPEN: '{',
    BRACE_CLOSE: '}',
    BRACKET_OPEN: '[',
    BRACKET_CLOSE: ']',
    COLON: ':',
    COMMA: ',',
    EMPTY: '',
    EQUALITY: '===',
    NEWLINE: '\n',
    QUESTION: '?',
    QUOTE: '\'',
    SEMICOLON: ';',
    SPACE: ' ',
    TAB: '    ',
    UNDERSCORE: '_'
});

var EXPRESSION = Object.freeze({
    ANON_F_OPEN: '(function () {',
    ANON_F_CLOSE: '})()',
    RETURN: 'return',
    UNDEFINED: 'undefined',
    VAR: 'var'
});

var GRAMMAR = Object.freeze({
    COLOR: 'paint',
    COMMENT: '#',
    CREATE_OPEN: 'create',
    CREATE_CLOSE: 'leave',
    FEEDBACK: 'feedback',
    NAME: 'name',
    NOTE: 'note',
    PROGRAM: 'program',
    USAGE: 'usage'
});

var PROPERTIES = [GRAMMAR.FEEDBACK, GRAMMAR.NAME, GRAMMAR.NOTE, GRAMMAR.PROGRAM, GRAMMAR.USAGE];

var CHILD_COLLECTION = 'children';

var indentLevel;

var pad = function (expression, afterOnly) {
    return (afterOnly ? CHAR.EMPTY : CHAR.SPACE) + expression + CHAR.SPACE;
};

var createIndent = function () {
    var indent = '';

    for (var i = 0; i < indentLevel; i++) {
        indent += CHAR.TAB;
    }

    return indent;
};

var createVesselName = function (keys) {
    keys.shift();

    return keys.join(CHAR.UNDERSCORE);
};

var createPropertyName = function (vessel, property) {
    return vessel + CHAR.UNDERSCORE + property;
};

var createPropertyValue = function (keys) {
    keys.shift();

    return keys.join(' ');
};

var parentVessel;

var getParentVessel = function () {
    return parentVessel[parentVessel.length - 1];
};

var currentVessel;
var has;
var children;

var Parser = function () {
    this.parse = function (line) {
        line = line.replace(/[^A-Z0-9#\,\.\s]/gi, '');

        var keys = line.match(/'[^']+'|\S+/g);
        var statement = '';

        if (keys === null) {
            return line + CHAR.NEWLINE;
        }

        if (keys[0] === GRAMMAR.COMMENT) {
            return CHAR.EMPTY;
        }

        if (keys[0] === GRAMMAR.CREATE_OPEN) {
            var vesselName = createVesselName(keys);

            if (currentVessel) {
                parentVessel.push(currentVessel);
            }

            currentVessel = vesselName;

            if (!has[currentVessel]) {
                has[currentVessel] = {};
            }

            has[currentVessel].name = true;

            if (getParentVessel()) {
                if (typeof children[getParentVessel()] === EXPRESSION.UNDEFINED) {
                    children[getParentVessel()] = [];
                }

                children[getParentVessel()].push(currentVessel);
            }

            if (indentLevel === 0) {
                statement += createIndent() + EXPRESSION.ANON_F_OPEN + CHAR.NEWLINE;
            } else {
                statement += createIndent() + pad(EXPRESSION.VAR, true) + vesselName + pad(CHAR.ASSIGNMENT) + EXPRESSION.ANON_F_OPEN + CHAR.NEWLINE;
            }

            indentLevel++;

            statement += createIndent() + pad(EXPRESSION.VAR, true) + createPropertyName(currentVessel, GRAMMAR.NAME) + pad(CHAR.ASSIGNMENT) + CHAR.QUOTE + vesselName + CHAR.QUOTE + CHAR.SEMICOLON + CHAR.NEWLINE;
        }

        if (keys[0] === GRAMMAR.CREATE_CLOSE) {

            statement += createIndent() + pad(EXPRESSION.RETURN, true) + CHAR.BRACE_OPEN + CHAR.NEWLINE;

            indentLevel++;

            for (var i in PROPERTIES) {
                statement += createIndent() + PROPERTIES[i] + pad(CHAR.COLON, true);

                if (has[currentVessel] && has[currentVessel][PROPERTIES[i]]) {
                    statement += currentVessel + CHAR.UNDERSCORE + PROPERTIES[i];
                } else {
                    statement += CHAR.QUOTE + CHAR.QUOTE;
                }

                statement += CHAR.COMMA + CHAR.NEWLINE
            }

            statement += createIndent() + CHILD_COLLECTION + pad(CHAR.COLON, true) + CHAR.BRACKET_OPEN + CHAR.NEWLINE;

            indentLevel++;

            for (var i in children[currentVessel]) {
                statement += createIndent() + children[currentVessel][i] + CHAR.COMMA + CHAR.NEWLINE;
            }

            currentVessel = parentVessel.pop();

            indentLevel--;

            statement += createIndent() + CHAR.BRACKET_CLOSE + CHAR.NEWLINE

            indentLevel--;

            statement += createIndent() + CHAR.BRACE_CLOSE + CHAR.SEMICOLON + CHAR.NEWLINE;

            indentLevel--;

            statement += createIndent() + EXPRESSION.ANON_F_CLOSE + CHAR.SEMICOLON + CHAR.NEWLINE;
        }

        if (keys[0] === GRAMMAR.FEEDBACK || keys[0] === GRAMMAR.NOTE || keys[0] === GRAMMAR.PROGRAM || keys[0] === GRAMMAR.USAGE) {
            var propertyName = keys[0];
            var propertyValue = createPropertyValue(keys);

            has[currentVessel][propertyName] = true;

            statement += createIndent() + pad(EXPRESSION.VAR, true) + createPropertyName(currentVessel, propertyName) + pad(CHAR.ASSIGNMENT) + CHAR.QUOTE + propertyValue + CHAR.QUOTE + CHAR.SEMICOLON + CHAR.NEWLINE;
        }

        return statement;
    };

    this.reset = function () {
        indentLevel = 0;
        parentVessel = [];
        currentVessel = null;
        has = {};
        children = {};
    };

    this.reset();
};

module.exports = Parser;
<|endoftext|>


<commit_before>/*!
 * Robots
 * Copyright(c) 2011 Eugene Kalinin
 * MIT Licensed
 */

var Rule = require('./rule').Rule
  , Entry = require('./entry').Entry
  , urlparser = require('url')
  , ut = require('./utils');

// Export these to make mocking possible
exports.http = require('http');
exports.https = require('https');

exports.RobotsParser = RobotsParser;

/**
 * Provides a set of methods to read, parse and answer
 * questions about a single robots.txt file.
 *
 * @constructor
 * @param {String} url
 * @param {String} userAgent User-Agent for fetching robots.txt
 */
function RobotsParser (url, options, after_parse) {
  this.entries = [];
  this.sitemaps = [];
  this.defaultEntry = '';
  this.disallowAll = false;
  this.statusCode = -1;
  this.allowAll = false;
  if (null === options){
    this.options = {};
  } else if (typeof options === 'string'){
    this.options = { headers: {userAgent:options}};
  } else if (typeof options === 'object'){
    this.options = options;
  }
  this.options = this.options || {};
  this.options.headers = this.options.headers || {};
  this.options.headers.userAgent = this.options.headers.userAgent || 'Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0';;

  this.setUrl(url, after_parse);
}

/*
 * You can clone a JSON representation (based on a prior created one)
 * to enhance with it functions
 *
 * @param {Object} parserToClone JSON representation
 * @returns {RobotsParser}
 */
exports.clone = clone = function(parserToClone) {
  var parser = new RobotsParser ();
  var parseEntry = function(value){
    var defaultEntry = new Entry();
    for(var sub in value){
      var setValue;
      if(sub === "rules"){
        var rules = [];
        var orgRules = value[sub];
        for(var rule in orgRules){
          rules.push(new Rule(orgRules[rule].path, orgRules[rule].allowance));
        }
        setValue = rules;
      }else{
        setValue = value[sub];
      }
      defaultEntry[sub] = setValue;
    }
    return defaultEntry;
  };
  for(var key in parserToClone){
    var value;
    var orgValue = parserToClone[key];
    if(key === "defaultEntry"){
      value = parseEntry(orgValue);
    }else if(key === "entries"){
      var entries = [];
      for (var entry in orgValue){
        entries.push(parseEntry(orgValue[entry]));
      }
      value = entries;
    }else{
      value = orgValue;
    }
    parser[key] = value;
  }
  return parser;
};

/**
 * Sets the URL referring to a robots.txt file
 *
 * @param {String} url
 * @param {String} read   Optional, default=true. Immediate read robots.txt
 *                        if read is a callback function, pass it through to this.read
 */
RobotsParser.prototype.setUrl = function(url, read) {
  this.url = url;
  if(url) {
    if(read === undefined || read === null || read === true) {
      this.read();
    } else if(typeof(read) === "function") {
      this.read(read)
    }
  }
};

/**
 * Reads the robots.txt URL and feeds it to the parser
 *
 * @param {Function} callback called after remote robots.txt is downloaded and parsed
 */
RobotsParser.prototype.read = function(after_parse) {
  var self = this;
  var url = urlparser.parse(this.url);
  var port;
  var protocol;
  if (url.protocol == 'https:') {
    protocol = exports.https;
  } else {
    protocol = exports.http;
  }

  var request_args = {
      'hostname': url.hostname,
      'port': url.port || port,
      'method': 'GET',
      'path': url.pathname
  };
  // apply options to the request args
  for (var x in this.options){
    request_args[x] = this.options[x];
  }
  ut.d('RobotsParser.read: requesting: ' + JSON.stringify(request_args))
  var request = protocol.request(request_args);

  ut.d('RobotsParser.read: start ...');
  if(typeof(after_parse) !== "function") {
    after_parse = function(obj, success) { };
  }
  self.chunks = [];
  request.on('response', function(resp) {
    ut.d('RobotsParser.read: get response, code: '+resp.statusCode);

    self.statusCode = resp.statusCode;

    if ( [401, 403].indexOf(resp.statusCode) > -1 ) {
      ut.d('RobotsParser.read: set disallowAll');
      self.disallowAll = true;
      resp.resume();
      after_parse(self, false);
    }
    else if (resp.statusCode >= 400) {
      ut.d('RobotsParser.read: set allowAll');
      self.allowAll = true;
      resp.resume();
      after_parse(self, false);
    }
    else if ([301, 302].indexOf(resp.statusCode) > -1) {
      // redirect
      var redirect_url = urlparser.resolve(
          self.url,
          resp.headers.location
      )
      resp.resume();
      self.setUrl(redirect_url, after_parse);
    }
    else {
      resp.setEncoding('utf8');
      resp.on('data', function (chunk) {
        ut.d('RobotsParser.read: reads robots.txt');
        self.chunks.push(chunk);
      });
      resp.on('end', function(chunk) {
        self.chunks.push(chunk);
        var allchunks = self.chunks.join('');
        ut.d('RobotsParser.end: allchunks - ' + allchunks);
        self.parse(allchunks.split(/\r\n|\r|\n/));
        after_parse(self, true);
      });
    }

  });

  request.on('error', function(error) {
    ut.d('RobotsParser.read: request error: ' + error)
    self.error = error;
    after_parse(self, false);
  });

  request.end();
};

/**
 * Adds entry
 *
 * @private
 * @param {Entry} entry
 */
RobotsParser.prototype._addEntry = function(entry) {
  ut.d('Parser._addEntry, entry: '+entry);
  if( entry.userAgents.indexOf('*') > -1 ) {
    // the default entry is considered last
    // the first default entry wins
    if ( !this.defaultEntry ) {
      this.defaultEntry = entry;
    }
  }
  else {
    this.entries.push(entry);
  }
};

/**
 * Parse the input lines from a robots.txt file.
 *
 * We allow that a user-agent: line is not preceded by
 * one or more blank lines.
 *
 * @param {Array} lines Array of rows from robots.txt
 */
RobotsParser.prototype.parse = function(lines) {
  // states:
  //    0: start state
  //    1: saw user-agent line
  //    2: saw an allow or disallow line
  var STATE_START = 0
    , STATE_SAW_AGENT = 1
    , STATE_SAW_ALLOW_OR_DISALLOW = 2
    , state = STATE_START
    , entry = new Entry()
    , line
    , comment
    , field
    , value;

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

    if (!line) {
      if (state === STATE_SAW_AGENT) {
        entry = new Entry();
        state = STATE_START;
      }
      else if (state === STATE_SAW_ALLOW_OR_DISALLOW) {
        this._addEntry(entry);
        entry = new Entry();
        state = STATE_START;
      }
    }

    // remove optional comment and strip line
    comment = line.indexOf('#')
    if (comment > -1) {
      line = line.substring(0, comment);
    }

    // strip line
    line = line.trim();
    // sitemaps hack
    line = line.replace('http:', 'http_').replace('https:', 'https_');
    // find 'field:value'
    line = line.split(':');
    if (line.length !== 2) {
      continue;
    }
    // sitemaps hack
    line[1] = line[1].replace('http_', 'http:').replace('https_', 'https:');

    field = line[0].trim().toLowerCase();
    value = line[1].trim();

    switch(field) {
      case 'user-agent':
        if (state === STATE_SAW_ALLOW_OR_DISALLOW) {
          this._addEntry(entry);
          entry = new Entry();
        }
        entry.userAgents.push(value);
        state = STATE_SAW_AGENT;
        break;

      case 'disallow':
      case 'allow':
        if (state !== STATE_START && ut.is_path_safe(value)) {
          entry.rules.push(new Rule(value, field === 'allow'));
          state = STATE_SAW_ALLOW_OR_DISALLOW;
        }
        break;

      case 'sitemap':
        if (ut.is_path_safe(value)) {
          this.sitemaps.push(value);
        }
        break;

      case 'crawl-delay':
        if (state !== STATE_START) {
          entry.crawl_delay = value;
          state = STATE_SAW_ALLOW_OR_DISALLOW;
        }
    }
  };
  if (state === STATE_SAW_ALLOW_OR_DISALLOW) {
    this._addEntry(entry);
  }
  return this;
};

/**
 * Using the parsed robots.txt decide if userAgent can fetch url.
 *
 * @param {String} userAgent
 * @param {String} url
 * @return {Boolean}
 *
 */
RobotsParser.prototype.canFetchSync = function(userAgent, url) {
  var url = url || '/'
    , entry;

  ut.d('Parser.canFetch: url:'+url);

  if (this.disallowAll) {
    return false;
  }
  if (this.allowAll) {
    return true;
  }

  // search for given user agent matches
  // the first match counts
  for (var i = 0; i < this.entries.length; i++) {
    entry = this.entries[i];
    if (entry.appliesTo(userAgent)) {
      return entry.allowance(url);
    }
  };

  // try the default entry last
  if (this.defaultEntry) {
    return this.defaultEntry.allowance(url);
  }

  // agent not found ==> access granted
  return true;
};

/**
 * Using the parsed robots.txt decide if userAgent can fetch url.
 *
 * @param {String}    userAgent
 * @param {String}    url
 * @param {Function}  callback    function (access, url, rule) { ... }
 *
 */
RobotsParser.prototype.canFetch = function(userAgent, url, callback) {
  var self = this
    , url = url || '/'
    , entry;

  process.nextTick( function () {
    if (self.disallowAll) {
      callback(false, url, {
        type:       "statusCode",
        statusCode: self.statusCode});
      return;
    }

    if (self.allowAll) {
      callback(true, url, {
        type:       "statusCode",
        statusCode: self.statusCode});
      return;
    }

    // search for given user agent matches
    // the first match counts
    for (var i = 0; i < self.entries.length; i++) {
      entry = self.entries[i];
      if (entry.appliesTo(userAgent)) {
        callback(entry.allowance(url), url, {
          type:     "entry",
          entry:    entry});
        return;
      }
    };

    // try the default entry last
    if (self.defaultEntry) {
      callback(self.defaultEntry.allowance(url), url, {
          type:     "defaultEntry",
          entry:    self.defaultEntry});
      return;
    }

    // agent not found ==> access granted
    callback(true, url, {type: "noRule"});
  });
};

/**
 * Using the parsed robots.txt decide if userAgent has a specified
 * Crawl-delay.
 *
 * @param {String}    userAgent
 * @param {String}    url
 * @param {Function}  callback    function (access, url, rule) { ... }
 * @return {Number} or undefined
 *
 */
RobotsParser.prototype.getCrawlDelay = function (userAgent) {
  var entry;
  for (var i = 0; i < this.entries.length; i++) {
    entry = this.entries[i];
    if (entry.appliesTo(userAgent) && (entry.crawl_delay != null)) {
      return entry.crawl_delay;
    }
  }
  return this.defaultEntry.crawl_delay;
};

/**
 * Returns a string representation of this RobotsParser
 *
 */
RobotsParser.prototype.toString = function() {
    var res = [];
    res.push("<Parser: Crawler User Agent: " + this.userAgent);
    res.push(this.defaultEntry.toString());
    for (var i in this.entries) {
        res.push(this.entries[i].toString());
    };
    res.push(">");
    return res.join('\n');
};

/**
 * Returns a (shorter) string representation of this RobotsParser
 *
 */
RobotsParser.prototype.toStringLite = function() {
  var res = [];
  var agent_names = [];
  function list_agent_names(entry) {
    var names = [];
    for(var j in entry.userAgents) {
      names.push(entry.userAgents[j]);
    }
    return names;
  }
  agent_names = list_agent_names(this.defaultEntry);
  for (var i in this.entries) {
    agent_names = agent_names.concat(list_agent_names(this.entries[i]));
  };
  var output = "<Parser: ";
  output += "Crawler User Agent is `" + this.userAgent + "`, ";
  output += "Listed Robot Agents: `" + agent_names.join('`, `');
  output += "`>";
  return output;
};

/**
 * Returns sitemaps from parsed robots.txt.
 *
 * @param {Function}  callback    function (sitemaps) { ... }
 *
 */
RobotsParser.prototype.getSitemaps = function(callback) {
  var self = this;

  process.nextTick( function () {
    callback(self.sitemaps);
  });
};
<commit_msg>Allow port indication in sitemap urls<commit_after>/*!
 * Robots
 * Copyright(c) 2011 Eugene Kalinin
 * MIT Licensed
 */

var Rule = require('./rule').Rule
  , Entry = require('./entry').Entry
  , urlparser = require('url')
  , ut = require('./utils');

// Export these to make mocking possible
exports.http = require('http');
exports.https = require('https');

exports.RobotsParser = RobotsParser;

/**
 * Provides a set of methods to read, parse and answer
 * questions about a single robots.txt file.
 *
 * @constructor
 * @param {String} url
 * @param {String} userAgent User-Agent for fetching robots.txt
 */
function RobotsParser (url, options, after_parse) {
  this.entries = [];
  this.sitemaps = [];
  this.defaultEntry = '';
  this.disallowAll = false;
  this.statusCode = -1;
  this.allowAll = false;
  if (null === options){
    this.options = {};
  } else if (typeof options === 'string'){
    this.options = { headers: {userAgent:options}};
  } else if (typeof options === 'object'){
    this.options = options;
  }
  this.options = this.options || {};
  this.options.headers = this.options.headers || {};
  this.options.headers.userAgent = this.options.headers.userAgent || 'Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0';;

  this.setUrl(url, after_parse);
}

/*
 * You can clone a JSON representation (based on a prior created one)
 * to enhance with it functions
 *
 * @param {Object} parserToClone JSON representation
 * @returns {RobotsParser}
 */
exports.clone = clone = function(parserToClone) {
  var parser = new RobotsParser ();
  var parseEntry = function(value){
    var defaultEntry = new Entry();
    for(var sub in value){
      var setValue;
      if(sub === "rules"){
        var rules = [];
        var orgRules = value[sub];
        for(var rule in orgRules){
          rules.push(new Rule(orgRules[rule].path, orgRules[rule].allowance));
        }
        setValue = rules;
      }else{
        setValue = value[sub];
      }
      defaultEntry[sub] = setValue;
    }
    return defaultEntry;
  };
  for(var key in parserToClone){
    var value;
    var orgValue = parserToClone[key];
    if(key === "defaultEntry"){
      value = parseEntry(orgValue);
    }else if(key === "entries"){
      var entries = [];
      for (var entry in orgValue){
        entries.push(parseEntry(orgValue[entry]));
      }
      value = entries;
    }else{
      value = orgValue;
    }
    parser[key] = value;
  }
  return parser;
};

/**
 * Sets the URL referring to a robots.txt file
 *
 * @param {String} url
 * @param {String} read   Optional, default=true. Immediate read robots.txt
 *                        if read is a callback function, pass it through to this.read
 */
RobotsParser.prototype.setUrl = function(url, read) {
  this.url = url;
  if(url) {
    if(read === undefined || read === null || read === true) {
      this.read();
    } else if(typeof(read) === "function") {
      this.read(read)
    }
  }
};

/**
 * Reads the robots.txt URL and feeds it to the parser
 *
 * @param {Function} callback called after remote robots.txt is downloaded and parsed
 */
RobotsParser.prototype.read = function(after_parse) {
  var self = this;
  var url = urlparser.parse(this.url);
  var port;
  var protocol;
  if (url.protocol == 'https:') {
    protocol = exports.https;
  } else {
    protocol = exports.http;
  }

  var request_args = {
      'hostname': url.hostname,
      'port': url.port || port,
      'method': 'GET',
      'path': url.pathname
  };
  // apply options to the request args
  for (var x in this.options){
    request_args[x] = this.options[x];
  }
  ut.d('RobotsParser.read: requesting: ' + JSON.stringify(request_args))
  var request = protocol.request(request_args);

  ut.d('RobotsParser.read: start ...');
  if(typeof(after_parse) !== "function") {
    after_parse = function(obj, success) { };
  }
  self.chunks = [];
  request.on('response', function(resp) {
    ut.d('RobotsParser.read: get response, code: '+resp.statusCode);

    self.statusCode = resp.statusCode;

    if ( [401, 403].indexOf(resp.statusCode) > -1 ) {
      ut.d('RobotsParser.read: set disallowAll');
      self.disallowAll = true;
      resp.resume();
      after_parse(self, false);
    }
    else if (resp.statusCode >= 400) {
      ut.d('RobotsParser.read: set allowAll');
      self.allowAll = true;
      resp.resume();
      after_parse(self, false);
    }
    else if ([301, 302].indexOf(resp.statusCode) > -1) {
      // redirect
      var redirect_url = urlparser.resolve(
          self.url,
          resp.headers.location
      )
      resp.resume();
      self.setUrl(redirect_url, after_parse);
    }
    else {
      resp.setEncoding('utf8');
      resp.on('data', function (chunk) {
        ut.d('RobotsParser.read: reads robots.txt');
        self.chunks.push(chunk);
      });
      resp.on('end', function(chunk) {
        self.chunks.push(chunk);
        var allchunks = self.chunks.join('');
        ut.d('RobotsParser.end: allchunks - ' + allchunks);
        self.parse(allchunks.split(/\r\n|\r|\n/));
        after_parse(self, true);
      });
    }

  });

  request.on('error', function(error) {
    ut.d('RobotsParser.read: request error: ' + error)
    self.error = error;
    after_parse(self, false);
  });

  request.end();
};

/**
 * Adds entry
 *
 * @private
 * @param {Entry} entry
 */
RobotsParser.prototype._addEntry = function(entry) {
  ut.d('Parser._addEntry, entry: '+entry);
  if( entry.userAgents.indexOf('*') > -1 ) {
    // the default entry is considered last
    // the first default entry wins
    if ( !this.defaultEntry ) {
      this.defaultEntry = entry;
    }
  }
  else {
    this.entries.push(entry);
  }
};

/**
 * Parse the input lines from a robots.txt file.
 *
 * We allow that a user-agent: line is not preceded by
 * one or more blank lines.
 *
 * @param {Array} lines Array of rows from robots.txt
 */
RobotsParser.prototype.parse = function(lines) {
  // states:
  //    0: start state
  //    1: saw user-agent line
  //    2: saw an allow or disallow line
  var STATE_START = 0
    , STATE_SAW_AGENT = 1
    , STATE_SAW_ALLOW_OR_DISALLOW = 2
    , state = STATE_START
    , entry = new Entry()
    , line
    , comment
    , field
    , value;

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

    if (!line) {
      if (state === STATE_SAW_AGENT) {
        entry = new Entry();
        state = STATE_START;
      }
      else if (state === STATE_SAW_ALLOW_OR_DISALLOW) {
        this._addEntry(entry);
        entry = new Entry();
        state = STATE_START;
      }
    }

    // remove optional comment and strip line
    comment = line.indexOf('#')
    if (comment > -1) {
      line = line.substring(0, comment);
    }

    // strip line
    line = line.trim();
    // sitemaps hack
    line = line.replace('http:', 'http_').replace('https:', 'https_');
    line = line.replace(/:(\d+\/)/, '__$1');
    // find 'field:value'
    line = line.split(':');
    if (line.length !== 2) {
      continue;
    }
    // sitemaps hack
    line[1] = line[1].replace('http_', 'http:').replace('https_', 'https:').replace(/__(\d+\/)/, ':$1');

    field = line[0].trim().toLowerCase();
    value = line[1].trim();

    switch(field) {
      case 'user-agent':
        if (state === STATE_SAW_ALLOW_OR_DISALLOW) {
          this._addEntry(entry);
          entry = new Entry();
        }
        entry.userAgents.push(value);
        state = STATE_SAW_AGENT;
        break;

      case 'disallow':
      case 'allow':
        if (state !== STATE_START && ut.is_path_safe(value)) {
          entry.rules.push(new Rule(value, field === 'allow'));
          state = STATE_SAW_ALLOW_OR_DISALLOW;
        }
        break;

      case 'sitemap':
        if (ut.is_path_safe(value)) {
          this.sitemaps.push(value);
        }
        break;

      case 'crawl-delay':
        if (state !== STATE_START) {
          entry.crawl_delay = value;
          state = STATE_SAW_ALLOW_OR_DISALLOW;
        }
    }
  };
  if (state === STATE_SAW_ALLOW_OR_DISALLOW) {
    this._addEntry(entry);
  }
  return this;
};

/**
 * Using the parsed robots.txt decide if userAgent can fetch url.
 *
 * @param {String} userAgent
 * @param {String} url
 * @return {Boolean}
 *
 */
RobotsParser.prototype.canFetchSync = function(userAgent, url) {
  var url = url || '/'
    , entry;

  ut.d('Parser.canFetch: url:'+url);

  if (this.disallowAll) {
    return false;
  }
  if (this.allowAll) {
    return true;
  }

  // search for given user agent matches
  // the first match counts
  for (var i = 0; i < this.entries.length; i++) {
    entry = this.entries[i];
    if (entry.appliesTo(userAgent)) {
      return entry.allowance(url);
    }
  };

  // try the default entry last
  if (this.defaultEntry) {
    return this.defaultEntry.allowance(url);
  }

  // agent not found ==> access granted
  return true;
};

/**
 * Using the parsed robots.txt decide if userAgent can fetch url.
 *
 * @param {String}    userAgent
 * @param {String}    url
 * @param {Function}  callback    function (access, url, rule) { ... }
 *
 */
RobotsParser.prototype.canFetch = function(userAgent, url, callback) {
  var self = this
    , url = url || '/'
    , entry;

  process.nextTick( function () {
    if (self.disallowAll) {
      callback(false, url, {
        type:       "statusCode",
        statusCode: self.statusCode});
      return;
    }

    if (self.allowAll) {
      callback(true, url, {
        type:       "statusCode",
        statusCode: self.statusCode});
      return;
    }

    // search for given user agent matches
    // the first match counts
    for (var i = 0; i < self.entries.length; i++) {
      entry = self.entries[i];
      if (entry.appliesTo(userAgent)) {
        callback(entry.allowance(url), url, {
          type:     "entry",
          entry:    entry});
        return;
      }
    };

    // try the default entry last
    if (self.defaultEntry) {
      callback(self.defaultEntry.allowance(url), url, {
          type:     "defaultEntry",
          entry:    self.defaultEntry});
      return;
    }

    // agent not found ==> access granted
    callback(true, url, {type: "noRule"});
  });
};

/**
 * Using the parsed robots.txt decide if userAgent has a specified
 * Crawl-delay.
 *
 * @param {String}    userAgent
 * @param {String}    url
 * @param {Function}  callback    function (access, url, rule) { ... }
 * @return {Number} or undefined
 *
 */
RobotsParser.prototype.getCrawlDelay = function (userAgent) {
  var entry;
  for (var i = 0; i < this.entries.length; i++) {
    entry = this.entries[i];
    if (entry.appliesTo(userAgent) && (entry.crawl_delay != null)) {
      return entry.crawl_delay;
    }
  }
  return this.defaultEntry.crawl_delay;
};

/**
 * Returns a string representation of this RobotsParser
 *
 */
RobotsParser.prototype.toString = function() {
    var res = [];
    res.push("<Parser: Crawler User Agent: " + this.userAgent);
    res.push(this.defaultEntry.toString());
    for (var i in this.entries) {
        res.push(this.entries[i].toString());
    };
    res.push(">");
    return res.join('\n');
};

/**
 * Returns a (shorter) string representation of this RobotsParser
 *
 */
RobotsParser.prototype.toStringLite = function() {
  var res = [];
  var agent_names = [];
  function list_agent_names(entry) {
    var names = [];
    for(var j in entry.userAgents) {
      names.push(entry.userAgents[j]);
    }
    return names;
  }
  agent_names = list_agent_names(this.defaultEntry);
  for (var i in this.entries) {
    agent_names = agent_names.concat(list_agent_names(this.entries[i]));
  };
  var output = "<Parser: ";
  output += "Crawler User Agent is `" + this.userAgent + "`, ";
  output += "Listed Robot Agents: `" + agent_names.join('`, `');
  output += "`>";
  return output;
};

/**
 * Returns sitemaps from parsed robots.txt.
 *
 * @param {Function}  callback    function (sitemaps) { ... }
 *
 */
RobotsParser.prototype.getSitemaps = function(callback) {
  var self = this;

  process.nextTick( function () {
    callback(self.sitemaps);
  });
};
<|endoftext|>