<commit_before>/**
 * The MIT License (MIT)
 * Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
 */

import fs from 'fs';

import colors from 'colors';

/**
 * Rust tokenizer template.
 */
const RUST_TOKENIZER_TEMPLATE = fs.readFileSync(
  `${__dirname}/templates/tokenizer.template.rs`,
  'utf-8'
);

/**
 * The trait is used by parser generators (LL/LR) for Rust.
 */
const RustParserGeneratorTrait = {

  /**
   * Generates parsing table in Rust vector format.
   */
  generateParseTable() {
    this.writeData(
      'TABLE',
      this._buildTable(this.generateParseTableData()),
    );
  },

  /**
   * Converts JS object into Rust HashMap.
   *
   * In Rust we represent a table as a vector, where index is a state number,
   * and a value is a hashmap of LR entries (shift/reduce/etc).
   *
   * Example:
   *
   * vec![
   *     hashmap! { 1 => TE::Shift(4), 3 => TE::Reduce(1), ... },
   * ]
   */
  _buildTable(table) {
    const entries = Object.keys(table).map(state => {
      const row = table[state];

      // Transform to Rust enum format: "s3" => TE::Shift(3), etc
      Object.keys(row).forEach(key => {
        const entry = row[key];
        if (entry[0] === 's') {
          row[key] = `TE::Shift(${entry.slice(1)})`;
        } else if (entry[0] === 'r') {
          row[key] = `TE::Reduce(${entry.slice(1)})`;
        } else if (entry === 'acc') {
          row[key] = `TE::Accept`;
        } else {
          row[key] = `TE::Transit(${entry})`;
        }
      });

      return this._toRustHashMap(table[state], 'number')
    });

    return `vec![\n    ${entries.join(',\n    ')}\n]`;
  },

  /**
   * Generates tokens table in Rust hashmap format.
   */
  generateTokensTable() {
    this.writeData(
      'TOKENS',
      this._toRustHashMap(this._tokens, 'string', 'number'),
    );
  },

  /**
   * Production handlers are implemented as methods on the parser class.
   */
  buildSemanticAction(production) {
    let originalAction = this.getSemanticActionCode(production);

    let { action, types } = this._extractDataTypes(originalAction);

    action = this._actionFromHandler(action, '.tokenizer');

    action = this._generateArgsPrologue(
      action,
      types,
      // Total number of args.
      production.isEpsilon() ? 0 : production.getRHS().length
    );

    // Append return value.
    const returnValue = types.hasOwnProperty('__')
      ? `SV::_${this._allTypes[types.__]}(__)`
      : `__`;

    action = action + `\n${returnValue}`;

    // Save the action, they are injected later.
    this._productionHandlers.push({ args: '&mut self', action });
    return null;
  },

  /**
   * Builds SV (stack value) enum from all the used types in handlers.
   */
  generateStackValueEnum() {
    const svEnum = Object.keys(this._allTypes).map(
      (typeName, idx) => `_${idx}(${typeName})`
    );
    this.writeData('SV_ENUM', svEnum.join(',\n    '));
  },

  /**
   * Generates prologue for fetching arguments from the parsing stack.
   */
  _generateArgsPrologue(action, types, totalArgsCount) {
    const argsPrologue = [];

    for (let i = totalArgsCount; i > 0; i--) {
      const arg = '_' + i;

      if (!types.hasOwnProperty(arg)) {
        // Just pop if arg is not used in the handler.
        argsPrologue.push(`self.values_stack.pop();`);
      } else {
        const typeInfo = types[arg];

        if (typeInfo) {
          argsPrologue.push(
            `let mut ${arg} = pop!(self.values_stack, ` +
            `_${this._allTypes[typeInfo]});`
          );
        } else {
          argsPrologue.push(
            `let mut ${arg} = self.values_stack.pop().unwrap();`
          );
        }
      }
    }

    return (
      '// Semantic values prologue.\n' +
      argsPrologue.join('\n') + '\n\n' +
      action
    );
  },

  /**
   * Extracts types of the used arguments, and return types.
   * If a type is defined, a popped stack value is casted,
   * otherwise, it's just popped (if doesn't participate in an operation
   * and just propagated).
   */
  _extractDataTypes(action) {
    const types = {};

    if (!action) {
      return { action: '', types };
    }

    const typesRe = /\s*\|([^|]*)\|\s*->\s*(\w+);/g;
    const typesData = typesRe.exec(action);

    if (typesData) {
      // '$1:i32, $3:i32'
      if (typesData[1].trim().length > 0) {
        const argTypes = typesData[1].trim().split(/\s*[,;]\s*/);
        argTypes.forEach(argData => {
          const data = argData.split(/\s*:\s*/);
          // types[$1] = 'i32';
          types[data[0]] = data[1];

          // Save the type to all types data to generate enum.
          if (!this._allTypes.hasOwnProperty(data[1])) {
            this._allTypes[data[1]] = this._allTypesIndex++;
          }
        });
      }

      // Result type.
      if (typesData[2]) {
        types.__ = typesData[2];

        if (!this._allTypes.hasOwnProperty(typesData[2])) {
          this._allTypes[typesData[2]] = this._allTypesIndex++;
        }
      }
    }

    // Strip the types info.
    action = action.replace(typesRe, '');

    // Extract other args, which do not use types.
    const argsRe = /_\d+/g;

    const usedArgs = action.match(argsRe);
    if (usedArgs) {
      usedArgs.forEach(usedArg => {
        if (!types.hasOwnProperty(usedArg)) {
          // Arg is used, but without a type.
          types[usedArg] = null;
        }
      });
    }

    return { types, action };
  },

  /**
   * Default format in the [ ] array notation.
   */
  generateProductionsData() {
    return this.generateRawProductionsData()
      .map(data => {
        // Remove the semantic action handler, since in Rust
        // we use a different structure to hold it.
        delete data[2];
        return `[${data.join(', ')}]`;
      });
  },

  /**
   * Generates built-in tokenizer instance.
   */
  generateBuiltInTokenizer() {
    this.writeData('TOKENIZER', RUST_TOKENIZER_TEMPLATE);
  },

  /**
   * Creates an action from raw handler.
   */
  _actionFromHandler(handler, context = '') {
    let action = (this._scopeVars(handler, context) || '').trim();

    if (!action) {
      return 'let __ = SV::Undefined;';
    }

    // From parser hooks, append ; at the end.
    if (context === '.tokenizer' && !/;\s*$/.test(action)) {
      action += ';';
    }

    return action;
  },

  /**
   * Generates rules for tokenizer.
   */
  generateLexRules() {
    const lexRulesArray = [];

    const lexRules = this._grammar.getLexGrammar().getRules().map((rule, i) => {
      let action = this._actionFromHandler(rule.getRawHandler());

      this._lexHandlers.push({ args: '&mut self', action });

      let flags = [];

      if (rule.isCaseInsensitive()) {
        flags.push('i');
      }

      if (flags.length > 0) {
        flags = `(?${flags.join('')})`
      } else {
        flags = '';
      }

      lexRulesArray.push(`Tokenizer::_lex_rule${i}`);

      let matcher = rule.getRawMatcher();

      // why there is f**king escape for / ?
      matcher = matcher.replace("\\/", "/");

      // well, maybe so many # is enough
      return `r##########"${flags}${matcher}"##########`;
    });

    this.writeData('LEX_RULE_HANDLERS_COUNT', lexRules.length);
    this.writeData(
      'LEX_RULE_HANDLERS_ARRAY',
      `[\n    ${lexRulesArray.join(',\n    ')}\n],`
    );

    this.writeData(
      'LEX_RULES',
      `[&'static str; ${lexRules.length}] = ` +
      `[\n    ${lexRules.join(',\n    ')}\n]`
    );
  },

  generateLexRulesByStartConditions() {
    const lexGrammar = this._grammar.getLexGrammar();
    const lexRulesByConditions = lexGrammar.getRulesByStartConditions();
    const result = [];

    for (const condition in lexRulesByConditions) {
      result[condition] = lexRulesByConditions[condition].map(lexRule =>
        lexGrammar.getRuleIndex(lexRule)
      );
    }

    this.writeData(
      'LEX_RULES_BY_START_CONDITIONS',
      `${this._toRustHashMap(result, 'string')}`,
    );
  },

  /**
   * Replaces global vars like `yytext`, `$$`, etc. to be
   * referred from `yyparse`.
   */
  _scopeVars(code, context = '') {
    return code
      .replace(/yytext/g, `self${context}.yytext`)
      .replace(/yyleng/g, `self${context}.yyleng`)
      .replace(/__\s*=/g, `let __ =`)
      .replace(/yyloc/g, 'Loc::from_tokens_range');
  },

  /**
   * Type-converts a key of a Rust HashMap, e.g. string or number, etc.
   */
  _hashKey(key, keyType) {
    switch (keyType) {
      case 'string': return `"${key}"`;
      case 'number': return Number(key);
      default:
        throw new Error('_hashKey: Incorrect type ' + keyType);
    }
  },

  /**
   * Type-converts a value of a Rust hashmap, e.g. string or number, etc.
   */
  _hashValue(value, valueType) {
    if (Array.isArray(value)) {
      // Support only int arrays here for simplicity.
      return `vec! [ ${value.join(', ')} ]`;
    }

    switch (valueType) {
      case 'string': return `"${value}"`;
      case 'number': return Number(value);
      default: return value;
    }
  },

  /**
   * Converts JS object to Rust HashMap representation.
   */
  _toRustHashMap(object, keyType, valueType) {
    let result = [];
    for (let k in object) {
      let value = object[k];
      let key = k.replace(/"/g, '\\"');
      result.push(
        `${this._hashKey(key, keyType)} => ` +
        `${this._hashValue(value, valueType)}`
      );
    }
    return `hashmap! { ${result.join(', ')} }`;
  },

  /**
   * Rust-specific lex rules handler declarations.
   */
  generateLexHandlers() {
    const handlers = this._generateHandlers(
      this._lexHandlers,
      '_lex_rule',
      "&'static str"
    );
    this.writeData('LEX_RULE_HANDLERS', handlers.join('\n\n'));
  },

  /**
   * Rust-specific handler declarations.
   */
  generateProductionHandlers() {
    const handlers = this._generateHandlers(
      this._productionHandlers,
      '_handler',
      'SV'
    );

    this.writeData('PRODUCTION_HANDLERS_COUNT', handlers.length);

    const handlersArray = [];
    for (let i = 0; i < handlers.length; i++) {
      handlersArray.push(`Parser::_handler${i}`);
    }

    this.writeData(
      'PRODUCTION_HANDLERS_ARRAY',
      `[\n    ${handlersArray.join(',\n    ')}\n],`
    );

    this.writeData('PRODUCTION_HANDLERS', handlers.join('\n\n'));
  },

  /**
   * Productions array in Rust format.
   */
  generateProductions() {
    const productionsData = this.generateProductionsData();
    const productionsCount = productionsData.length;
    this.writeData(
      'PRODUCTIONS',
      `[[i32; 2]; ${productionsCount}] = ` +
      `[\n    ${productionsData.join(',\n    ')}\n]`
    );
  },

  /**
   * Module include.
   */
  generateModuleInclude() {
    const moduleInclude = this._grammar.getModuleInclude();

    const resultTypeData = /type\s+TResult\s*=\s*([^;]+);/.exec(moduleInclude);

    if (!resultTypeData) {
      throw new Error(
        `\n\nRust plugin should provide module include, and define at least ` +
        `result type:\n\n  ${colors.bold('type TResult = <...>;\n')}`
      );
    }

    // Result type.
    const resultType = resultTypeData[1];

    if (!this._allTypes.hasOwnProperty(resultType)) {
      throw new Error(
        `Result type ${colors.bold(resultType)} is not found in ` +
        `handled types. Make sure your productions return it.\n`
      );
    }

    this.writeData('RESULT_TYPE', `_${this._allTypes[resultType]}`);

    // Parser hooks.
    const onParseBegin = moduleInclude.indexOf('fn on_parse_begin') !== -1
      ? 'on_parse_begin(self, string);'
      : '';

    const onParseEnd = moduleInclude.indexOf('fn on_parse_end') !== -1
      ? 'on_parse_end(self, &result);'
      : '';

    const onParseError = moduleInclude.indexOf('fn on_parse_error') !== -1
      ? 'on_parse_error(self, &token);'
      : 'self.tokenizer.panic_unexpected_token(token.value, token.start_line, token.start_column);';

    this.writeData('ON_PARSE_BEGIN_CALL', onParseBegin);
    this.writeData('ON_PARSE_END_CALL', onParseEnd);
    this.writeData('ON_PARSE_ERROR_CALL', onParseError);

    this.writeData('MODULE_INCLUDE', moduleInclude);
  },

  /**
   * Generates Rust function declarations for handlers.
   */
  _generateHandlers(handlers, name, returnType) {
    return handlers.map(({ args, action }, index) => {
      return `fn ${name}${index}` +
        `(${args}) -> ${returnType} {\n${action}\n}`
    });
  },
};

module.exports = RustParserGeneratorTrait;<commit_msg>Update rust-parser-generator-trait.js<commit_after>/**
 * The MIT License (MIT)
 * Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
 */

import fs from 'fs';

import colors from 'colors';

/**
 * Rust tokenizer template.
 */
const RUST_TOKENIZER_TEMPLATE = fs.readFileSync(
  `${__dirname}/templates/tokenizer.template.rs`,
  'utf-8'
);

/**
 * The trait is used by parser generators (LL/LR) for Rust.
 */
const RustParserGeneratorTrait = {

  /**
   * Generates parsing table in Rust vector format.
   */
  generateParseTable() {
    this.writeData(
      'TABLE',
      this._buildTable(this.generateParseTableData()),
    );
  },

  /**
   * Converts JS object into Rust HashMap.
   *
   * In Rust we represent a table as a vector, where index is a state number,
   * and a value is a hashmap of LR entries (shift/reduce/etc).
   *
   * Example:
   *
   * vec![
   *     hashmap! { 1 => TE::Shift(4), 3 => TE::Reduce(1), ... },
   * ]
   */
  _buildTable(table) {
    const entries = Object.keys(table).map(state => {
      const row = table[state];

      // Transform to Rust enum format: "s3" => TE::Shift(3), etc
      Object.keys(row).forEach(key => {
        const entry = row[key];
        if (entry[0] === 's') {
          row[key] = `TE::Shift(${entry.slice(1)})`;
        } else if (entry[0] === 'r') {
          row[key] = `TE::Reduce(${entry.slice(1)})`;
        } else if (entry === 'acc') {
          row[key] = `TE::Accept`;
        } else {
          row[key] = `TE::Transit(${entry})`;
        }
      });

      return this._toRustHashMap(table[state], 'number')
    });

    return `vec![\n    ${entries.join(',\n    ')}\n]`;
  },

  /**
   * Generates tokens table in Rust hashmap format.
   */
  generateTokensTable() {
    this.writeData(
      'TOKENS',
      this._toRustHashMap(this._tokens, 'string', 'number'),
    );
  },

  /**
   * Production handlers are implemented as methods on the parser class.
   */
  buildSemanticAction(production) {
    let originalAction = this.getSemanticActionCode(production);

    let {action, types} = this._extractDataTypes(originalAction);

    action = this._actionFromHandler(action, '.tokenizer');

    action = this._generateArgsPrologue(
      action,
      types,
      // Total number of args.
      production.isEpsilon() ? 0 : production.getRHS().length
    );

    // Append return value.
    const returnValue = types.hasOwnProperty('__')
      ? `SV::_${this._allTypes[types.__]}(__)`
      : `__`;

    action = action + `\n${returnValue}`;

    // Save the action, they are injected later.
    this._productionHandlers.push({args: '&mut self', action});
    return null;
  },

  /**
   * Builds SV (stack value) enum from all the used types in handlers.
   */
  generateStackValueEnum() {
    const svEnum = Object.keys(this._allTypes).map(
      (typeName, idx) => `_${idx}(${typeName})`
    );
    this.writeData('SV_ENUM', svEnum.join(',\n    '));
  },

  /**
   * Generates prologue for fetching arguments from the parsing stack.
   */
  _generateArgsPrologue(action, types, totalArgsCount) {
    const argsPrologue = [];

    for (let i = totalArgsCount; i > 0; i--) {
      const arg = '_' + i;

      if (!types.hasOwnProperty(arg)) {
        // Just pop if arg is not used in the handler.
        argsPrologue.push(`self.values_stack.pop();`);
      } else {
        const typeInfo = types[arg];

        if (typeInfo) {
          argsPrologue.push(
            `let mut ${arg} = pop!(self.values_stack, ` +
            `_${this._allTypes[typeInfo]});`
          );
        } else {
          argsPrologue.push(
            `let mut ${arg} = self.values_stack.pop().unwrap();`
          );
        }
      }
    }

    return (
      '// Semantic values prologue.\n' +
      argsPrologue.join('\n') + '\n\n' +
      action
    );
  },

  /**
   * Extracts types of the used arguments, and return types.
   * If a type is defined, a popped stack value is casted,
   * otherwise, it's just popped (if doesn't participate in an operation
   * and just propagated).
   */
  _extractDataTypes(action) {
    const types = {};

    if (!action) {
      return {action: '', types};
    }

    const typesRe = /\s*\|([^|]*)\|\s*->\s*(\w+);/g;
    const typesData = typesRe.exec(action);

    if (typesData) {
      // '$1:i32, $3:i32'
      if (typesData[1].trim().length > 0) {
        const argTypes = typesData[1].trim().split(/\s*[,;]\s*/);
        argTypes.forEach(argData => {
          const data = argData.split(/\s*:\s*/);
          // types[$1] = 'i32';
          types[data[0]] = data[1];

          // Save the type to all types data to generate enum.
          if (!this._allTypes.hasOwnProperty(data[1])) {
            this._allTypes[data[1]] = this._allTypesIndex++;
          }
        });
      }

      // Result type.
      if (typesData[2]) {
        types.__ = typesData[2];

        if (!this._allTypes.hasOwnProperty(typesData[2])) {
          this._allTypes[typesData[2]] = this._allTypesIndex++;
        }
      }
    }

    // Strip the types info.
    action = action.replace(typesRe, '');

    // Extract other args, which do not use types.
    const argsRe = /_\d+/g;

    const usedArgs = action.match(argsRe);
    if (usedArgs) {
      usedArgs.forEach(usedArg => {
        if (!types.hasOwnProperty(usedArg)) {
          // Arg is used, but without a type.
          types[usedArg] = null;
        }
      });
    }

    return {types, action};
  },

  /**
   * Default format in the [ ] array notation.
   */
  generateProductionsData() {
    return this.generateRawProductionsData()
      .map(data => {
        // Remove the semantic action handler, since in Rust
        // we use a different structure to hold it.
        delete data[2];
        return `[${data.join(', ')}]`;
      });
  },

  /**
   * Generates built-in tokenizer instance.
   */
  generateBuiltInTokenizer() {
    this.writeData('TOKENIZER', RUST_TOKENIZER_TEMPLATE);
  },

  /**
   * Creates an action from raw handler.
   */
  _actionFromHandler(handler, context = '') {
    let action = (this._scopeVars(handler, context) || '').trim();

    if (!action) {
      return 'let __ = SV::Undefined;';
    }

    // From parser hooks, append ; at the end.
    if (context === '.tokenizer' && !/;\s*$/.test(action)) {
      action += ';';
    }

    return action;
  },

  /**
   * Generates rules for tokenizer.
   */
  generateLexRules() {
    const lexRulesArray = [];

    const lexRules = this._grammar.getLexGrammar().getRules().map((rule, i) => {
      let action = this._actionFromHandler(rule.getRawHandler());

      this._lexHandlers.push({args: '&mut self', action});

      let flags = [];

      if (rule.isCaseInsensitive()) {
        flags.push('i');
      }

      if (flags.length > 0) {
        flags = `(?${flags.join('')})`
      } else {
        flags = '';
      }

      lexRulesArray.push(`Tokenizer::_lex_rule${i}`);

      let matcher = rule.getRawMatcher();

      // there is no need
      matcher = matcher.replace("\\/", "/");

      // well, maybe so many # is enough
      // this is for handling character '"' correctly
      // reference: rust's raw string literals https://rahul-thakoor.github.io/rust-raw-string-literals
      return `r##########"${flags}${matcher}"##########`;
    });

    this.writeData('LEX_RULE_HANDLERS_COUNT', lexRules.length);
    this.writeData(
      'LEX_RULE_HANDLERS_ARRAY',
      `[\n    ${lexRulesArray.join(',\n    ')}\n],`
    );

    this.writeData(
      'LEX_RULES',
      `[&'static str; ${lexRules.length}] = ` +
      `[\n    ${lexRules.join(',\n    ')}\n]`
    );
  },

  generateLexRulesByStartConditions() {
    const lexGrammar = this._grammar.getLexGrammar();
    const lexRulesByConditions = lexGrammar.getRulesByStartConditions();
    const result = [];

    for (const condition in lexRulesByConditions) {
      result[condition] = lexRulesByConditions[condition].map(lexRule =>
        lexGrammar.getRuleIndex(lexRule)
      );
    }

    this.writeData(
      'LEX_RULES_BY_START_CONDITIONS',
      `${this._toRustHashMap(result, 'string')}`,
    );
  },

  /**
   * Replaces global vars like `yytext`, `$$`, etc. to be
   * referred from `yyparse`.
   */
  _scopeVars(code, context = '') {
    return code
      .replace(/yytext/g, `self${context}.yytext`)
      .replace(/yyleng/g, `self${context}.yyleng`)
      .replace(/__\s*=/g, `let __ =`)
      .replace(/yyloc/g, 'Loc::from_tokens_range');
  },

  /**
   * Type-converts a key of a Rust HashMap, e.g. string or number, etc.
   */
  _hashKey(key, keyType) {
    switch (keyType) {
      case 'string': return `"${key}"`;
      case 'number': return Number(key);
      default:
        throw new Error('_hashKey: Incorrect type ' + keyType);
    }
  },

  /**
   * Type-converts a value of a Rust hashmap, e.g. string or number, etc.
   */
  _hashValue(value, valueType) {
    if (Array.isArray(value)) {
      // Support only int arrays here for simplicity.
      return `vec! [ ${value.join(', ')} ]`;
    }

    switch (valueType) {
      case 'string': return `"${value}"`;
      case 'number': return Number(value);
      default: return value;
    }
  },

  /**
   * Converts JS object to Rust HashMap representation.
   */
  _toRustHashMap(object, keyType, valueType) {
    let result = [];
    for (let k in object) {
      let value = object[k];
      let key = k.replace(/"/g, '\\"');
      result.push(
        `${this._hashKey(key, keyType)} => ` +
        `${this._hashValue(value, valueType)}`
      );
    }
    return `hashmap! { ${result.join(', ')} }`;
  },

  /**
   * Rust-specific lex rules handler declarations.
   */
  generateLexHandlers() {
    const handlers = this._generateHandlers(
      this._lexHandlers,
      '_lex_rule',
      "&'static str"
    );
    this.writeData('LEX_RULE_HANDLERS', handlers.join('\n\n'));
  },

  /**
   * Rust-specific handler declarations.
   */
  generateProductionHandlers() {
    const handlers = this._generateHandlers(
      this._productionHandlers,
      '_handler',
      'SV'
    );

    this.writeData('PRODUCTION_HANDLERS_COUNT', handlers.length);

    const handlersArray = [];
    for (let i = 0; i < handlers.length; i++) {
      handlersArray.push(`Parser::_handler${i}`);
    }

    this.writeData(
      'PRODUCTION_HANDLERS_ARRAY',
      `[\n    ${handlersArray.join(',\n    ')}\n],`
    );

    this.writeData('PRODUCTION_HANDLERS', handlers.join('\n\n'));
  },

  /**
   * Productions array in Rust format.
   */
  generateProductions() {
    const productionsData = this.generateProductionsData();
    const productionsCount = productionsData.length;
    this.writeData(
      'PRODUCTIONS',
      `[[i32; 2]; ${productionsCount}] = ` +
      `[\n    ${productionsData.join(',\n    ')}\n]`
    );
  },

  /**
   * Module include.
   */
  generateModuleInclude() {
    const moduleInclude = this._grammar.getModuleInclude();

    const resultTypeData = /type\s+TResult\s*=\s*([^;]+);/.exec(moduleInclude);

    if (!resultTypeData) {
      throw new Error(
        `\n\nRust plugin should provide module include, and define at least ` +
        `result type:\n\n  ${colors.bold('type TResult = <...>;\n')}`
      );
    }

    // Result type.
    const resultType = resultTypeData[1];

    if (!this._allTypes.hasOwnProperty(resultType)) {
      throw new Error(
        `Result type ${colors.bold(resultType)} is not found in ` +
        `handled types. Make sure your productions return it.\n`
      );
    }

    this.writeData('RESULT_TYPE', `_${this._allTypes[resultType]}`);

    // Parser hooks.
    const onParseBegin = moduleInclude.indexOf('fn on_parse_begin') !== -1
      ? 'on_parse_begin(self, string);'
      : '';

    const onParseEnd = moduleInclude.indexOf('fn on_parse_end') !== -1
      ? 'on_parse_end(self, &result);'
      : '';

    const onParseError = moduleInclude.indexOf('fn on_parse_error') !== -1
      ? 'on_parse_error(self, &token);'
      : 'self.tokenizer.panic_unexpected_token(token.value, token.start_line, token.start_column);';

    this.writeData('ON_PARSE_BEGIN_CALL', onParseBegin);
    this.writeData('ON_PARSE_END_CALL', onParseEnd);
    this.writeData('ON_PARSE_ERROR_CALL', onParseError);

    this.writeData('MODULE_INCLUDE', moduleInclude);
  },

  /**
   * Generates Rust function declarations for handlers.
   */
  _generateHandlers(handlers, name, returnType) {
    return handlers.map(({args, action
                         }, index) => {
      return `fn ${name}${index}` +
        `(${args}) -> ${returnType} {\n${action}\n}`
    });
  },
};

module.exports = RustParserGeneratorTrait;
<|endoftext|>


<commit_before>console.log('This would be the main JS file.');
<commit_msg>Delete main.js<commit_after><|endoftext|>


<commit_before>'use strict';
(function(module) {
  try {
    module = angular.module('tink.accordion');
  } catch (e) {
    module = angular.module('tink.accordion', ['tink.tinkApi']);
  }
  module.directive('tinkAccordion',['tinkApi',function (tinkApi) {
    return {
      restrict:'EA',
      controller:'TinkAccordionController',
      transclude: true,
      replace: false,
      scope:{
        startOpen:'=',
        oneAtATime:'='
      },
      template: '

',
      compile: function compile() {
        return {
          pre: function preLink(scope,element, attrs, accordionCtrl) {
            var options = {};
            angular.forEach(['oneAtATime','startOpen'], function(key) {
              if(angular.isDefined(attrs[key])) {
                if(typeof scope[key] === 'boolean'){
                  options[key] = scope[key];
                }else{
                  options[key] = attrs[key] === 'true';
                }
              }
            });
            var accordionElem = tinkApi.accordion(element);
            accordionCtrl.init(accordionElem,element,options);
          }
        };
      }
    };
  }])
  .directive('tinkAccordionPanel', function() {
    return {
      restrict:'EA',
      priority: 1500.1,
      compile:function(){
        //var header = tElement.find('data-header');
        //var content = tElement.find('data-content');
      }
    };
  })
  .directive('tinkAccordionPanel', function($compile,$timeout) {
    return {
      require:'^tinkAccordion',         // We need this directive to be inside an accordion group
      restrict:'EA',
      priority: 1500,
      transclude:true,              // It transcludes the contents of the directive into the template
      replace: true,                // The element containing the directive will be replaced with the template
      templateUrl:'templates/tinkAccordionPanel.html',
      scope: {
        onclick:'=?',
        isCollapsed:'=',
        hasPadding:'@',
        heading:'@'
      },
      compile:function(){
        return {
          pre: function preLink(scope, element, attrs, accordionCtrl,transclude) {

            transclude(function(clone,innerscope){
              var header = $(clone).filter('data-header');
              if(typeof scope.heading !== 'string'){
                element.find('.panel-title').append(header);
              }else{
                element.find('.panel-title').html('{{heading}}');
                $compile(element.find('.panel-title'))(scope);
              }
            },scope);

            var insertContent = function(){
              transclude(function(clone,innerscope){
                var content = $(clone).filter('data-content');
                if(typeof scope.heading !== 'string'){
                  element.find('.accordion-loaded-content').append(content);
                }else{
                  element.find('.accordion-loaded-content').append(clone);
                }
              });
            }

            var removeContent = function(){
              element.find('.accordion-loaded-content').empty();
            }

           var states = {closed:1,open:2,loading:0};
            var state = states.closed;
            var trackToggle;
            var onFunc = typeof scope.onclick === 'function';
            if(!onFunc){
              element.addClass('no-call-back');
            }

            // The panel has padding or not?
            scope.hasPadding = scope.hasPadding !== 'false';

            scope.toggleOpen = function(){
              if(state === states.closed){
                if(onFunc){
                  scope.loading();
                }else{
                  scope.open();
                }
              }else if(state === states.open){
                scope.close();
              }else if(state === states.loading){
                scope.cancel();
              }
            };

            if(attrs.isCollapsed){
              if(attrs.isCollapsed === 'true' || attrs.isCollapsed === 'false'){
                trackToggle = false;
              }else{
                trackToggle = true;
              }
            }

            if(trackToggle){
              scope.$watch('isCollapsed',function(newVar){
                if(newVar === true){
                  stateClose();
                }else if(newVar === false){
                  stateOpen();
                }
              });
            }

            scope.open = function(){
              if(trackToggle){
                scope.isCollapsed = false;
              }else{
                stateOpen();
              }
            };

            scope.close = function(){
              if(trackToggle){
                scope.isCollapsed = true;
              }else{
                stateClose();
              }
            };

            scope.loading = function(){
              if(trackToggle){
                scope.isCollapsed = false;
              }else{
                stateLoad();
              }
            };

            scope.cancel = function(){
              if(trackToggle){
                scope.isCollapsed = true;
              }else{
                cancelTrans();
              }
            };

            var stateLoad = function(){
              state = states.loading;
              callback('loading',function(){
                if(state === states.loading){
                  stateOpen();
                }
              });
              accordionCtrl.openGroup(element,scope);
            };

            var stateOpen = function(){
              insertContent();
              if((!onFunc && state === states.closed)||(onFunc && state === states.loading)){
                state = states.open;
                accordionCtrl.openGroup(element,scope);
                callback('open');
              }else if(onFunc && state === states.closed){
                stateLoad();
              }
            };

            var stateClose = function(){
              if(state === states.open){
                state = states.closed;
                accordionCtrl.closeGroup(element);
                callback('closed');
              }else if(state === states.canceld){
                cancelTrans();
              }
              $timeout(function(){
                removeContent();
              },1);
            };

            var cancelTrans = function(){
              state = states.closed;
              accordionCtrl.closeGroup(element);
               callback('canceld');
            };

            var callback = function(type,fn){
              if(onFunc){
                scope.onclick(type,fn);
              }
            };
            accordionCtrl.addGroup(scope,element);
          }
        };
      },
    };
  });
})();<commit_msg>duplication fix<commit_after>'use strict';
(function(module) {
  try {
    module = angular.module('tink.accordion');
  } catch (e) {
    module = angular.module('tink.accordion', ['tink.tinkApi']);
  }
  module.directive('tinkAccordion',['tinkApi',function (tinkApi) {
    return {
      restrict:'EA',
      controller:'TinkAccordionController',
      transclude: true,
      replace: false,
      scope:{
        startOpen:'=',
        oneAtATime:'='
      },
      template: '

',
      compile: function compile() {
        return {
          pre: function preLink(scope,element, attrs, accordionCtrl) {
            var options = {};
            angular.forEach(['oneAtATime','startOpen'], function(key) {
              if(angular.isDefined(attrs[key])) {
                if(typeof scope[key] === 'boolean'){
                  options[key] = scope[key];
                }else{
                  options[key] = attrs[key] === 'true';
                }
              }
            });
            var accordionElem = tinkApi.accordion(element);
            accordionCtrl.init(accordionElem,element,options);
          }
        };
      }
    };
  }])
  .directive('tinkAccordionPanel', function() {
    return {
      restrict:'EA',
      priority: 1500.1,
      compile:function(){
        //var header = tElement.find('data-header');
        //var content = tElement.find('data-content');
      }
    };
  })
  .directive('tinkAccordionPanel', function($compile,$timeout) {
    return {
      require:'^tinkAccordion',         // We need this directive to be inside an accordion group
      restrict:'EA',
      priority: 1500,
      transclude:true,              // It transcludes the contents of the directive into the template
      replace: true,                // The element containing the directive will be replaced with the template
      templateUrl:'templates/tinkAccordionPanel.html',
      scope: {
        onclick:'=?',
        isCollapsed:'=',
        hasPadding:'@',
        heading:'@'
      },
      compile:function(){
        return {
          pre: function preLink(scope, element, attrs, accordionCtrl,transclude) {

            transclude(function(clone,innerscope){
              var header = $(clone).filter('data-header');
              if(typeof scope.heading !== 'string'){
                element.find('.panel-title').html(header);
              }else{
                element.find('.panel-title').html('{{heading}}');
                $compile(element.find('.panel-title'))(scope);
              }
            },scope);

            var insertContent = function(){
              transclude(function(clone,innerscope){
                var content = $(clone).filter('data-content');
                if(typeof scope.heading !== 'string'){
                  element.find('.accordion-loaded-content').html(content);
                }else{
                  element.find('.accordion-loaded-content').html(clone);
                }
              });
            }

            var removeContent = function(){
              element.find('.accordion-loaded-content').empty();
            }

           var states = {closed:1,open:2,loading:0};
            var state = states.closed;
            var trackToggle;
            var onFunc = typeof scope.onclick === 'function';
            if(!onFunc){
              element.addClass('no-call-back');
            }

            // The panel has padding or not?
            scope.hasPadding = scope.hasPadding !== 'false';

            scope.toggleOpen = function(){
              if(state === states.closed){
                if(onFunc){
                  scope.loading();
                }else{
                  scope.open();
                }
              }else if(state === states.open){
                scope.close();
              }else if(state === states.loading){
                scope.cancel();
              }
            };

            if(attrs.isCollapsed){
              if(attrs.isCollapsed === 'true' || attrs.isCollapsed === 'false'){
                trackToggle = false;
              }else{
                trackToggle = true;
              }
            }

            if(trackToggle){
              scope.$watch('isCollapsed',function(newVar){
                if(newVar === true){
                  stateClose();
                }else if(newVar === false){
                  stateOpen();
                }
              });
            }

            scope.open = function(){
              if(trackToggle){
                scope.isCollapsed = false;
              }else{
                stateOpen();
              }
            };

            scope.close = function(){
              if(trackToggle){
                scope.isCollapsed = true;
              }else{
                stateClose();
              }
            };

            scope.loading = function(){
              if(trackToggle){
                scope.isCollapsed = false;
              }else{
                stateLoad();
              }
            };

            scope.cancel = function(){
              if(trackToggle){
                scope.isCollapsed = true;
              }else{
                cancelTrans();
              }
            };

            var stateLoad = function(){
              state = states.loading;
              callback('loading',function(){
                if(state === states.loading){
                  stateOpen();
                }
              });
              accordionCtrl.openGroup(element,scope);
            };

            var stateOpen = function(){
              insertContent();
              if((!onFunc && state === states.closed)||(onFunc && state === states.loading)){
                state = states.open;
                accordionCtrl.openGroup(element,scope);
                callback('open');
              }else if(onFunc && state === states.closed){
                stateLoad();
              }
            };

            var stateClose = function(){
              if(state === states.open){
                state = states.closed;
                accordionCtrl.closeGroup(element);
                callback('closed');
              }else if(state === states.canceld){
                cancelTrans();
              }
              $timeout(function(){
                removeContent();
              },1);
            };

            var cancelTrans = function(){
              state = states.closed;
              accordionCtrl.closeGroup(element);
               callback('canceld');
            };

            var callback = function(type,fn){
              if(onFunc){
                scope.onclick(type,fn);
              }
            };
            accordionCtrl.addGroup(scope,element);
          }
        };
      },
    };
  });
})();<|endoftext|>


<commit_before>console.log('This would be the main JS file.');
<commit_msg>main.js<commit_after>console.log('This would be the main JS file.');
 var app = angular.module('Scouting', ['ngMaterial']);
<|endoftext|>


<commit_before>enchant();

window.onload = function() {
  var game_ = new Game(320, 320);
  game_.fps = 24;
  game_.preload('./javascripts/enchant_js-0.8.3/images/start.png', './javascripts/enchant_js-0.8.3/images/gameover.png', './javascripts/enchant_js-0.8.3/images/chara1.png', './images/bg1.png', './images/bg2.png', './javascripts/enchant_js-0.8.3/images/chara6.png');

  game_.onload = function() {
    var createStartScene = function() {
      var scene = new Scene();
      scene.backgroundColor = '#fcc800';

      var startImage = new Sprite(236, 48);
      startImage.image = game_.assets['./javascripts/enchant_js-0.8.3/images/start.png'];
      startImage.x = 42;
      startImage.y = 50;
      scene.addChild(startImage);

      // 説明ラベル設定
      var selectStage1 = new Label('ステージA'); // ラベルを作る
      selectStage1.width = 320;
      selectStage1.textAlign = 'center';                 // 文字を中央寄せ
      selectStage1.color = '#ffffff';                    // 文字を白色に
      selectStage1.x = 0;                                // 横位置調整
      selectStage1.y = 222;                              // 縦位置調整
      selectStage1.font = '14px sans-serif';             // 28pxのゴシック体にする
      scene.addChild(selectStage1);                      // シーンに追加

      selectStage1.addEventListener(Event.TOUCH_START, function(e) {
          game_.replaceScene(createGameScene());
      });

      return scene;
    };

    var createGameScene = function() {
        var scroll = 0;

        var GROUND_LINE = 250;
        var SCROLL_SPEED = 5;

        var scene = new Scene();
        scene.backgroundColor = '#8cc820';

        var bg1 = new Sprite(320, 320);
        bg1.image = game_.assets['./images/bg1.png'];
        bg1.x = 0;
        bg1.y = 0;
        scene.addChild(bg1);

        var bg2 = new Sprite(320, 320);
        bg2.image = game_.assets['./images/bg2.png'];
        bg2.x = 320;
        bg2.y = 0;
        scene.addChild(bg2);

        var charactorPosition = 0;
        var charactor = new Sprite(32, 32);
        charactor.image = game_.assets['./javascripts/enchant_js-0.8.3/images/chara1.png'];
        charactor.x = 80;
        charactor.y = GROUND_LINE - charactor.height;
        scene.addChild(charactor);

        var charactorHit = new Sprite(1, 1);
        charactorHit.x = charactor.x + charactor.width / 2;
        charactorHit.y = charactor.y + charactor.height / 2;
        scene.addChild(charactorHit);

        var answers = [];
        for(var i = 0; i < 3; i++) {
          var answer = new Label('Answer' + (i + 1));
          answer.width = 100;
          answer.height = 32;
          answer.backgroundColor = '#000';
          answer.textAlign = 'center';
          answer.color = '#ffffff';
          answer.x = -answer.width;
          answer.y = 50 + (i * 100);
          answer.font = '14px sans-serif';
          answers.push(answer);
          scene.addChild(answer);
        }

        var enemy = new Sprite(32, 32);
        enemy.image = game_.assets['./javascripts/enchant_js-0.8.3/images/chara6.png'];
        enemy.x = -enemy.width;
        enemy.y = GROUND_LINE - enemy.height;
        scene.addChild(enemy);

        var charactorDead = function() {
          charactor.frame = 3;
          game_.pushScene(createGameoverScene(scroll));
        }

        scene.addEventListener(Event.ENTER_FRAME, function(){
            scroll += SCROLL_SPEED;

            if (scroll % 640 === 0) {
              enemy.x = 320;
              for(var i = 0; i < 3; i++) {
                answers[i].x = 320;
              }
            }

            for(var i = 0; i < 3; i++) {
              if (answers[i].x > -answers[i].width) {
                answers[i].x -= SCROLL_SPEED;
                console.log(answers[i].intersect(charactorHit));
                if (answers[i].intersect(charactorHit)) {
                  charactorDead();
                }
              }
            }

            if (enemy.x > -enemy.width) {
              enemy.x -= SCROLL_SPEED;
              if (enemy.intersect(charactorHit)) {
                charactorDead();
              }
            }

            charactor.frame ++;
            if (charactor.frame > 2) {
              charactor.frame = 0;
            }

            charactorHit.x = charactor.x + charactor.width/2;
            charactorHit.y = charactor.y + charactor.height/2;

            bg1.x -= SCROLL_SPEED;
            bg2.x -= SCROLL_SPEED;

            if (bg1.x <= -320) {
                bg1.x = 320;
            }
            if (bg2.x <= -320) {
                bg2.x = 320;
            }
        });

        scene.addEventListener(Event.TOUCH_START, function(e){
          if (charactorPosition == 0) {
            charactor.tl.moveTo(80, 50, 12);
          }
          else if (charactorPosition == 1) {
            charactor.tl.moveTo(80, 150, 12);
          }
          else if (charactorPosition == 2) {
            charactor.tl.moveTo(80, 250, 12);
          }
          charactorPosition += 1;
          charactorPosition = charactorPosition % 3;
        });

        return scene;
    }

    var createCorrectAnswerScene = function(scroll) {
      var scene = new Scene();
      scene.backgroundColor = 'rgba(0, 0, 0, 0.5)';

      var correctAnswerImage = new Sprite(189, 97);
      correctAnswerImage.image = game_.assets['./javascripts/enchant_js-0.8.3/images/clear.png'];
      correctAnswerImage.x = 66;
      correctAnswerImage.y = 170;
      scene.addChild(correctAnswerImage);

      var retryLabel = new Label('リトライ');
      retryLabel.width = 320;
      retryLabel.textAlign = 'center';
      retryLabel.color = '#ffffff';
      retryLabel.x = 0;
      retryLabel.y = 284;
      retryLabel.font = '24px sans-serif';
      scene.addChild(retryLabel);

      var buttonRetry = new Sprite(320, 32);
      buttonRetry.x = 0;
      buttonRetry.y = 284;
      scene.addChild(buttonRetry);

      buttonRetry.addEventListener(Event.TOUCH_END, function(){
        game_.popScene();
        game_.replaceScene(createStartScene());
      });

      return scene;
    };

    var createGameoverScene = function(scroll) {
      var scene = new Scene();
      scene.backgroundColor = 'rgba(0, 0, 0, 0.5)';

      var gameoverImage = new Sprite(189, 97);
      gameoverImage.image = game_.assets['./javascripts/enchant_js-0.8.3/images/gameover.png'];
      gameoverImage.x = 66;
      gameoverImage.y = 170;
      scene.addChild(gameoverImage);

      var retryLabel = new Label('リトライ');
      retryLabel.width = 320;
      retryLabel.textAlign = 'center';
      retryLabel.color = '#ffffff';
      retryLabel.x = 0;
      retryLabel.y = 284;
      retryLabel.font = '24px sans-serif';
      scene.addChild(retryLabel);

      var buttonRetry = new Sprite(320, 32);
      buttonRetry.x = 0;
      buttonRetry.y = 284;
      scene.addChild(buttonRetry);

      buttonRetry.addEventListener(Event.TOUCH_END, function(){
        game_.popScene();
        game_.replaceScene(createStartScene());
      });

      return scene;
    };

    game_.replaceScene(createStartScene());
  }

  game_.start();
}
<commit_msg>敵キャラを削除<commit_after>enchant();

window.onload = function() {
  var game_ = new Game(320, 320);
  game_.fps = 24;
  game_.preload('./javascripts/enchant_js-0.8.3/images/start.png', './javascripts/enchant_js-0.8.3/images/gameover.png', './javascripts/enchant_js-0.8.3/images/chara1.png', './images/bg1.png', './images/bg2.png', './javascripts/enchant_js-0.8.3/images/chara6.png');

  game_.onload = function() {
    var createStartScene = function() {
      var scene = new Scene();
      scene.backgroundColor = '#fcc800';

      var startImage = new Sprite(236, 48);
      startImage.image = game_.assets['./javascripts/enchant_js-0.8.3/images/start.png'];
      startImage.x = 42;
      startImage.y = 50;
      scene.addChild(startImage);

      // 説明ラベル設定
      var selectStage1 = new Label('ステージA'); // ラベルを作る
      selectStage1.width = 320;
      selectStage1.textAlign = 'center';                 // 文字を中央寄せ
      selectStage1.color = '#ffffff';                    // 文字を白色に
      selectStage1.x = 0;                                // 横位置調整
      selectStage1.y = 222;                              // 縦位置調整
      selectStage1.font = '14px sans-serif';             // 28pxのゴシック体にする
      scene.addChild(selectStage1);                      // シーンに追加

      selectStage1.addEventListener(Event.TOUCH_START, function(e) {
          game_.replaceScene(createGameScene());
      });

      return scene;
    };

    var createGameScene = function() {
        var scroll = 0;

        var GROUND_LINE = 250;
        var SCROLL_SPEED = 5;

        var scene = new Scene();
        scene.backgroundColor = '#8cc820';

        var bg1 = new Sprite(320, 320);
        bg1.image = game_.assets['./images/bg1.png'];
        bg1.x = 0;
        bg1.y = 0;
        scene.addChild(bg1);

        var bg2 = new Sprite(320, 320);
        bg2.image = game_.assets['./images/bg2.png'];
        bg2.x = 320;
        bg2.y = 0;
        scene.addChild(bg2);

        var charactorPosition = 0;
        var charactor = new Sprite(32, 32);
        charactor.image = game_.assets['./javascripts/enchant_js-0.8.3/images/chara1.png'];
        charactor.x = 80;
        charactor.y = GROUND_LINE - charactor.height;
        scene.addChild(charactor);

        var charactorHit = new Sprite(1, 1);
        charactorHit.x = charactor.x + charactor.width / 2;
        charactorHit.y = charactor.y + charactor.height / 2;
        scene.addChild(charactorHit);

        var answers = [];
        for(var i = 0; i < 3; i++) {
          var answer = new Label('Answer' + (i + 1));
          answer.width = 100;
          answer.height = 32;
          answer.backgroundColor = '#000';
          answer.textAlign = 'center';
          answer.color = '#ffffff';
          answer.x = -answer.width;
          answer.y = 50 + (i * 100);
          answer.font = '14px sans-serif';
          answers.push(answer);
          scene.addChild(answer);
        }

        var charactorDead = function() {
          charactor.frame = 3;
          game_.pushScene(createGameoverScene(scroll));
        }

        scene.addEventListener(Event.ENTER_FRAME, function(){
            scroll += SCROLL_SPEED;

            if (scroll % 640 === 0) {
              for(var i = 0; i < 3; i++) {
                answers[i].x = 320;
              }
            }

            for(var i = 0; i < 3; i++) {
              if (answers[i].x > -answers[i].width) {
                answers[i].x -= SCROLL_SPEED;
                console.log(answers[i].intersect(charactorHit));
                if (answers[i].intersect(charactorHit)) {
                  charactorDead();
                }
              }
            }

            charactor.frame ++;
            if (charactor.frame > 2) {
              charactor.frame = 0;
            }

            charactorHit.x = charactor.x + charactor.width/2;
            charactorHit.y = charactor.y + charactor.height/2;

            bg1.x -= SCROLL_SPEED;
            bg2.x -= SCROLL_SPEED;

            if (bg1.x <= -320) {
                bg1.x = 320;
            }
            if (bg2.x <= -320) {
                bg2.x = 320;
            }
        });

        scene.addEventListener(Event.TOUCH_START, function(e){
          if (charactorPosition == 0) {
            charactor.tl.moveTo(80, 50, 12);
          }
          else if (charactorPosition == 1) {
            charactor.tl.moveTo(80, 150, 12);
          }
          else if (charactorPosition == 2) {
            charactor.tl.moveTo(80, 250, 12);
          }
          charactorPosition += 1;
          charactorPosition = charactorPosition % 3;
        });

        return scene;
    }

    var createCorrectAnswerScene = function(scroll) {
      var scene = new Scene();
      scene.backgroundColor = 'rgba(0, 0, 0, 0.5)';

      var correctAnswerImage = new Sprite(189, 97);
      correctAnswerImage.image = game_.assets['./javascripts/enchant_js-0.8.3/images/clear.png'];
      correctAnswerImage.x = 66;
      correctAnswerImage.y = 170;
      scene.addChild(correctAnswerImage);

      var retryLabel = new Label('リトライ');
      retryLabel.width = 320;
      retryLabel.textAlign = 'center';
      retryLabel.color = '#ffffff';
      retryLabel.x = 0;
      retryLabel.y = 284;
      retryLabel.font = '24px sans-serif';
      scene.addChild(retryLabel);

      var buttonRetry = new Sprite(320, 32);
      buttonRetry.x = 0;
      buttonRetry.y = 284;
      scene.addChild(buttonRetry);

      buttonRetry.addEventListener(Event.TOUCH_END, function(){
        game_.popScene();
        game_.replaceScene(createStartScene());
      });

      return scene;
    };

    var createGameoverScene = function(scroll) {
      var scene = new Scene();
      scene.backgroundColor = 'rgba(0, 0, 0, 0.5)';

      var gameoverImage = new Sprite(189, 97);
      gameoverImage.image = game_.assets['./javascripts/enchant_js-0.8.3/images/gameover.png'];
      gameoverImage.x = 66;
      gameoverImage.y = 170;
      scene.addChild(gameoverImage);

      var retryLabel = new Label('リトライ');
      retryLabel.width = 320;
      retryLabel.textAlign = 'center';
      retryLabel.color = '#ffffff';
      retryLabel.x = 0;
      retryLabel.y = 284;
      retryLabel.font = '24px sans-serif';
      scene.addChild(retryLabel);

      var buttonRetry = new Sprite(320, 32);
      buttonRetry.x = 0;
      buttonRetry.y = 284;
      scene.addChild(buttonRetry);

      buttonRetry.addEventListener(Event.TOUCH_END, function(){
        game_.popScene();
        game_.replaceScene(createStartScene());
      });

      return scene;
    };

    game_.replaceScene(createStartScene());
  }

  game_.start();
}
<|endoftext|>


<commit_before>import { inject } from 'aurelia-framework';
import { Router } from 'aurelia-router';
import { EventAggregator } from 'aurelia-event-aggregator';
import StatisticsService from 'resources/services/statistics-service';
import TranslationService from 'resources/services/translation-service';

@inject(Router, EventAggregator, StatisticsService, TranslationService)
export class Statistics {
  constructor(router, eventAggregator, statisticsService, translationService) {
    this.router = router;
    this.eventAggregator = eventAggregator;
    this.statisticsService = statisticsService;
    this.translationService = translationService;
    this.general = {
      reported: 0,
      resolved: 0
    };
    this.reporters = [];
    this.resolvers = [];
    this.categories = [];
    this.tags = [];
    this.query = {
      page: 1,
      results: 10
    };
  }

  async activate() {
    await this.browseGeneralStatistics();
    await this.browseReporters();
    await this.browseResolvers();
    await this.browseCategories();
    await this.browseTags();
  }

  async attached() {
    this.remarkCreatedSubscription = await this.subscribeRemarkCreated();
    this.remarkResolvedSubscription = await this.subscribeRemarkResolved();
    this.remarkDeletedSubscription = await this.subscribeRemarkDeleted();
  }

  detached() {
    this.remarkCreatedSubscription.dispose();
    this.remarkResolvedSubscription.dispose();
    this.remarkDeletedSubscription.dispose();
  }

  async browseGeneralStatistics() {
    let generalStats = await this.statisticsService.getGeneralStatistics();
    this.general = [
      { name: this.translationService.trCapitalized('remark.state_new'), count: generalStats.newCount},
      { name: this.translationService.trCapitalized('remark.state_reported'), count: generalStats.reportedCount},
      { name: this.translationService.trCapitalized('remark.state_processing'), count: generalStats.rrocessingCount},
      { name: this.translationService.trCapitalized('remark.state_resolved'), count: generalStats.resolvedCount},
      { name: this.translationService.trCapitalized('remark.state_canceled'), count: generalStats.canceledCount},
      { name: this.translationService.trCapitalized('remark.state_deleted'), count: generalStats.deletedCount},
      { name: this.translationService.trCapitalized('remark.state_renewed'), count: generalStats.renewedCount}
    ];
  }

  async browseReporters() {
    let reporters = await this.statisticsService.browseUserStatistics({
      page: 1,
      results: 10,
      orderBy: 'reported'
    });
    reporters.forEach(x => {
      if (x.name) {
        x.count = x.remarks.reportedCount;
        x.url = this.router.generate('user-remarks', {username: x.name});
      }
    });
    this.reporters = reporters;
  }

  async browseResolvers() {
    let resolvers = await this.statisticsService.browseUserStatistics({
      page: 1,
      results: 10,
      orderBy: 'resolved'
    });
    resolvers.forEach(x => {
      if (x.name) {
        x.count = x.remarks.resolvedCount;
        x.url = this.router.generate('user-resolved-remarks', {resolver: x.name});
      }
    });
    this.resolvers = resolvers;
  }

  async browseCategories() {
    let categories = await this.statisticsService.browseCategoriesStatistics({
      page: 1,
      results: 5
    });
    categories.forEach(x => {
      x.url = this.router.generate('category-remarks', {category: x.name});
      x.name = this.translationService.tr(`remark.category_${x.name}`);
      x.count = x.remarks.reportedCount;
    });
    this.categories = categories;
  }

  async browseTags() {
    let tags = await this.statisticsService.browseTagsStatistics({
      page: 1,
      results: 5
    });
    tags.forEach(x => {
      x.url = this.router.generate('tag-remarks', {tag: x.name});
      x.name = this.translationService.trCapitalized(`tags.${x.name}`);
      x.count = x.remarks.reportedCount;
    });
    this.tags = tags;
  }

  async subscribeRemarkCreated() {
    return await this.eventAggregator
      .subscribe('remark:created', async message => {
        this.general.reported++;
        let index = this.reporters.findIndex(x => x.name === message.author);
        if (index >= 0) {
          this.reporters[index].count++;
        }
      });
  }

  async subscribeRemarkResolved() {
    return await this.eventAggregator
      .subscribe('remark:resolved', async message => {
        this.general.resolved++;
        let index = this.resolvers.findIndex(x => x.name === message.author);
        if (index >= 0) {
          this.resolvers[index].count++;
        }
      });
  }

  async subscribeRemarkDeleted() {
    return await this.eventAggregator
      .subscribe('remark:deleted', async message => {
        this.general.reported--;
      });
  }
}
<commit_msg>typo in statistics js<commit_after>import { inject } from 'aurelia-framework';
import { Router } from 'aurelia-router';
import { EventAggregator } from 'aurelia-event-aggregator';
import StatisticsService from 'resources/services/statistics-service';
import TranslationService from 'resources/services/translation-service';

@inject(Router, EventAggregator, StatisticsService, TranslationService)
export class Statistics {
  constructor(router, eventAggregator, statisticsService, translationService) {
    this.router = router;
    this.eventAggregator = eventAggregator;
    this.statisticsService = statisticsService;
    this.translationService = translationService;
    this.general = {
      reported: 0,
      resolved: 0
    };
    this.reporters = [];
    this.resolvers = [];
    this.categories = [];
    this.tags = [];
    this.query = {
      page: 1,
      results: 10
    };
  }

  async activate() {
    await this.browseGeneralStatistics();
    await this.browseReporters();
    await this.browseResolvers();
    await this.browseCategories();
    await this.browseTags();
  }

  async attached() {
    this.remarkCreatedSubscription = await this.subscribeRemarkCreated();
    this.remarkResolvedSubscription = await this.subscribeRemarkResolved();
    this.remarkDeletedSubscription = await this.subscribeRemarkDeleted();
  }

  detached() {
    this.remarkCreatedSubscription.dispose();
    this.remarkResolvedSubscription.dispose();
    this.remarkDeletedSubscription.dispose();
  }

  async browseGeneralStatistics() {
    let generalStats = await this.statisticsService.getGeneralStatistics();
    this.general = [
      { name: this.translationService.trCapitalized('remark.state_new'), count: generalStats.newCount},
      { name: this.translationService.trCapitalized('remark.state_reported'), count: generalStats.reportedCount},
      { name: this.translationService.trCapitalized('remark.state_processing'), count: generalStats.processingCount},
      { name: this.translationService.trCapitalized('remark.state_resolved'), count: generalStats.resolvedCount},
      { name: this.translationService.trCapitalized('remark.state_canceled'), count: generalStats.canceledCount},
      { name: this.translationService.trCapitalized('remark.state_deleted'), count: generalStats.deletedCount},
      { name: this.translationService.trCapitalized('remark.state_renewed'), count: generalStats.renewedCount}
    ];
  }

  async browseReporters() {
    let reporters = await this.statisticsService.browseUserStatistics({
      page: 1,
      results: 10,
      orderBy: 'reported'
    });
    reporters.forEach(x => {
      if (x.name) {
        x.count = x.remarks.reportedCount;
        x.url = this.router.generate('user-remarks', {username: x.name});
      }
    });
    this.reporters = reporters;
  }

  async browseResolvers() {
    let resolvers = await this.statisticsService.browseUserStatistics({
      page: 1,
      results: 10,
      orderBy: 'resolved'
    });
    resolvers.forEach(x => {
      if (x.name) {
        x.count = x.remarks.resolvedCount;
        x.url = this.router.generate('user-resolved-remarks', {resolver: x.name});
      }
    });
    this.resolvers = resolvers;
  }

  async browseCategories() {
    let categories = await this.statisticsService.browseCategoriesStatistics({
      page: 1,
      results: 5
    });
    categories.forEach(x => {
      x.url = this.router.generate('category-remarks', {category: x.name});
      x.name = this.translationService.tr(`remark.category_${x.name}`);
      x.count = x.remarks.reportedCount;
    });
    this.categories = categories;
  }

  async browseTags() {
    let tags = await this.statisticsService.browseTagsStatistics({
      page: 1,
      results: 5
    });
    tags.forEach(x => {
      x.url = this.router.generate('tag-remarks', {tag: x.name});
      x.name = this.translationService.trCapitalized(`tags.${x.name}`);
      x.count = x.remarks.reportedCount;
    });
    this.tags = tags;
  }

  async subscribeRemarkCreated() {
    return await this.eventAggregator
      .subscribe('remark:created', async message => {
        this.general.reported++;
        let index = this.reporters.findIndex(x => x.name === message.author);
        if (index >= 0) {
          this.reporters[index].count++;
        }
      });
  }

  async subscribeRemarkResolved() {
    return await this.eventAggregator
      .subscribe('remark:resolved', async message => {
        this.general.resolved++;
        let index = this.resolvers.findIndex(x => x.name === message.author);
        if (index >= 0) {
          this.resolvers[index].count++;
        }
      });
  }

  async subscribeRemarkDeleted() {
    return await this.eventAggregator
      .subscribe('remark:deleted', async message => {
        this.general.reported--;
      });
  }
}
<|endoftext|>


<commit_before>var map;
var directionsService;
var bikingDirectionsDisplay;
var walkingDirectionsDisplay;

var cities = [
    { name: "Montréal",
      url: "bikeStations-montreal.xml"
    },
    { name: "Toronto",
      url: "bikeStations-toronto.xml"
    },
    { name: "Ottawa",
      url: "bikeStations-capital.xml"
    }
];

var bixiStations = [];
var locationMarker;

function updateCity(cityIndex) {
    var imageCanvas = document.createElement("canvas");
    var geocoder = new google.maps.Geocoder();

    var bixiBounds = new google.maps.LatLngBounds();
    var infoWindow = new google.maps.InfoWindow();
    google.maps.event.addListener(map, 'click', function() {
	infoWindow.close();
    });

    $.ajax({
        type: "GET",
	url: cities[cityIndex].url,
	dataType: "xml",
	success: function(xml) {
	    $(xml).find('station').each(function() {
		var station = {};
		station.name = $(this).find('name').text();
		station.id = $(this).find('terminalName').text();
		station.numBikes = parseInt($(this).find('nbBikes').text());
		station.numEmptyDocks = parseInt($(this).find('nbEmptyDocks').text());
		station.latlng = new google.maps.LatLng(parseFloat($(this).find('lat').text()),
							parseFloat($(this).find('long').text()));
		bixiStations[bixiStations.length] = station;

		function createIcon(numBikes, numEmptyDocks) {
		    var radius;
		    var alpha;
		    if (numBikes == 0 && numEmptyDocks == 0) {
			// edge case: station with nothing in it
			radius = 5;
			alpha = 0.0;
		    } else {
			radius = (numBikes+numEmptyDocks)/2;
			if (radius > 20) {
			    radius = 20;
			} else if (radius < 5) {
			    radius = 8;
			}

			alpha = numBikes / (numBikes+numEmptyDocks);
			if (alpha > 0.5 && alpha < 1.0) {
			    alpha=0.5;
			} else if (alpha < 0.1 && alpha > 0.0) {
			    alpha = 0.1;
			}
		    }
		    imageCanvas.width = radius*2+4;
		    imageCanvas.height = radius*2+4;
		    var context = imageCanvas.getContext("2d");

		    context.clearRect(0,0,radius*2, radius*2);

		    context.fillStyle = "rgba(255,0,0," + alpha + ")";
		    context.beginPath();
		    context.arc(imageCanvas.width/2,imageCanvas.height/2,radius,0,Math.PI*2,true);
		    context.fill();

		    context.strokeStyle = "#f00";
		    context.beginPath();
		    context.arc(imageCanvas.width/2,imageCanvas.height/2,radius,0,Math.PI*2,true);
		    context.stroke();

		    return imageCanvas.toDataURL();

		}
		var iconURL = createIcon(station.numBikes, station.numEmptyDocks);

		bixiBounds.extend(station.latlng);

		station.marker = new google.maps.Marker({
		    position: station.latlng,
		    map: map,
		    icon: iconURL
		});

		google.maps.event.addListener(station.marker, 'click', function() {
		    infoWindow.setContent("" + station.name +
					  "
Bikes: " +
					  station.numBikes + ' - ' +
					  "Docks: " + station.numEmptyDocks);
		    infoWindow.open(map, station.marker);
		});

	    });

	    $("form#nearby-form").submit(function() {
		$("#city-hint-widget").hide();

		$("#nearby-input").blur();
		walkingDirectionsDisplay.setMap(null);

		var oldFindButtonVal = $("#find-nearby-button").val();

		$("#find-nearby-button").attr('disabled', 'disabled');
		$("#find-nearby-button").val("Working...");
		geocoder.geocode( {'address': $("#nearby-input").val(), 'bounds': bixiBounds }, function(results, status) {
		    $("#find-nearby-button").val(oldFindButtonVal);
		    $("#find-nearby-button").removeAttr('disabled');

		    var nearbyHTML = "";

		    if (results.length > 0) {
			var latlng = results[0].geometry.location;
			locationMarker.setPosition(latlng);
			locationMarker.setVisible(true);
			map.setCenter(latlng);
			map.setZoom(15);

			var nearby_stations = bixiStations.map(function(station) {
			    var distance = google.maps.geometry.spherical.computeDistanceBetween(latlng,
												 station.latlng);
			    return jQuery.extend({ distance: parseInt(distance) }, station);
			}).filter(function(station) {
			    return station.distance < 1000;
			}).sort(function(station1, station2) {
			    return station1.distance > station2.distance;
			}).slice(0,5);

			$('#nearby-content').replaceWith(ich.stations({
			    location: results[0].formatted_address,
			    stations: nearby_stations }));

			nearby_stations.map(function(station) {
			    $('#station-' + station.id).hover(function() {
				$(this).addClass("nearby-station-hover");
			    }, function() {
				$(this).removeClass("nearby-station-hover");
			    }).click(function() {
				$('.nearby-station').removeClass("nearby-station-selected");
				$(this).addClass("nearby-station-selected");
				directionsService.route({
				    origin: latlng,
				    destination: station.latlng,
				    travelMode: google.maps.DirectionsTravelMode.WALKING
				}, function(response, status) {
				    if (status == google.maps.DirectionsStatus.OK) {
					locationMarker.setVisible(false);
					walkingDirectionsDisplay.setDirections(response);
					walkingDirectionsDisplay.setMap(map);
				    } else {
					console.log("Error processing directions to station!");
				    }
				});

			    });

			    // Create pie chart

			    if (station.numBikes == 0 && station.numEmptyDocks == 0) {
				return;
			    }
			    console.log(station.numBikes + "-" + station.numEmptyDocks);
			    var data = {
				items: []
			    };

			    if (station.numBikes > 0) {
				data.items[data.items.length] = {label: 'bikes', data: station.numBikes };
			    } else {
			    }
			    if (station.numEmptyDocks > 0) {
				data.items[data.items.length] = {label: 'stations', data: station.numEmptyDocks };
			    }

			    var pieChart = new Bluff.Pie('station-graph-' + station.id, '40x40');
			    if (station.numBikes == 0) {
				pieChart.set_theme({
				    colors: ['#fbb' ],
				    background_colors: ['rgba(0,0,0,0)', 'rgba(0,0,0,0)']
				});
			    } else {
				pieChart.set_theme({
				    colors: ['#f00', '#fbb' ],
				    background_colors: ['rgba(0,0,0,0)', 'rgba(0,0,0,0)']
				});
			    }

			    pieChart.hide_labels_less_than = 100;
			    pieChart.hide_legend = true;
			    for (i in data.items) {
				var item = data.items[i];
				pieChart.data(item.label, item.data);
			    }

			    pieChart.draw();
			});
		    }
		});

		return false;
	    });

	    var autocomplete = {
		//This bit uses the geocoder to fetch address values
		source: function(request, response) {
		    geocoder.geocode( {'address': request.term, 'bounds': bixiBounds }, function(results, status) {
			response($.map(results, function(item) {
			    return {
				label: item.formatted_address,
				value: item.formatted_address,
			    }
			}));
		    })},
	    }

	    $("#nearby-input").autocomplete(autocomplete);
	    $("#from-input").autocomplete(autocomplete);
	    $("#to-input").autocomplete(autocomplete);

	    $('#map_canvas').show();
	    google.maps.event.trigger(map, "resize");
	    map.fitBounds(bixiBounds);
	}
    });
}

function init() {

    for (var i in cities) {
	$("#city-selector").append('<option value= ' + i + '>' + cities[i].name + '</option>');
    }

    map = new google.maps.Map(document.getElementById("map_canvas"), {
	zoom: 1,
	mapTypeId: google.maps.MapTypeId.ROADMAP,
	center: new google.maps.LatLng(45.64, -73.4)
    });
    var bikeLayer = new google.maps.BicyclingLayer();
    bikeLayer.setMap(map);

    $('body').layout({ defaults: { spacing_open: 0 },
                       applyDefaultStyles: true,
                       west: { size: 320 },
                       north: { innerHeight: 30 },
                       center: { onresize_end: function () { google.maps.event.trigger(map, "resize"); } } });

    bikingDirectionsDisplay = new google.maps.DirectionsRenderer({ map: map });
    walkingDirectionsDisplay = new google.maps.DirectionsRenderer({ markerOptions: {
	zIndex: google.maps.Marker.MAX_ZINDEX } });
    directionsService = new google.maps.DirectionsService();

    locationMarker = new google.maps.Marker({
	map: map,
	visible: false
    });

    $("#find-nearby-button").button();
    $("#plan-button").button();
    $("form#directions-form").submit(function() {
	$('#error-widget').hide();
	$('#from-input').blur();
	$('#to-input').blur();

	var oldPlanButtonVal = $("#plan-button").val();

	$("#plan-button").attr('disabled', 'disabled');
	$("#plan-button").val("Working...");

	var request = {
            origin: $("#from-input").val(),
            destination: $("#to-input").val(),
            travelMode: google.maps.DirectionsTravelMode.BICYCLING
	};

	directionsService.route(request, function(response, status) {
	    $("#plan-button").removeAttr('disabled');
	    $("#plan-button").val(oldPlanButtonVal);

	    if (status == google.maps.DirectionsStatus.OK) {
		bikingDirectionsDisplay.setDirections(response);
	    } else {
		$('#error-widget').show();
		console.log("Error processing directions!");
	    }
	});

	return false;
    });

    $("#tab-selector").buttonset();
    $('input#trip-planner').click(function()  {
	$('div#nearby-panel').hide();
	$('div#trip-planner-panel').show();
    });

    $('input#find-nearby').click(function() {
	$('div#nearby-panel').show();
	$('div#trip-planner-panel').hide();
    });

    $("#reverse-button").button({
	text: false,
	icons: {
	    primary: 'ui-icon-shuffle'
	}
    }).click(function() {
	var fromval = $("#from-input").val();
	$("#from-input").val($("#to-input").val());
	$("#to-input").val(fromval);
    });

    $("#city-selector").change(function() {
	$("#city-hint-widget").hide();

	var cityIndex = $("#city-selector").val();
	updateCity(cityIndex);
	localStorage["defaultCityIndex"] = cityIndex;
    });

    // get city preference from user and update to it. you might think it
    // would make sense to use geolocation for this, but that's actually
    // kinda slow at least with Firefox
    var cityIndex = localStorage["defaultCityIndex"];
    if (cityIndex == undefined) {
	cityIndex = 0;
	$("#city-hint-widget").show();
    }
    $("select#city-selector option[value=" + cityIndex + "]").attr("selected",
								   "selected");

    updateCity(parseInt(cityIndex)); // set city to default
}
<commit_msg>Remove unused variable<commit_after>var map;
var directionsService;
var bikingDirectionsDisplay;
var walkingDirectionsDisplay;

var cities = [
    { name: "Montréal",
      url: "bikeStations-montreal.xml"
    },
    { name: "Toronto",
      url: "bikeStations-toronto.xml"
    },
    { name: "Ottawa",
      url: "bikeStations-capital.xml"
    }
];

var bixiStations = [];
var locationMarker;

function updateCity(cityIndex) {
    var imageCanvas = document.createElement("canvas");
    var geocoder = new google.maps.Geocoder();

    var bixiBounds = new google.maps.LatLngBounds();
    var infoWindow = new google.maps.InfoWindow();
    google.maps.event.addListener(map, 'click', function() {
	infoWindow.close();
    });

    $.ajax({
        type: "GET",
	url: cities[cityIndex].url,
	dataType: "xml",
	success: function(xml) {
	    $(xml).find('station').each(function() {
		var station = {};
		station.name = $(this).find('name').text();
		station.id = $(this).find('terminalName').text();
		station.numBikes = parseInt($(this).find('nbBikes').text());
		station.numEmptyDocks = parseInt($(this).find('nbEmptyDocks').text());
		station.latlng = new google.maps.LatLng(parseFloat($(this).find('lat').text()),
							parseFloat($(this).find('long').text()));
		bixiStations[bixiStations.length] = station;

		function createIcon(numBikes, numEmptyDocks) {
		    var radius;
		    var alpha;
		    if (numBikes == 0 && numEmptyDocks == 0) {
			// edge case: station with nothing in it
			radius = 5;
			alpha = 0.0;
		    } else {
			radius = (numBikes+numEmptyDocks)/2;
			if (radius > 20) {
			    radius = 20;
			} else if (radius < 5) {
			    radius = 8;
			}

			alpha = numBikes / (numBikes+numEmptyDocks);
			if (alpha > 0.5 && alpha < 1.0) {
			    alpha=0.5;
			} else if (alpha < 0.1 && alpha > 0.0) {
			    alpha = 0.1;
			}
		    }
		    imageCanvas.width = radius*2+4;
		    imageCanvas.height = radius*2+4;
		    var context = imageCanvas.getContext("2d");

		    context.clearRect(0,0,radius*2, radius*2);

		    context.fillStyle = "rgba(255,0,0," + alpha + ")";
		    context.beginPath();
		    context.arc(imageCanvas.width/2,imageCanvas.height/2,radius,0,Math.PI*2,true);
		    context.fill();

		    context.strokeStyle = "#f00";
		    context.beginPath();
		    context.arc(imageCanvas.width/2,imageCanvas.height/2,radius,0,Math.PI*2,true);
		    context.stroke();

		    return imageCanvas.toDataURL();

		}
		var iconURL = createIcon(station.numBikes, station.numEmptyDocks);

		bixiBounds.extend(station.latlng);

		station.marker = new google.maps.Marker({
		    position: station.latlng,
		    map: map,
		    icon: iconURL
		});

		google.maps.event.addListener(station.marker, 'click', function() {
		    infoWindow.setContent("" + station.name +
					  "
Bikes: " +
					  station.numBikes + ' - ' +
					  "Docks: " + station.numEmptyDocks);
		    infoWindow.open(map, station.marker);
		});

	    });

	    $("form#nearby-form").submit(function() {
		$("#city-hint-widget").hide();

		$("#nearby-input").blur();
		walkingDirectionsDisplay.setMap(null);

		var oldFindButtonVal = $("#find-nearby-button").val();

		$("#find-nearby-button").attr('disabled', 'disabled');
		$("#find-nearby-button").val("Working...");
		geocoder.geocode( {'address': $("#nearby-input").val(), 'bounds': bixiBounds }, function(results, status) {
		    $("#find-nearby-button").val(oldFindButtonVal);
		    $("#find-nearby-button").removeAttr('disabled');

		    if (results.length > 0) {
			var latlng = results[0].geometry.location;
			locationMarker.setPosition(latlng);
			locationMarker.setVisible(true);
			map.setCenter(latlng);
			map.setZoom(15);

			var nearby_stations = bixiStations.map(function(station) {
			    var distance = google.maps.geometry.spherical.computeDistanceBetween(latlng,
												 station.latlng);
			    return jQuery.extend({ distance: parseInt(distance) }, station);
			}).filter(function(station) {
			    return station.distance < 1000;
			}).sort(function(station1, station2) {
			    return station1.distance > station2.distance;
			}).slice(0,5);

			$('#nearby-content').replaceWith(ich.stations({
			    location: results[0].formatted_address,
			    stations: nearby_stations }));

			nearby_stations.map(function(station) {
			    $('#station-' + station.id).hover(function() {
				$(this).addClass("nearby-station-hover");
			    }, function() {
				$(this).removeClass("nearby-station-hover");
			    }).click(function() {
				$('.nearby-station').removeClass("nearby-station-selected");
				$(this).addClass("nearby-station-selected");
				directionsService.route({
				    origin: latlng,
				    destination: station.latlng,
				    travelMode: google.maps.DirectionsTravelMode.WALKING
				}, function(response, status) {
				    if (status == google.maps.DirectionsStatus.OK) {
					locationMarker.setVisible(false);
					walkingDirectionsDisplay.setDirections(response);
					walkingDirectionsDisplay.setMap(map);
				    } else {
					console.log("Error processing directions to station!");
				    }
				});

			    });

			    // Create pie chart

			    if (station.numBikes == 0 && station.numEmptyDocks == 0) {
				return;
			    }
			    console.log(station.numBikes + "-" + station.numEmptyDocks);
			    var data = {
				items: []
			    };

			    if (station.numBikes > 0) {
				data.items[data.items.length] = {label: 'bikes', data: station.numBikes };
			    } else {
			    }
			    if (station.numEmptyDocks > 0) {
				data.items[data.items.length] = {label: 'stations', data: station.numEmptyDocks };
			    }

			    var pieChart = new Bluff.Pie('station-graph-' + station.id, '40x40');
			    if (station.numBikes == 0) {
				pieChart.set_theme({
				    colors: ['#fbb' ],
				    background_colors: ['rgba(0,0,0,0)', 'rgba(0,0,0,0)']
				});
			    } else {
				pieChart.set_theme({
				    colors: ['#f00', '#fbb' ],
				    background_colors: ['rgba(0,0,0,0)', 'rgba(0,0,0,0)']
				});
			    }

			    pieChart.hide_labels_less_than = 100;
			    pieChart.hide_legend = true;
			    for (i in data.items) {
				var item = data.items[i];
				pieChart.data(item.label, item.data);
			    }

			    pieChart.draw();
			});
		    }
		});

		return false;
	    });

	    var autocomplete = {
		//This bit uses the geocoder to fetch address values
		source: function(request, response) {
		    geocoder.geocode( {'address': request.term, 'bounds': bixiBounds }, function(results, status) {
			response($.map(results, function(item) {
			    return {
				label: item.formatted_address,
				value: item.formatted_address,
			    }
			}));
		    })},
	    }

	    $("#nearby-input").autocomplete(autocomplete);
	    $("#from-input").autocomplete(autocomplete);
	    $("#to-input").autocomplete(autocomplete);

	    $('#map_canvas').show();
	    google.maps.event.trigger(map, "resize");
	    map.fitBounds(bixiBounds);
	}
    });
}

function init() {

    for (var i in cities) {
	$("#city-selector").append('<option value= ' + i + '>' + cities[i].name + '</option>');
    }

    map = new google.maps.Map(document.getElementById("map_canvas"), {
	zoom: 1,
	mapTypeId: google.maps.MapTypeId.ROADMAP,
	center: new google.maps.LatLng(45.64, -73.4)
    });
    var bikeLayer = new google.maps.BicyclingLayer();
    bikeLayer.setMap(map);

    $('body').layout({ defaults: { spacing_open: 0 },
                       applyDefaultStyles: true,
                       west: { size: 320 },
                       north: { innerHeight: 30 },
                       center: { onresize_end: function () { google.maps.event.trigger(map, "resize"); } } });

    bikingDirectionsDisplay = new google.maps.DirectionsRenderer({ map: map });
    walkingDirectionsDisplay = new google.maps.DirectionsRenderer({ markerOptions: {
	zIndex: google.maps.Marker.MAX_ZINDEX } });
    directionsService = new google.maps.DirectionsService();

    locationMarker = new google.maps.Marker({
	map: map,
	visible: false
    });

    $("#find-nearby-button").button();
    $("#plan-button").button();
    $("form#directions-form").submit(function() {
	$('#error-widget').hide();
	$('#from-input').blur();
	$('#to-input').blur();

	var oldPlanButtonVal = $("#plan-button").val();

	$("#plan-button").attr('disabled', 'disabled');
	$("#plan-button").val("Working...");

	var request = {
            origin: $("#from-input").val(),
            destination: $("#to-input").val(),
            travelMode: google.maps.DirectionsTravelMode.BICYCLING
	};

	directionsService.route(request, function(response, status) {
	    $("#plan-button").removeAttr('disabled');
	    $("#plan-button").val(oldPlanButtonVal);

	    if (status == google.maps.DirectionsStatus.OK) {
		bikingDirectionsDisplay.setDirections(response);
	    } else {
		$('#error-widget').show();
		console.log("Error processing directions!");
	    }
	});

	return false;
    });

    $("#tab-selector").buttonset();
    $('input#trip-planner').click(function()  {
	$('div#nearby-panel').hide();
	$('div#trip-planner-panel').show();
    });

    $('input#find-nearby').click(function() {
	$('div#nearby-panel').show();
	$('div#trip-planner-panel').hide();
    });

    $("#reverse-button").button({
	text: false,
	icons: {
	    primary: 'ui-icon-shuffle'
	}
    }).click(function() {
	var fromval = $("#from-input").val();
	$("#from-input").val($("#to-input").val());
	$("#to-input").val(fromval);
    });

    $("#city-selector").change(function() {
	$("#city-hint-widget").hide();

	var cityIndex = $("#city-selector").val();
	updateCity(cityIndex);
	localStorage["defaultCityIndex"] = cityIndex;
    });

    // get city preference from user and update to it. you might think it
    // would make sense to use geolocation for this, but that's actually
    // kinda slow at least with Firefox
    var cityIndex = localStorage["defaultCityIndex"];
    if (cityIndex == undefined) {
	cityIndex = 0;
	$("#city-hint-widget").show();
    }
    $("select#city-selector option[value=" + cityIndex + "]").attr("selected",
								   "selected");

    updateCity(parseInt(cityIndex)); // set city to default
}
<|endoftext|>


<commit_before>process.env.TEST_ENV = true
const config = require('../../../config.js')
const Format = require('../../../structs/db/Format.js')
const mongoose = require('mongoose')
const dbName = 'test_int_base'
const CON_OPTIONS = {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useCreateIndex: true
}

jest.mock('../../../config.js')

describe('Int::structs/db/Format', function () {
  /** @type {import('mongoose').Collection} */
  let collection
  let feedID = new mongoose.Types.ObjectId()
  beforeAll(async function () {
    config.database.uri = 'mongodb://'
    await mongoose.connect(`mongodb://localhost:27017/${dbName}`, CON_OPTIONS)
    await mongoose.connection.db.dropDatabase()
    collection = mongoose.connection.db.collection('formats')
    await mongoose.connection.db.collection('feeds').insertOne({
      _id: feedID
    })
  })
  describe('pruneEmbeds', function () {

    it('works with fields', function () {
      const embeds = [{
        fields: [{}, {
          name: 'abc'
        }, {
          name: 'foo',
          value: 'bar'
        }]
      }]
      const format = new Format({
        feed: feedID.toHexString(),
        embeds
      })
      format.pruneEmbeds()
      expect(format.embeds).toEqual([{
        fields: [{
          name: 'foo',
          value: 'bar'
        }]
      }])
    })
    it('works with non-field', function () {
      const embeds = [{}, {
        title: 'dodat'
      }, {}]
      const format = new Format({
        feed: feedID.toHexString(),
        embeds
      })
      format.pruneEmbeds()
      expect(format.embeds).toEqual([{
        title: 'dodat'
      }])
    })
  })
  it('does not save embeds with _id', async function () {
    const embeds = [{
      title: 'jack'
    }]
    const data = {
      feed: feedID.toHexString(),
      text: 'no _id',
      embeds
    }
    const format = new Format(data)
    await format.save()
    const result = await collection.findOne({
      text: data.text
    })
    expect(result.embeds[0]).not.toHaveProperty('_id')
  })
  it('does not save embed fields with _id', async function () {
    const embeds = [{
      title: 'jack',
      fields: [{
        name: 'abas',
        value: 'w46ye5t'
      }]
    }]
    const data = {
      feed: feedID.toHexString(),
      text: 'no _id in fields',
      embeds
    }
    const format = new Format(data)
    await format.save()
    const result = await collection.findOne({
      text: data.text
    })
    expect(result.embeds[0].fields[0]).not.toHaveProperty('_id')
  })
  afterAll(async function () {
    await mongoose.connection.db.dropDatabase()
    await mongoose.connection.close()
  })
})
<commit_msg>Fix lint<commit_after>process.env.TEST_ENV = true
const config = require('../../../config.js')
const Format = require('../../../structs/db/Format.js')
const mongoose = require('mongoose')
const dbName = 'test_int_base'
const CON_OPTIONS = {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useCreateIndex: true
}

jest.mock('../../../config.js')

describe('Int::structs/db/Format', function () {
  /** @type {import('mongoose').Collection} */
  let collection
  let feedID = new mongoose.Types.ObjectId()
  beforeAll(async function () {
    config.database.uri = 'mongodb://'
    await mongoose.connect(`mongodb://localhost:27017/${dbName}`, CON_OPTIONS)
    await mongoose.connection.db.dropDatabase()
    collection = mongoose.connection.db.collection('formats')
    await mongoose.connection.db.collection('feeds').insertOne({
      _id: feedID
    })
  })
  describe('pruneEmbeds', function () {
    it('works with fields', function () {
      const embeds = [{
        fields: [{}, {
          name: 'abc'
        }, {
          name: 'foo',
          value: 'bar'
        }]
      }]
      const format = new Format({
        feed: feedID.toHexString(),
        embeds
      })
      format.pruneEmbeds()
      expect(format.embeds).toEqual([{
        fields: [{
          name: 'foo',
          value: 'bar'
        }]
      }])
    })
    it('works with non-field', function () {
      const embeds = [{}, {
        title: 'dodat'
      }, {}]
      const format = new Format({
        feed: feedID.toHexString(),
        embeds
      })
      format.pruneEmbeds()
      expect(format.embeds).toEqual([{
        title: 'dodat'
      }])
    })
  })
  it('does not save embeds with _id', async function () {
    const embeds = [{
      title: 'jack'
    }]
    const data = {
      feed: feedID.toHexString(),
      text: 'no _id',
      embeds
    }
    const format = new Format(data)
    await format.save()
    const result = await collection.findOne({
      text: data.text
    })
    expect(result.embeds[0]).not.toHaveProperty('_id')
  })
  it('does not save embed fields with _id', async function () {
    const embeds = [{
      title: 'jack',
      fields: [{
        name: 'abas',
        value: 'w46ye5t'
      }]
    }]
    const data = {
      feed: feedID.toHexString(),
      text: 'no _id in fields',
      embeds
    }
    const format = new Format(data)
    await format.save()
    const result = await collection.findOne({
      text: data.text
    })
    expect(result.embeds[0].fields[0]).not.toHaveProperty('_id')
  })
  afterAll(async function () {
    await mongoose.connection.db.dropDatabase()
    await mongoose.connection.close()
  })
})
<|endoftext|>


<commit_before>import nameStep from './your-name';
import genderStep from './gender';
import dateOfBirthStep from './date-of-birth';
import homeAddressLookupStep from './home-address-lookup';
import contactDetailsStep from './contact-details';
import previouslyArmedStep from './previously-armed';
import previouslyRegisteredStep from './previously-registered';
import currentGP from './current-gp';
import enterGpAddress from './current-gp-address';
import registeredAddressStep from './registered-address';
import registeredAddressDetailsStep from './registered-address-details';
import registeredNameStep from './registered-name';
import registeredNameDetailsStep from './registered-name-details';
import nhsNumberStep from './nhs-number';
import currentMedicationStep from './current-medication';
import allergiesStep from './allergies';
import medicalHistoryStep from './medical-history';
import countryAndTown from './countries-and-town';
import summaryStep from './summary';
/**
 * @typedef {object} InputField
 * @property {string} ID - maps to HTML element ID
 * @property {string} label - maps to HTML <label> text
 * @property {string} type - maps to HTML <input> type
 */

/**
 * A step in the registration journey
 * Each step will map to a page with a form that renders the defined
 * @Step#fields
 *
 * @typedef {object} Step
 * @property {string} key - a unique ID for url-helpers like request.aka.
 * @property {string} slug - slug used to make up the last part of the url
 * @property {string} title - Title of the page and what is asked to the user,
 * this is also slugified into the url for the page.
 * @property {string} beforeTemplate - A template to include before the fields
 * @property {object} schema - a Joi schema to validate the supplied data
 * against.
 * @property {object} handlers - a GET and POST handler, typically created
 * using the postHandlerFactory and getHandlerFactory defined in 'common.js'.
 * @property {Function} [checkApplies] - function that checks if this step
 * should be run.
 */

/**
 * A list of registration @Steps, where order is important because
 * that will control the order in which they'll be asked.
 * @type {Array.<Step>}
 */
const steps = [
  nhsNumberStep,
  genderStep,
  nameStep,
  dateOfBirthStep,
  homeAddressLookupStep,
  countryAndTown,
  contactDetailsStep,
  previouslyArmedStep,
  previouslyRegisteredStep,
  currentGP,
  enterGpAddress,
  registeredAddressStep,
  registeredAddressDetailsStep,
  registeredNameStep,
  registeredNameDetailsStep,
  currentMedicationStep,
  allergiesStep,
  medicalHistoryStep,
  summaryStep
];

export const stepDependency = {
  'home-address-lookup': [
    'home-address-manual',
    'home-address-lookup-choose'
  ],
  'previously-registered': [
    'registered-address',
    'registered-address-manual',
    'registered-name',
    'registered-name-details'
  ]
};
export default steps;<commit_msg>home address step has bug, disabled for while<commit_after>import nameStep from './your-name';
import genderStep from './gender';
import dateOfBirthStep from './date-of-birth';
import homeAddressLookupStep from './home-address-lookup';
import contactDetailsStep from './contact-details';
import previouslyArmedStep from './previously-armed';
import previouslyRegisteredStep from './previously-registered';
import currentGP from './current-gp';
import enterGpAddress from './current-gp-address';
import registeredAddressStep from './registered-address';
import registeredAddressDetailsStep from './registered-address-details';
import registeredNameStep from './registered-name';
import registeredNameDetailsStep from './registered-name-details';
import nhsNumberStep from './nhs-number';
import currentMedicationStep from './current-medication';
import allergiesStep from './allergies';
import medicalHistoryStep from './medical-history';
import countryAndTown from './countries-and-town';
import summaryStep from './summary';
/**
 * @typedef {object} InputField
 * @property {string} ID - maps to HTML element ID
 * @property {string} label - maps to HTML <label> text
 * @property {string} type - maps to HTML <input> type
 */

/**
 * A step in the registration journey
 * Each step will map to a page with a form that renders the defined
 * @Step#fields
 *
 * @typedef {object} Step
 * @property {string} key - a unique ID for url-helpers like request.aka.
 * @property {string} slug - slug used to make up the last part of the url
 * @property {string} title - Title of the page and what is asked to the user,
 * this is also slugified into the url for the page.
 * @property {string} beforeTemplate - A template to include before the fields
 * @property {object} schema - a Joi schema to validate the supplied data
 * against.
 * @property {object} handlers - a GET and POST handler, typically created
 * using the postHandlerFactory and getHandlerFactory defined in 'common.js'.
 * @property {Function} [checkApplies] - function that checks if this step
 * should be run.
 */

/**
 * A list of registration @Steps, where order is important because
 * that will control the order in which they'll be asked.
 * @type {Array.<Step>}
 */
const steps = [
  nhsNumberStep,
  genderStep,
  nameStep,
  dateOfBirthStep,
  // homeAddressLookupStep,
  countryAndTown,
  contactDetailsStep,
  previouslyArmedStep,
  previouslyRegisteredStep,
  currentGP,
  enterGpAddress,
  registeredAddressStep,
  registeredAddressDetailsStep,
  registeredNameStep,
  registeredNameDetailsStep,
  currentMedicationStep,
  allergiesStep,
  medicalHistoryStep,
  summaryStep
];

export const stepDependency = {
  'home-address-lookup': [
    'home-address-manual',
    'home-address-lookup-choose'
  ],
  'previously-registered': [
    'registered-address',
    'registered-address-manual',
    'registered-name',
    'registered-name-details'
  ]
};
export default steps;<|endoftext|>


<commit_before>/**
 * jquery.analytics.js - https://github.com/abrudtkuhl/jQuery-Analytics
 * Google analytics implementation for jQuery.
 *
 * Copyright (c) 2013 Andy Brudtkuhl (http://youmetandy.com)
 * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php).
 * 2013/02/21
**/

(function($) {
  $.fn.analytics = function() {
    // check if _gaq google analytics array is defined first
    if (typeof(_gaq) == 'undefined') {
      console.error("In order to use this plugin, you must have Google Analytics installed");
      return this;
    }
    // return this for proper chaining
    return this.ready(function() {
      // event delegate to track clicks to events
      $('*[data-track]').on('click', this, function() {
        // grab data
        var data = $(this).data('track');
        // push to google analytics
        _gaq.push(['_trackEvent', data.category, data.action, data.label, data.value]);
      });
    });
  };
})(jQuery);<commit_msg>updated to work with new version of google analytics and universal analytics<commit_after>/**
 * jquery.analytics.js - https://github.com/abrudtkuhl/jQuery-Analytics
 * Google analytics implementation for jQuery.
 *
 * Copyright (c) 2013 Andy Brudtkuhl (http://youmetandy.com)
 * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php).
 * 2013/02/21
**/

(function($) {
  $.fn.analytics = function() {
    // google analytics is installed and running
    if (typeof(_gaq) == 'undefined' && typeof(ga) == 'undefined') {
      console.error("In order to use this plugin, you must have Google Analytics installed");
      return this;
    }
    // return this for proper chaining
    return this.ready(function() {
      // event delegate to track clicks to events
      $('*[data-track]').on('click', this, function() {
        // grab data
        var data = $(this).data('track');
        // push to google analytics
        _gaq.push(['_trackEvent', data.category, data.action, data.label, data.value]);
      });
    });
  };
})(jQuery);<|endoftext|>