<commit_before>
function integrateWasmJS(Module) {

  // wasm lacks globals, so asm2wasm maps them into locations in memory. that information cannot
  // be present in the wasm output of asm2wasm, so we store it in a side file. If we load asm2wasm
  // output, either generated ahead of time or on the client, we need to apply those mapped
  // globals after loading the module.
  function applyMappedGlobals() {
    var mappedGlobals = JSON.parse(Module['read'](Module['wasmCodeFile'] + '.mappedGlobals'));
    for (var name in mappedGlobals) {
      var global = mappedGlobals[name];
      if (!global.import) continue; // non-imports are initialized to zero in the typed array anyhow, so nothing to do here
      var value = wasmJS['lookupImport'](global.module, global.base);
      var address = global.address;
      switch (global.type) {
        case WasmTypes.i32: Module['HEAP32'][address >> 2] = value; break;
        case WasmTypes.f32: Module['HEAPF32'][address >> 2] = value; break;
        case WasmTypes.f64: Module['HEAPF64'][address >> 3] = value; break;
        default: abort();
      }
    }
  }

  if (typeof WASM === 'object') {
    // Provide an "asm.js function" for the application, called to "link" the asm.js module. We instantiate
    // the wasm module at that time, and it receives imports and provides exports and so forth, the app
    // doesn't need to care that it is wasm and not asm.
    Module['asm'] = function(global, env, providedBuffer) {
      // Load the wasm module
      var binary = Module['readBinary'](Module['wasmCodeFile']);

      // Create an instance of the module using native support in the JS engine.
      var instance = WASM.instantiateModule(binary, {
        "global.Math": global.Math,
        "env": env
      });

      // The wasm instance creates its memory. But static init code might have written to
      // buffer already, and we must copy it over.
      // TODO: avoid this copy, by avoiding such static init writes
      // TODO: in shorter term, just copy up to the last static init write
      var oldBuffer = Module['buffer'];
      var newBuffer = instance.memory;
      assert(newBuffer.byteLength >= oldBuffer.byteLength, 'we might fail if we allocated more than TOTAL_MEMORY');
      // the wasm module does write out the memory initialization, in range STATIC_BASE..STATIC_BUMP, so avoid that
      (new Int8Array(newBuffer).subarray(0, STATIC_BASE)).set(new Int8Array(oldBuffer).subarray(0, STATIC_BASE));
      (new Int8Array(newBuffer).subarray(STATIC_BASE + STATIC_BUMP)).set(new Int8Array(oldBuffer).subarray(STATIC_BASE + STATIC_BUMP));
      updateGlobalBuffer(newBuffer);
      updateGlobalBufferViews();
      Module['reallocBuffer'] = function(size) {
        var old = Module['buffer'];
        wasmJS['asmExports']['__growWasmMemory'](size); // tiny wasm method that just does grow_memory
        return Module['buffer'] !== old ? Module['buffer'] : null; // if it was reallocated, it changed
      };

      applyMappedGlobals();

      return instance;
    };

    return;
  }

  var WasmTypes = {
    none: 0,
    i32: 1,
    i64: 2,
    f32: 3,
    f64: 4
  };

  // Use wasm.js to polyfill and execute code in a wasm interpreter.
  var wasmJS = WasmJS({});

  // XXX don't be confused. Module here is in the outside program. wasmJS is the inner wasm-js.cpp.
  wasmJS['outside'] = Module; // Inside wasm-js.cpp, Module['outside'] reaches the outside module.

  // Information for the instance of the module.
  var info = wasmJS['info'] = {
    global: null,
    env: null,
    asm2wasm: { // special asm2wasm imports
      "f64-rem": function(x, y) {
        return x % y;
      },
      "f64-to-int": function(x) {
        return x | 0;
      },
      "debugger": function() {
        debugger;
      },
    },
    parent: Module // Module inside wasm-js.cpp refers to wasm-js.cpp; this allows access to the outside program.
  };

  wasmJS['lookupImport'] = function(mod, base) {
    var lookup = info;
    if (mod.indexOf('.') < 0) {
      lookup = (lookup || {})[mod];
    } else {
      var parts = mod.split('.');
      lookup = (lookup || {})[parts[0]];
      lookup = (lookup || {})[parts[1]];
    }
    lookup = (lookup || {})[base];
    if (lookup === undefined) {
      abort('bad lookupImport to (' + mod + ').' + base);
    }
    return lookup;
  }

  // The asm.js function, called to "link" the asm.js module. At that time, we are provided imports
  // and respond with exports, and so forth.
  Module['asm'] = function(global, env, providedBuffer) {
    assert(providedBuffer === Module['buffer']); // we should not even need to pass it as a 3rd arg for wasm, but that's the asm.js way.

    info.global = global;
    info.env = env;

    // wasm code would create its own buffer, at this time. But static init code might have
    // written to the buffer already, and we must copy it over. We could just avoid
    // this copy in wasm.js polyfilling, but to be as close as possible to real wasm,
    // we do what wasm would do.
    // TODO: avoid this copy, by avoiding such static init writes
    // TODO: in shorter term, just copy up to the last static init write
    var oldBuffer = Module['buffer'];
    var newBuffer = new ArrayBuffer(oldBuffer.byteLength);
    (new Int8Array(newBuffer)).set(new Int8Array(oldBuffer));
    updateGlobalBuffer(newBuffer);
    updateGlobalBufferViews();
    wasmJS['providedTotalMemory'] = Module['buffer'].byteLength;

    Module['reallocBuffer'] = function(size) {
      var old = Module['buffer'];
      wasmJS['asmExports']['__growWasmMemory'](size); // tiny wasm method that just does grow_memory
      return Module['buffer'] !== old ? Module['buffer'] : null; // if it was reallocated, it changed
    };

    // Prepare to generate wasm, using either asm2wasm or wasm-s-parser
    var method = Module['wasmJSMethod'] || 'asm2wasm';
    assert(method == 'asm2wasm' || method == 'wasm-s-parser');
    var code = Module['read'](method == 'asm2wasm' ? Module['asmjsCodeFile'] : Module['wasmCodeFile']);
    var temp = wasmJS['_malloc'](code.length + 1);
    wasmJS['writeAsciiToMemory'](code, temp);
    if (method == 'asm2wasm') {
      wasmJS['_load_asm2wasm'](temp);
    } else {
      wasmJS['_load_s_expr2wasm'](temp);
      applyMappedGlobals();
    }
    wasmJS['_free'](temp);

    wasmJS['_instantiate'](temp);

    return wasmJS['asmExports'];
  };
}

<commit_msg>use wasm-s-parser method by efault<commit_after>
function integrateWasmJS(Module) {

  // wasm lacks globals, so asm2wasm maps them into locations in memory. that information cannot
  // be present in the wasm output of asm2wasm, so we store it in a side file. If we load asm2wasm
  // output, either generated ahead of time or on the client, we need to apply those mapped
  // globals after loading the module.
  function applyMappedGlobals() {
    var mappedGlobals = JSON.parse(Module['read'](Module['wasmCodeFile'] + '.mappedGlobals'));
    for (var name in mappedGlobals) {
      var global = mappedGlobals[name];
      if (!global.import) continue; // non-imports are initialized to zero in the typed array anyhow, so nothing to do here
      var value = wasmJS['lookupImport'](global.module, global.base);
      var address = global.address;
      switch (global.type) {
        case WasmTypes.i32: Module['HEAP32'][address >> 2] = value; break;
        case WasmTypes.f32: Module['HEAPF32'][address >> 2] = value; break;
        case WasmTypes.f64: Module['HEAPF64'][address >> 3] = value; break;
        default: abort();
      }
    }
  }

  if (typeof WASM === 'object') {
    // Provide an "asm.js function" for the application, called to "link" the asm.js module. We instantiate
    // the wasm module at that time, and it receives imports and provides exports and so forth, the app
    // doesn't need to care that it is wasm and not asm.
    Module['asm'] = function(global, env, providedBuffer) {
      // Load the wasm module
      var binary = Module['readBinary'](Module['wasmCodeFile']);

      // Create an instance of the module using native support in the JS engine.
      var instance = WASM.instantiateModule(binary, {
        "global.Math": global.Math,
        "env": env
      });

      // The wasm instance creates its memory. But static init code might have written to
      // buffer already, and we must copy it over.
      // TODO: avoid this copy, by avoiding such static init writes
      // TODO: in shorter term, just copy up to the last static init write
      var oldBuffer = Module['buffer'];
      var newBuffer = instance.memory;
      assert(newBuffer.byteLength >= oldBuffer.byteLength, 'we might fail if we allocated more than TOTAL_MEMORY');
      // the wasm module does write out the memory initialization, in range STATIC_BASE..STATIC_BUMP, so avoid that
      (new Int8Array(newBuffer).subarray(0, STATIC_BASE)).set(new Int8Array(oldBuffer).subarray(0, STATIC_BASE));
      (new Int8Array(newBuffer).subarray(STATIC_BASE + STATIC_BUMP)).set(new Int8Array(oldBuffer).subarray(STATIC_BASE + STATIC_BUMP));
      updateGlobalBuffer(newBuffer);
      updateGlobalBufferViews();
      Module['reallocBuffer'] = function(size) {
        var old = Module['buffer'];
        wasmJS['asmExports']['__growWasmMemory'](size); // tiny wasm method that just does grow_memory
        return Module['buffer'] !== old ? Module['buffer'] : null; // if it was reallocated, it changed
      };

      applyMappedGlobals();

      return instance;
    };

    return;
  }

  var WasmTypes = {
    none: 0,
    i32: 1,
    i64: 2,
    f32: 3,
    f64: 4
  };

  // Use wasm.js to polyfill and execute code in a wasm interpreter.
  var wasmJS = WasmJS({});

  // XXX don't be confused. Module here is in the outside program. wasmJS is the inner wasm-js.cpp.
  wasmJS['outside'] = Module; // Inside wasm-js.cpp, Module['outside'] reaches the outside module.

  // Information for the instance of the module.
  var info = wasmJS['info'] = {
    global: null,
    env: null,
    asm2wasm: { // special asm2wasm imports
      "f64-rem": function(x, y) {
        return x % y;
      },
      "f64-to-int": function(x) {
        return x | 0;
      },
      "debugger": function() {
        debugger;
      },
    },
    parent: Module // Module inside wasm-js.cpp refers to wasm-js.cpp; this allows access to the outside program.
  };

  wasmJS['lookupImport'] = function(mod, base) {
    var lookup = info;
    if (mod.indexOf('.') < 0) {
      lookup = (lookup || {})[mod];
    } else {
      var parts = mod.split('.');
      lookup = (lookup || {})[parts[0]];
      lookup = (lookup || {})[parts[1]];
    }
    lookup = (lookup || {})[base];
    if (lookup === undefined) {
      abort('bad lookupImport to (' + mod + ').' + base);
    }
    return lookup;
  }

  // The asm.js function, called to "link" the asm.js module. At that time, we are provided imports
  // and respond with exports, and so forth.
  Module['asm'] = function(global, env, providedBuffer) {
    assert(providedBuffer === Module['buffer']); // we should not even need to pass it as a 3rd arg for wasm, but that's the asm.js way.

    info.global = global;
    info.env = env;

    // wasm code would create its own buffer, at this time. But static init code might have
    // written to the buffer already, and we must copy it over. We could just avoid
    // this copy in wasm.js polyfilling, but to be as close as possible to real wasm,
    // we do what wasm would do.
    // TODO: avoid this copy, by avoiding such static init writes
    // TODO: in shorter term, just copy up to the last static init write
    var oldBuffer = Module['buffer'];
    var newBuffer = new ArrayBuffer(oldBuffer.byteLength);
    (new Int8Array(newBuffer)).set(new Int8Array(oldBuffer));
    updateGlobalBuffer(newBuffer);
    updateGlobalBufferViews();
    wasmJS['providedTotalMemory'] = Module['buffer'].byteLength;

    Module['reallocBuffer'] = function(size) {
      var old = Module['buffer'];
      wasmJS['asmExports']['__growWasmMemory'](size); // tiny wasm method that just does grow_memory
      return Module['buffer'] !== old ? Module['buffer'] : null; // if it was reallocated, it changed
    };

    // Prepare to generate wasm, using either asm2wasm or wasm-s-parser
    var method = Module['wasmJSMethod'] || 'wasm-s-parser';
    assert(method == 'asm2wasm' || method == 'wasm-s-parser');
    var code = Module['read'](method == 'asm2wasm' ? Module['asmjsCodeFile'] : Module['wasmCodeFile']);
    var temp = wasmJS['_malloc'](code.length + 1);
    wasmJS['writeAsciiToMemory'](code, temp);
    if (method == 'asm2wasm') {
      wasmJS['_load_asm2wasm'](temp);
    } else {
      wasmJS['_load_s_expr2wasm'](temp);
      applyMappedGlobals();
    }
    wasmJS['_free'](temp);

    wasmJS['_instantiate'](temp);

    return wasmJS['asmExports'];
  };
}

<|endoftext|>


<commit_before>/**
 * rect.
 */

var window = global.window,
    $ = window.$,
    mass = global.mass,
    _ = window._,
    document = window.document,
    console = window.console;

var main = {
    // 画热区
    drawMap: function(){
        var cache = mass.cache,
            imgCover = $('#J-image-process-cover'),
            imgItem = imgCover.parent(),
            newRect = $('

'),
            _drawMove = false,
            _drawMoving = false,
            scrollTop = 0,
            scrollLeft = 0,
            imgWidth, imgHeight,
            left, top, distX, distY, rect, rectCreated;

        var _rectMove = false,
            _rectMoving = false,
            rectAddX = 0,
            rectAddY = 0,
            currentRectId, trueLeft, trueTop, currentRect;

        var _rectResize = false,
            _rectResizing = false,
            trueWidth, trueHeight;

        // 点击层或resize时，将焦点放在当前的层，同时赋值基础数据
        var focusCurrentBlock = function(e, id){
            main.focus(id);
            currentRect = cache.rect[cache.focusRectId];

            left = e.clientX - cache.minusX + imgItem.scrollLeft();
            top = e.clientY - cache.minusY + imgItem.scrollTop();
            distX = e.clientX - cache.minusX + scrollLeft;
            distY = e.clientY - cache.minusY + scrollTop;
            imgWidth = cache.img.width;
            imgHeight = cache.img.height;
            rectAddX = left - currentRect.left;
            rectAddY = top - currentRect.top;

            var $curRect = $('#' + id);
            if($curRect.hasClass('rect-error')){
                $curRect.removeClass('rect-error');
            }
        };

        imgCover.mousedown(function(e){
            // 右键不触发
            if(e.button === 2) return;

            if(cache.drawMap){
                _drawMove = true;
                left = e.clientX - cache.minusX + imgItem.scrollLeft();
                top = e.clientY - cache.minusY + imgItem.scrollTop();
                distX = left;
                distY = top;

                imgWidth = cache.img.width;
                imgHeight = cache.img.height;
            }
        });

        $(document).mousemove(function(e){
            if(_drawMove || _rectMove || _rectResize){
                scrollLeft = imgItem.scrollLeft();
                scrollTop = imgItem.scrollTop();
                distX = e.clientX - cache.minusX + scrollLeft;
                distY = e.clientY - cache.minusY + scrollTop;

                distX = Math.min(distX, imgWidth - 2);
                distY = Math.min(distY, imgHeight - 2);

                var $currentRect = $('#' + currentRectId);
            }

            if(_drawMove){
                if(!rectCreated){
                    rect = newRect.clone().attr('id', 'rect-' + cache.rectuuid);
                    imgCover.append(rect);
                    rectCreated = true;
                    main.focus('rect-' + cache.rectuuid);

                    cache.rectuuid++;
                    cache.rectNum++;
                }

                rect.css({
                    left: left,
                    top: top,
                    width: distX - left,
                    height: distY - top
                });

                _drawMoving = true;
            }
            else if(_rectMove){
                trueLeft = distX - rectAddX;
                trueTop = distY - rectAddY;
                //cacheRect = cache.rect[currentRectId];

                // 子欲拖出图片区域之外，我偏不让
                // 拖到图像区域外松手时，将left值替换成边界值
                if(trueLeft > imgWidth - currentRect.width - 2){
                    trueLeft = imgWidth - currentRect.width - 2;
                }else if(trueLeft < 0){
                    trueLeft = 0;
                };
                if(trueTop > imgHeight - currentRect.height - 2){
                    trueTop = imgHeight - currentRect.height - 2;
                }else if(trueTop < 0){
                    trueTop = 0;
                };

                $currentRect.css({
                    left: trueLeft,
                    top: trueTop
                });

                _rectMoving = true;
            }
            else if(_rectResize){
                trueWidth = distX - currentRect.left;
                trueHeight = distY - currentRect.top;

                // 子欲拖出图片区域之外，我偏不让
                if(trueWidth > imgWidth - currentRect.left - 2){
                    trueWidth = imgWidth - currentRect.left - 2;
                }else if(trueWidth < 10){
                    trueWidth = 10;
                };
                if(trueHeight > imgHeight - currentRect.top - 2){
                    trueHeight = imgHeight - currentRect.top - 2;
                }else if(trueHeight < 10){
                    trueHeight = 10;
                };

                $currentRect.css({
                    width: trueWidth,
                    height: trueHeight
                });

                // 热区设置层同步赋值
                if(!$currentRect.find('.setting-area').hasClass('hide')){
                    $currentRect.find('input[data-type="width"]').val(trueWidth);
                    $currentRect.find('input[data-type="height"]').val(trueHeight);
                }

                _rectResizing = true;
            }
        }).mouseup(function(){
                if(_drawMove){
                    if(_drawMoving){
                        if(distX < left || distY < top){
                            main.delete(cache.focusRectId);
                        }
                        else{
                            // 超出图片区域之外，将dist值替换成边界值
                            if(distX > imgWidth - 2){
                                distX = imgWidth - 2;
                            }
                            if(distY > imgHeight - 2){
                                distY = imgHeight - 2;
                            }

                            cache.rect[cache.focusRectId] = {
                                left: left,
                                top: top,
                                width: Math.max(distX - left, 10),
                                height: Math.max(distY - top, 10),
                                url: '#',
                                open: false
                            };

                            rect.css('cursor', 'move')
                                .append($('#J-template-rect-setting').clone().removeAttr('id'))
                                .append('

');
                        }
                    }

                    _drawMove = false;
                    _drawMoving = false;
                    rectCreated = false;
                }

                if(_rectMove){
                    if(_rectMoving){
                        cache.rect[currentRectId].left = trueLeft;
                        cache.rect[currentRectId].top = trueTop;

                        _rectMoving = false;
                    }
                    _rectMove = false;
                }

                if(_rectResize){
                    if(_rectResizing){
                        cache.rect[currentRectId].width = trueWidth;
                        cache.rect[currentRectId].height = trueHeight;

                        _rectResizing = false;
                    }
                    _rectResize = false;

                    if(cache.drawMap){
                        imgCover.removeClass('resizing');
                    }
                }
            });

        // 热区移动
        imgCover.delegate('.rect', 'mousedown', function(e){
            e.stopPropagation();
            //if(e.button === 2) return;
            currentRectId = this.id;
            focusCurrentBlock(e, currentRectId);

            _rectMove = true;
        });

        // 热区收缩
        imgCover.delegate('.resize', 'mousedown', function(e){
            e.stopPropagation();

            currentRectId = this.parentNode.id;
            focusCurrentBlock(e, currentRectId);

            if(cache.drawMap){
                imgCover.addClass('resizing');
            }

            _rectResize = true;
        });

        // 热区设置
        imgCover.delegate('.setting', 'mousedown', function(e){
            e.stopPropagation();
            var that = $(this),
                area = that.prev(),
                rectId = that.parent().attr('id'),
                rect = cache.rect[rectId];

            if(area.hasClass('hide')){
                area.removeClass('hide');
                area.find('input').each(function(){
                    var curInput = $(this),
                        type = curInput.data('type');
                    if(curInput.is(':checkbox')){
                        if(rect[type]){
                            curInput.attr('checked', true);
                        }else{
                            curInput.removeAttr('checked');
                        }
                    }else{
                        rect[type] && curInput.val(rect[type]);
                    }
                });
            }else{
                area.addClass('hide');
            }
        }).delegate('.setting-area', 'mousedown', function(e){
                e.stopPropagation();
            }).delegate('.rect-setting-column input', 'change', function(e){
                var that = $(this),
                    value = that.val(),
                    type = that.data('type'),
                    rectId = that.parents('.rect').attr('id'),
                    curRect = $('#' + rectId),
                    rect = cache.rect[rectId];

                if(type === 'open'){
                    value = that.is(':checked');
                }
                else if(type === 'width'){
                    value = parseInt(value);
                    if(value > cache.img.width - rect.left - 2){
                        value = cache.img.width - rect.left - 2;
                    }
                    curRect.width(value)
                }
                else if(type === 'height'){
                    value = parseInt(value);
                    if(value > cache.img.height - rect.top - 2){
                        value = cache.img.height - rect.top - 2;
                    }
                    curRect.height(value)
                }

                rect[type] = value;
                that.val(value);

                e.stopPropagation();
            });
    },
    reset: function(){
        // 清除旧的热区
        $('#J-image-process-cover').find('.rect').remove();

        $.extend(true, mass.cache, {
            rectuuid: 1,
            rectNum: 0,
            focusRectId: null
        });

        mass.cache.rect = {};
    },
    /*
     * option:
     * {
     *     left: Number,
     *     top: Number,
     *     width: Number,
     *     height: Number,
     *     url: String,
     *     open: Boolean
     * }
     * */
    add: function(option){
        var cache = mass.cache,
            rectuuid = cache.rectuuid,
            rectId = 'rect-' + rectuuid,
            imgCover = $('#J-image-process-cover'),
            rectEntry = $('

'),
            settingArea = $('

'),
            resizeZone = $('

'),
            currentStyles;

        currentStyles = {
            left: option.left,
            top: option.top,
            width: option.width,
            height: option.height,
            cursor: 'move'
        };

        settingArea.append($('#J-template-rect-setting').html());
        rectEntry.attr('id', rectId).css(currentStyles).append(settingArea).append(resizeZone);
        imgCover.append(rectEntry);

        cache.rectuuid++;
        cache.rectNum++;
        cache.rect[rectId] = option;
    },
    focus: function(rectId){
        var cache = mass.cache,
            imgCover = $('#J-image-process-cover');

        cache.focusRectId = rectId;
        cache.focusLineId = null;
        cache.focusTextAreaId = null;
        imgCover.find('.lineX, .lineY').removeClass('line-focus');
        imgCover.find('.textzone').removeClass('textzone-focus');
        imgCover.find('.rect').removeClass('rect-focus');
        $('#' + rectId).addClass('rect-focus');
    },
    delete: function(rectId){
        var cache = mass.cache,
            curRect = $('#' + rectId);

        cache.rectNum--;

        delete mass.cache.rect[rectId];
        cache.focusRectId = null;

        curRect.remove();
    },
    // 批量导入热区
    import: function(rectObj, callback){
        _.each(rectObj, function(rect){
            main.add(rect);
        });

        if(callback){
            callback();
        }
    },
    // 检查热区位置
    check: function(){
        var cache = mass.cache,
            lines = cache.line,
            rects = cache.rect,
            isBig2 = !!(cache.img.width > 990 && cache.lineY > 1),
            critical = {
                X:{},
                Y:{}
            },
            rectInBlock = {},
            blocks,
            res = true;

        if(!cache.rectNum) return res;

        $('.rect').removeClass('rect-error');

        $.each(rects, function(rectId, rect){
            critical.Y.x = rect.left;
            critical.Y.y = rect.left + rect.width;
            critical.X.x = rect.top;
            critical.X.y = rect.top + rect.height;
            $.each(lines, function(lineId, line){
                var type = line.type,
                    pos = line.pos;

                if(pos > critical[type].x && pos < critical[type].y){
                    res = false;
                    console.log(rectId, type, critical);
                    console.log(lineId, pos);
                    console.log('--------');
                    $('#' + rectId).addClass('rect-error');
                }
            });
        });

        if(res){
            if(isBig2){
                blocks = mass.getCutBlocks('children');

                $.each(blocks, function(i, block){
                    var blockParentIndex = block.parentBlockIndex;
                    $.each(rects, function(rectId, rect){
                        var topAndSelfHeight = block.y + block.height,
                            leftAndSelfWidth = block.x + block.width;

                        if(block.width > rect.width && block.x < rect.left && block.y < rect.top && (topAndSelfHeight > rect.top + rect.height) && (leftAndSelfWidth > rect.left + rect.width)){
                            rectInBlock[blockParentIndex] = rectInBlock[blockParentIndex] || [];
                            rectInBlock[blockParentIndex].push({
                                rect: rect,
                                left: rect.left - block.x,
                                top: rect.top - block.y,
                                belongBlockIndex: block.index
                            });
                        }
                    });
                });
            }
            else{
                blocks = mass.getCutBlocks();

                $.each(blocks, function(i, block){
                    $.each(rects, function(rectId, rect){
                        var topAndSelfHeight = block.y + block.height,
                            leftAndSelfWidth = block.x + block.width;

                        if(block.width > rect.width && block.x < rect.left && block.y < rect.top && (topAndSelfHeight > rect.top + rect.height) && (leftAndSelfWidth > rect.left + rect.width)){
                            rectInBlock[i] = rectInBlock[i] || [];
                            rectInBlock[i].push({
                                rect: rect,
                                left: rect.left - block.x,
                                top: rect.top - block.y
                            });
                        }
                    });
                });
            }
        }
        cache.rectInBlock = rectInBlock;

        return res;
        //return false;
    }
};
module.exports = main;<commit_msg>:curly_loop:<commit_after>/**
 * rect.
 */

var window = global.window,
    $ = window.$,
    mass = global.mass,
    _ = window._,
    document = window.document,
    console = window.console;

var main = {
    // 画热区
    drawMap: function() {
        var cache = mass.cache,
            imgCover = $('#J-image-process-cover'),
            imgItem = imgCover.parent(),
            newRect = $('

'),
            _drawMove = false,
            _drawMoving = false,
            scrollTop = 0,
            scrollLeft = 0,
            imgWidth, imgHeight,
            left, top, distX, distY, rect, rectCreated;

        var _rectMove = false,
            _rectMoving = false,
            rectAddX = 0,
            rectAddY = 0,
            currentRectId, trueLeft, trueTop, currentRect;

        var _rectResize = false,
            _rectResizing = false,
            trueWidth, trueHeight;

        // 点击层或resize时，将焦点放在当前的层，同时赋值基础数据
        var focusCurrentBlock = function(e, id) {
            main.focus(id);
            currentRect = cache.rect[cache.focusRectId];

            left = e.clientX - cache.minusX + imgItem.scrollLeft();
            top = e.clientY - cache.minusY + imgItem.scrollTop();
            distX = e.clientX - cache.minusX + scrollLeft;
            distY = e.clientY - cache.minusY + scrollTop;
            imgWidth = cache.img.width;
            imgHeight = cache.img.height;
            rectAddX = left - currentRect.left;
            rectAddY = top - currentRect.top;

            var $curRect = $('#' + id);
            if ($curRect.hasClass('rect-error')) {
                $curRect.removeClass('rect-error');
            }
        };

        imgCover.mousedown(function(e) {
            // 右键不触发
            if (e.button === 2) return;

            if (cache.drawMap) {
                _drawMove = true;
                left = e.clientX - cache.minusX + imgItem.scrollLeft();
                top = e.clientY - cache.minusY + imgItem.scrollTop();
                distX = left;
                distY = top;

                imgWidth = cache.img.width;
                imgHeight = cache.img.height;
            }
        });

        $(document).mousemove(function(e) {
            if (_drawMove || _rectMove || _rectResize) {
                scrollLeft = imgItem.scrollLeft();
                scrollTop = imgItem.scrollTop();
                distX = e.clientX - cache.minusX + scrollLeft;
                distY = e.clientY - cache.minusY + scrollTop;

                distX = Math.min(distX, imgWidth - 2);
                distY = Math.min(distY, imgHeight - 2);

                var $currentRect = $('#' + currentRectId);
            }

            if (_drawMove) {
                if (!rectCreated) {
                    rect = newRect.clone().attr('id', 'rect-' + cache.rectuuid);
                    imgCover.append(rect);
                    rectCreated = true;
                    main.focus('rect-' + cache.rectuuid);

                    cache.rectuuid++;
                    cache.rectNum++;
                }

                rect.css({
                    left: left,
                    top: top,
                    width: distX - left,
                    height: distY - top
                });

                _drawMoving = true;
            }
            else if (_rectMove) {
                trueLeft = distX - rectAddX;
                trueTop = distY - rectAddY;
                //cacheRect = cache.rect[currentRectId];

                // 子欲拖出图片区域之外，我偏不让
                // 拖到图像区域外松手时，将left值替换成边界值
                if (trueLeft > imgWidth - currentRect.width - 2) {
                    trueLeft = imgWidth - currentRect.width - 2;
                } else if (trueLeft < 0) {
                    trueLeft = 0;
                }

                if (trueTop > imgHeight - currentRect.height - 2) {
                    trueTop = imgHeight - currentRect.height - 2;
                } else if (trueTop < 0) {
                    trueTop = 0;
                }

                $currentRect.css({
                    left: trueLeft,
                    top: trueTop
                });

                _rectMoving = true;
            }
            else if (_rectResize) {
                trueWidth = distX - currentRect.left;
                trueHeight = distY - currentRect.top;

                // 子欲拖出图片区域之外，我偏不让
                if (trueWidth > imgWidth - currentRect.left - 2) {
                    trueWidth = imgWidth - currentRect.left - 2;
                } else if (trueWidth < 10) {
                    trueWidth = 10;
                }
                if (trueHeight > imgHeight - currentRect.top - 2) {
                    trueHeight = imgHeight - currentRect.top - 2;
                } else if (trueHeight < 10) {
                    trueHeight = 10;
                }

                $currentRect.css({
                    width: trueWidth,
                    height: trueHeight
                });

                // 热区设置层同步赋值
                if (!$currentRect.find('.setting-area').hasClass('hide')) {
                    $currentRect.find('input[data-type="width"]').val(trueWidth);
                    $currentRect.find('input[data-type="height"]').val(trueHeight);
                }

                _rectResizing = true;
            }
        }).mouseup(function() {
                if (_drawMove) {
                    if (_drawMoving) {
                        if (distX < left || distY < top) {
                            main.delete(cache.focusRectId);
                        }
                        else{
                            // 超出图片区域之外，将dist值替换成边界值
                            if (distX > imgWidth - 2) {
                                distX = imgWidth - 2;
                            }
                            if (distY > imgHeight - 2) {
                                distY = imgHeight - 2;
                            }

                            cache.rect[cache.focusRectId] = {
                                left: left,
                                top: top,
                                width: Math.max(distX - left, 10),
                                height: Math.max(distY - top, 10),
                                url: '#',
                                open: false
                            };

                            rect.css('cursor', 'move')
                                .append($('#J-template-rect-setting').clone().removeAttr('id'))
                                .append('

');
                        }
                    }

                    _drawMove = false;
                    _drawMoving = false;
                    rectCreated = false;
                }

                if (_rectMove) {
                    if (_rectMoving) {
                        cache.rect[currentRectId].left = trueLeft;
                        cache.rect[currentRectId].top = trueTop;

                        _rectMoving = false;
                    }
                    _rectMove = false;
                }

                if (_rectResize) {
                    if (_rectResizing) {
                        cache.rect[currentRectId].width = trueWidth;
                        cache.rect[currentRectId].height = trueHeight;

                        _rectResizing = false;
                    }
                    _rectResize = false;

                    if (cache.drawMap) {
                        imgCover.removeClass('resizing');
                    }
                }
            });

        // 热区移动
        imgCover.delegate('.rect', 'mousedown', function(e) {
            e.stopPropagation();
            //if (e.button === 2) return;
            currentRectId = this.id;
            focusCurrentBlock(e, currentRectId);

            _rectMove = true;
        });

        // 热区收缩
        imgCover.delegate('.resize', 'mousedown', function(e) {
            e.stopPropagation();

            currentRectId = this.parentNode.id;
            focusCurrentBlock(e, currentRectId);

            if (cache.drawMap) {
                imgCover.addClass('resizing');
            }

            _rectResize = true;
        });

        // 热区设置
        imgCover.delegate('.setting', 'mousedown', function(e) {
            e.stopPropagation();
            var that = $(this),
                area = that.prev(),
                rectId = that.parent().attr('id'),
                rect = cache.rect[rectId];

            if (area.hasClass('hide')) {
                area.removeClass('hide');
                area.find('input').each(function() {
                    var curInput = $(this),
                        type = curInput.data('type');
                    if (curInput.is(':checkbox')) {
                        if (rect[type]) {
                            curInput.attr('checked', true);
                        } else{
                            curInput.removeAttr('checked');
                        }
                    } else{
                        rect[type] && curInput.val(rect[type]);
                    }
                });
            } else{
                area.addClass('hide');
            }
        }).delegate('.setting-area', 'mousedown', function(e) {
                e.stopPropagation();
            }).delegate('.rect-setting-column input', 'change', function(e) {
                var that = $(this),
                    value = that.val(),
                    type = that.data('type'),
                    rectId = that.parents('.rect').attr('id'),
                    curRect = $('#' + rectId),
                    rect = cache.rect[rectId];

                if (type === 'open') {
                    value = that.is(':checked');
                }
                else if (type === 'width') {
                    value = parseInt(value);
                    if (value > cache.img.width - rect.left - 2) {
                        value = cache.img.width - rect.left - 2;
                    }
                    curRect.width(value)
                }
                else if (type === 'height') {
                    value = parseInt(value);
                    if (value > cache.img.height - rect.top - 2) {
                        value = cache.img.height - rect.top - 2;
                    }
                    curRect.height(value)
                }

                rect[type] = value;
                that.val(value);

                e.stopPropagation();
            });
    },
    reset: function() {
        // 清除旧的热区
        $('#J-image-process-cover').find('.rect').remove();

        $.extend(true, mass.cache, {
            rectuuid: 1,
            rectNum: 0,
            focusRectId: null
        });

        mass.cache.rect = {};
    },
    /*
     * option:
     * {
     *     left: Number,
     *     top: Number,
     *     width: Number,
     *     height: Number,
     *     url: String,
     *     open: Boolean
     * }
     * */
    add: function(option) {
        var cache = mass.cache,
            rectuuid = cache.rectuuid,
            rectId = 'rect-' + rectuuid,
            imgCover = $('#J-image-process-cover'),
            rectEntry = $('

'),
            settingArea = $('

'),
            resizeZone = $('

'),
            currentStyles;

        currentStyles = {
            left: option.left,
            top: option.top,
            width: option.width,
            height: option.height,
            cursor: 'move'
        };

        settingArea.append($('#J-template-rect-setting').html());
        rectEntry.attr('id', rectId).css(currentStyles).append(settingArea).append(resizeZone);
        imgCover.append(rectEntry);

        cache.rectuuid++;
        cache.rectNum++;
        cache.rect[rectId] = option;
    },
    focus: function(rectId) {
        var cache = mass.cache,
            imgCover = $('#J-image-process-cover');

        cache.focusRectId = rectId;
        cache.focusLineId = null;
        cache.focusTextAreaId = null;
        imgCover.find('.lineX, .lineY').removeClass('line-focus');
        imgCover.find('.textzone').removeClass('textzone-focus');
        imgCover.find('.rect').removeClass('rect-focus');
        $('#' + rectId).addClass('rect-focus');
    },
    delete: function(rectId) {
        var cache = mass.cache,
            curRect = $('#' + rectId);

        cache.rectNum--;

        delete mass.cache.rect[rectId];
        cache.focusRectId = null;

        curRect.remove();
    },
    // 批量导入热区
    import: function(rectObj, callback) {
        _.each(rectObj, function(rect) {
            main.add(rect);
        });

        if (callback) {
            callback();
        }
    },
    // 检查热区位置
    check: function() {
        var cache = mass.cache,
            lines = cache.line,
            rects = cache.rect,
            isBig2 = !!(cache.img.width > 990 && cache.lineY > 1),
            critical = {
                X:{},
                Y:{}
            },
            rectInBlock = {},
            blocks,
            res = true;

        if (!cache.rectNum) return res;

        $('.rect').removeClass('rect-error');

        $.each(rects, function(rectId, rect) {
            critical.Y.x = rect.left;
            critical.Y.y = rect.left + rect.width;
            critical.X.x = rect.top;
            critical.X.y = rect.top + rect.height;
            $.each(lines, function(lineId, line) {
                var type = line.type,
                    pos = line.pos;

                if (pos > critical[type].x && pos < critical[type].y) {
                    res = false;
                    console.log(rectId, type, critical);
                    console.log(lineId, pos);
                    console.log('--------');
                    $('#' + rectId).addClass('rect-error');
                }
            });
        });

        if (res) {
            if (isBig2) {
                blocks = mass.getCutBlocks('children');

                $.each(blocks, function(i, block) {
                    var blockParentIndex = block.parentBlockIndex;
                    $.each(rects, function(rectId, rect) {
                        var topAndSelfHeight = block.y + block.height,
                            leftAndSelfWidth = block.x + block.width;

                        if (block.width > rect.width && block.x < rect.left && block.y < rect.top && (topAndSelfHeight > rect.top + rect.height) && (leftAndSelfWidth > rect.left + rect.width)) {
                            rectInBlock[blockParentIndex] = rectInBlock[blockParentIndex] || [];
                            rectInBlock[blockParentIndex].push({
                                rect: rect,
                                left: rect.left - block.x,
                                top: rect.top - block.y,
                                belongBlockIndex: block.index
                            });
                        }
                    });
                });
            }
            else{
                blocks = mass.getCutBlocks();

                $.each(blocks, function(i, block) {
                    $.each(rects, function(rectId, rect) {
                        var topAndSelfHeight = block.y + block.height,
                            leftAndSelfWidth = block.x + block.width;

                        if (block.width > rect.width && block.x < rect.left && block.y < rect.top && (topAndSelfHeight > rect.top + rect.height) && (leftAndSelfWidth > rect.left + rect.width)) {
                            rectInBlock[i] = rectInBlock[i] || [];
                            rectInBlock[i].push({
                                rect: rect,
                                left: rect.left - block.x,
                                top: rect.top - block.y
                            });
                        }
                    });
                });
            }
        }
        cache.rectInBlock = rectInBlock;

        return res;
        //return false;
    }
};
module.exports = main;<|endoftext|>


<commit_before>// @flow

type Config = {
  ignoreCase?: boolean,
  ignoreAccents?: boolean,
  stringify?: Object => string,
  trim?: boolean,
  matchFrom?: 'any' | 'start',
};

import { stripDiacritics } from './diacritics';

const trimString = str => str.replace(/^\s+|\s+$/g, '');
const defaulStringify = option => `${option.label} ${option.value}`;

export const createFilter = (config: ?Config) => (
  option: { label: string, value: string, data: any },
  rawInput: string
) => {
  const { ignoreCase, ignoreAccents, stringify, trim, matchFrom } = {
    ignoreCase: true,
    ignoreAccents: true,
    stringify: defaulStringify,
    trim: true,
    matchFrom: 'any',
    ...config,
  };
  let input = trim ? trimString(rawInput) : rawInput;
  let candidate = trim ? trimString(stringify(option)) : stringify(option);
  if (ignoreCase) {
    input = input.toLowerCase();
    candidate = candidate.toLowerCase();
  }
  if (ignoreAccents) {
    input = stripDiacritics(input);
    candidate = stripDiacritics(candidate);
  }
  return matchFrom === 'start'
    ? candidate.substr(0, input.length) === input
    : candidate.indexOf(input) > -1;
};
<commit_msg>fix typo<commit_after>// @flow

type Config = {
  ignoreCase?: boolean,
  ignoreAccents?: boolean,
  stringify?: Object => string,
  trim?: boolean,
  matchFrom?: 'any' | 'start',
};

import { stripDiacritics } from './diacritics';

const trimString = str => str.replace(/^\s+|\s+$/g, '');
const defaultStringify = option => `${option.label} ${option.value}`;

export const createFilter = (config: ?Config) => (
  option: { label: string, value: string, data: any },
  rawInput: string
) => {
  const { ignoreCase, ignoreAccents, stringify, trim, matchFrom } = {
    ignoreCase: true,
    ignoreAccents: true,
    stringify: defaultStringify,
    trim: true,
    matchFrom: 'any',
    ...config,
  };
  let input = trim ? trimString(rawInput) : rawInput;
  let candidate = trim ? trimString(stringify(option)) : stringify(option);
  if (ignoreCase) {
    input = input.toLowerCase();
    candidate = candidate.toLowerCase();
  }
  if (ignoreAccents) {
    input = stripDiacritics(input);
    candidate = stripDiacritics(candidate);
  }
  return matchFrom === 'start'
    ? candidate.substr(0, input.length) === input
    : candidate.indexOf(input) > -1;
};
<|endoftext|>


<commit_before>/**
 *
 * Adapted/refactored from konklone adaptation of csvkit's recursive JSON flattening algorithm
 * Konklone JSON - https://github.com/konklone/json/blob/gh-pages/assets/site.js#L28-L55
 * onyxfish csvkit - https://github.com/onyxfish/csvkit/blob/61b9c208b7665c20e9a8e95ba6eee811d04705f0/csvkit/convert/js.py#L15-L34
 *
 */

var EMPTY_STRING = "";
var DELIMITER = "/";

/**
 *
 * flattenObect
 * @param {object} obj - to flattening
 * @param {string} path - key path to current key
 * @returns {object}
 *
 */

function flatten(obj, path) {
    if (path == undefined) {
        path = EMPTY_STRING;
    }

    // TODO: Create improved name
    var d = {};

    if (isArray(obj) || isObject(obj)) {
        for (var key in obj) {
            // TODO: extract into method?
            var keyPath = [
                path,
                key,
                DELIMITER
            ].join('');

            // Recursive flatten call
            var newD = flatten(obj[key], keyPath);

            $.extend(d, newD);
        }

    } else if (isScalar(obj)) {
        var endPath = _stripRight(path, DELIMITER);

        d[endPath] = obj;
    }

    return d;
}

/**
 *
 * flattenMany
 * @param {array} data
 * @return {array}
 *
 */

function flattenMany(data) {
    var flattenedObjects = [];

    for (var i = 0; i < data.length; i++) {
        var currentObject = data[i];
        var flatObject = flatten(currentObject);

        flattenedObjects.push(flatObject);
    }

    return flattenedObjects;
}

/**
 *
 * _stripRight
 * @param {string} str
 * @param {string} match
 * @return {string}
 *
 */

function _stripRight(str, match) {
    var START_INDEX = 0;
    var END_INDEX = str.length - 1;

    if (str[END_INDEX] === match) {
        return str.substr(START_INDEX, END_INDEX);
    }

    return str;
}

/**
 *
 * isArray
 * @param {}
 * @return {boolean}
 *
 */

function isArray(value) {
    var type = $.type(value);

    return type === 'array';
}

/**
 *
 * isObject
 * @param {}
 * @return {boolean}
 *
 */

function isObject(value) {
    var type = $.type(value);

    return type === 'object';
}

/**
 *
 * isScalar
 * @param {}
 * @return {boolean}
 *
 */

function isScalar(value) {
    var type = $.type(value);

    return (type == "number" ||
        type == "string" ||
        type == "boolean" ||
        type == "null");
}<commit_msg>refactor: factor key string concatenation into well named function<commit_after>/**
 *
 * Adapted/refactored from konklone adaptation of csvkit's recursive JSON flattening algorithm
 * Konklone JSON - https://github.com/konklone/json/blob/gh-pages/assets/site.js#L28-L55
 * onyxfish csvkit - https://github.com/onyxfish/csvkit/blob/61b9c208b7665c20e9a8e95ba6eee811d04705f0/csvkit/convert/js.py#L15-L34
 *
 */

var EMPTY_STRING = "";
var DELIMITER = "/";

/**
 *
 * flattenObect
 * @param {object} obj - to flattening
 * @param {string} path - key path to current key
 * @returns {object}
 *
 */

function flatten(obj, path) {
    if (path == undefined) {
        path = EMPTY_STRING;
    }

    // TODO: Improve name of variable
    var d = {};

    if (isArray(obj) || isObject(obj)) {
        for (var key in obj) {
            var newKey = _createKeyString(path, key, DELIMITER);

            var newD = flatten(obj[key], newKey); // Recursive call

            $.extend(d, newD);
        }
    } else if (isScalar(obj)) {
        var endPath = _stripRight(path, DELIMITER);
        d[endPath] = obj;
    }

    return d;
}

/**
 *
 * flattenMany
 * @param {array} data
 * @return {array}
 *
 */

function flattenMany(data) {
    var flattenedObjects = [];

    for (var i = 0; i < data.length; i++) {
        var currentObject = data[i];

        var flatObject = flatten(currentObject);

        flattenedObjects.push(flatObject);
    }

    return flattenedObjects;
}

/**
 *
 * _createKeyString
 * @param {string} path
 * @param {string} key
 * @param {string} delimiter
 * @return {string}
 *
 */

function _createKeyString(path, key, delimiter) {
    return [
        path,
        key,
        DELIMITER
    ].join('');
}

/**
 *
 * _stripRight
 * @param {string} str
 * @param {string} match
 * @return {string}
 *
 */

function _stripRight(str, match) {
    var START_INDEX = 0;
    var END_INDEX = str.length - 1;

    if (str[END_INDEX] === match) {
        return str.substr(START_INDEX, END_INDEX);
    }

    return str;
}

/**
 *
 * isArray
 * @param {}
 * @return {boolean}
 *
 */

function isArray(value) {
    var type = $.type(value);

    return type === 'array';
}

/**
 *
 * isObject
 * @param {}
 * @return {boolean}
 *
 */

function isObject(value) {
    var type = $.type(value);

    return type === 'object';
}

/**
 *
 * isScalar
 * @param {}
 * @return {boolean}
 *
 */

function isScalar(value) {
    var type = $.type(value);

    return (type == "number" ||
        type == "string" ||
        type == "boolean" ||
        type == "null");
}<|endoftext|>


<commit_before>/**
 *
 * Adapted/refactored from konklone adaptation of csvkit's recursive JSON flattening algorithm
 * Konklone JSON - https://github.com/konklone/json/blob/gh-pages/assets/site.js#L28-L55
 * onyxfish csvkit - https://github.com/onyxfish/csvkit/blob/61b9c208b7665c20e9a8e95ba6eee811d04705f0/csvkit/convert/js.py#L15-L34
 *
 */

var EMPTY_STRING = "";
var DELIMITER = "/";

/**
 *
 * flattenObect
 * @param {object} obj - to flattening
 * @param {string} path - key path to current key
 * @returns {object}
 *
 */

function flattenObject(obj, path) {
    if (path == undefined) {
        path = EMPTY_STRING;
    }

    if (isArray(obj) || isObject(obj)) {
        var d = {};
        for (var i in obj) {

            var newD = flattenObject(obj[i], path + i + DELIMITER);
            $.extend(d, newD);
        }

        return d;
    } else if (isScalar(obj)) {
        var d = {};
        var endPath = path.substr(0, path.length-1);
        d[endPath] = obj;
        return d;
    }

    // ?
    else return {};
}

/**
 *
 * isArray
 * @param {}
 * @return {boolean}
 *
 */

function isArray(value) {
    var type = $.type(value);

    return type === 'array';
}

/**
 *
 * isObject
 * @param {}
 * @return {boolean}
 *
 */

function isObject(value) {
    var type = $.type(value);

    return type === 'object';
}

/**
 *
 * isScalar
 * @param {}
 * @return {boolean}
 *
 */

function isScalar(value) {
    var type = $.type(value);

    return (type == "number" ||
            type == "string" ||
            type == "boolean" ||
            type == "null");
}<commit_msg>refactor: improve var names, extract string concat into variable<commit_after>/**
 *
 * Adapted/refactored from konklone adaptation of csvkit's recursive JSON flattening algorithm
 * Konklone JSON - https://github.com/konklone/json/blob/gh-pages/assets/site.js#L28-L55
 * onyxfish csvkit - https://github.com/onyxfish/csvkit/blob/61b9c208b7665c20e9a8e95ba6eee811d04705f0/csvkit/convert/js.py#L15-L34
 *
 */

var EMPTY_STRING = "";
var DELIMITER = "/";

/**
 *
 * flattenObect
 * @param {object} obj - to flattening
 * @param {string} path - key path to current key
 * @returns {object}
 *
 */

function flattenObject(obj, path) {
    if (path == undefined) {
        path = EMPTY_STRING;
    }

    if (isArray(obj) || isObject(obj)) {
        var d = {};

        for (var key in obj) {
            // TODO: extract into method?
            var keyPath = [
                path,
                key,
                DELIMITER
            ].join('');

            // Recursive flatten call
            var newD = flattenObject(obj[key], keyPath);

            $.extend(d, newD);
        }

        return d;
    } else if (isScalar(obj)) {
        var d = {};
        var endPath = path.substr(0, path.length - 1);
        d[endPath] = obj;
        return d;
    }

    // ?
    else return {};
}

/**
 *
 * isArray
 * @param {}
 * @return {boolean}
 *
 */

function isArray(value) {
    var type = $.type(value);

    return type === 'array';
}

/**
 *
 * isObject
 * @param {}
 * @return {boolean}
 *
 */

function isObject(value) {
    var type = $.type(value);

    return type === 'object';
}

/**
 *
 * isScalar
 * @param {}
 * @return {boolean}
 *
 */

function isScalar(value) {
    var type = $.type(value);

    return (type == "number" ||
        type == "string" ||
        type == "boolean" ||
        type == "null");
}<|endoftext|>


<commit_before>'use strict'
module.exports = xs => {
  let seen = {};
  let out = [];
  let j = 0;

  for (let i = 0, len = xs.length; i < len; i += 1) {
    let item = xs[i];

    if (!seen[item]) {
      seen[item] = true;
      j += 1
      out[j] = item;
    }
  }
  return out
}
<commit_msg>Remove uniq - using a npm version<commit_after><|endoftext|>


<commit_before>/*
 * gkeypath
 * https://github.com/goliatone/gkeypath
 * Created with gbase.
 * Copyright (c) 2014 goliatone
 * Licensed under the MIT license.
 */
/* jshint strict: false, plusplus: true */
/*global define: false, require: false, module: false, exports: false */
(function(root, name, deps, factory) {
    "use strict";
    // Node
    if (typeof deps === 'function') {
        factory = deps;
        deps = [];
    }

    if (typeof exports === 'object') {
        module.exports = factory.apply(root, deps.map(require));
    } else if (typeof define === 'function' && 'amd' in define) {
        //require js, here we assume the file is named as the lower
        //case module name.
        define(name.toLowerCase(), deps, factory);
    } else {
        // Browser
        var d, i = 0,
            global = root,
            old = global[name],
            mod;
        while ((d = deps[i]) !== undefined) deps[i++] = root[d];
        global[name] = mod = factory.apply(global, deps);
        //Export no 'conflict module', aliases the module.
        mod.noConflict = function() {
            global[name] = old;
            return mod;
        };
    }
}(this, 'keypath', function() {

    var Keypath = {};

    var DEFAULTS = {
        assertionMessage: 'Assertion failed'
    };

    Keypath.VERSION = '0.11.1';

    /**
     * Set a value in target following keypath.
     *
     * @param {Object} target Source object
     * @param {String} path Keypath to desired property
     * @param {Mixed} value Value to be set
     * @param {Object} options
     * @param {Object} [options.useGetters=true]
     * @returns {Object} Returns target
     */
    Keypath.set = function(target, path, value, o = { useGetters: true }) {
        if (!target) return undefined;

        var keys = path.split('.');
        path = keys.pop();
        keys.forEach(function(prop) {
            if (!target[prop]) target[prop] = {};
            target = target[prop];
        });
        /**
         * We do not support `...` but we could
         * extend to enable array concatenation
         */
        if (path.match(/\w+\[(\d+)\]/)) {
            let [_, name, index] = path.match(/(\w+)\[(\d+)\]/);
            if (!Array.isArray(target[name])) target[name] = [];
            target = target[name];
            path = index;
        }

        Keypath._set(target, path, value, o); //target[path] = value;

        return target;
    };

    /**
     * Get the value of keypath in a given object.
     * If nothing is found then return `defaultValue`.
     *
     * @param {Object} target Source object
     * @param {String} path Keypath to desired property
     * @param {Mixed} defaultValue Default value if not matched
     * @param {Object} options
     * @param {Object} [options.useGetters=true]
     * @returns {Mixed}
     */
    Keypath.get = function(target, path, defaultValue, o = { useGetters: true }) {
        if (!target || !path) return defaultValue;

        path = path.split('.');
        var l = path.length,
            i = 0,
            p = '';
        for (; i < l; ++i) {
            p = path[i];

            /**
             * Handle array syntax: e.g. `arrayName.1`
             */
            if (p.match(/\w+\[(\d+)\]/)) {
                let [_, name, index] = p.match(/(\w+)\[(\d+)\]/);
                target = target[name];
                if (!target) return defaultValue;
                p = index;
            }

            if (target === null) return Keypath._get(defaultValue, o);

            if (target[p] !== undefined) target = target[p];
            else return Keypath._get(defaultValue, o);
        }
        return Keypath._get(target, o);
    };

    Keypath.del = function(source, path) {
        var target = source;
        if (!target || !path) return false;

        path = path.split('.');

        var l = path.length,
            i = 0,
            p = '';

        for (; i < l; ++i) {
            p = path[i];
            if (i === l - 1) {
                if (Array.isArray(target)) {
                    target[p] = undefined;
                } else delete target[p];
            } else if (target[p] !== undefined) target = target[p];
        }

        return source;
    };

    Keypath.has = function(target, path) {
        return this.get(target, path, '#$#NFV#$#') !== '#$#NFV#$#';
    };

    Keypath.assert = function(target, path, message) {
        message = message || Keypath.DEFAULTS.assertionMessage;
        var value = this.get(target, path, message);

        if (value !== message) return value;

        this.onError(message, path);

        return undefined;
    };

    /**
     * Takes an array of keypaths and returns the
     * value of the first matched path. If none match
     * will return `defaultValue`.
     *
     * @param {Object} target Source object
     * @param {Array} [paths=[]] Path list
     * @param {*} defaultValue Default value
     *
     * @returns {Mixed} Matched key or default value.
     */
    Keypath.oneOf = function(target, paths = [], defaultValue = undefined) {
        for (let path of paths) {
            if (this.has(target, path)) {
                return this.get(target, path);
            }
        }
        return defaultValue;
    };

    //TODO: we might want to reverse the order, and have a different
    //signature. target, propName, inject
    Keypath.wrap = function(target, inject, dataPropName) {
        var wrapper = new Wrapper(target, dataPropName);

        if (Proxy) {
            wrapper = new Proxy(wrapper, {
                get: function(receiver, prop) {
                    if (typeof receiver[prop] === 'function') {
                        return function(...args) {
                            return Reflect.apply(receiver[prop], receiver, args);
                        };
                    }
                    var out = receiver._target[prop];
                    if (out === undefined && receiver.hasOwnProperty(prop)) out = receiver[prop];
                    if (out === undefined && prop === dataPropName) out = receiver._target;
                    return out;
                },
                set: function(receiver, prop, value) {
                    receiver._target[prop] = value;
                }
            });
        }

        if (!inject) return wrapper;

        if (typeof inject === 'function') inject(target, wrapper);
        if (typeof inject === 'string') Keypath.set(target, inject, wrapper);

        return wrapper;
    };

    Keypath.onError = function() {
        console.error.call(console, arguments);
    };

    ///////////////////////////////////////////////////
    // PRIVATE METHODS
    ///////////////////////////////////////////////////
    Keypath._get = function(value, o = { useGetters: true }) {
        if (o.useGetters === false) return value;
        return typeof value === 'function' ? value() : value;
    };

    Keypath._set = function(src, method, val, o = { useGetters: true }) {
        if (typeof src[method] === 'function' && o.useGetters) {
            return src[method].call(src, val);
        }
        return src[method] = val;
    };

    ///////////////////////////////////////////////////
    // WRAPPER Internal Class
    ///////////////////////////////////////////////////
    /**
     * Wrapper Constructor
     * @param {Object} target Object to be wrapped
     */

    function Wrapper(target, prop) {
        prop = prop || 'target';
        this[prop] = this._target = target;
    }

    Wrapper.prototype.set = function(path, value, o = { useGetters: true }) {
        return Keypath.set(this._target, path, value, o);
    };

    Wrapper.prototype.get = function(path, defaultValue, o = { useGetters: true }) {
        return Keypath.get(this._target, path, defaultValue, o);
    };

    Wrapper.prototype.has = function(path) {
        return Keypath.has(this._target, path);
    };

    Keypath.Wrapper = Wrapper;

    return Keypath;
}));
<commit_msg>add: ifNotSet function<commit_after>/*
 * gkeypath
 * https://github.com/goliatone/gkeypath
 * Created with gbase.
 * Copyright (c) 2014 goliatone
 * Licensed under the MIT license.
 */
/* jshint strict: false, plusplus: true */
/*global define: false, require: false, module: false, exports: false */
(function(root, name, deps, factory) {
    "use strict";
    // Node
    if (typeof deps === 'function') {
        factory = deps;
        deps = [];
    }

    if (typeof exports === 'object') {
        module.exports = factory.apply(root, deps.map(require));
    } else if (typeof define === 'function' && 'amd' in define) {
        //require js, here we assume the file is named as the lower
        //case module name.
        define(name.toLowerCase(), deps, factory);
    } else {
        // Browser
        var d, i = 0,
            global = root,
            old = global[name],
            mod;
        while ((d = deps[i]) !== undefined) deps[i++] = root[d];
        global[name] = mod = factory.apply(global, deps);
        //Export no 'conflict module', aliases the module.
        mod.noConflict = function() {
            global[name] = old;
            return mod;
        };
    }
}(this, 'keypath', function() {

    var Keypath = {};

    var DEFAULTS = {
        assertionMessage: 'Assertion failed'
    };

    Keypath.VERSION = '0.11.1';

    /**
     * Set a value in target following keypath.
     *
     * @param {Object} target Source object
     * @param {String} path Keypath to desired property
     * @param {Mixed} value Value to be set
     * @param {Object} options
     * @param {Object} [options.useGetters=true]
     * @returns {Object} Returns target
     */
    Keypath.set = function(target, path, value, o = { useGetters: true }) {
        if (!target) return undefined;

        var keys = path.split('.');
        path = keys.pop();
        keys.forEach(function(prop) {
            if (!target[prop]) target[prop] = {};
            target = target[prop];
        });
        /**
         * We do not support `...` but we could
         * extend to enable array concatenation
         */
        if (path.match(/\w+\[(\d+)\]/)) {
            let [_, name, index] = path.match(/(\w+)\[(\d+)\]/);
            if (!Array.isArray(target[name])) target[name] = [];
            target = target[name];
            path = index;
        }

        Keypath._set(target, path, value, o); //target[path] = value;

        return target;
    };

    /**
     * Get the value of keypath in a given object.
     * If nothing is found then return `defaultValue`.
     *
     * @param {Object} target Source object
     * @param {String} path Keypath to desired property
     * @param {Mixed} defaultValue Default value if not matched
     * @param {Object} options
     * @param {Object} [options.useGetters=true]
     * @returns {Mixed}
     */
    Keypath.get = function(target, path, defaultValue, o = { useGetters: true }) {
        if (!target || !path) return defaultValue;

        path = path.split('.');
        var l = path.length,
            i = 0,
            p = '';
        for (; i < l; ++i) {
            p = path[i];

            /**
             * Handle array syntax: e.g. `arrayName.1`
             */
            if (p.match(/\w+\[(\d+)\]/)) {
                let [_, name, index] = p.match(/(\w+)\[(\d+)\]/);
                target = target[name];
                if (!target) return defaultValue;
                p = index;
            }

            if (target === null) return Keypath._get(defaultValue, o);

            if (target[p] !== undefined) target = target[p];
            else return Keypath._get(defaultValue, o);
        }
        return Keypath._get(target, o);
    };

    Keypath.del = function(source, path) {
        var target = source;
        if (!target || !path) return false;

        path = path.split('.');

        var l = path.length,
            i = 0,
            p = '';

        for (; i < l; ++i) {
            p = path[i];
            if (i === l - 1) {
                if (Array.isArray(target)) {
                    target[p] = undefined;
                } else delete target[p];
            } else if (target[p] !== undefined) target = target[p];
        }

        return source;
    };

    /**
     *
     * Test if `target` has a value at `path`.
     *
     * @param {Object} target Source object
     * @param {String} path Keypath to desired property
     * @returns {Boolean}
     */
    Keypath.has = function(target, path) {
        return this.get(target, path, '#$#NFV#$#') !== '#$#NFV#$#';
    };

    Keypath.assert = function(target, path, message) {
        message = message || Keypath.DEFAULTS.assertionMessage;
        var value = this.get(target, path, message);

        if (value !== message) return value;

        this.onError(message, path);

        return undefined;
    };

    /**
     * Takes an array of keypaths and returns the
     * value of the first matched path. If none match
     * will return `defaultValue`.
     *
     * @param {Object} target Source object
     * @param {Array} [paths=[]] Path list
     * @param {*} defaultValue Default value
     *
     * @returns {Mixed} Matched key or default value.
     */
    Keypath.oneOf = function(target, paths = [], defaultValue = undefined) {
        for (let path of paths) {
            if (this.has(target, path)) {
                return this.get(target, path);
            }
        }
        return defaultValue;
    };

    /**
     * Get `target`s value of `path` and if
     * the property is not set then set to `setValue`.
     *
     * Returns either the value of path or `setValue`.
     *
     * @param {Object} target Source object
     * @param {String} path Keypath to desired property
     * @param {any} setValue Value to be set
     * @param {Object} o Options
     * @param {Boolean} [o.useGetters=true] Wether to unwrap functions.
     * @returns Either the value of path or `setValue`
     */
    Keypath.ifNotSet = function(target, path, setValue, o = { useGetters: true }) {
        if (this.has(target, path)) return this.get(target, path, undefined, o);

        var value = this._get(setValue, o);

        this.set(target, path, value);

        return value;
    };

    //TODO: we might want to reverse the order, and have a different
    //signature. target, propName, inject
    Keypath.wrap = function(target, inject, dataPropName) {
        var wrapper = new Wrapper(target, dataPropName);

        if (Proxy) {
            wrapper = new Proxy(wrapper, {
                get: function(receiver, prop) {
                    if (typeof receiver[prop] === 'function') {
                        return function(...args) {
                            return Reflect.apply(receiver[prop], receiver, args);
                        };
                    }
                    var out = receiver._target[prop];
                    if (out === undefined && receiver.hasOwnProperty(prop)) out = receiver[prop];
                    if (out === undefined && prop === dataPropName) out = receiver._target;
                    return out;
                },
                set: function(receiver, prop, value) {
                    receiver._target[prop] = value;
                }
            });
        }

        if (!inject) return wrapper;

        if (typeof inject === 'function') inject(target, wrapper);
        if (typeof inject === 'string') Keypath.set(target, inject, wrapper);

        return wrapper;
    };

    Keypath.onError = function() {
        console.error.call(console, arguments);
    };

    ///////////////////////////////////////////////////
    // PRIVATE METHODS
    ///////////////////////////////////////////////////
    Keypath._get = function(value, o = { useGetters: true }) {
        if (o.useGetters === false) return value;
        return typeof value === 'function' ? value() : value;
    };

    Keypath._set = function(src, method, val, o = { useGetters: true }) {
        if (typeof src[method] === 'function' && o.useGetters) {
            return src[method].call(src, val);
        }
        return src[method] = val;
    };

    ///////////////////////////////////////////////////
    // WRAPPER Internal Class
    ///////////////////////////////////////////////////
    /**
     * Wrapper Constructor
     * @param {Object} target Object to be wrapped
     */

    function Wrapper(target, prop) {
        prop = prop || 'target';
        this[prop] = this._target = target;
    }

    Wrapper.prototype.set = function(path, value, o = { useGetters: true }) {
        return Keypath.set(this._target, path, value, o);
    };

    Wrapper.prototype.get = function(path, defaultValue, o = { useGetters: true }) {
        return Keypath.get(this._target, path, defaultValue, o);
    };

    Wrapper.prototype.has = function(path) {
        return Keypath.has(this._target, path);
    };

    Keypath.Wrapper = Wrapper;

    return Keypath;
}));
<|endoftext|>


<commit_before>
kff.useJQuery = true;

var matchesName;
if('webkitMatchesSelector' in document.documentElement) matchesName = 'webkitMatchesSelector';
else if('mozMatchesSelector' in document.documentElement) matchesName = 'mozMatchesSelector';
else if('oMatchesSelector' in document.documentElement) matchesName = 'oMatchesSelector';
else if('msMatchesSelector' in document.documentElement) matchesName = 'msMatchesSelector';

kff.Dom = kff.createClass(
{
	constructor: function(element)
	{
		this['0'] = element;
		this.handlers = {};
		this.$element = null;
	},

	// on: function()
	// {
	// 	if(!this.$element) this.$element = jQuery(this['0']);
	// 	return this.$element.on.apply(this.$element, arguments);
	// },

	// off: function()
	// {
	// 	if(!this.$element) this.$element = jQuery(this['0']);
	// 	return this.$element.off.apply(this.$element, arguments);
	// },

	on: function(type, selector, handler)
	{
		var types = type.split(/\s+/);
		for(var i = 0, l = types.length; i < l; i++)
		{
			if(arguments.length === 3)
			{
				if(!this.handlers[selector])
				{
					this.handlers[selector] = this.createHandler(this['0'], selector, handler);
				}
				this['0'].addEventListener(types[i], this.handlers[selector], false);
			}
			else
			{
				this['0'].addEventListener(types[i], selector, false);
			}
		}
	},

	off: function(type, selector, handler)
	{
		var types = type.split(/\s+/);
		for(var i = 0, l = types.length; i < l; i++)
		{
			if(arguments.length === 3)
			{
				if(this.handlers[selector])
				{
					this['0'].removeEventListener(types[i], this.handlers[selector], false);
				}
			}
			else
			{
				this['0'].removeEventListener(types[i], selector, false);
			}
		}
	},

	createHandler: function(el, selector, handler)
	{
		var that = this;
		return function(event)
		{
			var target = event.target;

			while(target !== el)
			{
				if(matchesName)
				{
					if(target[matchesName](selector))
					{
						handler.call(target, event, target);
						break;
					}
				}
				else
				{
					if(that.matches(el, target, selector))
					{
						handler.call(target, event, target);
						break;
					}
				}
				target = target.parentNode;
			}
		};
	},

	matches: function(el, target, selector)
	{
		var elements = el.querySelectorAll(selector);
		return kff.arrayIndexOf(elements, target) !== -1;
	}
});

var $ = function(element)
{
	if(kff.useJQuery && window.jQuery)
	{
		$ = window.jQuery;
	}
	else
	{
		$ = function(element)
		{
			if(element instanceof kff.Dom) var el = new kff.Dom(element[0]);
			else var el =  new kff.Dom(element);
			return el;
		};
	}
	return $(element);
};<commit_msg>refactor(kff.Dom): misc. optimizations<commit_after>
kff.useJquery = true;

var matchesMethodName;
if('webkitMatchesSelector' in document.documentElement) matchesMethodName = 'webkitMatchesSelector';
else if('mozMatchesSelector' in document.documentElement) matchesMethodName = 'mozMatchesSelector';
else if('oMatchesSelector' in document.documentElement) matchesMethodName = 'oMatchesSelector';
else if('msMatchesSelector' in document.documentElement) matchesMethodName = 'msMatchesSelector';

kff.Dom = kff.createClass(
{
	constructor: function(element)
	{
		this['0'] = element;
		this.handlers = {};
		this.$element = null;
	},

	on: function(type, selector, handler)
	{
		var types = type.split(/\s+/);
		for(var i = 0, l = types.length; i < l; i++)
		{
			if(arguments.length === 3)
			{
				if(!this.handlers[selector])
				{
					this.handlers[selector] = this.f(this.delegatedEventHandler, [this['0'], selector, handler]);
				}
				this['0'].addEventListener(types[i], this.handlers[selector], false);
			}
			else
			{
				this['0'].addEventListener(types[i], selector, false);
			}
		}
	},

	off: function(type, selector, handler)
	{
		var types = type.split(/\s+/);
		for(var i = 0, l = types.length; i < l; i++)
		{
			if(arguments.length === 3)
			{
				if(this.handlers[selector])
				{
					this['0'].removeEventListener(types[i], this.handlers[selector], false);
				}
			}
			else
			{
				this['0'].removeEventListener(types[i], selector, false);
			}
		}
	},

	delegatedEventHandler: function(el, selector, handler, event)
	{
		var target = event.target;

		while(target !== el)
		{
			if(matchesMethodName)
			{
				if(target[matchesMethodName](selector))
				{
					event.matchedTarget = target;
					handler.call(target, event);
					break;
				}
			}
			else
			{
				if(this.matches(el, target, selector))
				{
					event.matchedTarget = target;
					handler.call(target, event);
					break;
				}
			}
			target = target.parentNode;
		}
	},

	matches: function(el, target, selector)
	{
		var elements = el.querySelectorAll(selector);
		return kff.arrayIndexOf(elements, target) !== -1;
	}
});

kff.$ = function(element)
{
	if(kff.useJquery && window.jQuery)
	{
		kff.$ = window.jQuery;
	}
	else
	{
		kff.$ = function(element)
		{
			if(element instanceof kff.Dom) var el = new kff.Dom(element[0]);
			else var el =  new kff.Dom(element);
			return el;
		};
	}
	return kff.$(element);
};

var $ = kff.$;<|endoftext|>


<commit_before>define('lang.ru', ['lang', 'grammar'], function(Lang, Grammar) {

  var {Translation, Language} = Lang;
  var {Word} = Grammar;

  //TODO: Generic verb translation class
  //TODO: Generic verb translation ending class
  //TODO: Particular ending verb translation class

  class VerbTranslation extends Translation {

    constructor(root, defaultForm, conjugationRoots) {
      super(root, defaultForm);
      this.conjugations = this.conjugate(this.readConjugationRoots(conjugationRoots));
    }

    readConjugationRoots(conjugationRoots) {
      if (!conjugationRoots) {
        conjugationRoots = {};
      }
      ['now', 'future', 'past'].forEach((time) => {
        if (!conjugationRoots[time]) {
          conjugationRoots[time] = this.root;
        }
      });
      return conjugationRoots;
    }

    conjugate(conjugationRoots) {
      return {
        now: {
          I: conjugationRoots.now + 'ю',
          you: conjugationRoots.now + 'ешь',
          you_formal: conjugationRoots.now + 'ете',
          he: conjugationRoots.now + 'ет',
          she: conjugationRoots.now + 'ет',
          it: conjugationRoots.now + 'ет',
          we: conjugationRoots.now + 'ем',
          you_plural_formal: conjugationRoots.now + 'ете',
          you_plural: conjugationRoots.now + 'ете',
          they: conjugationRoots.now + 'ют'
        },
        future: {
          I: 'буду ' + conjugationRoots.future + 'еть',
          you: 'будешь ' + conjugationRoots.future + 'еть',
          you_formal: 'будете ' + conjugationRoots.future + 'еть',
          he: 'будет ' + conjugationRoots.future + 'еть',
          she: 'будет ' + conjugationRoots.future + 'еть',
          it: 'будет ' + conjugationRoots.future + 'еть',
          we: 'будем ' + conjugationRoots.future + 'еть',
          you_plural_formal: 'будете ' + conjugationRoots.future + 'еть',
          you_plural: 'будете ' + conjugationRoots.future + 'еть',
          they: 'будут ' + conjugationRoots.future + 'еть'
        },
        past: {
          I: conjugationRoots.past + 'ел',
          you: conjugationRoots.past + 'ел',
          you_formal: conjugationRoots.past + 'ели',
          he: conjugationRoots.past + 'ел',
          she: conjugationRoots.past + 'ела',
          it: conjugationRoots.past + 'ело',
          we: conjugationRoots.past + 'ели',
          you_plural_formal: conjugationRoots.past + 'ели',
          you_plural: conjugationRoots.past + 'ели',
          they: conjugationRoots.past + 'ели'
        }
      };
    }
  }

  var translations = {
    sun: new Translation('солнце'),
    sing: new VerbTranslation('п', 'петь', {
      now: 'по'
    }),
    do: new Translation('дел', 'делать', {
      now: {
        I: 'делаю',
        you: 'делаешь',
        you_formal: 'делаете',
        he: 'делает',
        she: 'делает',
        it: 'делает',
        we: 'делаем',
        you_plural_formal: 'делаете',
        you_plural: 'делаете',
        they: 'делают'
      },
      future: {
        I: 'буду делать',
        you: 'будешь делать',
        you_formal: 'будете делать',
        he: 'будет делать',
        she: 'будет делать',
        it: 'будет делать',
        we: 'будем делать',
        you_plural_formal: 'будете делать',
        you_plural: 'будете делать',
        they: 'будут делать'
      },
      past: {
        I: 'делал',
        you: 'делал',
        you_formal: 'делали',
        he: 'делал',
        she: 'делала',
        it: 'делало',
        we: 'делали',
        you_plural_formal: 'делали',
        you_plural: 'делали',
        they: 'делали'
      }
    }),
    go: new Translation('идти', 'ид', {
      now: {
        I: 'иду',
        you: 'идешь',
        you_formal: 'идете',
        he: 'идет',
        she: 'идет',
        it: 'идет',
        we: 'идем',
        you_plural_formal: 'идете',
        you_plural: 'идете',
        they: 'идут'
      },
      future: {
        I: 'буду идти',
        you: 'будешь идти',
        you_formal: 'будете идти',
        he: 'будет идти',
        she: 'будет идти',
        it: 'будет идти',
        we: 'будем идти',
        you_plural_formal: 'будете идти',
        you_plural: 'будете идти',
        they: 'будут идти'
      },
      past: {
        I: 'шел',
        you: 'шел',
        you_formal: 'шли',
        he: 'шел',
        she: 'шла',
        it: 'шло',
        we: 'шли',
        you_plural_formal: 'шли',
        you_plural: 'шли',
        they: 'шли'
      }
    }),
    sew: new Translation('шить', 'ш', {
      now: {
        I: 'шью',
        you: 'шьешь',
        you_formal: 'шьете',
        he: 'шьет',
        she: 'шьет',
        it: 'шьет',
        we: 'шьем',
        you_plural_formal: 'шьете',
        you_plural: 'шьете',
        they: 'шьют'
      },
      future: {
        I: 'буду шить',
        you: 'будешь шить',
        you_formal: 'будете шить',
        he: 'будет шить',
        she: 'будет шить',
        it: 'будет шить',
        we: 'будем шить',
        you_plural_formal: 'будете шить',
        you_plural: 'будете шить',
        they: 'будут шить'
      },
      past: {
        I: 'шил',
        you: 'шил',
        you_formal: 'шили',
        he: 'шил',
        she: 'шила',
        it: 'шило',
        we: 'шили',
        you_plural_formal: 'шили',
        you_plural: 'шили',
        they: 'шили'
      }
    }),
    build: new Translation('строить', 'стро', {
      now: {
        I: 'строю',
        you: 'строишь',
        you_formal: 'строите',
        he: 'строит',
        she: 'строит',
        it: 'строит',
        we: 'строим',
        you_plural_formal: 'строите',
        you_plural: 'строите',
        they: 'строят'
      },
      future: {
        I: 'буду строить',
        you: 'будешь строить',
        you_formal: 'будете строить',
        he: 'будет строить',
        she: 'будет строить',
        it: 'будет строить',
        we: 'будем строить',
        you_plural_formal: 'будете строить',
        you_plural: 'будете строить',
        they: 'будут строить'
      },
      past: {
        I: 'строил',
        you: 'строил',
        you_formal: 'строили',
        he: 'строил',
        she: 'строила',
        it: 'строило',
        we: 'строили',
        you_plural_formal: 'строили',
        you_plural: 'строили',
        they: 'строили'
      }
    }),
    give: new Translation('давать', 'да', {
      now: {
        I: 'даю',
        you: 'даешь',
        you_formal: 'даете',
        he: 'дает',
        she: 'дает',
        it: 'дает',
        we: 'даем',
        you_plural_formal: 'даете',
        you_plural: 'даете',
        they: 'дают'
      },
      future: {
        I: 'буду давать',
        you: 'будешь давать',
        you_formal: 'будете давать',
        he: 'будет давать',
        she: 'будет давать',
        it: 'будет давать',
        we: 'будем давать',
        you_plural_formal: 'будете давать',
        you_plural: 'будете давать',
        they: 'будут давать'
      },
      past: {
        I: 'давал',
        you: 'давал',
        you_formal: 'давали',
        he: 'давал',
        she: 'давала',
        it: 'давало',
        we: 'дают',
        you_plural_formal: 'давали',
        you_plural: 'давали',
        they: 'давали'
      }
    }),
    look: new Translation('смотреть', 'смотр', {
      now: {
        I: 'смотрю',
        you: 'смотришь',
        you_formal: 'смотрите',
        he: 'смотрит',
        she: 'смотрит',
        it: 'смотрит',
        we: 'смотрим',
        you_plural_formal: 'смотрите',
        you_plural: 'смотрите',
        they: 'смотрят'
      },
      future: {
        I: 'буду смотреть',
        you: 'будешь смотреть',
        you_formal: 'будете смотреть',
        he: 'будет смотреть',
        she: 'будет смотреть',
        it: 'будет смотреть',
        we: 'будем смотреть',
        you_plural_formal: 'будете смотреть',
        you_plural: 'будете смотреть',
        they: 'будут смотреть'
      },
      past: {
        I: 'смотрел',
        you: 'смотрел',
        you_formal: 'смотрели',
        he: 'смотрел',
        she: 'смотрела',
        it: 'смотрело',
        we: 'смотрим',
        you_plural_formal: 'смотрите',
        you_plural: 'смотрите',
        they: 'смотрели'
      }
    }),
    see: new Translation('видеть', 'вид', {
      now: {
        I: 'вижу',
        you: 'видишь',
        you_formal: 'видите',
        he: 'видит',
        she: 'видит',
        it: 'видит',
        we: 'видим',
        you_plural_formal: 'видете',
        you_plural: 'видете',
        they: 'видят'
      },
      future: {
        I: 'буду видеть',
        you: 'будешь видеть',
        you_formal: 'будете видеть',
        he: 'будет видеть',
        she: 'будет видеть',
        it: 'будет видеть',
        we: 'будем видеть',
        you_plural_formal: 'будете видеть',
        you_plural: 'будете видеть',
        they: 'будут видеть'
      },
      past: {
        I: 'видел',
        you: 'видел',
        you_formal: 'видели',
        he: 'видел',
        she: 'видела',
        it: 'видело',
        we: 'видим',
        you_plural_formal: 'видите',
        you_plural: 'видите',
        they: 'видели'
      }
    }),
    now: new Translation('сейчас'),
    future: new Translation('будущее'),
    past: new Translation('прошлое'),
    I: new Translation('я'),
    you: new Translation('ты'),
    you_formal: new Translation('вы'),
    he: new Translation('он'),
    she: new Translation('она'),
    it: new Translation('оно'),
    we: new Translation('мы'),
    you_plural: new Translation('вы'),
    you_plural_formal: new Translation('вы'),
    they: new Translation('они')
  };

  class Russian extends Language {

    constructor(translations) {
      super('Russian', translations);
    }
  }

  return new Russian(translations);
});<commit_msg>Re-factoring common conjugation logic into VerbTranslation, now two verbs are supported<commit_after>define('lang.ru', ['lang', 'grammar'], function(Lang, Grammar) {

  var {Translation, Language} = Lang;
  var {Word} = Grammar;

  //TODO: Generic verb translation class
  //TODO: Generic verb translation ending class
  //TODO: Particular ending verb translation class

  class VerbTranslation extends Translation {

    constructor(root, keyVowel, defaultForm, conjugationRoots) {
      super(root, defaultForm);
      this.conjugations = this.conjugate(this.readConjugationRoots(root, keyVowel, conjugationRoots));
    }

    readConjugationRoots(root, keyVowel, conjugationRoots) {
      if (!conjugationRoots) {
        conjugationRoots = {};
      }
      ['now', 'future', 'past'].forEach((time) => {
        if (!conjugationRoots[time]) {
          conjugationRoots[time] = root + keyVowel;
        }
      });
      return conjugationRoots;
    }

    conjugate(conjugationRoots) {
      return {
        now: {
          I: conjugationRoots.now + 'ю',
          you: conjugationRoots.now + 'ешь',
          you_formal: conjugationRoots.now + 'ете',
          he: conjugationRoots.now + 'ет',
          she: conjugationRoots.now + 'ет',
          it: conjugationRoots.now + 'ет',
          we: conjugationRoots.now + 'ем',
          you_plural_formal: conjugationRoots.now + 'ете',
          you_plural: conjugationRoots.now + 'ете',
          they: conjugationRoots.now + 'ют'
        },
        future: {
          I: 'буду ' + conjugationRoots.future + 'ть',
          you: 'будешь ' + conjugationRoots.future + 'ть',
          you_formal: 'будете ' + conjugationRoots.future + 'ть',
          he: 'будет ' + conjugationRoots.future + 'ть',
          she: 'будет ' + conjugationRoots.future + 'ть',
          it: 'будет ' + conjugationRoots.future + 'ть',
          we: 'будем ' + conjugationRoots.future + 'ть',
          you_plural_formal: 'будете ' + conjugationRoots.future + 'ть',
          you_plural: 'будете ' + conjugationRoots.future + 'ть',
          they: 'будут ' + conjugationRoots.future + 'ть'
        },
        past: {
          I: conjugationRoots.past + 'л',
          you: conjugationRoots.past + 'л',
          you_formal: conjugationRoots.past + 'ли',
          he: conjugationRoots.past + 'л',
          she: conjugationRoots.past + 'ла',
          it: conjugationRoots.past + 'ло',
          we: conjugationRoots.past + 'ли',
          you_plural_formal: conjugationRoots.past + 'ли',
          you_plural: conjugationRoots.past + 'ли',
          they: conjugationRoots.past + 'ли'
        }
      };
    }
  }

  var translations = {
    sun: new Translation('солнце'),
    sing: new VerbTranslation('п', 'е', 'петь', {
      now: 'по'
    }),
    do: new VerbTranslation('дел', 'а', 'делать'),
    go: new Translation('идти', 'ид', {
      now: {
        I: 'иду',
        you: 'идешь',
        you_formal: 'идете',
        he: 'идет',
        she: 'идет',
        it: 'идет',
        we: 'идем',
        you_plural_formal: 'идете',
        you_plural: 'идете',
        they: 'идут'
      },
      future: {
        I: 'буду идти',
        you: 'будешь идти',
        you_formal: 'будете идти',
        he: 'будет идти',
        she: 'будет идти',
        it: 'будет идти',
        we: 'будем идти',
        you_plural_formal: 'будете идти',
        you_plural: 'будете идти',
        they: 'будут идти'
      },
      past: {
        I: 'шел',
        you: 'шел',
        you_formal: 'шли',
        he: 'шел',
        she: 'шла',
        it: 'шло',
        we: 'шли',
        you_plural_formal: 'шли',
        you_plural: 'шли',
        they: 'шли'
      }
    }),
    sew: new Translation('шить', 'ш', {
      now: {
        I: 'шью',
        you: 'шьешь',
        you_formal: 'шьете',
        he: 'шьет',
        she: 'шьет',
        it: 'шьет',
        we: 'шьем',
        you_plural_formal: 'шьете',
        you_plural: 'шьете',
        they: 'шьют'
      },
      future: {
        I: 'буду шить',
        you: 'будешь шить',
        you_formal: 'будете шить',
        he: 'будет шить',
        she: 'будет шить',
        it: 'будет шить',
        we: 'будем шить',
        you_plural_formal: 'будете шить',
        you_plural: 'будете шить',
        they: 'будут шить'
      },
      past: {
        I: 'шил',
        you: 'шил',
        you_formal: 'шили',
        he: 'шил',
        she: 'шила',
        it: 'шило',
        we: 'шили',
        you_plural_formal: 'шили',
        you_plural: 'шили',
        they: 'шили'
      }
    }),
    build: new Translation('строить', 'стро', {
      now: {
        I: 'строю',
        you: 'строишь',
        you_formal: 'строите',
        he: 'строит',
        she: 'строит',
        it: 'строит',
        we: 'строим',
        you_plural_formal: 'строите',
        you_plural: 'строите',
        they: 'строят'
      },
      future: {
        I: 'буду строить',
        you: 'будешь строить',
        you_formal: 'будете строить',
        he: 'будет строить',
        she: 'будет строить',
        it: 'будет строить',
        we: 'будем строить',
        you_plural_formal: 'будете строить',
        you_plural: 'будете строить',
        they: 'будут строить'
      },
      past: {
        I: 'строил',
        you: 'строил',
        you_formal: 'строили',
        he: 'строил',
        she: 'строила',
        it: 'строило',
        we: 'строили',
        you_plural_formal: 'строили',
        you_plural: 'строили',
        they: 'строили'
      }
    }),
    give: new Translation('давать', 'да', {
      now: {
        I: 'даю',
        you: 'даешь',
        you_formal: 'даете',
        he: 'дает',
        she: 'дает',
        it: 'дает',
        we: 'даем',
        you_plural_formal: 'даете',
        you_plural: 'даете',
        they: 'дают'
      },
      future: {
        I: 'буду давать',
        you: 'будешь давать',
        you_formal: 'будете давать',
        he: 'будет давать',
        she: 'будет давать',
        it: 'будет давать',
        we: 'будем давать',
        you_plural_formal: 'будете давать',
        you_plural: 'будете давать',
        they: 'будут давать'
      },
      past: {
        I: 'давал',
        you: 'давал',
        you_formal: 'давали',
        he: 'давал',
        she: 'давала',
        it: 'давало',
        we: 'дают',
        you_plural_formal: 'давали',
        you_plural: 'давали',
        they: 'давали'
      }
    }),
    look: new Translation('смотреть', 'смотр', {
      now: {
        I: 'смотрю',
        you: 'смотришь',
        you_formal: 'смотрите',
        he: 'смотрит',
        she: 'смотрит',
        it: 'смотрит',
        we: 'смотрим',
        you_plural_formal: 'смотрите',
        you_plural: 'смотрите',
        they: 'смотрят'
      },
      future: {
        I: 'буду смотреть',
        you: 'будешь смотреть',
        you_formal: 'будете смотреть',
        he: 'будет смотреть',
        she: 'будет смотреть',
        it: 'будет смотреть',
        we: 'будем смотреть',
        you_plural_formal: 'будете смотреть',
        you_plural: 'будете смотреть',
        they: 'будут смотреть'
      },
      past: {
        I: 'смотрел',
        you: 'смотрел',
        you_formal: 'смотрели',
        he: 'смотрел',
        she: 'смотрела',
        it: 'смотрело',
        we: 'смотрим',
        you_plural_formal: 'смотрите',
        you_plural: 'смотрите',
        they: 'смотрели'
      }
    }),
    see: new Translation('видеть', 'вид', {
      now: {
        I: 'вижу',
        you: 'видишь',
        you_formal: 'видите',
        he: 'видит',
        she: 'видит',
        it: 'видит',
        we: 'видим',
        you_plural_formal: 'видете',
        you_plural: 'видете',
        they: 'видят'
      },
      future: {
        I: 'буду видеть',
        you: 'будешь видеть',
        you_formal: 'будете видеть',
        he: 'будет видеть',
        she: 'будет видеть',
        it: 'будет видеть',
        we: 'будем видеть',
        you_plural_formal: 'будете видеть',
        you_plural: 'будете видеть',
        they: 'будут видеть'
      },
      past: {
        I: 'видел',
        you: 'видел',
        you_formal: 'видели',
        he: 'видел',
        she: 'видела',
        it: 'видело',
        we: 'видим',
        you_plural_formal: 'видите',
        you_plural: 'видите',
        they: 'видели'
      }
    }),
    now: new Translation('сейчас'),
    future: new Translation('будущее'),
    past: new Translation('прошлое'),
    I: new Translation('я'),
    you: new Translation('ты'),
    you_formal: new Translation('вы'),
    he: new Translation('он'),
    she: new Translation('она'),
    it: new Translation('оно'),
    we: new Translation('мы'),
    you_plural: new Translation('вы'),
    you_plural_formal: new Translation('вы'),
    they: new Translation('они')
  };

  class Russian extends Language {

    constructor(translations) {
      super('Russian', translations);
    }
  }

  return new Russian(translations);
});<|endoftext|>