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

import CodeUnit from '../code-unit';
import Grammar from '../grammar/grammar';
import CanonicalCollection from './canonical-collection';
import LRParsingTable from './lr-parsing-table';
import LRParserGeneratorDefault from './lr-parser-generator-default';
import Tokenizer from '../tokenizer';
import {EOF} from '../special-symbols';

import debug from '../debug';

import os from 'os';
import path from 'path';

const EntryType = LRParsingTable.EntryType;

export default class LRParser {
  constructor({
    grammar,
    parserModule,
    resolveConflicts,
  }) {
    this._grammar = grammar;
    this._parserModule = parserModule;

    this._canonicalCollection = new CanonicalCollection({
      grammar: this._grammar,
    });

    this._table = new LRParsingTable({
      canonicalCollection: this._canonicalCollection,
      grammar: this._grammar,
      resolveConflicts,
    });

    this._tokenizer = new Tokenizer({
      lexGrammar: this._grammar.getLexGrammar(),
    });

    // Parsing stack.
    this._stack = [];

    // Execute module include code which may attach
    // handlers for some events, and define needed data.
    CodeUnit.eval(this._grammar.getModuleInclude(), /*shouldRewrite*/ false);

    // Parse object which may define handlers for parse events.
    this._yyparse = CodeUnit.getSandbox().yyparse;

    // Global storage accessible from semantic actions.
    this._yy = CodeUnit.getSandbox().yy;

    // Parser may access tokenizer, and affect its state.
    this._yy.tokenizer = this._tokenizer;

    // Alias for tokenizer.
    this._yy.lexer = this._tokenizer;

    // Whether locations should be captured.
    this._yy.captureLocations = grammar.shouldCaptureLocations();
  }

  getGrammar() {
    return this._grammar;
  }

  getTable() {
    return this._table;
  }

  getCanonicalCollection() {
    return this._canonicalCollection;
  }

  static fromParserGenerator({grammar, options}) {
    // Generate parser in the temp directory.
    const outputFile = path.resolve(os.tmpdir(), '.syntax-parser.js');

    const parserModule = new LRParserGeneratorDefault({
      grammar,
      outputFile,
      options,
    }).generate();

    return new LRParser({grammar, parserModule});
  }

  parse(string) {
    // If parser module has been generated, use it.
    if (this._parserModule) {
      debug.time('LR parsing from module');

      const value = this._parserModule.parse(string);

      debug.timeEnd('LR parsing from module');

      return {
        status: 'accept',
        value,
      };
    }

    debug.time('LR parsing');

    if (this._yyparse.onParseBegin) {
      this._yyparse.onParseBegin(string);
    }

    this._tokenizer.initString(string);

    this._stack = [];

    let startingState = this._canonicalCollection
      .getStartingState()
      .getNumber();

    // Start from the initial state.
    this._stack.push(startingState);

    let token = this._tokenizer.getNextToken();
    let shiftedToken = null;

    do {
      if (!token) {
        this._unexpectedEndOfInput();
      }

      let state = this._peek();
      let column = token.type;
      let entry = this._table.get()[state][column];

      if (!entry) {
        this._unexpectedToken(token);
      }

      switch (LRParsingTable.getEntryType(entry)) {
        case EntryType.SHIFT:
          this._shift(token, entry);
          shiftedToken = token;
          token = this._tokenizer.getNextToken();
          break;
        case EntryType.REDUCE:
          this._reduce(entry, shiftedToken);
          // Don't advance tokens on reduce.
          break;
        case EntryType.SR_CONFLICT:
          this._conflictError('shift-reduce', state, column);
          break;
        case EntryType.RR_CONFLICT:
          this._conflictError('reduce-reduce', state, column);
        case EntryType.ACCEPT: {
          // Pop starting production and its state number.
          this._stack.pop();
          let parsed = this._stack.pop();

          if (this._stack.length !== 1 ||
              this._stack[0] !== startingState ||
              this._tokenizer.hasMoreTokens()) {
            this._unexpectedToken(token);
          }

          let result = {status: 'accept'};

          if (parsed.hasOwnProperty('semanticValue')) {
            result.value = parsed.semanticValue;
          }

          if (this._yyparse.onParseEnd) {
            this._yyparse.onParseEnd(result.value);
          }

          debug.timeEnd('LR parsing');

          return result;
        }
      }

    } while (this._tokenizer.hasMoreTokens() || this._stack.length > 1);
  }

  _unexpectedEndOfInput() {
    this._parseError(`Unexpected end of input.`);
  }

  _unexpectedToken(token) {
    if (token.type === EOF) {
      this._unexpectedEndOfInput();
    }

    this._tokenizer.throwUnexpectedToken(
      token.value,
      token.startLine,
      token.startColumn,
    );
  }

  _parseError(message) {
    throw new SyntaxError(message);
  }

  _conflictError(conflictType, state, column) {
    this._parseError(
      `Found "${conflictType}" conflict ` +
      `at state ${state}, terminal ${column}.`
    );
  }

  _peek() {
    return this._stack[this._stack.length - 1];
  }

  _shift(token, entry) {
    let loc = null;

    if (this._grammar.shouldCaptureLocations()) {
      loc = {
        startOffset: token.startOffset,
        endOffset: token.endOffset,
        startLine: token.startLine,
        endLine: token.endLine,
        startColumn: token.startColumn,
        endColumn: token.endColumn,
      };
    }

    this._stack.push(
      {
        symbol: token.type,
        semanticValue: token.value,
        loc,
      },
      Number(entry.slice(1))
    );
  }

  _reduce(entry, token) {
    const productionNumber = entry.slice(1);
    const production = this._grammar.getProduction(productionNumber);
    const hasSemanticAction = production.hasSemanticAction();
    const semanticValueArgs = hasSemanticAction ? [] : null;

    const locationArgs = (
      hasSemanticAction && this._grammar.shouldCaptureLocations()
        ? []
        : null
    );

    // Pop 2x symbols from the stack (RHS + state number for each),
    // unless it's an ε-production for which nothing to pop.
    if (!production.isEpsilon()) {
      let rhsLengh = production.getRHS().length;
      while (rhsLengh--) {
        // Pop state number;
        this._stack.pop();
        // Pop production symbol.
        let stackEntry = this._stack.pop();

        if (hasSemanticAction) {
          semanticValueArgs.unshift(stackEntry.semanticValue);

          if (locationArgs) {
            locationArgs.unshift(stackEntry.loc);
          }
        }
      }
    }

    let previousState = this._peek();
    let symbolToReduceWith = production.getLHS().getSymbol();

    let reduceStackEntry = {symbol: symbolToReduceWith};

    if (hasSemanticAction) {
      CodeUnit.setBindings({
        yytext: token ? token.value : '',
        yyleng: token ? token.value.length : 0,
      });

      const semanticActionArgs = (
        locationArgs !== null
          ? semanticValueArgs.concat(locationArgs)
          : semanticValueArgs
      );

      // Run corresponding semantic action, result is in $$ (__).
      production.runSemanticAction(semanticActionArgs);

      reduceStackEntry.semanticValue = CodeUnit.getSandbox().__;

      if (locationArgs) {
        reduceStackEntry.loc = CodeUnit.getSandbox().__loc;
      }
    }

    // Then push LHS.
    this._stack.push(reduceStackEntry);

    let nextState = this._table.get()[previousState][symbolToReduceWith];

    // And push the next state (goto)
    this._stack.push(nextState);
  }
};
<commit_msg>yy.options in LRParser<commit_after>/**
 * The MIT License (MIT)
 * Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
 */

import CodeUnit from '../code-unit';
import Grammar from '../grammar/grammar';
import CanonicalCollection from './canonical-collection';
import LRParsingTable from './lr-parsing-table';
import LRParserGeneratorDefault from './lr-parser-generator-default';
import Tokenizer from '../tokenizer';
import {EOF} from '../special-symbols';

import debug from '../debug';

import os from 'os';
import path from 'path';

const EntryType = LRParsingTable.EntryType;

export default class LRParser {
  constructor({
    grammar,
    parserModule,
    resolveConflicts,
  }) {
    this._grammar = grammar;
    this._parserModule = parserModule;

    this._canonicalCollection = new CanonicalCollection({
      grammar: this._grammar,
    });

    this._table = new LRParsingTable({
      canonicalCollection: this._canonicalCollection,
      grammar: this._grammar,
      resolveConflicts,
    });

    this._tokenizer = new Tokenizer({
      lexGrammar: this._grammar.getLexGrammar(),
    });

    // Parsing stack.
    this._stack = [];

    // Execute module include code which may attach
    // handlers for some events, and define needed data.
    CodeUnit.eval(this._grammar.getModuleInclude(), /*shouldRewrite*/ false);

    // Parse object which may define handlers for parse events.
    this._yyparse = CodeUnit.getSandbox().yyparse;

    // Global storage accessible from semantic actions.
    this._yy = CodeUnit.getSandbox().yy;

    // Parse options.
    this._yy.options = {}

    // Parser may access tokenizer, and affect its state.
    this._yy.tokenizer = this._tokenizer;

    // Alias for tokenizer.
    this._yy.lexer = this._tokenizer;

    // Whether locations should be captured.
    this._yy.options.captureLocations = grammar.shouldCaptureLocations();
  }

  getGrammar() {
    return this._grammar;
  }

  getTable() {
    return this._table;
  }

  getCanonicalCollection() {
    return this._canonicalCollection;
  }

  static fromParserGenerator({grammar, options}) {
    // Generate parser in the temp directory.
    const outputFile = path.resolve(os.tmpdir(), '.syntax-parser.js');

    const parserModule = new LRParserGeneratorDefault({
      grammar,
      outputFile,
      options,
    }).generate();

    return new LRParser({grammar, parserModule});
  }

  parse(string) {
    // If parser module has been generated, use it.
    if (this._parserModule) {
      debug.time('LR parsing from module');

      const value = this._parserModule.parse(string);

      debug.timeEnd('LR parsing from module');

      return {
        status: 'accept',
        value,
      };
    }

    debug.time('LR parsing');

    if (this._yyparse.onParseBegin) {
      this._yyparse.onParseBegin(string);
    }

    this._tokenizer.initString(string);

    this._stack = [];

    let startingState = this._canonicalCollection
      .getStartingState()
      .getNumber();

    // Start from the initial state.
    this._stack.push(startingState);

    let token = this._tokenizer.getNextToken();
    let shiftedToken = null;

    do {
      if (!token) {
        this._unexpectedEndOfInput();
      }

      let state = this._peek();
      let column = token.type;
      let entry = this._table.get()[state][column];

      if (!entry) {
        this._unexpectedToken(token);
      }

      switch (LRParsingTable.getEntryType(entry)) {
        case EntryType.SHIFT:
          this._shift(token, entry);
          shiftedToken = token;
          token = this._tokenizer.getNextToken();
          break;
        case EntryType.REDUCE:
          this._reduce(entry, shiftedToken);
          // Don't advance tokens on reduce.
          break;
        case EntryType.SR_CONFLICT:
          this._conflictError('shift-reduce', state, column);
          break;
        case EntryType.RR_CONFLICT:
          this._conflictError('reduce-reduce', state, column);
        case EntryType.ACCEPT: {
          // Pop starting production and its state number.
          this._stack.pop();
          let parsed = this._stack.pop();

          if (this._stack.length !== 1 ||
              this._stack[0] !== startingState ||
              this._tokenizer.hasMoreTokens()) {
            this._unexpectedToken(token);
          }

          let result = {status: 'accept'};

          if (parsed.hasOwnProperty('semanticValue')) {
            result.value = parsed.semanticValue;
          }

          if (this._yyparse.onParseEnd) {
            this._yyparse.onParseEnd(result.value);
          }

          debug.timeEnd('LR parsing');

          return result;
        }
      }

    } while (this._tokenizer.hasMoreTokens() || this._stack.length > 1);
  }

  _unexpectedEndOfInput() {
    this._parseError(`Unexpected end of input.`);
  }

  _unexpectedToken(token) {
    if (token.type === EOF) {
      this._unexpectedEndOfInput();
    }

    this._tokenizer.throwUnexpectedToken(
      token.value,
      token.startLine,
      token.startColumn,
    );
  }

  _parseError(message) {
    throw new SyntaxError(message);
  }

  _conflictError(conflictType, state, column) {
    this._parseError(
      `Found "${conflictType}" conflict ` +
      `at state ${state}, terminal ${column}.`
    );
  }

  _peek() {
    return this._stack[this._stack.length - 1];
  }

  _shift(token, entry) {
    let loc = null;

    if (this._grammar.shouldCaptureLocations()) {
      loc = {
        startOffset: token.startOffset,
        endOffset: token.endOffset,
        startLine: token.startLine,
        endLine: token.endLine,
        startColumn: token.startColumn,
        endColumn: token.endColumn,
      };
    }

    this._stack.push(
      {
        symbol: token.type,
        semanticValue: token.value,
        loc,
      },
      Number(entry.slice(1))
    );
  }

  _reduce(entry, token) {
    const productionNumber = entry.slice(1);
    const production = this._grammar.getProduction(productionNumber);
    const hasSemanticAction = production.hasSemanticAction();
    const semanticValueArgs = hasSemanticAction ? [] : null;

    const locationArgs = (
      hasSemanticAction && this._grammar.shouldCaptureLocations()
        ? []
        : null
    );

    // Pop 2x symbols from the stack (RHS + state number for each),
    // unless it's an ε-production for which nothing to pop.
    if (!production.isEpsilon()) {
      let rhsLengh = production.getRHS().length;
      while (rhsLengh--) {
        // Pop state number;
        this._stack.pop();
        // Pop production symbol.
        let stackEntry = this._stack.pop();

        if (hasSemanticAction) {
          semanticValueArgs.unshift(stackEntry.semanticValue);

          if (locationArgs) {
            locationArgs.unshift(stackEntry.loc);
          }
        }
      }
    }

    let previousState = this._peek();
    let symbolToReduceWith = production.getLHS().getSymbol();

    let reduceStackEntry = {symbol: symbolToReduceWith};

    if (hasSemanticAction) {
      CodeUnit.setBindings({
        yytext: token ? token.value : '',
        yyleng: token ? token.value.length : 0,
      });

      const semanticActionArgs = (
        locationArgs !== null
          ? semanticValueArgs.concat(locationArgs)
          : semanticValueArgs
      );

      // Run corresponding semantic action, result is in $$ (__).
      production.runSemanticAction(semanticActionArgs);

      reduceStackEntry.semanticValue = CodeUnit.getSandbox().__;

      if (locationArgs) {
        reduceStackEntry.loc = CodeUnit.getSandbox().__loc;
      }
    }

    // Then push LHS.
    this._stack.push(reduceStackEntry);

    let nextState = this._table.get()[previousState][symbolToReduceWith];

    // And push the next state (goto)
    this._stack.push(nextState);
  }
};
<|endoftext|>


<commit_before>import React from 'react';
import reactMixin from 'react-mixin';
import autoBind from 'react-autobind';
import Reflux from 'reflux';
import {actions} from 'js/actions';
import {stores} from 'js/stores';
import {bem} from 'js/bem';

export default class FormJson extends React.Component {
  constructor(props) {
    super(props);
    this.state = {assetContent: false};
    autoBind(this);
  }

  componentDidMount() {
    this.listenTo(stores.asset, this.assetStoreTriggered);
    const uid = this.props.params.assetid || this.props.params.uid;
    actions.resources.loadAsset({id: uid});
  }

  assetStoreTriggered(data, uid) {
    this.setState({assetContent: data[uid].content});
  }

  render() {
    let content = null;
    if (this.state.assetContent) {
      content = JSON.stringify(this.state.assetContent, null, 4);
    }

    return (
      <bem.uiPanel>
        <bem.uiPanel__body>
          <bem.FormView>

                {content}

          </bem.FormView>
        </bem.uiPanel__body>
      </bem.uiPanel>
    );
  }
}

reactMixin(FormJson.prototype, Reflux.ListenerMixin);
<commit_msg>update after merge<commit_after>import React from 'react';
import reactMixin from 'react-mixin';
import autoBind from 'react-autobind';
import Reflux from 'reflux';
import {actions} from 'js/actions';
import assetStore from 'js/assetStore';
import {bem} from 'js/bem';

export default class FormJson extends React.Component {
  constructor(props) {
    super(props);
    this.state = {assetContent: false};
    autoBind(this);
  }

  componentDidMount() {
    this.listenTo(assetStore, this.assetStoreTriggered);
    const uid = this.props.params.assetid || this.props.params.uid;
    actions.resources.loadAsset({id: uid});
  }

  assetStoreTriggered(data, uid) {
    this.setState({assetContent: data[uid].content});
  }

  render() {
    let content = null;
    if (this.state.assetContent) {
      content = JSON.stringify(this.state.assetContent, null, 4);
    }

    return (
      <bem.uiPanel>
        <bem.uiPanel__body>
          <bem.FormView>

                {content}

          </bem.FormView>
        </bem.uiPanel__body>
      </bem.uiPanel>
    );
  }
}

reactMixin(FormJson.prototype, Reflux.ListenerMixin);
<|endoftext|>


<commit_before>var stomp = require('./lib/stomp');
var StompUtils = require('./lib/stomp-utils');
var http = require('http');
var WebSocketServer = require('ws').Server;
var EventEmitter = require('events');
var util = require('util');

/**
 * STOMP Server configuration
 * @typedef {object} ServerConfig
 * @param {http.Server} server Http server reference
 * @param {function} [debug=function(args) {}] Debug function
 * @param {string} [path=/stomp] Websocket path
 * */

/**
 * @class
 * @augments EventEmitter
 * Create stomp server with config
 * @param {ServerConfig} config Configuration for STOMP server
 */
var StompServer = function (config) {
  EventEmitter.call(this);
  if (config === undefined) {
    config = {};
  }
  this.conf = {
    server: config.server,
    serverName: config.serverName || 'STOMP-JS/1.1.2',
    path: config.path || '/stomp',
    debug: config.debug || function (args) {
    }
  };
  if (this.conf.server === undefined) {
    throw 'Server is required';
  }

  this.subscribes = [];
  this.frameHandler = new stomp.FrameHandler(this);
  this.heartBeatConfig = {client: 0, server: 0};

  this.socket = new WebSocketServer({
    server: this.conf.server,
    path: this.conf.path,
    perMessageDeflate: false
  });

  /**
   * Client connecting event, emitted after socket is opened.
   * @event StompServer#connecting
   * @type {object}
   * @property {string} sessionId
   * */
  this.socket.on('connection', function (webSocket) {
    webSocket.sessionId = StompUtils.genId();
    this.emit('connecting', webSocket.sessionId);
    this.conf.debug('Connect', webSocket.sessionId);
    webSocket.on('message', parseRequest.bind(this, webSocket));
    webSocket.on('close', connectionClose.bind(this, webSocket));
    webSocket.on('error', function (err) {
      this.conf.debug(err);
      this.emit('error', err);
    }.bind(this));
  }.bind(this));

  /*############# EVENTS ###################### */

  /**
   * Client connected event, emitted after connection established and negotiated
   * @event StompServer#connected
   * @type {object}
   * @property {string} sessionId
   * @property {object} headers
   * */
  this.onClientConnected = function (socket, args) {
    socket.clientHeartBeat = {client: args.hearBeat[0], server: args.hearBeat[1]};
    this.conf.debug('CONNECT', socket.sessionId, socket.clientHeartBeat, args.headers);
    this.emit('connected', socket.sessionId, args.headers);
    return true;
  };

  /**
   * Client disconnected event
   * @event StompServer#disconnected
   * @type {object}
   * @property {string} sessionId
   * */
  this.onDisconnect = function (socket, receiptId) {
    connectionClose(socket);
    this.conf.debug('DISCONNECT', socket.sessionId);
    this.emit('disconnected', socket.sessionId);
    this.conf.debug('DISCONNECT', socket.sessionId, receiptId);
    return true;
  };

  /**
   * Event emitted when broker send message to subscribers
   * @event StompServer#send
   * @type {object}
   * @property {string} dest Destination
   * @property {string} frame Message frame
   * */
  this.onSend = function (socket, args, callback) {
    var bodyObj = args.frame.body;
    var frame = this.frameSerializer(args.frame);
    var headers = { //default headers
      'message-id': StompUtils.genId('msg'),
      'content-type': 'text/plain'
    };
    if (frame.body !== undefined) {
      if (typeof frame.body !== 'string' &&
        !(typeof frame.body === 'object' && frame.body instanceof Buffer)
      )
        throw 'Message body is not string';
      frame.headers['content-length'] = frame.body.length;
    }
    if (frame.headers) {
      for (var key in frame.headers) {
        headers[key] = frame.headers[key];
      }
    }
    args.frame = frame;
    this.emit('send', {frame: {headers: frame.headers, body: bodyObj}, dest: args.dest});
    this._sendToSubscriptions(socket, args);
    if (callback) {
      callback(true);
    }
  };

  /**
   * Send message to matching subscribers.
   *
   * @param {object} socket websocket to send the message on
   * @param {string} args onSend args
   * @private
   */
  this._sendToSubscriptions = function (socket, args) {
    for (var i in this.subscribes) {
      var sub = this.subscribes[i];
      if (socket.sessionId === sub.sessionId) {
        continue;
      }
      var match = this._checkSubMatchDest(sub, args);
      if (match) {
        args.frame.headers.subscription = sub.id;
        args.frame.command = 'MESSAGE';
        var sock = sub.socket;
        if (sock !== undefined) {
          StompUtils.sendFrame(sock, args.frame);
        } else {
          this.emit(sub.id, args.frame.body, args.frame.headers);
        }
      }
    }
  };

  /**
   * Test if the input subscriber has subscribed to the target destination.
   *
   * @param sub the subscriber
   * @param args onSend args
   * @returns {boolean} true if the input subscription matches destination
   * @private
   */
  this._checkSubMatchDest = function (sub, args) {
    var match = true;
    //console.log(args.dest);
    var tokens = StompUtils.tokenizeDestination(args.dest);
    for (var t in tokens) {
      var token = tokens[t];
      if (sub.tokens[t] === undefined ||
        (sub.tokens[t] !== token && sub.tokens[t] !== '*' && sub.tokens[t] !== '**')) {
        match = false;
        break;
      } else if (sub.tokens[t] === '**') {
        break;
      }
    }
    return match;
  };

  /**
   * Client subscribe event, emitted when client subscribe topic
   * @event StompServer#subscribe
   * @type {object}
   * @property {string} id Subscription id
   * @property {string} sessionId Socket session id
   * @property {string} topic Destination topic
   * @property {string[]} tokens Tokenized topic
   * @property {object} socket Connected socket
   * */
  this.onSubscribe = function (socket, args) {
    var sub = {
      id: args.id,
      sessionId: socket.sessionId,
      topic: args.dest,
      tokens: StompUtils.tokenizeDestination(args.dest),
      socket: socket
    };
    this.subscribes.push(sub);
    this.emit('subscribe', sub);
    this.conf.debug('Server subscribe', args.id, args.dest);
    return true;
  };
  /**
   * Client subscribe event, emitted when client unsubscribe topic
   * @event StompServer#unsubscribe
   * @type {object}
   * @property {string} id Subscription id
   * @property {string} sessionId Socket session id
   * @property {string} topic Destination topic
   * @property {string[]} tokens Tokenized topic
   * @property {object} socket Connected socket
   * */

  /** @return {boolean} */
  this.onUnsubscribe = function (socket, subId) {
    for (var t in this.subscribes) {
      var sub = this.subscribes[t];
      if (sub.id === subId && sub.sessionId === socket.sessionId) {
        delete this.subscribes[t];
        this.emit('unsubscribe', sub);
        return true;
      }
    }
    return false;
  };
  /*############# END EVENTS ###################### */

  /* ################ FUNCTIONS ################### */

  var selfSocket = {
    sessionId: 'self_1234'
  };

  /**
   * Subscription callback method
   *
   * @callback OnSubscribedMessageCallback
   * @param {string} msg Message body
   * @param {object} headers Message headers
   * @param {string} headers.destination Message destination
   * @param {string} headers.subscription Id of subscription
   * @param {string} headers.message-id Id of message
   * @param {string} headers.content-type Content type
   * @param {string} headers.content-length Content length
   * */

  /** Subsribe topic
   * @param {string} topic Subscribed destination, wildcard is supported
   * @param {OnSubscribedMessageCallback=} callback Callback function
   * @param {object} headers Optional headers, used by client to provide a subscription ID (headers.id)
   * @return {string} Subscription id, when message is received event with this id is emitted
   * @example
   * stompServer.subscribe('/test.data', function(msg, headers) {});
   * //or alternative
   * var subs_id = stompServer.subscribe('/test.data');
   * stompServer.on(subs_id, function(msg, headers) {});
   * */
  this.subscribe = function (topic, callback, headers) {
    var id;
    if (!headers || !headers.id) {
      id = 'self_' + Math.floor(Math.random() * 99999999999);
    } else {
      id = headers.id;
    }
    var sub = {
      topic: topic,
      tokens: StompUtils.tokenizeDestination(topic),
      id: id,
      sessionId: 'self_1234'
    };
    this.subscribes.push(sub);
    this.emit('subscribe', sub);
    if (callback) {
      this.on(id, callback);
    }
    return id;
  };

  /** Unsubscribe topic with subscription id
   * @param {string} id Subscription id
   * @return {boolean} Subscription is deleted
   * */
  this.unsubscribe = function (id) {
    this.removeAllListeners(id);
    return this.onUnsubscribe(selfSocket, id);
  };

  /** Send message to topic
   * @param {string} topic Destination for message
   * @param {object} headers Message headers
   * @param {string} body Message body */
  this.send = function (topic, headers, body) {
    var _headers = {};
    if (headers) {
      for (var key in headers) {
        _headers[key] = headers[key];
      }
    }
    var frame = {
      body: body,
      headers: _headers
    };
    var args = {
      dest: topic,
      frame: this.frameParser(frame)
    };
    this.onSend(selfSocket, args);
  };

  /* ############# END FUNCTIONS ###################### */

  function connectionClose(socket) {
    var self = this;
    for (var t in self.subscribes) {
      var sub = self.subscribes[t];
      if (sub.sessionId === socket.sessionId) {
        delete self.subscribes[t];
      }
    }
  }

  /**
   * @typedef {object} MsgFrame
   * Message frame
   * */

  /** Serialize frame to string for send
   * @param {MsgFrame} frame Message frame
   * @return {MsgFrame} modified frame
   * */
  this.frameSerializer = function (frame) {
    if (frame.body !== undefined && frame.headers['content-type'] === 'application/json') {
      frame.body = JSON.stringify(frame.body);
    }
    return frame;
  };

  /** Parse frame to object for reading
   * @param {MsgFrame} frame Message frame
   * @return {MsgFrame} modified frame
   * */
  this.frameParser = function (frame) {
    if (frame.body !== undefined && frame.headers['content-type'] === 'application/json') {
      frame.body = JSON.parse(frame.body);
    }
    return frame;
  };

  function parseRequest(socket, data) {
    var frame = StompUtils.parseFrame(data);
    var cmdFunc = this.frameHandler[frame.command];
    if (cmdFunc) {
      frame = this.frameParser(frame);
      return cmdFunc(socket, frame);
    }
    return 'Command not found';
  }

};
util.inherits(StompServer, EventEmitter);

/** Create a StompServer */
module.exports = StompServer;
<commit_msg>Clean up a bit messy code<commit_after>var http            = require('http');
var WebSocketServer = require('ws').Server;
var EventEmitter    = require('events');
var util            = require('util');

var Stomp           = require('./lib/stomp');
var StompUtils      = require('./lib/stomp-utils');

/**
 * STOMP Server configuration
 *
 * @typedef {object} ServerConfig
 * @param {http.Server} server Http server reference
 * @param {function} [debug=function(args) {}] Debug function
 * @param {string} [path=/stomp] WebSocket path
 */

/**
 * @class
 * @augments EventEmitter
 *
 * Create Stomp server with config
 *
 * @param {ServerConfig} config Configuration for STOMP server
 */
var StompServer = function (config) {
  EventEmitter.call(this);

  if (config === undefined) {
    config = {};
  }

  this.conf = {
    server: config.server,
    serverName: config.serverName || 'STOMP-JS/1.1.2',
    path: config.path || '/stomp',
    debug: config.debug || function (args) {
    }
  };

  if (this.conf.server === undefined) {
    throw 'Server is required';
  }

  this.subscribes = [];
  this.frameHandler = new Stomp.FrameHandler(this);
  this.heartBeatConfig = {
    client: 0,
    server: 0
  };

  this.socket = new WebSocketServer({
    server: this.conf.server,
    path: this.conf.path,
    perMessageDeflate: false
  });

  /**
   * Client connecting event, emitted after socket is opened.
   *
   * @event StompServer#connecting
   * @type {object}
   * @property {string} sessionId
   */
  this.socket.on('connection', function (ws) {
    ws.sessionId = StompUtils.genId();
    this.emit('connecting', ws.sessionId);
    this.conf.debug('Connect', ws.sessionId);

    ws.on('message', parseRequest.bind(this, ws));
    ws.on('close', connectionClose.bind(this, ws));
    ws.on('error', function (err) {
      this.conf.debug(err);
      this.emit('error', err);
    }.bind(this));
  }.bind(this));

  //<editor-fold defaultstate="collapsed" desc="Events">

  /**
   * Client connected event, emitted after connection established and negotiated
   *
   * @event StompServer#connected
   * @type {object}
   * @property {string} sessionId
   * @property {object} headers
   */
  this.onClientConnected = function (socket, args) {
    socket.clientHeartBeat = {
      client: args.hearBeat[0],
      server: args.hearBeat[1]
    };
    this.conf.debug('CONNECT', socket.sessionId, socket.clientHeartBeat, args.headers);
    this.emit('connected', socket.sessionId, args.headers);
    return true;
  };

  /**
   * Client disconnected event
   *
   * @event StompServer#disconnected
   * @type {object}
   * @property {string} sessionId
   */
  this.onDisconnect = function (socket, receiptId) {
    connectionClose(socket);
    this.conf.debug('DISCONNECT', socket.sessionId);
    this.emit('disconnected', socket.sessionId);
    this.conf.debug('DISCONNECT', socket.sessionId, receiptId);
    return true;
  };

  /**
   * Event emitted when broker send message to subscribers
   *
   * @event StompServer#send
   * @type {object}
   * @property {string} dest Destination
   * @property {string} frame Message frame
   */
  this.onSend = function (socket, args, callback) {
    var bodyObj = args.frame.body;
    var frame = this.frameSerializer(args.frame);
    var headers = {
      //default headers
      'message-id': StompUtils.genId('msg'),
      'content-type': 'text/plain'
    };

    if (frame.body !== undefined) {
      if (typeof frame.body !== 'string' && !(typeof frame.body === 'object' && frame.body instanceof Buffer)) {
        throw 'Message body is not string';
      }
      frame.headers['content-length'] = frame.body.length;
    }

    if (frame.headers) {
      for (var key in frame.headers) {
        headers[key] = frame.headers[key];
      }
    }

    args.frame = frame;
    this.emit('send', {
      frame: {
        headers: frame.headers,
        body: bodyObj
      },
      dest: args.dest
    });

    this._sendToSubscriptions(socket, args);

    if (callback) {
      callback(true);
    }
  };

  /**
   * Client subscribe event, emitted when client subscribe topic
   *
   * @event StompServer#subscribe
   * @type {object}
   * @property {string} id Subscription id
   * @property {string} sessionId Socket session id
   * @property {string} topic Destination topic
   * @property {string[]} tokens Tokenized topic
   * @property {object} socket Connected socket
   */
  this.onSubscribe = function (socket, args) {
    var sub = {
      id: args.id,
      sessionId: socket.sessionId,
      topic: args.dest,
      tokens: StompUtils.tokenizeDestination(args.dest),
      socket: socket
    };
    this.subscribes.push(sub);
    this.emit('subscribe', sub);
    this.conf.debug('Server subscribe', args.id, args.dest);
    return true;
  };

  /**
   * Client subscribe event, emitted when client unsubscribe topic
   *
   * @event StompServer#unsubscribe
   * @type {object}
   * @property {string} id Subscription id
   * @property {string} sessionId Socket session id
   * @property {string} topic Destination topic
   * @property {string[]} tokens Tokenized topic
   * @property {object} socket Connected socket
   * @return {boolean}
   */
  this.onUnsubscribe = function (socket, subId) {
    for (var t in this.subscribes) {
      var sub = this.subscribes[t];
      if (sub.id === subId && sub.sessionId === socket.sessionId) {
        delete this.subscribes[t];
        this.emit('unsubscribe', sub);
        return true;
      }
    }
    return false;
  };

  //</editor-fold>

  //<editor-fold defaultstate="collapsed" desc="Subscribe & Unsubscribe">

  var selfSocket = {
    sessionId: 'self_1234'
  };

  /**
   * Subscription callback method
   *
   * @callback OnSubscribedMessageCallback
   * @param {string} msg Message body
   * @param {object} headers Message headers
   * @param {string} headers.destination Message destination
   * @param {string} headers.subscription Id of subscription
   * @param {string} headers.message-id Id of message
   * @param {string} headers.content-type Content type
   * @param {string} headers.content-length Content length
   */
  // TODO: Create this method or remove above comment

  /**
   * Subscribe topic
   *
   * @param {string} topic Subscribed destination, wildcard is supported
   * @param {OnSubscribedMessageCallback=} callback Callback function
   * @param {object} headers Optional headers, used by client to provide a subscription ID (headers.id)
   * @return {string} Subscription id, when message is received event with this id is emitted
   * @example
   * stompServer.subscribe('/test.data', function(msg, headers) {});
   * //or alternative
   * var subs_id = stompServer.subscribe('/test.data');
   * stompServer.on(subs_id, function(msg, headers) {});
   */
  this.subscribe = function (topic, callback, headers) {
    var id;
    if (!headers || !headers.id) {
      id = 'self_' + Math.floor(Math.random() * 99999999999);
    } else {
      id = headers.id;
    }
    var sub = {
      topic: topic,
      tokens: StompUtils.tokenizeDestination(topic),
      id: id,
      sessionId: 'self_1234'
    };
    this.subscribes.push(sub);
    this.emit('subscribe', sub);
    if (callback) {
      this.on(id, callback);
    }
    return id;
  };

  /** Unsubscribe topic with subscription id
   *
   * @param {string} id Subscription id
   * @return {boolean} Subscription is deleted
   */
  this.unsubscribe = function (id) {
    this.removeAllListeners(id);
    return this.onUnsubscribe(selfSocket, id);
  };

  //</editor-fold>

  //<editor-fold defaultstate="collapsed" desc="Send">

  /**
   * Send message to matching subscribers.
   *
   * @param {object} socket websocket to send the message on
   * @param {string} args onSend args
   * @private
   */
  this._sendToSubscriptions = function (socket, args) {
    for (var i in this.subscribes) {
      var sub = this.subscribes[i];
      if (socket.sessionId === sub.sessionId) {
        continue;
      }
      var match = this._checkSubMatchDest(sub, args);
      if (match) {
        args.frame.headers.subscription = sub.id;
        args.frame.command = 'MESSAGE';
        var sock = sub.socket;
        if (sock !== undefined) {
          StompUtils.sendFrame(sock, args.frame);
        } else {
          this.emit(sub.id, args.frame.body, args.frame.headers);
        }
      }
    }
  };

  /** Send message to topic
   *
   * @param {string} topic Destination for message
   * @param {object} headers Message headers
   * @param {string} body Message body
   */
  this.send = function (topic, headers, body) {
    var _headers = {};
    if (headers) {
      for (var key in headers) {
        _headers[key] = headers[key];
      }
    }
    var frame = {
      body: body,
      headers: _headers
    };
    var args = {
      dest: topic,
      frame: this.frameParser(frame)
    };
    this.onSend(selfSocket, args);
  };

  //</editor-fold>

  //<editor-fold defaultstate="collapsed" desc="Frames">

  /**
   * Serialize frame to string for send
   *
   * @param {MsgFrame} frame Message frame
   * @return {MsgFrame} modified frame
   * */
  this.frameSerializer = function (frame) {
    if (frame.body !== undefined && frame.headers['content-type'] === 'application/json') {
      frame.body = JSON.stringify(frame.body);
    }
    return frame;
  };

  /**
   * Parse frame to object for reading
   *
   * @param {MsgFrame} frame Message frame
   * @return {MsgFrame} modified frame
   * */
  this.frameParser = function (frame) {
    if (frame.body !== undefined && frame.headers['content-type'] === 'application/json') {
      frame.body = JSON.parse(frame.body);
    }
    return frame;
  };

  //</editor-fold>

  /**
   * Test if the input subscriber has subscribed to the target destination.
   *
   * @param sub the subscriber
   * @param args onSend args
   * @returns {boolean} true if the input subscription matches destination
   * @private
   */
  this._checkSubMatchDest = function (sub, args) {
    var match = true;
    //console.log(args.dest);
    var tokens = StompUtils.tokenizeDestination(args.dest);
    for (var t in tokens) {
      var token = tokens[t];
      if (sub.tokens[t] === undefined ||
        (sub.tokens[t] !== token && sub.tokens[t] !== '*' && sub.tokens[t] !== '**')) {
        match = false;
        break;
      } else if (sub.tokens[t] === '**') {
        break;
      }
    }
    return match;
  };

  function connectionClose(socket) {
    var self = this;
    for (var t in self.subscribes) {
      var sub = self.subscribes[t];
      if (sub.sessionId === socket.sessionId) {
        delete self.subscribes[t];
      }
    }
  }

  function parseRequest(socket, data) {
    var frame = StompUtils.parseFrame(data);
    var cmdFunc = this.frameHandler[frame.command];
    if (cmdFunc) {
      frame = this.frameParser(frame);
      return cmdFunc(socket, frame);
    }
    return 'Command not found';
  }

};

util.inherits(StompServer, EventEmitter);

// Export
module.exports = StompServer;
<|endoftext|>


<commit_before>var crypto = require('crypto')
var fs = require('fs')
var path = require('path')

var fingerprintPath = path.resolve(__dirname, '..', '..', 'node_modules', '.atom-ci-fingerprint')

module.exports = {
  fingerprint: function () {
    var packageJson = fs.readFileSync(path.resolve(__dirname, '..', '..', 'package.json'))
    var body = packageJson.toString() + process.platform + process.version
    return crypto.createHash('sha1').update(body).digest('hex')
  },

  writeFingerprint: function () {
    var fingerprint = this.fingerprint()
    fs.writeFileSync(fingerprintPath, fingerprint)
    console.log('Wrote ci fingerprint:', fingerprintPath, fingerprint)
  },

  readFingerprint: function() {
    if (fs.existsSync(fingerprintPath)) {
      return fs.readFileSync(fingerprintPath).toString()
    } else {
      return null
    }
  },

  fingerprintMatches: function () {
    return this.readFingerprint() && this.readFingerprint() === this.fingerprint()
  }
}
<commit_msg>Use electron version only in fingerprint<commit_after>var crypto = require('crypto')
var fs = require('fs')
var path = require('path')

var fingerprintPath = path.resolve(__dirname, '..', '..', 'node_modules', '.atom-ci-fingerprint')

module.exports = {
  fingerprint: function () {
    var packageJson = fs.readFileSync(path.resolve(__dirname, '..', '..', 'package.json'))

    //Include the electron minor version in the fingerprint since that changing requires a re-install
    var electronVersion = JSON.parse(packageJson).electronVersion.replace(/\.\d+$/, '')

    var body = electronVersion + process.platform + process.version
    return crypto.createHash('sha1').update(body).digest('hex')
  },

  writeFingerprint: function () {
    var fingerprint = this.fingerprint()
    fs.writeFileSync(fingerprintPath, fingerprint)
    console.log('Wrote ci fingerprint:', fingerprintPath, fingerprint)
  },

  readFingerprint: function() {
    if (fs.existsSync(fingerprintPath)) {
      return fs.readFileSync(fingerprintPath).toString()
    } else {
      return null
    }
  },

  fingerprintMatches: function () {
    return this.readFingerprint() && this.readFingerprint() === this.fingerprint()
  }
}
<|endoftext|>


<commit_before>'use strict';

var value    = require('../../Object/valid-value')
  , copy     = require('./copy')
  , contains = require('./contains')
  , remove   = require('./remove')

  , filter = Array.prototype.filter;

module.exports = function (other) {
	var r;
	if ((value(this).length >>> 0) > (value(other).length >>> 0)) {
		r = copy.call(this);
		remove.apply(r, other);
		return r;
	} else {
		return filter.call(this, function (item) {
			return !contains.call(other, item);
		});
	}
};
<commit_msg>Diff should return non sparse array in all cases<commit_after>'use strict';

var value    = require('../../Object/valid-value')
  , contains = require('./contains')

  , filter = Array.prototype.filter;

module.exports = function (other) {
	value(this) && value(other);
	return filter.call(this, function (item) {
		return !contains.call(other, item);
	});
};
<|endoftext|>


<commit_before>Dagaz.Controller.persistense = "none";

Dagaz.Model.WIDTH  = 8;
Dagaz.Model.HEIGHT = 8;

(function() {

Dagaz.Model.moveToString = function(move) {
  var r = "";
  _.each(move.actions, function(a) {
      if (a[1] === null) return;
      if ((r == "") && (a[0] != null)) {
          r = r + Dagaz.Model.posToString(a[0][0]);
      }
      if (a[1] !== null) {
          r = r + Dagaz.Model.posToString(a[1][0]);
      }
  });
  return r;
}

var getName = function() {
  var str = window.location.pathname.toString();
  var result = str.match(/\/([^.\/]+)\./);
  if (result) {
      return result[1].replace("-board", "").replace("-ai", "");
  } else {
      return str;
  }
}

var badName = function(str) {
  var result = str.match(/[?&]game=([^&*]*)/);
  if (result) {
      return result[1] != getName();
  } else {
      return true;
  }
}

var getCookie = function() {
  var result = localStorage.getItem('dagaz.setup');
  if (result) {
      if (badName(result)) return "";
      return result;
  } else {
      return "";
  }
}

var getSetup = function(setup) {
  var str = window.location.search.toString();
  if (setup) {
      str = setup;
  }
  var result = str.match(/[?&]setup=([^&]*)/);
  if (result) {
      return result[1];
  } else {
      str = getCookie();
      result = str.match(/[?&]setup=([^&]*)/);
      if (result) {
          return result[1];
      } else {
          return "";
      }
  }
}

var getTurn = function(setup) {
  var str = window.location.search.toString();
  if (setup) {
      str = setup;
  }
  var result = str.match(/[?&]turn=(\d+)/);
  if (result) {
      return result[1];
  } else {
      str = getCookie();
      result = str.match(/[?&]turn=(\d+)/);
      if (result) {
          return result[1];
      } else {
          return "";
      }
  }
}

var createPiece = function(design, c) {
  if (c == 'P') return Dagaz.Model.createPiece(design.getPieceType("Man"), 1);
  if (c == 'p') return Dagaz.Model.createPiece(design.getPieceType("Man"), 2);
  if (c == 'K') return Dagaz.Model.createPiece(design.getPieceType("King"), 1);
  if (c == 'k') return Dagaz.Model.createPiece(design.getPieceType("King"), 2);
  return null;
}

Dagaz.Model.setup = function(board, init) {
  var design = Dagaz.Model.design;
  var setup  = getSetup(init);
  var player = 1;
  if (setup) {
      board.clear();
      var pos = 0;
      for (var i = 0; i < setup.length; i++) {
           var c = setup[i];
           if (c != '/') {
               if ((c >= '0') && (c <= '9')) {
                   pos += +c;
               } else {
                   var piece = createPiece(design, c);
                   board.setPiece(pos, piece);
                   pos++;
               }
               if (pos >= Dagaz.Model.WIDTH * Dagaz.Model.HEIGHT) break;
           }
      }
      var turn = getTurn(init);
      if (turn) {
          board.turn   = +turn;
          board.player = design.currPlayer(board.turn);
      }
  }
}

var getPieceNotation = function(design, piece) {
  var r = 'P';
  if (piece.type == design.getPieceType("King")) r = 'K';
  if (piece.player > 1) {
      return r.toLowerCase();
  }
  return r;
}

Dagaz.Model.getSetup = function(design, board) {
  var str = "?turn=" + board.turn + ";&setup=";
  var k = 0; var c = 0;
  for (var pos = 0; pos < Dagaz.Model.WIDTH * Dagaz.Model.HEIGHT; pos++) {
       if (k >= Dagaz.Model.WIDTH) {
           if (c > 0) {
               str += c;
           }
           str += "/";
           k = 0;
           c = 0;
       }
       k++;
       var piece = board.getPiece(pos);
       if (piece === null) {
           if (c > 8) {
               str += c;
               c = 0;
           }
           c++;
       } else {
           if (c > 0) {
               str += c;
           }
           c = 0;
           str += getPieceNotation(design, piece);
       }
  }
  if (c > 0) {
      str += c;
  }
  if (board.turn == 0) {
      str += " w";
  } else {
      str += " b";
  }
  if (Dagaz.Controller.persistense == "setup") {
      var s = str + "&game=" + getName() + "*";
      localStorage.setItem('dagaz.setup', s);
  }
  return str;
}

var clearGame = Dagaz.Controller.clearGame;

Dagaz.Controller.clearGame = function() {
   localStorage.setItem('dagaz.setup', '');
   if (!_.isUndefined(clearGame)) {
       clearGame();
   }
}

})();
<commit_msg>Lika (checkers-fen fixed)<commit_after>Dagaz.Controller.persistense = "none";

Dagaz.Model.WIDTH  = 8;
Dagaz.Model.HEIGHT = 8;

(function() {

Dagaz.Model.moveToString = function(move) {
  var r = "";
  _.each(move.actions, function(a) {
      if (a[1] === null) return;
      if ((r == "") && (a[0] != null)) {
          r = r + Dagaz.Model.posToString(a[0][0]);
      }
      if ((a[1] !== null) && (a[0][0] != a[1][0])) {
          r = r + Dagaz.Model.posToString(a[1][0]);
      }
  });
  return r;
}

var getName = function() {
  var str = window.location.pathname.toString();
  var result = str.match(/\/([^.\/]+)\./);
  if (result) {
      return result[1].replace("-board", "").replace("-ai", "");
  } else {
      return str;
  }
}

var badName = function(str) {
  var result = str.match(/[?&]game=([^&*]*)/);
  if (result) {
      return result[1] != getName();
  } else {
      return true;
  }
}

var getCookie = function() {
  var result = localStorage.getItem('dagaz.setup');
  if (result) {
      if (badName(result)) return "";
      return result;
  } else {
      return "";
  }
}

var getSetup = function(setup) {
  var str = window.location.search.toString();
  if (setup) {
      str = setup;
  }
  var result = str.match(/[?&]setup=([^&]*)/);
  if (result) {
      return result[1];
  } else {
      str = getCookie();
      result = str.match(/[?&]setup=([^&]*)/);
      if (result) {
          return result[1];
      } else {
          return "";
      }
  }
}

var getTurn = function(setup) {
  var str = window.location.search.toString();
  if (setup) {
      str = setup;
  }
  var result = str.match(/[?&]turn=(\d+)/);
  if (result) {
      return result[1];
  } else {
      str = getCookie();
      result = str.match(/[?&]turn=(\d+)/);
      if (result) {
          return result[1];
      } else {
          return "";
      }
  }
}

var createPiece = function(design, c) {
  if (c == 'P') return Dagaz.Model.createPiece(design.getPieceType("Man"), 1);
  if (c == 'p') return Dagaz.Model.createPiece(design.getPieceType("Man"), 2);
  if (c == 'K') return Dagaz.Model.createPiece(design.getPieceType("King"), 1);
  if (c == 'k') return Dagaz.Model.createPiece(design.getPieceType("King"), 2);
  return null;
}

Dagaz.Model.setup = function(board, init) {
  var design = Dagaz.Model.design;
  var setup  = getSetup(init);
  var player = 1;
  if (setup) {
      board.clear();
      var pos = 0;
      for (var i = 0; i < setup.length; i++) {
           var c = setup[i];
           if (c != '/') {
               if ((c >= '0') && (c <= '9')) {
                   pos += +c;
               } else {
                   var piece = createPiece(design, c);
                   board.setPiece(pos, piece);
                   pos++;
               }
               if (pos >= Dagaz.Model.WIDTH * Dagaz.Model.HEIGHT) break;
           }
      }
      var turn = getTurn(init);
      if (turn) {
          board.turn   = +turn;
          board.player = design.currPlayer(board.turn);
      }
  }
}

var getPieceNotation = function(design, piece) {
  var r = 'P';
  if (piece.type == design.getPieceType("King")) r = 'K';
  if (piece.player > 1) {
      return r.toLowerCase();
  }
  return r;
}

Dagaz.Model.getSetup = function(design, board) {
  var str = "?turn=" + board.turn + ";&setup=";
  var k = 0; var c = 0;
  for (var pos = 0; pos < Dagaz.Model.WIDTH * Dagaz.Model.HEIGHT; pos++) {
       if (k >= Dagaz.Model.WIDTH) {
           if (c > 0) {
               str += c;
           }
           str += "/";
           k = 0;
           c = 0;
       }
       k++;
       var piece = board.getPiece(pos);
       if (piece === null) {
           if (c > 8) {
               str += c;
               c = 0;
           }
           c++;
       } else {
           if (c > 0) {
               str += c;
           }
           c = 0;
           str += getPieceNotation(design, piece);
       }
  }
  if (c > 0) {
      str += c;
  }
  if (board.turn == 0) {
      str += " w";
  } else {
      str += " b";
  }
  if (Dagaz.Controller.persistense == "setup") {
      var s = str + "&game=" + getName() + "*";
      localStorage.setItem('dagaz.setup', s);
  }
  return str;
}

var clearGame = Dagaz.Controller.clearGame;

Dagaz.Controller.clearGame = function() {
   localStorage.setItem('dagaz.setup', '');
   if (!_.isUndefined(clearGame)) {
       clearGame();
   }
}

})();
<|endoftext|>


<commit_before>// ==UserScript==
// @name            Novel downloader
// @name:vi         Novel downloader
// @namespace       https://lelinhtinh.github.io/
// @description     Tải truyện chữ định dạng EPUB.
// @description:vi  Tải truyện chữ định dạng EPUB.
// @version         1.0.0
// @icon            https://i.imgur.com/MkEc6dg.png
// @author          lelinhtinh
// @oujs:author     baivong
// @license         MIT; https://baivong.mit-license.org/license.txt
// @match           https://truyenfull.vn/*
// @require         https://unpkg.com/jszip@3.2.1/dist/jszip.min.js
// @require         https://unpkg.com/ejs@2.6.1/ejs.min.js
// @require         https://unpkg.com/jepub@2.1.1/dist/jepub.min.js
// @require         https://unpkg.com/file-saver@2.0.2/dist/FileSaver.min.js
// @require         https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js?v=a834d46
// @noframes
// @connect         self
// @supportURL      https://github.com/lelinhtinh/Userscript/issues
// @run-at          document-end
// @grant           GM_xmlhttpRequest
// @grant           GM.xmlHttpRequest
// ==/UserScript==

const configs = {
  'truyenfull.vn': {
    title: '.title[itemprop="name"]',
    author: '.info [itemprop="author"]',
    publisher: '.info .source',
    description: '.desc-text[itemprop="description"]',
    tags: '.info [itemprop="genre"]',
    cover: '.book [itemprop="image"]',
    first: '.list-chapter a',
    chapter: {
      id: '{{ id }}/',
      title: '.chapter-title',
      content: '#chapter-c',
      next: '#next_chap',
    },
  },
  /**
   * Example configs
   * @property {(String|Function)} - selector
   */
  'domain.test': {
    title: 'selector',
    author: 'selector',
    publisher: 'selector',
    description: 'selector', // optional
    tags: 'selector', // optional
    cover: 'selector', // optional
    first: 'selector',
    total: 'selector', // optional
    chapter: {
      id: 'chuong-{{ id }}',
      title: 'selector',
      content: 'selector',
      next: 'selector',
    },
    placement: 'top-right', // top-left|top-right|bottom-left|bottom-left
  },
};

/* === DO NOT CHANGE === */
const DEBUG = 3;

function debug(name, obj, level = 1) {
  if (!DEBUG || typeof DEBUG !== 'number' || typeof level !== 'number' || level > 3 || level < 1) return;
  if (level > DEBUG) return;

  const clone = () => {
    if (Array.isArray(obj)) {
      return Object.assign([], obj);
    } else if (obj.constructor === Object) {
      return Object.assign({}, obj);
    }
    return obj;
  };

  const palette = {
    1: '#1e63b8',
    2: '#ecac43',
    3: '#fe2c32',
  };
  console.log('%c' + name, 'color:' + palette[level], clone());
}

function showError(mess) {
  debug('Error', mess, 2);
}

function showFatal(mess) {
  debug('Fatal', mess, 3);
  throw mess;
}

function cleanSource(ele) {
  ele.querySelectorAll('style, script').forEach(ss => ss.remove());
  let str = ele.innerHTML;
  str = str.replace(/[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]+/gm, ''); // eslint-disable-line
  return str.trim();
}

function saveEpub(e) {
  if (e) {
    e.preventDefault();
    e.stopPropagation();
  }
}

function readSelector(
  selector,
  opts = {
    html: false,
    multi: false,
    attr: false,
  }
) {
  if (typeof selector === 'string') {
    try {
      if (opts.multi) {
        let temp = [];
        document.querySelectorAll(selector).forEach(ele => {
          ele = ele.textContent.trim();
          if (ele) temp.push(ele);
        });
        return temp;
      } else {
        let ele = document.querySelector(selector);
        if (opts.attr) {
          return ele.getAttribute(opts.attr);
        } else if (opts.html) {
          return cleanSource(ele);
        }
        return ele.textContent.trim();
      }
    } catch (e) {
      showError(e.message);
    }
    return null;
  } else if (typeof selector === 'function') {
    const result = selector();
    return result;
  }
  showFatal(selector);
}

function getChapter() {
  if (!novel.next) novel.next = novel.first;
  debug('Chapter URL', novel.next);
  fetch(novel.next)
    .then(res => res.text())
    .then(res => {
      const parser = new DOMParser();
      return parser.parseFromString(res, 'text/html');
    })
    .then(res => {
      let title = res.querySelector(novel.chapter.title);
      debug('Chapter title', title);
      let content = res.querySelector(novel.chapter.content);
      debug('Chapter content', content);
      let next = res.querySelector(novel.chapter.next);
      debug('Chapter next', next);

      if (content === null) {
        showError('Rỗng');
      } else {
        content = content.innerHTML;
      }

      if (title === null) {
        title = content.match(/(Chương .+?)(<|\n)/i);
        title = title === null ? 'Không đề' : title[1].trim();
      } else {
        title = title.textContent.trim();
      }

      if (!next || !next.href || !novel.chapter.patt.test(next.href)) {
        saveEpub();
        return;
      }
      next = next.href;
    });
}

function genEpub() {
  $download.removeEventListener('click', createEpub);
  $download.removeEventListener('contextmenu', setChapterBegin);
  $download.addEventListener('click', saveEpub);

  const props = {
    title: novel.title,
    author: novel.author,
    publisher: novel.publisher,
  };
  if (novel.description) props.description = novel.description;
  if (novel.tags.length) props.tags = novel.tags;

  jepub = new jEpub();
  jepub.init(props).uuid(novel.credit);
  debug('jEpub', jepub);

  GM.xmlHttpRequest({
    method: 'GET',
    url: novel.cover,
    responseType: 'arraybuffer',
    onload: function(response) {
      jepub.cover(response.response);
    },
    onerror: function(err) {
      console.error(err);
    },
  });

  getChapter();
}

function createEpub(e) {
  e.preventDefault();
  e.stopPropagation();
  genEpub();
}

function setChapterBegin(e) {
  e.preventDefault();
  e.stopPropagation();

  if (!novel.chapter.id) {
    showError('Không có cấu hình Chapter ID');
    return;
  }

  let chapId = novel.first.replace(novel.credit, '');
  try {
    chapId = chapId.match(novel.chapter.patt)[1];
  } catch (e) {
    showFatal('Cấu hình Chapter ID không đúng');
  }

  const beginId = prompt('Nhập ID chương truyện bắt đầu tải:', chapId);
  if (beginId !== null) chapId = beginId;

  novel.first = novel.credit + novel.chapter.id.replace(/\{\{\s*id\s*\}\}/i, chapId);
  debug('URL first', novel.first);

  genEpub();
}

function init() {
  $download = document.querySelector('#novelDownloaderButton');
  if ($download !== null) return;

  novel = configs[location.host];
  if (!novel) return;

  if (
    !novel.title ||
    !novel.first ||
    !novel.chapter ||
    !novel.chapter.title ||
    !novel.chapter.content ||
    !novel.chapter.next
  ) {
    showFatal('Cấu hình không hợp lệ');
  }

  novel.title = readSelector(novel.title);
  if (!novel.title) return;
  novel.first = readSelector(novel.first, { attr: 'href' });
  if (!novel.first) return;

  novel.credit = location.origin + location.pathname;
  novel.author = readSelector(novel.author) || 'Khuyết danh';
  novel.publisher = readSelector(novel.publisher) || 'Sưu tầm';
  novel.description = readSelector(novel.description, { html: true });
  novel.tags = readSelector(novel.tags, { multi: true });
  novel.cover = readSelector(novel.cover, { attr: 'src' });

  let patt = novel.chapter.id.replace(/([./?|()'"$*^])/g, '\\$1');
  patt = patt.replace(/\{\{\s*id\s*\}\}/i, '(.+?)');
  patt = new RegExp(patt);
  novel.chapter.patt = patt;
  debug('Novel', novel);

  $download = document.createElement('a');
  $download.id = 'novelDownloaderButton';
  $download.className = 'novel-btn novel-place-' + (novel.placement || 'top-right');
  $download.textContent = 'Tải EPUB';
  $download.href = '#download-epub';

  $download.addEventListener('click', createEpub);
  $download.addEventListener('contextmenu', setChapterBegin);

  document.body.appendChild($download);
  debug('Download button', $download);
}

/**
 * @type Document
 */
let $download;
let novel;
let jepub;

try {
  init();
} catch (e) {
  console.error(e);
}
<commit_msg>Fix undefined in selector<commit_after>// ==UserScript==
// @name            Novel downloader
// @name:vi         Novel downloader
// @namespace       https://lelinhtinh.github.io/
// @description     Tải truyện chữ định dạng EPUB.
// @description:vi  Tải truyện chữ định dạng EPUB.
// @version         1.0.0
// @icon            https://i.imgur.com/MkEc6dg.png
// @author          lelinhtinh
// @oujs:author     baivong
// @license         MIT; https://baivong.mit-license.org/license.txt
// @match           https://truyenfull.vn/*
// @require         https://unpkg.com/jszip@3.2.1/dist/jszip.min.js
// @require         https://unpkg.com/ejs@2.6.1/ejs.min.js
// @require         https://unpkg.com/jepub@2.1.1/dist/jepub.min.js
// @require         https://unpkg.com/file-saver@2.0.2/dist/FileSaver.min.js
// @require         https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js?v=a834d46
// @noframes
// @connect         self
// @supportURL      https://github.com/lelinhtinh/Userscript/issues
// @run-at          document-end
// @grant           GM_xmlhttpRequest
// @grant           GM.xmlHttpRequest
// ==/UserScript==

const configs = {
  'truyenfull.vn': {
    title: '.title[itemprop="name"]',
    author: '.info [itemprop="author"]',
    publisher: '.info .source',
    description: '.desc-text[itemprop="description"]',
    tags: '.info [itemprop="genre"]',
    cover: '.book [itemprop="image"]',
    first: '.list-chapter a',
    chapter: {
      id: '{{ id }}/',
      title: '.chapter-title',
      content: '#chapter-c',
      next: '#next_chap',
    },
  },
  /**
   * Example configs
   * @property {(String|Function)} - selector
   */
  'domain.test': {
    title: 'selector',
    author: 'selector',
    publisher: 'selector',
    description: 'selector', // optional
    tags: 'selector', // optional
    cover: 'selector', // optional
    first: 'selector',
    total: 'selector', // optional
    chapter: {
      id: 'chuong-{{ id }}',
      title: 'selector',
      content: 'selector',
      next: 'selector',
    },
    placement: 'top-right', // top-left|top-right|bottom-left|bottom-left
  },
};

/* === DO NOT CHANGE === */
const DEBUG = 3;

function debug(name, obj, level = 1) {
  if (!DEBUG || typeof DEBUG !== 'number' || typeof level !== 'number' || level > 3 || level < 1) return;
  if (level > DEBUG) return;

  const clone = () => {
    if (obj === null) return null;
    if (obj === undefined) return undefined;
    if (Array.isArray(obj)) {
      return Object.assign([], obj);
    } else if (obj.constructor === Object) {
      return Object.assign({}, obj);
    }
    return obj;
  };

  const palette = {
    1: '#1e63b8',
    2: '#ecac43',
    3: '#fe2c32',
  };
  console.log('%c' + name, 'color:' + palette[level], clone());
}

function showError(mess) {
  debug('Error', mess, 2);
}

function showFatal(mess) {
  debug('Fatal', mess, 3);
  throw mess;
}

function cleanSource(ele) {
  ele.querySelectorAll('style, script').forEach(ss => ss.remove());
  let str = ele.innerHTML;
  str = str.replace(/[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]+/gm, ''); // eslint-disable-line
  return str.trim();
}

function saveEpub(e) {
  if (e) {
    e.preventDefault();
    e.stopPropagation();
  }
}

function readSelector(
  selector,
  opts = {
    html: false,
    multi: false,
    attr: false,
  }
) {
  if (typeof selector === 'string') {
    try {
      if (opts.multi) {
        let temp = [];
        document.querySelectorAll(selector).forEach(ele => {
          ele = ele.textContent.trim();
          if (ele) temp.push(ele);
        });
        return temp;
      } else {
        let ele = document.querySelector(selector);
        if (opts.attr) {
          return ele.getAttribute(opts.attr);
        } else if (opts.html) {
          return cleanSource(ele);
        }
        return ele.textContent.trim();
      }
    } catch (e) {
      showError(e.message);
    }
    return null;
  } else if (typeof selector === 'function') {
    const result = selector();
    return result;
  }
  return null;
}

function getChapter() {
  if (!novel.next) novel.next = novel.first;
  debug('Chapter URL', novel.next);
  fetch(novel.next)
    .then(res => res.text())
    .then(res => {
      const parser = new DOMParser();
      return parser.parseFromString(res, 'text/html');
    })
    .then(res => {
      let title = res.querySelector(novel.chapter.title);
      debug('Chapter title', title);
      let content = res.querySelector(novel.chapter.content);
      debug('Chapter content', content);
      let next = res.querySelector(novel.chapter.next);
      debug('Chapter next', next);

      if (content === null) {
        showError('Rỗng');
      } else {
        content = content.innerHTML;
      }

      if (title === null) {
        title = content.match(/(Chương .+?)(<|\n)/i);
        title = title === null ? 'Không đề' : title[1].trim();
      } else {
        title = title.textContent.trim();
      }

      if (!next || !next.href || !novel.chapter.patt.test(next.href)) {
        saveEpub();
        return;
      }
      next = next.href;
    });
}

function genEpub() {
  $download.removeEventListener('click', createEpub);
  $download.removeEventListener('contextmenu', setChapterBegin);
  $download.addEventListener('click', saveEpub);

  const props = {
    title: novel.title,
    author: novel.author,
    publisher: novel.publisher,
  };
  if (novel.description) props.description = novel.description;
  if (novel.tags.length) props.tags = novel.tags;

  jepub = new jEpub();
  jepub.init(props).uuid(novel.credit);
  debug('jEpub', jepub);

  GM.xmlHttpRequest({
    method: 'GET',
    url: novel.cover,
    responseType: 'arraybuffer',
    onload: function(response) {
      jepub.cover(response.response);
    },
    onerror: function(err) {
      console.error(err);
    },
  });

  getChapter();
}

function createEpub(e) {
  e.preventDefault();
  e.stopPropagation();
  genEpub();
}

function setChapterBegin(e) {
  e.preventDefault();
  e.stopPropagation();

  if (!novel.chapter.id) {
    showError('Không có cấu hình Chapter ID');
    return;
  }

  let chapId = novel.first.replace(novel.credit, '');
  try {
    chapId = chapId.match(novel.chapter.patt)[1];
  } catch (e) {
    showFatal('Cấu hình Chapter ID không đúng');
  }

  const beginId = prompt('Nhập ID chương truyện bắt đầu tải:', chapId);
  if (beginId !== null) chapId = beginId;

  novel.first = novel.credit + novel.chapter.id.replace(/\{\{\s*id\s*\}\}/i, chapId);
  debug('URL first', novel.first);

  genEpub();
}

function init() {
  $download = document.querySelector('#novelDownloaderButton');
  if ($download !== null) return;

  novel = configs[location.host];
  if (!novel) return;

  if (
    !novel.title ||
    !novel.first ||
    !novel.chapter ||
    !novel.chapter.title ||
    !novel.chapter.content ||
    !novel.chapter.next
  ) {
    showFatal('Cấu hình không hợp lệ');
  }

  novel.title = readSelector(novel.title);
  if (!novel.title) return;
  novel.first = readSelector(novel.first, { attr: 'href' });
  if (!novel.first) return;

  novel.credit = location.origin + location.pathname;
  novel.author = readSelector(novel.author) || 'Khuyết danh';
  novel.publisher = readSelector(novel.publisher) || 'Sưu tầm';
  novel.description = readSelector(novel.description, { html: true });
  novel.tags = readSelector(novel.tags, { multi: true });
  novel.cover = readSelector(novel.cover, { attr: 'src' });

  let patt = novel.chapter.id.replace(/([./?|()'"$*^])/g, '\\$1');
  patt = patt.replace(/\{\{\s*id\s*\}\}/i, '(.+?)');
  patt = new RegExp(patt);
  novel.chapter.patt = patt;
  debug('Novel', novel);

  $download = document.createElement('a');
  $download.id = 'novelDownloaderButton';
  $download.className = 'novel-btn novel-place-' + (novel.placement || 'top-right');
  $download.textContent = 'Tải EPUB';
  $download.href = '#download-epub';

  $download.addEventListener('click', createEpub);
  $download.addEventListener('contextmenu', setChapterBegin);

  document.body.appendChild($download);
  debug('Download button', $download);
}

/**
 * @type Document
 */
let $download;
let novel;
let jepub;

try {
  init();
} catch (e) {
  console.error(e);
}
<|endoftext|>


<commit_before>module.exports = {
	getWP : function(theURL, bot, channelID, cheerio) {
		var request = require('request');
		request(theURL, function (error, response, body) {
			if (!error && response.statusCode == 200) {

				imageStart = body.indexOf("//4walled.cc/show");
				imageUrl = "https:" + body.substring((imageStart), (imageStart + 290))
				resStart = imageUrl.indexOf("Resolution");
				resText = imageUrl.substring((resStart), (resStart + 40))
				resEnd = resText.indexOf("'")
				resolution = resText.substring(0, resEnd)
				//imageEnd = imageUrl.indexOf("target") - 2
				//imageUrl = imageUrl.substring(0, imageEnd)

				var parsed = cheerio.load(body);

				var images = [];
				parsed('a').map(function(i, link) {
					var href = cheerio(link).attr('href')
					if (!href.match('//4walled.cc')) return
					images.push(href)
				});

				console.log(images);

				var imageUrl = "http:" + images[Math.floor(Math.random() * images.length) + 6];
				console.log(imageUrl);

					request(imageUrl, function (error, response, body) {
						if (!error && response.statusCode == 200) {
							imageStart = body.indexOf("//4walled.cc/src");
							imageUrl = "https:" + body.substring((imageStart), (imageStart + 90))
							imageEnd = imageUrl.indexOf("\"")
							imageUrl = imageUrl.substring(0, imageEnd)

							bot.sendMessage({
								to: channelID,
								message: imageUrl + " " + resolution
							})

						}

						else{
							bot.sendMessage({
								to: channelID,
								message: "Image not found"
							})
						}
					})
			}
		})
	}
}
<commit_msg>Update 4walled.js<commit_after>module.exports = {
	getWP : function(theURL, bot, channelID, cheerio) {
		var request = require('request');
		request(theURL, function (error, response, body) {
			if (!error && response.statusCode == 200) {

				//imageStart = body.indexOf("//4walled.cc/show");
				//imageUrl = "https:" + body.substring((imageStart), (imageStart + 290))
				//resStart = imageUrl.indexOf("Resolution");
				//resText = imageUrl.substring((resStart), (resStart + 40))
				//resEnd = resText.indexOf("'")
				//resolution = resText.substring(0, resEnd)
				//imageEnd = imageUrl.indexOf("target") - 2
				//imageUrl = imageUrl.substring(0, imageEnd)

				var parsed = cheerio.load(body);

				var images = [];
				parsed('a').map(function(i, link) {
					var href = cheerio(link).attr('href')
					if (!href.match('//4walled.cc')) return
					images.push(href)
				});

				var random = Math.floor(Math.random() * images.length);
				while (random <= 4) {
					random = Math.floor(Math.random() * images.length);
				}

				var imageUrl = "http:" + images[random];

					request(imageUrl, function (error, response, body) {
						if (!error && response.statusCode == 200) {
							imageStart = body.indexOf("//4walled.cc/src");
							imageUrl = "https:" + body.substring((imageStart), (imageStart + 200))
							resStart = body.indexOf("<span id=\"info_resolution\">");
							resStart = body.substring(resStart + 1, resStart + 50);
							low = resStart.indexOf(">");
							resStart = resStart.substring(low + 1, resStart.indexOf("<"))
							resolution = "Resolution: " + resStart;

							console.log(resStart);
							imageEnd = imageUrl.indexOf("\"")
							imageUrl = imageUrl.substring(0, imageEnd)

							bot.sendMessage({
								to: channelID,
								message: imageUrl + " " + resolution
							})

						}

						else{
							bot.sendMessage({
								to: channelID,
								message: "Image not found"
							})
						}
					})
			}
		})
	}
}
<|endoftext|>


<commit_before>var array_concat = (a,b) => a.concat(b);
var array_unique = (n, i, a) => a.indexOf(n) === i;

function processReport(results) {
    var knownIdlNames = results
        .map(r => r.idl && r.idl.idlNames ? Object.keys(r.idl.idlNames).filter(n => n !== "_dependencies") : [], [])
        .reduce(array_concat);
    var idlNamesIndex = {};
    knownIdlNames.forEach(name => {
        idlNamesIndex[name] = results.filter(spec => {
            return spec.idl &&
                spec.idl.idlNames &&
                spec.idl.idlNames[name];
        });
    });

    // TODO: we may end up with different variants of the WebIDL spec
    var WebIDLSpec = results.find(spec => (spec.shortname === 'WebIDL-1'));

    return results
        .sort((a, b) => {
            var A = a.title.toUpperCase();
            var B = b.title.toUpperCase();
            if (A < B) {
                return -1;
            }
            if (A > B) {
                return 1;
            }
            return 0;
        })
        .map(spec => {
            var idlDfns = (spec.idl && spec.idl.idlNames) ?
                Object.keys(spec.idl.idlNames).filter(name => (name !== '_dependencies')) : [];
            var idlDeps = (spec.idl && spec.idl.externalDependencies) ?
                spec.idl.externalDependencies : [];
            var report = {
                hasNormativeRefs: (spec.refs.normative &&
                    (spec.refs.normative.length > 0)),
                referencesWebIDL: (spec.refs.normative &&
                    spec.refs.normative.find(ref =>
                        ref.name.match(/^WebIDL/i) ||
                            (ref.url === WebIDLSpec.url) ||
                            (ref.url === WebIDLSpec.latest))
                ),
                hasIdl: !((Object.keys(spec.idl).length === 0) ||
                    (!spec.idl.idlNames && !spec.idl.message) ||
                    (spec.idl.idlNames &&
                        (Object.keys(spec.idl.idlNames).length === 1) &&
                        (Object.keys(spec.idl.idlExtendedNames).length === 0))),
                hasInvalidIdl: !!(!spec.idl.idlNames && spec.idl.message),
                unknownIdlNames: idlDeps
                    .filter(name => knownIdlNames.indexOf(name) === -1)
                    .sort(),
                redefinedIdlNames: idlDfns
                    .filter(name => (idlNamesIndex[name].length > 1))
                    .map(name => {
                        return {
                            name,
                            refs: idlNamesIndex[name].filter(ref => (ref.url !== spec.url))
                        };
                    }),
                missingReferences: idlDeps
                    .filter(name => knownIdlNames.indexOf(name) !== -1)
                    .map(name => {
                        var refs = idlNamesIndex[name];
                        var ref = null;
                        if (spec.refs && spec.refs.normative) {
                            ref = refs.find(s =>
                                !!spec.refs.normative.find(r =>
                                    s.versions.includes(r.url) ||
                                    s.versions.includes(r.url.replace(/^http:/, 'https:')) ||
                                    s.versions.includes(r.url.replace(/^https:/, 'http:'))
                                )
                            );
                        }
                        return (ref ? null : {
                            name,
                            refs
                        });
                    })
                    .filter(i => !!i)
            };
            report.ok = report.hasNormativeRefs &&
                report.hasIdl &&
                !report.hasInvalidIdl &&
                report.referencesWebIDL &&
                (!report.unknownIdlNames || (report.unknownIdlNames.length === 0)) &&
                (!report.redefinedIdlNames || (report.redefinedIdlNames.length === 0)) &&
                (!report.missingReferences || (report.missingReferences.length === 0));
            var res = {
                title: spec.title,
                shortname: spec.shortname,
                date: spec.date,
                url: spec.url,
                latest: spec.latest,
                report
            };
            return res;
        });
}

function generateReportPerSpec(results) {
    var count = 0;
    var w = console.log.bind(console);

    // Compute report information
    results = processReport(results);

    w('# Reffy crawl report');
    w();
    w('## Specifications without known issues');
    w();
    count = 0;
    results
        .filter(spec => spec.report.ok)
        .forEach(spec => {
            count += 1;
            w('- [' + spec.title + '](' + (spec.latest || spec.url) + ')');
        });
    w();
    w('=> ' + count + ' specification' + ((count > 1) ? 's' : '') + ' found');
    w();
    w();

    w('## Specifications with possible issues');
    w();
    results
        .filter(spec => !spec.report.ok)
        .forEach(spec => {
            w('### ' + spec.title);
            w();
            w('Crawl info:');
            w('- URL: [' + (spec.latest ?
                ((spec.latest.indexOf('www.w3.org/TR/') !== -1) ? 'Latest published version' : 'Editor\'s Draft') :
                ((spec.url.indexOf('spec.whatwg.org') !== -1) ? 'Living Standard' : 'Initial URL'))
                + '](' + (spec.latest || spec.url) + ')');
            w('- Shortname: ' + (spec.shortname || 'no shortname'));
            w('- Date: ' + (spec.date || 'unknown'));
            w();

            var report = spec.report;
            w('Potential issue(s):');
            if (!report.hasNormativeRefs) {
                w('- No normative references found');
            }
            if (!report.hasIdl) {
                w('- No WebIDL definitions found');
            }
            if (report.hasInvalidIdl) {
                w('- Invalid WebIDL content found');
            }
            if (report.hasIdl && !report.referencesWebIDL) {
                w('- Spec uses WebIDL but does not reference it normatively');
            }
            if (report.unknownIdlNames &&
                (report.unknownIdlNames.length > 0)) {
                w('- Unknown WebIDL names used: ' +
                    report.unknownIdlNames.map(name => '`' + name + '`').join(', '));
            }
            if (report.redefinedIdlNames &&
                (report.redefinedIdlNames.length > 0)) {
                w('- WebIDL names also defined elsewhere: ');
                report.redefinedIdlNames.map(i => {
                    w(' * `' + i.name + '` also defined in ' +
                        i.refs.map(ref => ('[' + ref.title + '](' + (ref.latest || ref.url) + ')')).join(' and '));
                });
            }
            if (report.missingReferences &&
                (report.missingReferences.length > 0)) {
                w('- Missing references for WebIDL names: ');
                report.missingReferences.map(i => {
                    w(' * `' + i.name + '` defined in ' +
                        i.refs.map(ref => ('[' + ref.title + '](' + (ref.latest || ref.url) + ')')).join(' or '));
                });
            }
            w();
            w();
        });
    w();
    w();
}

function generateReport(results) {
    var count = 0;
    var w = console.log.bind(console);

    // Compute report information
    results = processReport(results);

    w('# Reffy crawl report');
    w();

    w('## Specifications crawled');
    w();
    count = results.length;
    w('- ' + count + ' specification' + ((count > 1) ? 's' : '') + ' crawled');
    w();
    w();

    count = 0;
    w('## Specifications without normative dependencies');
    w();
    results
        .filter(spec => !spec.report.hasNormativeRefs)
        .forEach(spec => {
            count += 1;
            w('- [' + spec.title + '](' + (spec.latest || spec.url) + ')');
        });
    w();
    w('=> ' + count + ' specification' + ((count > 1) ? 's' : '') + ' found');
    w();
    w('**NB:** it may be normal!');
    w();
    w();

    count = 0;
    w('## Specifications without WebIDL definitions');
    w();
    results
        .filter(spec => !spec.report.hasIdl)
        .forEach(spec => {
            count += 1;
            w('- [' + spec.title + '](' + (spec.latest || spec.url) + ')');
        });
    w();
    w('=> ' + count + ' specification' + ((count > 1) ? 's' : '') + ' found');
    w();
    w();

    count = 0;
    w('## List of specifications with invalid WebIDL content');
    w();
    results
        .filter(spec => spec.report.hasInvalidIdl)
        .forEach(spec => {
            count += 1;
            w('- [' + spec.title + '](' + (spec.latest || spec.url) + ')');
        });
    w();
    w('=> ' + count + ' specification' + ((count > 1) ? 's' : '') + ' found');
    w();
    w('**NB:** this may be due to WebIDL having evolved in the meantime');
    w();
    w();

    count = 0;
    w('## Specifications that use WebIDL but do not reference the WebIDL spec');
    w();
    results.forEach(spec => {
        if (spec.report.hasIdl && !spec.report.referencesWebIDL) {
            count += 1;
            w('- [' + spec.title + '](' + (spec.latest || spec.url) + ')');
        }
    });
    w();
    w('=> ' + count + ' specification' + ((count > 1) ? 's' : '') + ' found');
    w();
    w();

    count = 0;
    w('## List of WebIDL names not defined in the specifications crawled');
    w();
    var idlNames = {};
    results.forEach(spec => {
        if (!spec.report.unknownIdlNames ||
            (spec.report.unknownIdlNames.length === 0)) {
            return;
        }
        spec.report.unknownIdlNames.forEach(name => {
            if (!idlNames[name]) {
                idlNames[name] = [];
            }
            idlNames[name].push(spec);
        });
    });
    Object.keys(idlNames).sort().forEach(name => {
        count += 1;
        w('- `' + name + '` used in ' +
            idlNames[name].map(ref => ('[' + ref.title + '](' + (ref.latest || ref.url) + ')')).join(', '));
    });
    w();
    w('=> ' + count + ' WebIDL name' + ((count > 1) ? 's' : '') + ' found');
    w();
    w('**NB:** some of them are likely type errors in specs');
    w('(e.g. "int" does not exist, "Array" cannot be used on its own, etc.)');
    w();
    w();

    count = 0;
    w('## List of WebIDL names defined in more than one spec');
    w();
    idlNames = {};
    results.forEach(spec => {
        if (!spec.report.redefinedIdlNames ||
            (spec.report.redefinedIdlNames.length === 0)) {
            return;
        }
        spec.report.redefinedIdlNames.forEach(i => {
            if (!idlNames[i.name]) {
                idlNames[i.name] = [];
            }
            idlNames[i.name].push(spec);
        });
    });
    Object.keys(idlNames).sort().forEach(name => {
        count += 1;
        w('- `' + name + '` defined in ' +
            idlNames[name].map(ref => ('[' + ref.title + '](' + (ref.latest || ref.url) + ')')).join(' and '));
    });
    w();
    w('=> ' + count + ' WebIDL name' + ((count > 1) ? 's' : '') + ' found');
    w();
    w();

    count = 0;
    var countrefs = 0;
    w('## Missing references for WebIDL names');
    w();
    results.forEach(spec => {
        if (spec.report.missingReferences &&
            (spec.report.missingReferences.length > 0)) {
            count += 1;
            if (spec.report.missingReferences.length === 1) {
                countrefs += 1;
                let i = spec.report.missingReferences[0];
                w('- [' + spec.title + '](' + (spec.latest || spec.url) + ')' +
                    ' uses `' + i.name + '` but does not reference ' +
                    i.refs.map(ref => ('[' + ref.title + '](' + (ref.latest || ref.url) + ')')).join(' or '));
            }
            else {
                w('- [' + spec.title + '](' + (spec.latest || spec.url) + ') uses:');
                spec.report.missingReferences.map(i => {
                    countrefs += 1;
                    w(' * `' + i.name + '` but does not reference ' +
                        i.refs.map(ref => ('[' + ref.title + '](' + (ref.latest || ref.url) + ')')).join(' or '));
                });
            }
        }
    });
    w();
    w('=> ' + countrefs + ' missing reference' + ((count > 1) ? 's' : '') +
        ' found in ' + count + ' specification' + ((count > 1) ? 's' : ''));
}

/**************************************************
Code run if the code is run as a stand-alone module
**************************************************/
if (require.main === module) {
    var specResultsPath = process.argv[2];
    var perSpec = !!process.argv[3];
    if (!specResultsPath) {
        console.error("Required filename parameter missing");
        process.exit(2);
    }
    var specResults;
    try {
        specResults = require(specResultsPath);
    } catch(e) {
        console.error("Impossible to read " + specresultsPath + ": " + e);
        process.exit(3);
    }
    if (perSpec) {
        generateReportPerSpec(specResults);
    }
    else {
        generateReport(specResults);
    }
}
<commit_msg>Use 4 spaces indent in study report<commit_after>var array_concat = (a,b) => a.concat(b);
var array_unique = (n, i, a) => a.indexOf(n) === i;

function processReport(results) {
    var knownIdlNames = results
        .map(r => r.idl && r.idl.idlNames ? Object.keys(r.idl.idlNames).filter(n => n !== "_dependencies") : [], [])
        .reduce(array_concat);
    var idlNamesIndex = {};
    knownIdlNames.forEach(name => {
        idlNamesIndex[name] = results.filter(spec => {
            return spec.idl &&
                spec.idl.idlNames &&
                spec.idl.idlNames[name];
        });
    });

    // TODO: we may end up with different variants of the WebIDL spec
    var WebIDLSpec = results.find(spec => (spec.shortname === 'WebIDL-1'));

    return results
        .sort((a, b) => {
            var A = a.title.toUpperCase();
            var B = b.title.toUpperCase();
            if (A < B) {
                return -1;
            }
            if (A > B) {
                return 1;
            }
            return 0;
        })
        .map(spec => {
            var idlDfns = (spec.idl && spec.idl.idlNames) ?
                Object.keys(spec.idl.idlNames).filter(name => (name !== '_dependencies')) : [];
            var idlDeps = (spec.idl && spec.idl.externalDependencies) ?
                spec.idl.externalDependencies : [];
            var report = {
                hasNormativeRefs: (spec.refs.normative &&
                    (spec.refs.normative.length > 0)),
                referencesWebIDL: (spec.refs.normative &&
                    spec.refs.normative.find(ref =>
                        ref.name.match(/^WebIDL/i) ||
                            (ref.url === WebIDLSpec.url) ||
                            (ref.url === WebIDLSpec.latest))
                ),
                hasIdl: !((Object.keys(spec.idl).length === 0) ||
                    (!spec.idl.idlNames && !spec.idl.message) ||
                    (spec.idl.idlNames &&
                        (Object.keys(spec.idl.idlNames).length === 1) &&
                        (Object.keys(spec.idl.idlExtendedNames).length === 0))),
                hasInvalidIdl: !!(!spec.idl.idlNames && spec.idl.message),
                unknownIdlNames: idlDeps
                    .filter(name => knownIdlNames.indexOf(name) === -1)
                    .sort(),
                redefinedIdlNames: idlDfns
                    .filter(name => (idlNamesIndex[name].length > 1))
                    .map(name => {
                        return {
                            name,
                            refs: idlNamesIndex[name].filter(ref => (ref.url !== spec.url))
                        };
                    }),
                missingReferences: idlDeps
                    .filter(name => knownIdlNames.indexOf(name) !== -1)
                    .map(name => {
                        var refs = idlNamesIndex[name];
                        var ref = null;
                        if (spec.refs && spec.refs.normative) {
                            ref = refs.find(s =>
                                !!spec.refs.normative.find(r =>
                                    s.versions.includes(r.url) ||
                                    s.versions.includes(r.url.replace(/^http:/, 'https:')) ||
                                    s.versions.includes(r.url.replace(/^https:/, 'http:'))
                                )
                            );
                        }
                        return (ref ? null : {
                            name,
                            refs
                        });
                    })
                    .filter(i => !!i)
            };
            report.ok = report.hasNormativeRefs &&
                report.hasIdl &&
                !report.hasInvalidIdl &&
                report.referencesWebIDL &&
                (!report.unknownIdlNames || (report.unknownIdlNames.length === 0)) &&
                (!report.redefinedIdlNames || (report.redefinedIdlNames.length === 0)) &&
                (!report.missingReferences || (report.missingReferences.length === 0));
            var res = {
                title: spec.title,
                shortname: spec.shortname,
                date: spec.date,
                url: spec.url,
                latest: spec.latest,
                report
            };
            return res;
        });
}

function generateReportPerSpec(results) {
    var count = 0;
    var w = console.log.bind(console);

    // Compute report information
    results = processReport(results);

    w('# Reffy crawl report');
    w();
    w('## Specifications without known issues');
    w();
    count = 0;
    results
        .filter(spec => spec.report.ok)
        .forEach(spec => {
            count += 1;
            w('- [' + spec.title + '](' + (spec.latest || spec.url) + ')');
        });
    w();
    w('=> ' + count + ' specification' + ((count > 1) ? 's' : '') + ' found');
    w();
    w();

    w('## Specifications with possible issues');
    w();
    results
        .filter(spec => !spec.report.ok)
        .forEach(spec => {
            w('### ' + spec.title);
            w();
            w('Crawl info:');
            w('- URL: [' + (spec.latest ?
                ((spec.latest.indexOf('www.w3.org/TR/') !== -1) ? 'Latest published version' : 'Editor\'s Draft') :
                ((spec.url.indexOf('spec.whatwg.org') !== -1) ? 'Living Standard' : 'Initial URL'))
                + '](' + (spec.latest || spec.url) + ')');
            w('- Shortname: ' + (spec.shortname || 'no shortname'));
            w('- Date: ' + (spec.date || 'unknown'));
            w();

            var report = spec.report;
            w('Potential issue(s):');
            if (!report.hasNormativeRefs) {
                w('- No normative references found');
            }
            if (!report.hasIdl) {
                w('- No WebIDL definitions found');
            }
            if (report.hasInvalidIdl) {
                w('- Invalid WebIDL content found');
            }
            if (report.hasIdl && !report.referencesWebIDL) {
                w('- Spec uses WebIDL but does not reference it normatively');
            }
            if (report.unknownIdlNames &&
                (report.unknownIdlNames.length > 0)) {
                w('- Unknown WebIDL names used: ' +
                    report.unknownIdlNames.map(name => '`' + name + '`').join(', '));
            }
            if (report.redefinedIdlNames &&
                (report.redefinedIdlNames.length > 0)) {
                w('- WebIDL names also defined elsewhere: ');
                report.redefinedIdlNames.map(i => {
                    w('    * `' + i.name + '` also defined in ' +
                        i.refs.map(ref => ('[' + ref.title + '](' + (ref.latest || ref.url) + ')')).join(' and '));
                });
            }
            if (report.missingReferences &&
                (report.missingReferences.length > 0)) {
                w('- Missing references for WebIDL names: ');
                report.missingReferences.map(i => {
                    w('     * `' + i.name + '` defined in ' +
                        i.refs.map(ref => ('[' + ref.title + '](' + (ref.latest || ref.url) + ')')).join(' or '));
                });
            }
            w();
            w();
        });
    w();
    w();
}

function generateReport(results) {
    var count = 0;
    var w = console.log.bind(console);

    // Compute report information
    results = processReport(results);

    w('# Reffy crawl report');
    w();

    w('## Specifications crawled');
    w();
    count = results.length;
    w('- ' + count + ' specification' + ((count > 1) ? 's' : '') + ' crawled');
    w();
    w();

    count = 0;
    w('## Specifications without normative dependencies');
    w();
    results
        .filter(spec => !spec.report.hasNormativeRefs)
        .forEach(spec => {
            count += 1;
            w('- [' + spec.title + '](' + (spec.latest || spec.url) + ')');
        });
    w();
    w('=> ' + count + ' specification' + ((count > 1) ? 's' : '') + ' found');
    w();
    w('**NB:** it may be normal!');
    w();
    w();

    count = 0;
    w('## Specifications without WebIDL definitions');
    w();
    results
        .filter(spec => !spec.report.hasIdl)
        .forEach(spec => {
            count += 1;
            w('- [' + spec.title + '](' + (spec.latest || spec.url) + ')');
        });
    w();
    w('=> ' + count + ' specification' + ((count > 1) ? 's' : '') + ' found');
    w();
    w();

    count = 0;
    w('## List of specifications with invalid WebIDL content');
    w();
    results
        .filter(spec => spec.report.hasInvalidIdl)
        .forEach(spec => {
            count += 1;
            w('- [' + spec.title + '](' + (spec.latest || spec.url) + ')');
        });
    w();
    w('=> ' + count + ' specification' + ((count > 1) ? 's' : '') + ' found');
    w();
    w('**NB:** this may be due to WebIDL having evolved in the meantime');
    w();
    w();

    count = 0;
    w('## Specifications that use WebIDL but do not reference the WebIDL spec');
    w();
    results.forEach(spec => {
        if (spec.report.hasIdl && !spec.report.referencesWebIDL) {
            count += 1;
            w('- [' + spec.title + '](' + (spec.latest || spec.url) + ')');
        }
    });
    w();
    w('=> ' + count + ' specification' + ((count > 1) ? 's' : '') + ' found');
    w();
    w();

    count = 0;
    w('## List of WebIDL names not defined in the specifications crawled');
    w();
    var idlNames = {};
    results.forEach(spec => {
        if (!spec.report.unknownIdlNames ||
            (spec.report.unknownIdlNames.length === 0)) {
            return;
        }
        spec.report.unknownIdlNames.forEach(name => {
            if (!idlNames[name]) {
                idlNames[name] = [];
            }
            idlNames[name].push(spec);
        });
    });
    Object.keys(idlNames).sort().forEach(name => {
        count += 1;
        w('- `' + name + '` used in ' +
            idlNames[name].map(ref => ('[' + ref.title + '](' + (ref.latest || ref.url) + ')')).join(', '));
    });
    w();
    w('=> ' + count + ' WebIDL name' + ((count > 1) ? 's' : '') + ' found');
    w();
    w('**NB:** some of them are likely type errors in specs');
    w('(e.g. "int" does not exist, "Array" cannot be used on its own, etc.)');
    w();
    w();

    count = 0;
    w('## List of WebIDL names defined in more than one spec');
    w();
    idlNames = {};
    results.forEach(spec => {
        if (!spec.report.redefinedIdlNames ||
            (spec.report.redefinedIdlNames.length === 0)) {
            return;
        }
        spec.report.redefinedIdlNames.forEach(i => {
            if (!idlNames[i.name]) {
                idlNames[i.name] = [];
            }
            idlNames[i.name].push(spec);
        });
    });
    Object.keys(idlNames).sort().forEach(name => {
        count += 1;
        w('- `' + name + '` defined in ' +
            idlNames[name].map(ref => ('[' + ref.title + '](' + (ref.latest || ref.url) + ')')).join(' and '));
    });
    w();
    w('=> ' + count + ' WebIDL name' + ((count > 1) ? 's' : '') + ' found');
    w();
    w();

    count = 0;
    var countrefs = 0;
    w('## Missing references for WebIDL names');
    w();
    results.forEach(spec => {
        if (spec.report.missingReferences &&
            (spec.report.missingReferences.length > 0)) {
            count += 1;
            if (spec.report.missingReferences.length === 1) {
                countrefs += 1;
                let i = spec.report.missingReferences[0];
                w('- [' + spec.title + '](' + (spec.latest || spec.url) + ')' +
                    ' uses `' + i.name + '` but does not reference ' +
                    i.refs.map(ref => ('[' + ref.title + '](' + (ref.latest || ref.url) + ')')).join(' or '));
            }
            else {
                w('- [' + spec.title + '](' + (spec.latest || spec.url) + ') uses:');
                spec.report.missingReferences.map(i => {
                    countrefs += 1;
                    w('    * `' + i.name + '` but does not reference ' +
                        i.refs.map(ref => ('[' + ref.title + '](' + (ref.latest || ref.url) + ')')).join(' or '));
                });
            }
        }
    });
    w();
    w('=> ' + countrefs + ' missing reference' + ((count > 1) ? 's' : '') +
        ' found in ' + count + ' specification' + ((count > 1) ? 's' : ''));
}

/**************************************************
Code run if the code is run as a stand-alone module
**************************************************/
if (require.main === module) {
    var specResultsPath = process.argv[2];
    var perSpec = !!process.argv[3];
    if (!specResultsPath) {
        console.error("Required filename parameter missing");
        process.exit(2);
    }
    var specResults;
    try {
        specResults = require(specResultsPath);
    } catch(e) {
        console.error("Impossible to read " + specresultsPath + ": " + e);
        process.exit(3);
    }
    if (perSpec) {
        generateReportPerSpec(specResults);
    }
    else {
        generateReport(specResults);
    }
}
<|endoftext|>