<commit_before>'use strict';

var pbc = Py2blockConfig.prototype;
pbc.objectFunctionD.get('is_pressed')['NUMBER'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    py2block_config.pinType = "pins_button";
    var objblock = py2block.convert(func.value);
    py2block_config.pinType = null;
    return block("sensor_button_is_pressed", func.lineno, {}, {
        "btn" : objblock
    }, {
        "inline": "true"
    });
}

pbc.objectFunctionD.get('was_pressed')['NUMBER'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    py2block_config.pinType = "pins_button";
    var objblock = py2block.convert(func.value);
    py2block_config.pinType = null;
    return block("sensor_button_was_pressed", func.lineno, {}, {
        "btn" : objblock
    }, {
        "inline": "true"
    });
}

pbc.objectFunctionD.get('get_presses')['NUMBER'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    py2block_config.pinType = "pins_button";
    var objblock = py2block.convert(func.value);
    py2block_config.pinType = null;
    return block("sensor_button_get_presses", func.lineno, {}, {
        "btn" : objblock
    }, {
        "inline": "true"
    });
}

pbc.moduleFunctionD.get('accelerometer')['get_x'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return block("sensor_get_acceleration", func.lineno, {
            "key" : "x"
        },
        {}, {
            "inline": "true"
        });
}

pbc.moduleFunctionD.get('accelerometer')['get_y'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return block("sensor_get_acceleration", func.lineno, {
            "key" : "y"
        },
        {}, {
            "inline": "true"
        });
}

pbc.moduleFunctionD.get('accelerometer')['get_z'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return block("sensor_get_acceleration", func.lineno, {
            "key" : "z"
        },
        {}, {
            "inline": "true"
        });
}

pbc.moduleFunctionD.get('accelerometer')['get_values'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return block("sensor_get_acceleration", func.lineno, {
            "key" : "values"
        },
        {}, {
            "inline": "true"
        });
}

pbc.moduleFunctionD.get('accelerometer')['get_gestures'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    var objblock = py2block.convert(func.value);
    return block("sensor_get_gestures", func.lineno, {
        'GES':"all"
    }, {
        "btn" : objblock
    }, {
        "inline": "true"
    });
}

pbc.moduleFunctionD.get('accelerometer')['current_gesture'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    var objblock = py2block.convert(func.value);
    return block("sensor_get_gestures", func.lineno, {
        'GES':"current"
    }, {
        "btn" : objblock
    }, {
        "inline": "true"
    });
}

pbc.moduleFunctionD.get('compass')['calibrate'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return [block("sensor_calibrate_compass", func.lineno, {
    }, {
    }, {
        "inline": "true"
    })];
}

pbc.moduleFunctionD.get('compass')['is_calibrated'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return block("sensor_is_compass_calibrated", func.lineno, {}, {
    }, {
        "inline": "true"
    });
}

pbc.moduleFunctionD.get('compass')['get_field_strength'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    var objblock = py2block.convert(func.value);
    return block("sensor_field_strength", func.lineno, {
        'compass':'strength'
    }, {
        "btn" : objblock
    }, {
        "inline": "true"
    });
}

pbc.moduleFunctionD.get('compass')['heading'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    var objblock = py2block.convert(func.value);
    return block("sensor_field_strength", func.lineno, {
        'compass' : 'heading'
    }, {
        "btn" : objblock
    }, {
        "inline": "true"
    });
}

pbc.moduleFunctionD.get('compass')['clear_calibration'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return [block("sensor_compass_reset", func.lineno, {}, {
    }, {
        "inline": "true"
    })];
}

pbc.globalFunctionD['temperature'] = function(py2block, func, args, keywords, starargs, kwargs, node){
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return block("sensor_temperature", func.lineno, {}, {
    }, {
        "inline": "true"
    });
}<commit_msg>update sensor<commit_after>'use strict';

var pbc = Py2blockConfig.prototype;
var ignoreL = ['HCSR04', 'sonar', 'DS1307', 'DS1307_I2C_ADDRESS', 'DS1307_REG_SECOND', 'DS1307_REG_MINUTE',
    'DS1307_REG_HOUR', 'DS1307_REG_WEEKDAY', 'DS1307_REG_DAY', 'DS1307_REG_MONTH', 'DS1307_REG_YEAR',
    'DS1307_REG_CTRL', 'DS1307_REG_RAM'];
for (var i = 0; i < ignoreL.length; i++) {
    pbc.ignoreS.add(ignoreL[i]);
}
pbc.objectFunctionD.get('is_pressed')['NUMBER'] = function (py2block, func, args, keywords, starargs, kwargs, node) {
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    py2block_config.pinType = "pins_button";
    var objblock = py2block.convert(func.value);
    py2block_config.pinType = null;
    return block("sensor_button_is_pressed", func.lineno, {}, {
        "btn": objblock
    }, {
        "inline": "true"
    });
}

pbc.objectFunctionD.get('was_pressed')['NUMBER'] = function (py2block, func, args, keywords, starargs, kwargs, node) {
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    py2block_config.pinType = "pins_button";
    var objblock = py2block.convert(func.value);
    py2block_config.pinType = null;
    return block("sensor_button_was_pressed", func.lineno, {}, {
        "btn": objblock
    }, {
        "inline": "true"
    });
}

pbc.objectFunctionD.get('get_presses')['NUMBER'] = function (py2block, func, args, keywords, starargs, kwargs, node) {
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    py2block_config.pinType = "pins_button";
    var objblock = py2block.convert(func.value);
    py2block_config.pinType = null;
    return block("sensor_button_get_presses", func.lineno, {}, {
        "btn": objblock
    }, {
        "inline": "true"
    });
}

pbc.ifStatementD.get('if_is_gesture')['check_condition'] = function (py2block, node, test, body, orelse) {
    if (test._astname == "Call" && py2block.Name_str(test.func.value) == "accelerometer"
        && py2block.identifier(test.func.attr) == "is_gesture"
        && test.args.length == 1) {
        return true;
    }
    return false;
}

pbc.ifStatementD.get('if_is_gesture')['create_block'] = function (py2block, node, test, body, orelse) {
    return block("controls_attachGestureInterrupt", node.lineno, {
        'gesture': py2block.identifier(test.args[0].s)
    }, {}, {
        "inline": "true"
    }, {}, {
        "DO": py2block.convertBody(body[0])
    });
}

pbc.ifStatementD.get('if_was_gesture')['check_condition'] = function (py2block, node, test, body, orelse) {
    if (test._astname == "Call" && py2block.Name_str(test.func.value) == "accelerometer"
        && py2block.identifier(test.func.attr) == "was_gesture"
        && test.args.length == 1) {
        return true;
    }
    return false;
}

pbc.ifStatementD.get('if_was_gesture')['create_block'] = function (py2block, node, test, body, orelse) {
    return block("controls_attachGestureInterrupt2", node.lineno, {
        'gesture': py2block.identifier(test.args[0].s)
    }, {}, {
        "inline": "true"
    }, {}, {
        "DO": py2block.convertBody(body[0])
    });
}

function acceleromeerGetDir(mode) {
    function converter(py2block, func, args, keywords, starargs, kwargs, node) {
        if (args.length !== 0) {
            throw new Error("Incorrect number of arguments");
        }
        return block("sensor_get_acceleration", func.lineno, {
                "key": mode
            },
            {}, {
                "inline": "true"
            });
    }

    return converter;
}

pbc.moduleFunctionD.get('accelerometer')['get_x'] = acceleromeerGetDir('x');
pbc.moduleFunctionD.get('accelerometer')['get_y'] = acceleromeerGetDir('y');
pbc.moduleFunctionD.get('accelerometer')['get_z'] = acceleromeerGetDir('z');
pbc.moduleFunctionD.get('accelerometer')['get_values'] = acceleromeerGetDir('values');

function accelerometerGetGestrues(mode) {
    function converter(py2block, func, args, keywords, starargs, kwargs, node) {
        if (args.length !== 0) {
            throw new Error("Incorrect number of arguments");
        }
        return block("sensor_get_gestures", func.lineno, {
            'GES': mode
        }, {}, {
            "inline": "true"
        });
    }

    return converter;
}

pbc.moduleFunctionD.get('accelerometer')['get_gestures'] = accelerometerGetGestrues('all');
pbc.moduleFunctionD.get('accelerometer')['current_gesture'] = accelerometerGetGestrues('current');

pbc.moduleFunctionD.get('compass')['calibrate'] = function (py2block, func, args, keywords, starargs, kwargs, node) {
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return [block("sensor_calibrate_compass", func.lineno, {}, {}, {
        "inline": "true"
    })];
}

pbc.moduleFunctionD.get('compass')['is_calibrated'] = function (py2block, func, args, keywords, starargs, kwargs, node) {
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return block("sensor_is_compass_calibrated", func.lineno, {}, {}, {
        "inline": "true"
    });
}

function getStrength(mode) {
    function converter(py2block, func, args, keywords, starargs, kwargs, node) {
        if (args.length !== 0) {
            throw new Error("Incorrect number of arguments");
        }
        return block("sensor_field_strength", func.lineno, {
            'compass': mode
        }, {}, {
            "inline": "true"
        });
    }

    return converter;
}

pbc.moduleFunctionD.get('compass')['get_field_strength'] = getStrength('strength');
pbc.moduleFunctionD.get('compass')['heading'] = getStrength('heading');

pbc.moduleFunctionD.get('compass')['clear_calibration'] = function (py2block, func, args, keywords, starargs, kwargs, node) {
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return [block("sensor_compass_reset", func.lineno, {}, {}, {
        "inline": "true"
    })];
}

pbc.globalFunctionD['temperature'] = function (py2block, func, args, keywords, starargs, kwargs, node) {
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return block("sensor_temperature", func.lineno, {}, {}, {
        "inline": "true"
    });
}

pbc.moduleFunctionD.get('sonar')['distance_cm'] = function (py2block, func, args, keywords, starargs, kwargs, node) {
    if (args.length !== 0) {
        throw new Error("Incorrect number of arguments");
    }
    return block("sensor_distance_hrsc04", func.lineno, {}, {}, {
        "inline": "true"
    });
}

function ds1307GetTime(mode) {
    function converter(py2block, func, args, keywords, starargs, kwargs, node) {
        if (args.length !== 0) {
            throw new Error("Incorrect number of arguments");
        }
        return block("RTC_get_time", func.lineno, {
                'TIME_TYPE': mode
            }, {}, {
                "inline": "true"
            });
    }
    return converter;
}

pbc.moduleFunctionD.get('ds')['Year'] = ds1307GetTime('Year');
pbc.moduleFunctionD.get('ds')['Month'] = ds1307GetTime('Month');
pbc.moduleFunctionD.get('ds')['Day'] = ds1307GetTime('Day');
pbc.moduleFunctionD.get('ds')['Hour'] = ds1307GetTime('Hour');
pbc.moduleFunctionD.get('ds')['Minute'] = ds1307GetTime('Minute');
pbc.moduleFunctionD.get('ds')['Second'] = ds1307GetTime('Second');
pbc.moduleFunctionD.get('ds')['Week'] = ds1307GetTime('Week');
pbc.moduleFunctionD.get('ds')['get_time'] = ds1307GetTime('Mix2');
pbc.moduleFunctionD.get('ds')['get_date'] = ds1307GetTime('Mix1');

pbc.moduleFunctionD.get('ds')['set_date'] = function (py2block, func, args, keywords, starargs, kwargs, node) {
    if (args.length != 3) {
        throw new Error("Incorrect number of arguments");
    }
    var yearblock = py2block.convert(args[0]);
    var monthblock = py2block.convert(args[1]);
    var dayblock = py2block.convert(args[2]);
    return [block("RTC_set_date", func.lineno, {},
        {
            'year': yearblock,
            "month": monthblock,
            "day": dayblock,
        }, {
            "inline": "false"
        })];
}

pbc.moduleFunctionD.get('ds')['set_time'] = function (py2block, func, args, keywords, starargs, kwargs, node) {
    if (args.length != 3) {
        throw new Error("Incorrect number of arguments");
    }
    var hourblock = py2block.convert(args[0]);
    var minuteblock = py2block.convert(args[1]);
    var secondblock = py2block.convert(args[2]);
    return [block("RTC_set_time", func.lineno, {},
        {
            "hour": hourblock,
            "minute": minuteblock,
            "second": secondblock,
        }, {
            "inline": "false"
        })];
}
<|endoftext|>


<commit_before>// Options: --free-variable-checker --require --validate
/*global module require*/

// See https://github.com/erights/quasiParserGenerator/blob/master/test/tinyses/tinyses.md
// for documentation of the TinySES grammar defined here.

module.exports = (function() {
  "use strict";

  const {def} = require('../../src/sesshim.js');
  const {bnf} = require('../../src/bootbnf.js');

  const binary = (left,rights) => rights.reduce((prev,[op,right]) => [op,prev,right], left);

  const qunpack = (h,ms,t) => {
    const result = [h];
    if (ms.length === 1) {
      const [[m,pairs]] = ms;
      result.push(m);
      for (let [q,e] of pairs) {
        result.push(q,e);
      }
    }
    result.push(t);
    return result;
  };

  const {FAIL, Packratter} = require('../../src/scanner.js');
  // Packratter._debug = true;

  const tinyses = bnf`

    # The start production includes scripts, modules, and function
    # bodies. Does it therefore include Node modules? I think so.
    # Distinctions between these three would be post-parsing.
    # TODO: module syntax
    start ::= body EOF                                     ${(b,_) => (..._) => ['script', b]};

    # TODO: Error if whitespace includes newline
    NO_NEWLINE ::= ;

    # TODO: quasiliterals aka template literals
    QUASI_ALL ::= ${() => FAIL};
    QUASI_HEAD ::= ${() => FAIL};
    QUASI_MID ::= ${() => FAIL};
    QUASI_TAIL ::= ${() => FAIL};

    # Omit "async", "arguments", and "eval" from IDENT in TinySES even
    # though ES2017 considers them in IDENT.
    RESERVED_WORD ::=
      KEYWORD / RESERVED_KEYWORD / FUTURE_RESERVED_WORD
    / "null" / "false" / "true"
    / "async" / "arguments" / "eval";

    KEYWORD ::=
      "break"
    / "case" / "catch" / "const" / "continue"
    / "debugger" / "default"
    / "else" / "export"
    / "finally" / "for" / "function"
    / "if" / "import"
    / "return"
    / "switch"
    / "throw" / "try" / "typeof"
    / "void"
    / "while";

    # Unused by TinySES but enumerated here, in order to omit them
    # from the IDENT token.
    # TODO: Should add "new" back into TinySES.
    RESERVED_KEYWORD ::=
      "class"
    / "delete" / "do"
    / "extends"
    / "in" / "instanceof"
    / "new"
    / "super"
    / "this"
    / "var"
    / "with"
    / "yield";

    FUTURE_RESERVED_WORD ::=
      "await" / "enum"
    / "implements" / "package" / "protected"
    / "interface" / "private" / "public";

    dataLiteral ::=  "null" / "false" / "true" / NUMBER / STRING;

    identName ::= IDENT / RESERVED_WORD;
    useVar ::= IDENT                                       ${id => ['use',id]};
    defVar ::= IDENT                                       ${id => ['def',id]}

    # For most identifiers that ES2017 treats as IDENT but recognizes
    # as pseudo-keywords in a context dependent manner, TinySES simply makes
    # keywords. However, this would be too painful for "get" and
    # "set", so instead we use our parser-generator's support syntactic
    # predicates. TODO: Is it really too painful? Try it.
    identGet ::= IDENT                                     ${id => (id === "get" ? id : FAIL)};
    identSet ::= IDENT                                     ${id => (id === "set" ? id : FAIL)};

    # TinySES primaryExpr does not include "this", ClassExpression,
    # GeneratorExpression, or RegularExpressionLiteral.
    primaryExpr ::=
      dataLiteral                                          ${n => ['data',JSON.parse(n)]}
    / "[" arg ** "," "]"                                   ${(_,es,_2) => ['array',es]}
    / "{" prop ** "," "}"                                  ${(_,ps,_2) => ['object',ps]}
    / functionExpr
    / quasiExpr
    / "(" expr ")"                                         ${(_,e,_2) => e}
    / useVar
    / HOLE                                                 ${h => ['exprHole',h]};

    pattern ::=
      dataLiteral                                          ${n => ['matchData',JSON.parse(n)]}
    / "[" param ** "," "]"                                 ${(_,ps,_2) => ['matchArray',ps]}
    / "{" propParam ** "," "}"                             ${(_,ps,_2) => ['matchObj',ps]}
    / defVar
    / HOLE                                                 ${h => ['patternHole',h]};

    arg ::=
      "..." expr                                           ${(_,e) => ['spread',e]}
    / expr;

    param ::=
      "..." pattern                                        ${(_,p) => ['rest',p]}
    / defVar "=" expr                                      ${(v,_,e) => ['optional',v,e]}
    / pattern;

    prop ::=
      "..." expr                                           ${(_,e) => ['spreadObj',e]}
    / propName ":" expr                                    ${(k,_,e) => ['prop',k,e]}
    / methodDef
    / IDENT                                                ${id => ['prop',id,id]};

    propParam ::=
      "..." pattern                                        ${(_,p) => ['restObj',p]}
    / propName ":" pattern                                 ${(k,_,p) => ['matchProp',k,p]}
    / IDENT "=" expr                                       ${(id,_,e) => ['optionalProp',id,id,e]}
    / IDENT                                                ${id => ['matchProp',id,id]};

    # No computed property name
    propName ::=  identName / STRING / NUMBER;

    quasiExpr ::=
      QUASI_ALL                                            ${q => ['quasi',[q]]}
    / QUASI_HEAD (expr (QUASI_MID expr)*)? QUASI_TAIL      ${(h,ms,t) => ['quasi',qunpack(h,ms,t)]};

    later ::= NO_NEWLINE "!";

    # No "new", "super", or MetaProperty. Without "new" we don't need
    # separate MemberExpr and CallExpr productions.
    # Recognize b!foo(x) as distinct from calling b!foo post-parse.
    # TODO: Should add "new" back into TinySES.
    postExpr ::= primaryExpr postOp*                       ${binary};
    postOp ::=
      "." identName                                        ${(_,id) => ['get',id]}
    / "[" indexExpr "]"                                    ${(_,e,_2) => ['index',e]}
    / "(" arg ** "," ")"                                   ${(_,args,_2) => ['call',args]}
    / quasiExpr                                            ${q => ['tag',q]}

    / later identName                                      ${(_,id) => ['getLater',id]}
    / later "[" indexExpr "]"                              ${(_,_2,e,_3) => ['indexLater',e]}
    / later "(" arg ** "," ")"                             ${(_,_2,args,_3) => ['callLater',args]};

    # Omit ("delete" fieldExpr) to avoid mutating properties
    preExpr ::=
      preOp preExpr                                        ${(op,e) => [op,e]}
    / postExpr;

    # No prefix or postfix "++" or "--".
    # No "delete". No bitwise "~".
    preOp ::= "void" / "typeof" / "+" / "-" / "!";

    # Restrict index access to number-names, including
    # floating point, NaN, Infinity, and -Infinity.
    indexExpr ::= "+" preExpr                              ${(op,e) => [op,e]};

    # No bitwise operators, "instanceof", "in", "==", or "!=".  Unlike
    # ES8, none of the relational operators (including equality)
    # associate. To help readers, mixing relational operators always
    # requires explicit parens.
    # TODO: exponentiation "**" operator.
    multExpr ::= preExpr (("*" / "/" / "%") preExpr)*      ${binary};
    addExpr ::= multExpr (("+" / "-") multExpr)*           ${binary};
    relExpr ::= addExpr (relOp addExpr)?                   ${binary};
    relOp ::= "<" / ">" / "<=" / ">=" / "===" / "!==";
    andThenExpr ::= relExpr ("&&" relExpr)*                ${binary};
    orElseExpr ::= andThenExpr ("||" andThenExpr)*         ${binary};

    # No trinary ("?:") expression
    # No comma expression, so assignment expression is expr.
    # TODO: Need to be able to write (1,array[i])(args), which
    # either requires that we readmit the comma expression, or
    # that we add a weird special case to the grammar.
    expr ::=
      lValue assignOp expr                                 ${(lv,op,rv) => [op,lv,rv]}
    / arrow
    / orElseExpr;

    # lValue is only useVar or elementExpr in TinySES.
    # Include only elementExpr from fieldExpr to avoid mutating
    # non-number-named properties.
    # Syntactically disallow ("delete" IDENT).
    # No pseudo-pattern lValues.
    # TODO: re-allow assignment to statically named fields,
    # since it is useful during initialization and prevented
    # thereafter by mandatory tamper-proofing.
    lValue ::= elementExpr / useVar;

    elementExpr ::=
      primaryExpr "[" indexExpr "]"                        ${(pe,_,e,_2) => ['index',pe,e]}
    / primaryExpr later "[" indexExpr "]"                  ${(pe,_,_2,e,_3) => ['indexLater',pe,e]};

    fieldExpr ::=
      primaryExpr "." identName                            ${(pe,_,id) => ['get',pe,id]}
    / primaryExpr later "." identName                      ${(pe,_,_2,id) => ['getLater',pe,id]}
    / elementExpr;

    # No bitwise operators
    assignOp ::= "=" / "*=" / "/=" / "%=" / "+=" / "-=";

    # The expr form must come after the block form, to make proper use
    # of PEG prioritized choice.
    arrow ::=
      params NO_NEWLINE "=>" block                         ${(ps,_,_2,b) => ['arrow',ps,b]}
    / params NO_NEWLINE "=>" expr                          ${(ps,_,_2,e) => ['lambda',ps,e]};
    params ::=
      IDENT                                                ${id => [['def',id]]}
    / "(" param ** "," ")"                                 ${(_,ps,_2) => ps};

    # No "var", empty statement, "with", "do/while", or "for/in". None
    # of the insane variations of "for". Only blocks are accepted for
    # flow-of-control statements.
    # The expr production must go last, so PEG's prioritized choice will
    # interpret {} as a block rather than an expression.
    statement ::=
      block
    / "if" "(" expr ")" block "else" block                 ${(_,_2,c,_3,t,_4,e) => ['if',c,t,e]}
    / "if" "(" expr ")" block                              ${(_,_2,c,_3,t) => ['if',c,t]}
    / "for" "(" declaration expr? ";" expr? ")" block      ${(_,_2,d,c,_3,i,_4,b) => ['for',d,c,i,b]}
    / "for" "(" declOp binding "of" expr ")" block         ${(_,_2,d,_3,e,_4,b) => ['forOf',d,e,b]}
    / "while" "(" expr ")" block                           ${(_,_2,c,_3,b) => ['while',c,b]}
    / "switch" "(" expr ")" "{" branch* "}"                ${(_,_2,e,_3,_4,bs,_5) => ['switch',e,bs]}
    / IDENT ":" statement                                  ${(label,_,stat) => ['label',label,stat]}
    / "try" block catcher finalizer                        ${(_,b,c,f) => ['try',b,c,f]}
    / "try" block finalizer                                ${(_,b,f) => ['try',b,f]}
    / "try" block catcher                                  ${(_,b,c) => ['try',b,c]}
    / terminator
    / "debugger" ";"                                       ${(_,_2) => ['debugger']}
    / expr ";"                                             ${(e,_) => e};

    # Each case branch must end in a terminating statement.
    terminator ::=
      "return" NO_NEWLINE expr ";"                         ${(_,_2,e,_3) => ['return',e]}
    / "return" ";"                                         ${(_,_2) => ['return']}
    / "break" NO_NEWLINE IDENT ";"                         ${(_,_2,label,_3) => ['break',label]}
    / "break" ";"                                          ${(_,_2) => ['break']}
    / "continue" NO_NEWLINE IDENT ";"                      ${(_,_2,label,_3) => ['continue',label]}
    / "continue" ";"                                       ${(_,_2) => ['continue']}
    / "throw" expr ";"                                     ${(_,e,_2) => ['throw',e]};

    # No generator or "class" declaration.
    # No async function yet, but might get added.
    declaration ::=
      declOp binding ** "," ";"                            ${(op,decls,_) => [op, decls]}
    / functionDecl;

    declOp ::= "const" / "let";
    # Initializer is mandatory
    binding ::= pattern "=" expr                           ${(p,_,e) => ['bind', p, e]};

    catcher ::= "catch" "(" pattern ")" block              ${(_,_2,p,_3,b) => ['catch',p,b]};
    finalizer ::= "finally" block                          ${(_,b) => ['finally',b]};

    branch ::= caseLabel+ "{" body terminator "}"          ${(cs,_,b,t,_2) => ['branch',cs,[...b,t]]};
    caseLabel ::=
      "case" expr ":"                                      ${(_,e) => ['case', e]}
    / "default" ":"                                        ${(_,_2) => ['default']};

    block ::= "{" body "}"                                 ${(_,b,_2) => ['block', b]};
    body ::= (statement / declaration)*;

    functionExpr ::=
      "function" defVar? "(" params ")" block          ${(_,n,_2,p,_3,b) => ['functionExpr',n,p,b]};
    functionDecl ::=
      "function" defVar "(" params ")" block           ${(_,n,_2,p,_3,b) => ['functionDecl',n,p,b]};
    methodDef
      propName "(" params ")" block                    ${(n,_,p,_2,b) => ['methodDef',n,p,b]}
    / identGet propName "(" ")" block                  ${(_,n,_2,_3,b) => ['getter',n,[],b]}
    / identSet propName "(" param ")" block            ${(_,n,_2,p,_3,b) => ['setter',n,[p],b]};

  `;

  return def({tinyses});
}());
<commit_msg>Update tinyses.js<commit_after>

// Work on TinySES has moved to https://github.com/Agoric/TinySES

// Options: --free-variable-checker --require --validate
/*global module require*/

// See https://github.com/erights/quasiParserGenerator/blob/master/test/tinyses/tinyses.md
// for documentation of the TinySES grammar defined here.

module.exports = (function() {
  "use strict";

  const {def} = require('../../src/sesshim.js');
  const {bnf} = require('../../src/bootbnf.js');

  const binary = (left,rights) => rights.reduce((prev,[op,right]) => [op,prev,right], left);

  const qunpack = (h,ms,t) => {
    const result = [h];
    if (ms.length === 1) {
      const [[m,pairs]] = ms;
      result.push(m);
      for (let [q,e] of pairs) {
        result.push(q,e);
      }
    }
    result.push(t);
    return result;
  };

  const {FAIL, Packratter} = require('../../src/scanner.js');
  // Packratter._debug = true;

  const tinyses = bnf`

    # The start production includes scripts, modules, and function
    # bodies. Does it therefore include Node modules? I think so.
    # Distinctions between these three would be post-parsing.
    # TODO: module syntax
    start ::= body EOF                                     ${(b,_) => (..._) => ['script', b]};

    # TODO: Error if whitespace includes newline
    NO_NEWLINE ::= ;

    # TODO: quasiliterals aka template literals
    QUASI_ALL ::= ${() => FAIL};
    QUASI_HEAD ::= ${() => FAIL};
    QUASI_MID ::= ${() => FAIL};
    QUASI_TAIL ::= ${() => FAIL};

    # Omit "async", "arguments", and "eval" from IDENT in TinySES even
    # though ES2017 considers them in IDENT.
    RESERVED_WORD ::=
      KEYWORD / RESERVED_KEYWORD / FUTURE_RESERVED_WORD
    / "null" / "false" / "true"
    / "async" / "arguments" / "eval";

    KEYWORD ::=
      "break"
    / "case" / "catch" / "const" / "continue"
    / "debugger" / "default"
    / "else" / "export"
    / "finally" / "for" / "function"
    / "if" / "import"
    / "return"
    / "switch"
    / "throw" / "try" / "typeof"
    / "void"
    / "while";

    # Unused by TinySES but enumerated here, in order to omit them
    # from the IDENT token.
    # TODO: Should add "new" back into TinySES.
    RESERVED_KEYWORD ::=
      "class"
    / "delete" / "do"
    / "extends"
    / "in" / "instanceof"
    / "new"
    / "super"
    / "this"
    / "var"
    / "with"
    / "yield";

    FUTURE_RESERVED_WORD ::=
      "await" / "enum"
    / "implements" / "package" / "protected"
    / "interface" / "private" / "public";

    dataLiteral ::=  "null" / "false" / "true" / NUMBER / STRING;

    identName ::= IDENT / RESERVED_WORD;
    useVar ::= IDENT                                       ${id => ['use',id]};
    defVar ::= IDENT                                       ${id => ['def',id]}

    # For most identifiers that ES2017 treats as IDENT but recognizes
    # as pseudo-keywords in a context dependent manner, TinySES simply makes
    # keywords. However, this would be too painful for "get" and
    # "set", so instead we use our parser-generator's support syntactic
    # predicates. TODO: Is it really too painful? Try it.
    identGet ::= IDENT                                     ${id => (id === "get" ? id : FAIL)};
    identSet ::= IDENT                                     ${id => (id === "set" ? id : FAIL)};

    # TinySES primaryExpr does not include "this", ClassExpression,
    # GeneratorExpression, or RegularExpressionLiteral.
    primaryExpr ::=
      dataLiteral                                          ${n => ['data',JSON.parse(n)]}
    / "[" arg ** "," "]"                                   ${(_,es,_2) => ['array',es]}
    / "{" prop ** "," "}"                                  ${(_,ps,_2) => ['object',ps]}
    / functionExpr
    / quasiExpr
    / "(" expr ")"                                         ${(_,e,_2) => e}
    / useVar
    / HOLE                                                 ${h => ['exprHole',h]};

    pattern ::=
      dataLiteral                                          ${n => ['matchData',JSON.parse(n)]}
    / "[" param ** "," "]"                                 ${(_,ps,_2) => ['matchArray',ps]}
    / "{" propParam ** "," "}"                             ${(_,ps,_2) => ['matchObj',ps]}
    / defVar
    / HOLE                                                 ${h => ['patternHole',h]};

    arg ::=
      "..." expr                                           ${(_,e) => ['spread',e]}
    / expr;

    param ::=
      "..." pattern                                        ${(_,p) => ['rest',p]}
    / defVar "=" expr                                      ${(v,_,e) => ['optional',v,e]}
    / pattern;

    prop ::=
      "..." expr                                           ${(_,e) => ['spreadObj',e]}
    / propName ":" expr                                    ${(k,_,e) => ['prop',k,e]}
    / methodDef
    / IDENT                                                ${id => ['prop',id,id]};

    propParam ::=
      "..." pattern                                        ${(_,p) => ['restObj',p]}
    / propName ":" pattern                                 ${(k,_,p) => ['matchProp',k,p]}
    / IDENT "=" expr                                       ${(id,_,e) => ['optionalProp',id,id,e]}
    / IDENT                                                ${id => ['matchProp',id,id]};

    # No computed property name
    propName ::=  identName / STRING / NUMBER;

    quasiExpr ::=
      QUASI_ALL                                            ${q => ['quasi',[q]]}
    / QUASI_HEAD (expr (QUASI_MID expr)*)? QUASI_TAIL      ${(h,ms,t) => ['quasi',qunpack(h,ms,t)]};

    later ::= NO_NEWLINE "!";

    # No "new", "super", or MetaProperty. Without "new" we don't need
    # separate MemberExpr and CallExpr productions.
    # Recognize b!foo(x) as distinct from calling b!foo post-parse.
    # TODO: Should add "new" back into TinySES.
    postExpr ::= primaryExpr postOp*                       ${binary};
    postOp ::=
      "." identName                                        ${(_,id) => ['get',id]}
    / "[" indexExpr "]"                                    ${(_,e,_2) => ['index',e]}
    / "(" arg ** "," ")"                                   ${(_,args,_2) => ['call',args]}
    / quasiExpr                                            ${q => ['tag',q]}

    / later identName                                      ${(_,id) => ['getLater',id]}
    / later "[" indexExpr "]"                              ${(_,_2,e,_3) => ['indexLater',e]}
    / later "(" arg ** "," ")"                             ${(_,_2,args,_3) => ['callLater',args]};

    # Omit ("delete" fieldExpr) to avoid mutating properties
    preExpr ::=
      preOp preExpr                                        ${(op,e) => [op,e]}
    / postExpr;

    # No prefix or postfix "++" or "--".
    # No "delete". No bitwise "~".
    preOp ::= "void" / "typeof" / "+" / "-" / "!";

    # Restrict index access to number-names, including
    # floating point, NaN, Infinity, and -Infinity.
    indexExpr ::= "+" preExpr                              ${(op,e) => [op,e]};

    # No bitwise operators, "instanceof", "in", "==", or "!=".  Unlike
    # ES8, none of the relational operators (including equality)
    # associate. To help readers, mixing relational operators always
    # requires explicit parens.
    # TODO: exponentiation "**" operator.
    multExpr ::= preExpr (("*" / "/" / "%") preExpr)*      ${binary};
    addExpr ::= multExpr (("+" / "-") multExpr)*           ${binary};
    relExpr ::= addExpr (relOp addExpr)?                   ${binary};
    relOp ::= "<" / ">" / "<=" / ">=" / "===" / "!==";
    andThenExpr ::= relExpr ("&&" relExpr)*                ${binary};
    orElseExpr ::= andThenExpr ("||" andThenExpr)*         ${binary};

    # No trinary ("?:") expression
    # No comma expression, so assignment expression is expr.
    # TODO: Need to be able to write (1,array[i])(args), which
    # either requires that we readmit the comma expression, or
    # that we add a weird special case to the grammar.
    expr ::=
      lValue assignOp expr                                 ${(lv,op,rv) => [op,lv,rv]}
    / arrow
    / orElseExpr;

    # lValue is only useVar or elementExpr in TinySES.
    # Include only elementExpr from fieldExpr to avoid mutating
    # non-number-named properties.
    # Syntactically disallow ("delete" IDENT).
    # No pseudo-pattern lValues.
    # TODO: re-allow assignment to statically named fields,
    # since it is useful during initialization and prevented
    # thereafter by mandatory tamper-proofing.
    lValue ::= elementExpr / useVar;

    elementExpr ::=
      primaryExpr "[" indexExpr "]"                        ${(pe,_,e,_2) => ['index',pe,e]}
    / primaryExpr later "[" indexExpr "]"                  ${(pe,_,_2,e,_3) => ['indexLater',pe,e]};

    fieldExpr ::=
      primaryExpr "." identName                            ${(pe,_,id) => ['get',pe,id]}
    / primaryExpr later "." identName                      ${(pe,_,_2,id) => ['getLater',pe,id]}
    / elementExpr;

    # No bitwise operators
    assignOp ::= "=" / "*=" / "/=" / "%=" / "+=" / "-=";

    # The expr form must come after the block form, to make proper use
    # of PEG prioritized choice.
    arrow ::=
      params NO_NEWLINE "=>" block                         ${(ps,_,_2,b) => ['arrow',ps,b]}
    / params NO_NEWLINE "=>" expr                          ${(ps,_,_2,e) => ['lambda',ps,e]};
    params ::=
      IDENT                                                ${id => [['def',id]]}
    / "(" param ** "," ")"                                 ${(_,ps,_2) => ps};

    # No "var", empty statement, "with", "do/while", or "for/in". None
    # of the insane variations of "for". Only blocks are accepted for
    # flow-of-control statements.
    # The expr production must go last, so PEG's prioritized choice will
    # interpret {} as a block rather than an expression.
    statement ::=
      block
    / "if" "(" expr ")" block "else" block                 ${(_,_2,c,_3,t,_4,e) => ['if',c,t,e]}
    / "if" "(" expr ")" block                              ${(_,_2,c,_3,t) => ['if',c,t]}
    / "for" "(" declaration expr? ";" expr? ")" block      ${(_,_2,d,c,_3,i,_4,b) => ['for',d,c,i,b]}
    / "for" "(" declOp binding "of" expr ")" block         ${(_,_2,d,_3,e,_4,b) => ['forOf',d,e,b]}
    / "while" "(" expr ")" block                           ${(_,_2,c,_3,b) => ['while',c,b]}
    / "switch" "(" expr ")" "{" branch* "}"                ${(_,_2,e,_3,_4,bs,_5) => ['switch',e,bs]}
    / IDENT ":" statement                                  ${(label,_,stat) => ['label',label,stat]}
    / "try" block catcher finalizer                        ${(_,b,c,f) => ['try',b,c,f]}
    / "try" block finalizer                                ${(_,b,f) => ['try',b,f]}
    / "try" block catcher                                  ${(_,b,c) => ['try',b,c]}
    / terminator
    / "debugger" ";"                                       ${(_,_2) => ['debugger']}
    / expr ";"                                             ${(e,_) => e};

    # Each case branch must end in a terminating statement.
    terminator ::=
      "return" NO_NEWLINE expr ";"                         ${(_,_2,e,_3) => ['return',e]}
    / "return" ";"                                         ${(_,_2) => ['return']}
    / "break" NO_NEWLINE IDENT ";"                         ${(_,_2,label,_3) => ['break',label]}
    / "break" ";"                                          ${(_,_2) => ['break']}
    / "continue" NO_NEWLINE IDENT ";"                      ${(_,_2,label,_3) => ['continue',label]}
    / "continue" ";"                                       ${(_,_2) => ['continue']}
    / "throw" expr ";"                                     ${(_,e,_2) => ['throw',e]};

    # No generator or "class" declaration.
    # No async function yet, but might get added.
    declaration ::=
      declOp binding ** "," ";"                            ${(op,decls,_) => [op, decls]}
    / functionDecl;

    declOp ::= "const" / "let";
    # Initializer is mandatory
    binding ::= pattern "=" expr                           ${(p,_,e) => ['bind', p, e]};

    catcher ::= "catch" "(" pattern ")" block              ${(_,_2,p,_3,b) => ['catch',p,b]};
    finalizer ::= "finally" block                          ${(_,b) => ['finally',b]};

    branch ::= caseLabel+ "{" body terminator "}"          ${(cs,_,b,t,_2) => ['branch',cs,[...b,t]]};
    caseLabel ::=
      "case" expr ":"                                      ${(_,e) => ['case', e]}
    / "default" ":"                                        ${(_,_2) => ['default']};

    block ::= "{" body "}"                                 ${(_,b,_2) => ['block', b]};
    body ::= (statement / declaration)*;

    functionExpr ::=
      "function" defVar? "(" params ")" block          ${(_,n,_2,p,_3,b) => ['functionExpr',n,p,b]};
    functionDecl ::=
      "function" defVar "(" params ")" block           ${(_,n,_2,p,_3,b) => ['functionDecl',n,p,b]};
    methodDef
      propName "(" params ")" block                    ${(n,_,p,_2,b) => ['methodDef',n,p,b]}
    / identGet propName "(" ")" block                  ${(_,n,_2,_3,b) => ['getter',n,[],b]}
    / identSet propName "(" param ")" block            ${(_,n,_2,p,_3,b) => ['setter',n,[p],b]};

  `;

  return def({tinyses});
}());
<|endoftext|>


<commit_before>function cleanMess(){
  $('#fixture').empty().html('<canvas id="testCanvas"></canvas>

');
}

function startDrawing(fidel,x,y){
  var down = $.Event('mousedown');

  down.layerX = x;
  down.layerY = y;

  movePoint(fidel,x,y);
  fidel.shadowCanvas.trigger(down);
}

function stopDrawing(fidel){
  var up = $.Event('mouseup');
  fidel.shadowCanvas.trigger(up);
}

function drawLine1(fidel){
  startDrawing(fidel,0,0);
  for (var i = 0; i < 5; i++){
    movePoint(fidel,i,i);
  }
  stopDrawing(fidel);
  return fidel.toJSON();
}

function movePoint(fidel,x,y){
  var move = $.Event('mousemove');

  move.layerX = x;
  move.layerY = y;

  fidel.shadowCanvas.trigger(move);
}

suite('scribble', function() {
  var canvas, div, canvasFidel, scribble;
  var line1Mock = [{"points":[{"x":0,"y":0,"size":2,"color":"#000000","tool":"pencil"},{"x":0,"y":0,"size":2,"color":"#000000","tool":"pencil"},{"x":0,"y":0,"size":2,"color":"#000000","tool":"pencil"},{"x":1,"y":1,"size":2,"color":"#000000","tool":"pencil"},{"x":2,"y":2,"size":2,"color":"#000000","tool":"pencil"},{"x":3,"y":3,"size":2,"color":"#000000","tool":"pencil"},{"x":4,"y":4,"size":2,"color":"#000000","tool":"pencil"}]}];

  setup(function(){
    cleanMess();
    canvas = $('#testCanvas');
    div = $('#testDiv');
    canvas.scribble();
    canvasFidel = canvas.data('scribble');
    div.scribble();
  });
  suite('init',function(){
    test('scribble should exists in jQuery\'s namespace', function(){
      assert.equal(typeof $().scribble, 'function');
    });
    suite('structure creating',function(){
      suite('canvas attributes',function(){
        test('main canvas should have id attribute',function(){
          assert.notEqual(div.find('canvas').first().attr('id'),'');
        });
        test('last canvas should have id attribute',function(){
          assert.notEqual(div.find('canvas').last().attr('id'),'');
        });
        test('main canvas should have width attribute',function(){
          assert.notEqual(div.find('canvas').first().attr('width'),'');
        });
        test('last canvas should have width attribute',function(){
          assert.notEqual(div.find('canvas').last().attr('width'),'');
        });
        test('main canvas should have height attribute',function(){
          assert.notEqual(div.find('canvas').first().attr('height'),'');
        });
        test('last canvas should have height attribute',function(){
          assert.notEqual(div.find('canvas').last().attr('height'),'');
        });
      });
      suite('canvas target',function(){
        test('scribble should create a wrapper div', function(){
          assert.ok(canvas.parent().hasClass(canvasFidel.cssClasses['canvas-holder']));
        });
        test('scribble should create a shadow canvas next to the target one', function(){
          assert.ok(canvas.next().hasClass(canvasFidel.cssClasses['shadow-canvas']));
        });
        test('scribble should assign the correct class to the canvas',function(){
          assert.ok(canvas.hasClass(canvasFidel.cssClasses['main-canvas']));
        });
      });
      suite('div target',function(){
        test('scribble should two canvas inside', function(){
          assert.equal(div.find('canvas').length,2);
        });
        test('scribble should add class to div', function(){
          assert.ok(div.hasClass(canvasFidel.cssClasses['canvas-holder']));
        });
        test('scribble should create a shadow canvas next to the target one', function(){
          assert.equal(div.find('canvas.' +canvasFidel.cssClasses['shadow-canvas']).length,1);
        });
        test('scribble should assign the correct class to the canvas',function(){
          assert.equal(div.find('canvas.' +canvasFidel.cssClasses['main-canvas']).length,1);
        });
      });
    });
  });
  suite('feature',function(){
    setup(function(){
      cleanMess();
      canvas = $('#testCanvas');
      div = $('#testDiv');
      canvas.scribble();
      canvasFidel = canvas.data('scribble');
      div.scribble();
    });
    suite('drawing',function(){
      setup(function(){
        cleanMess();
        canvas = $('#testCanvas');
        scribble = canvas.scribble();
        canvasFidel = canvas.data('scribble');
        drawLine1(canvasFidel);
      });
      test('scribble should draw a line when user tries to',function(){
        assert.deepEqual(canvasFidel.toJSON(),line1Mock);
      });
      test('scribble should undo the drawing when calling undo',function(){
        canvasFidel.undo();
        assert.deepEqual(canvasFidel.toJSON(),[]);
      });
      test('scribble should redo the drawing when calling redo',function(){
        canvasFidel.undo();
        canvasFidel.redo();
        assert.deepEqual(canvasFidel.toJSON(),line1Mock);
      });
      test('scribble should empty the canvas on clear',function(){
        canvasFidel.clear();
        assert.deepEqual(canvasFidel.toJSON(),[]);
      });
      test('scribble should fire an event when line was drawn', function(done){
        scribble.on('drawing.changed',function(){
          done();
        });
      });
    });
    suite('exporting',function(){
      setup(function(){
        cleanMess();
        canvas = $('#testCanvas');
        canvas.scribble();
        canvasFidel = canvas.data('scribble');
        drawLine1(canvasFidel);
      });
      test('scribble should export drawing to JSON',function(){
        assert.deepEqual(canvasFidel.toJSON(),line1Mock);
      });
      test('scribble should export drawing to base64',function(){
        assert.ok(canvasFidel.toDataURL().indexOf('data:image/png;base64') !== -1);
      });
    });
    suite('importing',function(){
      setup(function(){
        cleanMess();
        canvas = $('#testCanvas');
        canvas.scribble();
        canvasFidel = canvas.data('scribble');
      });
      test('scribble should import drawing from JSON',function(){
        canvasFidel.loadJSON(line1Mock);
        assert.deepEqual(canvasFidel.toJSON(),line1Mock);
      });
    });
  });
});
<commit_msg>Updating mocks to reflect new behaviour<commit_after>function cleanMess(){
  $('#fixture').empty().html('<canvas id="testCanvas"></canvas>

');
}

function startDrawing(fidel,x,y){
  var down = $.Event('mousedown');

  down.layerX = x;
  down.layerY = y;

  movePoint(fidel,x,y);
  fidel.shadowCanvas.trigger(down);
}

function stopDrawing(fidel){
  var up = $.Event('mouseup');
  fidel.shadowCanvas.trigger(up);
}

function drawLine1(fidel){
  startDrawing(fidel,0,0);

  for (var i = 1; i < 5; i++){
    movePoint(fidel,i,i);
  }
  stopDrawing(fidel);

  return fidel.toJSON();
}

function movePoint(fidel,x,y){
  var move = $.Event('mousemove');

  move.layerX = x;
  move.layerY = y;

  fidel.shadowCanvas.trigger(move);
}

suite('scribble', function() {
  var canvas, div, canvasFidel, scribble;
  var line1Mock = [{"points":[{"x":0,"y":0,"size":2,"color":"#000000","tool":"pencil"},{"x":1,"y":1,"size":2,"color":"#000000","tool":"pencil"},{"x":2,"y":2,"size":2,"color":"#000000","tool":"pencil"},{"x":3,"y":3,"size":2,"color":"#000000","tool":"pencil"},{"x":4,"y":4,"size":2,"color":"#000000","tool":"pencil"}]}];

  setup(function(){
    cleanMess();
    canvas = $('#testCanvas');
    div = $('#testDiv');
    canvas.scribble();
    canvasFidel = canvas.data('scribble');
    div.scribble();
  });
  suite('init',function(){
    test('scribble should exists in jQuery\'s namespace', function(){
      assert.equal(typeof $().scribble, 'function');
    });
    suite('structure creating',function(){
      suite('canvas attributes',function(){
        test('main canvas should have id attribute',function(){
          assert.notEqual(div.find('canvas').first().attr('id'),'');
        });
        test('last canvas should have id attribute',function(){
          assert.notEqual(div.find('canvas').last().attr('id'),'');
        });
        test('main canvas should have width attribute',function(){
          assert.notEqual(div.find('canvas').first().attr('width'),'');
        });
        test('last canvas should have width attribute',function(){
          assert.notEqual(div.find('canvas').last().attr('width'),'');
        });
        test('main canvas should have height attribute',function(){
          assert.notEqual(div.find('canvas').first().attr('height'),'');
        });
        test('last canvas should have height attribute',function(){
          assert.notEqual(div.find('canvas').last().attr('height'),'');
        });
      });
      suite('canvas target',function(){
        test('scribble should create a wrapper div', function(){
          assert.ok(canvas.parent().hasClass(canvasFidel.cssClasses['canvas-holder']));
        });
        test('scribble should create a shadow canvas next to the target one', function(){
          assert.ok(canvas.next().hasClass(canvasFidel.cssClasses['shadow-canvas']));
        });
        test('scribble should assign the correct class to the canvas',function(){
          assert.ok(canvas.hasClass(canvasFidel.cssClasses['main-canvas']));
        });
      });
      suite('div target',function(){
        test('scribble should two canvas inside', function(){
          assert.equal(div.find('canvas').length,2);
        });
        test('scribble should add class to div', function(){
          assert.ok(div.hasClass(canvasFidel.cssClasses['canvas-holder']));
        });
        test('scribble should create a shadow canvas next to the target one', function(){
          assert.equal(div.find('canvas.' +canvasFidel.cssClasses['shadow-canvas']).length,1);
        });
        test('scribble should assign the correct class to the canvas',function(){
          assert.equal(div.find('canvas.' +canvasFidel.cssClasses['main-canvas']).length,1);
        });
      });
    });
  });
  suite('feature',function(){
    setup(function(){
      cleanMess();
      canvas = $('#testCanvas');
      div = $('#testDiv');
      canvas.scribble();
      canvasFidel = canvas.data('scribble');
      div.scribble();
    });
    suite('drawing',function(){
      setup(function(){
        cleanMess();
        canvas = $('#testCanvas');
        scribble = canvas.scribble();
        canvasFidel = canvas.data('scribble');
        drawLine1(canvasFidel);
      });
      test('scribble should draw a line when user tries to',function(){
        console.log(JSON.stringify(canvasFidel));
        console.log(JSON.stringify(line1Mock));
        assert.deepEqual(canvasFidel.toJSON(),line1Mock);
      });
      test('scribble should undo the drawing when calling undo',function(){
        canvasFidel.undo();
        assert.deepEqual(canvasFidel.toJSON(),[]);
      });
      test('scribble should redo the drawing when calling redo',function(){
        canvasFidel.undo();
        canvasFidel.redo();
        assert.deepEqual(canvasFidel.toJSON(),line1Mock);
      });
      test('scribble should empty the canvas on clear',function(){
        canvasFidel.clear();
        assert.deepEqual(canvasFidel.toJSON(),[]);
      });
      test('scribble should fire an event when line was drawn', function(done){
        scribble.on('drawing.changed',function(){
          done();
        });
      });
    });
    suite('exporting',function(){
      setup(function(){
        cleanMess();
        canvas = $('#testCanvas');
        canvas.scribble();
        canvasFidel = canvas.data('scribble');
        drawLine1(canvasFidel);
      });
      test('scribble should export drawing to JSON',function(){
        assert.deepEqual(canvasFidel.toJSON(),line1Mock);
      });
      test('scribble should export drawing to base64',function(){
        assert.ok(canvasFidel.toDataURL().indexOf('data:image/png;base64') !== -1);
      });
    });
    suite('importing',function(){
      setup(function(){
        cleanMess();
        canvas = $('#testCanvas');
        canvas.scribble();
        canvasFidel = canvas.data('scribble');
      });
      test('scribble should import drawing from JSON',function(){
        canvasFidel.loadJSON(line1Mock);
        assert.deepEqual(canvasFidel.toJSON(),line1Mock);
      });
    });
  });
});
<|endoftext|>


<commit_before><%
function recursiveRoutes(routes, tab, components) {
  let res = ''
  routes.forEach((route, i) => {
    // Customize route
    const cRoute = options.routes[route.name]
    if (cRoute) {
      Object.assign(route, cRoute)
    }

    // Fix path for F7
    route.path = (route.path.replace(/[\*\?]/g, '') + '/').replace('//', '/')

    route._name = '_' + hash(route.component)
    components.push({ _name: route._name, component: route.component, name: route.name, chunkName: route.chunkName })
    res += tab + '{\n'
    res += tab + '\tpath: ' + JSON.stringify(route.path) + ',\n'
    res += tab + '\tcomponent: ' + route._name
    res += (route.children) ? ',\n\t' + tab + 'routes: [\n' + recursiveRoutes(routes[i].children, tab + '\t\t', components) + '\n\t' + tab + ']' : ''
    res += (route.tabs) ? ',\n\t' + tab + 'tabs: ' + JSON.stringify(route.tabs) : ''
    res += '\n' + tab + '}' + (i + 1 === routes.length ? '' : ',\n')
  })
  return res
}
const _components = []

const _routes = recursiveRoutes(router.routes, '\t\t', _components)

%>

<% uniqBy(_components, '_name').forEach(route => { %>import <%= route._name %> from '<%= relativeToBuild(route.component) %>'
<% }) %>

export const routes = [
<%= _routes %>
]
<commit_msg>fix: remove trailing slash of routes<commit_after><%
function recursiveRoutes(routes, tab, components) {
  let res = ''
  routes.forEach((route, i) => {
    // Customize route
    const cRoute = options.routes[route.name]
    if (cRoute) {
      Object.assign(route, cRoute)
    }

    // Fix path for F7
    route.path = route.path.replace(/[\*\?]/g, '')

    route._name = '_' + hash(route.component)
    components.push({ _name: route._name, component: route.component, name: route.name, chunkName: route.chunkName })
    res += tab + '{\n'
    res += tab + '\tpath: ' + JSON.stringify(route.path) + ',\n'
    res += tab + '\tcomponent: ' + route._name
    res += (route.children) ? ',\n\t' + tab + 'routes: [\n' + recursiveRoutes(routes[i].children, tab + '\t\t', components) + '\n\t' + tab + ']' : ''
    res += (route.tabs) ? ',\n\t' + tab + 'tabs: ' + JSON.stringify(route.tabs) : ''
    res += '\n' + tab + '}' + (i + 1 === routes.length ? '' : ',\n')
  })
  return res
}
const _components = []

const _routes = recursiveRoutes(router.routes, '\t\t', _components)

%>

<% uniqBy(_components, '_name').forEach(route => { %>import <%= route._name %> from '<%= relativeToBuild(route.component) %>'
<% }) %>

export const routes = [
<%= _routes %>
]
<|endoftext|>


<commit_before>/* bender-tags: editor,unit */
/* bender-ckeditor-plugins: link,toolbar */

// based off of dialog test for 'link' plugin
// https://github.com/ckeditor/ckeditor-dev/blob/master/tests/plugins/link/dialog.js

( function() {
	'use strict';

	function assertHeadingDiscovery( bot, expIds, expTextContents ) {
		bot.dialog( 'link', function( dialog ) {
			dialog.setValueOf( 'info', 'linkType', 'heading' );

			var headingOptions = dialog.getContentElement( 'info', 'heading' ).getInputElement().$.options,
				textContents = [],
				ids = [];

			for ( var i = headingOptions.length; i--; )
				ids.push( headingOptions[ i ][ 1 ].value );

			for ( i = headingOptions.length; i--; )
				textContents.push( headingOptions[ i ][ 0 ].value );

			assert.areSame( expIds.sort().join( ',' ), ids.sort().join( ',' ), 'Heading IDs discovered properly' );
			assert.areSame( expTextContents.sort().join( ',' ), textContents.sort().join( ',' ), 'Heading names discovered properly' );
		} );
	}

	bender.editors = {
		framed: {
			name: 'framed',
			creator: 'replace',
			startupData: '
' +
				'a' +
				'b' +
				'' +
			'
'
		},
		inline: {
			name: 'inline',
			creator: 'inline',
			startupData: '
' +
				'm' +
				'n' +
				'' +
			'
'
		},
		divarea: {
			name: 'divarea',
			creator: 'replace',
			startupData: '
' +
				't' +
				'u' +
				'' +
			'
',
			config: {
				extraPlugins: 'divarea'
			}
		}
	};

	bender.editorsConfig = {
		extraAllowedContent: 'a[id,name]'
	};

	bender.test( {
		tearDown: function() {
			CKEDITOR.dialog.getCurrent().hide();
		},

		'test discovery of anchors (framed)': function() {
			assertAnchorDiscovery( this.editorBots.framed,
				[ 'aa', 'ba', 'ca', '' ],
				[ 'ab', 'bb', 'cb', '' ] );
		},

		'test discovery of anchors (inline)': function() {
			assertAnchorDiscovery( this.editorBots.inline,
				[ 'ga', 'ha', 'ma', 'na', 'oa', 'ta', 'ua', 'wa', '' ],
				[ 'gb', 'hb', 'mb', 'nb', 'ob', 'tb', 'ub', 'wb', '' ] );
		},

		'test discovery of anchors (divarea)': function() {
			assertAnchorDiscovery( this.editorBots.divarea,
				[ 'ta', 'ua', 'wa', '' ],
				[ 'tb', 'ub', 'wb', '' ] );
		}
	} );
} )();
<commit_msg>Change startup data to use headings<commit_after>/* bender-tags: editor,unit */
/* bender-ckeditor-plugins: link,toolbar */

// based off of dialog test for 'link' plugin
// https://github.com/ckeditor/ckeditor-dev/blob/master/tests/plugins/link/dialog.js

( function() {
	'use strict';

	function assertHeadingDiscovery( bot, expIds, expTextContents ) {
		bot.dialog( 'link', function( dialog ) {
			dialog.setValueOf( 'info', 'linkType', 'heading' );

			var headingOptions = dialog.getContentElement( 'info', 'heading' ).getInputElement().$.options,
				textContents = [],
				ids = [];

			for ( var i = headingOptions.length; i--; )
				ids.push( headingOptions[ i ][ 1 ].value );

			for ( i = headingOptions.length; i--; )
				textContents.push( headingOptions[ i ][ 0 ].value );

			assert.areSame( expIds.sort().join( ',' ), ids.sort().join( ',' ), 'Heading IDs discovered properly' );
			assert.areSame( expTextContents.sort().join( ',' ), textContents.sort().join( ',' ), 'Heading names discovered properly' );
		} );
	}

	bender.editors = {
		framed: {
			name: 'framed',
			creator: 'replace',
			startupData: '
' +
				'
a
' +
				'
b
' +
				'

' +
			'
'
		},
		inline: {
			name: 'inline',
			creator: 'inline',
			startupData: '
' +
				'
m
' +
				'
n
' +
				'

' +
			'
'
		},
		divarea: {
			name: 'divarea',
			creator: 'replace',
			startupData: '
' +
				'
t
' +
				'
u
' +
				'

' +
			'
',
			config: {
				extraPlugins: 'divarea'
			}
		}
	};

	bender.editorsConfig = {
		extraAllowedContent: 'a[id,name]'
	};

	bender.test( {
		tearDown: function() {
			CKEDITOR.dialog.getCurrent().hide();
		},

		'test discovery of anchors (framed)': function() {
			assertAnchorDiscovery( this.editorBots.framed,
				[ 'aa', 'ba', 'ca', '' ],
				[ 'ab', 'bb', 'cb', '' ] );
		},

		'test discovery of anchors (inline)': function() {
			assertAnchorDiscovery( this.editorBots.inline,
				[ 'ga', 'ha', 'ma', 'na', 'oa', 'ta', 'ua', 'wa', '' ],
				[ 'gb', 'hb', 'mb', 'nb', 'ob', 'tb', 'ub', 'wb', '' ] );
		},

		'test discovery of anchors (divarea)': function() {
			assertAnchorDiscovery( this.editorBots.divarea,
				[ 'ta', 'ua', 'wa', '' ],
				[ 'tb', 'ub', 'wb', '' ] );
		}
	} );
} )();
<|endoftext|>


<commit_before>var Util, DOM, CloudFunc, join;

(function(scope, Util, DOM, CloudFunc) {
    'use strict';

    scope.CloudCmd = new CloudCmdProto(Util, DOM, CloudFunc);

    function CloudCmdProto(Util, DOM, CloudFunc) {
        var Key,
            Debug,
            log                     = function(str) {
                if (Debug)
                    console.log(str);
            },

            Listeners,
            Files                   = DOM.Files,
            Images                  = DOM.Images,
            Info                    = DOM.CurrentInfo,
            CloudCmd                = this,
            Storage                 = DOM.Storage,
            type                    = Util.type;

        this.log                    = log,
        this.PREFIX                 = '',
        this.LIBDIR                 = '/lib/';
        this.LIBDIRCLIENT           = '/lib/client/';
        this.MIN_ONE_PANEL_WIDTH    = 1155;
        this.HOST                   = location.origin ||
                                      location.protocol + '//' + location.host;

        log.enable     = function() {
            Debug = true;
        };

        log.disable    = function() {
            Debug = false;
        };

        /**
         * Функция привязываеться ко всем ссылкам и
         *  загружает содержимое каталогов
         *
         * @param params - {
         *          paramLink - ссылка
         *          needRefresh - необходимость обязательной загрузки данных с сервера
         *          panel
         *      }
         * @param callback
         */
        this.loadDir                = function(params, callback) {
            var link, imgPosition, panelChanged, pathParams,
                noCurrent,
                isRefresh, panel, history,
                p               = params;

            if (params) {
                pathParams      = p.path;
                isRefresh       = p.isRefresh;
                panel           = p.panel;
                history         = p.history;
                noCurrent       = p.noCurrent;
            }

            if (pathParams)
                link = pathParams;

            if (!noCurrent)
                if (panel && panel !== Info.panel) {
                    DOM.changePanel();
                    panelChanged = true;
                }

            if (panelChanged || isRefresh || !history)
                imgPosition = 'top';

            Images.show.load(imgPosition, panel);

            /* загружаем содержимое каталога */
            ajaxLoad(link, {
                refresh     : isRefresh,
                history     : history,
                noCurrent   : noCurrent
            }, panel, callback);
        };

        /**
         * function load modules
         * @pParams = {name, path, func, dobefore, arg}
         */
        function loadModule(params) {
            var name, path, func, doBefore,
                funcName, isContain;

            if (params) {
                name        = params.name,
                path        = params.path,
                func        = params.func,
                funcName    = params.funcName,
                doBefore    = params.dobefore;

                if (path && !name) {
                    name = getStrBigFirst(path);
                    name = name.replace(/.js$/, '');
                }

                isContain   = /\.js/.test(path);

                if (!isContain)
                    path += '.js';

                if (!CloudCmd[name]) {
                    CloudCmd[name] = function() {
                        var pathFull    = CloudCmd.LIBDIRCLIENT + path,
                            args        = arguments;

                        Util.exec(doBefore);

                        return DOM.load.js(pathFull, func ||
                            function(error) {
                                var Proto = CloudCmd[name];

                                if (!error && type.function(Proto))
                                    CloudCmd[name] = applyConstructor(Proto, args);
                            });
                    };

                    CloudCmd[name][funcName] = CloudCmd[name];
                }
            }
        }

        /*
         * apply arguemnts to constructor
         *
         * @param constructor
         * @param args
         */
        function applyConstructor(constructor, args) {
            var F = function () {
                return constructor.apply(this, args);
            };

            F.prototype = constructor.prototype;
            return new F();
        }

        /** Конструктор CloudClient, который
         * выполняет весь функционал по
         * инициализации
         */
        this.init                    = function(prefix) {
            var func        = function() {
                    Util.exec.series([
                        initModules,
                        baseInit,
                        function() {
                            CloudCmd.route(location.hash);
                        }
                    ]);
                },

                funcBefore  = function(callback) {
                    var src = prefix + '/join:' + [
                            CloudCmd.LIBDIRCLIENT + 'polyfill.js',
                            '/modules/domtokenlist-shim/dist/domtokenlist.min.js',
                        ].join(':');

                    DOM.loadJquery(function() {
                        DOM.load.js(src, callback);
                    });
                };

            CloudCmd.PREFIX         = prefix;
            CloudCmd.LIBDIR         = prefix + '/lib/';
            CloudCmd.LIBDIRCLIENT   = prefix + '/lib/client/';

            Util.exec.if(document.body.scrollIntoViewIfNeeded, func, funcBefore);
        };

        this.join                   = function(urls) {
            var prefix  = CloudCmd.PREFIX;

            Util.check(arguments, ['urls']);

            urls = urls.map(function(url) {
                return url.replace(prefix, '');
            });

            return prefix + join(urls);
        };

        this.route                   = function(path) {
            var module, file, current, msg,
                query   = path.split('/');

            if (path) {
                module      = query[0];
                module      = module.slice(1).join('');
                module      = getStrBigFirst(module);

                file        = query[1];
                current     = DOM.getCurrentByName(file);

                if (file && !current) {
                    msg = CloudFunc.formatMsg('set current file', file, 'error');
                    CloudCmd.log(msg);
                } else {
                    DOM.setCurrentFile(current);
                    CloudCmd.execFromModule(module, 'show');
                }
            }
        };

        this.logOut = function() {
            DOM.load.ajax({
                url: '/logout',
                error: function() {
                    document.location.reload();
                }
            });
        };

        function initModules(callback) {
            Util.exec.if(CloudCmd.Key, function() {
                Key          = new CloudCmd.Key();
                CloudCmd.Key = Key;
                Key.bind();
            }, function(callback) {
                loadModule({
                    /* привязываем клавиши к функциям */
                    path    : 'key.js',
                    func    : callback
                });
            });

            Files.get('modules', function(error, modules) {
                var storageObj, mod, path,
                    STORAGE     = 'storage',
                    showLoad    = Images.show.load,

                    doBefore    = {
                        'edit'                  : showLoad,
                        'menu'                  : showLoad,
                        'storage/_filepicker'   : showLoad
                    },

                    load = function(name, path, func) {
                        var isTmpl = path === 'template';

                        loadModule({
                            name        : name,
                            funcName    : isTmpl ? 'get' : 'show',
                            path        : path,
                            dobefore    : func
                        });
                    };

                if (!modules)
                    modules = [];

                modules.forEach(function(module) {
                    var isStr = type.string(module);

                    if (isStr)
                        load(null, module, doBefore[module]);
                });

                storageObj = Util.findObjByNameInArr(modules, STORAGE),
                mod        = Util.getNamesFromObjArray(storageObj);

                mod.forEach(function(name) {
                    path = STORAGE + '/_' + name.toLowerCase();

                    load(name, path, doBefore[path]);
                });

                callback();
            });
        }

        function baseInit(callback) {
            var dirPath = '',
                files   = DOM.getFiles();

            /* выделяем строку с первым файлом */
            if (files)
                DOM.setCurrentFile(files[0]);

            dirPath     = DOM.getCurrentDirPath(),
            Listeners   = CloudCmd.Listeners;
            Listeners.init();

            Listeners.setOnPanel('left');
            Listeners.setOnPanel('right');

            Listeners.initKeysPanel();

            /* Устанавливаем кэш корневого каталога */
            dirPath     = CloudFunc.rmLastSlash(dirPath);

            Storage.get(dirPath, function(error, data) {
                if (!data) {
                    data    = getJSONfromFileTable();
                    Storage.set(dirPath, data);
                }
            });

            callback();
        }

        this.execFromModule         = function(moduleName, funcName, params) {
            var obj     = CloudCmd[moduleName],
                isObj   = Util.type.object(obj);

            Util.exec.if(isObj,
                function() {
                    var obj     = CloudCmd[moduleName],
                        func    = obj[funcName];

                    Util.exec(func, params);
                },

                function(callback) {
                    Util.exec(obj, callback);
                });
        };

        this.refresh                =  function(panelParam, options, callback) {
            var panel           = panelParam || Info.panel,
                noCurrent,
                NEEDREFRESH     = true,
                path            = DOM.getCurrentDirPath(panel),
                notSlashlLink   = CloudFunc.rmLastSlash(path);

            if (options)
                noCurrent       = options.noCurrent

            if (!callback && typeof options === 'function') {
                callback    = options;
                options     = {};
            }

            CloudCmd.loadDir({
                path        : notSlashlLink,
                isRefresh   : NEEDREFRESH,
                history     : false,
                panel       : panel,
                noCurrent   : noCurrent
            }, callback);
        };

        /**
         * Функция загружает json-данные о Файловой Системе
         * через ajax-запрос.
         * @param path - каталог для чтения
         * @param options
         * { refresh, history } - необходимость обновить данные о каталоге
         * @param panel
         * @param callback
         *
         */
        function ajaxLoad(path, options, panel, callback) {
            var create = function(error, json) {
                var RESTful     = DOM.RESTful,
                    name        = Info.name,
                    obj         = Util.json.parse(json),
                    isRefresh   = options.refresh,
                    noCurrent   = options.noCurrent;

                if (!isRefresh && json)
                    createFileTable(obj, panel, options, callback);
                else
                    RESTful.read(path, 'json', function(error, obj) {
                        if (!error) {
                            Storage.set(path, obj);

                            createFileTable(obj, panel, options, function() {
                                var current;

                                if (isRefresh && !noCurrent) {
                                    current = DOM.getCurrentByName(name);
                                    DOM.setCurrentFile(current);
                                }

                                Util.exec(callback);
                            });
                        }
                    });
            };

            if (!options)
                options    = {};

            CloudCmd.log('reading dir: "' + path + '";');

            Files.get('config', function(error, config) {
                var dirStorage;

                if (error)
                    alert(error);
                else
                    dirStorage = config.dirStorage;

                if (dirStorage)
                    Storage.get(path, create);
                else
                    create();
            });
        }

        /**
         * Функция строит файловую таблицу
         * @param json  - данные о файлах
         * @param panelParam
         * @param history
         * @param callback
         */
        function createFileTable(json, panelParam, options, callback) {
            var history     = options.history,
                noCurrent   = options.noCurrent,
                names       = ['file', 'path', 'link', 'pathLink'];

            Files.get(names, function(error, templFile, templPath, templLink, templPathLink) {
                var childNodes, i,
                    Dialog      = DOM.Dialog,
                    current,
                    panel       = panelParam || DOM.getPanel(),

                    dir         = Info.dir,
                    name        = Info.name;

                if (error) {
                    Dialog.alert(error.responseText);
                } else {
                    childNodes  = panel.childNodes;
                    i           = childNodes.length;

                    while (i--)
                        panel.removeChild(panel.lastChild);

                    panel.innerHTML = CloudFunc.buildFromJSON({
                        data        : json,
                        id          : panel.id,
                        template    : {
                            file        : templFile,
                            path        : templPath,
                            pathLink    : templPathLink,
                            link        : templLink
                        }
                    });

                    Listeners.setOnPanel(panel);

                    if (!noCurrent) {
                        if (name === '..' && dir !== '/')
                            current = DOM.getCurrentByName(dir);

                        if (!current)
                            current = DOM.getFiles(panel)[0];

                        DOM.setCurrentFile(current, {
                            history: history
                        });
                    }

                    Util.exec(callback);
                }
            });
        }

        /**
         * Функция генерирует JSON из html-таблицы файлов и
         * используеться при первом заходе в корень
         */
        function getJSONfromFileTable() {
            var name, size, owner, mode, ret,
                path        = DOM.getCurrentDirPath(),
                infoFiles   = Info.files || [],

                fileTable   = {
                    path    : path,
                    files   : []
                },

                files       = fileTable.files;

            [].forEach.call(infoFiles, function(current) {
                name        = DOM.getCurrentName(current);
                size        = DOM.getCurrentSize(current);
                owner       = DOM.getCurrentOwner(current);
                mode        = DOM.getCurrentMode(current);

                if (name !== '..')
                    files.push({
                        name    : name,
                        size    : size,
                        mode    : mode,
                        owner   : owner
                    });
            });

            ret = Util.json.stringify(fileTable);

            return ret;
        }

        this.goToParentDir          = function() {
            var path        = Info.dirPath,
                parentPath  = Info.parentDirPath;

            if (path !== parentPath) {
                path    = parentPath;
                path    = CloudFunc.rmLastSlash(path);

                CloudCmd.loadDir({
                    path: path
                });
            }
        };

        function getStrBigFirst(str) {
            var first;

            if (!str)
                throw Error('str could not be empty!');

            first   = str[0].toUpperCase();
            str     = first + str.slice(1);

            return str;
        }

    }
})(this, Util, DOM, CloudFunc);
<commit_msg>chore(client) add ";"<commit_after>var Util, DOM, CloudFunc, join;

(function(scope, Util, DOM, CloudFunc) {
    'use strict';

    scope.CloudCmd = new CloudCmdProto(Util, DOM, CloudFunc);

    function CloudCmdProto(Util, DOM, CloudFunc) {
        var Key,
            Debug,
            log                     = function(str) {
                if (Debug)
                    console.log(str);
            },

            Listeners,
            Files                   = DOM.Files,
            Images                  = DOM.Images,
            Info                    = DOM.CurrentInfo,
            CloudCmd                = this,
            Storage                 = DOM.Storage,
            type                    = Util.type;

        this.log                    = log,
        this.PREFIX                 = '',
        this.LIBDIR                 = '/lib/';
        this.LIBDIRCLIENT           = '/lib/client/';
        this.MIN_ONE_PANEL_WIDTH    = 1155;
        this.HOST                   = location.origin ||
                                      location.protocol + '//' + location.host;

        log.enable     = function() {
            Debug = true;
        };

        log.disable    = function() {
            Debug = false;
        };

        /**
         * Функция привязываеться ко всем ссылкам и
         *  загружает содержимое каталогов
         *
         * @param params - {
         *          paramLink - ссылка
         *          needRefresh - необходимость обязательной загрузки данных с сервера
         *          panel
         *      }
         * @param callback
         */
        this.loadDir                = function(params, callback) {
            var link, imgPosition, panelChanged, pathParams,
                noCurrent,
                isRefresh, panel, history,
                p               = params;

            if (params) {
                pathParams      = p.path;
                isRefresh       = p.isRefresh;
                panel           = p.panel;
                history         = p.history;
                noCurrent       = p.noCurrent;
            }

            if (pathParams)
                link = pathParams;

            if (!noCurrent)
                if (panel && panel !== Info.panel) {
                    DOM.changePanel();
                    panelChanged = true;
                }

            if (panelChanged || isRefresh || !history)
                imgPosition = 'top';

            Images.show.load(imgPosition, panel);

            /* загружаем содержимое каталога */
            ajaxLoad(link, {
                refresh     : isRefresh,
                history     : history,
                noCurrent   : noCurrent
            }, panel, callback);
        };

        /**
         * function load modules
         * @pParams = {name, path, func, dobefore, arg}
         */
        function loadModule(params) {
            var name, path, func, doBefore,
                funcName, isContain;

            if (params) {
                name        = params.name,
                path        = params.path,
                func        = params.func,
                funcName    = params.funcName,
                doBefore    = params.dobefore;

                if (path && !name) {
                    name = getStrBigFirst(path);
                    name = name.replace(/.js$/, '');
                }

                isContain   = /\.js/.test(path);

                if (!isContain)
                    path += '.js';

                if (!CloudCmd[name]) {
                    CloudCmd[name] = function() {
                        var pathFull    = CloudCmd.LIBDIRCLIENT + path,
                            args        = arguments;

                        Util.exec(doBefore);

                        return DOM.load.js(pathFull, func ||
                            function(error) {
                                var Proto = CloudCmd[name];

                                if (!error && type.function(Proto))
                                    CloudCmd[name] = applyConstructor(Proto, args);
                            });
                    };

                    CloudCmd[name][funcName] = CloudCmd[name];
                }
            }
        }

        /*
         * apply arguemnts to constructor
         *
         * @param constructor
         * @param args
         */
        function applyConstructor(constructor, args) {
            var F = function () {
                return constructor.apply(this, args);
            };

            F.prototype = constructor.prototype;
            return new F();
        }

        /** Конструктор CloudClient, который
         * выполняет весь функционал по
         * инициализации
         */
        this.init                    = function(prefix) {
            var func        = function() {
                    Util.exec.series([
                        initModules,
                        baseInit,
                        function() {
                            CloudCmd.route(location.hash);
                        }
                    ]);
                },

                funcBefore  = function(callback) {
                    var src = prefix + '/join:' + [
                            CloudCmd.LIBDIRCLIENT + 'polyfill.js',
                            '/modules/domtokenlist-shim/dist/domtokenlist.min.js',
                        ].join(':');

                    DOM.loadJquery(function() {
                        DOM.load.js(src, callback);
                    });
                };

            CloudCmd.PREFIX         = prefix;
            CloudCmd.LIBDIR         = prefix + '/lib/';
            CloudCmd.LIBDIRCLIENT   = prefix + '/lib/client/';

            Util.exec.if(document.body.scrollIntoViewIfNeeded, func, funcBefore);
        };

        this.join                   = function(urls) {
            var prefix  = CloudCmd.PREFIX;

            Util.check(arguments, ['urls']);

            urls = urls.map(function(url) {
                return url.replace(prefix, '');
            });

            return prefix + join(urls);
        };

        this.route                   = function(path) {
            var module, file, current, msg,
                query   = path.split('/');

            if (path) {
                module      = query[0];
                module      = module.slice(1).join('');
                module      = getStrBigFirst(module);

                file        = query[1];
                current     = DOM.getCurrentByName(file);

                if (file && !current) {
                    msg = CloudFunc.formatMsg('set current file', file, 'error');
                    CloudCmd.log(msg);
                } else {
                    DOM.setCurrentFile(current);
                    CloudCmd.execFromModule(module, 'show');
                }
            }
        };

        this.logOut = function() {
            DOM.load.ajax({
                url: '/logout',
                error: function() {
                    document.location.reload();
                }
            });
        };

        function initModules(callback) {
            Util.exec.if(CloudCmd.Key, function() {
                Key          = new CloudCmd.Key();
                CloudCmd.Key = Key;
                Key.bind();
            }, function(callback) {
                loadModule({
                    /* привязываем клавиши к функциям */
                    path    : 'key.js',
                    func    : callback
                });
            });

            Files.get('modules', function(error, modules) {
                var storageObj, mod, path,
                    STORAGE     = 'storage',
                    showLoad    = Images.show.load,

                    doBefore    = {
                        'edit'                  : showLoad,
                        'menu'                  : showLoad,
                        'storage/_filepicker'   : showLoad
                    },

                    load = function(name, path, func) {
                        var isTmpl = path === 'template';

                        loadModule({
                            name        : name,
                            funcName    : isTmpl ? 'get' : 'show',
                            path        : path,
                            dobefore    : func
                        });
                    };

                if (!modules)
                    modules = [];

                modules.forEach(function(module) {
                    var isStr = type.string(module);

                    if (isStr)
                        load(null, module, doBefore[module]);
                });

                storageObj = Util.findObjByNameInArr(modules, STORAGE),
                mod        = Util.getNamesFromObjArray(storageObj);

                mod.forEach(function(name) {
                    path = STORAGE + '/_' + name.toLowerCase();

                    load(name, path, doBefore[path]);
                });

                callback();
            });
        }

        function baseInit(callback) {
            var dirPath = '',
                files   = DOM.getFiles();

            /* выделяем строку с первым файлом */
            if (files)
                DOM.setCurrentFile(files[0]);

            dirPath     = DOM.getCurrentDirPath(),
            Listeners   = CloudCmd.Listeners;
            Listeners.init();

            Listeners.setOnPanel('left');
            Listeners.setOnPanel('right');

            Listeners.initKeysPanel();

            /* Устанавливаем кэш корневого каталога */
            dirPath     = CloudFunc.rmLastSlash(dirPath);

            Storage.get(dirPath, function(error, data) {
                if (!data) {
                    data    = getJSONfromFileTable();
                    Storage.set(dirPath, data);
                }
            });

            callback();
        }

        this.execFromModule         = function(moduleName, funcName, params) {
            var obj     = CloudCmd[moduleName],
                isObj   = Util.type.object(obj);

            Util.exec.if(isObj,
                function() {
                    var obj     = CloudCmd[moduleName],
                        func    = obj[funcName];

                    Util.exec(func, params);
                },

                function(callback) {
                    Util.exec(obj, callback);
                });
        };

        this.refresh                =  function(panelParam, options, callback) {
            var panel           = panelParam || Info.panel,
                noCurrent,
                NEEDREFRESH     = true,
                path            = DOM.getCurrentDirPath(panel),
                notSlashlLink   = CloudFunc.rmLastSlash(path);

            if (options)
                noCurrent       = options.noCurrent;

            if (!callback && typeof options === 'function') {
                callback    = options;
                options     = {};
            }

            CloudCmd.loadDir({
                path        : notSlashlLink,
                isRefresh   : NEEDREFRESH,
                history     : false,
                panel       : panel,
                noCurrent   : noCurrent
            }, callback);
        };

        /**
         * Функция загружает json-данные о Файловой Системе
         * через ajax-запрос.
         * @param path - каталог для чтения
         * @param options
         * { refresh, history } - необходимость обновить данные о каталоге
         * @param panel
         * @param callback
         *
         */
        function ajaxLoad(path, options, panel, callback) {
            var create = function(error, json) {
                var RESTful     = DOM.RESTful,
                    name        = Info.name,
                    obj         = Util.json.parse(json),
                    isRefresh   = options.refresh,
                    noCurrent   = options.noCurrent;

                if (!isRefresh && json)
                    createFileTable(obj, panel, options, callback);
                else
                    RESTful.read(path, 'json', function(error, obj) {
                        if (!error) {
                            Storage.set(path, obj);

                            createFileTable(obj, panel, options, function() {
                                var current;

                                if (isRefresh && !noCurrent) {
                                    current = DOM.getCurrentByName(name);
                                    DOM.setCurrentFile(current);
                                }

                                Util.exec(callback);
                            });
                        }
                    });
            };

            if (!options)
                options    = {};

            CloudCmd.log('reading dir: "' + path + '";');

            Files.get('config', function(error, config) {
                var dirStorage;

                if (error)
                    alert(error);
                else
                    dirStorage = config.dirStorage;

                if (dirStorage)
                    Storage.get(path, create);
                else
                    create();
            });
        }

        /**
         * Функция строит файловую таблицу
         * @param json  - данные о файлах
         * @param panelParam
         * @param history
         * @param callback
         */
        function createFileTable(json, panelParam, options, callback) {
            var history     = options.history,
                noCurrent   = options.noCurrent,
                names       = ['file', 'path', 'link', 'pathLink'];

            Files.get(names, function(error, templFile, templPath, templLink, templPathLink) {
                var childNodes, i,
                    Dialog      = DOM.Dialog,
                    current,
                    panel       = panelParam || DOM.getPanel(),

                    dir         = Info.dir,
                    name        = Info.name;

                if (error) {
                    Dialog.alert(error.responseText);
                } else {
                    childNodes  = panel.childNodes;
                    i           = childNodes.length;

                    while (i--)
                        panel.removeChild(panel.lastChild);

                    panel.innerHTML = CloudFunc.buildFromJSON({
                        data        : json,
                        id          : panel.id,
                        template    : {
                            file        : templFile,
                            path        : templPath,
                            pathLink    : templPathLink,
                            link        : templLink
                        }
                    });

                    Listeners.setOnPanel(panel);

                    if (!noCurrent) {
                        if (name === '..' && dir !== '/')
                            current = DOM.getCurrentByName(dir);

                        if (!current)
                            current = DOM.getFiles(panel)[0];

                        DOM.setCurrentFile(current, {
                            history: history
                        });
                    }

                    Util.exec(callback);
                }
            });
        }

        /**
         * Функция генерирует JSON из html-таблицы файлов и
         * используеться при первом заходе в корень
         */
        function getJSONfromFileTable() {
            var name, size, owner, mode, ret,
                path        = DOM.getCurrentDirPath(),
                infoFiles   = Info.files || [],

                fileTable   = {
                    path    : path,
                    files   : []
                },

                files       = fileTable.files;

            [].forEach.call(infoFiles, function(current) {
                name        = DOM.getCurrentName(current);
                size        = DOM.getCurrentSize(current);
                owner       = DOM.getCurrentOwner(current);
                mode        = DOM.getCurrentMode(current);

                if (name !== '..')
                    files.push({
                        name    : name,
                        size    : size,
                        mode    : mode,
                        owner   : owner
                    });
            });

            ret = Util.json.stringify(fileTable);

            return ret;
        }

        this.goToParentDir          = function() {
            var path        = Info.dirPath,
                parentPath  = Info.parentDirPath;

            if (path !== parentPath) {
                path    = parentPath;
                path    = CloudFunc.rmLastSlash(path);

                CloudCmd.loadDir({
                    path: path
                });
            }
        };

        function getStrBigFirst(str) {
            var first;

            if (!str)
                throw Error('str could not be empty!');

            first   = str[0].toUpperCase();
            str     = first + str.slice(1);

            return str;
        }

    }
})(this, Util, DOM, CloudFunc);
<|endoftext|>