<commit_before>/**
 * Digital Organisms Script - (DOS) is a simple language for VM.
 * This file contains all available operators implementation. For example:
 * for, if, variable declaration, steps, eating etc... User may override
 * this class for own needs and change operator list to custom.
 *
 * @author flatline
 */
const Helper    = require('./../../../../../../common/src/Helper');
const EVENTS    = require('./../../../../../src/share/Events').EVENTS;
const OConfig   = require('./../Config');
const Operators = require('./../../../../vm/Operators');
const Num       = require('./../../../../vm/Num');

/**
 * {Function} Is created to speed up this function call. constants are run
 * much faster, then Helper.normalize()
 */
const IN_WORLD              = Helper.inWorld;
const IS_FINITE             = Number.isFinite;
const IS_NAN                = Number.isNaN;
/**
 * {Function} Just a shortcuts
 */
const FOUR_BITS             = 4;
const CONDITION_BITS        = 2;
const MAX_VAL               = Number.MAX_VALUE;
/**
 * {Array} Available conditions for if operator. Amount should be
 * the same like (1 << Num.BITS_PER_VAR)
 */
const CONDITIONS = [(a,b)=>a<b, (a,b)=>a>b, (a,b)=>a===b, (a,b)=>a!==b];
/**
 * {Array} Available operators for math calculations
 */
const OPERATORS = [
    (a,b) => {const v=a+b; return IS_FINITE(v)?v:MAX_VAL},
    (a,b) => {const v=a-b; return IS_FINITE(v)?v:-MAX_VAL},
    (a,b) => {const v=a*b; return IS_FINITE(v)?v:MAX_VAL},
    (a,b) => {const v=a/b; return IS_FINITE(v)?v:MAX_VAL},
    (a,b) => {const v=a%b; return IS_NAN(v)?0:v},
    (a,b) => a&b,
    (a,b) => a|b,
    (a,b) => a^b,
    (a,b) => a>>b,
    (a,b) => a<<b,
    (a,b) => a>>>b,
    (a,b) => +(a<b),
    (a,b) => +(a>b),
    (a,b) => +(a===b),
    (a,b) => +(a!==b),
    (a,b) => +(a<=b)
];

class OperatorsDos extends Operators {
    constructor(offs, vars, obs) {
        super(offs, vars, obs);
        /**
         * {Object} These operator handlers should return string, which
         * will be added to the final string script for evaluation.
         */
        this._OPERATORS_CB = [
            this.onVar.bind(this),
            this.onConst.bind(this),
            this.onCondition.bind(this),
            this.onLoop.bind(this),
            this.onOperator.bind(this),
            this.onLookAt.bind(this),
            this.onEatLeft.bind(this),
            this.onEatRight.bind(this),
            this.onEatUp.bind(this),
            this.onEatDown.bind(this),
            this.onStepLeft.bind(this),
            this.onStepRight.bind(this),
            this.onStepUp.bind(this),
            this.onStepDown.bind(this),
            this.onFromMem.bind(this),
            this.onToMem.bind(this),
            this.onMyX.bind(this),
            this.onMyY.bind(this),
            this.onCheckLeft.bind(this),
            this.onCheckRight.bind(this),
            this.onCheckUp.bind(this),
            this.onCheckDown.bind(this)
        ];
        /**
         * {Object} Reusable object to pass it as a parameter to this.fire(..., ret)
         */
        this._ret = {ret: 0, x: 0, y: 0};
        //
        // We have to set amount of available operators for correct
        // working of mutations of operators.
        //
        Num.init(this._OPERATORS_CB.length);

        this._BITS_AFTER_ONE_VAR    = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR;
        this._BITS_AFTER_TWO_VARS   = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR * 2;
        this._BITS_AFTER_THREE_VARS = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR * 3;
    }

    destroy() {
        super.destroy();
        this._OPERATORS_CB = null;
    }

    get operators() {return this._OPERATORS_CB}

    /**
     * Handler of variable assignment operator. 'xx' means, that amount of
     * bits depends on configuration. '...' means, that all other bits are
     * ignored. Example:
     * bits  :        8 xx xx
     * number: 00000000 00 01...
     * desc  :      var  v0 v1
     * string: v0 = v1
     *
     * @param {Number} num One bit packed byte code row
     * @param {Number} line Current line in DOS code
     * @return {Number} Next line number to proceed
     */
    onVar(num, line) {
        this.vars[Num.getVar0(num)] = this.vars[Num.getVar1(num)];
        return ++line;
    }

    /**
     * Handler of numeric constant assignment operator. 'xx' means, that amount of
     * bits depends on configuration. '...' means, that all other bits are
     * ignored. Example:
     * bits  :        8 xx xx
     * number: 00000001 00 01...
     * desc  :    const v0  1
     * string: v0 = 1
     *
     * @param {Number} num One bit packed byte code row
     * @param {Number} line Current line in DOS code
     * @return {Number} Next line number to proceed
     */
    onConst(num, line) {
        this.vars[Num.getVar0(num)] = Num.getBits(num, this._BITS_AFTER_ONE_VAR, OConfig.codeConstBits);
        return ++line;
    }

    /**
     * Handler of 'if' operator. 'xx' means, that amount of bits depends on
     * configuration. '...' means, that all other bits are
     * ignored. Offset of closing bracket means row number after, which this
     * bracket will be added. Offset of closing bracket is calculating using
     * formula: line + offs. Example:
     * bits  :        8 xx xx  2 xx
     * number: 00000010 00 01 00 00...
     * desc  :       if v0 v1  <  }
     * string: if (v0 < v1) {}
     *
     * @param {Number} num One bit packed byte code row
     * @param {Number} line Current line in DOS code
     * @return {Number} Next line number to proceed
     */
    onCondition(num, line) {
        const cond = Num.getBits(num, this._BITS_AFTER_TWO_VARS, CONDITION_BITS);
        const offs = this._getOffs(line, Num.getBits(num, this._BITS_AFTER_TWO_VARS + CONDITION_BITS, OConfig.codeBitsPerBlock));

        if (CONDITIONS[cond](this.vars[Num.getVar0(num)], this.vars[Num.getVar1(num)])) {
            this.offs.push(offs, offs);
            return ++line;
        }

        return offs;
    }

    /**
     * while(v0 op v1) goto offs
     */
    onLoop(num, line) {
        const cond = Num.getBits(num, this._BITS_AFTER_TWO_VARS, CONDITION_BITS);
        const offs = this._getOffs(line, Num.getBits(num, this._BITS_AFTER_TWO_VARS + CONDITION_BITS, OConfig.codeBitsPerBlock));

        if (CONDITIONS[cond](this.vars[Num.getVar0(num)], this.vars[Num.getVar1(num)])) {
            this.offs.push(line, offs);
            return ++line;
        }

        return offs;
    }

    onOperator(num, line) {
        const vars = this.vars;
        vars[Num.getVar0(num)] = OPERATORS[Num.getBits(num, this._BITS_AFTER_THREE_VARS, FOUR_BITS)](vars[Num.getVar1(num)], vars[Num.getVar2(num)]);
        return ++line;
    }

    onLookAt(num, line) {
        const vars = this.vars;
        const x    = (vars[Num.getVar1(num)] + .5) << 0;
        const y    = (vars[Num.getVar2(num)] + .5) << 0;

        if (IN_WORLD(x, y)) {
            this.obs.fire(EVENTS.GET_ENERGY, x, y, this._ret);
            vars[Num.getVar0(num)] = this._ret.ret;
            return ++line;
        }

        vars[Num.getVar0(num)] = 0;
        return ++line;
    }

    onEatLeft(num, line, org)   {this.vars[Num.getVar0(num)] = this._eat(org, num, org.x - 1, org.y); return ++line}
    onEatRight(num, line, org)  {this.vars[Num.getVar0(num)] = this._eat(org, num, org.x + 1, org.y); return ++line}
    onEatUp(num, line, org)     {this.vars[Num.getVar0(num)] = this._eat(org, num, org.x, org.y - 1); return ++line}
    onEatDown(num, line, org)   {this.vars[Num.getVar0(num)] = this._eat(org, num, org.x, org.y + 1); return ++line}

    onStepLeft(num, line, org)  {this.vars[Num.getVar0(num)] = this._step(org, org.x, org.y, org.x - 1, org.y); return ++line}
    onStepRight(num, line, org) {this.vars[Num.getVar0(num)] = this._step(org, org.x, org.y, org.x + 1, org.y); return ++line}
    onStepUp(num, line, org)    {this.vars[Num.getVar0(num)] = this._step(org, org.x, org.y, org.x, org.y - 1); return ++line}
    onStepDown(num, line, org)  {this.vars[Num.getVar0(num)] = this._step(org, org.x, org.y, org.x, org.y + 1); return ++line}

    onFromMem(num, line, org) {
        if (Num.getBits(num, this._BITS_AFTER_TWO_VARS, 1)) {
            const offs = (this.vars[Num.getVar1(num)] + .5) << 0;
            this.vars[Num.getVar0(num)] = org.mem[offs >= org.mem.length || offs < 0 ? 0 : offs];
            return ++line;
        }

        this.vars[Num.getVar0(num)] = org.mem[Num.getBits(num, this._BITS_AFTER_TWO_VARS + 1, OConfig.orgMemBits)];
        return ++line;
    }
    onToMem(num, line, org) {
        if (Num.getBits(num, this._BITS_AFTER_TWO_VARS, 1)) {
            const offs = (this.vars[Num.getVar0(num)] + .5) << 0;
            org.mem[offs >= org.mem.length || offs < 0 ? 0 : offs] = this.vars[Num.getVar1(num)];
            return ++line;
        }

        org.mem[Num.getBits(num, this._BITS_AFTER_TWO_VARS + 1, OConfig.orgMemBits)] = this.vars[Num.getVar0(num)];
        return ++line;
    }

    onMyX(num, line, org) {this.vars[Num.getVar0(num)] = org.x; return ++line}
    onMyY(num, line, org) {this.vars[Num.getVar0(num)] = org.y; return ++line}

    onCheckLeft(num, line, org)  {return this._checkAt(num, line, org.x - 1, org.y)}
    onCheckRight(num, line, org) {return this._checkAt(num, line, org.x + 1, org.y)}
    onCheckUp(num, line, org)    {return this._checkAt(num, line, org.x, org.y - 1)}
    onCheckDown(num, line, org)  {return this._checkAt(num, line, org.x, org.y + 1)}

    _checkAt(num, line, x, y) {
        this.obs.fire(EVENTS.CHECK_AT, x, y, this._ret);
        this.vars[Num.getVar0(num)] = this._ret.ret;
        return ++line;
    }

    _eat(org, num, x, y) {
        const amount = this.vars[Num.getVar1(num)];
        if (amount === 0) {return 0}
        const ret    = this._ret;

        ret.ret = amount;
        this.obs.fire(EVENTS.EAT, org, x, y, ret);
        org.energy += ret.ret;

        return ret.ret;
    }

    _step(org, x1, y1, x2, y2) {
        this.obs.fire(EVENTS.STEP, org, x1, y1, x2, y2);
        return +(org.energy > 0);
    }

    /**
     * Returns offset for closing bracket of block operators like
     * "if", "for" and so on. These operators shouldn't overlap each
     * other. for example:
     *
     *     for (...) {     // 0
     *         if (...) {  // 1
     *             ...     // 2
     *         }           // 3
     *     }               // 4
     *
     * Closing bracket in line 3 shouldn't be after bracket in line 4.
     * So it's possible to set it to one of  1...3. So we change it in
     * real time to fix the overlap problem.
     * @param {Number} line Current line index
     * @param {Number} offs Local offset of closing bracket we want to set
     * @returns {Number}
     */
    _getOffs(line, offs) {
        const offsets = this.offs || [0];
        return line + offs > offsets[offsets.length - 1] ? offsets[offsets.length - 1] : line + offs + 1;
    }
}

module.exports = OperatorsDos;<commit_msg>small refactoring<commit_after>/**
 * Digital Organisms Script - (DOS) is a simple language for VM.
 * This file contains all available operators implementation. For example:
 * for, if, variable declaration, steps, eating etc... User may override
 * this class for own needs and change operator list to custom.
 *
 * @author flatline
 */
const Helper    = require('./../../../../../../common/src/Helper');
const EVENTS    = require('./../../../../../src/share/Events').EVENTS;
const OConfig   = require('./../Config');
const Operators = require('./../../../../vm/Operators');
const Num       = require('./../../../../vm/Num');

/**
 * {Function} Is created to speed up this function call. constants are run
 * much faster, then Helper.normalize()
 */
const IN_WORLD              = Helper.inWorld;
const IS_FINITE             = Number.isFinite;
const IS_NAN                = Number.isNaN;
/**
 * {Function} Just a shortcuts
 */
const FOUR_BITS             = 4;
const CONDITION_BITS        = 2;
const MAX_VAL               = Number.MAX_VALUE;
/**
 * {Array} Available conditions for if operator. Amount should be
 * the same like (1 << Num.BITS_PER_VAR)
 */
const CONDITIONS = [(a,b)=>a<b, (a,b)=>a>b, (a,b)=>a===b, (a,b)=>a!==b];
/**
 * {Array} Available operators for math calculations
 */
const OPERATORS = [
    (a,b) => {const v=a+b; return IS_FINITE(v)?v:MAX_VAL},
    (a,b) => {const v=a-b; return IS_FINITE(v)?v:-MAX_VAL},
    (a,b) => {const v=a*b; return IS_FINITE(v)?v:MAX_VAL},
    (a,b) => {const v=a/b; return IS_FINITE(v)?v:MAX_VAL},
    (a,b) => {const v=a%b; return IS_NAN(v)?0:v},
    (a,b) => a&b,
    (a,b) => a|b,
    (a,b) => a^b,
    (a,b) => a>>b,
    (a,b) => a<<b,
    (a,b) => a>>>b,
    (a,b) => +(a<b),
    (a,b) => +(a>b),
    (a,b) => +(a===b),
    (a,b) => +(a!==b),
    (a,b) => +(a<=b)
];

class OperatorsDos extends Operators {
    constructor(offs, vars, obs) {
        super(offs, vars, obs);
        /**
         * {Object} These operator handlers should return string, which
         * will be added to the final string script for evaluation.
         */
        this._OPERATORS_CB = [
            this.onVar.bind(this),
            this.onConst.bind(this),
            this.onCondition.bind(this),
            this.onLoop.bind(this),
            this.onOperator.bind(this),
            this.onLookAt.bind(this),
            this.onEatLeft.bind(this),
            this.onEatRight.bind(this),
            this.onEatUp.bind(this),
            this.onEatDown.bind(this),
            this.onStepLeft.bind(this),
            this.onStepRight.bind(this),
            this.onStepUp.bind(this),
            this.onStepDown.bind(this),
            this.onFromMem.bind(this),
            this.onToMem.bind(this),
            this.onMyX.bind(this),
            this.onMyY.bind(this),
            this.onCheckLeft.bind(this),
            this.onCheckRight.bind(this),
            this.onCheckUp.bind(this),
            this.onCheckDown.bind(this)
        ];
        /**
         * {Object} Reusable object to pass it as a parameter to this.fire(..., ret)
         */
        this._ret = {ret: 0};
        //
        // We have to set amount of available operators for correct
        // working of mutations of operators.
        //
        Num.init(this._OPERATORS_CB.length);

        this._BITS_AFTER_ONE_VAR    = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR;
        this._BITS_AFTER_TWO_VARS   = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR * 2;
        this._BITS_AFTER_THREE_VARS = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR * 3;
    }

    destroy() {
        super.destroy();
        this._OPERATORS_CB = null;
    }

    get operators() {return this._OPERATORS_CB}

    /**
     * Handler of variable assignment operator. 'xx' means, that amount of
     * bits depends on configuration. '...' means, that all other bits are
     * ignored. Example:
     * bits  :        8 xx xx
     * number: 00000000 00 01...
     * desc  :      var  v0 v1
     * string: v0 = v1
     *
     * @param {Number} num One bit packed byte code row
     * @param {Number} line Current line in DOS code
     * @return {Number} Next line number to proceed
     */
    onVar(num, line) {
        this.vars[Num.getVar0(num)] = this.vars[Num.getVar1(num)];
        return ++line;
    }

    /**
     * Handler of numeric constant assignment operator. 'xx' means, that amount of
     * bits depends on configuration. '...' means, that all other bits are
     * ignored. Example:
     * bits  :        8 xx xx
     * number: 00000001 00 01...
     * desc  :    const v0  1
     * string: v0 = 1
     *
     * @param {Number} num One bit packed byte code row
     * @param {Number} line Current line in DOS code
     * @return {Number} Next line number to proceed
     */
    onConst(num, line) {
        this.vars[Num.getVar0(num)] = Num.getBits(num, this._BITS_AFTER_ONE_VAR, OConfig.codeConstBits);
        return ++line;
    }

    /**
     * Handler of 'if' operator. 'xx' means, that amount of bits depends on
     * configuration. '...' means, that all other bits are
     * ignored. Offset of closing bracket means row number after, which this
     * bracket will be added. Offset of closing bracket is calculating using
     * formula: line + offs. Example:
     * bits  :        8 xx xx  2 xx
     * number: 00000010 00 01 00 00...
     * desc  :       if v0 v1  <  }
     * string: if (v0 < v1) {}
     *
     * @param {Number} num One bit packed byte code row
     * @param {Number} line Current line in DOS code
     * @return {Number} Next line number to proceed
     */
    onCondition(num, line) {
        const cond = Num.getBits(num, this._BITS_AFTER_TWO_VARS, CONDITION_BITS);
        const offs = this._getOffs(line, Num.getBits(num, this._BITS_AFTER_TWO_VARS + CONDITION_BITS, OConfig.codeBitsPerBlock));

        if (CONDITIONS[cond](this.vars[Num.getVar0(num)], this.vars[Num.getVar1(num)])) {
            this.offs.push(offs, offs);
            return ++line;
        }

        return offs;
    }

    /**
     * while(v0 op v1) goto offs
     */
    onLoop(num, line) {
        const cond = Num.getBits(num, this._BITS_AFTER_TWO_VARS, CONDITION_BITS);
        const offs = this._getOffs(line, Num.getBits(num, this._BITS_AFTER_TWO_VARS + CONDITION_BITS, OConfig.codeBitsPerBlock));

        if (CONDITIONS[cond](this.vars[Num.getVar0(num)], this.vars[Num.getVar1(num)])) {
            this.offs.push(line, offs);
            return ++line;
        }

        return offs;
    }

    onOperator(num, line) {
        const vars = this.vars;
        vars[Num.getVar0(num)] = OPERATORS[Num.getBits(num, this._BITS_AFTER_THREE_VARS, FOUR_BITS)](vars[Num.getVar1(num)], vars[Num.getVar2(num)]);
        return ++line;
    }

    onLookAt(num, line) {
        const vars = this.vars;
        const x    = (vars[Num.getVar1(num)] + .5) << 0;
        const y    = (vars[Num.getVar2(num)] + .5) << 0;

        if (IN_WORLD(x, y)) {
            this.obs.fire(EVENTS.GET_ENERGY, x, y, this._ret);
            vars[Num.getVar0(num)] = this._ret.ret;
            return ++line;
        }

        vars[Num.getVar0(num)] = 0;
        return ++line;
    }

    onEatLeft(num, line, org)   {this.vars[Num.getVar0(num)] = this._eat(org, num, org.x - 1, org.y); return ++line}
    onEatRight(num, line, org)  {this.vars[Num.getVar0(num)] = this._eat(org, num, org.x + 1, org.y); return ++line}
    onEatUp(num, line, org)     {this.vars[Num.getVar0(num)] = this._eat(org, num, org.x, org.y - 1); return ++line}
    onEatDown(num, line, org)   {this.vars[Num.getVar0(num)] = this._eat(org, num, org.x, org.y + 1); return ++line}

    onStepLeft(num, line, org)  {this.vars[Num.getVar0(num)] = this._step(org, org.x, org.y, org.x - 1, org.y); return ++line}
    onStepRight(num, line, org) {this.vars[Num.getVar0(num)] = this._step(org, org.x, org.y, org.x + 1, org.y); return ++line}
    onStepUp(num, line, org)    {this.vars[Num.getVar0(num)] = this._step(org, org.x, org.y, org.x, org.y - 1); return ++line}
    onStepDown(num, line, org)  {this.vars[Num.getVar0(num)] = this._step(org, org.x, org.y, org.x, org.y + 1); return ++line}

    onFromMem(num, line, org) {
        if (Num.getBits(num, this._BITS_AFTER_TWO_VARS, 1)) {
            const offs = (this.vars[Num.getVar1(num)] + .5) << 0;
            this.vars[Num.getVar0(num)] = org.mem[offs >= org.mem.length || offs < 0 ? 0 : offs];
            return ++line;
        }

        this.vars[Num.getVar0(num)] = org.mem[Num.getBits(num, this._BITS_AFTER_TWO_VARS + 1, OConfig.orgMemBits)];
        return ++line;
    }
    onToMem(num, line, org) {
        if (Num.getBits(num, this._BITS_AFTER_TWO_VARS, 1)) {
            const offs = (this.vars[Num.getVar0(num)] + .5) << 0;
            org.mem[offs >= org.mem.length || offs < 0 ? 0 : offs] = this.vars[Num.getVar1(num)];
            return ++line;
        }

        org.mem[Num.getBits(num, this._BITS_AFTER_TWO_VARS + 1, OConfig.orgMemBits)] = this.vars[Num.getVar0(num)];
        return ++line;
    }

    onMyX(num, line, org) {this.vars[Num.getVar0(num)] = org.x; return ++line}
    onMyY(num, line, org) {this.vars[Num.getVar0(num)] = org.y; return ++line}

    onCheckLeft(num, line, org)  {return this._checkAt(num, line, org.x - 1, org.y)}
    onCheckRight(num, line, org) {return this._checkAt(num, line, org.x + 1, org.y)}
    onCheckUp(num, line, org)    {return this._checkAt(num, line, org.x, org.y - 1)}
    onCheckDown(num, line, org)  {return this._checkAt(num, line, org.x, org.y + 1)}

    _checkAt(num, line, x, y) {
        this.obs.fire(EVENTS.CHECK_AT, x, y, this._ret);
        this.vars[Num.getVar0(num)] = this._ret.ret;
        return ++line;
    }

    _eat(org, num, x, y) {
        const amount = this.vars[Num.getVar1(num)];
        if (amount === 0) {return 0}
        const ret    = this._ret;

        ret.ret = amount;
        this.obs.fire(EVENTS.EAT, org, x, y, ret);
        org.energy += ret.ret;

        return ret.ret;
    }

    _step(org, x1, y1, x2, y2) {
        this.obs.fire(EVENTS.STEP, org, x1, y1, x2, y2);
        return +(org.energy > 0);
    }

    /**
     * Returns offset for closing bracket of block operators like
     * "if", "for" and so on. These operators shouldn't overlap each
     * other. for example:
     *
     *     for (...) {     // 0
     *         if (...) {  // 1
     *             ...     // 2
     *         }           // 3
     *     }               // 4
     *
     * Closing bracket in line 3 shouldn't be after bracket in line 4.
     * So it's possible to set it to one of  1...3. So we change it in
     * real time to fix the overlap problem.
     * @param {Number} line Current line index
     * @param {Number} offs Local offset of closing bracket we want to set
     * @returns {Number}
     */
    _getOffs(line, offs) {
        const offsets = this.offs || [0];
        return line + offs > offsets[offsets.length - 1] ? offsets[offsets.length - 1] : line + offs + 1;
    }
}

module.exports = OperatorsDos;<|endoftext|>


<commit_before>/*global define*/
define([
       '../Core/Color',
       '../Core/defined',
       '../Core/defineProperties',
       '../ThirdParty/jsep'
    ], function(
        Color,
        defined,
        defineProperties,
        jsep) {
    "use strict";

    /**
     * DOC_TBA
     */
    function Expression(styleEngine, expression) {
        this._styleEngine = styleEngine;

        var ast = jsep(expression);
        console.log(ast);

        this._runtimeAst = createRuntimeAst(this, ast);
        console.log(this._runtimeAst);
    }

    defineProperties(Expression.prototype, {
    });

    Expression.prototype.evaluate = function(feature) {
        return this._runtimeAst.evaluate(feature);
    };

    function Node(value, left, right) {
        this._value = value;
        this._left = left;
        this._right = right;
        this.evaluate = undefined;

        setEvaluateFunction(this);
    }

    function createRuntimeAst(expression, ast) {
        var node;
        var op;
        var val;

        if (ast.type === 'Literal') {
            node = new Node(ast.value);
        } else if (ast.type === 'CallExpression') {
            var call = ast.callee.name;
            var args = ast.arguments;
            if (call === 'color') {
                val = Color.fromCssColorString(args[0].value);
                if (defined(val)) {
                    node = new Node(val);
                }
            } else if (call === 'rgb') {
                val = Color.fromBytes(args[0].value, args[1].value, args[2].value);
                if (defined(val)) {
                    node = new Node(val);
                }
            } else if (call === 'hsl') {
                val = Color.fromHsl(args[0].value, args[1].value, args[2].value);
                if (defined(val)) {
                    node = new Node(val);
                }
            } else if (call === 'rgba') {
                val = Color.fromBytes(args[0].value, args[1].value, args[2].value, args[3].value);
                if (defined(val)) {
                    node = new Node(val);
                }
            } else if (call === 'hsla') {
                val = Color.fromHsl(args[0].value, args[1].value, args[2].value, args[3].value);
                if (defined(val)) {
                    node = new Node(val);
                }
            }
        } else if (ast.type === 'UnaryExpression') {
            op = ast.operator;
            var child = createRuntimeAst(expression, ast.argument);
            if (op === '!' || op === '-') {
                node = new Node(op, child);
            }
        } else if (ast.type === 'BinaryExpression') {
            op = ast.operator;
            var left = createRuntimeAst(expression, ast.left);
            var right = createRuntimeAst(expression, ast.right);
            if (op === '+' || op === '-' || op === '*' ||
                op === '/' || op === '%' || op === '===' ||
                op === '!==') {
                node = new Node(op, left, right);
            }
        }

        return node;
    }

    function setEvaluateFunction(node) {
        if (defined(node._right)) {
            if (node._value === '+') {
                node.evaluate = node._evaluatePlus;
            } else if (node._value === '-') {
                node.evaluate = node._evaluateMinus;
            } else if (node._value === '*') {
                node.evaluate = node._evaluateTimes;
            } else if (node._value === '/') {
                node.evaluate = node._evaluateDivide;
            } else if (node._value === '%') {
                node.evaluate = node._evaluateMod;
            } else if (node._value === '===') {
                node.evaluate = node._evaluateEquals;
            } else if (node._value === '!==') {
                node.evaluate = node._evaluateNotEquals;
            }
        } else if (defined(node._left)) {
            if (node._value === '!') {
                node.evaluate = node._evaluateNot;
            } else if (node._value === '-') {
                node.evaluate = node._evaluateNegative;
            }
        } else {
            node.evaluate = node._evaluateLiteral;
        }
    }

    Node.prototype._evaluateLiteral = function(feature) {
        return this._value;
    };

    Node.prototype._evaluateNot = function(feature) {
        return !(this._left.evaluate(feature));
    };

    Node.prototype._evaluateNegative = function(feature) {
        return -(this._left.evaluate(feature));
    };

    Node.prototype._evaluatePlus = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return Color.add(left, right, new Color());
        }
        return left + right;
    };

    Node.prototype._evaluateMinus = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return Color.subtract(left, right, new Color());
        }
        return left - right;
    };

    Node.prototype._evaluateTimes = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return Color.multiply(left, right, new Color());
        } else if (right instanceof Color && typeof(left) === 'number') {
            return Color.multiplyByScalar(right, left, new Color());
        } else if (left instanceof Color && typeof(right) === 'number') {
            return Color.multiplyByScalar(left, right, new Color());
        }
        return left * right;
    };

    Node.prototype._evaluateDivide = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return Color.divide(left, right, new Color());
        } else if (left instanceof Color && typeof(right) === 'number') {
            return Color.divideByScalar(left, right, new Color());
        }
        return left / right;
    };

    Node.prototype._evaluateMod = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return Color.mod(left, right, new Color());
        }
        return left % right;
    };

    Node.prototype._evaluateEquals = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return Color.equals(left, right);
        }
        return left === right;
    };

    Node.prototype._evaluateNotEquals = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return !Color.equals(left, right);
        }
        return left !== right;
    };

    return Expression;
});
<commit_msg>Added performance idea<commit_after>/*global define*/
define([
       '../Core/Color',
       '../Core/defined',
       '../Core/defineProperties',
       '../ThirdParty/jsep'
    ], function(
        Color,
        defined,
        defineProperties,
        jsep) {
    "use strict";

    /**
     * DOC_TBA
     */
    function Expression(styleEngine, expression) {
        this._styleEngine = styleEngine;

        var ast = jsep(expression);
        console.log(ast);

        this._runtimeAst = createRuntimeAst(this, ast);
        console.log(this._runtimeAst);
    }

    defineProperties(Expression.prototype, {
    });

    Expression.prototype.evaluate = function(feature) {
        return this._runtimeAst.evaluate(feature);
    };

    function Node(value, left, right) {
        this._value = value;
        this._left = left;
        this._right = right;
        this.evaluate = undefined;

        setEvaluateFunction(this);
    }

    function createRuntimeAst(expression, ast) {
        var node;
        var op;
        var val;

        if (ast.type === 'Literal') {
            node = new Node(ast.value);
        } else if (ast.type === 'CallExpression') {
            var call = ast.callee.name;
            var args = ast.arguments;
            if (call === 'color') {
                val = Color.fromCssColorString(args[0].value);
                if (defined(val)) {
                    node = new Node(val);
                }
            } else if (call === 'rgb') {
                val = Color.fromBytes(args[0].value, args[1].value, args[2].value);
                if (defined(val)) {
                    node = new Node(val);
                }
            } else if (call === 'hsl') {
                val = Color.fromHsl(args[0].value, args[1].value, args[2].value);
                if (defined(val)) {
                    node = new Node(val);
                }
            } else if (call === 'rgba') {
                val = Color.fromBytes(args[0].value, args[1].value, args[2].value, args[3].value);
                if (defined(val)) {
                    node = new Node(val);
                }
            } else if (call === 'hsla') {
                val = Color.fromHsl(args[0].value, args[1].value, args[2].value, args[3].value);
                if (defined(val)) {
                    node = new Node(val);
                }
            }
        } else if (ast.type === 'UnaryExpression') {
            op = ast.operator;
            var child = createRuntimeAst(expression, ast.argument);
            if (op === '!' || op === '-') {
                node = new Node(op, child);
            }
        } else if (ast.type === 'BinaryExpression') {
            op = ast.operator;
            var left = createRuntimeAst(expression, ast.left);
            var right = createRuntimeAst(expression, ast.right);
            if (op === '+' || op === '-' || op === '*' ||
                op === '/' || op === '%' || op === '===' ||
                op === '!==') {
                node = new Node(op, left, right);
            }
        }

        return node;
    }

    function setEvaluateFunction(node) {
        if (defined(node._right)) {
            if (node._value === '+') {
                node.evaluate = node._evaluatePlus;
            } else if (node._value === '-') {
                node.evaluate = node._evaluateMinus;
            } else if (node._value === '*') {
                node.evaluate = node._evaluateTimes;
            } else if (node._value === '/') {
                node.evaluate = node._evaluateDivide;
            } else if (node._value === '%') {
                node.evaluate = node._evaluateMod;
            } else if (node._value === '===') {
                node.evaluate = node._evaluateEquals;
            } else if (node._value === '!==') {
                node.evaluate = node._evaluateNotEquals;
            }
        } else if (defined(node._left)) {
            if (node._value === '!') {
                node.evaluate = node._evaluateNot;
            } else if (node._value === '-') {
                node.evaluate = node._evaluateNegative;
            }
        } else {
            node.evaluate = node._evaluateLiteral;
        }
    }

    Node.prototype._evaluateLiteral = function(feature) {
        return this._value;
    };

    Node.prototype._evaluateNot = function(feature) {
        return !(this._left.evaluate(feature));
    };

    Node.prototype._evaluateNegative = function(feature) {
        return -(this._left.evaluate(feature));
    };

    // PERFORMANCE_IDEA: Have "fast path" functions that deal only with specific types
    // that we can assign if we know the types before runtime
    Node.prototype._evaluatePlus = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return Color.add(left, right, new Color());
        }
        return left + right;
    };

    Node.prototype._evaluateMinus = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return Color.subtract(left, right, new Color());
        }
        return left - right;
    };

    Node.prototype._evaluateTimes = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return Color.multiply(left, right, new Color());
        } else if (right instanceof Color && typeof(left) === 'number') {
            return Color.multiplyByScalar(right, left, new Color());
        } else if (left instanceof Color && typeof(right) === 'number') {
            return Color.multiplyByScalar(left, right, new Color());
        }
        return left * right;
    };

    Node.prototype._evaluateDivide = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return Color.divide(left, right, new Color());
        } else if (left instanceof Color && typeof(right) === 'number') {
            return Color.divideByScalar(left, right, new Color());
        }
        return left / right;
    };

    Node.prototype._evaluateMod = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return Color.mod(left, right, new Color());
        }
        return left % right;
    };

    Node.prototype._evaluateEquals = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return Color.equals(left, right);
        }
        return left === right;
    };

    Node.prototype._evaluateNotEquals = function(feature) {
        var left = this._left.evaluate(feature);
        var right = this._right.evaluate(feature);
        if (right instanceof Color && left instanceof Color) {
            return !Color.equals(left, right);
        }
        return left !== right;
    };

    return Expression;
});
<|endoftext|>


<commit_before>Ext.namespace('Vpc.Abstract.List');

Vpc.Abstract.List.MultiFileUploadPanel = Ext.extend(Ext.Panel,
{
    afterRender: function() {
        Vpc.Abstract.List.MultiFileUploadPanel.superclass.afterRender.call(this);

        if (this.list.multiFileUpload.allowOnlyImages) {
            fileTypes = '*.jpg;*.jpeg;*.gif;*.png';
            fileTypesDescription = trlVps('Web Image Files');
        } else {
            fileTypes = '*.*';
            fileTypesDescription = trlVps('All Files');
        }

        //cookie als post mitschicken
        var params = {};
        var cookies = document.cookie.split(';');
        Ext.each(cookies, function(c) {
            c = c.split('=');

            if (c[0].trim() == 'PHPSESSID' && c[1]) {
                params.PHPSESSID = c[1];
            }
        });
        params.maxResolution = this.list.multiFileUpload.maxResolution;
        if (!params.PHPSESSID) return;

        var container = this.body.createChild();

        this.swf = new SWFUpload({
            custom_settings: {list: this.list},
            upload_url: location.protocol+'/'+'/'+location.host+'/vps/media/upload/json-upload',
            flash_url: '/assets/swfupload/Flash/swfupload.swf',
            file_size_limit: this.list.multiFileUpload.fileSizeLimit,
            file_types: fileTypes,
            file_types_description: fileTypesDescription,
            post_params: params,
            button_placeholder_id: container.id,
            button_image_url: "/assets/vps/Vps_js/Form/SwfUploadField/button.jpg",
            button_width: "120",
            button_height: "21",
            button_text: ''+trlVps('Upload Files')+'',
            button_text_style: ".theFont { font-size: 11px; font-family:tahoma,verdana, helvetica;}",
            button_text_left_padding: 28,
            button_text_top_padding: 2,
            button_window_mode: SWFUpload.WINDOW_MODE.OPAQUE,
            button_cursor : SWFUpload.CURSOR.HAND,
            button_action : SWFUpload.BUTTON_ACTION.SELECT_FILES,

            file_queued_handler: function(file) {
                if (!this.vpsFiles) this.vpsFiles = [];
                this.vpsFiles.push(file.id);

                if (this.running) {
                    return;
                }

                this.uploadedIds = [];
                this.running = true;
                this.progress = Ext.MessageBox.show({
                    title : trlVps('Upload'),
                    msg : trlVps('Uploading files'),
                    buttons: false,
                    progress:true,
                    closable:false,
                    minWidth: 250,
                    buttons: Ext.MessageBox.CANCEL,
                    scope: this,
                    fn: function(button) {
                        for(var i=0;i<this.vpsFiles.length;i++) {
                            this.cancelUpload(this.getFile(i).id);
                        }
                        this.vpsFiles = [];
                        this.running = false;
                    }
                });
                this.startUpload(file.id);
            },
            upload_progress_handler: function(file, done, total) {
                var total = 0;
                var sumDone = 0;
                for(var i=0;i<this.vpsFiles.length;i++) {
                    total += this.getFile(i).size;
                    if (this.getFile(i).id == file.id) {
                        sumDone += done;
                    } else if (this.getFile(i).filestatus != SWFUpload.FILE_STATUS.QUEUED) {
                        sumDone += this.getFile(i).size;
                    }
                }
                this.progress.updateProgress(sumDone/total);
            },
            upload_success_handler: function(file, response) {
                try {
                    var r = Ext.util.JSON.decode(response);
                } catch(e) {
                    Vps.handleError('Upload Error');
                    return;
                }
                if (r.success) {

                    this.uploadedIds.push(r.value.uploadId);

                    for(var i=0;i<this.vpsFiles.length;i++) {
                        if (this.getFile(i).filestatus == SWFUpload.FILE_STATUS.QUEUED) {
                            //neeext
                            this.startUpload(this.getFile(i).id);
                            return;
                        }
                    }
                    this.running = false;
                    this.vpsFiles = [];
                    this.progress.hide();

                    var params = Ext.apply(this.customSettings.list.getBaseParams(), { uploadIds: this.uploadedIds.join(',')});
                    Ext.Ajax.request({
                        url: location.protocol+'/'+'/'+location.host+this.customSettings.list.controllerUrl+'/json-multi-upload',
                        params: params,
                        success: function() {
                            this.customSettings.list.grid.reload();
                        },
                        scope: this
                    });
                } else {
                    this.progress.hide();
                    if (r.wrongversion) {
                        Ext.Msg.alert(trlVps('Error - wrong version'),
                        trlVps('Because of an application update the application has to be reloaded.'),
                        function(){
                            location.reload();
                        });
                        return;
                    }
                    if (r.login) {
                        var dlg = new Vps.User.Login.Dialog({
                            message: r.message,
                            success: function() {
                                //redo action...
                                this.startUpload(file.id);
                            },
                            scope: this
                        });
                        Ext.getBody().unmask();
                        dlg.showLogin();
                        return;
                    }
                    if (r.error) {
                        Ext.Msg.alert(trlVps('Error'), r.error);
                    } else {
                        Vps.handleError(r.exception, 'Error', !r.exception);
                    }
                    this.running = false;
                    for(var i=0;i<this.vpsFiles.length;i++) {
                        this.cancelUpload(this.getFile(i).id, false);
                    }
                    this.vpsFiles = [];
                }
            },
            upload_error_handler: function(file, errorCode, errorMessage) {
                this.progress.hide();
                // Upload Errors
                var message = errorMessage;
                if (errorCode == SWFUpload.UPLOAD_ERROR.HTTP_ERROR) {
                    message = trlVps("A http Error occured.");
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL) {
                    message = trlVps("Upload URL string is empty.");
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.IO_ERROR) {
                    message = trlVps("IO Error.");
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.SECURITY_ERROR) {
                    message = trlVps("Security Error.");
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED) {
                    message = trlVps("The upload limit has been reached.");
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED) {
                    message = errorMessage;
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.SPECIFIED_FILE_ID_NOT_FOUND) {
                    message = "File ID not found in the queue.";
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED) {
                    message = "Call to uploadStart return false. Not uploading file.";
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.FILE_CANCELLED) {
                    message = trlVps("File Upload Cancelled.");
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED) {
                    message = trlVps("Upload Stopped");
                }
                if (errorCode != SWFUpload.UPLOAD_ERROR.FILE_CANCELLED) {
                    //Vps.handleError(errorMessage, 'Upload Error');
                    Ext.Msg.alert(trlVps("Upload Error"), message);
                }
            },
            file_queue_error_handler: function(file, errorCode, errorMessage) {
                var message = trlVps("File is zero bytes or cannot be accessed and cannot be uploaded.");
                if (errorCode == SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT) {
                    message = trlVps("File size exceeds allowed limit.");
                } else if (errorCode == SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED) {
                    message = trlVps("File size exceeds allowed limit.");
                } else if (errorCode == SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE) {
                    message = trlVps("File is zero bytes and cannot be uploaded.");
                } else if (errorCode == SWFUpload.QUEUE_ERROR.INVALID_FILETYPE) {
                    message = trlVps("File is not an allowed file type.");
                }
                Ext.Msg.alert(trlVps("Upload Error"), message);
            }
        });
    }
});
Vpc.Abstract.List.Panel = Ext.extend(Vps.Binding.ProxyPanel,
{
    initComponent: function()
    {
        this.childPanel = Ext.ComponentMgr.create(Ext.applyIf(this.childConfig, {
            region: 'center'
        }));

        this.grid = new Vps.Auto.GridPanel({
            controllerUrl: this.controllerUrl,
            split: true,
            region: 'center',
            baseParams: this.baseParams, //Kompatibilität zu ComponentPanel
            autoLoad: this.autoLoad,
            bindings: [{
                item        : this.childPanel,
                componentIdSuffix: '-{0}'
            }],
            onAdd: this.onAdd
        });
        this.proxyItem = this.grid;

        this.grid.on('datachange', function() {
            this.childPanel.reload();
        }, this);

        var westItems = [this.grid];

        if (this.multiFileUpload) {
            this.multiFileUploadPanel = new Vpc.Abstract.List.MultiFileUploadPanel({
                border: false,
                region: 'south',
                height: 50,
                bodyStyle: 'padding-top: 15px; padding-left:80px;',
                list: this
            });
            westItems.push(this.multiFileUploadPanel);
        }

        this.layout = 'border';
        this.items = [{
            layout: 'border',
            region: 'west',
            width: 300,
            items: westItems
        }, this.childPanel];
        Vpc.Abstract.List.Panel.superclass.initComponent.call(this);
    },

    load: function()
    {
        this.grid.load();
        this.grid.selectId(false);

        this.childPanel.setBaseParams({});
        var f = this.childPanel.getForm();
        if (f) {
            f.clearValues();
            f.clearInvalid();
        }
        this.childPanel.disable();
    },

    onAdd : function()
    {
        Ext.Ajax.request({
            mask: true,
            url: this.controllerUrl + '/json-insert',
            params: this.getBaseParams(),
            success: function(response, options, r) {
                this.getSelectionModel().clearSelections();
                this.reload({
                    callback: function(o, r, s) {
                        this.getSelectionModel().selectLastRow();
                    },
                    scope: this
                });
            },
            scope: this
        });
    }
});
Ext.reg('vpc.list', Vpc.Abstract.List.Panel);
<commit_msg>Fix multi file upload mit mehreren durchgaengen<commit_after>Ext.namespace('Vpc.Abstract.List');

Vpc.Abstract.List.MultiFileUploadPanel = Ext.extend(Ext.Panel,
{
    afterRender: function() {
        Vpc.Abstract.List.MultiFileUploadPanel.superclass.afterRender.call(this);

        if (this.list.multiFileUpload.allowOnlyImages) {
            fileTypes = '*.jpg;*.jpeg;*.gif;*.png';
            fileTypesDescription = trlVps('Web Image Files');
        } else {
            fileTypes = '*.*';
            fileTypesDescription = trlVps('All Files');
        }

        //cookie als post mitschicken
        var params = {};
        var cookies = document.cookie.split(';');
        Ext.each(cookies, function(c) {
            c = c.split('=');

            if (c[0].trim() == 'PHPSESSID' && c[1]) {
                params.PHPSESSID = c[1];
            }
        });
        params.maxResolution = this.list.multiFileUpload.maxResolution;
        if (!params.PHPSESSID) return;

        var container = this.body.createChild();

        this.swf = new SWFUpload({
            custom_settings: {list: this.list},
            upload_url: location.protocol+'/'+'/'+location.host+'/vps/media/upload/json-upload',
            flash_url: '/assets/swfupload/Flash/swfupload.swf',
            file_size_limit: this.list.multiFileUpload.fileSizeLimit,
            file_types: fileTypes,
            file_types_description: fileTypesDescription,
            post_params: params,
            button_placeholder_id: container.id,
            button_image_url: "/assets/vps/Vps_js/Form/SwfUploadField/button.jpg",
            button_width: "120",
            button_height: "21",
            button_text: ''+trlVps('Upload Files')+'',
            button_text_style: ".theFont { font-size: 11px; font-family:tahoma,verdana, helvetica;}",
            button_text_left_padding: 28,
            button_text_top_padding: 2,
            button_window_mode: SWFUpload.WINDOW_MODE.OPAQUE,
            button_cursor : SWFUpload.CURSOR.HAND,
            button_action : SWFUpload.BUTTON_ACTION.SELECT_FILES,

            file_queued_handler: function(file) {
                if (!this.vpsFiles) this.vpsFiles = [];
                this.vpsFiles.push(file.id);

                if (this.running) {
                    return;
                }

                this.uploadedIds = [];
                this.running = true;
                this.progress = Ext.MessageBox.show({
                    title : trlVps('Upload'),
                    msg : trlVps('Uploading files'),
                    buttons: false,
                    progress:true,
                    closable:false,
                    minWidth: 250,
                    buttons: Ext.MessageBox.CANCEL,
                    scope: this,
                    fn: function(button) {
                        for(var i=0;i<this.vpsFiles.length;i++) {
                            this.cancelUpload(this.vpsFiles[i]);
                        }
                        this.vpsFiles = [];
                        this.running = false;
                    }
                });
                this.startUpload(file.id);
            },
            upload_progress_handler: function(file, done, total) {
                var total = 0;
                var sumDone = 0;
                for(var i=0;i<this.vpsFiles.length;i++) {
                    var f = this.getFile(this.vpsFiles[i]);
                    total += f.size;
                    if (this.vpsFiles[i] == file.id) {
                        sumDone += done;
                    } else if (f.filestatus != SWFUpload.FILE_STATUS.QUEUED) {
                        sumDone += f.size;
                    }
                }
                this.progress.updateProgress(sumDone/total);
            },
            upload_success_handler: function(file, response) {
                try {
                    var r = Ext.util.JSON.decode(response);
                } catch(e) {
                    Vps.handleError('Upload Error');
                    return;
                }
                if (r.success) {

                    this.uploadedIds.push(r.value.uploadId);

                    for(var i=0;i<this.vpsFiles.length;i++) {
                        var f = this.getFile(this.vpsFiles[i]);
                        if (f && f.filestatus == SWFUpload.FILE_STATUS.QUEUED) {
                            //neeext
                            this.startUpload(f.id);
                            return;
                        }
                    }
                    this.running = false;
                    this.vpsFiles = [];
                    this.progress.hide();

                    var params = Ext.apply(this.customSettings.list.getBaseParams(), { uploadIds: this.uploadedIds.join(',')});
                    Ext.Ajax.request({
                        url: location.protocol+'/'+'/'+location.host+this.customSettings.list.controllerUrl+'/json-multi-upload',
                        params: params,
                        success: function() {
                            this.customSettings.list.grid.reload();
                        },
                        scope: this
                    });
                } else {
                    this.progress.hide();
                    if (r.wrongversion) {
                        Ext.Msg.alert(trlVps('Error - wrong version'),
                        trlVps('Because of an application update the application has to be reloaded.'),
                        function(){
                            location.reload();
                        });
                        return;
                    }
                    if (r.login) {
                        var dlg = new Vps.User.Login.Dialog({
                            message: r.message,
                            success: function() {
                                //redo action...
                                this.startUpload(file.id);
                            },
                            scope: this
                        });
                        Ext.getBody().unmask();
                        dlg.showLogin();
                        return;
                    }
                    if (r.error) {
                        Ext.Msg.alert(trlVps('Error'), r.error);
                    } else {
                        Vps.handleError(r.exception, 'Error', !r.exception);
                    }
                    this.running = false;
                    for(var i=0;i<this.vpsFiles.length;i++) {
                        this.cancelUpload(this.vpsFiles[i], false);
                    }
                    this.vpsFiles = [];
                }
            },
            upload_error_handler: function(file, errorCode, errorMessage) {
                this.progress.hide();
                // Upload Errors
                var message = errorMessage;
                if (errorCode == SWFUpload.UPLOAD_ERROR.HTTP_ERROR) {
                    message = trlVps("A http Error occured.");
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL) {
                    message = trlVps("Upload URL string is empty.");
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.IO_ERROR) {
                    message = trlVps("IO Error.");
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.SECURITY_ERROR) {
                    message = trlVps("Security Error.");
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED) {
                    message = trlVps("The upload limit has been reached.");
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED) {
                    message = errorMessage;
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.SPECIFIED_FILE_ID_NOT_FOUND) {
                    message = "File ID not found in the queue.";
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED) {
                    message = "Call to uploadStart return false. Not uploading file.";
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.FILE_CANCELLED) {
                    message = trlVps("File Upload Cancelled.");
                } else if (errorCode == SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED) {
                    message = trlVps("Upload Stopped");
                }
                if (errorCode != SWFUpload.UPLOAD_ERROR.FILE_CANCELLED) {
                    //Vps.handleError(errorMessage, 'Upload Error');
                    Ext.Msg.alert(trlVps("Upload Error"), message);
                }
            },
            file_queue_error_handler: function(file, errorCode, errorMessage) {
                var message = trlVps("File is zero bytes or cannot be accessed and cannot be uploaded.");
                if (errorCode == SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT) {
                    message = trlVps("File size exceeds allowed limit.");
                } else if (errorCode == SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED) {
                    message = trlVps("File size exceeds allowed limit.");
                } else if (errorCode == SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE) {
                    message = trlVps("File is zero bytes and cannot be uploaded.");
                } else if (errorCode == SWFUpload.QUEUE_ERROR.INVALID_FILETYPE) {
                    message = trlVps("File is not an allowed file type.");
                }
                Ext.Msg.alert(trlVps("Upload Error"), message);
            },
            swfupload_loaded_handler: function() {
                //console.log('loaded');
                if (typeof(this.getMovieElement().CallFunction) == "undefined") {
                    //console.log('GED NED');
                }
            }
        });
    }
});
Vpc.Abstract.List.Panel = Ext.extend(Vps.Binding.ProxyPanel,
{
    initComponent: function()
    {
        this.childPanel = Ext.ComponentMgr.create(Ext.applyIf(this.childConfig, {
            region: 'center'
        }));

        this.grid = new Vps.Auto.GridPanel({
            controllerUrl: this.controllerUrl,
            split: true,
            region: 'center',
            baseParams: this.baseParams, //Kompatibilität zu ComponentPanel
            autoLoad: this.autoLoad,
            bindings: [{
                item        : this.childPanel,
                componentIdSuffix: '-{0}'
            }],
            onAdd: this.onAdd
        });
        this.proxyItem = this.grid;

        this.grid.on('datachange', function() {
            this.childPanel.reload();
        }, this);

        var westItems = [this.grid];

        if (this.multiFileUpload) {
            this.multiFileUploadPanel = new Vpc.Abstract.List.MultiFileUploadPanel({
                border: false,
                region: 'south',
                height: 50,
                bodyStyle: 'padding-top: 15px; padding-left:80px;',
                list: this
            });
            westItems.push(this.multiFileUploadPanel);
        }

        this.layout = 'border';
        this.items = [{
            layout: 'border',
            region: 'west',
            width: 300,
            items: westItems
        }, this.childPanel];
        Vpc.Abstract.List.Panel.superclass.initComponent.call(this);
    },

    load: function()
    {
        this.grid.load();
        this.grid.selectId(false);

        this.childPanel.setBaseParams({});
        var f = this.childPanel.getForm();
        if (f) {
            f.clearValues();
            f.clearInvalid();
        }
        this.childPanel.disable();
    },

    onAdd : function()
    {
        Ext.Ajax.request({
            mask: true,
            url: this.controllerUrl + '/json-insert',
            params: this.getBaseParams(),
            success: function(response, options, r) {
                this.getSelectionModel().clearSelections();
                this.reload({
                    callback: function(o, r, s) {
                        this.getSelectionModel().selectLastRow();
                    },
                    scope: this
                });
            },
            scope: this
        });
    }
});
Ext.reg('vpc.list', Vpc.Abstract.List.Panel);
<|endoftext|>


<commit_before>Ext.namespace('Vps.Auto.Form');

Vps.Auto.Form.Window = Ext.extend(Ext.Window, {
    initComponent : function()
    {
        if (!this.editTitle) {
            this.editTitle = trlVps('Edit');
            if (this.title) {
                this.editTitle = trlVps('Edit {0}', this.title);
            }
        }
        if (!this.addTitle) {
            this.addTitle = trlVps('Add');
            if (this.title) {
                this.addTitle = trlVps('Add {0}', this.title);
            }
        }
        if (!this.autoForm) {
            if (!this.formConfig) this.formConfig = {};

            Ext.applyIf(this.formConfig, {
                baseCls: 'x-plain',
                controllerUrl: this.controllerUrl,
                autoLoad: false,
                checkDirty: false
            });
            this.autoForm = new Vps.Auto.FormPanel(this.formConfig);
        } else if (typeof this.autoForm == 'string') {
            try {
                var d = eval(this.autoForm);
            } catch (e) {
                throw new Error("Invalid autoForm \'"+this.autoForm+"': "+e);
            }
            this.autoForm = new d({ baseCls: 'x-plain' });
        }

        var onRender = function() {
            this.getForm().waitMsgTarget = this.el;
            this.getForm().loadAfterSave = false; //dialog wird geschlossen nach speichern, ist also nicht nötig
        };
        if (!this.autoForm.rendered) {
            this.on('renderform', onRender, this);
        } else {
            onRender();
        }

        var buttons = this.getButtons();
        if (this.initialConfig['close']) {
            buttons = [this.getAction('close')];
        }
        Ext.applyIf(this, {
            width: 400,
            height: 300,
            layout: 'fit',
            bodyStyle:'padding:5px;',
            plain: true,
            modal: true,
            baseCls: 'x-plain',
            buttons: buttons
        });
        this.closeAction = 'hide';

        this.items = [this.autoForm];

        this.relayEvents(this.autoForm, ['renderform', 'datachange', 'beforeloadform', 'loadform', 'addaction']);

        Vps.Auto.Form.Window.superclass.initComponent.call(this);
    },

    getButtons : function ()
    {
        return [this.getAction('save'), this.getAction('cancel')];
    },

    getAction : function(type)
    {
        if (!this.actions) { this.actions = {}; }
        if (this.actions[type]) return this.actions[type];

        if (type == 'save') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Save'),
                handler : function() {
                    this.getAutoForm().submit({
                        success: function() {
                            this.hide();
                        },
                        scope: this
                    });
                },
                scope   : this
            });
        } else if (type == 'cancel') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Cancel'),
                handler : function() {
                    this.hide();
                },
                scope   : this
            });
        } else if (type == 'close') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Close'),
                handler : function() {
                    this.hide();
                },
                scope   : this
            });
        } else {
            throw 'unknown action-type: ' + type;
        }
        return this.actions[type];
    },

    showAdd : function()
    {
        this.setTitle(this.addTitle);
        this.show();
        this.getAutoForm().onAdd();
    },

    showEdit : function(id, record)
    {
        this.setTitle(this.editTitle);
        this.show();
        if (id) {
            this.getAutoForm().load(id);
        }
    },

    getAutoForm : function()
    {
        return this.autoForm;
    },

    getForm : function()
    {
        return this.getAutoForm().getForm();
    },

    findField: function(f)
    {
        return this.getAutoForm().findField(f);
    },

    getBaseParams: function()
    {
        return this.getAutoForm().getBaseParams();
    },
    applyBaseParams: function(p)
    {
        this.getAutoForm().applyBaseParams(p);
    }
});
Ext.reg('vps.autoformwindow', Vps.Auto.Form.Window);
<commit_msg>auch für eigene AutoForm die parameter verwenden<commit_after>Ext.namespace('Vps.Auto.Form');

Vps.Auto.Form.Window = Ext.extend(Ext.Window, {
    initComponent : function()
    {
        if (!this.editTitle) {
            this.editTitle = trlVps('Edit');
            if (this.title) {
                this.editTitle = trlVps('Edit {0}', this.title);
            }
        }
        if (!this.addTitle) {
            this.addTitle = trlVps('Add');
            if (this.title) {
                this.addTitle = trlVps('Add {0}', this.title);
            }
        }
        if (!this.autoForm) {
            if (!this.formConfig) this.formConfig = {};

            Ext.applyIf(this.formConfig, {
                baseCls: 'x-plain',
                controllerUrl: this.controllerUrl,
                autoLoad: false,
                checkDirty: false
            });
            this.autoForm = new Vps.Auto.FormPanel(this.formConfig);
        } else if (typeof this.autoForm == 'string') {
            try {
                var d = eval(this.autoForm);
            } catch (e) {
                throw new Error("Invalid autoForm \'"+this.autoForm+"': "+e);
            }
            this.autoForm = new d({
                baseCls: 'x-plain',
                controllerUrl: this.controllerUrl,
                autoLoad: false,
                checkDirty: false
            });
        }

        var onRender = function() {
            this.getForm().waitMsgTarget = this.el;
            this.getForm().loadAfterSave = false; //dialog wird geschlossen nach speichern, ist also nicht nötig
        };
        if (!this.autoForm.rendered) {
            this.on('renderform', onRender, this);
        } else {
            onRender();
        }

        var buttons = this.getButtons();
        if (this.initialConfig['close']) {
            buttons = [this.getAction('close')];
        }
        Ext.applyIf(this, {
            width: 400,
            height: 300,
            layout: 'fit',
            bodyStyle:'padding:5px;',
            plain: true,
            modal: true,
            baseCls: 'x-plain',
            buttons: buttons
        });
        this.closeAction = 'hide';

        this.items = [this.autoForm];

        this.relayEvents(this.autoForm, ['renderform', 'datachange', 'beforeloadform', 'loadform', 'addaction']);

        Vps.Auto.Form.Window.superclass.initComponent.call(this);
    },

    getButtons : function ()
    {
        return [this.getAction('save'), this.getAction('cancel')];
    },

    getAction : function(type)
    {
        if (!this.actions) { this.actions = {}; }
        if (this.actions[type]) return this.actions[type];

        if (type == 'save') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Save'),
                handler : function() {
                    this.getAutoForm().submit({
                        success: function() {
                            this.hide();
                        },
                        scope: this
                    });
                },
                scope   : this
            });
        } else if (type == 'cancel') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Cancel'),
                handler : function() {
                    this.hide();
                },
                scope   : this
            });
        } else if (type == 'close') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Close'),
                handler : function() {
                    this.hide();
                },
                scope   : this
            });
        } else {
            throw 'unknown action-type: ' + type;
        }
        return this.actions[type];
    },

    showAdd : function()
    {
        this.setTitle(this.addTitle);
        this.show();
        this.getAutoForm().onAdd();
    },

    showEdit : function(id, record)
    {
        this.setTitle(this.editTitle);
        this.show();
        if (id) {
            this.getAutoForm().load(id);
        }
    },

    getAutoForm : function()
    {
        return this.autoForm;
    },

    getForm : function()
    {
        return this.getAutoForm().getForm();
    },

    findField: function(f)
    {
        return this.getAutoForm().findField(f);
    },

    getBaseParams: function()
    {
        return this.getAutoForm().getBaseParams();
    },
    applyBaseParams: function(p)
    {
        this.getAutoForm().applyBaseParams(p);
    }
});
Ext.reg('vps.autoformwindow', Vps.Auto.Form.Window);
<|endoftext|>


<commit_before>/*
 * NTwitBot - util.test.js
 * Copyright (C) 2016-2017 Jordan Sne
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const Util = require('../../lib/util.js');
const utils = new Util();

describe('Util', () => {
    it('should capitalize words', () => {
        expect(utils.capitalize('test')).toBe('Test');
        expect(utils.capitalize('Test')).toBe('Test');
        expect(utils.capitalize('#test')).toBe('#test');
    });

    it('should check capitalization of chars', () => {
        expect(utils.isUpperCase('t')).toBe(false);
        expect(utils.isUpperCase('T')).toBe(true);
    });

    it('should check if an object is in an array', () => {
        const someObject = { 'id': 2322 };
        const otherObject1 = { 'id': 3232 };
        const otherObject2 = { 'id': 4322 };

        const array1 = [someObject, otherObject1];
        const array2 = [otherObject1, otherObject2];

        expect(utils.isInArray(array1, someObject)).toBe(true);
        expect(utils.isInArray(array2, someObject)).toBe(false);
    });

    it('should generate arrays', () => {
        const array = utils.generateShuffledArray(4);

        expect(array).toHaveLength(4);
        expect(array).toContain(0);
        expect(array).toContain(1);
        expect(array).toContain(2);
        expect(array).toContain(3);
    });

    it('should check for ending punctuation', () => {
        expect(utils.endsWithPunc('This is a test.')).toBe(true);
        expect(utils.endsWithPunc('This is a test!')).toBe(true);
        expect(utils.endsWithPunc('This is a test?')).toBe(true);
        expect(utils.endsWithPunc('This is a test')).toBe(false);
    });
});
<commit_msg>Improve tests for generateShuffledArray & shuffleArray<commit_after>/*
 * NTwitBot - util.test.js
 * Copyright (C) 2016-2017 Jordan Sne
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const Util = require('../../lib/util.js');
const utils = new Util();

describe('Util', () => {
    it('should capitalize words', () => {
        expect(utils.capitalize('test')).toBe('Test');
        expect(utils.capitalize('Test')).toBe('Test');
        expect(utils.capitalize('#test')).toBe('#test');
    });

    it('should check capitalization of chars', () => {
        expect(utils.isUpperCase('t')).toBe(false);
        expect(utils.isUpperCase('T')).toBe(true);
    });

    it('should check if an object is in an array', () => {
        const someObject = { 'id': 2322 };
        const otherObject1 = { 'id': 3232 };
        const otherObject2 = { 'id': 4322 };

        const array1 = [someObject, otherObject1];
        const array2 = [otherObject1, otherObject2];

        expect(utils.isInArray(array1, someObject)).toBe(true);
        expect(utils.isInArray(array2, someObject)).toBe(false);
    });

    it('should generate shuffled arrays', () => {
        const shuffleMock = jest.spyOn(utils, 'shuffleArray');

        expect.assertions(2);
        shuffleMock.mockImplementationOnce((array) => {
            expect(array).toEqual([ 0, 1, 2, 3 ]);
            return [2, 0, 1, 3];
        });
        expect(utils.generateShuffledArray(4)).toEqual([ 2, 0, 1, 3 ]);

        shuffleMock.mockRestore();
    });

    it('should not change any elements when shuffling an array', () => {
        const array = utils.shuffleArray([ 1, 3, 5 ]);

        expect(array).toHaveLength(3);
        expect(array).toContain(1);
        expect(array).toContain(3);
        expect(array).toContain(5);
    });

    it('should check for ending punctuation', () => {
        expect(utils.endsWithPunc('This is a test.')).toBe(true);
        expect(utils.endsWithPunc('This is a test!')).toBe(true);
        expect(utils.endsWithPunc('This is a test?')).toBe(true);
        expect(utils.endsWithPunc('This is a test')).toBe(false);
    });
});
<|endoftext|>


<commit_before>/*
 *   处理特殊键的兼容性问题
 */
UE.plugins['keystrokes'] = function() {
    var me = this;

    me.addListener('keydown', function(type, evt) {
        var keyCode = evt.keyCode || evt.which,
            rng = me.selection.getRange();

        //处理全选的情况
        if(!rng.collapsed && !(evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey || keyCode == 9 )){

            var tmpNode = rng.startContainer;
            if(domUtils.isFillChar(tmpNode)){
                rng.setStartBefore(tmpNode)
            }
            tmpNode = rng.endContainer;
            if(domUtils.isFillChar(tmpNode)){
                rng.setEndAfter(tmpNode)
            }
            rng.txtToElmBoundary();
            //结束边界可能放到了br的前边，要把br包含进来
            // x[xxx]

            if(rng.endContainer && rng.endContainer.nodeType == 1){
                tmpNode = rng.endContainer.childNodes[rng.endOffset];
                if(tmpNode && domUtils.isBr(tmpNode)){
                    rng.setEndAfter(tmpNode);
                }
            }
            if(rng.startOffset == 0){
                tmpNode = rng.startContainer;
                if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
                    tmpNode = rng.endContainer;
                    if(rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
                        me.fireEvent('saveScene');
                        me.body.innerHTML = '
'+(browser.ie ? '' : '
')+'
';
                        rng.setStart(me.body.firstChild,0).setCursor(false,true);
                        browser.ie && me._selectionChange();
                        domUtils.preventDefault(evt);
                        return;
                    }
                }
            }
        }

        //处理backspace/del
        if (keyCode == 8) {//|| keyCode == 46
            var start,end;
            //避免按两次删除才能生效的问题
            if(rng.collapsed && rng.inFillChar()){
                start = rng.startContainer;

                if(domUtils.isFillChar(start)){
                    rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
                    domUtils.remove(start)
                }else{
                    start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar ),'');
                    rng.startOffset--;
                    rng.collapse(true).select(true)
                }
            }

            //解决选中control元素不能删除的问题
            if (start = rng.getClosedNode()) {
                me.fireEvent('saveScene');
                rng.setStartBefore(start);
                domUtils.remove(start);
                rng.setCursor();
                me.fireEvent('saveScene');
                domUtils.preventDefault(evt);
                return;
            }
            //阻止在table上的删除
            if (!browser.ie) {
                start = domUtils.findParentByTagName(rng.startContainer, 'table', true);
                end = domUtils.findParentByTagName(rng.endContainer, 'table', true);
                if (start && !end || !start && end || start !== end) {
                    evt.preventDefault();
                    return;
                }
            }

        }
        //处理tab键的逻辑
        if (keyCode == 9) {
            //不处理以下标签
            var excludeTagNameForTabKey = {
                'ol' : 1,
                'ul' : 1,
                'table':1
            };
            //处理组件里的tab按下事件
            if(me.fireEvent('tabkeydown')){
                domUtils.preventDefault(evt);
                return;
            }
            var range = me.selection.getRange();
            me.fireEvent('saveScene');
            for (var i = 0,txt = '',tabSize = me.options.tabSize|| 4,tabNode =  me.options.tabNode || ' '; i < tabSize; i++) {
                txt += tabNode;
            }
            var span = me.document.createElement('span');
            span.innerHTML = txt + domUtils.fillChar;
            if (range.collapsed) {
                range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
            } else {
                //普通的情况
                start = domUtils.findParent(range.startContainer, filterFn);
                end = domUtils.findParent(range.endContainer, filterFn);
                if (start && end && start === end) {
                    range.deleteContents();
                    range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
                } else {
                    var bookmark = range.createBookmark(),
                        filterFn = function(node) {
                            return domUtils.isBlockElm(node) && !excludeTagNameForTabKey[node.tagName.toLowerCase()]

                        };
                    range.enlarge(true);
                    var bookmark2 = range.createBookmark(),
                        current = domUtils.getNextDomNode(bookmark2.start, false, filterFn);
                    while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
                        current.insertBefore(span.cloneNode(true).firstChild, current.firstChild);
                        current = domUtils.getNextDomNode(current, false, filterFn);
                    }
                    range.moveToBookmark(bookmark2).moveToBookmark(bookmark).select();
                }
            }
            domUtils.preventDefault(evt)
        }
        //trace:1634
        //ff的del键在容器空的时候，也会删除
        if(browser.gecko && keyCode == 46){
            range = me.selection.getRange();
            if(range.collapsed){
                start = range.startContainer;
                if(domUtils.isEmptyBlock(start)){
                    var parent = start.parentNode;
                    while(domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)){
                        start = parent;
                        parent = parent.parentNode;
                    }
                    if(start === parent.lastChild)
                        evt.preventDefault();
                    return;
                }
            }
        }
    });
    me.addListener('keyup', function(type, evt) {
        var keyCode = evt.keyCode || evt.which,
            rng;
        if(keyCode == 8){
            rng = me.selection.getRange();
            //处理当删除到body时，要重新给p标签展位
            if(rng.collapsed && domUtils.isBody(rng.startContainer)){
                var tmpNode = domUtils.createElement(me.document,'p',{
                    'innerHTML' : browser.ie ? domUtils.fillChar : '
'
                });
                rng.insertNode(tmpNode).setStart(tmpNode,0).setCursor(false,true);
            }
//            //chrome下如果删除了inline标签，浏览器会有记忆，在输入文字还是会套上刚才删除的标签，所以这里再选一次就不会了
            if(browser.chrome && rng.collapsed && rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer)){
                rng.select(true);
            }
        }
    })
};<commit_msg>by zhanyi<commit_after>/*
 *   处理特殊键的兼容性问题
 */
UE.plugins['keystrokes'] = function() {
    var me = this;

    me.addListener('keydown', function(type, evt) {
        var keyCode = evt.keyCode || evt.which,
            rng = me.selection.getRange();

        //处理全选的情况
        if(!rng.collapsed && !(evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey || keyCode == 9 )){

            var tmpNode = rng.startContainer;
            if(domUtils.isFillChar(tmpNode)){
                rng.setStartBefore(tmpNode)
            }
            tmpNode = rng.endContainer;
            if(domUtils.isFillChar(tmpNode)){
                rng.setEndAfter(tmpNode)
            }
            rng.txtToElmBoundary();
            //结束边界可能放到了br的前边，要把br包含进来
            // x[xxx]

            if(rng.endContainer && rng.endContainer.nodeType == 1){
                tmpNode = rng.endContainer.childNodes[rng.endOffset];
                if(tmpNode && domUtils.isBr(tmpNode)){
                    rng.setEndAfter(tmpNode);
                }
            }
            if(rng.startOffset == 0){
                tmpNode = rng.startContainer;
                if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
                    tmpNode = rng.endContainer;
                    if(rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
                        me.fireEvent('saveScene');
                        me.body.innerHTML = '
'+(browser.ie ? '' : '
')+'
';
                        rng.setStart(me.body.firstChild,0).setCursor(false,true);
                        browser.ie && me._selectionChange();
                        domUtils.preventDefault(evt);
                        return;
                    }
                }
            }
        }

        //处理backspace
        if (keyCode == 8) {
            rng = me.selection.getRange();
            var start,end;
            //避免按两次删除才能生效的问题
            if(rng.collapsed && rng.inFillChar()){
                start = rng.startContainer;

                if(domUtils.isFillChar(start)){
                    rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
                    domUtils.remove(start)
                }else{
                    start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar ),'');
                    rng.startOffset--;
                    rng.collapse(true).select(true)
                }
            }

            //解决选中control元素不能删除的问题
            if (start = rng.getClosedNode()) {
                me.fireEvent('saveScene');
                rng.setStartBefore(start);
                domUtils.remove(start);
                rng.setCursor();
                me.fireEvent('saveScene');
                domUtils.preventDefault(evt);
                return;
            }
            //阻止在table上的删除
            if (!browser.ie) {
                start = domUtils.findParentByTagName(rng.startContainer, 'table', true);
                end = domUtils.findParentByTagName(rng.endContainer, 'table', true);
                if (start && !end || !start && end || start !== end) {
                    evt.preventDefault();
                    return;
                }
            }

        }
        //处理tab键的逻辑
        if (keyCode == 9) {
            //不处理以下标签
            var excludeTagNameForTabKey = {
                'ol' : 1,
                'ul' : 1,
                'table':1
            };
            //处理组件里的tab按下事件
            if(me.fireEvent('tabkeydown')){
                domUtils.preventDefault(evt);
                return;
            }
            var range = me.selection.getRange();
            me.fireEvent('saveScene');
            for (var i = 0,txt = '',tabSize = me.options.tabSize|| 4,tabNode =  me.options.tabNode || ' '; i < tabSize; i++) {
                txt += tabNode;
            }
            var span = me.document.createElement('span');
            span.innerHTML = txt + domUtils.fillChar;
            if (range.collapsed) {
                range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
            } else {
                //普通的情况
                start = domUtils.findParent(range.startContainer, filterFn);
                end = domUtils.findParent(range.endContainer, filterFn);
                if (start && end && start === end) {
                    range.deleteContents();
                    range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
                } else {
                    var bookmark = range.createBookmark(),
                        filterFn = function(node) {
                            return domUtils.isBlockElm(node) && !excludeTagNameForTabKey[node.tagName.toLowerCase()]

                        };
                    range.enlarge(true);
                    var bookmark2 = range.createBookmark(),
                        current = domUtils.getNextDomNode(bookmark2.start, false, filterFn);
                    while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
                        current.insertBefore(span.cloneNode(true).firstChild, current.firstChild);
                        current = domUtils.getNextDomNode(current, false, filterFn);
                    }
                    range.moveToBookmark(bookmark2).moveToBookmark(bookmark).select();
                }
            }
            domUtils.preventDefault(evt)
        }
        //trace:1634
        //ff的del键在容器空的时候，也会删除
        if(browser.gecko && keyCode == 46){
            range = me.selection.getRange();
            if(range.collapsed){
                start = range.startContainer;
                if(domUtils.isEmptyBlock(start)){
                    var parent = start.parentNode;
                    while(domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)){
                        start = parent;
                        parent = parent.parentNode;
                    }
                    if(start === parent.lastChild)
                        evt.preventDefault();
                    return;
                }
            }
        }
    });
    me.addListener('keyup', function(type, evt) {
        var keyCode = evt.keyCode || evt.which,
            rng;
        if(keyCode == 8){
            rng = me.selection.getRange();
            if(rng.collapsed){
                var tmpNode,
                    autoClearTagName = ['h1','h2','h3','h4','h5','h6'];
                if(tmpNode = domUtils.findParentByTagName(rng.startContainer,autoClearTagName,true)){
                    if(domUtils.isEmptyBlock(tmpNode)){
                        var pre = tmpNode.previousSibling;
                        if(pre && pre.nodeName != 'TABLE'){
                            domUtils.remove(tmpNode);
                            rng.setStartAtLast(pre).setCursor(false,true);
                            return;
                        }else{
                            var next = tmpNode.nextSibling;
                            if(next && next.nodeName != 'TABLE'){
                                domUtils.remove(tmpNode);
                                rng.setStartAtFirst(next).setCursor(false,true);
                                return;
                            }
                        }
                    }
                }
                //处理当删除到body时，要重新给p标签展位
                if(domUtils.isBody(rng.startContainer)){
                    var tmpNode = domUtils.createElement(me.document,'p',{
                        'innerHTML' : browser.ie ? domUtils.fillChar : '
'
                    });
                    rng.insertNode(tmpNode).setStart(tmpNode,0).setCursor(false,true);
                }
            }

            //chrome下如果删除了inline标签，浏览器会有记忆，在输入文字还是会套上刚才删除的标签，所以这里再选一次就不会了
            if((rng.startContainer.nodeType == 3 || rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))){
                if(browser.ie){
                    var span = rng.document.createElement('span');
                    rng.insertNode(span).setStartBefore(span).collapse(true);
                    rng.select();
                    domUtils.remove(span)
                }else{
                    rng.select()
                }

            }
        }

    })
};<|endoftext|>


<commit_before>import OsfAdapter from './osf-adapter';

export default OsfAdapter.extend({
    // Override _buildRelationshipURL on ember-osf.  Instead of relationship link, need a PATCH to self link
    _buildRelationshipURL(snapshot) {
        if (snapshot.record.get('links.self')) {
            return snapshot.record.get('links.self');
        }
        return null;
    },
    // Override _doRelatedRequest on ember-osf.  Need to serializer preprint instead of file.
    _doRelatedRequest(store, snapshot, relatedSnapshots, relationship, url) {
        return this.ajax(url, 'PATCH', {
            data: store.serializerFor('preprint').serialize(snapshot),
            isBulk: false
        });
    }

});
<commit_msg>Instead of overriding two private methods on the OsfAdapter, use parent's parent updateRecord method. Preprints do not need special handling.<commit_after>import OsfAdapter from './osf-adapter';

export default OsfAdapter.extend({
    // Overrides updateRecord on OsfAdapter. Is identical to JSONAPIAdapter > Update Record (parent's parent method).
    // Updates to preprints do not need special handling.
    updateRecord(store, type, snapshot) {
        var data = {};
        var serializer = store.serializerFor(type.modelName);

        serializer.serializeIntoHash(data, type, snapshot, { includeId: true });

        var id = snapshot.id;
        var url = this.buildURL(type.modelName, id, snapshot, 'updateRecord');

        return this.ajax(url, 'PATCH', { data: data });
        }
});
<|endoftext|>