<commit_before>function Module() {
    this.tx_max_len = 35;
    this.tx_data = new Array(this.tx_max_len);

    this.sensor_data = {
        left_infared: 0,
        right_infared: 0,
        motion: 0,
        ultrasonic: 0,
        gas: 0,
        cds: 0,
        tmp: 0,
        vibe: 0
    }

    this.worker_data = {
        tone: 0,
        buz_octave: 0,
        buz_note: 0,
        ultrasonic: 0,
        motion: 0,
        motor_state: 0,
        servo_angle: 0,
        neopixel: {
            first: {
                r: 0,
                g: 0,
                b: 0
            },
            second: {
                r: 0,
                g: 0,
                b: 0
            },
            third: {
                r: 0,
                g: 0,
                b: 0
            },
            fourth: {
                r: 0,
                g: 0,
                b: 0
            },
            fifth: {
                r: 0,
                g: 0,
                b: 0
            },
            sixth: {
                r: 0,
                g: 0,
                b: 0
            },
            seventh: {
                r: 0,
                g: 0,
                b: 0
            },
            eighth: {
                r: 0,
                g: 0,
                b: 0
            }
        },
        outer_motor: 0
    }

    this.sensorValueSize = {
        FLOAT: 2,
        SHORT: 3,
    };

    this.sensorTypes = {
        ALIVE: 0,
        DIGITAL: 1,
        ANALOG: 2,
        ULTRASONIC: 3,
        MOTION: 4,
        TIMER: 5,
    };
}

var TESTINO = {
    TONE: 'tone',
    MOTOR_STATE: 'motorState',
    SERVO_ANGLE: 'servoAngle',
    ULTRASONIC: 'ultrasonic',
    MOTION: 'motion',
    NEOPIXEL: 'neopixel',
    OUTER_MOTOR: 'outerMotor'
}

Module.prototype.init = function(handler, config) {};

Module.prototype.setSerialPort = function(sp) {
    this.sp = sp;
};

Module.prototype.requestInitialData = function() {
    let tx_data = this.tx_data;
    tx_data[0] = 0xff; // 시작 255
    tx_data[1] = 0x23; // 길이 35
    for (let i=2; i < this.tx_max_len-2; i++) {
        tx_data[i] = 0;
    }
    tx_data[33] = 0x0;
    tx_data[34] = 0xa;
    return tx_data;
};

Module.prototype.checkInitialData = function(data, config) {
    return true;
};

Module.prototype.afterConnect = function(that, cb) {
    that.connected = true;
    if (cb) {
        cb('connected');
    }
};

Module.prototype.validateLocalData = function(data) {
    return true;
};

/* 엔트리HW -> 엔트리JS */
Module.prototype.requestRemoteData = function(handler) {
    let sensor_data = this.sensor_data;
    for (let key in sensor_data) {
        handler.write(key, sensor_data[key]);
    }
};

/** 엔트리JS -> 엔트리HW */
Module.prototype.handleRemoteData = function(handler) {
    let worker_data = this.worker_data;
    let new_value;

    if (handler.e(TESTINO.TONE)) {
        new_value = handler.read(TESTINO.TONE);
        if (new_value.data) {
            let value = new_value.data.value;
            worker_data.buz_octave = parseInt(value / 256);
            worker_data.buz_note = (value % 256);
        } else if (new_value === 0) {
            worker_data.buz_octave = 0;
            worker_data.buz_note = 0;
        } else {
            worker_data.buz_octave = 0;
            worker_data.buz_note = 0;
        }
    }

    if (handler.e(TESTINO.MOTOR_STATE)) {
        new_value = handler.read(TESTINO.MOTOR_STATE);

        if (worker_data.motor_state != new_value) {
            worker_data.motor_state = new_value;
        }
    }

    if (handler.e(TESTINO.SERVO_ANGLE)) {
        new_value = handler.read(TESTINO.SERVO_ANGLE);
        if (new_value == 0) {
            new_value = 90;
        } else if (new_value > 130) {
            new_value = 130;
        } else if (new_value < 50) {
            new_value = 50;
        }
        worker_data.servo_angle = new_value;
    }

    if (handler.e(TESTINO.ULTRASONIC)) {
        new_value = handler.read(TESTINO.ULTRASONIC);
        worker_data.ultrasonic = new_value;
    }

    if (handler.e(TESTINO.MOTION)) {
        new_value = handler.read(TESTINO.MOTION);
        worker_data.motion = new_value;
    }

    if (handler.e(TESTINO.NEOPIXEL)) {
        new_value = handler.read(TESTINO.NEOPIXEL);
        if (new_value.data) {

            let red = new_value.data.red;
            let green = new_value.data.green;
            let blue = new_value.data.blue;
            let numAble = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth'];

            if (new_value.data.numStr) {
                let num = new_value.data.numStr;

                if (numAble.includes(num)) {
                    worker_data.neopixel[num].r = red;
                    worker_data.neopixel[num].g = green;
                    worker_data.neopixel[num].b = blue;
                }
            } else {
                for (able in numAble) {
                    worker_data.neopixel[numAble[able]].r = red;
                    worker_data.neopixel[numAble[able]].g = green;
                    worker_data.neopixel[numAble[able]].b = blue;
                }
            }
        } else {
            worker_data.neopixel.first.r = 0;
            worker_data.neopixel.first.g = 0;
            worker_data.neopixel.first.b = 0;
            worker_data.neopixel.second.r = 0;
            worker_data.neopixel.second.g = 0;
            worker_data.neopixel.second.b = 0;
            worker_data.neopixel.third.r = 0;
            worker_data.neopixel.third.g = 0;
            worker_data.neopixel.third.b = 0;
            worker_data.neopixel.fourth.r = 0;
            worker_data.neopixel.fourth.g = 0;
            worker_data.neopixel.fourth.b = 0;
            worker_data.neopixel.fifth.r = 0;
            worker_data.neopixel.fifth.g = 0;
            worker_data.neopixel.fifth.b = 0;
            worker_data.neopixel.sixth.r = 0;
            worker_data.neopixel.sixth.g = 0;
            worker_data.neopixel.sixth.b = 0;
            worker_data.neopixel.seventh.r = 0;
            worker_data.neopixel.seventh.g = 0;
            worker_data.neopixel.seventh.b = 0;
            worker_data.neopixel.eighth.r = 0;
            worker_data.neopixel.eighth.g = 0;
            worker_data.neopixel.eighth.b = 0;
        }
    }

    if (handler.e(TESTINO.OUTER_MOTOR)) {
        new_value = handler.read(TESTINO.OUTER_MOTOR);
        worker_data.outer_motor = new_value;
    }

    this.worker_data = worker_data;
};

/* 엔트리HW -> 교구 */
Module.prototype.requestLocalData = function() {
    let worker_data = this.worker_data
    let tx_data = this.tx_data;
    let check_sum = 0;
    let data_len = 35;

    tx_data[0] = 0xff;
    tx_data[1] = 0x23;
    tx_data[2] = worker_data.buz_octave;
    tx_data[3] = worker_data.buz_note;
    tx_data[4] = worker_data.ultrasonic;
    tx_data[5] = worker_data.motion;
    tx_data[6] = worker_data.motor_state;
    tx_data[7] = worker_data.servo_angle;
    tx_data[8] = worker_data.neopixel.first.r;
    tx_data[9] = worker_data.neopixel.first.g;
    tx_data[10] = worker_data.neopixel.first.b;
    tx_data[11] = worker_data.neopixel.second.r;
    tx_data[12] = worker_data.neopixel.second.g;
    tx_data[13] = worker_data.neopixel.second.b;
    tx_data[14] = worker_data.neopixel.third.r;
    tx_data[15] = worker_data.neopixel.third.g;
    tx_data[16] = worker_data.neopixel.third.b;
    tx_data[17] = worker_data.neopixel.fourth.r;
    tx_data[18] = worker_data.neopixel.fourth.g;
    tx_data[19] = worker_data.neopixel.fourth.b;
    tx_data[20] = worker_data.neopixel.fifth.r;
    tx_data[21] = worker_data.neopixel.fifth.g;
    tx_data[22] = worker_data.neopixel.fifth.b;
    tx_data[23] = worker_data.neopixel.sixth.r;
    tx_data[24] = worker_data.neopixel.sixth.g;
    tx_data[25] = worker_data.neopixel.sixth.b;
    tx_data[26] = worker_data.neopixel.seventh.r;
    tx_data[27] = worker_data.neopixel.seventh.g;
    tx_data[28] = worker_data.neopixel.seventh.b;
    tx_data[29] = worker_data.neopixel.eighth.r;
    tx_data[30] = worker_data.neopixel.eighth.g;
    tx_data[31] = worker_data.neopixel.eighth.b;
    tx_data[32] = worker_data.outer_motor;
    tx_data[34] = 0xa;

    for (let i = 2; i < data_len-2; i++) {
        check_sum += tx_data[i];
    }
    tx_data[data_len-2] = check_sum % 256;

    this.tx_data = tx_data;

    return tx_data;
};

/* 교구 -> 엔트리HW */
Module.prototype.handleLocalData = function(data) {
    let self = this;
    let datas = this.getDataByBuffer(data);
    let sensor_data = this.sensor_data;

    datas.forEach(function(data) {
        if (data.length <= 4 || data[0] !== 255 || data[1] !== 12) {
            return;
        }
        let readData = data.subarray(2, data.length);
        let value;

        switch (readData[0]) {
            case self.sensorValueSize.FLOAT: {
                value = new Buffer(readData.subarray(2, 6)).readFloatLE();
                value = Math.round(value * 100) / 100;
                break;
            }
            case self.sensorValueSize.SHORT: {
                value = new Buffer(readData.subarray(2, 4)).readInt16LE();
                break;
            }
            default: {
                value = 0;
                break;
            }
        }

        let type = readData[readData.length - 1];
        let port = readData[1];

        switch (type) {
            case self.sensorTypes.DIGITAL: {
                switch (port) {
                    case 5: {
                        sensor_data.left_infared = value;
                        break;
                    }
                    case 6: {
                        sensor_data.left_infared = value;
                        break;
                    }
                    case 11: {
                        sensor_data.motion = value;
                        break;
                    }
                    default: {
                        break;
                    }
                }
                break;
            }
            case self.sensorTypes.ANALOG: {
                switch (port) {
                    case 0: {
                        sensor_data.gas = value;
                        break;
                    }
                    case 1: {
                        sensor_data.cds = value;
                        break;
                    }
                    case 2: {
                        sensor_data.tmp = ((value * 5.0 * 100) / 1024.0 - 3.0).toFixed(2);
                        break;
                    }
                    case 3: {
                        sensor_data.vibe = value;
                        break;
                    }
                    default: {
                        break;
                    }
                }
                break;
            }
            case self.sensorTypes.ULTRASONIC: {
                switch (port) {
                    case 13: {
                        sensor_data.ultrasonic = value;
                        break;
                    }
                    default: {
                        break;
                    }
                }
                break;
            }
            default: {
                break;
            }
        }
    })

    self.sensor_data = sensor_data;
};

Module.prototype.getDataByBuffer = function(buffer) {
    let datas = [];
    let lastIndex = 0;
    buffer.forEach(function(value, idx) {
        if (value == 13 && buffer[idx + 1] == 10) {
            datas.push(buffer.subarray(lastIndex, idx));
            lastIndex = idx + 2;
        }
    })
    return datas;
}

Module.prototype.disconnect = function(connect) {
    var self = this;
    connect.close();
    if (self.sp) {
        delete self.sp;
    }
};

Module.prototype.reset = function() {
    this.lastTime = 0;
    this.lastSendTime = 0;
};

module.exports = new Module();
<commit_msg>[-]Fix eslint<commit_after>function Module() {
    this.tx_max_len = 35;
    this.tx_data = new Array(this.tx_max_len);

    this.sensor_data = {
        left_infared: 0,
        right_infared: 0,
        motion: 0,
        ultrasonic: 0,
        gas: 0,
        cds: 0,
        tmp: 0,
        vibe: 0,
    };

    this.worker_data = {
        tone: 0,
        buz_octave: 0,
        buz_note: 0,
        ultrasonic: 0,
        motion: 0,
        motor_state: 0,
        servo_angle: 0,
        neopixel: {
            first: {
                r: 0,
                g: 0,
                b: 0,
            },
            second: {
                r: 0,
                g: 0,
                b: 0,
            },
            third: {
                r: 0,
                g: 0,
                b: 0,
            },
            fourth: {
                r: 0,
                g: 0,
                b: 0,
            },
            fifth: {
                r: 0,
                g: 0,
                b: 0,
            },
            sixth: {
                r: 0,
                g: 0,
                b: 0,
            },
            seventh: {
                r: 0,
                g: 0,
                b: 0,
            },
            eighth: {
                r: 0,
                g: 0,
                b: 0,
            },
        },
        outer_motor: 0,
    };

    this.sensorValueSize = {
        FLOAT: 2,
        SHORT: 3,
    };

    this.sensorTypes = {
        ALIVE: 0,
        DIGITAL: 1,
        ANALOG: 2,
        ULTRASONIC: 3,
        MOTION: 4,
        TIMER: 5,
    };
}

const NEOSPIDER = {
    TONE: 'tone',
    MOTOR_STATE: 'motorState',
    SERVO_ANGLE: 'servoAngle',
    ULTRASONIC: 'ultrasonic',
    MOTION: 'motion',
    NEOPIXEL: 'neopixel',
    OUTER_MOTOR: 'outerMotor',
};

Module.prototype.init = function(handler, config) {};

Module.prototype.setSerialPort = function(sp) {
    this.sp = sp;
};

Module.prototype.requestInitialData = function() {
    const txData = this.tx_data;
    txData[0] = 0xff; // 시작 255
    txData[1] = 0x23; // 길이 35
    for (let i = 2; i < this.tx_max_len - 2; i++) {
        txData[i] = 0;
    }
    txData[33] = 0x0;
    txData[34] = 0xa;
    return txData;
};

Module.prototype.checkInitialData = function(data, config) {
    return true;
};

Module.prototype.afterConnect = function(that, cb) {
    that.connected = true;
    if (cb) {
        cb('connected');
    }
};

Module.prototype.validateLocalData = function(data) {
    return true;
};

/* 엔트리HW -> 엔트리JS */
Module.prototype.requestRemoteData = function(handler) {
    const sensorData = this.sensor_data;
    for (const key in sensorData) {
        handler.write(key, sensorData[key]);
    }
};

/** 엔트리JS -> 엔트리HW */
Module.prototype.handleRemoteData = function(handler) {
    const workerData = this.worker_data;
    let newValue;

    if (handler.e(NEOSPIDER.TONE)) {
        newValue = handler.read(NEOSPIDER.TONE);
        if (newValue.data) {
            const value = newValue.data.value;
            workerData.buz_octave = parseInt(value / 256, 10);
            workerData.buz_note = (value % 256);
        } else if (newValue === 0) {
            workerData.buz_octave = 0;
            workerData.buz_note = 0;
        } else {
            workerData.buz_octave = 0;
            workerData.buz_note = 0;
        }
    }

    if (handler.e(NEOSPIDER.MOTOR_STATE)) {
        newValue = handler.read(NEOSPIDER.MOTOR_STATE);

        if (workerData.motor_state != newValue) {
            workerData.motor_state = newValue;
        }
    }

    if (handler.e(NEOSPIDER.SERVO_ANGLE)) {
        newValue = handler.read(NEOSPIDER.SERVO_ANGLE);
        if (newValue == 0) {
            newValue = 90;
        } else if (newValue > 130) {
            newValue = 130;
        } else if (newValue < 50) {
            newValue = 50;
        }
        workerData.servo_angle = newValue;
    }

    if (handler.e(NEOSPIDER.ULTRASONIC)) {
        newValue = handler.read(NEOSPIDER.ULTRASONIC);
        workerData.ultrasonic = newValue;
    }

    if (handler.e(NEOSPIDER.MOTION)) {
        newValue = handler.read(NEOSPIDER.MOTION);
        workerData.motion = newValue;
    }

    if (handler.e(NEOSPIDER.NEOPIXEL)) {
        newValue = handler.read(NEOSPIDER.NEOPIXEL);
        if (newValue.data) {
            const red = newValue.data.red;
            const green = newValue.data.green;
            const blue = newValue.data.blue;
            const numAble = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth'];

            if (newValue.data.numStr) {
                const num = newValue.data.numStr;

                if (numAble.includes(num)) {
                    workerData.neopixel[num].r = red;
                    workerData.neopixel[num].g = green;
                    workerData.neopixel[num].b = blue;
                }
            } else {
                for (const able in numAble) {
                    workerData.neopixel[numAble[able]].r = red;
                    workerData.neopixel[numAble[able]].g = green;
                    workerData.neopixel[numAble[able]].b = blue;
                }
            }
        } else {
            workerData.neopixel.first.r = 0;
            workerData.neopixel.first.g = 0;
            workerData.neopixel.first.b = 0;
            workerData.neopixel.second.r = 0;
            workerData.neopixel.second.g = 0;
            workerData.neopixel.second.b = 0;
            workerData.neopixel.third.r = 0;
            workerData.neopixel.third.g = 0;
            workerData.neopixel.third.b = 0;
            workerData.neopixel.fourth.r = 0;
            workerData.neopixel.fourth.g = 0;
            workerData.neopixel.fourth.b = 0;
            workerData.neopixel.fifth.r = 0;
            workerData.neopixel.fifth.g = 0;
            workerData.neopixel.fifth.b = 0;
            workerData.neopixel.sixth.r = 0;
            workerData.neopixel.sixth.g = 0;
            workerData.neopixel.sixth.b = 0;
            workerData.neopixel.seventh.r = 0;
            workerData.neopixel.seventh.g = 0;
            workerData.neopixel.seventh.b = 0;
            workerData.neopixel.eighth.r = 0;
            workerData.neopixel.eighth.g = 0;
            workerData.neopixel.eighth.b = 0;
        }
    }

    if (handler.e(NEOSPIDER.OUTER_MOTOR)) {
        newValue = handler.read(NEOSPIDER.OUTER_MOTOR);
        workerData.outer_motor = newValue;
    }

    this.worker_data = workerData;
};

/* 엔트리HW -> 교구 */
Module.prototype.requestLocalData = function() {
    const workerData = this.worker_data;
    const txData = this.tx_data;
    let checkSum = 0;
    const dataLen = 35;

    txData[0] = 0xff;
    txData[1] = 0x23;
    txData[2] = workerData.buz_octave;
    txData[3] = workerData.buz_note;
    txData[4] = workerData.ultrasonic;
    txData[5] = workerData.motion;
    txData[6] = workerData.motor_state;
    txData[7] = workerData.servo_angle;
    txData[8] = workerData.neopixel.first.r;
    txData[9] = workerData.neopixel.first.g;
    txData[10] = workerData.neopixel.first.b;
    txData[11] = workerData.neopixel.second.r;
    txData[12] = workerData.neopixel.second.g;
    txData[13] = workerData.neopixel.second.b;
    txData[14] = workerData.neopixel.third.r;
    txData[15] = workerData.neopixel.third.g;
    txData[16] = workerData.neopixel.third.b;
    txData[17] = workerData.neopixel.fourth.r;
    txData[18] = workerData.neopixel.fourth.g;
    txData[19] = workerData.neopixel.fourth.b;
    txData[20] = workerData.neopixel.fifth.r;
    txData[21] = workerData.neopixel.fifth.g;
    txData[22] = workerData.neopixel.fifth.b;
    txData[23] = workerData.neopixel.sixth.r;
    txData[24] = workerData.neopixel.sixth.g;
    txData[25] = workerData.neopixel.sixth.b;
    txData[26] = workerData.neopixel.seventh.r;
    txData[27] = workerData.neopixel.seventh.g;
    txData[28] = workerData.neopixel.seventh.b;
    txData[29] = workerData.neopixel.eighth.r;
    txData[30] = workerData.neopixel.eighth.g;
    txData[31] = workerData.neopixel.eighth.b;
    txData[32] = workerData.outer_motor;
    txData[34] = 0xa;

    for (let i = 2; i < dataLen - 2; i++) {
        checkSum += txData[i];
    }
    txData[dataLen - 2] = checkSum & 255;

    this.tx_data = txData;

    return txData;
};

/* 교구 -> 엔트리HW */
Module.prototype.handleLocalData = function(data) {
    const datas = this.getDataByBuffer(data);
    const sensorData = this.sensor_data;

    datas.forEach((data) => {
        if (data.length <= 4 || data[0] !== 255 || data[1] !== 12) {
            return;
        }
        const readData = data.subarray(2, data.length);
        let value;

        if (readData.length != 8) {
            return;
        }

        switch (readData[0]) {
            case this.sensorValueSize.FLOAT: {
                value = new Buffer.from(readData.subarray(2, 6)).readFloatLE();
                value = Math.round(value * 100) / 100;
                break;
            }
            case this.sensorValueSize.SHORT: {
                value = new Buffer.from(readData.subarray(2, 4)).readInt16LE();
                break;
            }
            default: {
                value = 0;
                break;
            }
        }

        let checkSum = 0;
        checkSum += readData[0];
        checkSum += readData[1];
        checkSum += parseInt(value, 10);
        checkSum += readData[readData.length - 2];
        checkSum = checkSum & 255;

        if (readData[readData.length - 1] != checkSum) {
            return;
        }

        const type = readData[readData.length - 2];
        const port = readData[1];

        switch (type) {
            case this.sensorTypes.DIGITAL: {
                switch (port) {
                    case 5: {
                        sensorData.left_infared = value;
                        break;
                    }
                    case 6: {
                        sensorData.right_infared = value;
                        break;
                    }
                    case 11: {
                        sensorData.motion = value;
                        break;
                    }
                    default: {
                        break;
                    }
                }
                break;
            }
            case this.sensorTypes.ANALOG: {
                switch (port) {
                    case 0: {
                        sensorData.gas = value;
                        break;
                    }
                    case 1: {
                        sensorData.cds = value;
                        break;
                    }
                    case 2: {
                        sensorData.tmp = (value * 5.0 / 1024.0 / 0.01).toFixed(2);
                        break;
                    }
                    case 3: {
                        sensorData.vibe = value;
                        break;
                    }
                    default: {
                        break;
                    }
                }
                break;
            }
            case this.sensorTypes.ULTRASONIC: {
                switch (port) {
                    case 13: {
                        sensorData.ultrasonic = value;
                        break;
                    }
                    default: {
                        break;
                    }
                }
                break;
            }
            default: {
                break;
            }
        }
    });
    this.sensor_data = sensorData;
};

Module.prototype.getDataByBuffer = function(buffer) {
    const datas = [];
    let lastIndex = 0;
    buffer.forEach((value, idx) => {
        if (value == 13 && buffer[idx + 1] == 10) {
            datas.push(buffer.subarray(lastIndex, idx));
            lastIndex = idx + 2;
        }
    });
    return datas;
};

Module.prototype.disconnect = function(connect) {
    connect.close();
    if (this.sp) {
        delete this.sp;
    }
};

Module.prototype.reset = function() {
    this.lastTime = 0;
    this.lastSendTime = 0;
};

module.exports = new Module();
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const _ = require('lodash');
const entities = require('entities');

const LiquidDocs = JSON.parse(fs.readFileSync('../liquid_doc.json'));
const allItems = new Map();

function formatExampleTag(example) {
  return `\`\`\`liquid title="${example.name}"
${example.text}
\`\`\``;
}

function formatParamTag(tag) {
  return `#### \`${tag.name}\` ${tag?.types ? `(${tag.types.map(formatTypeName).join(', ')})` : ''}

${tag?.text ?? ''}`;
}

function formatParamTags(tags) {
  const sortedParams = _.sortBy(tags, (tag) => tag.name);
  return sortedParams.map((tag) => formatParamTag(tag)).join('\n\n');
}

function linkifyTypeName(typeName) {
  const item = allItems.get(typeName);
  if (item) {
    return `<a href="/docs/liquid/${item.itemType()}s/${item.filename()}">${entities.encodeHTML(typeName)}</a>`;
  } else {
    return entities.encodeHTML(typeName);
  }
}

function formatTypeName(typeName) {
  if (allItems.has(typeName)) {
    return `<code>${linkifyTypeName(typeName)}</code>`;
  }

  return `<code>${typeName.replace(/\<(\w+)\>/g, (match, p1) => `<${linkifyTypeName(p1)}>`)}</code>`;
}

function formatMethodDoc(method) {
  const returnTag = method.tags.find((tag) => tag.tag_name === 'return');
  const exampleTags = method.tags.filter((tag) => tag.tag_name === 'example');

  return `#### \`${method.name}\` ${returnTag?.types ? `(${returnTag.types.map(formatTypeName).join(', ')})` : ''}

${returnTag?.text ?? ''}

${exampleTags.map(formatExampleTag).join('\n\n')}`;
}

function formatMethodDocs(klass) {
  const visibleMethods = klass.methods.filter((method) => !method.tags.some((tag) => tag.tag_name === 'api'));
  const sortedMethods = _.sortBy(visibleMethods, (method) => method.name);

  return sortedMethods.map((method) => formatMethodDoc(method)).join('\n\n');
}

function formatFrontmatter(id, title) {
  return `---
id: ${id}
title: ${title}
---
`;
}

function formatClass(id, name, klass) {
  const examples = klass.tags.filter((tag) => tag.tag_name === 'example');

  return `${formatFrontmatter(id, name)}

${klass.docstring}

${examples.length > 0 ? '### Examples' : ''}
${examples.map(formatExampleTag).join('\n\n')}`;
}

function formatDrop(id, name, klass) {
  return `${formatClass(id, name, klass)}

### Fields

${formatMethodDocs(klass)}`;
}

function formatFilterMethod(id, name, filterMethod) {
  const params = filterMethod.tags.filter((tag) => tag.tag_name === 'param');
  const returnTag = filterMethod.tags.find((tag) => tag.tag_name === 'return');
  const examples = filterMethod.tags.filter((tag) => tag.tag_name === 'example');

  return `${formatFrontmatter(id, name)}

${filterMethod.docstring}

${params.length > 0 ? '### Parameters' : ''}
${formatParamTags(params)}

${
  returnTag
    ? `### Returns ${returnTag?.types ? `(${returnTag.types.map(formatTypeName).join(', ')})` : ''}\n${returnTag.text}`
    : ''
}

${examples.length > 0 ? '### Examples' : ''}
${examples.map(formatExampleTag).join('\n\n')}`;
}

class DocItem {
  constructor(item, isFilter) {
    this.item = item;
    this.isFilter = isFilter;
  }

  itemType() {
    if (this.isFilter) {
      return 'filter';
    } else if (this.item.superclasses.includes('Liquid::Drop')) {
      return 'drop';
    } else if (this.item.superclasses.includes('Liquid::Tag')) {
      return 'tag';
    } else if (this.item.superclasses.includes('Liquid::Block')) {
      return 'block';
    }
  }

  identifier() {
    if (this.itemType() === 'drop' || this.itemType() === 'filter') {
      return this.item.name;
    } else if (this.itemType() === 'tag' || this.itemType() === 'block') {
      return this.item.tags.find((tag) => tag.tag_name === 'liquid_tag_name')?.text;
    }
  }

  filename() {
    return _.kebabCase(this.identifier());
  }

  outputPath() {
    if (this.itemType() === 'drop') {
      return `./docs/liquid/drops/${this.filename()}.mdx`;
    } else if (this.itemType() === 'block') {
      return `./docs/liquid/blocks/${this.filename()}.mdx`;
    } else if (this.itemType() === 'tag') {
      return `./docs/liquid/tags/${this.filename()}.mdx`;
    } else if (this.itemType() === 'filter') {
      return `./docs/liquid/filters/${this.filename()}.mdx`;
    }
  }

  format() {
    if (this.itemType() === 'drop') {
      return formatDrop(this.filename(), this.identifier(), this.item);
    } else if (this.itemType() === 'block' || this.itemType() === 'tag') {
      return formatClass(this.filename(), this.identifier(), this.item);
    } else {
      return formatFilterMethod(this.filename(), this.identifier(), this.item);
    }
  }

  write() {
    const outputPath = this.outputPath();
    if (outputPath == null) {
      console.warn(`WARNING: couldn't determine output path for ${this.item.name}`);
      return;
    }

    fs.mkdirSync(path.dirname(outputPath), { recursive: true });
    fs.writeFileSync(outputPath, this.format());
  }
}

console.log('Scanning JSON...');

LiquidDocs.classes.forEach((klass) => {
  const item = new DocItem(klass, false);
  allItems.set(item.identifier(), item);
});

LiquidDocs.filter_methods.forEach((filter) => {
  const item = new DocItem(filter, true);
  allItems.set(item.identifier(), item);
});

console.log('Writing category metadata...');
[
  ['drops', 'Drops'],
  ['tags', 'Tags'],
  ['blocks', 'Blocks'],
  ['filters', 'Filters'],
].forEach(([subdir, categoryName]) => {
  fs.mkdirSync(`./docs/liquid/${subdir}`, { recursive: true });
  fs.writeFileSync(`./docs/liquid/${subdir}/_category_.yml`, `label: ${categoryName}\n`);
});

console.log('Writing sidebar schema...');
fs.writeFileSync(
  './docs/liquid/sidebar-schema.js',
  `module.exports = {
  schemaSidebar: [
    {
      type: 'autogenerated',
      dirName: 'liquid',
    },
  ],
};`,
);

console.log('Writing docs...');
allItems.forEach((item) => {
  item.write();
});
<commit_msg>Add "see" tag handling<commit_after>const fs = require('fs');
const path = require('path');
const _ = require('lodash');
const entities = require('entities');

const LiquidDocs = JSON.parse(fs.readFileSync('../liquid_doc.json'));
const allItems = new Map();

function formatExampleTag(example) {
  return `\`\`\`liquid title="${example.name}"
${example.text}
\`\`\``;
}

function formatParamTag(tag) {
  return `#### \`${tag.name}\` ${tag?.types ? `(${tag.types.map(formatTypeName).join(', ')})` : ''}

${tag?.text ?? ''}`;
}

function formatSeeTag(tag) {
  return `* ${tag.text}: ${tag.name}`;
}

function formatParamTags(tags) {
  const sortedParams = _.sortBy(tags, (tag) => tag.name);
  return sortedParams.map((tag) => formatParamTag(tag)).join('\n\n');
}

function linkifyTypeName(typeName) {
  const item = allItems.get(typeName);
  if (item) {
    return `<a href="/docs/liquid/${item.itemType()}s/${item.filename()}">${entities.encodeHTML(typeName)}</a>`;
  } else {
    return entities.encodeHTML(typeName);
  }
}

function formatTypeName(typeName) {
  if (allItems.has(typeName)) {
    return `<code>${linkifyTypeName(typeName)}</code>`;
  }

  return `<code>${typeName.replace(/\<(\w+)\>/g, (match, p1) => `<${linkifyTypeName(p1)}>`)}</code>`;
}

function formatMethodDoc(method) {
  const returnTag = method.tags.find((tag) => tag.tag_name === 'return');
  const seeTags = method.tags.filter((tag) => tag.tag_name === 'see');
  const exampleTags = method.tags.filter((tag) => tag.tag_name === 'example');

  return `#### \`${method.name}\` ${returnTag?.types ? `(${returnTag.types.map(formatTypeName).join(', ')})` : ''}

${returnTag?.text ?? ''}

${exampleTags.map(formatExampleTag).join('\n\n')}

${seeTags.length > 0 ? `### See also\n${seeTags.map(formatSeeTag).join('\n')}` : ''}`;
}

function formatMethodDocs(klass) {
  const visibleMethods = klass.methods.filter((method) => !method.tags.some((tag) => tag.tag_name === 'api'));
  const sortedMethods = _.sortBy(visibleMethods, (method) => method.name);

  return sortedMethods.map((method) => formatMethodDoc(method)).join('\n\n');
}

function formatFrontmatter(id, title) {
  return `---
id: ${id}
title: ${title}
---
`;
}

function formatClass(id, name, klass) {
  const examples = klass.tags.filter((tag) => tag.tag_name === 'example');

  return `${formatFrontmatter(id, name)}

${klass.docstring}

${examples.length > 0 ? '### Examples' : ''}
${examples.map(formatExampleTag).join('\n\n')}`;
}

function formatDrop(id, name, klass) {
  return `${formatClass(id, name, klass)}

### Fields

${formatMethodDocs(klass)}`;
}

function formatFilterMethod(id, name, filterMethod) {
  const params = filterMethod.tags.filter((tag) => tag.tag_name === 'param');
  const returnTag = filterMethod.tags.find((tag) => tag.tag_name === 'return');
  const examples = filterMethod.tags.filter((tag) => tag.tag_name === 'example');
  const seeTags = filterMethod.tags.filter((tag) => tag.tag_name === 'see');

  return `${formatFrontmatter(id, name)}

${filterMethod.docstring}

${params.length > 0 ? '### Parameters' : ''}
${formatParamTags(params)}

${
  returnTag
    ? `### Returns ${returnTag?.types ? `(${returnTag.types.map(formatTypeName).join(', ')})` : ''}\n${returnTag.text}`
    : ''
}

${examples.length > 0 ? '### Examples' : ''}
${examples.map(formatExampleTag).join('\n\n')}

${seeTags.length > 0 ? `### See also\n${seeTags.map(formatSeeTag).join('\n')}` : ''}`;
}

class DocItem {
  constructor(item, isFilter) {
    this.item = item;
    this.isFilter = isFilter;
  }

  itemType() {
    if (this.isFilter) {
      return 'filter';
    } else if (this.item.superclasses.includes('Liquid::Drop')) {
      return 'drop';
    } else if (this.item.superclasses.includes('Liquid::Tag')) {
      return 'tag';
    } else if (this.item.superclasses.includes('Liquid::Block')) {
      return 'block';
    }
  }

  identifier() {
    if (this.itemType() === 'drop' || this.itemType() === 'filter') {
      return this.item.name;
    } else if (this.itemType() === 'tag' || this.itemType() === 'block') {
      return this.item.tags.find((tag) => tag.tag_name === 'liquid_tag_name')?.text;
    }
  }

  filename() {
    return _.kebabCase(this.identifier());
  }

  outputPath() {
    if (this.itemType() === 'drop') {
      return `./docs/liquid/drops/${this.filename()}.mdx`;
    } else if (this.itemType() === 'block') {
      return `./docs/liquid/blocks/${this.filename()}.mdx`;
    } else if (this.itemType() === 'tag') {
      return `./docs/liquid/tags/${this.filename()}.mdx`;
    } else if (this.itemType() === 'filter') {
      return `./docs/liquid/filters/${this.filename()}.mdx`;
    }
  }

  format() {
    if (this.itemType() === 'drop') {
      return formatDrop(this.filename(), this.identifier(), this.item);
    } else if (this.itemType() === 'block' || this.itemType() === 'tag') {
      return formatClass(this.filename(), this.identifier(), this.item);
    } else {
      return formatFilterMethod(this.filename(), this.identifier(), this.item);
    }
  }

  write() {
    const outputPath = this.outputPath();
    if (outputPath == null) {
      console.warn(`WARNING: couldn't determine output path for ${this.item.name}`);
      return;
    }

    fs.mkdirSync(path.dirname(outputPath), { recursive: true });
    fs.writeFileSync(outputPath, this.format());
  }
}

console.log('Scanning JSON...');

LiquidDocs.classes.forEach((klass) => {
  const item = new DocItem(klass, false);
  allItems.set(item.identifier(), item);
});

LiquidDocs.filter_methods.forEach((filter) => {
  const item = new DocItem(filter, true);
  allItems.set(item.identifier(), item);
});

console.log('Writing category metadata...');
[
  ['drops', 'Drops'],
  ['tags', 'Tags'],
  ['blocks', 'Blocks'],
  ['filters', 'Filters'],
].forEach(([subdir, categoryName]) => {
  fs.mkdirSync(`./docs/liquid/${subdir}`, { recursive: true });
  fs.writeFileSync(`./docs/liquid/${subdir}/_category_.yml`, `label: ${categoryName}\n`);
});

console.log('Writing sidebar schema...');
fs.writeFileSync(
  './docs/liquid/sidebar-schema.js',
  `module.exports = {
  schemaSidebar: [
    {
      type: 'autogenerated',
      dirName: 'liquid',
    },
  ],
};`,
);

console.log('Writing docs...');
allItems.forEach((item) => {
  item.write();
});
<|endoftext|>


<commit_before>/* eslint-disable */
const {
  createMessageConnection,
  StreamMessageReader,
  StreamMessageWriter,
} = require("vscode-jsonrpc");

const { exec } = require("child_process");

const connection = createMessageConnection(
  new StreamMessageReader(process.stdin),
  new StreamMessageWriter(process.stdout),
);

function getDiagnostics(openedPath) {
  exec(
    `${process.env.ESLINT_BINARY} ${openedPath} -f json`,
    { cwd: process.env.ESLINT_CWD },
    (err, stdout, stderr) => {
      if (stderr) {
        // Note: err is non-null when eslint finds diagnostics in the code
        // (because the executable returns 1)
        // To check for actual execution errors, we need to read stderr
        void err;
        console.error("Error running Eslint: ", stderr.toString());
      } else {
        try {
          const json = JSON.parse(stdout.toString());
          connection.sendNotification("textDocument/publishDiagnostics", {
            uri: openedPath,
            diagnostics: json[0].messages.map(message => ({
              source: "eslint",
              severity: message.severity > 1 ? 1 : 2,
              message: message.message,
              range: {
                start: {
                  line: message.line - 1,
                  character: message.column - 1,
                },
                end: {
                  line: message.endLine - 1,
                  character: message.endColumn - 1,
                },
              },
            })),
          });
        } catch (e) {
          console.error("Error in Eslint Language Server: ", e);
        }
      }
    },
  );
}

connection.onRequest("initialize", async () => {
  connection.onNotification("textDocument/didOpen", didOpenEvent => {
    getDiagnostics(didOpenEvent.textDocument.uri);
  });

  connection.onNotification("textDocument/didSave", didSaveEvent => {
    getDiagnostics(didSaveEvent.textDocument.uri);
  });
});

connection.listen();
<commit_msg>Add regex to check js files in eslint lsp (#471)<commit_after>/* eslint-disable */
const {
  createMessageConnection,
  StreamMessageReader,
  StreamMessageWriter,
} = require("vscode-jsonrpc");

const { exec } = require("child_process");

const path = require("path");

const connection = createMessageConnection(
  new StreamMessageReader(process.stdin),
  new StreamMessageWriter(process.stdout),
);

function isValidEslintFile(filePath) {
  const fileName = path.basename(filePath);
  const jsRegex = /\.jsx?/g;

  return fileName.match(jsRegex) != null;
}

function getDiagnostics(openedPath) {
  if (isValidEslintFile(openedPath))
    exec(
      `${process.env.ESLINT_BINARY} ${openedPath} -f json`,
      { cwd: process.env.ESLINT_CWD },
      (err, stdout, stderr) => {
        if (stderr) {
          // Note: err is non-null when eslint finds diagnostics in the code
          // (because the executable returns 1)
          // To check for actual execution errors, we need to read stderr
          void err;
          console.error("Error running Eslint: ", stderr.toString());
        } else {
          try {
            const json = JSON.parse(stdout.toString());
            connection.sendNotification("textDocument/publishDiagnostics", {
              uri: openedPath,
              diagnostics: json[0].messages.map(message => ({
                source: "eslint",
                severity: message.severity > 1 ? 1 : 2,
                message: message.message,
                range: {
                  start: {
                    line: message.line - 1,
                    character: message.column - 1,
                  },
                  end: {
                    line: message.endLine - 1,
                    character: message.endColumn - 1,
                  },
                },
              })),
            });
          } catch (e) {
            console.error("Error in Eslint Language Server: ", e);
          }
        }
      },
    );
}

connection.onRequest("initialize", async () => {
  connection.onNotification("textDocument/didOpen", didOpenEvent => {
    getDiagnostics(didOpenEvent.textDocument.uri);
  });

  connection.onNotification("textDocument/didSave", didSaveEvent => {
    getDiagnostics(didSaveEvent.textDocument.uri);
  });
});

connection.listen();
<|endoftext|>


<commit_before>module.exports = {
  dependency: {
    platforms: {
      android: {
        componentDescriptors: [
          'NVActionBarComponentDescriptor',
          'NVBarButtonComponentDescriptor',
          'NVBottomAppBarComponentDescriptor',
          'NVBottomSheetComponentDescriptor',
          'NVCollapsingBarComponentDescriptor',
          'NVCoordinatorLayoutComponentDescriptor',
          'NVExtendedFloatingActionButtonComponentDescriptor',
          'NVFloatingActionButtonComponentDescriptor',
          'NVNavigationBarComponentDescriptor',
          'NVNavigationStackComponentDescriptor',
          'NVSceneComponentDescriptor',
          'NVSearchBarComponentDescriptor',
          'NVSharedElementComponentDescriptor',
          'NVStatusBarComponentDescriptor',
          'NVTabBarItemComponentDescriptor',
          'NVTabBarComponentDescriptor',
          'NVTabBarPagerComponentDescriptor',
          'NVTabBarPagerRTLComponentDescriptor',
          'NVTabLayoutComponentDescriptor',
          'NVTabLayoutRTLComponentDescriptor',
          'NVTabNavigationComponentDescriptor',
          'NVTitleBarComponentDescriptor',
          'NVToolbarComponentDescriptor',
        ],
        cmakeListsPath: "../cpp/CMakeLists.txt",
        libraryName: "navigationreactnative"
      },
    },
  },
};<commit_msg>Removed library name now reading from package.json<commit_after>module.exports = {
  dependency: {
    platforms: {
      android: {
        componentDescriptors: [
          'NVActionBarComponentDescriptor',
          'NVBarButtonComponentDescriptor',
          'NVBottomAppBarComponentDescriptor',
          'NVBottomSheetComponentDescriptor',
          'NVCollapsingBarComponentDescriptor',
          'NVCoordinatorLayoutComponentDescriptor',
          'NVExtendedFloatingActionButtonComponentDescriptor',
          'NVFloatingActionButtonComponentDescriptor',
          'NVNavigationBarComponentDescriptor',
          'NVNavigationStackComponentDescriptor',
          'NVSceneComponentDescriptor',
          'NVSearchBarComponentDescriptor',
          'NVSharedElementComponentDescriptor',
          'NVStatusBarComponentDescriptor',
          'NVTabBarItemComponentDescriptor',
          'NVTabBarComponentDescriptor',
          'NVTabBarPagerComponentDescriptor',
          'NVTabBarPagerRTLComponentDescriptor',
          'NVTabLayoutComponentDescriptor',
          'NVTabLayoutRTLComponentDescriptor',
          'NVTabNavigationComponentDescriptor',
          'NVTitleBarComponentDescriptor',
          'NVToolbarComponentDescriptor',
        ],
        androidMkPath: "../cpp/Android.mk"
      },
    },
  },
};<|endoftext|>


<commit_before>const messages = [];

page.on('console', async msg => {
    const args = await Promise.all(msg.args().map(arg => arg.jsonValue()));
    messages.push({
        type: msg._type,
        args
    });
});

exports.toHaveLogs = function(received) {
    expect(page).toEqual(received);
    return {
        pass: messages.length !== 0,
        message: () => {
            if (messages.length === 0) {
                return `There were no messages logged.`;
            }
            const formattedMessages = messages.map(
                ({ type, args }) => `${type}: ${args.join(',')}`
            );
            return `The following messages were logged - \n${formattedMessages.join(
                '\n'
            )}`;
        }
    };
};
<commit_msg>docs: fix toHaveLogs message<commit_after>const { inspect } = require('util');

const messages = [];

beforeEach(() => {
    messages.length = 0;
    page.on('console', async msg => {
        const args = await Promise.all(msg.args().map(arg => arg.jsonValue()));
        messages.push({
            type: msg.type(),
            text: msg.text(),
            args
        });
    });
});

exports.toHaveLogs = function(received) {
    expect(page).toEqual(received);
    return {
        pass: messages.length !== 0,
        message: () => {
            if (messages.length === 0) {
                return `There were no messages logged.`;
            }
            const formattedMessages = messages.map(
                ({ type, text, args }) => `${type}: ${text} ${inspect(args)}`
            );
            return `The following messages were logged - \n${formattedMessages.join(
                '\n'
            )}`;
        }
    };
};
<|endoftext|>


<commit_before>'use strict';

var angularMeteorUtils = angular.module('angular-meteor.utils', []);

angularMeteorUtils.service('$meteorUtils', [
  '$q', '$timeout',
  function ($q, $timeout) {

    var self = this;

    this.autorun = function(scope, fn) {

      console.warn('[angular-meteor.utils.autorun] Please note that this module is deprecated since 1.3.0 and will be removed in 1.4.0! http://info.meteor.com/blog/angular-meteor-1.3');

      // wrapping around Deps.autorun
      var comp = Tracker.autorun(function(c) {
        fn(c);
        // this is run immediately for the first call
        // but after that, we need to $apply to start Angular digest
        if (!c.firstRun) $timeout(angular.noop, 0);
      });

      // stop autorun when scope is destroyed
      scope.$on('$destroy', function() {
        comp.stop();
      });

      // return autorun object so that it can be stopped manually
      return comp;
    };

    // Borrowed from angularFire
    // https://github.com/firebase/angularfire/blob/master/src/utils.js#L445-L454
    this.stripDollarPrefixedKeys = function (data) {
      if (!_.isObject(data) ||
          data instanceof Date ||
          data instanceof File ||
          EJSON.toJSONValue(data).$type === 'oid' ||
          (typeof FS === 'object' && data instanceof FS.File))
        return data;

      var out = _.isArray(data) ? [] : {};

      _.each(data, function(v,k) {
        if(typeof k !== 'string' || k.charAt(0) !== '$')
          out[k] = self.stripDollarPrefixedKeys(v);
      });

      return out;
    };

    // Returns a callback which fulfills promise
    this.fulfill = function(deferred, boundError, boundResult) {
      return function(err, result) {
        if (err)
          deferred.reject(boundError == null ? err : boundError);
        else if (typeof boundResult == "function")
          deferred.resolve(boundResult == null ? result : boundResult(result));
        else
          deferred.resolve(boundResult == null ? result : boundResult);
      };
    };

    // creates a function which invokes method with the given arguments and returns a promise
    this.promissor = function(obj, method) {
      return function() {
        var deferred = $q.defer();
        var fulfill = self.fulfill(deferred);
        var args = _.toArray(arguments).concat(fulfill);
        obj[method].apply(obj, args);
        return deferred.promise;
      };
    };

    // creates a $q.all() promise and call digestion loop on fulfillment
    this.promiseAll = function(promises) {
      var allPromise = $q.all(promises);

      allPromise.finally(function() {
        // calls digestion loop with no conflicts
        $timeout(angular.noop);
      });

      return allPromise;
    };

    this.getCollectionByName = function(string){
      return Mongo.Collection.get(string);
    };

    this.findIndexById = function(collection, doc) {
      var foundDoc = _.find(collection, function(colDoc) {
        // EJSON.equals used to compare Mongo.ObjectIDs and Strings.
        return EJSON.equals(colDoc._id, doc._id);
      });

      return _.indexOf(collection, foundDoc);
    };
  }
]);

angularMeteorUtils.run([
  '$rootScope', '$meteorUtils',
  function($rootScope, $meteorUtils) {
    Object.getPrototypeOf($rootScope).$meteorAutorun = function(fn) {
      return $meteorUtils.autorun(this, fn);
    };
}]);
<commit_msg>Remove angular utils deprecation message<commit_after>'use strict';

var angularMeteorUtils = angular.module('angular-meteor.utils', []);

angularMeteorUtils.service('$meteorUtils', [
  '$q', '$timeout',
  function ($q, $timeout) {

    var self = this;

    this.autorun = function(scope, fn) {
      // wrapping around Deps.autorun
      var comp = Tracker.autorun(function(c) {
        fn(c);
        // this is run immediately for the first call
        // but after that, we need to $apply to start Angular digest
        if (!c.firstRun) $timeout(angular.noop, 0);
      });

      // stop autorun when scope is destroyed
      scope.$on('$destroy', function() {
        comp.stop();
      });

      // return autorun object so that it can be stopped manually
      return comp;
    };

    // Borrowed from angularFire
    // https://github.com/firebase/angularfire/blob/master/src/utils.js#L445-L454
    this.stripDollarPrefixedKeys = function (data) {
      if (!_.isObject(data) ||
          data instanceof Date ||
          data instanceof File ||
          EJSON.toJSONValue(data).$type === 'oid' ||
          (typeof FS === 'object' && data instanceof FS.File))
        return data;

      var out = _.isArray(data) ? [] : {};

      _.each(data, function(v,k) {
        if(typeof k !== 'string' || k.charAt(0) !== '$')
          out[k] = self.stripDollarPrefixedKeys(v);
      });

      return out;
    };

    // Returns a callback which fulfills promise
    this.fulfill = function(deferred, boundError, boundResult) {
      return function(err, result) {
        if (err)
          deferred.reject(boundError == null ? err : boundError);
        else if (typeof boundResult == "function")
          deferred.resolve(boundResult == null ? result : boundResult(result));
        else
          deferred.resolve(boundResult == null ? result : boundResult);
      };
    };

    // creates a function which invokes method with the given arguments and returns a promise
    this.promissor = function(obj, method) {
      return function() {
        var deferred = $q.defer();
        var fulfill = self.fulfill(deferred);
        var args = _.toArray(arguments).concat(fulfill);
        obj[method].apply(obj, args);
        return deferred.promise;
      };
    };

    // creates a $q.all() promise and call digestion loop on fulfillment
    this.promiseAll = function(promises) {
      var allPromise = $q.all(promises);

      allPromise.finally(function() {
        // calls digestion loop with no conflicts
        $timeout(angular.noop);
      });

      return allPromise;
    };

    this.getCollectionByName = function(string){
      return Mongo.Collection.get(string);
    };

    this.findIndexById = function(collection, doc) {
      var foundDoc = _.find(collection, function(colDoc) {
        // EJSON.equals used to compare Mongo.ObjectIDs and Strings.
        return EJSON.equals(colDoc._id, doc._id);
      });

      return _.indexOf(collection, foundDoc);
    };
  }
]);

angularMeteorUtils.run([
  '$rootScope', '$meteorUtils',
  function($rootScope, $meteorUtils) {
    Object.getPrototypeOf($rootScope).$meteorAutorun = function(fn) {
      return $meteorUtils.autorun(this, fn);
    };
}]);
<|endoftext|>


<commit_before>'use strict'

// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigation

module.exports = () => {
  let api = Object.create(null)
  const nav = performance.navigation
  for (let prop in nav) {
    if ((prop === 'type') || (prop === 'redirectCount')) {
      api[prop] = nav[prop]
    }
  }

  return api
}
<commit_msg>:art:<commit_after>'use strict'

// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigation

module.exports = () => {
  const nav = performance.navigation

  let api = Object.create(null)
  api.type = nav.type
  api.redirectCount = nav.redirectCount

  return api
}
<|endoftext|>


<commit_before>import angular from 'angular';

import uiRouter from 'angular-ui-router';

import view from './view';

//====================================================================

export default angular.module('xoWebApp.pool', [
  uiRouter,
])
  .config(function ($stateProvider) {
    $stateProvider.state('pools_view', {
      url: '/pools/:id',
      controller: 'PoolCtrl',
      template: view,
    });
  })
  .controller('PoolCtrl', function ($scope, $stateParams, xoApi, xo, modal) {
    $scope.$watch(() => xoApi.get($stateParams.id), function (pool) {
      $scope.pool = pool;
    });

    $scope.savePool = function ($data) {
      let {pool} = $scope;
      let {name_label, name_description} = $data;

      $data = {
        id: pool.UUID,
      }
      if (name_label !== pool.name_label) {
        $data.name_label = name_label;
      }
      if (name_description !== pool.name_description) {
        $data.name_description = name_description;
      }

      xoApi.call('pool.set', $data);
    };

    $scope.deleteAllLog = function () {
      return modal.confirm({
        title: 'Log deletion',
        message: 'Are you sure you want to delete all the logs?',
      }).then(function () {
        // TODO: return all promises.
        for (let log in $scope.pool.messages) {
          console.log('Remove log', log);
          xo.log.delete(log);
        }
      });
    };

    $scope.deleteLog = function (id) {
      console.log('Remove log', id);
      return xo.log.delete(id);
    };

    // $scope.patchPool = ($files, id) ->
    //   file = $files[0]
    //   xo.pool.patch id
    //   .then ({ $sendTo: url }) ->
    //     return $upload.http {
    //       method: 'POST'
    //       url
    //       data: file
    //     }
    //     .progress throttle(
    //       (event) ->
    //         percentage = (100 * event.loaded / event.total)|0

    //         notify.info
    //           title: 'Upload patch'
    //           message: "#{percentage}%"
    //       6e3
    //     )
    //   .then (result) ->
    //     throw result.status if result.status isnt 200
    //     notify.info
    //       title: 'Upload patch'
    //       message: 'Success'

  })

  // A module exports its name.
  .name
;
<commit_msg>fix remove all logs in Pool view<commit_after>import angular from 'angular';

import uiRouter from 'angular-ui-router';

import view from './view';

//====================================================================

export default angular.module('xoWebApp.pool', [
  uiRouter,
])
  .config(function ($stateProvider) {
    $stateProvider.state('pools_view', {
      url: '/pools/:id',
      controller: 'PoolCtrl',
      template: view,
    });
  })
  .controller('PoolCtrl', function ($scope, $stateParams, xoApi, xo, modal) {
    $scope.$watch(() => xoApi.get($stateParams.id), function (pool) {
      $scope.pool = pool;
    });

    $scope.savePool = function ($data) {
      let {pool} = $scope;
      let {name_label, name_description} = $data;

      $data = {
        id: pool.UUID,
      }
      if (name_label !== pool.name_label) {
        $data.name_label = name_label;
      }
      if (name_description !== pool.name_description) {
        $data.name_description = name_description;
      }

      xoApi.call('pool.set', $data);
    };

    $scope.deleteAllLog = function () {
      return modal.confirm({
        title: 'Log deletion',
        message: 'Are you sure you want to delete all the logs?',
      }).then(function () {
        // TODO: return all promises.
        angular.forEach($scope.pool.messages, function(value, key) {
          xo.log.delete(value);
          console.log('Remove log', value);
        });
      });
    };

    $scope.deleteLog = function (id) {
      console.log('Remove log', id);
      return xo.log.delete(id);
    };

    // $scope.patchPool = ($files, id) ->
    //   file = $files[0]
    //   xo.pool.patch id
    //   .then ({ $sendTo: url }) ->
    //     return $upload.http {
    //       method: 'POST'
    //       url
    //       data: file
    //     }
    //     .progress throttle(
    //       (event) ->
    //         percentage = (100 * event.loaded / event.total)|0

    //         notify.info
    //           title: 'Upload patch'
    //           message: "#{percentage}%"
    //       6e3
    //     )
    //   .then (result) ->
    //     throw result.status if result.status isnt 200
    //     notify.info
    //       title: 'Upload patch'
    //       message: 'Success'

  })

  // A module exports its name.
  .name
;
<|endoftext|>


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

const MenuView      = require('./menu_view.js').MenuView;
const ansi          = require('./ansi_term.js');
const strUtil       = require('./string_util.js');

const util          = require('util');
const assert        = require('assert');
const _             = require('lodash');

exports.SpinnerMenuView = SpinnerMenuView;

function SpinnerMenuView(options) {
    options.justify = options.justify || 'left';
    options.cursor  = options.cursor || 'hide';

    MenuView.call(this, options);

    this.initDefaultWidth();

    var self = this;

    /*
    this.cachePositions = function() {
        self.positionCacheExpired = false;
    };
    */

    this.updateSelection = function() {
        //assert(!self.positionCacheExpired);

        assert(this.focusedItemIndex >= 0 && this.focusedItemIndex <= self.items.length);

        this.drawItem(this.focusedItemIndex);
        this.emit('index update', this.focusedItemIndex);
    };

    this.drawItem = function() {
        var item = self.items[this.focusedItemIndex];
        if(!item) {
            return;
        }

        this.client.term.write(ansi.goto(this.position.row, this.position.col));
        this.client.term.write(self.hasFocus ? self.getFocusSGR() : self.getSGR());

        var text = strUtil.stylizeString(item.text, item.focused ? self.focusTextStyle : self.textStyle);

        self.client.term.write(
            strUtil.pad(text, this.dimens.width + 1, this.fillChar, this.justify));
    };
}

util.inherits(SpinnerMenuView, MenuView);

SpinnerMenuView.prototype.redraw = function() {
    SpinnerMenuView.super_.prototype.redraw.call(this);

    //this.cachePositions();
    this.drawItem(this.focusedItemIndex);
};

SpinnerMenuView.prototype.setFocus = function(focused) {
    SpinnerMenuView.super_.prototype.setFocus.call(this, focused);

    this.redraw();
};

SpinnerMenuView.prototype.setFocusItemIndex = function(index) {
    SpinnerMenuView.super_.prototype.setFocusItemIndex.call(this, index);   //  sets this.focusedItemIndex

    this.updateSelection(); //  will redraw
};

SpinnerMenuView.prototype.onKeyPress = function(ch, key) {
    if(key) {
        if(this.isKeyMapped('up', key.name)) {
            if(0 === this.focusedItemIndex) {
                this.focusedItemIndex = this.items.length - 1;
            } else {
                this.focusedItemIndex--;
            }

            this.updateSelection();
            return;
        } else if(this.isKeyMapped('down', key.name)) {
            if(this.items.length - 1 === this.focusedItemIndex) {
                this.focusedItemIndex = 0;
            } else {
                this.focusedItemIndex++;
            }

            this.updateSelection();
            return;
        }
    }

    SpinnerMenuView.super_.prototype.onKeyPress.call(this, ch, key);
};

SpinnerMenuView.prototype.getData = function() {
    const item = this.getItem(this.focusedItemIndex);
    return _.isString(item.data) ? item.data : this.focusedItemIndex;
};

SpinnerMenuView.prototype.setItems = function(items) {
    SpinnerMenuView.super_.prototype.setItems.call(this, items);

    var longest = 0;
    for(var i = 0; i < this.items.length; ++i) {
        if(longest < this.items[i].text.length) {
            longest = this.items[i].text.length;
        }
    }

    this.dimens.width = longest;
};<commit_msg>Spinner Menu now supports itemFormat and focusItemFormat<commit_after>/* jslint node: true */
'use strict';

const MenuView          = require('./menu_view.js').MenuView;
const ansi              = require('./ansi_term.js');
const strUtil           = require('./string_util.js');
const { pipeToAnsi }    = require('./color_codes.js');
const formatString      = require('./string_format');

const util          = require('util');
const assert        = require('assert');
const _             = require('lodash');

exports.SpinnerMenuView = SpinnerMenuView;

function SpinnerMenuView(options) {
    options.justify = options.justify || 'left';
    options.cursor  = options.cursor || 'hide';

    MenuView.call(this, options);

    this.initDefaultWidth();

    var self = this;

    /*
    this.cachePositions = function() {
        self.positionCacheExpired = false;
    };
    */

    this.updateSelection = function() {
        //assert(!self.positionCacheExpired);

        assert(this.focusedItemIndex >= 0 && this.focusedItemIndex <= self.items.length);

        this.drawItem(this.focusedItemIndex);
        this.emit('index update', this.focusedItemIndex);
    };

    this.drawItem = function(index) {
        const item = this.items[index];
        if(!item) {
            return;
        }

        const cached = this.getRenderCacheItem(index, this.hasFocus);
        if(cached) {
            return this.client.term.write(`${ansi.goto(this.position.row, this.position.col)}${cached}`);
        }

        let text;
        let sgr;
        if(this.complexItems) {
            text    = pipeToAnsi(formatString(this.hasFocus && this.focusItemFormat ? this.focusItemFormat : this.itemFormat, item));
            sgr     = this.focusItemFormat ? '' : (this.hasFocus ? this.getFocusSGR() : self.getSGR());
        } else {
            text    = strUtil.stylizeString(item.text, this.hasFocus ? self.focusTextStyle : self.textStyle);
            sgr     = this.hasFocus ? this.getFocusSGR() : this.getSGR();
        }

        text = `${sgr}${strUtil.pad(text, this.dimens.width, this.fillChar, this.justify)}`;
        this.client.term.write(`${ansi.goto(this.position.row, this.position.col)}${text}`);
        this.setRenderCacheItem(index, text, this.hasFocus);
   }
}

util.inherits(SpinnerMenuView, MenuView);

SpinnerMenuView.prototype.redraw = function() {
    SpinnerMenuView.super_.prototype.redraw.call(this);
    this.drawItem(this.focusedItemIndex);
};

SpinnerMenuView.prototype.setFocus = function(focused) {
    SpinnerMenuView.super_.prototype.setFocus.call(this, focused);
    this.redraw();
};

SpinnerMenuView.prototype.setFocusItemIndex = function(index) {
    SpinnerMenuView.super_.prototype.setFocusItemIndex.call(this, index);   //  sets this.focusedItemIndex
    this.updateSelection(); //  will redraw
};

SpinnerMenuView.prototype.onKeyPress = function(ch, key) {
    if(key) {
        if(this.isKeyMapped('up', key.name)) {
            if(0 === this.focusedItemIndex) {
                this.focusedItemIndex = this.items.length - 1;
            } else {
                this.focusedItemIndex--;
            }

            this.updateSelection();
            return;
        } else if(this.isKeyMapped('down', key.name)) {
            if(this.items.length - 1 === this.focusedItemIndex) {
                this.focusedItemIndex = 0;
            } else {
                this.focusedItemIndex++;
            }

            this.updateSelection();
            return;
        }
    }

    SpinnerMenuView.super_.prototype.onKeyPress.call(this, ch, key);
};

SpinnerMenuView.prototype.getData = function() {
    const item = this.getItem(this.focusedItemIndex);
    return _.isString(item.data) ? item.data : this.focusedItemIndex;
};
<|endoftext|>


<commit_before>
'use strict';

var fs = require('fs'),
    http = require('http'),
    https = require('https'),
    httpProxy = require('http-proxy'),
    cache = require('./cache'),
    memoryMonitor = require('./memorymonitor');

var rootDir = fs.realpathSync(__dirname + '/../');

var logType = {
    log: 1,
    accessLog: 2
};

var log = function (msg, type) {
    // Send the log data to the master
    process.send({
        type: (type === undefined) ? logType.log : type,
        from: process.env.NODE_WORKER_ID || process.pid,
        data: msg
    });
};

var debug = function (debugMode) {
    return function (msg) {
        if (debugMode !== true) {
            return;
        }
        log(msg, logType.log);
    };
};

// Ignore SIGUSR
process.on('SIGUSR1', function () {});
process.on('SIGUSR2', function () {});

function Worker(config) {
    if (!(this instanceof Worker)) {
        return new Worker(config);
    }

    debug = debug(config.server.debug);
    process.on('exit', function () {
        log('Terminating worker (#' + process.pid + ')');
    });
    this.cache = cache(config, {
        logHandler: log,
        debugHandler: debug
    });
    this.runServer(config.server);
}

Worker.prototype.runServer = function (config) {
    httpProxy.setMaxSockets(config.maxSockets);

    var proxyErrorHandler = function (err, req, res) {
        if (err.code === 'ECONNREFUSED' ||
                err.code === 'ETIMEDOUT' ||
                req.error !== undefined) {
            // This backend is dead
            var backendId = req.meta.backendId;
            this.cache.markDeadBackend(req.meta.frontend, backendId);
            if (req.error) {
                err = req.error;
                // Clearing the error
                delete req.error;
            }
            log(req.headers.host + ': backend #' + backendId + ' is dead (' + JSON.stringify(err) + ') while handling request for ' + req.url);
        } else {
            log(req.headers.host + ': backend #' + req.meta.backendId + ' reported an error (' + JSON.stringify(err) + ') while handling request for ' + req.url);
        }
        req.retries = (req.retries === undefined) ? 0 : req.retries + 1;
        if (res.connection.destroyed === true) {
            // FIXME: When there is a TCP timeout, the socket of the Response
            // object is closed. Not possible to return a result after a retry.
            // BugID:5654
            log(req.headers.host + ': Response socket already closed, aborting.');
            return;
        }
        req.emit('retry');
    }.bind(this);

    var startHandler = function (req, res) {
        var remoteAddr = getRemoteAddress(req);

        // TCP timeout to 30 sec
        req.connection.setTimeout(config.tcpTimeout * 1000);
        // Make sure the listener won't be set again on retry
        if (req.connection.listeners('timeout').length < 2) {
            req.connection.once('timeout', function () {
                req.error = 'TCP timeout';
            });
        }

        // Set forwarded headers
        if (remoteAddr === null) {
            return errorMessage(res, 'Cannot read the remote address.');
        }
        if (remoteAddr.slice(0, 2) !== '::') {
            remoteAddr = '::ffff:' + remoteAddr;
        }
        // Play nicely when behind other proxies
        if (req.headers['x-forwarded-for'] === undefined) {
            req.headers['x-forwarded-for'] = remoteAddr;
        }
        if (req.headers['x-real-ip'] === undefined) {
            req.headers['x-real-ip'] = remoteAddr;
        }
        if (req.headers['x-forwarded-protocol'] === undefined) {
            req.headers['x-forwarded-protocol'] = req.connection.pair ? 'https' : 'http';
        }
        if (req.headers['x-forwarded-proto'] === undefined) {
            req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http';
        }
        if (req.headers['x-forwarded-port'] === undefined) {
            // FIXME: replace by the real port instead of hardcoding it
            req.headers['x-forwarded-port'] = req.connection.pair ? '443' : '80';
        }
    };

    var getRemoteAddress = function (req) {
        if (req.connection === undefined) {
            return null;
        }
        if (req.connection.remoteAddress) {
            return req.connection.remoteAddress;
        }
        if (req.connection.socket && req.connection.socket.remoteAddress) {
            return req.connection.socket.remoteAddress;
        }
        return null;
    };

    var errorMessage = function (res, message, code) {
        if (message === undefined) {
            message = '';
        }
        code = isNaN(code) ? 400 : parseInt(code, 10);

        var staticPath = function (name) {
            return rootDir + '/static/error_' + name + '.html';
        };

        var serve = function (filePath) {
            var stream = fs.createReadStream(filePath);
            var headers = {
                'content-type': 'text/html'
            };
            if (res.debug === true) {
                headers['x-debug-error'] = message;
            }
            res.writeHead(code, headers);
            stream.on('data', function (data) {
                res.write(data);
            });
            stream.on('error', function () {
                res.end();
            });
            stream.on('end', function () {
                res.end();
            });
        };

        var errorPage = staticPath(code);
        fs.exists(errorPage, function (exists) {
            if (exists === true) {
                return serve(errorPage);
            }
            errorPage = staticPath('default');
            fs.exists(errorPage, function (exists) {
                var headers;

                if (exists === true) {
                    return serve(errorPage);
                }
                headers = {
                    'content-length': message.length,
                    'content-type': 'text/html'
                };
                if (res.debug === true) {
                    headers['x-debug-error'] = message;
                }
                res.writeHead(code, headers);
                res.write(message);
                res.end();
            });
        });
    };

    var getDomainName = function (hostname) {
        var idx = hostname.lastIndexOf('.');

        if (idx < 0) {
            return hostname;
        }
        idx = hostname.lastIndexOf('.', idx - 1);
        if (idx < 0) {
            return hostname;
        }
        return hostname.substr(idx);
    };

    var httpRequestHandler = function (req, res) {
        res.timer = {
            start: (new Date()).getTime()
        };

        // Patch the response object
        (function () {
            // Enable debug?
            res.debug = (req.headers['x-debug'] !== undefined);
            // Patch res.writeHead to return debug headers
            if (res.debug === true) {
                var resWriteHead = res.writeHead;
                res.writeHead = function () {
                    res.timer.end = (new Date()).getTime();
                    if (req.meta === undefined) {
                        return resWriteHead.apply(res, arguments);
                    }
                    res.setHeader('x-debug-backend-url', req.meta.backendUrl);
                    res.setHeader('x-debug-backend-id', req.meta.backendId);
                    res.setHeader('x-debug-vhost', req.meta.virtualHost);
                    res.setHeader('x-debug-frontend-key', req.meta.frontend);
                    res.setHeader('x-debug-time-total', (res.timer.end - res.timer.start));
                    res.setHeader('x-debug-time-backend', (res.timer.end - res.timer.startBackend));
                    return resWriteHead.apply(res, arguments);
                };
            }
            // Patch res.end to log the response stats
            var resEnd = res.end;
            res.end = function () {
                resEnd.apply(res, arguments);
                var socketBytesWritten = res.socket ? res.socket.bytesWritten : 0;
                if (req.meta === undefined ||
                        req.headers['x-real-ip'] === undefined) {
                    return; // Nothing to log
                }
                if (res.timer.end === undefined) {
                    res.timer.end = (new Date()).getTime();
                }
                // Log the request
                log({
                    remoteAddr: req.headers['x-real-ip'],
                    currentTime: res.timer.start,
                    totalTimeSpent: (res.timer.end - res.timer.start),
                    backendTimeSpent: (res.timer.end - res.timer.startBackend),
                    method: req.method,
                    url: req.url,
                    httpVersion: req.httpVersion,
                    statusCode: res.statusCode,
                    socketBytesWritten: socketBytesWritten,
                    referer: req.headers.referer,
                    userAgent: req.headers['user-agent'],
                    virtualHost: req.meta.virtualHost
                }, logType.accessLog);
            };
        }());

        // Proxy the HTTP request
        var proxyRequest = function () {
            var buffer = httpProxy.buffer(req);

            this.cache.getBackendFromHostHeader(req.headers.host, function (err, code, backend) {
                if (err) {
                    return errorMessage(res, err, code);
                }
                req.meta = {
                    backendId: backend.id,
                    frontend: backend.frontend,
                    virtualHost: backend.virtualHost,
                    backendUrl: backend.href
                };
                // Proxy the request to the backend
                res.timer.startBackend = (new Date()).getTime();
                var proxy = new httpProxy.HttpProxy({
                    target: {
                        host: backend.hostname,
                        port: backend.port
                    },
                    enable: {
                        xforward: false
                    }
                });
                proxy.on('proxyError', proxyErrorHandler);
                proxy.on('start', startHandler);
                proxy.proxyRequest(req, res, buffer);
            });
        }.bind(this);
        req.on('retry', function () {
            log('Retrying on ' + req.headers.host);
            if (req.retries > config.errorMaxRetries) {
                log(req.headers.host + ': Retried too many times, aborting.');
                return errorMessage(res, 'Reached max retries limit', 502);
            }
            proxyRequest();
        });
        proxyRequest();
    }.bind(this);

    var wsRequestHandler = function (req, socket, head) {
        var buffer = httpProxy.buffer(socket);

        this.cache.getBackendFromHostHeader(req.headers.host, function (err, code, backend) {
            var proxy;

            if (err) {
                log('proxyWebSocketRequest: ' + err);
                return;
            }
            // Proxy the WebSocket request to the backend
            proxy = new httpProxy.HttpProxy({
                target: {
                    host: backend.hostname,
                    port: backend.port
                }
            });
            proxy.proxyWebSocketRequest(req, socket, head, buffer);
        });
    }.bind(this);

    var monitor = memoryMonitor({
        logHandler: log
    });

    // Ipv4
    var ipv4HttpServer = http.createServer(httpRequestHandler);
    ipv4HttpServer.on('upgrade', wsRequestHandler);
    ipv4HttpServer.listen(config.port);

    // Ipv6
    var ipv6HttpServer = http.createServer(httpRequestHandler);
    ipv6HttpServer.on('upgrade', wsRequestHandler);
    ipv6HttpServer.listen(config.port, '::1');

    monitor.addServer(ipv4HttpServer);
    monitor.addServer(ipv6HttpServer);

    if (config.https) {
        var options = config.https;
        options.key = fs.readFileSync(options.key, 'utf8');
        options.cert = fs.readFileSync(options.cert, 'utf8');

        var ipv4HttpsServer = https.createServer(options, httpRequestHandler);
        ipv4HttpsServer.on('upgrade', wsRequestHandler);
        ipv4HttpsServer.listen(config.https.port);

        var ipv6HttpsServer = https.createServer(options, httpRequestHandler);
        ipv6HttpsServer.on('upgrade', wsRequestHandler);
        ipv6HttpsServer.listen(config.https.port, '::1');

        monitor.addServer(ipv4HttpsServer);
        monitor.addServer(ipv6HttpsServer);
    }
};

module.exports = Worker;
<commit_msg>Fixed the logging of bytes number written on the response socket<commit_after>
'use strict';

var fs = require('fs'),
    http = require('http'),
    https = require('https'),
    httpProxy = require('http-proxy'),
    cache = require('./cache'),
    memoryMonitor = require('./memorymonitor');

var rootDir = fs.realpathSync(__dirname + '/../');

var logType = {
    log: 1,
    accessLog: 2
};

var log = function (msg, type) {
    // Send the log data to the master
    process.send({
        type: (type === undefined) ? logType.log : type,
        from: process.env.NODE_WORKER_ID || process.pid,
        data: msg
    });
};

var debug = function (debugMode) {
    return function (msg) {
        if (debugMode !== true) {
            return;
        }
        log(msg, logType.log);
    };
};

// Ignore SIGUSR
process.on('SIGUSR1', function () {});
process.on('SIGUSR2', function () {});

function Worker(config) {
    if (!(this instanceof Worker)) {
        return new Worker(config);
    }

    debug = debug(config.server.debug);
    process.on('exit', function () {
        log('Terminating worker (#' + process.pid + ')');
    });
    this.cache = cache(config, {
        logHandler: log,
        debugHandler: debug
    });
    this.runServer(config.server);
}

Worker.prototype.runServer = function (config) {
    httpProxy.setMaxSockets(config.maxSockets);

    var proxyErrorHandler = function (err, req, res) {
        if (err.code === 'ECONNREFUSED' ||
                err.code === 'ETIMEDOUT' ||
                req.error !== undefined) {
            // This backend is dead
            var backendId = req.meta.backendId;
            this.cache.markDeadBackend(req.meta.frontend, backendId);
            if (req.error) {
                err = req.error;
                // Clearing the error
                delete req.error;
            }
            log(req.headers.host + ': backend #' + backendId + ' is dead (' + JSON.stringify(err) + ') while handling request for ' + req.url);
        } else {
            log(req.headers.host + ': backend #' + req.meta.backendId + ' reported an error (' + JSON.stringify(err) + ') while handling request for ' + req.url);
        }
        req.retries = (req.retries === undefined) ? 0 : req.retries + 1;
        if (res.connection.destroyed === true) {
            // FIXME: When there is a TCP timeout, the socket of the Response
            // object is closed. Not possible to return a result after a retry.
            // BugID:5654
            log(req.headers.host + ': Response socket already closed, aborting.');
            return;
        }
        req.emit('retry');
    }.bind(this);

    var startHandler = function (req, res) {
        var remoteAddr = getRemoteAddress(req);

        // TCP timeout to 30 sec
        req.connection.setTimeout(config.tcpTimeout * 1000);
        // Make sure the listener won't be set again on retry
        if (req.connection.listeners('timeout').length < 2) {
            req.connection.once('timeout', function () {
                req.error = 'TCP timeout';
            });
        }

        // Set forwarded headers
        if (remoteAddr === null) {
            return errorMessage(res, 'Cannot read the remote address.');
        }
        if (remoteAddr.slice(0, 2) !== '::') {
            remoteAddr = '::ffff:' + remoteAddr;
        }
        // Play nicely when behind other proxies
        if (req.headers['x-forwarded-for'] === undefined) {
            req.headers['x-forwarded-for'] = remoteAddr;
        }
        if (req.headers['x-real-ip'] === undefined) {
            req.headers['x-real-ip'] = remoteAddr;
        }
        if (req.headers['x-forwarded-protocol'] === undefined) {
            req.headers['x-forwarded-protocol'] = req.connection.pair ? 'https' : 'http';
        }
        if (req.headers['x-forwarded-proto'] === undefined) {
            req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http';
        }
        if (req.headers['x-forwarded-port'] === undefined) {
            // FIXME: replace by the real port instead of hardcoding it
            req.headers['x-forwarded-port'] = req.connection.pair ? '443' : '80';
        }
    };

    var getRemoteAddress = function (req) {
        if (req.connection === undefined) {
            return null;
        }
        if (req.connection.remoteAddress) {
            return req.connection.remoteAddress;
        }
        if (req.connection.socket && req.connection.socket.remoteAddress) {
            return req.connection.socket.remoteAddress;
        }
        return null;
    };

    var errorMessage = function (res, message, code) {
        if (message === undefined) {
            message = '';
        }
        code = isNaN(code) ? 400 : parseInt(code, 10);

        var staticPath = function (name) {
            return rootDir + '/static/error_' + name + '.html';
        };

        var serve = function (filePath) {
            var stream = fs.createReadStream(filePath);
            var headers = {
                'content-type': 'text/html'
            };
            if (res.debug === true) {
                headers['x-debug-error'] = message;
            }
            res.writeHead(code, headers);
            stream.on('data', function (data) {
                res.write(data);
            });
            stream.on('error', function () {
                res.end();
            });
            stream.on('end', function () {
                res.end();
            });
        };

        var errorPage = staticPath(code);
        fs.exists(errorPage, function (exists) {
            if (exists === true) {
                return serve(errorPage);
            }
            errorPage = staticPath('default');
            fs.exists(errorPage, function (exists) {
                var headers;

                if (exists === true) {
                    return serve(errorPage);
                }
                headers = {
                    'content-length': message.length,
                    'content-type': 'text/html'
                };
                if (res.debug === true) {
                    headers['x-debug-error'] = message;
                }
                res.writeHead(code, headers);
                res.write(message);
                res.end();
            });
        });
    };

    var getDomainName = function (hostname) {
        var idx = hostname.lastIndexOf('.');

        if (idx < 0) {
            return hostname;
        }
        idx = hostname.lastIndexOf('.', idx - 1);
        if (idx < 0) {
            return hostname;
        }
        return hostname.substr(idx);
    };

    var httpRequestHandler = function (req, res) {
        res.timer = {
            start: (new Date()).getTime()
        };

        // Patch the response object
        (function () {
            // Enable debug?
            res.debug = (req.headers['x-debug'] !== undefined);
            // Patch res.writeHead to return debug headers
            if (res.debug === true) {
                var resWriteHead = res.writeHead;
                res.writeHead = function () {
                    res.timer.end = (new Date()).getTime();
                    if (req.meta === undefined) {
                        return resWriteHead.apply(res, arguments);
                    }
                    res.setHeader('x-debug-backend-url', req.meta.backendUrl);
                    res.setHeader('x-debug-backend-id', req.meta.backendId);
                    res.setHeader('x-debug-vhost', req.meta.virtualHost);
                    res.setHeader('x-debug-frontend-key', req.meta.frontend);
                    res.setHeader('x-debug-time-total', (res.timer.end - res.timer.start));
                    res.setHeader('x-debug-time-backend', (res.timer.end - res.timer.startBackend));
                    return resWriteHead.apply(res, arguments);
                };
            }
            // Patch res.end to log the response stats
            var resEnd = res.end;
            res.end = function () {
                resEnd.apply(res, arguments);
                // Number of bytes written on the client socket
                var socketBytesWritten = req.connection ? req.connection._bytesDispatched : 0;
                if (req.meta === undefined ||
                        req.headers['x-real-ip'] === undefined) {
                    return; // Nothing to log
                }
                if (res.timer.end === undefined) {
                    res.timer.end = (new Date()).getTime();
                }
                // Log the request
                log({
                    remoteAddr: req.headers['x-real-ip'],
                    currentTime: res.timer.start,
                    totalTimeSpent: (res.timer.end - res.timer.start),
                    backendTimeSpent: (res.timer.end - res.timer.startBackend),
                    method: req.method,
                    url: req.url,
                    httpVersion: req.httpVersion,
                    statusCode: res.statusCode,
                    socketBytesWritten: socketBytesWritten,
                    referer: req.headers.referer,
                    userAgent: req.headers['user-agent'],
                    virtualHost: req.meta.virtualHost
                }, logType.accessLog);
            };
        }());

        // Proxy the HTTP request
        var proxyRequest = function () {
            var buffer = httpProxy.buffer(req);

            this.cache.getBackendFromHostHeader(req.headers.host, function (err, code, backend) {
                if (err) {
                    return errorMessage(res, err, code);
                }
                req.meta = {
                    backendId: backend.id,
                    frontend: backend.frontend,
                    virtualHost: backend.virtualHost,
                    backendUrl: backend.href
                };
                // Proxy the request to the backend
                res.timer.startBackend = (new Date()).getTime();
                var proxy = new httpProxy.HttpProxy({
                    target: {
                        host: backend.hostname,
                        port: backend.port
                    },
                    enable: {
                        xforward: false
                    }
                });
                proxy.on('proxyError', proxyErrorHandler);
                proxy.on('start', startHandler);
                proxy.proxyRequest(req, res, buffer);
            });
        }.bind(this);
        req.on('retry', function () {
            log('Retrying on ' + req.headers.host);
            if (req.retries > config.errorMaxRetries) {
                log(req.headers.host + ': Retried too many times, aborting.');
                return errorMessage(res, 'Reached max retries limit', 502);
            }
            proxyRequest();
        });
        proxyRequest();
    }.bind(this);

    var wsRequestHandler = function (req, socket, head) {
        var buffer = httpProxy.buffer(socket);

        this.cache.getBackendFromHostHeader(req.headers.host, function (err, code, backend) {
            var proxy;

            if (err) {
                log('proxyWebSocketRequest: ' + err);
                return;
            }
            // Proxy the WebSocket request to the backend
            proxy = new httpProxy.HttpProxy({
                target: {
                    host: backend.hostname,
                    port: backend.port
                }
            });
            proxy.proxyWebSocketRequest(req, socket, head, buffer);
        });
    }.bind(this);

    var monitor = memoryMonitor({
        logHandler: log
    });

    // Ipv4
    var ipv4HttpServer = http.createServer(httpRequestHandler);
    ipv4HttpServer.on('upgrade', wsRequestHandler);
    ipv4HttpServer.listen(config.port);

    // Ipv6
    var ipv6HttpServer = http.createServer(httpRequestHandler);
    ipv6HttpServer.on('upgrade', wsRequestHandler);
    ipv6HttpServer.listen(config.port, '::1');

    monitor.addServer(ipv4HttpServer);
    monitor.addServer(ipv6HttpServer);

    if (config.https) {
        var options = config.https;
        options.key = fs.readFileSync(options.key, 'utf8');
        options.cert = fs.readFileSync(options.cert, 'utf8');

        var ipv4HttpsServer = https.createServer(options, httpRequestHandler);
        ipv4HttpsServer.on('upgrade', wsRequestHandler);
        ipv4HttpsServer.listen(config.https.port);

        var ipv6HttpsServer = https.createServer(options, httpRequestHandler);
        ipv6HttpsServer.on('upgrade', wsRequestHandler);
        ipv6HttpsServer.listen(config.https.port, '::1');

        monitor.addServer(ipv4HttpsServer);
        monitor.addServer(ipv6HttpsServer);
    }
};

module.exports = Worker;
<|endoftext|>