<commit_before>'use strict'

const amqp = require('amqplib/callback_api')
const EventQueue = require('./EventQueue')
const ResponseQueue = require('./ResponseQueue')

class Messenger {
  constructor (options) {
    this.options = options || {}
    this.changeExchange = this.options.changeExchange || 'domino_change'
    this.amqpURL = this.options.amqp || 'amqp://localhost'
    this.assertedQueues = {}
  }

  start (callback) {
    amqp.connect(this.amqpURL, (err, conn) => {
      if(err) return callback(err)

      conn.createChannel( (err, channel) => {
        if(err) return callback(err)

        this.channel = channel
        this.channel.assertExchange(
          this.changeExchange,
          'topic',
          {durable: false}
        )

        if (this.options.rpcQueue == true)
          this.initRpcQueue(callback)
        else
          callback(null, this)
      })
    })
  }

  ack (msg) {
    this.channel.ack(msg)
  }

  consume (queue, callback) {
    this.assertQueue(queue)
    this.channel.consume(queue, (msg) => {
      msg.content = JSON.parse(msg.content)
      callback(msg)
      this.ack(msg)
    })
  }

  broadcast (topic, json) {
    const body = new Buffer(JSON.stringify(json))
    this.channel.publish(this.changeExchange, topic, body)

    console.log(`Broadcast message to ${topic}: `, json)
  }

  responseQueue () {
    return new ResponseQueue(this.channel)
  }

  eventQueue () {
    return new EventQueue(this.channel, this.changeExchange)
  }

  publish (queue, json, options) {
    const isReserved = /^amq\./.test(queue)
    const message = new Buffer(JSON.stringify(json))

    if(!isReserved) this.assertQueue(queue)

    this.channel.sendToQueue(queue, message, Object.assign({persistent: true}, options))

    console.log(`Published message to ${queue}: `, json.payload)
  }

  assertQueue (queue) {
    if (queue in this.assertedQueues)
      return

    this.channel.assertQueue(queue, {durable: true})
    this.assertedQueues[queue] = true
  }
}

module.exports = Messenger<commit_msg>log entire payload<commit_after>'use strict'

const amqp = require('amqplib/callback_api')
const EventQueue = require('./EventQueue')
const ResponseQueue = require('./ResponseQueue')

class Messenger {
  constructor (options) {
    this.options = options || {}
    this.changeExchange = this.options.changeExchange || 'domino_change'
    this.amqpURL = this.options.amqp || 'amqp://localhost'
    this.assertedQueues = {}
  }

  start (callback) {
    amqp.connect(this.amqpURL, (err, conn) => {
      if(err) return callback(err)

      conn.createChannel( (err, channel) => {
        if(err) return callback(err)

        this.channel = channel
        this.channel.assertExchange(
          this.changeExchange,
          'topic',
          {durable: false}
        )

        if (this.options.rpcQueue == true)
          this.initRpcQueue(callback)
        else
          callback(null, this)
      })
    })
  }

  ack (msg) {
    this.channel.ack(msg)
  }

  consume (queue, callback) {
    this.assertQueue(queue)
    this.channel.consume(queue, (msg) => {
      msg.content = JSON.parse(msg.content)
      callback(msg)
      this.ack(msg)
    })
  }

  broadcast (topic, json) {
    const body = new Buffer(JSON.stringify(json))
    this.channel.publish(this.changeExchange, topic, body)

    console.log(`Broadcast message to ${topic}: `, json)
  }

  responseQueue () {
    return new ResponseQueue(this.channel)
  }

  eventQueue () {
    return new EventQueue(this.channel, this.changeExchange)
  }

  publish (queue, json, options) {
    const isReserved = /^amq\./.test(queue)
    const message = new Buffer(JSON.stringify(json))

    if(!isReserved) this.assertQueue(queue)

    this.channel.sendToQueue(queue, message, Object.assign({persistent: true}, options))

    console.log(`Published message to ${queue}: `, json)
  }

  assertQueue (queue) {
    if (queue in this.assertedQueues)
      return

    this.channel.assertQueue(queue, {durable: true})
    this.assertedQueues[queue] = true
  }
}

module.exports = Messenger<|endoftext|>


<commit_before>$().ready(function() {
    // 根据屏幕宽度置顶样式
    var width = document.body.clientWidth,
        names = [
            '阿迪达斯',
            '新百伦',
            '耐克',
            '他她',
            '万斯',

            '匡威',
            '彪马',
            '拔佳',
            '森达',
            '百丽',

            '法鳄',
            '三叶草',
            '亚瑟士',
            '大嘴猴',
            '天美意',

            '奥卡索',
            '思加图',
            '接吻猫',
            '百思图',
            '鬼冢虎',

            '莱尔斯丹',
        ], found = {}, voice = true;

    if (width <= 400) {
        $('#finddiff-set').show();
        $('#finddiff-set').click(function() {
            window.location.reload();
        });
        return;
    }

    var logos = $('.logo-wrap').children(),
        idx = 0,
        stages = [
            {theme: 'min-logo-wrap', total: 8, bonus: 1},
            {theme: 'min-logo-wrap', total: 8, bonus: 1},
            {theme: 'min-logo-wrap', total: 8, bonus: 1},
            {theme: 'min-logo-wrap', total: 8, bonus: 1},
            {theme: 'min-logo-wrap', total: 8, bonus: 1},

            {theme: 'mid-logo-wrap', total: 10, bonus: 1},
            {theme: 'mid-logo-wrap', total: 10, bonus: 1},
            {theme: 'mid-logo-wrap', total: 10, bonus: 1},
            {theme: 'mid-logo-wrap', total: 10, bonus: 1},
            {theme: 'mid-logo-wrap', total: 10, bonus: 1},

            {theme: 'max-logo-wrap', total: 12, bonus: 1},
            {theme: 'max-logo-wrap', total: 12, bonus: 1},
            {theme: 'max-logo-wrap', total: 12, bonus: 1},
            {theme: 'max-logo-wrap', total: 12, bonus: 1},
            {theme: 'max-logo-wrap', total: 12, bonus: 1},
        ],
        finds = [],
        find = 0,
        playing = false,
        bonus = 0,
        seconds = 30;

    if (logos.length == 0) {
        alert('无Logo数据');
        return;
    }
    if (logos.length != names.length) {
        alert('Logo数据异常');
        return;
    }

    // add event
    $('.logo-wrap').on('click', '.active', function(e){
        if (!playing) { return; }

        var element = $(e.currentTarget);

        if (parseInt(element.attr('data-idx')) == find) {
            bonus += stages[idx].bonus;
            idx++;
            scene();
            if (voice) {
                $('#rightAudio')[0].play();
            }
        } else {
            $(".minus-seconds").css({
                left:element.position().left + 60,
                top:element.position().top + 30
            }).addClass('fadeout');

            $(e.currentTarget).addClass('wrong');

            setTimeout(function() {
                $(".minus-seconds").removeClass('fadeout');
                $(e.currentTarget).removeClass('wrong');
            }, 1000);

            seconds -= 3;
            if (voice) {
                $('#wrongAudio')[0].play();
            }
        }
    });

    function scene() {
        // next
        if (idx > stages.length) {
            idx = stages.length - 1;
        }

        // reset
        $('.logo-wrap').removeClass('min-logo-wrap');
        $('.logo-wrap').removeClass('mid-logo-wrap');
        $('.logo-wrap').removeClass('max-logo-wrap');
        $('.logo-wrap .active').removeClass('active');
        finds = [];

        // init
        var len = names.length;
        var stage = stages[idx], token = parseInt(12345678 * Math.random()) + parseInt(87654321 * Math.random());
        var map = {}, foundIdx;
        for (var i=0; i<stage.total; i++) {
            var position = (token + i) % len;
            finds.push(position);
        }

        foundIdx = parseInt(finds.length * Math.random());
        // 不能重复
        if (found[foundIdx]) {
            for (var i=parseInt(foundIdx * Math.random()); i<finds.length; i++) {
                if (!found[foundIdx]) {
                    foundIdx = i;
                    break;
                }
            }
        }

        //console.info(finds);
        //console.info(found);
        found[foundIdx] = 'used';
        find = finds[foundIdx];

        // render
        $('.logo-wrap').addClass(stage.theme);
        for (var i=0; i<finds.length; i++) {
            $(logos[finds[i]]).addClass('active');
            $(logos[finds[i]]).attr('data-idx', finds[i]);
        }
        $('.logo-name-wrap span').text(names[find]);
    }

    function start() {
        if (voice) {
            $('#timeAudio')[0].play();
        }

        $('#finddiff-entry').hide();
        $('#finddiff-end').hide();

        $('.start').removeClass("layer-show").addClass("layer");
        $('.end').removeClass("layer-show").addClass("layer");
        $('#finddiff-game').show();

        // 开始游戏
        playing = true;
        // 生成场景
        scene();
        // 倒计时
        seconds = 30;
        var timers = setInterval(function() {
            if (seconds <= 0) {
                playing = false;
                end();
                clearInterval(timers);
                update();
            } else {
                $('.remain-time').text('剩余时间：' + --seconds + '秒 金币数：' + bonus + '个');
                $('#finddiff-end .result-wrap span').text(bonus);
                if (seconds < 10) {
                    $('.remain-time').addClass('remain-time-warning');
                }
            }
        }, 1000);
    }

    function update() {
        $.ajax({
            type: 'GET',
            url: './' + $('#finddiff-game').attr('data-id') + '/bonus',
            data: { bonus: bonus, token: (new Date()).getTime() },
            dataType: 'text',
            success: function(data) {
                $('.remain-time').text('剩余时间：' + seconds + '秒 金币数：' + bonus + '个');
                setTimeout(function() {
                    $('#finddiff-last').hide();
                }, 3000);
            },
            error: function(xhr, type) {
                alert('无法保存游戏分数！');
            }
        })
    }

    function end() {
        $('#finddiff-entry').hide();
        $('#finddiff-game').hide();
        $('#finddiff-end').show();
        $('#timeAudio')[0].pause();
    }

    $('#finddiff-voice').click(function() {
        if ($('#finddiff-voice').hasClass('disable')) {
            voice = true;
            $('#finddiff-voice').removeClass('disable');
        } else {
            voice = false;
            $('#finddiff-voice').addClass('disable');
            $('#timeAudio')[0].pause();
        }
    });

    $('.start-btn').click(function() {
        start();
    });

    $('.restart-btn').click(function() {
        start();
    });

    $('.share-btn').click(function() {
        $("#finddiff-mask").show();
    });
    $("#finddiff-mask").click(function() {
        $(this).hide();
    });

    $('#finddiff-game').hide();
});<commit_msg>enhance<commit_after>$().ready(function() {
    // 根据屏幕宽度置顶样式
    var width = document.body.clientWidth,
        names = [
            '阿迪达斯',
            '新百伦',
            '耐克',
            '他她',
            '万斯',

            '匡威',
            '彪马',
            '拔佳',
            '森达',
            '百丽',

            '法鳄',
            '三叶草',
            '亚瑟士',
            '大嘴猴',
            '天美意',

            '奥卡索',
            '思加图',
            '接吻猫',
            '百思图',
            '鬼冢虎',

            '莱尔斯丹',
        ], found = {}, voice = true;

    if (width <= 400) {
        $('#finddiff-set').show();
        $('#finddiff-set').click(function() {
            window.location.reload();
        });
        return;
    }

    var logos = $('.logo-wrap').children(),
        idx = 0,
        stages = [
            {theme: 'min-logo-wrap', total: 8, bonus: 1},
            {theme: 'min-logo-wrap', total: 8, bonus: 1},
            {theme: 'min-logo-wrap', total: 8, bonus: 1},
            {theme: 'min-logo-wrap', total: 8, bonus: 1},
            {theme: 'min-logo-wrap', total: 8, bonus: 1},

            {theme: 'mid-logo-wrap', total: 10, bonus: 1},
            {theme: 'mid-logo-wrap', total: 10, bonus: 1},
            {theme: 'mid-logo-wrap', total: 10, bonus: 1},
            {theme: 'mid-logo-wrap', total: 10, bonus: 1},
            {theme: 'mid-logo-wrap', total: 10, bonus: 1},

            {theme: 'max-logo-wrap', total: 12, bonus: 1},
            {theme: 'max-logo-wrap', total: 12, bonus: 1},
            {theme: 'max-logo-wrap', total: 12, bonus: 1},
            {theme: 'max-logo-wrap', total: 12, bonus: 1},
            {theme: 'max-logo-wrap', total: 12, bonus: 1},
        ],
        finds = [],
        find = 0,
        playing = false,
        bonus = 0,
        seconds = 30;

    if (logos.length == 0) {
        alert('无Logo数据');
        return;
    }
    if (logos.length != names.length) {
        alert('Logo数据异常');
        return;
    }

    // add event
    $('.logo-wrap').on('click', '.active', function(e){
        if (!playing) { return; }

        var element = $(e.currentTarget);

        if (parseInt(element.attr('data-idx')) == find) {
            bonus += stages[idx].bonus;
            idx++;
            scene();
            if (voice) {
                $('#rightAudio')[0].play();
            }
        } else {
            $(".minus-seconds").css({
                left:element.position().left + 60,
                top:element.position().top + 30
            }).addClass('fadeout');

            $(e.currentTarget).addClass('wrong');

            setTimeout(function() {
                $(".minus-seconds").removeClass('fadeout');
                $(e.currentTarget).removeClass('wrong');
            }, 1000);

            seconds -= 3;
            if (voice) {
                $('#wrongAudio')[0].play();
            }
        }
    });

    function scene() {
        // next
        if (idx > stages.length) {
            idx = stages.length - 1;
        }

        // reset
        $('.logo-wrap').removeClass('min-logo-wrap');
        $('.logo-wrap').removeClass('mid-logo-wrap');
        $('.logo-wrap').removeClass('max-logo-wrap');
        $('.logo-wrap .active').removeClass('active');
        finds = [];

        // init
        var len = names.length;
        var stage = stages[idx], token = parseInt(12345678 * Math.random()) + parseInt(87654321 * Math.random());
        var map = {}, foundIdx;
        for (var i=0; i<stage.total; i++) {
            var position = (token + i) % len;
            finds.push(position);
        }

        foundIdx = parseInt(finds.length * Math.random());
        // 不能重复
        if (found[foundIdx]) {
            for (var i=parseInt(foundIdx * Math.random()); i<finds.length; i++) {
                if (!found[foundIdx]) {
                    foundIdx = i;
                    break;
                }
            }
        }

        //console.info(finds);
        //console.info(found);
        found[foundIdx] = 'used';
        find = finds[foundIdx];

        // render
        $('.logo-wrap').addClass(stage.theme);
        for (var i=0; i<finds.length; i++) {
            $(logos[finds[i]]).addClass('active');
            $(logos[finds[i]]).attr('data-idx', finds[i]);
        }
        $('.logo-name-wrap span').text(names[find]);
    }

    function start() {
        if (voice) {
            $('#timeAudio')[0].play();
        }

        $('#finddiff-entry').hide();
        $('#finddiff-end').hide();

        $('.start').removeClass("layer-show").addClass("layer");
        $('.end').removeClass("layer-show").addClass("layer");
        $('#finddiff-game').show();

        // 开始游戏
        playing = true;
        // 生成场景
        scene();
        // 倒计时
        seconds = 30;

        idx = 0;
        var timers = setInterval(function() {
            if (seconds <= 0) {
                playing = false;
                end();
                clearInterval(timers);
                update();
            } else {
                $('.remain-time').text('剩余时间：' + --seconds + '秒 金币数：' + bonus + '个');
                $('#finddiff-end .result-wrap span').text(bonus);
                if (seconds < 10) {
                    $('.remain-time').addClass('remain-time-warning');
                }
            }
        }, 1000);
    }

    function update() {
        $.ajax({
            type: 'GET',
            url: './' + $('#finddiff-game').attr('data-id') + '/bonus',
            data: { bonus: bonus, token: (new Date()).getTime() },
            dataType: 'text',
            success: function(data) {
                $('.remain-time').text('剩余时间：' + seconds + '秒 金币数：' + bonus + '个');
                setTimeout(function() {
                    $('#finddiff-last').hide();
                }, 3000);
            },
            error: function(xhr, type) {
                alert('无法保存游戏分数！');
            }
        })
    }

    function end() {
        $('#finddiff-entry').hide();
        $('#finddiff-game').hide();
        $('#finddiff-end').show();
        $('#timeAudio')[0].pause();
    }

    $('#finddiff-voice').click(function() {
        if ($('#finddiff-voice').hasClass('disable')) {
            voice = true;
            $('#finddiff-voice').removeClass('disable');
        } else {
            voice = false;
            $('#finddiff-voice').addClass('disable');
            $('#timeAudio')[0].pause();
        }
    });

    $('.start-btn').click(function() {
        start();
    });

    $('.restart-btn').click(function() {
        start();
    });

    $('.share-btn').click(function() {
        $("#finddiff-mask").show();
    });
    $("#finddiff-mask").click(function() {
        $(this).hide();
    });

    $('#finddiff-game').hide();
});<|endoftext|>


<commit_before>export default class ModelBase {
    static get _classNameKey() {
        return "__REACT_NATIVE_MODELS_CLASS_NAME__";
    }

    /**
     * className used instead name because babel replaces him at run-time.
     */
    static get className() {
        return "ModelBase";
    }

    /**
     * Create private properties and getProperty/setProperty methods for them.
     *
     * @param {object} properties propertyName: propertyType
     * @returns {ModelBase}
     */
    constructor(properties) {
        for (const propertyName in properties) {
            if (ModelBase._isOwnProperty(propertyName, properties) === false) {
                continue;
            }

            if (propertyName.charAt(0) === "_") {
                throw new Error("Properties beginning at underscore not supported.");
            }

            const propertyType = properties[propertyName];
            const privatePropertyName = "_" + propertyName;
            const propertyNameCapitalized =
                propertyName.charAt(0).toUpperCase() +
                propertyName.slice(1);

            this[privatePropertyName] = null;
            this["set" + propertyNameCapitalized] = (value) => {
                if (ModelBase._checkType(value, propertyType) === false) {
                    throw new TypeError(`"${propertyName}" is of type "${ModelBase._getTypeName(value)}". Expected "${propertyType}".`);
                }

                this[privatePropertyName] = value;
            };

            this["get" + propertyNameCapitalized] = () => {
                return this[privatePropertyName];
            };
        }
    }

    /**
     * Create state object, equivalent this model.
     *
     * @returns {object} state
     */
    createState() {
        const state = Object.create(null);
        for (const propertyName in this) {
            if (ModelBase._isOwnProperty(propertyName, this) === false) {
                continue;
            }

            if (propertyName.charAt(0) !== "_") {
                continue;
            }

            const statePropery = propertyName.slice(1);
            state[statePropery] = this[propertyName];
        }

        return state;
    }

    /**
     * Populate members of the model from state.
     *
     * @param {object} state state
     */
    populateFromState(state) {
        // Create instance of ModelBase which has property's types in closure.
        const constructor = ModelBase._getConstructor(this.constructor.className);
        const typeCheckObject = new constructor();

        for (const propertyName in state) {
            if (ModelBase._isOwnProperty(propertyName, state) === false) {
                continue;
            }

            const privatePropertyName = "_" + propertyName;
            const propertyNameCapitalized =
                propertyName.charAt(0).toUpperCase() +
                propertyName.slice(1);

            if (!(privatePropertyName in this)) {
                throw new Error(`Property "${propertyName}" does not exists in "${this.constructor.className}".`);
            }

            // Should throw exception if type of property is invalid.
            const setter = "set" + propertyNameCapitalized;
            typeCheckObject[setter](state[propertyName]);

            this[privatePropertyName] = state[propertyName];
        }
    }

    /**
     * Create new instance of the model from givent state.
     *
     * @static
     * @param {object} state
     * @param {object} properties
     * @return {ModelBase}
     */
    static fromState(state, properties) {
        const classConstructor = ModelBase._getConstructor(this.className);
        const model = new classConstructor(properties);
        model.populateFromState(state);
        return model;
    }

    static _isOwnProperty(propertyName, object) {
        return Object.prototype.hasOwnProperty.apply(object, [propertyName]);
    }

    /**
     * Check type of value and return true if his matches with requiredType.
     *
     * @static
     * @param {any} value
     * @param {string} requiredType
     * @returns {boolean}
     */
    static _checkType(value, requiredType) {
        return ModelBase._getTypeName(value) === requiredType;
    }

    static _getTypeName(value) {
        if (value === undefined) {
            return "Undefined";
        }

        if (value === null) {
            return "Object";
        }

        if (value.constructor === undefined || value.constructor.className === undefined) {
            const matches = Object.prototype.toString.call(value).match(/\[object (.+)\]/);

            if (matches === null) {
                throw new Error("Unknow type");
            }

            return matches[1];
        }

        return value.constructor.className;
    }

    /**
     * Serialize self.
     *
     * @returns {string} JSON string
     */
    serialize() {
        return JSON.stringify(ModelBase._serialize(this));
    }

    /**
     * Serialize recursively instance of ModelBase.
     *
     * @static
     * @param {ModelBase} object
     * @returns {object}
     */
    static _serialize(object) {
        const container = Object.create(null);
        container[ModelBase._classNameKey] = object.constructor.className;

        const data = Object.create(null);
        for (const key in object) {
            if (ModelBase._isOwnProperty(key, object) === false) {
                continue;
            }

            if (key.charAt(0) !== "_") {
                continue;
            }

            const value = object[key];

            if (ModelBase._isObjectOrArray(value, "serialization")) {
                data[key] = ModelBase._processObjectOrArray(value, "serialization");
            } else {
                data[key] = ModelBase._processScalar(value, "serialization");
            }
        }

        container["data"] = data;
        return container;
    }

    /**
     * Deserialize JSON string
     *
     * @static
     * @param {string} JSONString
     */
    static deserialize(JSONString) {
        return ModelBase._deserialize(JSON.parse(JSONString));
    }

    /**
     * Deserialize instance of ModelBase.
     *
     * @static
     * @param {object} container
     * @return {ModelBase}
     */
    static _deserialize(container) {
        const className = container[ModelBase._classNameKey];

        if (className === undefined) {
            throw new Error("Invalid object");
        }

        const constructor = ModelBase._getConstructor(className);
        const instance = new constructor();
        const data = container["data"];

        for (const key in data) {
            if (ModelBase._isOwnProperty(key, data) === false) {
                continue;
            }

            const value = data[key];

            if (ModelBase._isObjectOrArray(value, "deserialization")) {
                instance[key] = ModelBase._processObjectOrArray(value, "deserialization");
            } else {
                instance[key] = ModelBase._processScalar(value, "deserialization");
            }
        }

        return instance;
    }

    /**
     * Check if value is plain object or array.
     *
     * @static
     * @param {any} value
     * @returns {boolean}
     */
    static _isObjectOrArray(value, action = "serialization") {
        if (value === undefined || value === null) {
            return false;
        }

        if (action === "serialization" && value instanceof ModelBase) {
            return false;
        }

        if (action === "deserialization" && value[ModelBase._classNameKey] !== undefined) {
            return false;
        }

        return ModelBase._checkType(value, "Object") || ModelBase._checkType(value, "Array");
    }

    /**
     * Serialize/deserialize instance of ModelBase, Number, Boolean, String, Date.
     *
     * @static
     * @param {any} scalar
     * @param {string} action One of "serialization" or "deserialization".
     * @returns {any}
     */
    static _processScalar(scalar, action = "serialization") {
        if (scalar === undefined || scalar === null) {
            return scalar;
        }

        if (action === "serialization") {
            if (scalar instanceof ModelBase) {
                return ModelBase._serialize(scalar);
            }

            if (scalar instanceof Date) {
                throw new Error("Serialization of Date objects not supported.");
            }

            if (scalar instanceof RegExp) {
                throw new Error("Serialization of RegExp objects not supported.");
            }
        }

        if (action === "deserialization") {
            if (scalar[ModelBase._classNameKey]) {
                return ModelBase._deserialize(scalar);
            }
        }

        return scalar; /* Number, Boolean, String */
    }

    /**
     * Serialize/deserialization recursively plain object or array.
     *
     * @param {any} iterable object or array
     * @param {string} action One of "serialization" or "deserialization".
     * @returns {any}
     */
    static _processObjectOrArray(iterable, action = "serialization") {
        let data = null;

        if (Array.isArray(iterable)) {
            data = [];
            for (let i = 0; i < iterable.length; i++) {
                const value = iterable[i];
                if (ModelBase._isObjectOrArray(value, action)) {
                    data.push(ModelBase._processObjectOrArray(value, action));
                } else {
                    data.push(ModelBase._processScalar(value, action));
                }
            }
        } else {
            data = Object.create(null);

            for (const key in iterable) {
                if (ModelBase._isOwnProperty(key, iterable) === false) {
                    continue;
                }

                const value = iterable[key];

                if (ModelBase._isObjectOrArray(value, action)) {
                    data[key] = ModelBase._processObjectOrArray(value, action);
                } else {
                    data[key] = ModelBase._processScalar(value, action);
                }
            }
        }

        return data;
    }

    static require(classConstructor) {
        if (classConstructor === undefined) {
            throw new Error("constructor is undefined");
        }

        if (classConstructor.className === undefined) {
            throw new Error("constructor must have className static property.");
        }

        // if (classConstructor.className in ModelBase.classConstructors) {
        //     throw new Error(`${classConstructor.className} already in use.`);
        // }

        ModelBase.classConstructors[classConstructor.className] = classConstructor;
    }

    static _getConstructor(className) {
        if (!(className in ModelBase.classConstructors)) {
            throw new Error(`Unknow class. Use ${this.className}.require(${className}).`);
        }

        return ModelBase.classConstructors[className];
    }
}

ModelBase.classConstructors = Object.create(null);
<commit_msg>Fixed text of the comments.<commit_after>export default class ModelBase {
    static get _classNameKey() {
        return "__REACT_NATIVE_MODELS_CLASS_NAME__";
    }

    /**
     * className used instead of the name because babel replaces him at run-time.
     */
    static get className() {
        return "ModelBase";
    }

    /**
     * Create private properties and getProperty/setProperty methods for them.
     *
     * @param {object} properties propertyName: propertyType
     * @returns {ModelBase}
     */
    constructor(properties) {
        for (const propertyName in properties) {
            if (ModelBase._isOwnProperty(propertyName, properties) === false) {
                continue;
            }

            if (propertyName.charAt(0) === "_") {
                throw new Error("Properties beginning at underscore not supported.");
            }

            const propertyType = properties[propertyName];
            const privatePropertyName = "_" + propertyName;
            const propertyNameCapitalized =
                propertyName.charAt(0).toUpperCase() +
                propertyName.slice(1);

            this[privatePropertyName] = null;
            this["set" + propertyNameCapitalized] = (value) => {
                if (ModelBase._checkType(value, propertyType) === false) {
                    throw new TypeError(`"${propertyName}" is of type "${ModelBase._getTypeName(value)}". Expected "${propertyType}".`);
                }

                this[privatePropertyName] = value;
            };

            this["get" + propertyNameCapitalized] = () => {
                return this[privatePropertyName];
            };
        }
    }

    /**
     * Create state object, equivalent this model.
     *
     * @returns {object} state
     */
    createState() {
        const state = Object.create(null);
        for (const propertyName in this) {
            if (ModelBase._isOwnProperty(propertyName, this) === false) {
                continue;
            }

            if (propertyName.charAt(0) !== "_") {
                continue;
            }

            const statePropery = propertyName.slice(1);
            state[statePropery] = this[propertyName];
        }

        return state;
    }

    /**
     * Populate members of the model from the state.
     *
     * @param {object} state state
     */
    populateFromState(state) {
        // Create the instance of ModelBase which has property's types in closure.
        const constructor = ModelBase._getConstructor(this.constructor.className);
        const typeCheckObject = new constructor();

        for (const propertyName in state) {
            if (ModelBase._isOwnProperty(propertyName, state) === false) {
                continue;
            }

            const privatePropertyName = "_" + propertyName;
            const propertyNameCapitalized =
                propertyName.charAt(0).toUpperCase() +
                propertyName.slice(1);

            if (!(privatePropertyName in this)) {
                throw new Error(`Property "${propertyName}" does not exists in "${this.constructor.className}".`);
            }

            // Should throw exception if type of property is invalid.
            const setter = "set" + propertyNameCapitalized;
            typeCheckObject[setter](state[propertyName]);

            this[privatePropertyName] = state[propertyName];
        }
    }

    /**
     * Create a new instance of the model from given state.
     *
     * @static
     * @param {object} state
     * @param {object} properties
     * @return {ModelBase}
     */
    static fromState(state, properties) {
        const classConstructor = ModelBase._getConstructor(this.className);
        const model = new classConstructor(properties);
        model.populateFromState(state);
        return model;
    }

    static _isOwnProperty(propertyName, object) {
        return Object.prototype.hasOwnProperty.apply(object, [propertyName]);
    }

    /**
     * Check the type of value and return true if his matches with requiredType.
     *
     * @static
     * @param {any} value
     * @param {string} requiredType
     * @returns {boolean}
     */
    static _checkType(value, requiredType) {
        return ModelBase._getTypeName(value) === requiredType;
    }

    static _getTypeName(value) {
        if (value === undefined) {
            return "Undefined";
        }

        if (value === null) {
            return "Object";
        }

        if (value.constructor === undefined || value.constructor.className === undefined) {
            const matches = Object.prototype.toString.call(value).match(/\[object (.+)\]/);

            if (matches === null) {
                throw new Error("Unknow type");
            }

            return matches[1];
        }

        return value.constructor.className;
    }

    /**
     * Serialize self.
     *
     * @returns {string} JSON string
     */
    serialize() {
        return JSON.stringify(ModelBase._serialize(this));
    }

    /**
     * Serialize recursively instance of ModelBase.
     *
     * @static
     * @param {ModelBase} object
     * @returns {object}
     */
    static _serialize(object) {
        const container = Object.create(null);
        container[ModelBase._classNameKey] = object.constructor.className;

        const data = Object.create(null);
        for (const key in object) {
            if (ModelBase._isOwnProperty(key, object) === false) {
                continue;
            }

            if (key.charAt(0) !== "_") {
                continue;
            }

            const value = object[key];

            if (ModelBase._isObjectOrArray(value, "serialization")) {
                data[key] = ModelBase._processObjectOrArray(value, "serialization");
            } else {
                data[key] = ModelBase._processScalar(value, "serialization");
            }
        }

        container["data"] = data;
        return container;
    }

    /**
     * Deserialize JSON string
     *
     * @static
     * @param {string} JSONString
     */
    static deserialize(JSONString) {
        return ModelBase._deserialize(JSON.parse(JSONString));
    }

    /**
     * Deserialize instance of ModelBase.
     *
     * @static
     * @param {object} container
     * @return {ModelBase}
     */
    static _deserialize(container) {
        const className = container[ModelBase._classNameKey];

        if (className === undefined) {
            throw new Error("Invalid object");
        }

        const constructor = ModelBase._getConstructor(className);
        const instance = new constructor();
        const data = container["data"];

        for (const key in data) {
            if (ModelBase._isOwnProperty(key, data) === false) {
                continue;
            }

            const value = data[key];

            if (ModelBase._isObjectOrArray(value, "deserialization")) {
                instance[key] = ModelBase._processObjectOrArray(value, "deserialization");
            } else {
                instance[key] = ModelBase._processScalar(value, "deserialization");
            }
        }

        return instance;
    }

    /**
     * Check if the value is a plain object or array.
     *
     * @static
     * @param {any} value
     * @returns {boolean}
     */
    static _isObjectOrArray(value, action = "serialization") {
        if (value === undefined || value === null) {
            return false;
        }

        if (action === "serialization" && value instanceof ModelBase) {
            return false;
        }

        if (action === "deserialization" && value[ModelBase._classNameKey] !== undefined) {
            return false;
        }

        return ModelBase._checkType(value, "Object") || ModelBase._checkType(value, "Array");
    }

    /**
     * Serialize/deserialize instance of ModelBase, Number, Boolean, String, Date.
     *
     * @static
     * @param {any} scalar
     * @param {string} action One of "serialization" or "deserialization".
     * @returns {any}
     */
    static _processScalar(scalar, action = "serialization") {
        if (scalar === undefined || scalar === null) {
            return scalar;
        }

        if (action === "serialization") {
            if (scalar instanceof ModelBase) {
                return ModelBase._serialize(scalar);
            }

            if (scalar instanceof Date) {
                throw new Error("Serialization of Date objects not supported.");
            }

            if (scalar instanceof RegExp) {
                throw new Error("Serialization of RegExp objects not supported.");
            }
        }

        if (action === "deserialization") {
            if (scalar[ModelBase._classNameKey]) {
                return ModelBase._deserialize(scalar);
            }
        }

        return scalar; /* Number, Boolean, String */
    }

    /**
     * Serialize/deserialization recursively plain object or array.
     *
     * @param {any} iterable object or array
     * @param {string} action One of "serialization" or "deserialization".
     * @returns {any}
     */
    static _processObjectOrArray(iterable, action = "serialization") {
        let data = null;

        if (Array.isArray(iterable)) {
            data = [];
            for (let i = 0; i < iterable.length; i++) {
                const value = iterable[i];
                if (ModelBase._isObjectOrArray(value, action)) {
                    data.push(ModelBase._processObjectOrArray(value, action));
                } else {
                    data.push(ModelBase._processScalar(value, action));
                }
            }
        } else {
            data = Object.create(null);

            for (const key in iterable) {
                if (ModelBase._isOwnProperty(key, iterable) === false) {
                    continue;
                }

                const value = iterable[key];

                if (ModelBase._isObjectOrArray(value, action)) {
                    data[key] = ModelBase._processObjectOrArray(value, action);
                } else {
                    data[key] = ModelBase._processScalar(value, action);
                }
            }
        }

        return data;
    }

    static require(classConstructor) {
        if (classConstructor === undefined) {
            throw new Error("constructor is undefined");
        }

        if (classConstructor.className === undefined) {
            throw new Error("constructor must have className static property.");
        }

        // if (classConstructor.className in ModelBase.classConstructors) {
        //     throw new Error(`${classConstructor.className} already in use.`);
        // }

        ModelBase.classConstructors[classConstructor.className] = classConstructor;
    }

    static _getConstructor(className) {
        if (!(className in ModelBase.classConstructors)) {
            throw new Error(`Unknow class. Use ${this.className}.require(${className}).`);
        }

        return ModelBase.classConstructors[className];
    }
}

ModelBase.classConstructors = Object.create(null);
<|endoftext|>


<commit_before>const path = require('path')
const fs = require('fs')

const resolve = dir => {
  return path.join(__dirname, dir)
}

const env = process.env.NODE_ENV || 'development'
fs.writeFileSync(path.join(__dirname, './config/env.js'), `export default '${env}'
`)

// 这里需要修改为你线上项目存放的路径
// 比如你打包的文件放到服务器的my-app文件夹，域名为a.com，则应改为
// http(s)://a.com/my-app/
const BASE_URL = 'https://iview.github.io/iview-admin/'

module.exports = {
  // Project deployment base
  // By default we assume your app will be deployed at the root of a domain,
  // e.g. https://www.my-app.com/
  // If your app is deployed at a sub-path, you will need to specify that
  // sub-path here. For example, if your app is deployed at
  // https://www.foobar.com/my-app/
  // then change this to '/my-app/'
  baseUrl: BASE_URL,
  // tweak internal webpack configuration.
  // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
  chainWebpack: config => {
    config.resolve.alias
      .set('@', resolve('src')) // key,value自行定义，比如.set('@@', resolve('src/components'))
      .set('_c', resolve('src/components'))
      .set('_conf', resolve('config'))
  }
}
<commit_msg>修改base_url<commit_after>const path = require('path')
const fs = require('fs')

const resolve = dir => {
  return path.join(__dirname, dir)
}

const env = process.env.NODE_ENV || 'development'
fs.writeFileSync(path.join(__dirname, './config/env.js'), `export default '${env}'
`)

// 项目部署基础
// 默认情况下，我们假设你的应用将被部署在域的根目录下,
// 例如：https://www.my-app.com/
// 如果您的应用程序部署在子路径中，则需要在这指定子路径
// 例如：https://www.foobar.com/my-app/
// 需要将它改为'/my-app/'
const BASE_URL = '/'

module.exports = {
  // Project deployment base
  // By default we assume your app will be deployed at the root of a domain,
  // e.g. https://www.my-app.com/
  // If your app is deployed at a sub-path, you will need to specify that
  // sub-path here. For example, if your app is deployed at
  // https://www.foobar.com/my-app/
  // then change this to '/my-app/'
  baseUrl: BASE_URL,
  // tweak internal webpack configuration.
  // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
  chainWebpack: config => {
    config.resolve.alias
      .set('@', resolve('src')) // key,value自行定义，比如.set('@@', resolve('src/components'))
      .set('_c', resolve('src/components'))
      .set('_conf', resolve('config'))
  }
}
<|endoftext|>


<commit_before>var path = require('path');
var npm = require('npm');
var fs = require('fs');

// must be run in Me directory!
// MUST HAVE CONSISTENT SYNCLET DIRS, xxxx-1 ones will be skipped!

var regBase = 'http://registry.singly.com';
var npmConfig = {registry:regBase, cache:'.npm'};

// make sure node_modules and set npm to run locally here
try {
    fs.mkdirSync("node_modules", 0755); // ensure a local home for npm to use
} catch(E) {}
npm.load(npmConfig, function(err) {
    if(err) {
        console.error(err);
        process.exit(1);
    }
    meScan();
})

function meScan()
{
    var dirs = fs.readdirSync(".");
    for (var i = 0; i < dirs.length; i++) {
        if(dirs[i] == "diary") continue;
        var dir =  dirs[i];
        try {
            if(!fs.statSync(dir).isDirectory()) continue;
            if(!path.existsSync(path.join(dir, 'me.json'))) continue;
            var js = JSON.parse(fs.readFileSync(path.join(dir, 'me.json'), 'utf8'));
            // first skip if it's done
            if(js.installed) {
                console.log("already did "+dir);
                continue;
            };
            if(js.srcdir.indexOf("Collections") == 0)
            {
                console.log("merging collection "+dir);
                var uc = dir.charAt(0).toUpperCase() + dir.substr(1);
                var js2 = JSON.parse(fs.readFileSync(path.join("../Collections", uc, dir+'.collection'), 'utf8'));
                js = extend(js, js2);
                js.manifest = path.join("Collections", uc, dir+'.collection');
                js.handle = dir;
                saver(js);
                continue;
            }
            // dir name must match registry name
            js.handle = dir;
            // GITHUB IS SPECIAL
            if(js.srcdir && js.srcdir.indexOf("Me/github/") == 0)
            {
                console.log("merging github "+dir);
                var app = getapp(js.srcdir.substr(3)); // we're in Me/ already
                var js2 = JSON.parse(fs.readFileSync(app, 'utf8'));
                js = extend(js, js2);
                js.manifest = "Me/"+app;
                saver(js);
                continue;
            }
            // this happens async from here on out
            console.log("installing from registry "+js.handle);
            install(js);
        } catch (E) {
            console.error(dirs[i]+" failed (" +E+ ")");
        }
    }

}

function install(js)
{
    npm.commands.install([js.handle], function(err){
        if(err){ // only warn here cuz we can re-run if install fails
            console.log("failed to install "+js.handle+": "+err);
            return;
        }
        var js2 = JSON.parse(fs.readFileSync(path.join("node_modules", js.handle, 'package.json'), 'utf8'));
        js = extend(js, js2.repository);
        js.manifest = path.join("Me/node_modules", js.handle, 'package.json');
        saver(js);
    });

}

function saver(js)
{
    js.id = js.provider = js.handle; // kinda legacy where they could differ
    js.srcdir = path.dirname(js.manifest);
    js.installed = Date.now();
    fs.writeFileSync(path.join(js.id, 'me.json'), JSON.stringify(js, null, 4));

}

// UGH
function getapp(dir)
{
    var dirs = fs.readdirSync(dir);
    for (var i = 0; i < dirs.length; i++) {
        if(dirs[i].indexOf(".app") > 0) return path.join(dir, dirs[i]);
    }
    return false;
}

// copied from lutil
function extend() {
  // copy reference to target object
  var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;

  // Handle a deep copy situation
  if (typeof target === "boolean") {
    deep = target;
    target = arguments[1] || {};
    // skip the boolean and the target
    i = 2;
  }

  // Handle case when target is a string or something (possible in deep copy)
  if (typeof target !== "object" && typeof target !== 'function')
    target = {};

  var isPlainObject = function(obj) {
    // Must be an Object.
    // Because of IE, we also have to check the presence of the constructor property.
    // Make sure that DOM nodes and window objects don't pass through, as well
    if (!obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval)
      return false;

    var has_own_constructor = hasOwnProperty.call(obj, "constructor");
    var has_is_property_of_method = hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf");
    // Not own constructor property must be Object
    if (obj.constructor && !has_own_constructor && !has_is_property_of_method)
      return false;

    // Own properties are enumerated firstly, so to speed up,
    // if last one is own, then all properties are own.

    var last_key;
    for (var key in obj)
      last_key = key;

    return typeof last_key === "undefined" || hasOwnProperty.call(obj, last_key);
  };

  for (; i < length; i++) {
    // Only deal with non-null/undefined values
    if ((options = arguments[i]) !== null) {
      // Extend the base object
      for (name in options) {
        src = target[name];
        copy = options[name];

        // Prevent never-ending loop
        if (target === copy)
            continue;

        // Recurse if we're merging object literal values or arrays
        if (deep && copy && (isPlainObject(copy) || Array.isArray(copy))) {
          var clone = src && (isPlainObject(src) || Array.isArray(src)) ? src : Array.isArray(copy) ? [] : {};

          // Never move original objects, clone them
          target[name] = exports.extend(deep, clone, copy);

        // Don't bring in undefined values
        } else if (typeof copy !== "undefined")
          target[name] = copy;
      }
    }
  }

  // Return the modified object
  return target;
};<commit_msg>set authed flag too<commit_after>var path = require('path');
var npm = require('npm');
var fs = require('fs');

// must be run in Me directory!
// MUST HAVE CONSISTENT SYNCLET DIRS, xxxx-1 ones will be skipped!

var regBase = 'http://registry.singly.com';
var npmConfig = {registry:regBase, cache:'.npm'};

// make sure node_modules and set npm to run locally here
try {
    fs.mkdirSync("node_modules", 0755); // ensure a local home for npm to use
} catch(E) {}
npm.load(npmConfig, function(err) {
    if(err) {
        console.error(err);
        process.exit(1);
    }
    meScan();
})

function meScan()
{
    var dirs = fs.readdirSync(".");
    for (var i = 0; i < dirs.length; i++) {
        if(dirs[i] == "diary") continue;
        var dir =  dirs[i];
        try {
            if(!fs.statSync(dir).isDirectory()) continue;
            if(!path.existsSync(path.join(dir, 'me.json'))) continue;
            var js = JSON.parse(fs.readFileSync(path.join(dir, 'me.json'), 'utf8'));
            // first skip if it's done
            if(js.installed) {
                console.log("already did "+dir);
                continue;
            };
            if(js.srcdir.indexOf("Collections") == 0)
            {
                console.log("merging collection "+dir);
                var uc = dir.charAt(0).toUpperCase() + dir.substr(1);
                var js2 = JSON.parse(fs.readFileSync(path.join("../Collections", uc, dir+'.collection'), 'utf8'));
                js = extend(js, js2);
                js.manifest = path.join("Collections", uc, dir+'.collection');
                js.handle = dir;
                saver(js);
                continue;
            }
            // dir name must match registry name
            js.handle = dir;
            // GITHUB IS SPECIAL
            if(js.srcdir && js.srcdir.indexOf("Me/github/") == 0)
            {
                console.log("merging github "+dir);
                var app = getapp(js.srcdir.substr(3)); // we're in Me/ already
                var js2 = JSON.parse(fs.readFileSync(app, 'utf8'));
                js = extend(js, js2);
                js.manifest = "Me/"+app;
                saver(js);
                continue;
            }
            // this happens async from here on out
            console.log("installing from registry "+js.handle);
            install(js);
        } catch (E) {
            console.error(dirs[i]+" failed (" +E+ ")");
        }
    }

}

function install(js)
{
    npm.commands.install([js.handle], function(err){
        if(err){ // only warn here cuz we can re-run if install fails
            console.log("failed to install "+js.handle+": "+err);
            return;
        }
        var js2 = JSON.parse(fs.readFileSync(path.join("node_modules", js.handle, 'package.json'), 'utf8'));
        js = extend(js, js2.repository);
        if(js.auth) js.authed = Date.now();
        js.manifest = path.join("Me/node_modules", js.handle, 'package.json');
        saver(js);
    });

}

function saver(js)
{
    js.id = js.provider = js.handle; // kinda legacy where they could differ
    js.srcdir = path.dirname(js.manifest);
    js.installed = Date.now();
    fs.writeFileSync(path.join(js.id, 'me.json'), JSON.stringify(js, null, 4));

}

// UGH
function getapp(dir)
{
    var dirs = fs.readdirSync(dir);
    for (var i = 0; i < dirs.length; i++) {
        if(dirs[i].indexOf(".app") > 0) return path.join(dir, dirs[i]);
    }
    return false;
}

// copied from lutil
function extend() {
  // copy reference to target object
  var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;

  // Handle a deep copy situation
  if (typeof target === "boolean") {
    deep = target;
    target = arguments[1] || {};
    // skip the boolean and the target
    i = 2;
  }

  // Handle case when target is a string or something (possible in deep copy)
  if (typeof target !== "object" && typeof target !== 'function')
    target = {};

  var isPlainObject = function(obj) {
    // Must be an Object.
    // Because of IE, we also have to check the presence of the constructor property.
    // Make sure that DOM nodes and window objects don't pass through, as well
    if (!obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval)
      return false;

    var has_own_constructor = hasOwnProperty.call(obj, "constructor");
    var has_is_property_of_method = hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf");
    // Not own constructor property must be Object
    if (obj.constructor && !has_own_constructor && !has_is_property_of_method)
      return false;

    // Own properties are enumerated firstly, so to speed up,
    // if last one is own, then all properties are own.

    var last_key;
    for (var key in obj)
      last_key = key;

    return typeof last_key === "undefined" || hasOwnProperty.call(obj, last_key);
  };

  for (; i < length; i++) {
    // Only deal with non-null/undefined values
    if ((options = arguments[i]) !== null) {
      // Extend the base object
      for (name in options) {
        src = target[name];
        copy = options[name];

        // Prevent never-ending loop
        if (target === copy)
            continue;

        // Recurse if we're merging object literal values or arrays
        if (deep && copy && (isPlainObject(copy) || Array.isArray(copy))) {
          var clone = src && (isPlainObject(src) || Array.isArray(src)) ? src : Array.isArray(copy) ? [] : {};

          // Never move original objects, clone them
          target[name] = exports.extend(deep, clone, copy);

        // Don't bring in undefined values
        } else if (typeof copy !== "undefined")
          target[name] = copy;
      }
    }
  }

  // Return the modified object
  return target;
};<|endoftext|>


<commit_before>/**
 * Mousetrap wrapper for AngularJS
 * @version v0.0.1 - 2013-12-30
 * @link https://github.com/mgonto/mgo-mousetrap
 * @author Martin Gontovnikas <martin@gon.to>
 * @license MIT License, http://www.opensource.org/licenses/MIT
 */
angular.module('mgo-mousetrap', []).directive('wMousetrap', function () {
    return {
        restrict: 'A',
        controller: ['$scope', '$element', '$attrs',
                     function ($scope, $element, $attrs) {

            $scope.$watch($attrs.wMousetrap, function(mousetrap) {
                _.forIn(mousetrap, function(value, key) {
                   Mousetrap.unbind(key);
                   Mousetrap.bind(key, applyWrapper(value));
                });
            }, true);

            function applyWrapper(func) {
                return function(e) {
                    $scope.$apply(function() {
                        func(e);
                    });
                };
            }

        }]
    }
});
<commit_msg>Removed Lodash dependency<commit_after>/**
 * Mousetrap wrapper for AngularJS
 * @version v0.0.1 - 2013-12-30
 * @link https://github.com/mgonto/mgo-mousetrap
 * @author Martin Gontovnikas <martin@gon.to>
 * @license MIT License, http://www.opensource.org/licenses/MIT
 */
angular.module('mgo-mousetrap', []).directive('wMousetrap', function () {
    return {
        restrict: 'A',
        controller: ['$scope', '$element', '$attrs',
                     function ($scope, $element, $attrs) {

            $scope.$watch($attrs.wMousetrap, function(mousetrap) {
                for (var key in mousetrap) {
                    if (mousetrap.hasOwnProperty(key)) {
                        Mousetrap.unbind(key);
                        Mousetrap.bind(key, applyWrapper(mousetrap[key]));
                    }
                }
            }, true);

            function applyWrapper(func) {
                return function(e) {
                    $scope.$apply(function() {
                        func(e);
                    });
                };
            }

        }]
    }
});
<|endoftext|>


<commit_before>import crypto from 'crypto';
import {IPreset} from '../interface';

const IV_LEN = 16;

// available ciphers
const ciphers = [
  // both supported
  'aes-128-ctr', 'aes-192-ctr', 'aes-256-ctr',
  'aes-128-cfb', 'aes-192-cfb', 'aes-256-cfb',
  'camellia-128-cfb', 'camellia-192-cfb', 'camellia-256-cfb',
  // blinksocks supported
  'aes-128-ofb', 'aes-192-ofb', 'aes-256-ofb',
  'aes-128-cbc', 'aes-192-cbc', 'aes-256-cbc'
];

/**
 * md5 message digest
 * @param buffer
 * @returns {*}
 */
function md5(buffer) {
  const md5 = crypto.createHash('md5');
  md5.update(buffer);
  return md5.digest();
}

/**
 * EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt
 *
 * @algorithm
 *   D_i = HASH^count(D_(i-1) || data || salt)
 */
function EVP_BytesToKey(password, keyLen, ivLen = IV_LEN) {
  let _data = Buffer.from(password);
  let i = 0;
  const bufs = [];
  while (Buffer.concat(bufs).length < (keyLen + ivLen)) {
    if (i > 0) {
      _data = Buffer.concat([bufs[i - 1], Buffer.from(password)]);
    }
    bufs.push(md5(_data));
    i += 1;
  }
  return Buffer.concat(bufs).slice(0, keyLen);
}

/**
 * @description
 *   Perform encrypt/decrypt using Node.js 'crypto' module(OpenSSL wrappers).
 *
 * @params
 *   cipher (String): Which cipher is picked from OpenSSL library.
 *
 * @examples
 *   "crypto": "openssl"
 *   "crypto_params": "aes-256-cfb"
 *
 * @protocol
 *
 *   # TCP handshake
 *   +-------+----------------------------+
 *   |  IV   |          PAYLOAD           |
 *   +-------+----------------------------+
 *   | Fixed |         Variable           |
 *   +-------+----------------------------+
 *
 *   # TCP chunk
 *   +----------------------------+
 *   |          PAYLOAD           |
 *   +----------------------------+
 *   |         Variable           |
 *   +----------------------------+
 *
 * @explain
 *   1. Key derivation function is EVP_BytesToKey.
 *   2. IV is plaintext.
 *   3. Client Cipher IV = Server Decipher IV, vice versa.
 *
 * @reference
 *   [1] EVP_BytesToKey
 *       https://www.openssl.org/docs/man1.0.2/crypto/EVP_BytesToKey.html
 *       https://github.com/shadowsocks/shadowsocks/blob/master/shadowsocks/cryptor.py#L53
 */
export default class OpenSSLCrypto extends IPreset {

  _cipherName = '';

  _key = null;

  _cipher = null;

  _decipher = null;

  constructor(cipherName) {
    super();
    if (typeof cipherName !== 'string' || cipherName === '') {
      throw Error('\'crypto_params\' requires [cipher] parameter.');
    }
    if (!ciphers.includes(cipherName)) {
      throw Error(`cipher \'${cipherName}\' is not supported.`);
    }
    this._cipherName = cipherName;
    this._key = EVP_BytesToKey(__KEY__, this._cipherName.split('-')[1] / 8);
  }

  beforeOut({buffer}) {
    if (!this._cipher) {
      const iv = crypto.randomBytes(IV_LEN);
      this._cipher = crypto.createCipheriv(this._cipherName, this._key, iv);
      return Buffer.concat([iv, this.encrypt(buffer)]);
    } else {
      return this.encrypt(buffer);
    }
  }

  beforeIn({buffer}) {
    if (!this._decipher) {
      const iv = buffer.slice(0, IV_LEN);
      this._decipher = crypto.createDecipheriv(this._cipherName, this._key, iv);
      return this.decrypt(buffer.slice(IV_LEN));
    } else {
      return this.decrypt(buffer);
    }
  }

  encrypt(buffer) {
    return this._cipher.update(buffer);
  }

  decrypt(buffer) {
    return this._decipher.update(buffer);
  }

}
<commit_msg>chore(crypto): export EVP_BytesToKey<commit_after>import crypto from 'crypto';
import {IPreset} from '../interface';

const IV_LEN = 16;

// available ciphers
const ciphers = [
  // both supported
  'aes-128-ctr', 'aes-192-ctr', 'aes-256-ctr',
  'aes-128-cfb', 'aes-192-cfb', 'aes-256-cfb',
  'camellia-128-cfb', 'camellia-192-cfb', 'camellia-256-cfb',
  // blinksocks supported
  'aes-128-ofb', 'aes-192-ofb', 'aes-256-ofb',
  'aes-128-cbc', 'aes-192-cbc', 'aes-256-cbc'
];

/**
 * md5 message digest
 * @param buffer
 * @returns {*}
 */
function md5(buffer) {
  const md5 = crypto.createHash('md5');
  md5.update(buffer);
  return md5.digest();
}

/**
 * EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt
 *
 * @algorithm
 *   D_i = HASH^count(D_(i-1) || data || salt)
 */
export function EVP_BytesToKey(password, keyLen, ivLen = IV_LEN) {
  let _data = Buffer.from(password);
  let i = 0;
  const bufs = [];
  while (Buffer.concat(bufs).length < (keyLen + ivLen)) {
    if (i > 0) {
      _data = Buffer.concat([bufs[i - 1], Buffer.from(password)]);
    }
    bufs.push(md5(_data));
    i += 1;
  }
  return Buffer.concat(bufs).slice(0, keyLen);
}

/**
 * @description
 *   Perform encrypt/decrypt using Node.js 'crypto' module(OpenSSL wrappers).
 *
 * @params
 *   cipher (String): Which cipher is picked from OpenSSL library.
 *
 * @examples
 *   "crypto": "openssl"
 *   "crypto_params": "aes-256-cfb"
 *
 * @protocol
 *
 *   # TCP handshake
 *   +-------+----------------------------+
 *   |  IV   |          PAYLOAD           |
 *   +-------+----------------------------+
 *   | Fixed |         Variable           |
 *   +-------+----------------------------+
 *
 *   # TCP chunk
 *   +----------------------------+
 *   |          PAYLOAD           |
 *   +----------------------------+
 *   |         Variable           |
 *   +----------------------------+
 *
 * @explain
 *   1. Key derivation function is EVP_BytesToKey.
 *   2. IV is plaintext.
 *   3. Client Cipher IV = Server Decipher IV, vice versa.
 *
 * @reference
 *   [1] EVP_BytesToKey
 *       https://www.openssl.org/docs/man1.0.2/crypto/EVP_BytesToKey.html
 *       https://github.com/shadowsocks/shadowsocks/blob/master/shadowsocks/cryptor.py#L53
 */
export default class OpenSSLCrypto extends IPreset {

  _cipherName = '';

  _key = null;

  _cipher = null;

  _decipher = null;

  constructor(cipherName) {
    super();
    if (typeof cipherName !== 'string' || cipherName === '') {
      throw Error('\'crypto_params\' requires [cipher] parameter.');
    }
    if (!ciphers.includes(cipherName)) {
      throw Error(`cipher \'${cipherName}\' is not supported.`);
    }
    this._cipherName = cipherName;
    this._key = EVP_BytesToKey(__KEY__, this._cipherName.split('-')[1] / 8);
  }

  beforeOut({buffer}) {
    if (!this._cipher) {
      const iv = crypto.randomBytes(IV_LEN);
      this._cipher = crypto.createCipheriv(this._cipherName, this._key, iv);
      return Buffer.concat([iv, this.encrypt(buffer)]);
    } else {
      return this.encrypt(buffer);
    }
  }

  beforeIn({buffer}) {
    if (!this._decipher) {
      const iv = buffer.slice(0, IV_LEN);
      this._decipher = crypto.createDecipheriv(this._cipherName, this._key, iv);
      return this.decrypt(buffer.slice(IV_LEN));
    } else {
      return this.decrypt(buffer);
    }
  }

  encrypt(buffer) {
    return this._cipher.update(buffer);
  }

  decrypt(buffer) {
    return this._decipher.update(buffer);
  }

}
<|endoftext|>


<commit_before>var MapModule = require("./map_module");
var indoors = require("../public/indoors/indoors");
var IndoorWatermarkController = require("./indoor_watermark_controller");

var IndoorsModule = function(emscriptenApi, mapController, mapId, indoorId, floorIndex, center, headingDegrees, zoom, showWrldWatermark) {

    var _emscriptenApi = emscriptenApi;
    var _mapController = mapController;

    var _activeIndoorMap = null;
    var _entrances = {};

    var _ready = false;
    var _pendingEnterTransition = null;
    var _transitioningToIndoorMap = false;

    var _indoorWatermarkController = new IndoorWatermarkController(mapId, showWrldWatermark);

    var _startingIndoorId = indoorId;
    var _startingFloorIndex = floorIndex;
    var _center = center;
    var _headingDegrees = headingDegrees;
    var _zoom = zoom;

    var _this = this;

    var _createIndoorMapObject = function() {
        var mapId = _emscriptenApi.indoorsApi.getActiveIndoorMapId();
        var mapName = _emscriptenApi.indoorsApi.getActiveIndoorMapName();
        var sourceVendor = _emscriptenApi.indoorsApi.getActiveIndoorMapSourceVendor();
        var floorCount = _emscriptenApi.indoorsApi.getActiveIndoorMapFloorCount();
        var floors = _createFloorsArray(floorCount);
        var searchTags = _createSearchTagsArray();
        var exitFunc = _this.exit;
        var indoorMap = new indoors.IndoorMap(mapId, mapName, sourceVendor, floorCount, floors, searchTags, exitFunc);
        return indoorMap;
    };

    var _createFloorsArray = function(floorCount) {
        var floors = [];
        for (var i=0; i<floorCount; ++i) {
            var floorIndex = i;
            var floorName = _emscriptenApi.indoorsApi.getFloorName(i);
            var floorShortName = _emscriptenApi.indoorsApi.getFloorShortName(i);
            var floorNumber = _emscriptenApi.indoorsApi.getFloorNumber(i);

            var floorId = floorNumber;

            var floor = new indoors.IndoorMapFloor(floorId, floorIndex, floorName, floorShortName);
            floors.push(floor);
        }
        return floors;
    };

    var _createSearchTagsArray = function() {
        var userData;
        try {
            userData = JSON.parse(_emscriptenApi.indoorsApi.getActiveIndoorMapUserData());
        }
        catch (e) {
            return [];
        }

        if (typeof userData.search_menu_items !== "object") { return []; }
        if (!(userData.search_menu_items.items instanceof Array)) { return []; }

        var searchTags = [];
        userData.search_menu_items.items.forEach(function(item) {
            searchTags.push({
                name: item.name,
                search_tag: item.search_tag,
                icon_key: item.icon_key
            });
        });
        return searchTags;
    };

    var _executeIndoorMapEnteredCallbacks = function() {
        _activeIndoorMap = _createIndoorMapObject();
        _this.fire("indoormapenter", {indoorMap: _activeIndoorMap});
    };

    var _executeIndoorMapEnterFailedCallbacks = function() {
        _this.fire("indoormapenterfailed", {});
    };

    var _executeIndoorMapExitedCallbacks = function() {
        var indoorMap = _activeIndoorMap;
        _activeIndoorMap = null;
        _indoorWatermarkController.hideWatermark();
        _this.fire("indoormapexit", {indoorMap: indoorMap});
    };

    var _executeIndoorMapFloorChangedCallbacks = function() {
        _this.fire("indoormapfloorchange", {floor: _this.getFloor()});
    };

    var _executeIndoorMapEntranceAddedCallbacks = function(indoorMapId, indoorMapName, indoorMapLatLng) {
        // discard the altitude, as we're going to use the SDK IPointOnMap positioning to snap it to the terrain (this is now default)
        // alternative is to use the altitude, but use "elevationMode: heightAboveSeaLevel" when creating the indoor entrance marker
        var entrance = new indoors.IndoorMapEntrance(indoorMapId, indoorMapName, L.latLng([indoorMapLatLng.lat, indoorMapLatLng.lng]));
        _entrances[entrance.getIndoorMapId()] = entrance;
        _this.fire("indoorentranceadd", {entrance: entrance});
    };

    var _executeIndoorMapEntranceRemovedCallbacks = function(indoorMapId, indoorMapName, indoorMapLatLng) {
        var entrance = new indoors.IndoorMapEntrance(indoorMapId, indoorMapName, indoorMapLatLng);
        delete _entrances[entrance.getIndoorMapId()];
        _this.fire("indoorentranceremove", {entrance: entrance});
    };

    var _executeIndoorMapLoadedCallbacks = function(indoorMapId) {
        _this.fire("indoormapload", {indoorMapId: indoorMapId});
    };

    var _executeIndoorMapUnloadedCallbacks = function(indoorMapId) {
        _this.fire("indoormapunload", {indoorMapId: indoorMapId});
    };

    var _executeEntityClickedCallbacks = function(ids) {
        var idArray = ids.split("|");
        _this.fire("indoorentityclick", {ids: idArray});
    };

    var _onCollapseStart = function() {
        _this.fire("collapsestart");
    };

    var _onCollapse = function() {
        _this.fire("collapse");
    };

    var _onCollapseEnd = function() {
        _this.fire("collapseend");
    };

    var _onExpandStart = function() {
        _this.fire("expandstart");
    };

    var _onExpand = function() {
        _this.fire("expand");
    };

    var _onExpandEnd = function() {
        _this.fire("expandend");
    };

    var _enterIndoorMap = function(indoorMapId) {
        _this.fire("indoormapenterrequested");
        _emscriptenApi.indoorsApi.enterIndoorMap(indoorMapId);
    };

    var _transitionToIndoorMap = function(config) {

        _transitioningToIndoorMap = true;

        if (!_ready) {
            _pendingEnterTransition = config;
            return;
        }
        _emscriptenApi.cameraApi.setView({location: config.latLng, distance: config.distance, allowInterruption: false, headingDegrees: config.orientation});
        _mapController._setIndoorTransitionCompleteEventListener(function() { _enterIndoorMap(config.indoorMapId); });

        _this.once("indoormapenter", function() {
            _transitioningToIndoorMap = false;
            var vendorKey = _activeIndoorMap.getIndoorMapSourceVendor();
            _indoorWatermarkController.showWatermarkForVendor(vendorKey);
        });
        _this.once("indoormapenterfailed", function() {
            _transitioningToIndoorMap = false;
        });
    };

    this.onInitialized = function() {
        _emscriptenApi.indoorsApi.onInitialized();

        _emscriptenApi.indoorsApi.setNotificationCallbacks(
            _executeIndoorMapEnteredCallbacks,
            _executeIndoorMapEnterFailedCallbacks,
            _executeIndoorMapExitedCallbacks,
            _executeIndoorMapFloorChangedCallbacks,
            _executeIndoorMapEntranceAddedCallbacks,
            _executeIndoorMapEntranceRemovedCallbacks,
            _executeIndoorMapLoadedCallbacks,
            _executeIndoorMapUnloadedCallbacks
            );

        _emscriptenApi.indoorEntityApi.onInitialized();
        _emscriptenApi.indoorEntityApi.registerIndoorEntityPickedCallback(_executeEntityClickedCallbacks);

        _emscriptenApi.expandFloorsApi.setCollapseStartCallback(_onCollapseStart);
        _emscriptenApi.expandFloorsApi.setCollapseCallback(_onCollapse);
        _emscriptenApi.expandFloorsApi.setCollapseEndCallback(_onCollapseEnd);
        _emscriptenApi.expandFloorsApi.setExpandStartCallback(_onExpandStart);
        _emscriptenApi.expandFloorsApi.setExpandCallback(_onExpand);
        _emscriptenApi.expandFloorsApi.setExpandEndCallback(_onExpandEnd);
    };

    this.onInitialStreamingCompleted = function() {
        _ready = true;

        if(_startingIndoorId)
        {
            var config = {
                latLng: _center,
                zoom: _zoom,
                indoorMapId: _startingIndoorId,
                orientation: _headingDegrees
            };
            this.enter(_startingIndoorId, config);

            if(_startingFloorIndex)
            {
                this.once("indoormapenter", function() { this.setFloor(Number(_startingFloorIndex)); });
            }
        }

        if (_pendingEnterTransition !== null) {
            _transitionToIndoorMap(_pendingEnterTransition);
            _pendingEnterTransition = null;
        }
    };

    this.exit = function() {
        if (_emscriptenApi.ready()) {
            _emscriptenApi.indoorsApi.exitIndoorMap();
            _indoorWatermarkController.hideWatermark();
        }
        _pendingEnterTransition = null;
        _transitioningToIndoorMap = false;
        return this;
    };

    this.isIndoors = function() {
        return _activeIndoorMap !== null;
    };

    this.getActiveIndoorMap = function() {
        return _activeIndoorMap;
    };

    this.getFloor = function() {
        if (this.isIndoors()) {
            var index = _emscriptenApi.indoorsApi.getSelectedFloorIndex();
            return _activeIndoorMap.getFloors()[index];
        }
        return null;
    };

    this.setFloor = function(floor) {
        if (!this.isIndoors()) {
            return false;
        }

        var index = null;
        var floors = _activeIndoorMap.getFloors();

        if (typeof floor === "number") {
            index = floor;
        }
        else if (typeof floor === "object") {
            var floorIndex = floors.indexOf(floor);
            index = (floorIndex >= 0) ? floorIndex : null;
        }
        else if (typeof floor === "string") {
            for (var i=0; i<floors.length; ++i) {
                if (floors[i].getFloorShortName() === floor) {
                    index = i;
                    break;
                }
            }
        }

        if (index !== null) {
            return _emscriptenApi.indoorsApi.setSelectedFloorIndex(index);
        }

        return false;
    };

    this.moveUp = function(numberOfFloors) {
        var delta = (typeof numberOfFloors === "undefined") ? 1 : numberOfFloors;
        var thisFloor = this.getFloor();
        if (thisFloor === null) {
            return false;
        }
        return this.setFloor(thisFloor.getFloorIndex() + delta);
    };

    this.moveDown = function(numberOfFloors) {
        var delta = (typeof numberOfFloors === "undefined") ? -1 : -numberOfFloors;
        return this.moveUp(delta);
    };

    this.enter = function(indoorMap, config) {
        if (this.isIndoors() || _transitioningToIndoorMap) {
            return false;
        }

        var indoorMapId = null;
        if (typeof indoorMap === "object" && "getIndoorMapId" in indoorMap && typeof indoorMap["getIndoorMapId"] === "function") {
            indoorMapId = indoorMap.getIndoorMapId();
        }
        else if (typeof indoorMap === "string") {
            indoorMapId = indoorMap;
        }

        var entrance = _entrances[indoorMapId] || null;

        var latLng = null;
        if (entrance !== null) {
            latLng = entrance.getLatLng();
        }
        else if(config !== null && config.latLng) {
            latLng = config.latLng;
        }

        if (latLng === null) {
            return false;
        }

        var distance = 400;

        if(!config) {
            config = {
                latLng: latLng,
                distance: distance,
                indoorMapId: indoorMapId,
                orientation: 0
            };
        }

        _transitionToIndoorMap(config);

        return true;
    };

    this.getFloorInterpolation = function() {
        if (_activeIndoorMap !== null) {
            var floorParam = _emscriptenApi.expandFloorsApi.getFloorParam();
            var normalizedValue = floorParam / _activeIndoorMap.getFloorCount();
            return normalizedValue;
        }
        return 0;
    };

    this.getFloorHeightAboveSeaLevel = function(floorIndex) {
        if (this.isIndoors() &&
            floorIndex >= 0 &&
            floorIndex < _activeIndoorMap.getFloorCount())
        {
            return _emscriptenApi.indoorsApi.getFloorHeightAboveSeaLevel(floorIndex);
        }

        return null;
    };

    this.tryGetReadableName = function(indoorMapId) {
        if (_emscriptenApi.ready()) {
            return _emscriptenApi.indoorsApi.tryGetReadableName(indoorMapId);
        }
        return null;
    };

    this.tryGetFloorReadableName = function(indoorMapId, indoorMapFloorId) {
        if (_emscriptenApi.ready()) {
            return _emscriptenApi.indoorsApi.tryGetFloorReadableName(indoorMapId, indoorMapFloorId);
        }
        return null;
    };

    this.tryGetFloorShortName = function(indoorMapId, indoorMapFloorId) {
        if (_emscriptenApi.ready()) {
            return _emscriptenApi.indoorsApi.tryGetFloorShortName(indoorMapId, indoorMapFloorId);
        }
        return null;
    };

    this.setFloorInterpolation = function(value) {
        if (_activeIndoorMap !== null) {
            var floorParam = value * _activeIndoorMap.getFloorCount();
            _emscriptenApi.expandFloorsApi.setFloorParam(floorParam);
        }
        return this;
    };

    this.setFloorFromInterpolation = function(interpolationParam) {
        if (_activeIndoorMap === null) {
            return false;
        }

        var t = (typeof interpolationParam === "undefined") ? this.getFloorInterpolation() : interpolationParam;
        var floorIndex = Math.round(t * _activeIndoorMap.getFloorCount());
        return this.setFloor(floorIndex);
    };

    this.expand = function() {
        _emscriptenApi.expandFloorsApi.expandIndoorMap();
        return this;
    };

    this.collapse = function() {
        _emscriptenApi.expandFloorsApi.collapseIndoorMap();
        return this;
    };

    this.setEntityHighlights = function(ids, color, indoorMapId) {
        if (!_ready) return;

        indoorMapId = _indoorMapIdOrDefault(indoorMapId);
        _emscriptenApi.indoorEntityApi.setHighlights(ids, color, indoorMapId);
    };

    this.clearEntityHighlights = function(ids, indoorMapId) {
        if (!_ready) return;

        indoorMapId = _indoorMapIdOrDefault(indoorMapId);
        _emscriptenApi.indoorEntityApi.clearHighlights(ids, indoorMapId);
    };

    var _indoorMapIdOrDefault = function(indoorMapId) {
        if (indoorMapId === undefined || indoorMapId === null) {
            if (_activeIndoorMap !== null) {
                indoorMapId = _activeIndoorMap.getIndoorMapId();
            }
        }

        return indoorMapId;
    };

};

var IndoorsPrototype = L.extend({}, MapModule, L.Mixin.Events);

IndoorsModule.prototype = IndoorsPrototype;

module.exports = IndoorsModule;
<commit_msg>Global fix for MPLY-11371. Enter map should allow for undefined config. Buddy: Sam<commit_after>var MapModule = require("./map_module");
var indoors = require("../public/indoors/indoors");
var IndoorWatermarkController = require("./indoor_watermark_controller");

var IndoorsModule = function(emscriptenApi, mapController, mapId, indoorId, floorIndex, center, headingDegrees, zoom, showWrldWatermark) {

    var _emscriptenApi = emscriptenApi;
    var _mapController = mapController;

    var _activeIndoorMap = null;
    var _entrances = {};

    var _ready = false;
    var _pendingEnterTransition = null;
    var _transitioningToIndoorMap = false;

    var _indoorWatermarkController = new IndoorWatermarkController(mapId, showWrldWatermark);

    var _startingIndoorId = indoorId;
    var _startingFloorIndex = floorIndex;
    var _center = center;
    var _headingDegrees = headingDegrees;
    var _zoom = zoom;

    var _this = this;

    var _createIndoorMapObject = function() {
        var mapId = _emscriptenApi.indoorsApi.getActiveIndoorMapId();
        var mapName = _emscriptenApi.indoorsApi.getActiveIndoorMapName();
        var sourceVendor = _emscriptenApi.indoorsApi.getActiveIndoorMapSourceVendor();
        var floorCount = _emscriptenApi.indoorsApi.getActiveIndoorMapFloorCount();
        var floors = _createFloorsArray(floorCount);
        var searchTags = _createSearchTagsArray();
        var exitFunc = _this.exit;
        var indoorMap = new indoors.IndoorMap(mapId, mapName, sourceVendor, floorCount, floors, searchTags, exitFunc);
        return indoorMap;
    };

    var _createFloorsArray = function(floorCount) {
        var floors = [];
        for (var i=0; i<floorCount; ++i) {
            var floorIndex = i;
            var floorName = _emscriptenApi.indoorsApi.getFloorName(i);
            var floorShortName = _emscriptenApi.indoorsApi.getFloorShortName(i);
            var floorNumber = _emscriptenApi.indoorsApi.getFloorNumber(i);

            var floorId = floorNumber;

            var floor = new indoors.IndoorMapFloor(floorId, floorIndex, floorName, floorShortName);
            floors.push(floor);
        }
        return floors;
    };

    var _createSearchTagsArray = function() {
        var userData;
        try {
            userData = JSON.parse(_emscriptenApi.indoorsApi.getActiveIndoorMapUserData());
        }
        catch (e) {
            return [];
        }

        if (typeof userData.search_menu_items !== "object") { return []; }
        if (!(userData.search_menu_items.items instanceof Array)) { return []; }

        var searchTags = [];
        userData.search_menu_items.items.forEach(function(item) {
            searchTags.push({
                name: item.name,
                search_tag: item.search_tag,
                icon_key: item.icon_key
            });
        });
        return searchTags;
    };

    var _executeIndoorMapEnteredCallbacks = function() {
        _activeIndoorMap = _createIndoorMapObject();
        _this.fire("indoormapenter", {indoorMap: _activeIndoorMap});
    };

    var _executeIndoorMapEnterFailedCallbacks = function() {
        _this.fire("indoormapenterfailed", {});
    };

    var _executeIndoorMapExitedCallbacks = function() {
        var indoorMap = _activeIndoorMap;
        _activeIndoorMap = null;
        _indoorWatermarkController.hideWatermark();
        _this.fire("indoormapexit", {indoorMap: indoorMap});
    };

    var _executeIndoorMapFloorChangedCallbacks = function() {
        _this.fire("indoormapfloorchange", {floor: _this.getFloor()});
    };

    var _executeIndoorMapEntranceAddedCallbacks = function(indoorMapId, indoorMapName, indoorMapLatLng) {
        // discard the altitude, as we're going to use the SDK IPointOnMap positioning to snap it to the terrain (this is now default)
        // alternative is to use the altitude, but use "elevationMode: heightAboveSeaLevel" when creating the indoor entrance marker
        var entrance = new indoors.IndoorMapEntrance(indoorMapId, indoorMapName, L.latLng([indoorMapLatLng.lat, indoorMapLatLng.lng]));
        _entrances[entrance.getIndoorMapId()] = entrance;
        _this.fire("indoorentranceadd", {entrance: entrance});
    };

    var _executeIndoorMapEntranceRemovedCallbacks = function(indoorMapId, indoorMapName, indoorMapLatLng) {
        var entrance = new indoors.IndoorMapEntrance(indoorMapId, indoorMapName, indoorMapLatLng);
        delete _entrances[entrance.getIndoorMapId()];
        _this.fire("indoorentranceremove", {entrance: entrance});
    };

    var _executeIndoorMapLoadedCallbacks = function(indoorMapId) {
        _this.fire("indoormapload", {indoorMapId: indoorMapId});
    };

    var _executeIndoorMapUnloadedCallbacks = function(indoorMapId) {
        _this.fire("indoormapunload", {indoorMapId: indoorMapId});
    };

    var _executeEntityClickedCallbacks = function(ids) {
        var idArray = ids.split("|");
        _this.fire("indoorentityclick", {ids: idArray});
    };

    var _onCollapseStart = function() {
        _this.fire("collapsestart");
    };

    var _onCollapse = function() {
        _this.fire("collapse");
    };

    var _onCollapseEnd = function() {
        _this.fire("collapseend");
    };

    var _onExpandStart = function() {
        _this.fire("expandstart");
    };

    var _onExpand = function() {
        _this.fire("expand");
    };

    var _onExpandEnd = function() {
        _this.fire("expandend");
    };

    var _enterIndoorMap = function(indoorMapId) {
        _this.fire("indoormapenterrequested");
        _emscriptenApi.indoorsApi.enterIndoorMap(indoorMapId);
    };

    var _transitionToIndoorMap = function(config) {

        _transitioningToIndoorMap = true;

        if (!_ready) {
            _pendingEnterTransition = config;
            return;
        }
        _emscriptenApi.cameraApi.setView({location: config.latLng, distance: config.distance, allowInterruption: false, headingDegrees: config.orientation});
        _mapController._setIndoorTransitionCompleteEventListener(function() { _enterIndoorMap(config.indoorMapId); });

        _this.once("indoormapenter", function() {
            _transitioningToIndoorMap = false;
            var vendorKey = _activeIndoorMap.getIndoorMapSourceVendor();
            _indoorWatermarkController.showWatermarkForVendor(vendorKey);
        });
        _this.once("indoormapenterfailed", function() {
            _transitioningToIndoorMap = false;
        });
    };

    this.onInitialized = function() {
        _emscriptenApi.indoorsApi.onInitialized();

        _emscriptenApi.indoorsApi.setNotificationCallbacks(
            _executeIndoorMapEnteredCallbacks,
            _executeIndoorMapEnterFailedCallbacks,
            _executeIndoorMapExitedCallbacks,
            _executeIndoorMapFloorChangedCallbacks,
            _executeIndoorMapEntranceAddedCallbacks,
            _executeIndoorMapEntranceRemovedCallbacks,
            _executeIndoorMapLoadedCallbacks,
            _executeIndoorMapUnloadedCallbacks
            );

        _emscriptenApi.indoorEntityApi.onInitialized();
        _emscriptenApi.indoorEntityApi.registerIndoorEntityPickedCallback(_executeEntityClickedCallbacks);

        _emscriptenApi.expandFloorsApi.setCollapseStartCallback(_onCollapseStart);
        _emscriptenApi.expandFloorsApi.setCollapseCallback(_onCollapse);
        _emscriptenApi.expandFloorsApi.setCollapseEndCallback(_onCollapseEnd);
        _emscriptenApi.expandFloorsApi.setExpandStartCallback(_onExpandStart);
        _emscriptenApi.expandFloorsApi.setExpandCallback(_onExpand);
        _emscriptenApi.expandFloorsApi.setExpandEndCallback(_onExpandEnd);
    };

    this.onInitialStreamingCompleted = function() {
        _ready = true;

        if(_startingIndoorId)
        {
            var config = {
                latLng: _center,
                zoom: _zoom,
                indoorMapId: _startingIndoorId,
                orientation: _headingDegrees
            };
            this.enter(_startingIndoorId, config);

            if(_startingFloorIndex)
            {
                this.once("indoormapenter", function() { this.setFloor(Number(_startingFloorIndex)); });
            }
        }

        if (_pendingEnterTransition !== null) {
            _transitionToIndoorMap(_pendingEnterTransition);
            _pendingEnterTransition = null;
        }
    };

    this.exit = function() {
        if (_emscriptenApi.ready()) {
            _emscriptenApi.indoorsApi.exitIndoorMap();
            _indoorWatermarkController.hideWatermark();
        }
        _pendingEnterTransition = null;
        _transitioningToIndoorMap = false;
        return this;
    };

    this.isIndoors = function() {
        return _activeIndoorMap !== null;
    };

    this.getActiveIndoorMap = function() {
        return _activeIndoorMap;
    };

    this.getFloor = function() {
        if (this.isIndoors()) {
            var index = _emscriptenApi.indoorsApi.getSelectedFloorIndex();
            return _activeIndoorMap.getFloors()[index];
        }
        return null;
    };

    this.setFloor = function(floor) {
        if (!this.isIndoors()) {
            return false;
        }

        var index = null;
        var floors = _activeIndoorMap.getFloors();

        if (typeof floor === "number") {
            index = floor;
        }
        else if (typeof floor === "object") {
            var floorIndex = floors.indexOf(floor);
            index = (floorIndex >= 0) ? floorIndex : null;
        }
        else if (typeof floor === "string") {
            for (var i=0; i<floors.length; ++i) {
                if (floors[i].getFloorShortName() === floor) {
                    index = i;
                    break;
                }
            }
        }

        if (index !== null) {
            return _emscriptenApi.indoorsApi.setSelectedFloorIndex(index);
        }

        return false;
    };

    this.moveUp = function(numberOfFloors) {
        var delta = (typeof numberOfFloors === "undefined") ? 1 : numberOfFloors;
        var thisFloor = this.getFloor();
        if (thisFloor === null) {
            return false;
        }
        return this.setFloor(thisFloor.getFloorIndex() + delta);
    };

    this.moveDown = function(numberOfFloors) {
        var delta = (typeof numberOfFloors === "undefined") ? -1 : -numberOfFloors;
        return this.moveUp(delta);
    };

    this.enter = function(indoorMap, config) {
        if (this.isIndoors() || _transitioningToIndoorMap) {
            return false;
        }

        var indoorMapId = null;
        if (typeof indoorMap === "object" && "getIndoorMapId" in indoorMap && typeof indoorMap["getIndoorMapId"] === "function") {
            indoorMapId = indoorMap.getIndoorMapId();
        }
        else if (typeof indoorMap === "string") {
            indoorMapId = indoorMap;
        }

        var entrance = _entrances[indoorMapId] || null;

        var latLng = null;
        if (entrance !== null) {
            latLng = entrance.getLatLng();
        }
        else if(config && config.latLng) {
            latLng = config.latLng;
        }

        if (latLng === null) {
            return false;
        }

        var distance = 400;

        if(!config) {
            config = {
                latLng: latLng,
                distance: distance,
                indoorMapId: indoorMapId,
                orientation: 0
            };
        }

        _transitionToIndoorMap(config);

        return true;
    };

    this.getFloorInterpolation = function() {
        if (_activeIndoorMap !== null) {
            var floorParam = _emscriptenApi.expandFloorsApi.getFloorParam();
            var normalizedValue = floorParam / _activeIndoorMap.getFloorCount();
            return normalizedValue;
        }
        return 0;
    };

    this.getFloorHeightAboveSeaLevel = function(floorIndex) {
        if (this.isIndoors() &&
            floorIndex >= 0 &&
            floorIndex < _activeIndoorMap.getFloorCount())
        {
            return _emscriptenApi.indoorsApi.getFloorHeightAboveSeaLevel(floorIndex);
        }

        return null;
    };

    this.tryGetReadableName = function(indoorMapId) {
        if (_emscriptenApi.ready()) {
            return _emscriptenApi.indoorsApi.tryGetReadableName(indoorMapId);
        }
        return null;
    };

    this.tryGetFloorReadableName = function(indoorMapId, indoorMapFloorId) {
        if (_emscriptenApi.ready()) {
            return _emscriptenApi.indoorsApi.tryGetFloorReadableName(indoorMapId, indoorMapFloorId);
        }
        return null;
    };

    this.tryGetFloorShortName = function(indoorMapId, indoorMapFloorId) {
        if (_emscriptenApi.ready()) {
            return _emscriptenApi.indoorsApi.tryGetFloorShortName(indoorMapId, indoorMapFloorId);
        }
        return null;
    };

    this.setFloorInterpolation = function(value) {
        if (_activeIndoorMap !== null) {
            var floorParam = value * _activeIndoorMap.getFloorCount();
            _emscriptenApi.expandFloorsApi.setFloorParam(floorParam);
        }
        return this;
    };

    this.setFloorFromInterpolation = function(interpolationParam) {
        if (_activeIndoorMap === null) {
            return false;
        }

        var t = (typeof interpolationParam === "undefined") ? this.getFloorInterpolation() : interpolationParam;
        var floorIndex = Math.round(t * _activeIndoorMap.getFloorCount());
        return this.setFloor(floorIndex);
    };

    this.expand = function() {
        _emscriptenApi.expandFloorsApi.expandIndoorMap();
        return this;
    };

    this.collapse = function() {
        _emscriptenApi.expandFloorsApi.collapseIndoorMap();
        return this;
    };

    this.setEntityHighlights = function(ids, color, indoorMapId) {
        if (!_ready) return;

        indoorMapId = _indoorMapIdOrDefault(indoorMapId);
        _emscriptenApi.indoorEntityApi.setHighlights(ids, color, indoorMapId);
    };

    this.clearEntityHighlights = function(ids, indoorMapId) {
        if (!_ready) return;

        indoorMapId = _indoorMapIdOrDefault(indoorMapId);
        _emscriptenApi.indoorEntityApi.clearHighlights(ids, indoorMapId);
    };

    var _indoorMapIdOrDefault = function(indoorMapId) {
        if (indoorMapId === undefined || indoorMapId === null) {
            if (_activeIndoorMap !== null) {
                indoorMapId = _activeIndoorMap.getIndoorMapId();
            }
        }

        return indoorMapId;
    };

};

var IndoorsPrototype = L.extend({}, MapModule, L.Mixin.Events);

IndoorsModule.prototype = IndoorsPrototype;

module.exports = IndoorsModule;
<|endoftext|>