<commit_before>/*
 *  GPIOBridge.js
 *
 *  David Janes
 *  IOTDB.org
 *  2014-04-30
 *
 *  Copyright [2013-2015] [David P. Janes]
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

"use strict";

var fs = require('fs')
var child_process = require('child_process')

var rpi = require('./rpi');

var iotdb = require('iotdb');
var _ = iotdb._;

var logger = iotdb.logger({
    name: 'homestar-gpio',
    module: 'GPIOBridge',
});

/* --- constructor --- */

/**
 *  See {iotdb.bridge.Bridge#Bridge} for documentation.
 *

 *  @param {object|undefined} native
 *  only used for instances, should be
 */
var GPIOBridge = function (initd, native) {
    var self = this;

    self.initd = _.defaults(
        initd,
        iotdb.keystore().get("bridges/GPIOBridge/initd"),
        {
            poll: 30
        }
    );

    self.native = native;
};

GPIOBridge.prototype = new iotdb.Bridge();

GPIOBridge.prototype.name = function () {
    return "GPIOBridge";
};

/* --- lifecycle --- */

/**
 *  See {iotdb.bridge.Bridge#discover} for documentation.
 */
GPIOBridge.prototype.discover = function () {
    var self = this;

    if (!self.initd.pins) {
        logger.error({
            method: "discover",
            cause: "you must define 'pins' for the Thing - otherwise how would we know?",
        }, "no 'pins'");
    }

    logger.info({
        method: "discover",
        pins: self.initd.pins,
    }, "called");

    var bridge;
    var native;

    if (rpi.check()) {
        native = new rpi.Delegate();
        bridge = new GPIOBridge(self.initd, native);
    } else {
        return;
    }

    /* complex but it works - we want to do setup before the offical discovery */
    bridge._setup(function(error) {
        if (error) {
            logger.error({
                method: "discover/_setup",
                error: error,
            }, "error while setting up pins");
            return;
        }

        logger.info({
            method: "_setup",
        }, "discovered");

        self.discovered(bridge);
    });
};

/**
 */
GPIOBridge.prototype._setup = function (done) {
    this.native.setup(this, done);
};

/**
 *  See {iotdb.bridge.Bridge#connect} for documentation.
 */
GPIOBridge.prototype.connect = function (connectd) {
    var self = this;

    if (!self.native) {
        return;
    }

    self._validate_connect(connectd);

    self.native.connect(self, connectd);

    process.nextTick(function() {
        self.pull();
    });
};

GPIOBridge.prototype._forget = function () {
    var self = this;
    if (!self.native) {
        return;
    }

    logger.info({
        method: "_forget"
    }, "called");

    self.native = null;
    self.pulled();
};

/**
 *  See {iotdb.bridge.Bridge#disconnect} for documentation.
 */
GPIOBridge.prototype.disconnect = function () {
    var self = this;
    if (!self.native || !self.native) {
        return;
    }

    self._forget();
};

/* --- data --- */

/**
 *  See {iotdb.bridge.Bridge#push} for documentation.
 */
GPIOBridge.prototype.push = function (pushd, done) {
    var self = this;
    if (!self.native) {
        done(new Error("not connected", pushd));
        return;
    }

    self._validate_push(pushd);

    logger.info({
        method: "push",
        pushd: pushd
    }, "push");

    self.native.push(self, pushd, done);
};

/**
 *  See {iotdb.bridge.Bridge#pull} for documentation.
 */
GPIOBridge.prototype.pull = function () {
    var self = this;
    if (!self.native) {
        return;
    }

    self.native.pull(self);
};

/* --- state --- */

/**
 *  See {iotdb.bridge.Bridge#meta} for documentation.
 */
GPIOBridge.prototype.meta = function () {
    var self = this;
    if (!self.native) {
        return;
    }

    return {
        "iot:thing-id": _.id.thing_urn.unique("GPIO", self.native.uuid, self.initd.number),
        "schema:name": self.native.name || "GPIO",

        // "iot:thing-number": self.initd.number,
        // "iot:device-id": _.id.thing_urn.unique("GPIO", self.native.uuid),
        // "schema:manufacturer": "",
        // "schema:model": "",
    };
};

/**
 *  See {iotdb.bridge.Bridge#reachable} for documentation.
 */
GPIOBridge.prototype.reachable = function () {
    return this.native !== null;
};

/**
 *  See {iotdb.bridge.Bridge#configure} for documentation.
 */
GPIOBridge.prototype.configure = function (app) {};

/*
 *  API
 */
exports.Bridge = GPIOBridge;
<commit_msg>fixed up 'push' validation<commit_after>/*
 *  GPIOBridge.js
 *
 *  David Janes
 *  IOTDB.org
 *  2014-04-30
 *
 *  Copyright [2013-2015] [David P. Janes]
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

"use strict";

var fs = require('fs')
var child_process = require('child_process')

var rpi = require('./rpi');

var iotdb = require('iotdb');
var _ = iotdb._;

var logger = iotdb.logger({
    name: 'homestar-gpio',
    module: 'GPIOBridge',
});

/* --- constructor --- */

/**
 *  See {iotdb.bridge.Bridge#Bridge} for documentation.
 *

 *  @param {object|undefined} native
 *  only used for instances, should be
 */
var GPIOBridge = function (initd, native) {
    var self = this;

    self.initd = _.defaults(
        initd,
        iotdb.keystore().get("bridges/GPIOBridge/initd"),
        {
            poll: 30
        }
    );

    self.native = native;
};

GPIOBridge.prototype = new iotdb.Bridge();

GPIOBridge.prototype.name = function () {
    return "GPIOBridge";
};

/* --- lifecycle --- */

/**
 *  See {iotdb.bridge.Bridge#discover} for documentation.
 */
GPIOBridge.prototype.discover = function () {
    var self = this;

    if (!self.initd.pins) {
        logger.error({
            method: "discover",
            cause: "you must define 'pins' for the Thing - otherwise how would we know?",
        }, "no 'pins'");
    }

    logger.info({
        method: "discover",
        pins: self.initd.pins,
    }, "called");

    var bridge;
    var native;

    if (rpi.check()) {
        native = new rpi.Delegate();
        bridge = new GPIOBridge(self.initd, native);
    } else {
        return;
    }

    /* complex but it works - we want to do setup before the offical discovery */
    bridge._setup(function(error) {
        if (error) {
            logger.error({
                method: "discover/_setup",
                error: error,
            }, "error while setting up pins");
            return;
        }

        logger.info({
            method: "_setup",
        }, "discovered");

        self.discovered(bridge);
    });
};

/**
 */
GPIOBridge.prototype._setup = function (done) {
    this.native.setup(this, done);
};

/**
 *  See {iotdb.bridge.Bridge#connect} for documentation.
 */
GPIOBridge.prototype.connect = function (connectd) {
    var self = this;

    if (!self.native) {
        return;
    }

    self._validate_connect(connectd);

    self.native.connect(self, connectd);

    process.nextTick(function() {
        self.pull();
    });
};

GPIOBridge.prototype._forget = function () {
    var self = this;
    if (!self.native) {
        return;
    }

    logger.info({
        method: "_forget"
    }, "called");

    self.native = null;
    self.pulled();
};

/**
 *  See {iotdb.bridge.Bridge#disconnect} for documentation.
 */
GPIOBridge.prototype.disconnect = function () {
    var self = this;
    if (!self.native || !self.native) {
        return;
    }

    self._forget();
};

/* --- data --- */

/**
 *  See {iotdb.bridge.Bridge#push} for documentation.
 */
GPIOBridge.prototype.push = function (pushd, done) {
    var self = this;
    if (!self.native) {
        done(new Error("not connected", pushd));
        return;
    }

    self._validate_push(pushd, done);

    logger.info({
        method: "push",
        pushd: pushd
    }, "push");

    self.native.push(self, pushd, done);
};

/**
 *  See {iotdb.bridge.Bridge#pull} for documentation.
 */
GPIOBridge.prototype.pull = function () {
    var self = this;
    if (!self.native) {
        return;
    }

    self.native.pull(self);
};

/* --- state --- */

/**
 *  See {iotdb.bridge.Bridge#meta} for documentation.
 */
GPIOBridge.prototype.meta = function () {
    var self = this;
    if (!self.native) {
        return;
    }

    return {
        "iot:thing-id": _.id.thing_urn.unique("GPIO", self.native.uuid, self.initd.number),
        "schema:name": self.native.name || "GPIO",

        // "iot:thing-number": self.initd.number,
        // "iot:device-id": _.id.thing_urn.unique("GPIO", self.native.uuid),
        // "schema:manufacturer": "",
        // "schema:model": "",
    };
};

/**
 *  See {iotdb.bridge.Bridge#reachable} for documentation.
 */
GPIOBridge.prototype.reachable = function () {
    return this.native !== null;
};

/**
 *  See {iotdb.bridge.Bridge#configure} for documentation.
 */
GPIOBridge.prototype.configure = function (app) {};

/*
 *  API
 */
exports.Bridge = GPIOBridge;
<|endoftext|>


<commit_before>'use strict';

var ObjectId = require("mongodb").ObjectId;
var should = require('should');
var helper = require("../../helper");
var CustomsManager = require("../../../src/managers/garment-purchasing/customs-manager");
var customsManager = null;
var DeliveryOrderManager = require("../../../src/managers/garment-purchasing/delivery-order-manager");
var deliveryOrderManager = null;
var customsDataUtil = require("../../data-util/garment-purchasing/customs-data-util");
var validate = require("dl-models").validator.garmentPurchasing.customs;
var moment = require('moment');
var dateNow;
var dateAfter;

before('#00. connect db', function (done) {
    helper.getDb()
        .then(db => {
            customsManager = new CustomsManager(db, {
                username: 'unit-test'
            });
            deliveryOrderManager = new DeliveryOrderManager(db, {
                username: 'unit-test'
            });
            dateNow = new Date();
            dateAfter = new Date();
            dateAfter = dateAfter.setDate(dateAfter.getDate() + 2);
            done();
        })
        .catch(e => {
            done(e);
        })
});

it("#01. should error when create new data with empty data", function (done) {
    customsManager.create({})
        .then((id) => {
            done("should error when create new data with empty data");
        })
        .catch((e) => {
            e.name.should.equal("ValidationError");
            e.should.have.property("errors");
            e.errors.should.instanceof(Object);
            done();
        });
});

it("#02. should error when create new data with no exist data supplier", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            data.supplierId = "supplierId";
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with no exist data supplier");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('supplier');
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#03. should error when create new data with no exist data currency", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            data.currencyId = "currencyId";
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with no exist data currency");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('currency');
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#04. should error when create new data with customs date more than this day", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            data.customsDate = moment(dateAfter).format('YYYY-MM-DD');
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with customs date more than this day");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('customsDate');
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#05. should error when create new data with validation date more than this day", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            data.validateDate = moment(dateAfter).format('YYYY-MM-DD');
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with validation date more than this day");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('validateDate');
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#06. should error when create new data with validation date less than customs date", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            var date = new Date();
            date = date.setDate(date.getDate() - 2);
            data.validateDate = moment(date).format('YYYY-MM-DD');
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with validation date less than customs date");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('validateDate');
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#07. should error when create new data with bruto less than netto", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            data.bruto = 15;
            data.netto = 20;
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with bruto less than netto");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('netto');
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#08. should error when create new data with no exist data delivery order", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            var tamp = [];
            for (var a of data.deliveryOrders) {
                a._id = "id";
                tamp.push(a);
            }
            data.deliveryOrders = tamp;
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with bruto less than netto");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('deliveryOrders');
                    e.errors.deliveryOrders.should.instanceof(Array);
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#09. should error when create new data with same no, supplier, customs date, validation date", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            customsManager.create(data)
                .then((id1) => {
                    customsManager.create(data)
                        .then((id2) => {
                            done("should error when create new data with same no, supplier, customs date, validation date");
                        })
                        .catch((e) => {
                            e.name.should.equal("ValidationError");
                            e.should.have.property("errors");
                            e.errors.should.instanceof(Object);
                            e.errors.should.have.property('no');
                            done();
                        });
                })
                .catch((e) => {
                    done(e);
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#10. should error when create new data with data delivery order useCostumes = false", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            var updateDeliveryOrder = [];
            for (var deliveryOrder of data.deliveryOrders) {
                updateDeliveryOrder.push(deliveryOrderManager.collection.updateOne({ _id: new ObjectId(deliveryOrder._id) }, { $set: { useCostumes: false } }));
            }
            Promise.all(updateDeliveryOrder)
                .then((result) => {
                    customsManager.create(data)
                        .then((id) => {
                            done("should error when create new data with data delivery order useCostumes = false");
                        })
                        .catch((e) => {
                            e.name.should.equal("ValidationError");
                            e.should.have.property("errors");
                            e.errors.should.instanceof(Object);
                            e.errors.should.have.property('deliveryOrders');
                            done();
                        });
                })
                .catch((e) => {
                    done(e);
                });
        })
        .catch((e) => {
            done(e);
        });
});<commit_msg>fix unit test<commit_after>'use strict';

var ObjectId = require("mongodb").ObjectId;
var should = require('should');
var helper = require("../../helper");
var CustomsManager = require("../../../src/managers/garment-purchasing/customs-manager");
var customsManager = null;
var DeliveryOrderManager = require("../../../src/managers/garment-purchasing/delivery-order-manager");
var deliveryOrderManager = null;
var customsDataUtil = require("../../data-util/garment-purchasing/customs-data-util");
var validate = require("dl-models").validator.garmentPurchasing.customs;
var moment = require('moment');
var dateNow;
var dateAfter;

before('#00. connect db', function (done) {
    helper.getDb()
        .then(db => {
            customsManager = new CustomsManager(db, {
                username: 'unit-test'
            });
            deliveryOrderManager = new DeliveryOrderManager(db, {
                username: 'unit-test'
            });
            dateNow = new Date();
            dateAfter = new Date();
            dateAfter = dateAfter.setDate(dateAfter.getDate() + 2);
            done();
        })
        .catch(e => {
            done(e);
        })
});

it("#01. should error when create new data with empty data", function (done) {
    customsManager.create({})
        .then((id) => {
            done("should error when create new data with empty data");
        })
        .catch((e) => {
            e.name.should.equal("ValidationError");
            e.should.have.property("errors");
            e.errors.should.instanceof(Object);
            done();
        });
});

it("#02. should error when create new data with no exist data supplier", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            data.supplierId = "supplierId";
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with no exist data supplier");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('supplier');
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#03. should error when create new data with no exist data currency", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            data.currencyId = "currencyId";
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with no exist data currency");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('currency');
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#04. should error when create new data with customs date more than this day", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            data.customsDate = moment(dateAfter).format('YYYY-MM-DD');
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with customs date more than this day");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('customsDate');
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#05. should error when create new data with validation date more than this day", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            data.validateDate = moment(dateAfter).format('YYYY-MM-DD');
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with validation date more than this day");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('validateDate');
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#06. should error when create new data with validation date less than customs date", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            var date = new Date();
            date = date.setDate(date.getDate() - 2);
            data.validateDate = moment(date).format('YYYY-MM-DD');
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with validation date less than customs date");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('validateDate');
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#07. should error when create new data with bruto less than netto", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            data.bruto = 15;
            data.netto = 20;
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with bruto less than netto");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('netto');
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#08. should error when create new data with no exist data delivery order", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            var tamp = [];
            for (var a of data.deliveryOrders) {
                a._id = "id";
                tamp.push(a);
            }
            data.deliveryOrders = tamp;
            customsManager.create(data)
                .then((id) => {
                    done("should error when create new data with bruto less than netto");
                })
                .catch((e) => {
                    e.name.should.equal("ValidationError");
                    e.should.have.property("errors");
                    e.errors.should.instanceof(Object);
                    e.errors.should.have.property('deliveryOrders');
                    e.errors.deliveryOrders.should.instanceof(Array);
                    done();
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#09. should error when create new data with same no, supplier, customs date, validation date", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            customsManager.create(data)
                .then((id1) => {
                    customsManager.create(data)
                        .then((id2) => {
                            done("should error when create new data with same no, supplier, customs date, validation date");
                        })
                        .catch((e) => {
                            e.name.should.equal("ValidationError");
                            e.should.have.property("errors");
                            e.errors.should.instanceof(Object);
                            e.errors.should.have.property('no');
                            done();
                        });
                })
                .catch((e) => {
                    done(e);
                });
        })
        .catch((e) => {
            done(e);
        });
});

it("#10. should error when create new data with data delivery order useCostumes = false", function (done) {
    customsDataUtil.getNewData()
        .then((data) => {
            var updateDeliveryOrders = [];
            for (var deliveryOrder of data.deliveryOrders) {
                var updateDeliveryOrder = deliveryOrderManager.getSingleById(deliveryOrder._id)
                    .then((_deliveryOrder) => {
                        _deliveryOrder.useCustoms = false;
                        return deliveryOrderManager.collection
                            .updateOne({
                                _id: _deliveryOrder._id
                            }, {
                                $set: _deliveryOrder
                            })
                    })
                    updateDeliveryOrders.push(updateDeliveryOrder)
            }
            Promise.all(updateDeliveryOrders)
                .then((result) => {
                    customsManager.create(data)
                        .then((id) => {
                            done("should error when create new data with data delivery order useCostumes = false");
                        })
                        .catch((e) => {
                            e.name.should.equal("ValidationError");
                            e.should.have.property("errors");
                            e.errors.should.instanceof(Object);
                            e.errors.should.have.property('deliveryOrders');
                            done();
                        });
                })
                .catch((e) => {
                    done(e);
                });
        })
        .catch((e) => {
            done(e);
        });
});<|endoftext|>


<commit_before>"use strict";

var packageJson = require("../../package.json"); // In the future this plugin might be broken out to an independent repo. For now it has the same version number as elq.
var forEach = require("lodash.foreach");
var unique = require("lodash.uniq");
var filter = require("lodash.filter");

var BP_UNITS = {};
BP_UNITS.PX = "px";
BP_UNITS.EM = "em";
BP_UNITS.REM = "rem";

function isUnitTypeValid(val) {
    for (var prop in BP_UNITS) {
        if (BP_UNITS.hasOwnProperty(prop) && BP_UNITS[prop] === val) {
            return true;
        }
    }
    return false;
}

module.exports = {
    getName: function () {
        return "elq-breakpoints";
    },
    getVersion: function () {
        return packageJson.version;
    },
    isCompatible: function (elq) {
        var versionParts = elq.getVersion().split(".");
        var lesser = parseInt(versionParts[1]);
        return lesser >= 3;
    },
    make: function (elq, options) {
        var cycleDetector           = elq.cycleDetector;
        var reporter                = elq.reporter;
        var idHandler               = elq.idHandler;
        var batchUpdater            = elq.BatchUpdater();

        var defaultUnit             = options.defaultUnit || "px";
        var cycleDetection          = !!cycleDetector; // Default is 'true' when there's a cycleDetector available.

        if (options.cycleDetection !== undefined) {
            if (options.cycleDetection && !cycleDetector) {
                reporter.error("Elq's cycleDetector subsystem is required when option cycleDetection is enabled.");
            }
            cycleDetection = !!options.cycleDetection;
        }

        var elementBreakpointsListeners = {};
        var currentElementBreakpointClasses = {};

        if (!isUnitTypeValid(defaultUnit)) {
            reporter.error("Invalid default unit: " + defaultUnit);
        }

        function start(elements) {
            function onElementResize(batchUpdater, element) {
                //Read breakpoints by the format elq-breakpoints-widths="300px 500em 200rem 100 ...".
                function getBreakpoints(element, dimension) {
                    function Breakpoint(string, value, valuePx, unit, element) {
                        var bp = {};
                        bp.string = string; // Can be either just value as a string or value + unit
                        bp.value = value;
                        bp.valuePx = valuePx;
                        bp.unit = unit;
                        bp.element = element;
                        return bp;
                    }

                    function getElementFontSizeInPixels(element) {
                        return parseFloat(getComputedStyle(element).fontSize.replace("px", ""));
                    }

                    var breakpointPixelValueConverters = {};

                    breakpointPixelValueConverters[BP_UNITS.PX] = function (value) {
                        return value;
                    };

                    var cachedRootFontSize; // to avoid unnecessarily asking the DOM for the font-size multiple times for the same resize of this element.
                    breakpointPixelValueConverters[BP_UNITS.REM] = function (value) {
                        function getRootElementFontSize() {
                            if (!cachedRootFontSize) {
                                cachedRootFontSize = getElementFontSizeInPixels(document.documentElement);
                            }
                            return cachedRootFontSize;
                        }
                        return value * getRootElementFontSize();
                    };

                    var cachedElementFontSize; // to avoid unnecessarily asking the DOM for the font-size multiple times for the same resize of this element.
                    breakpointPixelValueConverters[BP_UNITS.EM] = function (value) {
                        function getElementFontSize() {
                            if (!cachedElementFontSize) {
                                cachedElementFontSize = getElementFontSizeInPixels(element);
                            }
                            return cachedElementFontSize;
                        }
                        return value * getElementFontSize();
                    };

                    function getFromMainAttr(element, dimension) {
                        var breakpoints = element.getAttribute("elq-breakpoints-" + dimension + "s");

                        if (!breakpoints) {
                            return [];
                        }

                        breakpoints = breakpoints.replace(/\s+/g, " ").trim();
                        breakpoints = breakpoints.split(" ");

                        breakpoints = breakpoints.map(function (breakpointString) {
                            var valueMatch = breakpointString.match(/^([0-9]+)/g);
                            // a breakpoint value must exist
                            if (!valueMatch) {
                                reporter.error("Invalid breakpoint: " + breakpointString + " for element ", element);
                            }

                            var unitMatch = breakpointString.match(/([a-zA-Z]+)$/g); // the unit is allowed to be omitted
                            var unit = unitMatch ? unitMatch[0] : defaultUnit;

                            if (!isUnitTypeValid(unit)) {
                                reporter.error("Elq breakpoint found with invalid unit: " + unit + " for element ", element);
                            }

                            var value = parseFloat(valueMatch[0]);
                            var valuePx = breakpointPixelValueConverters[unit](value);

                            return Breakpoint(breakpointString, value, valuePx, unit, element);
                        });
                        return breakpoints;
                    }

                    function uniqueBreakpoints(breakpoints) {
                        return unique(breakpoints, function uniqueFunction(bp) {
                            // Can not simply take breakpoint.string since unit is allowed to be omitted
                            return bp.value + bp.unit;
                        });
                    }

                    function sortBreakpoints(breakpoints) {
                        return breakpoints.sort(function (bp1, bp2) {
                            return bp1.valuePx - bp2.valuePx;
                        });
                    }

                    var breakpoints = getFromMainAttr(element, dimension);
                    breakpoints = uniqueBreakpoints(breakpoints);
                    // Sort for the visual aspect of having the classes in order in the html
                    breakpoints = sortBreakpoints(breakpoints);
                    return breakpoints;
                }

                function getClasses(breakpoints, dimension, value) {
                    var classes = [];

                    if (!breakpoints.length) {
                        return classes;
                    }

                    breakpoints.forEach(function (breakpoint) {
                        var dir = "max";

                        if (value >= breakpoint.valuePx) {
                            dir = "min";
                        }

                        classes.push("elq-" + dir + "-" + dimension + "-" + breakpoint.value + breakpoint.unit);
                    });

                    return classes;
                }

                var widthBreakpoints = getBreakpoints(element, "width");
                var heightBreakpoints = getBreakpoints(element, "height");

                var width = element.offsetWidth;
                var height = element.offsetHeight;

                var widthClasses = getClasses(widthBreakpoints, "width", width);
                var heightClasses = getClasses(heightBreakpoints, "height", height);
                var breakpointClasses = widthClasses.join(" ") + " " + heightClasses.join(" ");

                var id = idHandler.get(element);
                var elementOptions = getElementOptions(element);

                batchUpdater.update(id, function mutateElementBreakpointClasses() {
                    if (currentElementBreakpointClasses[id] !== breakpointClasses) {
                        if (cycleDetection && !elementOptions.notcyclic && cycleDetector.isUpdateCyclic(element, breakpointClasses)) {
                            reporter.warn("Cyclic rules detected! Breakpoint classes has not been updated. Element: ", element);
                            return;
                        }

                        if (!elementOptions.noclasses) {
                            updateBreakpointClasses(element, breakpointClasses);
                        }

                        currentElementBreakpointClasses[id] = breakpointClasses;
                        forEach(elementBreakpointsListeners[id], function (listener) {
                            listener(element);
                        });
                    }
                });
            }

            elements = filter(elements, function filterElements(element) {
                return element.hasAttribute("elq-breakpoints");
            });

            //Before listening to each element (which is a heavy task) it is important to apply the right classes
            //to the elements so that a correct render can occur before all objects are injected to the elements.
            var manualBatchUpdater = elq.BatchUpdater({ async: false, auto: false });
            forEach(elements, function onElementResizeLoop(element) {
                onElementResize(manualBatchUpdater, element);
            });

            function onElementResizeProxy(element) {
                return onElementResize(batchUpdater, element);
            }

            forEach(elements, function listenToLoop(element) {
                elq.listenTo({
                    callOnAdd: true,
                    batchUpdater: batchUpdater
                }, element, onElementResizeProxy);
            });

            //Force everything currently in the batch to execute synchronously.
            //Important that his is done after the listenToLoop since it reads the DOM style and the batch will write the DOM.
            manualBatchUpdater.force();
        }

        function listenToElementBreakpoints(element, callback) {
            var id = idHandler.get(element);

            elementBreakpointsListeners[id] = elementBreakpointsListeners[id] || [];
            elementBreakpointsListeners[id].push(callback);
        }

        function getBreakpointClasses(element) {
            var id = idHandler.get(element);
            return currentElementBreakpointClasses[id];
        }

        //TODO: This function should maybe take into consideration if the target element has the noclasses option set.
        function updateBreakpointClasses(element, breakpointClasses) {
            var classes = element.className;

            //Remove all old breakpoints.
            var breakpointRegexp = new RegExp("elq-(min|max)-(width|height)-[0-9]+[a-zA-Z]+" , "g");
            classes = classes.replace(breakpointRegexp, "");

            //Add new classes
            classes += " " + breakpointClasses;

            //Format classes before putting it in.
            classes = classes.replace(/\s+/g, " ").trim();

            element.className = classes;
        }

        function getElementOptions(element) {
            var elOptions = {};

            var elOptionsString = element.getAttribute("elq-breakpoints") || "";
            elOptionsString = elOptionsString.toLowerCase();

            elOptions.noclasses = !!~elOptionsString.indexOf("noclasses");
            elOptions.notcyclic = !!~elOptionsString.indexOf("notcyclic");

            return elOptions;
        }

        return {
            start: start,
            listenToElementBreakpoints: listenToElementBreakpoints,
            getBreakpointClasses: getBreakpointClasses,
            updateBreakpointClasses: updateBreakpointClasses
        };
    }
};
<commit_msg>Rearranged some stuff<commit_after>"use strict";

var packageJson = require("../../package.json"); // In the future this plugin might be broken out to an independent repo. For now it has the same version number as elq.
var forEach = require("lodash.foreach");
var unique = require("lodash.uniq");
var filter = require("lodash.filter");

var BP_UNITS = {};
BP_UNITS.PX = "px";
BP_UNITS.EM = "em";
BP_UNITS.REM = "rem";

function isUnitTypeValid(val) {
    for (var prop in BP_UNITS) {
        if (BP_UNITS.hasOwnProperty(prop) && BP_UNITS[prop] === val) {
            return true;
        }
    }
    return false;
}

module.exports = {
    getName: function () {
        return "elq-breakpoints";
    },
    getVersion: function () {
        return packageJson.version;
    },
    isCompatible: function (elq) {
        var versionParts = elq.getVersion().split(".");
        var lesser = parseInt(versionParts[1]);
        return lesser >= 3;
    },
    make: function (elq, options) {
        var cycleDetector           = elq.cycleDetector;
        var reporter                = elq.reporter;
        var idHandler               = elq.idHandler;
        var batchUpdater            = elq.BatchUpdater();

        var defaultUnit             = options.defaultUnit || "px";
        var cycleDetection          = !!cycleDetector; // Default is 'true' when there's a cycleDetector available.

        if (!isUnitTypeValid(defaultUnit)) {
            reporter.error("Invalid default unit: " + defaultUnit);
        }

        if (options.cycleDetection !== undefined) {
            if (options.cycleDetection && !cycleDetector) {
                reporter.error("Elq's cycleDetector subsystem is required when option cycleDetection is enabled.");
            }
            cycleDetection = !!options.cycleDetection;
        }

        var elementBreakpointsListeners = {};
        var currentElementBreakpointClasses = {};

        function start(elements) {
            function onElementResize(batchUpdater, element) {
                //Read breakpoints by the format elq-breakpoints-widths="300px 500em 200rem 100 ...".
                function getBreakpoints(element, dimension) {
                    function Breakpoint(string, value, valuePx, unit, element) {
                        var bp = {};
                        bp.string = string; // Can be either just value as a string or value + unit
                        bp.value = value;
                        bp.valuePx = valuePx;
                        bp.unit = unit;
                        bp.element = element;
                        return bp;
                    }

                    function getElementFontSizeInPixels(element) {
                        return parseFloat(getComputedStyle(element).fontSize.replace("px", ""));
                    }

                    var breakpointPixelValueConverters = {};

                    breakpointPixelValueConverters[BP_UNITS.PX] = function (value) {
                        return value;
                    };

                    var cachedRootFontSize; // to avoid unnecessarily asking the DOM for the font-size multiple times for the same resize of this element.
                    breakpointPixelValueConverters[BP_UNITS.REM] = function (value) {
                        function getRootElementFontSize() {
                            if (!cachedRootFontSize) {
                                cachedRootFontSize = getElementFontSizeInPixels(document.documentElement);
                            }
                            return cachedRootFontSize;
                        }
                        return value * getRootElementFontSize();
                    };

                    var cachedElementFontSize; // to avoid unnecessarily asking the DOM for the font-size multiple times for the same resize of this element.
                    breakpointPixelValueConverters[BP_UNITS.EM] = function (value) {
                        function getElementFontSize() {
                            if (!cachedElementFontSize) {
                                cachedElementFontSize = getElementFontSizeInPixels(element);
                            }
                            return cachedElementFontSize;
                        }
                        return value * getElementFontSize();
                    };

                    function getFromMainAttr(element, dimension) {
                        var breakpoints = element.getAttribute("elq-breakpoints-" + dimension + "s");

                        if (!breakpoints) {
                            return [];
                        }

                        breakpoints = breakpoints.replace(/\s+/g, " ").trim();
                        breakpoints = breakpoints.split(" ");

                        breakpoints = breakpoints.map(function (breakpointString) {
                            var valueMatch = breakpointString.match(/^([0-9]+)/g);
                            // a breakpoint value must exist
                            if (!valueMatch) {
                                reporter.error("Invalid breakpoint: " + breakpointString + " for element ", element);
                            }

                            var unitMatch = breakpointString.match(/([a-zA-Z]+)$/g); // the unit is allowed to be omitted
                            var unit = unitMatch ? unitMatch[0] : defaultUnit;

                            if (!isUnitTypeValid(unit)) {
                                reporter.error("Elq breakpoint found with invalid unit: " + unit + " for element ", element);
                            }

                            var value = parseFloat(valueMatch[0]);
                            var valuePx = breakpointPixelValueConverters[unit](value);

                            return Breakpoint(breakpointString, value, valuePx, unit, element);
                        });
                        return breakpoints;
                    }

                    function uniqueBreakpoints(breakpoints) {
                        return unique(breakpoints, function uniqueFunction(bp) {
                            // Can not simply take breakpoint.string since unit is allowed to be omitted
                            return bp.value + bp.unit;
                        });
                    }

                    function sortBreakpoints(breakpoints) {
                        return breakpoints.sort(function (bp1, bp2) {
                            return bp1.valuePx - bp2.valuePx;
                        });
                    }

                    var breakpoints = getFromMainAttr(element, dimension);
                    breakpoints = uniqueBreakpoints(breakpoints);
                    // Sort for the visual aspect of having the classes in order in the html
                    breakpoints = sortBreakpoints(breakpoints);
                    return breakpoints;
                }

                function getClasses(breakpoints, dimension, value) {
                    var classes = [];

                    if (!breakpoints.length) {
                        return classes;
                    }

                    breakpoints.forEach(function (breakpoint) {
                        var dir = "max";

                        if (value >= breakpoint.valuePx) {
                            dir = "min";
                        }

                        classes.push("elq-" + dir + "-" + dimension + "-" + breakpoint.value + breakpoint.unit);
                    });

                    return classes;
                }

                var widthBreakpoints = getBreakpoints(element, "width");
                var heightBreakpoints = getBreakpoints(element, "height");

                var width = element.offsetWidth;
                var height = element.offsetHeight;

                var widthClasses = getClasses(widthBreakpoints, "width", width);
                var heightClasses = getClasses(heightBreakpoints, "height", height);
                var breakpointClasses = widthClasses.join(" ") + " " + heightClasses.join(" ");

                var id = idHandler.get(element);
                var elementOptions = getElementOptions(element);

                batchUpdater.update(id, function mutateElementBreakpointClasses() {
                    if (currentElementBreakpointClasses[id] !== breakpointClasses) {
                        if (cycleDetection && !elementOptions.notcyclic && cycleDetector.isUpdateCyclic(element, breakpointClasses)) {
                            reporter.warn("Cyclic rules detected! Breakpoint classes has not been updated. Element: ", element);
                            return;
                        }

                        if (!elementOptions.noclasses) {
                            updateBreakpointClasses(element, breakpointClasses);
                        }

                        currentElementBreakpointClasses[id] = breakpointClasses;
                        forEach(elementBreakpointsListeners[id], function (listener) {
                            listener(element);
                        });
                    }
                });
            }

            elements = filter(elements, function filterElements(element) {
                return element.hasAttribute("elq-breakpoints");
            });

            //Before listening to each element (which is a heavy task) it is important to apply the right classes
            //to the elements so that a correct render can occur before all objects are injected to the elements.
            var manualBatchUpdater = elq.BatchUpdater({ async: false, auto: false });
            forEach(elements, function onElementResizeLoop(element) {
                onElementResize(manualBatchUpdater, element);
            });

            function onElementResizeProxy(element) {
                return onElementResize(batchUpdater, element);
            }

            forEach(elements, function listenToLoop(element) {
                elq.listenTo({
                    callOnAdd: true,
                    batchUpdater: batchUpdater
                }, element, onElementResizeProxy);
            });

            //Force everything currently in the batch to execute synchronously.
            //Important that his is done after the listenToLoop since it reads the DOM style and the batch will write the DOM.
            manualBatchUpdater.force();
        }

        function listenToElementBreakpoints(element, callback) {
            var id = idHandler.get(element);

            elementBreakpointsListeners[id] = elementBreakpointsListeners[id] || [];
            elementBreakpointsListeners[id].push(callback);
        }

        function getBreakpointClasses(element) {
            var id = idHandler.get(element);
            return currentElementBreakpointClasses[id];
        }

        //TODO: This function should maybe take into consideration if the target element has the noclasses option set.
        function updateBreakpointClasses(element, breakpointClasses) {
            var classes = element.className;

            //Remove all old breakpoints.
            var breakpointRegexp = new RegExp("elq-(min|max)-(width|height)-[0-9]+[a-zA-Z]+" , "g");
            classes = classes.replace(breakpointRegexp, "");

            //Add new classes
            classes += " " + breakpointClasses;

            //Format classes before putting it in.
            classes = classes.replace(/\s+/g, " ").trim();

            element.className = classes;
        }

        function getElementOptions(element) {
            var elOptions = {};

            var elOptionsString = element.getAttribute("elq-breakpoints") || "";
            elOptionsString = elOptionsString.toLowerCase();

            elOptions.noclasses = !!~elOptionsString.indexOf("noclasses");
            elOptions.notcyclic = !!~elOptionsString.indexOf("notcyclic");

            return elOptions;
        }

        return {
            start: start,
            listenToElementBreakpoints: listenToElementBreakpoints,
            getBreakpointClasses: getBreakpointClasses,
            updateBreakpointClasses: updateBreakpointClasses
        };
    }
};
<|endoftext|>


<commit_before>/**
 * Command parser
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * This is the command parser. Call it with CommandParser.parse
 * (scroll down to its definition for details)
 *
 * Individual commands are put in:
 *   commands.js - "core" commands that shouldn't be modified
 *   config/commands.js - other commands that can be safely modified
 *
 * The command API is (mostly) documented in config/commands.js
 *
 * @license MIT license
 */

/*

To reload chat commands:

/hotpatch chat

*/

const MAX_MESSAGE_LENGTH = 300;

const BROADCAST_COOLDOWN = 20*1000;

const MESSAGE_COOLDOWN = 5*60*1000;

const MAX_PARSE_RECURSION = 10;

var crypto = require('crypto');

var modlog = exports.modlog = modlog || fs.createWriteStream('logs/modlog.txt', {flags:'a+'});

/**
 * Command parser
 *
 * Usage:
 *   CommandParser.parse(message, room, user, connection)
 *
 * message - the message the user is trying to say
 * room - the room the user is trying to say it in
 * user - the user that sent the message
 * connection - the connection the user sent the message from
 *
 * Returns the message the user should say, or a falsy value which
 * means "don't say anything"
 *
 * Examples:
 *   CommandParser.parse("/join lobby", room, user, connection)
 *     will make the user join the lobby, and return false.
 *
 *   CommandParser.parse("Hi, guys!", room, user, connection)
 *     will return "Hi, guys!" if the user isn't muted, or
 *     if he's muted, will warn him that he's muted, and
 *     return false.
 */
var parse = exports.parse = function(message, room, user, connection, levelsDeep) {
	var cmd = '', target = '';
	if (!message || !message.trim().length) return;
	if (!levelsDeep) {
		levelsDeep = 0;
		// if (config.emergencylog && (connection.ip === '62.195.195.62' || connection.ip === '86.141.154.222' || connection.ip === '189.134.175.221' || message.length > 2048 || message.length > 256 && message.substr(0,5) !== '/utm ' && message.substr(0,5) !== '/trn ')) {
		if (config.emergencylog && (user.userid === 'pindapinda' || connection.ip === '62.195.195.62' || connection.ip === '86.141.154.222' || connection.ip === '189.134.175.221')) {
			config.emergencylog.write('<'+user.name+'@'+connection.ip+'> '+message+'\n');
		}
	}

	if (message.substr(0,3) === '>> ') {
		// multiline eval
		message = '/eval '+message.substr(3);
	} else if (message.substr(0,4) === '>>> ') {
		// multiline eval
		message = '/evalbattle '+message.substr(4);
	}

	if (message.substr(0,2) !== '//' && message.substr(0,1) === '/') {
		var spaceIndex = message.indexOf(' ');
		if (spaceIndex > 0) {
			cmd = message.substr(1, spaceIndex-1);
			target = message.substr(spaceIndex+1);
		} else {
			cmd = message.substr(1);
			target = '';
		}
	} else if (message.substr(0,1) === '!') {
		var spaceIndex = message.indexOf(' ');
		if (spaceIndex > 0) {
			cmd = message.substr(0, spaceIndex);
			target = message.substr(spaceIndex+1);
		} else {
			cmd = message;
			target = '';
		}
	}
	cmd = cmd.toLowerCase();
	var broadcast = false;
	if (cmd.charAt(0) === '!') {
		broadcast = true;
		cmd = cmd.substr(1);
	}

	var commandHandler = commands[cmd];
	if (typeof commandHandler === 'string') {
		// in case someone messed up, don't loop
		commandHandler = commands[commandHandler];
	}
	if (commandHandler) {
		var context = {
			sendReply: function(data) {
				if (this.broadcasting) {
					room.add(data, true);
				} else {
					connection.sendTo(room, data);
				}
			},
			sendReplyBox: function(html) {
				this.sendReply('|raw|
'+html+'
');
			},
			popupReply: function(message) {
				connection.popup(message);
			},
			add: function(data) {
				room.add(data, true);
			},
			send: function(data) {
				room.send(data);
			},
			privateModCommand: function(data) {
				for (var i in room.users) {
					if (room.users[i].isStaff) {
						room.users[i].sendTo(room, data);
					}
				}
				this.logEntry(data);
				this.logModCommand(data);
			},
			logEntry: function(data) {
				room.logEntry(data);
			},
			addModCommand: function(result) {
				this.add(result);
				this.logModCommand(result);
			},
			logModCommand: function(result) {
				modlog.write('['+(new Date().toJSON())+'] ('+room.id+') '+result+'\n');
			},
			can: function(permission, target, room) {
				if (!user.can(permission, target, room)) {
					this.sendReply('/'+cmd+' - Access denied.');
					return false;
				}
				return true;
			},
			canBroadcast: function() {
				if (broadcast) {
					message = this.canTalk(message);
					if (!message) return false;
					if (!user.can('broadcast', null, room)) {
						connection.sendTo(room, "You need to be voiced to broadcast this command's information.");
						connection.sendTo(room, "To see it for yourself, use: /"+message.substr(1));
						return false;
					}

					// broadcast cooldown
					var normalized = toId(message);
					if (room.lastBroadcast === normalized &&
							room.lastBroadcastTime >= Date.now() - BROADCAST_COOLDOWN) {
						connection.sendTo(room, "You can't broadcast this because it was just broadcast.")
						return false;
					}
					this.add('|c|'+user.getIdentity(room.id)+'|'+message);
					room.lastBroadcast = normalized;
					room.lastBroadcastTime = Date.now();

					this.broadcasting = true;
				}
				return true;
			},
			parse: function(message) {
				if (levelsDeep > MAX_PARSE_RECURSION) {
					return this.sendReply("Error: Too much recursion");
				}
				return parse(message, room, user, connection, levelsDeep+1);
			},
			canTalk: function(message, relevantRoom) {
				var innerRoom = (relevantRoom !== undefined) ? relevantRoom : room;
				return canTalk(user, innerRoom, connection, message);
			},
			targetUserOrSelf: function(target) {
				if (!target) return user;
				this.splitTarget(target);
				return this.targetUser;
			},
			splitTarget: splitTarget
		};

		var result = commandHandler.call(context, target, room, user, connection, cmd, message);
		if (result === undefined) result = false;

		return result;
	} else {
		// Check for mod/demod/admin/deadmin/etc depending on the group ids
		for (var g in config.groups) {
			if (cmd === config.groups[g].id) {
				return parse('/promote ' + toUserid(target) + ',' + g, room, user, connection);
			} else if (cmd === 'de' + config.groups[g].id || cmd === 'un' + config.groups[g].id) {
				var nextGroup = config.groupsranking[config.groupsranking.indexOf(g) - 1];
				if (!nextGroup) nextGroup = config.groupsranking[0];
				return parse('/demote ' + toUserid(target) + ',' + nextGroup, room, user, connection);
			}
		}

		if (message.substr(0,1) === '/' && cmd) {
			// To guard against command typos, we now emit an error message
			return connection.send('The command "/'+cmd+'" was unrecognized. To send a message starting with "/'+cmd+'", type "//'+cmd+'".');
		}
	}

	message = canTalk(user, room, connection, message);
	if (!message) return false;

	return message;
};

function splitTarget(target, exactName) {
	var commaIndex = target.indexOf(',');
	if (commaIndex < 0) {
		targetUser = Users.get(target, exactName)
		this.targetUser = targetUser;
		this.targetUsername = (targetUser?targetUser.name:target);
		return '';
	}
	var targetUser = Users.get(target.substr(0, commaIndex), exactName);
	if (!targetUser) {
		targetUser = null;
	}
	this.targetUser = targetUser;
	this.targetUsername = (targetUser?targetUser.name:target.substr(0, commaIndex));
	return target.substr(commaIndex+1).trim();
}

/**
 * Can this user talk?
 * Shows an error message if not.
 */
function canTalk(user, room, connection, message) {
	if (!user.named) {
		connection.popup("You must choose a name before you can talk.");
		return false;
	}
	if (room && user.locked) {
		connection.sendTo(room, 'You are locked from talking in chat.');
		return false;
	}
	if (room && user.mutedRooms[room.id]) {
		connection.sendTo(room, 'You are muted and cannot talk in this room.');
		return false;
	}
	if (room && room.modchat) {
		if (room.modchat === 'crash') {
			if (!user.can('ignorelimits')) {
				connection.sendTo(room, 'Because the server has crashed, you cannot speak in lobby chat.');
				return false;
			}
		} else {
			var userGroup = user.group;
			if (room.auth) {
				if (room.auth[user.userid]) {
					userGroup = room.auth[user.userid];
				} else if (userGroup !== ' ') {
					userGroup = '+';
				}
			}
			if (!user.authenticated && room.modchat === true) {
				connection.sendTo(room, 'Because moderated chat is set, you must be registered to speak in lobby chat. To register, simply win a rated battle by clicking the look for battle button');
				return false;
			} else if (config.groupsranking.indexOf(userGroup) < config.groupsranking.indexOf(room.modchat)) {
				var groupName = config.groups[room.modchat].name;
				if (!groupName) groupName = room.modchat;
				connection.sendTo(room, 'Because moderated chat is set, you must be of rank ' + groupName +' or higher to speak in lobby chat.');
				return false;
			}
		}
	}
	if (room && !(user.userid in room.users)) {
		connection.popup("You can't send a message to this room without being in it.");
		return false;
	}

	if (typeof message === 'string') {
		if (!message) {
			connection.popup("Your message can't be blank.");
			return false;
		}
		if (message.length > MAX_MESSAGE_LENGTH && !user.can('ignorelimits')) {
			connection.popup("Your message is too long:\n\n"+message);
			return false;
		}

		// hardcoded low quality website
		if (/\bnimp\.org\b/i.test(message)) return false;

		// remove zalgo
		message = message.replace(/[\u0300-\u036f\u0E2F-\u0E4F]{3,}/g,'');

		if (room && room.id === 'lobby') {
			var normalized = message.trim();
			if ((normalized === user.lastMessage) &&
					((Date.now() - user.lastMessageTime) < MESSAGE_COOLDOWN)) {
				connection.popup("You can't send the same message again so soon.");
				return false;
			}
			user.lastMessage = message;
			user.lastMessageTime = Date.now();
		}

		if (config.chatfilter) {
			return config.chatfilter(user, room, connection.socket, message);
		}
		return message;
	}

	return true;
}

exports.package = {};
fs.readFile('package.json', function(err, data) {
	if (err) return;
	exports.package = JSON.parse(data);
});

exports.uncacheTree = function(root) {
	var uncache = [require.resolve(root)];
	do {
		var newuncache = [];
		for (var i = 0; i < uncache.length; ++i) {
			if (require.cache[uncache[i]]) {
				newuncache.push.apply(newuncache,
					require.cache[uncache[i]].children.map(function(module) {
						return module.filename;
					})
				);
				delete require.cache[uncache[i]];
			}
		}
		uncache = newuncache;
	} while (uncache.length > 0);
};

// This function uses synchronous IO in order to keep it relatively simple.
// The function takes about 0.023 seconds to run on one tested computer,
// which is acceptable considering how long the server takes to start up
// anyway (several seconds).
exports.computeServerVersion = function() {
	/**
	 * `filelist.txt` is a list of all the files in this project. It is used
	 * for computing a checksum of the project for the /version command. This
	 * information cannot be determined at runtime because the user may not be
	 * using a git repository (for example, the user may have downloaded an
	 * archive of the files).
	 *
	 * `filelist.txt` is generated by running `git ls-files > filelist.txt`.
	 */
	var filenames;
	try {
		var data = fs.readFileSync('filelist.txt', {encoding: 'utf8'});
		filenames = data.split('\n');
	} catch (e) {
		return 0;
	}
	var hash = crypto.createHash('md5');
	for (var i = 0; i < filenames.length; ++i) {
		try {
			hash.update(fs.readFileSync(filenames[i]));
		} catch (e) {}
	}
	return hash.digest('hex');
};

exports.serverVersion = exports.computeServerVersion();

/*********************************************************
 * Commands
 *********************************************************/

var commands = exports.commands = require('./commands.js').commands;

var customCommands = require('./config/commands.js');
if (customCommands && customCommands.commands) {
	Object.merge(commands, customCommands.commands);
}
<commit_msg>If a command is unrecognized, send the error message to the correct room.<commit_after>/**
 * Command parser
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * This is the command parser. Call it with CommandParser.parse
 * (scroll down to its definition for details)
 *
 * Individual commands are put in:
 *   commands.js - "core" commands that shouldn't be modified
 *   config/commands.js - other commands that can be safely modified
 *
 * The command API is (mostly) documented in config/commands.js
 *
 * @license MIT license
 */

/*

To reload chat commands:

/hotpatch chat

*/

const MAX_MESSAGE_LENGTH = 300;

const BROADCAST_COOLDOWN = 20*1000;

const MESSAGE_COOLDOWN = 5*60*1000;

const MAX_PARSE_RECURSION = 10;

var crypto = require('crypto');

var modlog = exports.modlog = modlog || fs.createWriteStream('logs/modlog.txt', {flags:'a+'});

/**
 * Command parser
 *
 * Usage:
 *   CommandParser.parse(message, room, user, connection)
 *
 * message - the message the user is trying to say
 * room - the room the user is trying to say it in
 * user - the user that sent the message
 * connection - the connection the user sent the message from
 *
 * Returns the message the user should say, or a falsy value which
 * means "don't say anything"
 *
 * Examples:
 *   CommandParser.parse("/join lobby", room, user, connection)
 *     will make the user join the lobby, and return false.
 *
 *   CommandParser.parse("Hi, guys!", room, user, connection)
 *     will return "Hi, guys!" if the user isn't muted, or
 *     if he's muted, will warn him that he's muted, and
 *     return false.
 */
var parse = exports.parse = function(message, room, user, connection, levelsDeep) {
	var cmd = '', target = '';
	if (!message || !message.trim().length) return;
	if (!levelsDeep) {
		levelsDeep = 0;
		// if (config.emergencylog && (connection.ip === '62.195.195.62' || connection.ip === '86.141.154.222' || connection.ip === '189.134.175.221' || message.length > 2048 || message.length > 256 && message.substr(0,5) !== '/utm ' && message.substr(0,5) !== '/trn ')) {
		if (config.emergencylog && (user.userid === 'pindapinda' || connection.ip === '62.195.195.62' || connection.ip === '86.141.154.222' || connection.ip === '189.134.175.221')) {
			config.emergencylog.write('<'+user.name+'@'+connection.ip+'> '+message+'\n');
		}
	}

	if (message.substr(0,3) === '>> ') {
		// multiline eval
		message = '/eval '+message.substr(3);
	} else if (message.substr(0,4) === '>>> ') {
		// multiline eval
		message = '/evalbattle '+message.substr(4);
	}

	if (message.substr(0,2) !== '//' && message.substr(0,1) === '/') {
		var spaceIndex = message.indexOf(' ');
		if (spaceIndex > 0) {
			cmd = message.substr(1, spaceIndex-1);
			target = message.substr(spaceIndex+1);
		} else {
			cmd = message.substr(1);
			target = '';
		}
	} else if (message.substr(0,1) === '!') {
		var spaceIndex = message.indexOf(' ');
		if (spaceIndex > 0) {
			cmd = message.substr(0, spaceIndex);
			target = message.substr(spaceIndex+1);
		} else {
			cmd = message;
			target = '';
		}
	}
	cmd = cmd.toLowerCase();
	var broadcast = false;
	if (cmd.charAt(0) === '!') {
		broadcast = true;
		cmd = cmd.substr(1);
	}

	var commandHandler = commands[cmd];
	if (typeof commandHandler === 'string') {
		// in case someone messed up, don't loop
		commandHandler = commands[commandHandler];
	}
	if (commandHandler) {
		var context = {
			sendReply: function(data) {
				if (this.broadcasting) {
					room.add(data, true);
				} else {
					connection.sendTo(room, data);
				}
			},
			sendReplyBox: function(html) {
				this.sendReply('|raw|
'+html+'
');
			},
			popupReply: function(message) {
				connection.popup(message);
			},
			add: function(data) {
				room.add(data, true);
			},
			send: function(data) {
				room.send(data);
			},
			privateModCommand: function(data) {
				for (var i in room.users) {
					if (room.users[i].isStaff) {
						room.users[i].sendTo(room, data);
					}
				}
				this.logEntry(data);
				this.logModCommand(data);
			},
			logEntry: function(data) {
				room.logEntry(data);
			},
			addModCommand: function(result) {
				this.add(result);
				this.logModCommand(result);
			},
			logModCommand: function(result) {
				modlog.write('['+(new Date().toJSON())+'] ('+room.id+') '+result+'\n');
			},
			can: function(permission, target, room) {
				if (!user.can(permission, target, room)) {
					this.sendReply('/'+cmd+' - Access denied.');
					return false;
				}
				return true;
			},
			canBroadcast: function() {
				if (broadcast) {
					message = this.canTalk(message);
					if (!message) return false;
					if (!user.can('broadcast', null, room)) {
						connection.sendTo(room, "You need to be voiced to broadcast this command's information.");
						connection.sendTo(room, "To see it for yourself, use: /"+message.substr(1));
						return false;
					}

					// broadcast cooldown
					var normalized = toId(message);
					if (room.lastBroadcast === normalized &&
							room.lastBroadcastTime >= Date.now() - BROADCAST_COOLDOWN) {
						connection.sendTo(room, "You can't broadcast this because it was just broadcast.")
						return false;
					}
					this.add('|c|'+user.getIdentity(room.id)+'|'+message);
					room.lastBroadcast = normalized;
					room.lastBroadcastTime = Date.now();

					this.broadcasting = true;
				}
				return true;
			},
			parse: function(message) {
				if (levelsDeep > MAX_PARSE_RECURSION) {
					return this.sendReply("Error: Too much recursion");
				}
				return parse(message, room, user, connection, levelsDeep+1);
			},
			canTalk: function(message, relevantRoom) {
				var innerRoom = (relevantRoom !== undefined) ? relevantRoom : room;
				return canTalk(user, innerRoom, connection, message);
			},
			targetUserOrSelf: function(target) {
				if (!target) return user;
				this.splitTarget(target);
				return this.targetUser;
			},
			splitTarget: splitTarget
		};

		var result = commandHandler.call(context, target, room, user, connection, cmd, message);
		if (result === undefined) result = false;

		return result;
	} else {
		// Check for mod/demod/admin/deadmin/etc depending on the group ids
		for (var g in config.groups) {
			if (cmd === config.groups[g].id) {
				return parse('/promote ' + toUserid(target) + ',' + g, room, user, connection);
			} else if (cmd === 'de' + config.groups[g].id || cmd === 'un' + config.groups[g].id) {
				var nextGroup = config.groupsranking[config.groupsranking.indexOf(g) - 1];
				if (!nextGroup) nextGroup = config.groupsranking[0];
				return parse('/demote ' + toUserid(target) + ',' + nextGroup, room, user, connection);
			}
		}

		if (message.substr(0,1) === '/' && cmd) {
			// To guard against command typos, we now emit an error message
			return connection.sendTo(room.id, 'The command "/'+cmd+'" was unrecognized. To send a message starting with "/'+cmd+'", type "//'+cmd+'".');
		}
	}

	message = canTalk(user, room, connection, message);
	if (!message) return false;

	return message;
};

function splitTarget(target, exactName) {
	var commaIndex = target.indexOf(',');
	if (commaIndex < 0) {
		targetUser = Users.get(target, exactName)
		this.targetUser = targetUser;
		this.targetUsername = (targetUser?targetUser.name:target);
		return '';
	}
	var targetUser = Users.get(target.substr(0, commaIndex), exactName);
	if (!targetUser) {
		targetUser = null;
	}
	this.targetUser = targetUser;
	this.targetUsername = (targetUser?targetUser.name:target.substr(0, commaIndex));
	return target.substr(commaIndex+1).trim();
}

/**
 * Can this user talk?
 * Shows an error message if not.
 */
function canTalk(user, room, connection, message) {
	if (!user.named) {
		connection.popup("You must choose a name before you can talk.");
		return false;
	}
	if (room && user.locked) {
		connection.sendTo(room, 'You are locked from talking in chat.');
		return false;
	}
	if (room && user.mutedRooms[room.id]) {
		connection.sendTo(room, 'You are muted and cannot talk in this room.');
		return false;
	}
	if (room && room.modchat) {
		if (room.modchat === 'crash') {
			if (!user.can('ignorelimits')) {
				connection.sendTo(room, 'Because the server has crashed, you cannot speak in lobby chat.');
				return false;
			}
		} else {
			var userGroup = user.group;
			if (room.auth) {
				if (room.auth[user.userid]) {
					userGroup = room.auth[user.userid];
				} else if (userGroup !== ' ') {
					userGroup = '+';
				}
			}
			if (!user.authenticated && room.modchat === true) {
				connection.sendTo(room, 'Because moderated chat is set, you must be registered to speak in lobby chat. To register, simply win a rated battle by clicking the look for battle button');
				return false;
			} else if (config.groupsranking.indexOf(userGroup) < config.groupsranking.indexOf(room.modchat)) {
				var groupName = config.groups[room.modchat].name;
				if (!groupName) groupName = room.modchat;
				connection.sendTo(room, 'Because moderated chat is set, you must be of rank ' + groupName +' or higher to speak in lobby chat.');
				return false;
			}
		}
	}
	if (room && !(user.userid in room.users)) {
		connection.popup("You can't send a message to this room without being in it.");
		return false;
	}

	if (typeof message === 'string') {
		if (!message) {
			connection.popup("Your message can't be blank.");
			return false;
		}
		if (message.length > MAX_MESSAGE_LENGTH && !user.can('ignorelimits')) {
			connection.popup("Your message is too long:\n\n"+message);
			return false;
		}

		// hardcoded low quality website
		if (/\bnimp\.org\b/i.test(message)) return false;

		// remove zalgo
		message = message.replace(/[\u0300-\u036f\u0E2F-\u0E4F]{3,}/g,'');

		if (room && room.id === 'lobby') {
			var normalized = message.trim();
			if ((normalized === user.lastMessage) &&
					((Date.now() - user.lastMessageTime) < MESSAGE_COOLDOWN)) {
				connection.popup("You can't send the same message again so soon.");
				return false;
			}
			user.lastMessage = message;
			user.lastMessageTime = Date.now();
		}

		if (config.chatfilter) {
			return config.chatfilter(user, room, connection.socket, message);
		}
		return message;
	}

	return true;
}

exports.package = {};
fs.readFile('package.json', function(err, data) {
	if (err) return;
	exports.package = JSON.parse(data);
});

exports.uncacheTree = function(root) {
	var uncache = [require.resolve(root)];
	do {
		var newuncache = [];
		for (var i = 0; i < uncache.length; ++i) {
			if (require.cache[uncache[i]]) {
				newuncache.push.apply(newuncache,
					require.cache[uncache[i]].children.map(function(module) {
						return module.filename;
					})
				);
				delete require.cache[uncache[i]];
			}
		}
		uncache = newuncache;
	} while (uncache.length > 0);
};

// This function uses synchronous IO in order to keep it relatively simple.
// The function takes about 0.023 seconds to run on one tested computer,
// which is acceptable considering how long the server takes to start up
// anyway (several seconds).
exports.computeServerVersion = function() {
	/**
	 * `filelist.txt` is a list of all the files in this project. It is used
	 * for computing a checksum of the project for the /version command. This
	 * information cannot be determined at runtime because the user may not be
	 * using a git repository (for example, the user may have downloaded an
	 * archive of the files).
	 *
	 * `filelist.txt` is generated by running `git ls-files > filelist.txt`.
	 */
	var filenames;
	try {
		var data = fs.readFileSync('filelist.txt', {encoding: 'utf8'});
		filenames = data.split('\n');
	} catch (e) {
		return 0;
	}
	var hash = crypto.createHash('md5');
	for (var i = 0; i < filenames.length; ++i) {
		try {
			hash.update(fs.readFileSync(filenames[i]));
		} catch (e) {}
	}
	return hash.digest('hex');
};

exports.serverVersion = exports.computeServerVersion();

/*********************************************************
 * Commands
 *********************************************************/

var commands = exports.commands = require('./commands.js').commands;

var customCommands = require('./config/commands.js');
if (customCommands && customCommands.commands) {
	Object.merge(commands, customCommands.commands);
}
<|endoftext|>


<commit_before>
(function() {

function Geocoding(address, table, geocoder) {
  this.georeferencing = false;
  this.address = address;
  this.table = table;
  this.geoCodingPace = 300; // one row each 300ms
  this.geocoder = geocoder || cdb.geo.geocoder.NOKIA;
}

_.extend(Geocoding.prototype, Backbone.Events);

//
// public methods
//

/**
 * start the georeferencing
 */
Geocoding.prototype.start = function() {
  var self = this;
  this.lastCartoDbId = 0;
  this.queue = [];
  this.current_connections = 0;
  this.total_connections = 0;
  this.avgResponseTime = this.geoCodingPace;
  this.totalTime = 0;
  this.totalRegisters = 0;
  this.errors = 0;
  // create the column to georef
  this.georeferencing = true;
  this._createGeorefColumn(function() {
    self.trigger('started');
    self._getTotalRegisters(function() {
      self._feedQueue();
    });
  });
};

/**
 * change the address
 */
Geocoding.prototype.setAddress = function(address) {
  this.address = address;
};

/**
 * cancel the current progress
 */
Geocoding.prototype.cancel = function() {
  this.georeferencing = false;
};

//
// private methods
//

Geocoding.prototype._createGeorefColumn = function(done) {
  if(!this.table.containsColumn('cartodb_georef_status')) {
    this.table.addColumn('cartodb_georef_status', 'boolean', done);
  } else {
    done();
  }
};

/**
 * get total registers, only for stats pourposes
 */
Geocoding.prototype._getTotalRegisters = function(done) {
  var self = this;
  var sql = _.template("SELECT count(cartodb_id) from <%= table %> where cartodb_georef_status is NULL OR cartodb_georef_status is FALSE");
  this.table.data()._sqlQuery(
    sql({
      table: self.table.get('name')
    }),
    function(data) {
      if(data.rows.length) {
        self.totalRegisters = data.rows[0].count;
        if (self.totalRegisters == 0) {
          self.trigger('no-data');
        }
      }
      done();
    });
};

/**
 * fetch rows with cartodb_georef_status == NULL and put them on the quere to be georeferenced.
 * You can specify the number of rows to be fecthed, 100 by default
 */
Geocoding.prototype._feedQueue = function(rows) {
  var self = this;
  rows = rows || 100;
  var sql = _.template("SELECT * from <%= table %> where (cartodb_georef_status is " +
    " NULL OR cartodb_georef_status is FALSE) AND cartodb_id > "+this.lastCartoDbId +
    " order by cartodb_id limit <%= rows %> ");
  this.table.data()._sqlQuery(
    sql({
      table: self.table.get('name'),
      rows: rows
    }),
    function(data) {
      if(data.rows.length) {
        self.queue = self.queue.concat(data.rows);
        self._next();
      } else {
        self._finished();
      }
    }
  );
}

// the georeference has finished, stop the timers
Geocoding.prototype._finished = function() {
  this.georeferencing = false;
  this.trigger('finished');
}

Geocoding.prototype._next = function() {
  if(!this.georeferencing)  {
    return;
  }

  var self = this;
  if(self.current_connections< 10) {
    var row = this.queue.shift();
    if(row) {
      self.lastCartoDbId = row['cartodb_id'];
      $.when(self.geocodeRow(row)).then(function() {
        self._next();
      });
    } else {
      self._feedQueue();
    }
  } else {
    self.bind('connectionFreed', function() {
      self.unbind('connectionFreed');
      self._next.bind(self);
    });
  }

}

Geocoding.prototype.geocodeRow = function(row) {
  var self = this;
  var dfd = $.Deferred();
  var oldTemplateSettings= _.templateSettings;
  _.templateSettings = { interpolate : /\{(.+?)\}/g };
  var address = _.template(this.address, row);
  _.templateSettings = oldTemplateSettings;

  ++self.current_connections;
  ++self.total_connections;
  var t0 = new Date().getTime();
  this.geocoder.geocode(address, function(coords) {
    self.totalTime += new Date().getTime() - t0;
    self.avgResponseTime = self.totalTime/self.total_connections;
    --self.current_connections;
    self.trigger('connectionFreed');
    if(coords.length) {
      var coordinates = coords[0];

      $.when(self.table.data().getRow(row.cartodb_id).save({
        the_geom: {
          "type":"Point",
          "coordinates":[
            coordinates.lon,
            coordinates.lat
          ]
        },
        cartodb_georef_status: true
      }, {
        silent: true
      })).done(function() {
        self.trigger('progress', self.total_connections, self.totalRegisters);
        self.table.data().getRow(row.cartodb_id).trigger('change');
        dfd.resolve();
      });
    } else {
      $.when(self.table.data().getRow(row.cartodb_id).save({
        cartodb_georef_status: false
      }, {
        silent: true
      })).done(function() {
        self.table.data().getRow(row.cartodb_id).trigger('change');
        self.trigger('progress', self.total_connections, self.totalRegisters);
        dfd.resolve();
      });;
    }

  });
  return dfd.promise();

};

cdb.admin.Geocoding = Geocoding;

})();
<commit_msg>removed legacy code<commit_after>
(function() {

function Geocoding(address, table, geocoder) {
  this.georeferencing = false;
  this.address = address;
  this.table = table;
  this.geoCodingPace = 300; // one row each 300ms
  this.geocoder = geocoder || cdb.geo.geocoder.NOKIA;
}

_.extend(Geocoding.prototype, Backbone.Events);

//
// public methods
//

/**
 * start the georeferencing
 */
Geocoding.prototype.start = function() {
  var self = this;
  this.lastCartoDbId = 0;
  this.queue = [];
  this.current_connections = 0;
  this.total_connections = 0;
  this.totalRegisters = 0;
  this.errors = 0;
  // create the column to georef
  this.georeferencing = true;
  this._createGeorefColumn(function() {
    self.trigger('started');
    self._getTotalRegisters(function() {
      self._feedQueue();
    });
  });
};

/**
 * change the address
 */
Geocoding.prototype.setAddress = function(address) {
  this.address = address;
};

/**
 * cancel the current progress
 */
Geocoding.prototype.cancel = function() {
  this.georeferencing = false;
};

//
// private methods
//

Geocoding.prototype._createGeorefColumn = function(done) {
  if(!this.table.containsColumn('cartodb_georef_status')) {
    this.table.addColumn('cartodb_georef_status', 'boolean', done);
  } else {
    done();
  }
};

/**
 * get total registers, only for stats pourposes
 */
Geocoding.prototype._getTotalRegisters = function(done) {
  var self = this;
  var sql = _.template("SELECT count(cartodb_id) from <%= table %> where cartodb_georef_status is NULL OR cartodb_georef_status is FALSE");
  this.table.data()._sqlQuery(
    sql({
      table: self.table.get('name')
    }),
    function(data) {
      if(data.rows.length) {
        self.totalRegisters = data.rows[0].count;
        if (self.totalRegisters == 0) {
          self.trigger('no-data');
        }
      }
      done();
    });
};

/**
 * fetch rows with cartodb_georef_status == NULL and put them on the quere to be georeferenced.
 * You can specify the number of rows to be fecthed, 100 by default
 */
Geocoding.prototype._feedQueue = function(rows) {
  var self = this;
  rows = rows || 100;
  var sql = _.template("SELECT * from <%= table %> where (cartodb_georef_status is " +
    " NULL OR cartodb_georef_status is FALSE) AND cartodb_id > "+this.lastCartoDbId +
    " order by cartodb_id limit <%= rows %> ");
  this.table.data()._sqlQuery(
    sql({
      table: self.table.get('name'),
      rows: rows
    }),
    function(data) {
      if(data.rows.length) {
        self.queue = self.queue.concat(data.rows);
        self._next();
      } else {
        self._finished();
      }
    }
  );
}

// the georeference has finished, stop the timers
Geocoding.prototype._finished = function() {
  this.georeferencing = false;
  this.trigger('finished');
}

Geocoding.prototype._next = function() {
  if(!this.georeferencing)  {
    return;
  }

  var self = this;
  if(self.current_connections< 10) {
    var row = this.queue.shift();
    if(row) {
      self.lastCartoDbId = row['cartodb_id'];
      $.when(self.geocodeRow(row)).then(function() {
        self._next();
      });
    } else {
      self._feedQueue();
    }
  } else {
    self.bind('connectionFreed', function() {
      self.unbind('connectionFreed');
      self._next.bind(self);
    });
  }

}

Geocoding.prototype.geocodeRow = function(row) {
  var self = this;
  var dfd = $.Deferred();
  var oldTemplateSettings= _.templateSettings;
  _.templateSettings = { interpolate : /\{(.+?)\}/g };
  var address = _.template(this.address, row);
  _.templateSettings = oldTemplateSettings;

  ++self.current_connections;
  ++self.total_connections;
  this.geocoder.geocode(address, function(coords) {
    --self.current_connections;
    self.trigger('connectionFreed');
    if(coords.length) {
      var coordinates = coords[0];

      $.when(self.table.data().getRow(row.cartodb_id).save({
        the_geom: {
          "type":"Point",
          "coordinates":[
            coordinates.lon,
            coordinates.lat
          ]
        },
        cartodb_georef_status: true
      }, {
        silent: true
      })).done(function() {
        self.trigger('progress', self.total_connections, self.totalRegisters);
        self.table.data().getRow(row.cartodb_id).trigger('change');
        dfd.resolve();
      });
    } else {
      $.when(self.table.data().getRow(row.cartodb_id).save({
        cartodb_georef_status: false
      }, {
        silent: true
      })).done(function() {
        self.table.data().getRow(row.cartodb_id).trigger('change');
        self.trigger('progress', self.total_connections, self.totalRegisters);
        dfd.resolve();
      });;
    }

  });
  return dfd.promise();

};

cdb.admin.Geocoding = Geocoding;

})();
<|endoftext|>