<commit_before>
import Debug from 'debug'
import isEqual from 'lodash/isEqual'
import isPlainObject from 'is-plain-object'
import { List, Record, Stack } from 'immutable'

import MODEL_TYPES from '../constants/model-types'

/**
 * Debug.
 *
 * @type {Function}
 */

const debug = Debug('slate:history')

/**
 * Default properties.
 *
 * @type {Object}
 */

const DEFAULTS = {
  redos: new Stack(),
  undos: new Stack(),
}

/**
 * History.
 *
 * @type {History}
 */

class History extends Record(DEFAULTS) {

  /**
   * Create a new `History` with `attrs`.
   *
   * @param {Object|History} attrs
   * @return {History}
   */

  static create(attrs = {}) {
    if (History.isHistory(attrs)) {
      return attrs
    }

    if (isPlainObject(attrs)) {
      return History.fromJSON(attrs)
    }

    throw new Error(`\`History.create\` only accepts objects or histories, but you passed it: ${attrs}`)
  }

  /**
   * Create a `History` from a JSON `object`.
   *
   * @param {Object} object
   * @return {History}
   */

  static fromJSON(object) {
    const {
      redos = [],
      undos = [],
    } = object

    const history = new History({
      redos: new Stack(redos),
      undos: new Stack(undos),
    })

    return history
  }

  /**
   * Alias `fromJS`.
   */

  static fromJS = History.fromJSON

  /**
   * Check if `any` is a `History`.
   *
   * @param {Any} any
   * @return {Boolean}
   */

  static isHistory(any) {
    return !!(any && any[MODEL_TYPES.HISTORY])
  }

  /**
   * Get the kind.
   *
   * @return {String}
   */

  get kind() {
    return 'history'
  }

  /**
   * Save an `operation` into the history.
   *
   * @param {Object} operation
   * @param {Object} options
   * @return {History}
   */

  save(operation, options = {}) {
    let history = this
    let { undos, redos } = history
    let { merge, skip } = options
    const prevBatch = undos.peek()
    const prevOperation = prevBatch && prevBatch.last()

    if (skip == null) {
      skip = shouldSkip(operation, prevOperation)
    }

    if (skip) {
      return history
    }

    if (merge == null) {
      merge = shouldMerge(operation, prevOperation)
    }

    debug('save', { operation, merge })

    // If the `merge` flag is true, add the operation to the previous batch.
    if (merge && prevBatch) {
      const batch = prevBatch.push(operation)
      undos = undos.pop()
      undos = undos.push(batch)
    }

    // Otherwise, create a new batch with the operation.
    else {
      const batch = new List([operation])
      undos = undos.push(batch)
    }

    // Constrain the history to 100 entries for memory's sake.
    if (undos.length > 100) {
      undos = undos.take(100)
    }

    // Clear the redos and update the history.
    redos = redos.clear()
    history = history.set('undos', undos).set('redos', redos)
    return history
  }

  /**
   * Return a JSON representation of the history.
   *
   * @return {Object}
   */

  toJSON() {
    const object = {
      kind: this.kind,
      redos: this.redos.toJSON(),
      undos: this.undos.toJSON(),
    }

    return object
  }

  /**
   * Alias `toJS`.
   */

  toJS() {
    return this.toJSON()
  }

}

/**
 * Attach a pseudo-symbol for type checking.
 */

History.prototype[MODEL_TYPES.HISTORY] = true

/**
 * Check whether to merge a new operation `o` into the previous operation `p`.
 *
 * @param {Object} o
 * @param {Object} p
 * @return {Boolean}
 */

function shouldMerge(o, p) {
  if (!p) return false

  const merge = (
    (
      o.type == 'set_selection' &&
      p.type == 'set_selection'
    ) || (
      o.type == 'insert_text' &&
      p.type == 'insert_text' &&
      o.offset == p.offset + p.text.length &&
      isEqual(o.path, p.path)
    ) || (
      o.type == 'remove_text' &&
      p.type == 'remove_text' &&
      o.offset + o.text.length == p.offset &&
      isEqual(o.path, p.path)
    )
  )

  return merge
}

/**
 * Check whether to skip a new operation `o`, given previous operation `p`.
 *
 * @param {Object} o
 * @param {Object} p
 * @return {Boolean}
 */

function shouldSkip(o, p) {
  if (!p) return false

  const skip = (
    o.type == 'set_selection' &&
    p.type == 'set_selection'
  )

  return skip
}

/**
 * Export.
 *
 * @type {History}
 */

export default History
<commit_msg>Managing History limits (#1421)<commit_after>
import Debug from 'debug'
import isEqual from 'lodash/isEqual'
import isPlainObject from 'is-plain-object'
import { List, Record, Stack } from 'immutable'

import MODEL_TYPES from '../constants/model-types'

/**
 * Debug.
 *
 * @type {Function}
 */

const debug = Debug('slate:history')

/**
 * Default properties.
 *
 * @type {Object}
 */

const DEFAULTS = {
  redos: new Stack(),
  undos: new Stack(),
}

/**
 * History.
 *
 * @type {History}
 */

class History extends Record(DEFAULTS) {

  /**
   * Create a new `History` with `attrs`.
   *
   * @param {Object|History} attrs
   * @return {History}
   */

  static create(attrs = {}) {
    if (History.isHistory(attrs)) {
      return attrs
    }

    if (isPlainObject(attrs)) {
      return History.fromJSON(attrs)
    }

    throw new Error(`\`History.create\` only accepts objects or histories, but you passed it: ${attrs}`)
  }

  /**
   * Create a `History` from a JSON `object`.
   *
   * @param {Object} object
   * @return {History}
   */

  static fromJSON(object) {
    const {
      redos = [],
      undos = [],
    } = object

    const history = new History({
      redos: new Stack(redos),
      undos: new Stack(undos),
    })

    return history
  }

  /**
   * Alias `fromJS`.
   */

  static fromJS = History.fromJSON

  /**
   * Check if `any` is a `History`.
   *
   * @param {Any} any
   * @return {Boolean}
   */

  static isHistory(any) {
    return !!(any && any[MODEL_TYPES.HISTORY])
  }

  /**
   * Get the kind.
   *
   * @return {String}
   */

  get kind() {
    return 'history'
  }

  /**
   * Save an `operation` into the history.
   *
   * @param {Object} operation
   * @param {Object} options
   * @return {History}
   */

  save(operation, options = {}) {
    let history = this
    let { undos, redos } = history
    let { merge, skip } = options
    const prevBatch = undos.peek()
    const prevOperation = prevBatch && prevBatch.last()

    if (skip == null) {
      skip = shouldSkip(operation, prevOperation)
    }

    if (skip) {
      return history
    }

    if (merge == null) {
      merge = shouldMerge(operation, prevOperation)
    }

    debug('save', { operation, merge })

    // If the `merge` flag is true, add the operation to the previous batch.
    if (merge && prevBatch) {
      const batch = prevBatch.push(operation)
      undos = undos.pop()
      undos = undos.push(batch)
    }

    // Otherwise, create a new batch with the operation.
    else {
      const batch = new List([operation])
      undos = undos.push(batch)
    }

    // Constrain the history to 100 entries for memory's sake.
    if (undos.size > 100) {
      undos = undos.take(100)
    }

    // Clear the redos and update the history.
    redos = redos.clear()
    history = history.set('undos', undos).set('redos', redos)
    return history
  }

  /**
   * Return a JSON representation of the history.
   *
   * @return {Object}
   */

  toJSON() {
    const object = {
      kind: this.kind,
      redos: this.redos.toJSON(),
      undos: this.undos.toJSON(),
    }

    return object
  }

  /**
   * Alias `toJS`.
   */

  toJS() {
    return this.toJSON()
  }

}

/**
 * Attach a pseudo-symbol for type checking.
 */

History.prototype[MODEL_TYPES.HISTORY] = true

/**
 * Check whether to merge a new operation `o` into the previous operation `p`.
 *
 * @param {Object} o
 * @param {Object} p
 * @return {Boolean}
 */

function shouldMerge(o, p) {
  if (!p) return false

  const merge = (
    (
      o.type == 'set_selection' &&
      p.type == 'set_selection'
    ) || (
      o.type == 'insert_text' &&
      p.type == 'insert_text' &&
      o.offset == p.offset + p.text.length &&
      isEqual(o.path, p.path)
    ) || (
      o.type == 'remove_text' &&
      p.type == 'remove_text' &&
      o.offset + o.text.length == p.offset &&
      isEqual(o.path, p.path)
    )
  )

  return merge
}

/**
 * Check whether to skip a new operation `o`, given previous operation `p`.
 *
 * @param {Object} o
 * @param {Object} p
 * @return {Boolean}
 */

function shouldSkip(o, p) {
  if (!p) return false

  const skip = (
    o.type == 'set_selection' &&
    p.type == 'set_selection'
  )

  return skip
}

/**
 * Export.
 *
 * @type {History}
 */

export default History
<|endoftext|>


<commit_before>var articleParent = document.querySelector('#storyContent');
var articleParas = document.querySelectorAll('#storyContent p');

for(var x = 0; x < articleParas.length; x += 1){
	articleParas[x].style.display = "none";
}

var adMessage = document.createElement('div');
adMessage.innerHTML = '
This article will be back shortly. We have delayed your access to it for a short while because we believe you are using ad blocking technology. The FT generates income from advertising on the site which helps ensure we can bring you intesting content like the article you will shortly be reading.
';
articleParent.insertBefore(adMessage, articleParent.firstChild);

function showContent() {
	adMessage.style.display = 'none';
	for(var x = 0; x < articleParas.length; x += 1){
		articleParas[x].style.display = "block";
	}
}

window.setTimeout(showContent, 10000);
<commit_msg>Hide images in article content too.<commit_after>var articleParent = document.querySelector('#storyContent');
var articleParas = document.querySelectorAll('#storyContent p');
var storyImages = document.querySelectorAll('span.story-image');

for(var x = 0; x < articleParas.length; x += 1){
	articleParas[x].style.display = "none";
}

for(var y = 0; y < storyImages.length; y += 1){
	storyImages[y].style.display = "none";
}

var adMessage = document.createElement('div');
adMessage.innerHTML = '
This article will be back shortly. We have delayed your access to it for a short while because we believe you are using ad blocking technology. The FT generates income from advertising on the site which helps ensure we can bring you intesting content like the article you will shortly be reading.
';
articleParent.insertBefore(adMessage, articleParent.firstChild);

function showContent() {
	adMessage.style.display = 'none';
	for(var z = 0; z < articleParas.length; z += 1){
		articleParas[z].style.display = "block";
	}

	for(var w = 0; w < storyImages.length; w += 1){
		storyImages[y].style.display = "block";
	}

}

window.setTimeout(showContent, 10000);
<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const path = require('path');
const iterwait = require('./iterwait');
const BaseDB = require('./db-base');
const Filters = require('./filters');
const StatementCache = require('./cache-stmt');
// const { decompose, recombine } = require('./parsecfg');

class UserDB extends BaseDB {

  constructor (filename, options = {}) {
    if (!filename) {
      throw new Error('No filename given');
    }

    super(filename, _.pick(options, ['create_file', 'single_conn']));

    if (!options.user_id && _.get(options, 'require_id', true)) {
      throw new Error('No user id given');
    }
    this.id_str = options.user_id ? String(options.user_id) : null;

    // this.options = options;
  }

  static open (filename, options = {}) {
    return iterwait((function* () {
      let user;
      try {
        user = yield new Promise((resolve, reject) => {
          // Event handlers - each will remove the other when called
          let on_open, on_err;
          on_open = (file, base_db) => {
            base_db.removeListener('open:error', on_err);
            resolve(base_db);
          };
          on_err = (err, base_db) => {
            const fn = () => reject(err);
            base_db.removeListener('open:success', on_open);
            base_db.close().then(fn, fn);
          };

          // Create and register event handlers
          new UserDB(filename, options)
            .once('open:success', on_open)
            .once('open:error', on_err);
        });

        if (_.get(options, 'migrate', true)) {
          yield user.db.migrate({
            force: _.get(options, 'force', false),
            migrationsPath: path.join(__dirname, '../migrations/user'),
          });
        }

        if (_.get(options, 'init', true)) {
          yield user._init();
        }
      }
      finally {
        if (user) {
          yield user.close();
        }
      }

      return user;
    })());
  }

  _init () {
    return this.run_async(function* (trx) {
      // get_config() will update cache itself
      yield this.get_config('', { trx });

      // Check to ensure stored and given user id match
      if (this.id_str) {
        const user = this.config.user;

        if (!user || !user.id_str) {
          yield this.set_config(this.id_str, 'user.id_str', { trx });
        }
        else if (this.id_str !== user.id_str)
          throw new Error(
            `Given user id ${this.id_str} doesn't match stored id ${user.id_str}`
          );
        }
      }

      // TODO: load usersets?

      return this;
    }, { start_trx: true });
  }

  write_item () {}

  write_queue () {}

  get_user () {}

  write_user () {}

  write_user_set () {}

  update_user_set () {}

  write_tweet () {}

  delete_tweet () {}

  write_alias (alias, { cache = null, trx = null }) {
    if (!_.isObject(alias) && !_.isArray(alias)) {
      return Promise.reject(new Error(`Invalid alias: ${alias}`));
    }
    const aliases = _.isArray(alias) ? alias : [alias];

    return this.run_async(function* _write_alias(conn) {
      // Prepare statement
      const use_cache = cache || new StatementCache(conn);
      const { insert } = yield use_cache.load(
        {
          insert: `INSERT OR IGNORE
INTO alias (user_id, screen_name, time_ms)
VALUES (?, ?, ?)`,
        },
        'alias'
      );

      // Insert each
      const now = Date.now();
      const results = yield Promise.all(aliases.map(
        ({ user_id_str, screen_name, time }) => {
          const use_time = (time === undefined || time === null)
            ? now
            : Number.isInteger(time)
              ? time
              : new Date(time).getTime();

          // user_id/screen_name check constraint will skip insert
          return insert.run([user_id_str, screen_name, use_time])
          .then(({ changes }) => [user_id_str, screen_name, !!changes]);
        }
      ));

      // Finalize statements if not using provided cache
      if (!cache) {
        yield use_cache.finalize();
      }

      return results;
    }, { use_conn: trx, start_trx: true});
  }

  write_mention () {}

  write_fav () {}

  delete_fav () {}

}

module.exports = UserDB;
<commit_msg>Added UserDB.write_mention()<commit_after>'use strict';

const _ = require('lodash');
const path = require('path');
const iterwait = require('./iterwait');
const BaseDB = require('./db-base');
const Filters = require('./filters');
const StatementCache = require('./cache-stmt');
// const { decompose, recombine } = require('./parsecfg');

class UserDB extends BaseDB {

  constructor (filename, options = {}) {
    if (!filename) {
      throw new Error('No filename given');
    }

    super(filename, _.pick(options, ['create_file', 'single_conn']));

    if (!options.user_id && _.get(options, 'require_id', true)) {
      throw new Error('No user id given');
    }
    this.id_str = options.user_id ? String(options.user_id) : null;

    // this.options = options;
  }

  static open (filename, options = {}) {
    return iterwait((function* () {
      let user;
      try {
        user = yield new Promise((resolve, reject) => {
          // Event handlers - each will remove the other when called
          let on_open, on_err;
          on_open = (file, base_db) => {
            base_db.removeListener('open:error', on_err);
            resolve(base_db);
          };
          on_err = (err, base_db) => {
            const fn = () => reject(err);
            base_db.removeListener('open:success', on_open);
            base_db.close().then(fn, fn);
          };

          // Create and register event handlers
          new UserDB(filename, options)
            .once('open:success', on_open)
            .once('open:error', on_err);
        });

        if (_.get(options, 'migrate', true)) {
          yield user.db.migrate({
            force: _.get(options, 'force', false),
            migrationsPath: path.join(__dirname, '../migrations/user'),
          });
        }

        if (_.get(options, 'init', true)) {
          yield user._init();
        }
      }
      finally {
        if (user) {
          yield user.close();
        }
      }

      return user;
    })());
  }

  _init () {
    return this.run_async(function* (trx) {
      // get_config() will update cache itself
      yield this.get_config('', { trx });

      // Check to ensure stored and given user id match
      if (this.id_str) {
        const user = this.config.user;

        if (!user || !user.id_str) {
          yield this.set_config(this.id_str, 'user.id_str', { trx });
        }
        else if (this.id_str !== user.id_str)
          throw new Error(
            `Given user id ${this.id_str} doesn't match stored id ${user.id_str}`
          );
        }
      }

      // TODO: load usersets?

      return this;
    }, { start_trx: true });
  }

  write_item () {}

  write_queue () {}

  get_user () {}

  write_user () {}

  write_user_set () {}

  update_user_set () {}

  write_tweet () {}

  delete_tweet () {}

  write_alias (alias, { cache = null, trx = null }) {
    if (!_.isObject(alias) && !_.isArray(alias)) {
      return Promise.reject(new Error(`Invalid alias: ${alias}`));
    }
    const aliases = _.isArray(alias) ? alias : [alias];

    return this.run_async(function* _write_alias(conn) {
      // Prepare statement
      const use_cache = cache || new StatementCache(conn);
      const { insert } = yield use_cache.load(
        {
          insert: `INSERT OR IGNORE
INTO alias (user_id, screen_name, time_ms)
VALUES (?, ?, ?)`,
        },
        'alias'
      );

      // Insert each
      const now = Date.now();
      const results = yield Promise.all(aliases.map(
        ({ user_id_str, screen_name, time }) => {
          const use_time = (time === undefined || time === null)
            ? now
            : Number.isInteger(time)
              ? time
              : new Date(time).getTime();

          // Check constraints will skip insert if req'd fields missing
          return insert.run([user_id_str, screen_name, use_time])
          .then(({ changes }) => [[user_id_str, screen_name], !!changes]);
        }
      ));

      // Finalize statements if not using provided cache
      if (!cache) {
        yield use_cache.finalize();
      }

      return results;
    }, { use_conn: trx, start_trx: true});
  }

  write_mention (mention, { cache = null, trx = null }) {
    if (!_.isObject(mention) && !_.isArray(mention)) {
      return Promise.reject(new Error(`Invalid mention: ${mention}`));
    }
    const mentions = _.isArray(mention) ? mention : [mention];

    return this.run_async(function* _write_mention(conn) {
      // Prepare statement
      const use_cache = cache || new StatementCache(conn);
      const { insert } = yield use_cache.load(
        {
          insert: `INSERT OR IGNORE
INTO mention (
  tweet_id,
  of_user_id,
  by_user_id,
  reply_to_user_id,
  quoted_user_id,
  retweeted_user_id,
  time_ms
)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
        },
        'mention'
      );

      // Insert each
      const now = Date.now();
      const results = yield Promise.all(mentions.map(
        (m) => {
          const {
            tweet_id_str,
            of_user_id_str,
            by_user_id_str,
            reply_to_user_id_str,
            quoted_user_id_str,
            retweeted_user_id_str,
            time
          } = m;

          const use_time = (time === undefined || time === null)
            ? now
            : Number.isInteger(time)
              ? time
              : new Date(time).getTime();

          // Check constraints will skip insert if req'd fields missing
          return insert.run([
            tweet_id_str,
            of_user_id_str,
            by_user_id_str,
            reply_to_user_id_str || null,
            quoted_user_id_str || null,
            retweeted_user_id_str || null,
            use_time
          ])
          .then(({ changes }) => [[tweet_id_str, of_user_id_str], !!changes]);
        }
      ));

      // Finalize statements if not using provided cache
      if (!cache) {
        yield use_cache.finalize();
      }

      return results;
    }, { use_conn: trx, start_trx: true});
  }

  write_fav () {}

  delete_fav () {}

}

module.exports = UserDB;
<|endoftext|>


<commit_before>/*
 * Copyright 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
 * Please see distribution for license.
 */
$.register_module({
    name: 'og.common.gadgets.GadgetsContainer',
    dependencies: ['og.common.gadgets.manager', 'og.api.text'],
    obj: function () {
        var api = og.api, tabs_template, overflow_template, dropbox_template, counter = 1,
            header = ' .ui-layout-header';
        return function (selector_prefix, pane) {
            var initialized = false, loading, gadgets = [], container = this, selector = selector_prefix + pane,
                class_prefix = selector_prefix.substring(1),
                live_id, // active tab id
                overflow = {}, // document offet of overflow panel
                $overflow_panel; // panel that houses non visible tabs
            var extract_id = function (str) {return +str.replace(/^og\-tab\-(\d+)\s(?:.*)$/, '$1');};
            var extract_index = function (id) {
                return gadgets.reduce(function (acc, val, idx) {return acc + (val.id === id ? idx : 0);}, 0)
            };
            /**
             * @param {Number|Null} id
             *        if id is a Number set the active tab to that ID
             *        if id is undefined use the current Live ID and reflow tabs
             *        if id is null set tabs to a single empty tab
             */
            var update_tabs = function (id) {
                var $header = $(selector + header), tabs;
                /**
                 * @param id Id of gadget to show, hide all others
                 */
                var show_gadget = function (id) {
                    $(selector).find('.OG-gadget-container [class*=OG-gadget-]').hide()
                        .filter('.OG-gadget-' + id).show();
                    live_id = id;
                };
                /** Manages the widths of the tabs when the panel is resized, the following stages exist:
                 *  0 - all tabs are full size
                 *  1 - all but the active tab are truncated
                 *  2 - tabs are truncated plus some/all are moved to an overflow panel
                 *  3 - same as above with active tab being truncated
                 */
                var reflow = function () {
                    var overflow_buffer = 25, // space for the overflow button
                        buttons_buffer = {'south': 23}, // add extra buffer space if the tabs area has buttons
                        min_tab_width = 50,
                        $tabs_container = $(selector + ' .OG-gadget-tabs'),
                        $tabs = $tabs_container.find('li[class^=og-tab-]'),
                        $active_tab = $(selector + ' .og-active'),
                        $overflow_button = $(selector + ' .og-overflow'),
                        active_tab_width = $active_tab.outerWidth(),
                        num_tabs = $tabs.length,
                        num_inactive_tabs = num_tabs - 1,
                        new_tab_width, // new truncated width of a tab
                        compressed_tabs_width, // new full width of all tabs
                        space_needed, // number of pixles needed to reclaim
                        num_tabs_to_hide, // number of tabs to move to overflow
                        $tabs_to_move, // the actual tabs we are moving
                        // full available width
                        full_width = $tabs_container.width() - (overflow_buffer + (buttons_buffer[pane] || 0)),
                        // the full width of all the tabs
                        tabs_width = Array.prototype.reduce.apply($tabs
                            .map(function () {return $(this).outerWidth();}), [function (a, b) {return a + b;}, 0]),
                        new_window = function (i, dropped) {
                            var prefix = 'gadget.ftl#/gadgetscontainer/', url,
                                index = extract_index(extract_id($(this).attr('class'))),
                                id = gadgets[index].config.options.id,
                                type = gadgets[index].type;
                            switch (type) {
                                case 'timeseries': url = prefix + type + ':' + id; break;
                                case 'surface': url = prefix + type + ':' + id; break;
                                case 'grid': url = prefix + type + ':' + id; break;
                            }
                            if (!dropped) {
                                window.open(url);
                                setTimeout(container.del.partial(gadgets[i]));
                            }
                        };
                    // stage 1
                    if (tabs_width > full_width) {
                        new_tab_width = ~~((full_width - active_tab_width) / num_inactive_tabs);
                        new_tab_width = new_tab_width < min_tab_width ? min_tab_width : new_tab_width;
                        compressed_tabs_width = (num_inactive_tabs * new_tab_width) + active_tab_width;
                        // stage 2
                        if (compressed_tabs_width > full_width) {
                            if (num_tabs > 1) {
                                $overflow_button.show().on('click', function (e) {
                                    e.stopPropagation();
                                    $overflow_panel.toggle();
                                    var wins = [window].concat(Array.prototype.slice.call(window.frames));
                                    if ($overflow_panel.is(":visible")) $(wins).on('click.overflow_handler', function () {
                                        $overflow_panel.hide();
                                        $(wins).off('click.overflow_handler');
                                    });
                                    else $(wins).off('click.overflow_handler');
                                });
                            }
                            space_needed = full_width - compressed_tabs_width;
                            num_tabs_to_hide = Math.ceil(Math.abs(space_needed) / new_tab_width);
                            if (num_tabs_to_hide) $overflow_panel.find('ul').html(
                                $tabs_to_move = $tabs.filter(':not(.og-active):lt(' + num_tabs_to_hide + ')')
                            );
                            // stage 3
                            if (num_tabs_to_hide >= num_tabs) $active_tab.width(
                                active_tab_width
                                + (space_needed + ((num_inactive_tabs * min_tab_width) - overflow_buffer))
                                + 'px'
                            );
                        } else {
                            $overflow_panel.hide();
                        }
                        // set inactive tab widths to calculated value
                        $tabs.each(function () {if (!$(this).hasClass('og-active')) $(this).outerWidth(new_tab_width)});
                        // unset width of tabs in overflow panel
                        if ($tabs_to_move) $tabs_to_move.each(function () {$(this).attr('style', '');});
                        // set position of overflow panel
                        overflow.right = $(document).width() - ($overflow_button.offset().left + 25 - 5);
                        overflow.height = $overflow_button.height();
                        overflow.top = $overflow_button.offset().top + overflow.height + 1;
                        $overflow_panel.css({'right': overflow.right + 'px', 'top': overflow.top + 'px'});
                        $tabs.each(function () { // add tooltips to truncated tabs only
                            var $this = $(this);
                            if (!!$this.attr('style')) $this.attr('title', $this.text().trim());
                        });
                    }
                    // implement drag
                    $tabs.each(function (i) {
                        $(this).draggable({
                            cursor: 'move', zIndex: 3, cursorAt: {top: 25}, scroll: false,
                            iframeFix: true, appendTo: 'body', distance: 20,
                            revert: new_window.partial(i),
                            stop: function () {$(this).draggable('option','revert', new_window.partial(i));},
                            helper: function() {return dropbox_template({label: $(this).text().trim()});}
                        }).data({gadget: gadgets[i], handler: function () {container.del(gadgets[i]);}});
                    });
                };
                if (id === null) $header.html(tabs_template({'tabs': [{'name': 'empty'}]})); // empty tabs
                else {
                    if (id === void 0) id = live_id;
                    tabs = gadgets.reduce(function (acc, val) {
                        return acc.push({
                            'name': val.config.name, 'active': (id === val.id), 'delete': true, 'id': val.id
                        }) && acc;
                    }, []);
                    $header.html(tabs_template({'tabs': tabs}));
                    reflow();
                    show_gadget(id);
                }
            };
            container.init = function (arr) {
                var toggle_dropbox = function () {
                        var $db = $('.og-drop').length, $dbs_span = $('.OG-dropbox span');
                        if ($db) $dbs_span.removeClass('og-icon-new-window').addClass('og-icon-drop');
                        else $dbs_span.removeClass('og-icon-drop').addClass('og-icon-new-window');
                    };
                loading = true;
                $.when(
                    api.text({module: 'og.analytics.tabs_tash'}),
                    api.text({module: 'og.analytics.tabs_overflow_tash'}),
                    api.text({module: 'og.analytics.dropbox_tash'})
                ).then(function (tabs_tmpl, overflow_tmpl, dropbox_tmpl) {
                    if (!tabs_template) tabs_template = Handlebars.compile(tabs_tmpl);
                    if (!overflow_template) overflow_template = Handlebars.compile(overflow_tmpl);
                    if (!dropbox_template) dropbox_template = Handlebars.compile(dropbox_tmpl);
                    if (!$overflow_panel) $overflow_panel = $(overflow_template({pane: pane})).appendTo('body');
                    initialized = true;
                    loading = false;
                    // setup click handlers
                    $(selector + header + ' , .og-js-overflow-' + pane)
                        // handler for tabs (including the ones in the overflow pane)
                        .on('click', 'li[class^=og-tab-]', function (e) {
                            var id = extract_id($(this).attr('class')), index = extract_index(id);
                            if ($(e.target).hasClass('og-delete')) container.del(gadgets[index]);
                            else if (!$(this).hasClass('og-active')) {
                                update_tabs(id || null);
                                if (id) gadgets[index].gadget.resize();
                            }
                        });
                    if (!arr) update_tabs(null); else container.add(arr);
                    // implement drop
                    $(selector).droppable({
                        hoverClass: 'og-drop',
                        accept: function (draggable) {return $(draggable).is('li[class^=og-tab-]')}, // is it a tab...
                        over: function () {setTimeout(toggle_dropbox)}, // can't guarantee over and out fire in correct
                        out: function () {setTimeout(toggle_dropbox)},  // order, toggle function seems to solve issue
                        drop: function(e, ui) {
                            var has_ancestor = function (elm, sel) {return $(elm).closest('.' + sel).length},
                                pane_class = class_prefix + pane,
                                overflow_class = 'og-js-overflow-' + pane,
                                data = ui.draggable.data(),
                                gadget = data.gadget.config.options,
                                re = new RegExp(selector_prefix + '(.*?)\\s');
                            if (has_ancestor(ui.draggable, pane_class) || has_ancestor(ui.draggable, overflow_class)) {
                                ui.draggable.draggable('option', 'revert', true);
                            } else {
                                ui.draggable.draggable('option', 'revert', false);
                                gadget.selector = gadget.selector.replace(re, selector_prefix + pane + ' ');
                                container.add([data.gadget.config]);
                                setTimeout(data.handler); // setTimeout to ensure handler is called after drag evt ends
                            }
                        }
                    });
                    og.common.gadgets.manager.register(container);
                });
            };
            /**
             * @param {String|Array} data A String that defines what gadgets to load, or an Array of gadgets to load
             *
             * The data Array is a list of objects that describe the gadgets to load
             *     obj.gadget   Function
             *     obj.options  Object
             *     obj.name     String
             *     obj.margin   Boolean
             */
            container.add = function (data) {
                var panel_container = selector + ' .OG-gadget-container', new_gadgets, arr;
                if (!loading && !initialized)
                    return container.init(), setTimeout(container.add.partial(data || null), 10), container;
                if (!initialized) return setTimeout(container.add.partial(data || null), 10), container;
                if (!data) return container; // no gadgets for this container
                if (!selector) throw new TypeError('GadgetsContainer has not been initialized');
                var generate_arr = function (data) { // create gadgets object array from url args using default settings
                    var obj, type, gadgets = [], options = {};
                    obj = data.split(';').reduce(function (acc, val) {
                        var data = val.split(':');
                        acc[data[0]] = data[1].split(',');
                        return acc;
                    }, {});
                    // TODO: move default options to gadgets
                    options.timeseries = function (id) {
                        return {
                            gadget: 'og.common.gadgets.timeseries',
                            options: {id: id, datapoints_link: false, child: true},
                            name: 'Timeseries ' + id
                        }
                    };
                    options.surface = function (id) {
                        return {
                            gadget: 'og.common.gadgets.surface',
                            name: 'Surface ' + id,
                            options: {id: id, child: true}
                        }
                    };
                    options.grid = function (id) {
                        return {gadget: 'og.analytics.Grid', name: 'grid ' + id, options: {}}
                    };
                    for (type in obj) if (obj.hasOwnProperty(type))
                        obj[type].forEach(function (val) {gadgets.push(options[type](val));});
                    return gadgets;
                };
                arr = typeof data === 'string' ? generate_arr(data) : data;
                new_gadgets = arr.map(function (obj, i) {
                    var id, gadget_class = 'OG-gadget-' + (id = counter++), gadget,
                        gadget_selector = panel_container + ' .' + gadget_class,
                        options = $.extend(true, obj.options || {}, {selector: gadget_selector}),
                        constructor = obj.gadget.split('.').reduce(function (acc, val) {return acc[val];}, window),
                        type = obj.gadget.replace(/^[a-z0-9.-_]+\.([a-z0-9.-_]+?)$/, '$1').toLowerCase();
                    $(panel_container)
                        .append('<div class="' + gadget_class + '" />')
                        .find('.' + gadget_class)
                        .css({height: '100%', display: i === arr.length - 1 ? 'block' : 'none'});
                    gadgets.push(gadget = {id: id, config: obj, type: type, gadget: new constructor(options)});
                    return gadget;
                });
                update_tabs(new_gadgets[new_gadgets.length - 1].id);
                return container;
            };
            container.del = function (obj) {
                var id;
                $(selector + ' .OG-gadget-container .OG-gadget-' + obj.id).remove();
                gadgets[gadgets.length - 1].gadget.alive();
                gadgets.splice(gadgets.indexOf(obj), 1);
                id = gadgets.length
                    ? live_id === obj.id ? gadgets[gadgets.length - 1].id : live_id
                    : null;
                if (id) gadgets[extract_index(id)].gadget.resize();
                update_tabs(id); // new active tab or empty
            };
            container.alive = function () {
                gadgets.forEach(function (obj) {obj.gadget.alive();});
                return !!$(selector).length;
            };
            container.resize = function () {
                gadgets.forEach(function (obj) {
                    if (obj.gadget.alive()) {
                        $(selector + ' .ui-layout-content').show();
                        update_tabs();
                        obj.gadget.resize();
                    }
                });
            }
        };
    }
});<commit_msg>dealocating buffers before removing element<commit_after>/*
 * Copyright 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
 * Please see distribution for license.
 */
$.register_module({
    name: 'og.common.gadgets.GadgetsContainer',
    dependencies: ['og.common.gadgets.manager', 'og.api.text'],
    obj: function () {
        var api = og.api, tabs_template, overflow_template, dropbox_template, counter = 1,
            header = ' .ui-layout-header';
        return function (selector_prefix, pane) {
            var initialized = false, loading, gadgets = [], container = this, selector = selector_prefix + pane,
                class_prefix = selector_prefix.substring(1),
                live_id, // active tab id
                overflow = {}, // document offet of overflow panel
                $overflow_panel; // panel that houses non visible tabs
            var extract_id = function (str) {return +str.replace(/^og\-tab\-(\d+)\s(?:.*)$/, '$1');};
            var extract_index = function (id) {
                return gadgets.reduce(function (acc, val, idx) {return acc + (val.id === id ? idx : 0);}, 0)
            };
            /**
             * @param {Number|Null} id
             *        if id is a Number set the active tab to that ID
             *        if id is undefined use the current Live ID and reflow tabs
             *        if id is null set tabs to a single empty tab
             */
            var update_tabs = function (id) {
                var $header = $(selector + header), tabs;
                /**
                 * @param id Id of gadget to show, hide all others
                 */
                var show_gadget = function (id) {
                    $(selector).find('.OG-gadget-container [class*=OG-gadget-]').hide()
                        .filter('.OG-gadget-' + id).show();
                    live_id = id;
                };
                /** Manages the widths of the tabs when the panel is resized, the following stages exist:
                 *  0 - all tabs are full size
                 *  1 - all but the active tab are truncated
                 *  2 - tabs are truncated plus some/all are moved to an overflow panel
                 *  3 - same as above with active tab being truncated
                 */
                var reflow = function () {
                    var overflow_buffer = 25, // space for the overflow button
                        buttons_buffer = {'south': 23}, // add extra buffer space if the tabs area has buttons
                        min_tab_width = 50,
                        $tabs_container = $(selector + ' .OG-gadget-tabs'),
                        $tabs = $tabs_container.find('li[class^=og-tab-]'),
                        $active_tab = $(selector + ' .og-active'),
                        $overflow_button = $(selector + ' .og-overflow'),
                        active_tab_width = $active_tab.outerWidth(),
                        num_tabs = $tabs.length,
                        num_inactive_tabs = num_tabs - 1,
                        new_tab_width, // new truncated width of a tab
                        compressed_tabs_width, // new full width of all tabs
                        space_needed, // number of pixles needed to reclaim
                        num_tabs_to_hide, // number of tabs to move to overflow
                        $tabs_to_move, // the actual tabs we are moving
                        // full available width
                        full_width = $tabs_container.width() - (overflow_buffer + (buttons_buffer[pane] || 0)),
                        // the full width of all the tabs
                        tabs_width = Array.prototype.reduce.apply($tabs
                            .map(function () {return $(this).outerWidth();}), [function (a, b) {return a + b;}, 0]),
                        new_window = function (i, dropped) {
                            var prefix = 'gadget.ftl#/gadgetscontainer/', url,
                                index = extract_index(extract_id($(this).attr('class'))),
                                id = gadgets[index].config.options.id,
                                type = gadgets[index].type;
                            switch (type) {
                                case 'timeseries': url = prefix + type + ':' + id; break;
                                case 'surface': url = prefix + type + ':' + id; break;
                                case 'grid': url = prefix + type + ':' + id; break;
                            }
                            if (!dropped) {
                                window.open(url);
                                setTimeout(container.del.partial(gadgets[i]));
                            }
                        };
                    // stage 1
                    if (tabs_width > full_width) {
                        new_tab_width = ~~((full_width - active_tab_width) / num_inactive_tabs);
                        new_tab_width = new_tab_width < min_tab_width ? min_tab_width : new_tab_width;
                        compressed_tabs_width = (num_inactive_tabs * new_tab_width) + active_tab_width;
                        // stage 2
                        if (compressed_tabs_width > full_width) {
                            if (num_tabs > 1) {
                                $overflow_button.show().on('click', function (e) {
                                    e.stopPropagation();
                                    $overflow_panel.toggle();
                                    var wins = [window].concat(Array.prototype.slice.call(window.frames));
                                    if ($overflow_panel.is(":visible")) $(wins).on('click.overflow_handler', function () {
                                        $overflow_panel.hide();
                                        $(wins).off('click.overflow_handler');
                                    });
                                    else $(wins).off('click.overflow_handler');
                                });
                            }
                            space_needed = full_width - compressed_tabs_width;
                            num_tabs_to_hide = Math.ceil(Math.abs(space_needed) / new_tab_width);
                            if (num_tabs_to_hide) $overflow_panel.find('ul').html(
                                $tabs_to_move = $tabs.filter(':not(.og-active):lt(' + num_tabs_to_hide + ')')
                            );
                            // stage 3
                            if (num_tabs_to_hide >= num_tabs) $active_tab.width(
                                active_tab_width
                                + (space_needed + ((num_inactive_tabs * min_tab_width) - overflow_buffer))
                                + 'px'
                            );
                        } else {
                            $overflow_panel.hide();
                        }
                        // set inactive tab widths to calculated value
                        $tabs.each(function () {if (!$(this).hasClass('og-active')) $(this).outerWidth(new_tab_width)});
                        // unset width of tabs in overflow panel
                        if ($tabs_to_move) $tabs_to_move.each(function () {$(this).attr('style', '');});
                        // set position of overflow panel
                        overflow.right = $(document).width() - ($overflow_button.offset().left + 25 - 5);
                        overflow.height = $overflow_button.height();
                        overflow.top = $overflow_button.offset().top + overflow.height + 1;
                        $overflow_panel.css({'right': overflow.right + 'px', 'top': overflow.top + 'px'});
                        $tabs.each(function () { // add tooltips to truncated tabs only
                            var $this = $(this);
                            if (!!$this.attr('style')) $this.attr('title', $this.text().trim());
                        });
                    }
                    // implement drag
                    $tabs.each(function (i) {
                        $(this).draggable({
                            cursor: 'move', zIndex: 3, cursorAt: {top: 25}, scroll: false,
                            iframeFix: true, appendTo: 'body', distance: 20,
                            revert: new_window.partial(i),
                            stop: function () {$(this).draggable('option','revert', new_window.partial(i));},
                            helper: function() {return dropbox_template({label: $(this).text().trim()});}
                        }).data({gadget: gadgets[i], handler: function () {container.del(gadgets[i]);}});
                    });
                };
                if (id === null) $header.html(tabs_template({'tabs': [{'name': 'empty'}]})); // empty tabs
                else {
                    if (id === void 0) id = live_id;
                    tabs = gadgets.reduce(function (acc, val) {
                        return acc.push({
                            'name': val.config.name, 'active': (id === val.id), 'delete': true, 'id': val.id
                        }) && acc;
                    }, []);
                    $header.html(tabs_template({'tabs': tabs}));
                    reflow();
                    show_gadget(id);
                }
            };
            container.init = function (arr) {
                var toggle_dropbox = function () {
                        var $db = $('.og-drop').length, $dbs_span = $('.OG-dropbox span');
                        if ($db) $dbs_span.removeClass('og-icon-new-window').addClass('og-icon-drop');
                        else $dbs_span.removeClass('og-icon-drop').addClass('og-icon-new-window');
                    };
                loading = true;
                $.when(
                    api.text({module: 'og.analytics.tabs_tash'}),
                    api.text({module: 'og.analytics.tabs_overflow_tash'}),
                    api.text({module: 'og.analytics.dropbox_tash'})
                ).then(function (tabs_tmpl, overflow_tmpl, dropbox_tmpl) {
                    if (!tabs_template) tabs_template = Handlebars.compile(tabs_tmpl);
                    if (!overflow_template) overflow_template = Handlebars.compile(overflow_tmpl);
                    if (!dropbox_template) dropbox_template = Handlebars.compile(dropbox_tmpl);
                    if (!$overflow_panel) $overflow_panel = $(overflow_template({pane: pane})).appendTo('body');
                    initialized = true;
                    loading = false;
                    // setup click handlers
                    $(selector + header + ' , .og-js-overflow-' + pane)
                        // handler for tabs (including the ones in the overflow pane)
                        .on('click', 'li[class^=og-tab-]', function (e) {
                            var id = extract_id($(this).attr('class')), index = extract_index(id);
                            if ($(e.target).hasClass('og-delete')) container.del(gadgets[index]);
                            else if (!$(this).hasClass('og-active')) {
                                update_tabs(id || null);
                                if (id) gadgets[index].gadget.resize();
                            }
                        });
                    if (!arr) update_tabs(null); else container.add(arr);
                    // implement drop
                    $(selector).droppable({
                        hoverClass: 'og-drop',
                        accept: function (draggable) {return $(draggable).is('li[class^=og-tab-]')}, // is it a tab...
                        over: function () {setTimeout(toggle_dropbox)}, // can't guarantee over and out fire in correct
                        out: function () {setTimeout(toggle_dropbox)},  // order, toggle function seems to solve issue
                        drop: function(e, ui) {
                            var has_ancestor = function (elm, sel) {return $(elm).closest('.' + sel).length},
                                pane_class = class_prefix + pane,
                                overflow_class = 'og-js-overflow-' + pane,
                                data = ui.draggable.data(),
                                gadget = data.gadget.config.options,
                                re = new RegExp(selector_prefix + '(.*?)\\s');
                            if (has_ancestor(ui.draggable, pane_class) || has_ancestor(ui.draggable, overflow_class)) {
                                ui.draggable.draggable('option', 'revert', true);
                            } else {
                                ui.draggable.draggable('option', 'revert', false);
                                gadget.selector = gadget.selector.replace(re, selector_prefix + pane + ' ');
                                container.add([data.gadget.config]);
                                setTimeout(data.handler); // setTimeout to ensure handler is called after drag evt ends
                            }
                        }
                    });
                    og.common.gadgets.manager.register(container);
                });
            };
            /**
             * @param {String|Array} data A String that defines what gadgets to load, or an Array of gadgets to load
             *
             * The data Array is a list of objects that describe the gadgets to load
             *     obj.gadget   Function
             *     obj.options  Object
             *     obj.name     String
             *     obj.margin   Boolean
             */
            container.add = function (data) {
                var panel_container = selector + ' .OG-gadget-container', new_gadgets, arr;
                if (!loading && !initialized)
                    return container.init(), setTimeout(container.add.partial(data || null), 10), container;
                if (!initialized) return setTimeout(container.add.partial(data || null), 10), container;
                if (!data) return container; // no gadgets for this container
                if (!selector) throw new TypeError('GadgetsContainer has not been initialized');
                var generate_arr = function (data) { // create gadgets object array from url args using default settings
                    var obj, type, gadgets = [], options = {};
                    obj = data.split(';').reduce(function (acc, val) {
                        var data = val.split(':');
                        acc[data[0]] = data[1].split(',');
                        return acc;
                    }, {});
                    // TODO: move default options to gadgets
                    options.timeseries = function (id) {
                        return {
                            gadget: 'og.common.gadgets.timeseries',
                            options: {id: id, datapoints_link: false, child: true},
                            name: 'Timeseries ' + id
                        }
                    };
                    options.surface = function (id) {
                        return {
                            gadget: 'og.common.gadgets.surface',
                            name: 'Surface ' + id,
                            options: {id: id, child: true}
                        }
                    };
                    options.grid = function (id) {
                        return {gadget: 'og.analytics.Grid', name: 'grid ' + id, options: {}}
                    };
                    for (type in obj) if (obj.hasOwnProperty(type))
                        obj[type].forEach(function (val) {gadgets.push(options[type](val));});
                    return gadgets;
                };
                arr = typeof data === 'string' ? generate_arr(data) : data;
                new_gadgets = arr.map(function (obj, i) {
                    var id, gadget_class = 'OG-gadget-' + (id = counter++), gadget,
                        gadget_selector = panel_container + ' .' + gadget_class,
                        options = $.extend(true, obj.options || {}, {selector: gadget_selector}),
                        constructor = obj.gadget.split('.').reduce(function (acc, val) {return acc[val];}, window),
                        type = obj.gadget.replace(/^[a-z0-9.-_]+\.([a-z0-9.-_]+?)$/, '$1').toLowerCase();
                    $(panel_container)
                        .append('<div class="' + gadget_class + '" />')
                        .find('.' + gadget_class)
                        .css({height: '100%', display: i === arr.length - 1 ? 'block' : 'none'});
                    gadgets.push(gadget = {id: id, config: obj, type: type, gadget: new constructor(options)});
                    return gadget;
                });
                update_tabs(new_gadgets[new_gadgets.length - 1].id);
                return container;
            };
            container.del = function (obj) {
                var id;
                gadgets[gadgets.length - 1].gadget.alive();
                $(selector + ' .OG-gadget-container .OG-gadget-' + obj.id).remove();
                gadgets.splice(gadgets.indexOf(obj), 1);
                id = gadgets.length
                    ? live_id === obj.id ? gadgets[gadgets.length - 1].id : live_id
                    : null;
                if (id) gadgets[extract_index(id)].gadget.resize();
                update_tabs(id); // new active tab or empty
            };
            container.alive = function () {
                gadgets.forEach(function (obj) {obj.gadget.alive();});
                return !!$(selector).length;
            };
            container.resize = function () {
                gadgets.forEach(function (obj) {
                    if (obj.gadget.alive()) {
                        $(selector + ' .ui-layout-content').show();
                        update_tabs();
                        obj.gadget.resize();
                    }
                });
            }
        };
    }
});<|endoftext|>


<commit_before>;
(function () {
    window.delegate = function (elRoot, events) {
        var arr, key, eventType, method, selector;

        for (key in events) {
            if (events.hasOwnProperty(key)) {
                arr = key.split(' ');
                eventType = arr.shift();
                selector = arr.join(' ');
                method = events[key];

                elRoot.addEventListener(
                    eventType,
                    getEventHandler(elRoot, selector, method),
                    shouldUseCapture(eventType)
                );
            }
        }
    };

    function shouldUseCapture(eventType) {
        return ['blur', 'error', 'focus', 'load', 'resize', 'scroll'].indexOf(eventType) !== -1;
    }

    function getEventHandler(rootElement, selector, method) {
        return function (e) {
            var result = true,
                stopElement = e.currentTarget,
                targetElement = e.target;
            do {
                if (isEventTarget(rootElement, targetElement, selector)) {
                    e.target = targetElement;
                    result = method.apply(targetElement, arguments);
                }
            } while (result && targetElement != stopElement && (targetElement = targetElement.parentNode));
            return result;
        }
    }

    function isEventTarget(elRoot, elChild, selector) {
        var nodeList = elRoot.querySelectorAll(selector);
        for (var key in nodeList) {
            if (nodeList.hasOwnProperty(key) && nodeList[key] === elChild) {
                return true;
            }
        }
    }
})();<commit_msg>to set e.target，must delete e.target.<commit_after>;
(function () {
    window.delegate = function (elRoot, events) {
        var arr, key, eventType, method, selector;

        for (key in events) {
            if (events.hasOwnProperty(key)) {
                arr = key.split(' ');
                eventType = arr.shift();
                selector = arr.join(' ');
                method = events[key];

                elRoot.addEventListener(
                    eventType,
                    getEventHandler(elRoot, selector, method),
                    shouldUseCapture(eventType)
                );
            }
        }
    };

    function shouldUseCapture(eventType) {
        return ['blur', 'error', 'focus', 'load', 'resize', 'scroll'].indexOf(eventType) !== -1;
    }

    function getEventHandler(rootElement, selector, method) {
        return function (e) {
            var result = true,
                stopElement = e.currentTarget,
                targetElement = e.target;
            do {
                if (isEventTarget(rootElement, targetElement, selector)) {

                    delete e.target;
                    e.target = targetElement;

                    result = method.apply(targetElement, arguments);
                }
            } while (result && targetElement != stopElement && (targetElement = targetElement.parentNode));
            return result;
        }
    }

    function isEventTarget(elRoot, elChild, selector) {
        var nodeList = elRoot.querySelectorAll(selector);
        for (var key in nodeList) {
            if (nodeList.hasOwnProperty(key) && nodeList[key] === elChild) {
                return true;
            }
        }
    }
})();<|endoftext|>


<commit_before>if (typeof define !== 'function') {
  var define = require('amdefine')(module);
}
define(['jaribu/colors'], function (colors, undefined) {

  var pub = {};
  var cfg = {};
  cfg.type = 'console';
  var _ = {};
  var c = colors;
  //console.log(c.reset); // reset colors
  var passed = c.green + 'passed' + c.reset;
  var failed = c.red + 'failed' + c.reset;

  pub.linebreak = function () {
    _.linebreak[cfg.type]();
  };
  _.linebreak = {};
  _.linebreak.console = function () {
    console.log("\n");
  };

  pub.begin = function (num_suites, total_tests) {
    _.begin[cfg.type](num_suites, total_tests);
  };
  _.begin = {};
  _.begin.console = function (num_suites, total_tests) {
    console.log("\nrunning... "+num_suites+" suites, "+total_tests+" tests.");
  };

  pub.print = function (msg) {
    _.print[cfg.type](msg);
  };
  _.print = {};
  _.print.console = function (msg) {
    process.stdout.write(msg+"\n");
  };

  /*
  * Suite info display functions
  */
  pub.suiteBorder = function () {
    _.suiteBorder[cfg.type]();
  };
  _.suiteBorder = {};
  _.suiteBorder.console = function () {
    process.stdout.write("\n\n\n==========\n= ");
  };

  // test or suite details
  pub.details = function (name, o) {
    _.details[cfg.type](name, o);
  };
  _.details = {};
  _.details.console = function (name, o) {
    if (name === 'suite') {
      if (typeof o.name !== 'undefined') {
        process.stdout.write(c.cyan + o.name + c.reset + ' - ');
      }
      process.stdout.write(c.purple + o.desc + c.reset );
    } else {
      console.log("\n- " + '[' + o.parent.position +
          '/' + o.position + '] test ' + c.reset +
          c.purple + o.desc + c.reset);
    }
  };

  pub.beforeEach = function () {
    _.beforeEach[cfg.type]();
  };
  _.beforeEach = {};
  _.beforeEach.console = function () {
    process.stdout.write(' ... beforeEach ... ');
  };

  pub.afterEach = function () {
    _.afterEach[cfg.type]();
  };
  _.afterEach = {};
  _.afterEach.console = function () {
    process.stdout.write("... afterEach ... ");
  };

  // setup intro
  pub.setup = function (name) {
    _.setup[cfg.type](name);
  };
  _.setup = {};
  _.setup.console = function (name) {
    if (name === 'suite') {
      process.stdout.write("\n= setup ... ");
    } else {
      process.stdout.write('- test setup ... ');
    }
  };

  // takedowns
  pub.takedown = function (name) {
    _.takedown[cfg.type](name);
  };
  _.takedown = {};
  _.takedown.console = function (name) {
    if (name === 'suite') {
      process.stdout.write("... suite takedown ... ");
    } else {
      process.stdout.write("\n- test takedown ... ");
    }
  };

  _.mergeMessages = {};
  _.mergeMessages.console = function (type, o) {
    var msg = '';
    if (o[type].failmsg) {
      msg = 'failed (' + o[type].failmsg + ')';
    } else {
      msg = 'failed';
    }
    return msg + " " + c.yellow + o[type]._message + c.reset;
  };

  /*
   * display test results
   */

  // FAIL
  pub.fail = function (type, o) {
    var msg = _.mergeMessages[cfg.type](type, o);
    _.fail[cfg.type](type, o, msg);
  };
  _.fail = {};
  _.fail.console = function (type, o, msg) {
    if (type === 'actual') {
      console.log("\n" + c.redbg + ' FAIL' + c.reset + ' ' + c.cyan + o.name  +
         c.reset + ' test ' + c.red + msg + c.reset);
    } else {
      console.log(c.red + msg + c.reset);
    }

    if (o[type]._stackTrace !== undefined) {
      console.log(c.greybg+"Stack Trace:\n"+c.reset+c.red + String(o[type]._stackTrace) + c.reset);
    }
  };

  // PASS
  pub.pass = function (type, o) {
    _.pass[cfg.type](type, o);
  };
  _.pass = {};
  _.pass.console = function (type, o) {
    if (type === 'actual') {
      process.stdout.write("\n" + c.greenbg + '  OK ' + c.reset + ' ' + c.cyan + o.name  +
            c.reset + ' test' + c.green + ' passed' + c.reset);
    } else {
      process.stdout.write(c.blue + 'completed ' + c.reset);
    }
  };

  /*
  * summary displays
  */
  pub.summary = function (num_suites, summary) {
    _.summary[cfg.type](num_suites, summary);
  };

  _.summary = {};
  _.summary.console = function(num_suites, summary) {
    console.log("\n\nSummary\n=======\n");
    process.stdout.write('scaffolding report  ');
    console.log(c.red+summary.scaffolding.failed+c.reset+' failed,  ' +
          c.green+summary.scaffolding.passed+c.reset+' passed, '+
          c.cyan+summary.scaffolding.skipped+c.reset+' skipped, '+
          c.blue+summary.scaffolding.total+c.reset+' total.');
    process.stdout.write('       test report  ');
    console.log(c.red+summary.tests.failed+c.reset+' failed,  ' +
          c.green+summary.tests.passed+c.reset+' passed, '+
          c.cyan+summary.tests.skipped+c.reset+' skipped, '+
          c.blue+summary.tests.total+c.reset+' total.');
    console.log("\n");
  };

  pub.failures = function (summary) {
    _.failures[cfg.type](summary);
  };
  _.failures = {};
  _.failures.console = function (summary) {
    var i, o;
    var failedScaffoldingLength = summary.scaffolding.failObjs.length;
    if (failedScaffoldingLength > 0) {
      process.stdout.write("\nfailed scaffolding:");
    }
    pub.linebreak();
    for (i = 0; i < failedScaffoldingLength; i += 1) {
      o = summary.scaffolding.failObjs[i];
      if (o.obj.parent !== undefined) {
        pub.details('suite', o.obj.parent);
        pub.details(o.name, o.obj);
      } else {
        pub.details('suite', o.obj);
      }
      process.stdout.write("\n"+o.type+": ");
      pub.fail(o.type, o.obj);
    }

    var failedTestsLength = summary.tests.failObjs.length;
    if (failedTestsLength > 0) {
      console.log("\nfailed tests:");
    }
    for (i = 0; i < failedTestsLength; i += 1) {
      o = summary.tests.failObjs[i];
      pub.details('suite', o.obj.parent);
      pub.details(o.name, o.obj);
      pub.fail(o.type, o.obj);
      pub.linebreak();
    }
  };

  return pub;
});
<commit_msg>added very simple browser output functionality<commit_after>if (typeof define !== 'function') {
  var define = require('amdefine')(module);
}
define(['jaribu/colors'], function (colors, undefined) {

  var pub = {},
      _ = {},
      c = colors,
      cfg = {
        type: 'console'
      },
      //console.log(c.reset); // reset colors
      passed = c.green + 'passed' + c.reset,
      failed = c.red + 'failed' + c.reset;

  if ((typeof window !== 'undefined') && (typeof window.document === 'object')) {
    cfg.type = 'browser';
  }

  function __browserWrite (line) {
    window.document.write(line);
  }

  pub.linebreak = function () {
    _.linebreak[cfg.type]();
  };
  _.linebreak = {
    console: function () {
      console.log("\n");
    },
    browser: function () {
      __browserWrite('
');
    }
  };

  pub.begin = function (num_suites, total_tests) {
    _.begin[cfg.type](num_suites, total_tests);
  };
  _.begin = {
    console: function (num_suites, total_tests) {
      console.log("\nrunning... " + num_suites + ' suites, ' + total_tests + ' tests.');
    },
    browser: function (num_suites, total_tests) {
      __browserWrite('
running... ' + num_suites + ' suites, ' + total_tests + ' tests.
');
    }
  };

  pub.print = function (msg) {
    _.print[cfg.type](msg);
  };
  _.print = {
    console: function (msg) {
      process.stdout.write(msg + "\n");
    },
    browser: function (msg) {
      __browserWrite(msg + '
');
    }
  };

  /*
  * Suite info display functions
  */
  pub.suiteBorder = function () {
    _.suiteBorder[cfg.type]();
  };
  _.suiteBorder = {
    console: function () {
      process.stdout.write("\n\n\n==========\n= ");
    },
    browser: function () {
      __browserWrite('

==========
= ');
    }
  };

  // test or suite details
  pub.details = function (name, o) {
    _.details[cfg.type](name, o);
  };
  _.details = {
    console: function (name, o) {
      if (name === 'suite') {
        if (typeof o.name !== 'undefined') {
          process.stdout.write(c.cyan + o.name + c.reset + ' - ');
        }
        process.stdout.write(c.purple + o.desc + c.reset );
      } else {
        console.log("\n- " + '[' + o.parent.position +
            '/' + o.position + '] test ' + c.reset +
            c.purple + o.desc + c.reset);
      }
    },
    browser: function (name, o) {
      if (name === 'suite') {
        if (typeof o.name !== 'undefined') {
          __browserWrite(c.cyan + o.name + c.reset + ' - ');
        }
        __browserWrite(c.purple + o.desc + c.reset );
      } else {
        __browserWrite("
- " + '[' + o.parent.position +
            '/' + o.position + '] test ' + c.reset +
            c.purple + o.desc + c.reset + '
');
      }
    }
  };

  pub.beforeEach = function () {
    _.beforeEach[cfg.type]();
  };
  _.beforeEach = {
    console: function () {
      process.stdout.write(' ... beforeEach ... ');
    },
    browser: function () {
      __browserWrite(' ... beforeEach ... ');
    }
  };

  pub.afterEach = function () {
    _.afterEach[cfg.type]();
  };
  _.afterEach = {
    console: function () {
      process.stdout.write('... afterEach ... ');
    },
    browser: function () {
      __browserWrite('... afterEach ... ');
    }
  };

  // setup intro
  pub.setup = function (name) {
    _.setup[cfg.type](name);
  };
  _.setup = {
    console: function (name) {
      if (name === 'suite') {
        process.stdout.write("\n" + '= setup ... ');
      } else {
        process.stdout.write('- test setup ... ');
      }
    },
    browser: function (name) {
      if (name === 'suite') {
        __browserWrite('
= setup ... ');
      } else {
        __browserWrite('- test setup ... ');
      }
    }
  };

  // takedowns
  pub.takedown = function (name) {
    _.takedown[cfg.type](name);
  };
  _.takedown = {
    console: function (name) {
      if (name === 'suite') {
        process.stdout.write('... suite takedown ... ');
      } else {
        process.stdout.write("\n" + '- test takedown ... ');
      }
    },
    browser: function (name) {
      if (name === 'suite') {
        __browserWrite('... suite takedown ... ');
      } else {
        __browserWrite('
- test takedown ... ');
      }
    }
  };

  _.mergeMessages = {
    console: function (type, o) {
      var msg = '';
      if (o[type].failmsg) {
        msg = 'failed (' + o[type].failmsg + ')';
      } else {
        msg = 'failed';
      }
      return msg + " " + c.yellow + o[type]._message + c.reset;
    },
    browser: function (type, o) {
      var msg = '';
      if (o[type].failmsg) {
        msg = 'failed (' + o[type].failmsg + ')';
      } else {
        msg = 'failed';
      }
      return msg + " " + c.yellow + o[type]._message + c.reset;
    }
  };

  /*
   * display test results
   */

  // FAIL
  pub.fail = function (type, o) {
    var msg = _.mergeMessages[cfg.type](type, o);
    _.fail[cfg.type](type, o, msg);
  };
  _.fail = {
    console: function (type, o, msg) {
      if (type === 'actual') {
        console.log("\n" + c.redbg + ' FAIL' + c.reset + ' ' + c.cyan + o.name  +
           c.reset + ' test ' + c.red + msg + c.reset);
      } else {
        console.log(c.red + msg + c.reset);
      }

      if (o[type]._stackTrace !== undefined) {
        console.log(c.greybg + 'Stack Trace:' + "\n" + c.reset + c.red + String(o[type]._stackTrace) + c.reset);
      }
    },
    browser: function (type, o, msg) {
      if (type === 'actual') {
        __browserWrite('
' + c.redbg + ' FAIL' + c.reset + ' ' + c.cyan + o.name  +
           c.reset + ' test ' + c.red + msg + c.reset);
      } else {
        __browserWrite('
' + c.red + msg + c.reset + '
');
      }

      if (o[type]._stackTrace !== undefined) {
        __browserWrite('
' + c.greybg + 'Stack Trace:' + "
" + c.reset + c.red + String(o[type]._stackTrace) + c.reset + '
');
      }
    }
  };

  // PASS
  pub.pass = function (type, o) {
    _.pass[cfg.type](type, o);
  };
  _.pass = {
    console: function (type, o) {
      if (type === 'actual') {
        process.stdout.write("\n" + c.greenbg + '  OK ' + c.reset + ' ' + c.cyan + o.name  +
              c.reset + ' test' + c.green + ' passed' + c.reset);
      } else {
        process.stdout.write(c.blue + 'completed ' + c.reset);
      }
    },
    browser: function (type, o) {
      if (type === 'actual') {
        __browserWrite('
' + c.greenbg + '  OK ' + c.reset + ' ' + c.cyan + o.name  +
              c.reset + ' test' + c.green + ' passed' + c.reset);
      } else {
        __browserWrite(c.blue + 'completed ' + c.reset);
      }
    }
  };

  /*
  * summary displays
  */
  pub.summary = function (num_suites, summary) {
    _.summary[cfg.type](num_suites, summary);
  };

  _.summary = {
    console: function (num_suites, summary) {
      console.log("\n\nSummary\n=======\n");
      process.stdout.write('scaffolding report  ');
      console.log(c.red + summary.scaffolding.failed + c.reset + ' failed,  ' +
            c.green + summary.scaffolding.passed + c.reset + ' passed, ' +
            c.cyan + summary.scaffolding.skipped + c.reset + ' skipped, ' +
            c.blue + summary.scaffolding.total + c.reset + ' total.');
      process.stdout.write('       test report  ');
      console.log(c.red + summary.tests.failed + c.reset + ' failed,  ' +
            c.green + summary.tests.passed + c.reset + ' passed, '+
            c.cyan + summary.tests.skipped + c.reset + ' skipped, '+
            c.blue + summary.tests.total + c.reset + ' total.');
      console.log("\n");
    },
    browser: function (num_suites, summary) {
      __browserWrite('

Summary

');
      __browserWrite('scaffolding report  ');
      __browserWrite('
' + c.red + summary.scaffolding.failed + c.reset + ' failed,  ' +
            c.green + summary.scaffolding.passed + c.reset + ' passed, '+
            c.cyan + summary.scaffolding.skipped + c.reset + ' skipped, '+
            c.blue + summary.scaffolding.total + c.reset + ' total.
');
      __browserWrite('       test report  ');
      __browserWrite('
' + c.red + summary.tests.failed + c.reset + ' failed,  ' +
            c.green + summary.tests.passed + c.reset + ' passed, '+
            c.cyan + summary.tests.skipped + c.reset +' skipped, '+
            c.blue + summary.tests.total + c.reset + ' total.
');
      __browserWrite('

');
    }
  };

  pub.failures = function (summary) {
    _.failures[cfg.type](summary);
  };
  _.failures = {
    console: function (summary) {
      var i, o;
      var failedScaffoldingLength = summary.scaffolding.failObjs.length;
      if (failedScaffoldingLength > 0) {
        process.stdout.write("\nfailed scaffolding:");
      }
      pub.linebreak();
      for (i = 0; i < failedScaffoldingLength; i += 1) {
        o = summary.scaffolding.failObjs[i];
        if (o.obj.parent !== undefined) {
          pub.details('suite', o.obj.parent);
          pub.details(o.name, o.obj);
        } else {
          pub.details('suite', o.obj);
        }
        process.stdout.write("\n" + o.type + ': ');
        pub.fail(o.type, o.obj);
      }

      var failedTestsLength = summary.tests.failObjs.length;
      if (failedTestsLength > 0) {
        console.log("\nfailed tests:");
      }
      for (i = 0; i < failedTestsLength; i += 1) {
        o = summary.tests.failObjs[i];
        pub.details('suite', o.obj.parent);
        pub.details(o.name, o.obj);
        pub.fail(o.type, o.obj);
        pub.linebreak();
      }
    },
    browser: function (summary) {
      var i, o;
      var failedScaffoldingLength = summary.scaffolding.failObjs.length;
      if (failedScaffoldingLength > 0) {
        __browserWrite("
failed scaffolding:");
      }
      pub.linebreak();
      for (i = 0; i < failedScaffoldingLength; i += 1) {
        o = summary.scaffolding.failObjs[i];
        if (o.obj.parent !== undefined) {
          pub.details('suite', o.obj.parent);
          pub.details(o.name, o.obj);
        } else {
          pub.details('suite', o.obj);
        }
        __browserWrite("\n" + o.type + ': ');
        pub.fail(o.type, o.obj);
      }

      var failedTestsLength = summary.tests.failObjs.length;
      if (failedTestsLength > 0) {
        console.log("
failed tests:");
      }
      for (i = 0; i < failedTestsLength; i += 1) {
        o = summary.tests.failObjs[i];
        pub.details('suite', o.obj.parent);
        pub.details(o.name, o.obj);
        pub.fail(o.type, o.obj);
        pub.linebreak();
      }
    }
  };

  return pub;
});
<|endoftext|>


<commit_before>const ReplManager = require("./repl");
const Command = require("./command");
const provision = require("truffle-provisioner");
const contract = require("truffle-contract");
const { Web3Shim } = require("truffle-interface-adapter");
const vm = require("vm");
const expect = require("truffle-expect");
const TruffleError = require("truffle-error");
const fse = require("fs-extra");
const path = require("path");
const EventEmitter = require("events");

class Console extends EventEmitter {
  constructor(tasks, options) {
    super();
    EventEmitter.call(this);

    expect.options(options, [
      "working_directory",
      "contracts_directory",
      "contracts_build_directory",
      "migrations_directory",
      "networks",
      "network",
      "network_id",
      "provider",
      "resolver",
      "build_directory"
    ]);

    this.options = options;

    this.repl = options.repl || new ReplManager(options);
    this.command = new Command(tasks);

    this.web3 = new Web3Shim({
      provider: options.provider,
      networkType: options.networks[options.network].type
    });

    // Bubble the ReplManager's exit event
    this.repl.on("exit", () => this.emit("exit"));
  }

  start(callback) {
    if (!this.repl) this.repl = new Repl(this.options);

    // TODO: This should probalby be elsewhere.
    // It's here to ensure the repl manager instance gets
    // passed down to commands.
    this.options.repl = this.repl;

    try {
      const abstractions = this.provision();
      this.web3.eth.getAccounts().then(accounts => {
        this.repl.start({
          prompt: "truffle(" + this.options.network + ")> ",
          context: {
            web3: this.web3,
            accounts: accounts
          },
          interpreter: this.interpret.bind(this),
          done: callback
        });
      });

      this.resetContractsInConsoleContext(abstractions);
    } catch (error) {
      this.options.logger.log(
        "Unexpected error: Cannot provision contracts while instantiating the console."
      );
      this.options.logger.log(error.stack || error.message || error);
    }
  }

  provision() {
    let files;
    try {
      files = fse.readdirSync(this.options.contracts_build_directory);
    } catch (error) {
      // Error reading the build directory? Must mean it doesn't exist or we don't have access to it.
      // Couldn't provision the contracts if we wanted. It's possible we're hiding very rare FS
      // errors, but that's better than showing the user error messages that will be "build folder
      // doesn't exist" 99.9% of the time.
    }

    let jsonBlobs = [];
    files = files || [];

    files.forEach(file => {
      try {
        const body = fse.readFileSync(
          path.join(this.options.contracts_build_directory, file),
          "utf8"
        );
        jsonBlobs.push(JSON.parse(body));
      } catch (error) {
        throw new Error(`Error parsing or reading ${file}: ${error.message}`);
      }
    });

    const abstractions = jsonBlobs.map(json => {
      const abstraction = contract(json);
      provision(abstraction, this.options);
      return abstraction;
    });

    this.resetContractsInConsoleContext(abstractions);

    return abstractions;
  }

  resetContractsInConsoleContext(abstractions) {
    abstractions = abstractions || [];

    const contextVars = {};

    abstractions.forEach(abstraction => {
      contextVars[abstraction.contract_name] = abstraction;
    });

    this.repl.setContextVars(contextVars);
  }

  interpret(cmd, context, filename, callback) {
    if (this.command.getCommand(cmd.trim(), this.options.noAliases) != null) {
      return this.command.run(cmd.trim(), this.options, error => {
        if (error) {
          // Perform error handling ourselves.
          if (error instanceof TruffleError) {
            console.log(error.message);
          } else {
            // Bubble up all other unexpected errors.
            console.log(error.stack || error.toString());
          }
          return callback();
        }

        // Reprovision after each command as it may change contracts.
        try {
          this.provision();
          callback();
        } catch (error) {
          // Don't pass abstractions to the callback if they're there or else
          // they'll get printed in the repl.
          callback(error);
        }
      });
    }

    // Much of the following code is from here, though spruced up:
    // https://github.com/nfcampos/await-outside

    /*
    - allow whitespace before everything else
    - optionally capture `var|let|const <varname> = `
      - varname only matches if it starts with a-Z or _ or $
        and if contains only those chars or numbers
      - this is overly restrictive but is easier to maintain
    - capture `await <anything that follows it>`
    */
    let includesAwait = /^\s*((?:(?:var|const|let)\s+)?[a-zA-Z_$][0-9a-zA-Z_$]*\s*=\s*)?(\(?\s*await[\s\S]*)/;

    const match = cmd.match(includesAwait);
    let source = cmd;
    let assignment = null;

    // If our code includes an await, add special processing to ensure it's evaluated properly.
    if (match) {
      let assign = match[1];
      const expression = match[2];

      const RESULT = "__await_outside_result";

      // Wrap the await inside an async function.
      // Strange indentation keeps column offset correct in stack traces
      source = `(async function() { try { ${
        assign ? `global.${RESULT} =` : "return"
      } (
  ${expression.trim()}
  ); } catch(e) { global.ERROR = e; throw e; } }())`;

      assignment = assign
        ? `${assign.trim()} global.${RESULT}; void delete global.${RESULT};`
        : null;
    }

    const runScript = script => {
      const options = {
        displayErrors: true,
        breakOnSigint: true,
        filename: filename
      };
      return script.runInContext(context, options);
    };

    let script;
    try {
      const options = { displayErrors: true, lineOffset: -1 };
      script = vm.createScript(source, options);
    } catch (error) {
      // If syntax error, or similar, bail.
      return callback(error);
    }

    // Ensure our script returns a promise whether we're using an
    // async function or not. If our script is an async function,
    // this will ensure the console waits until that await is finished.
    Promise.resolve(runScript(script))
      .then(value => {
        // If there's an assignment to run, run that.
        if (assignment) return runScript(vm.createScript(assignment));
        return value;
      })
      .then(value => {
        // All good? Return the value (e.g., eval'd script or assignment)
        callback(null, value);
      })
      .catch(callback);
  }
}

module.exports = Console;
<commit_msg>To prevent some implicit ordering<commit_after>const ReplManager = require("./repl");
const Command = require("./command");
const provision = require("truffle-provisioner");
const contract = require("truffle-contract");
const { Web3Shim } = require("truffle-interface-adapter");
const vm = require("vm");
const expect = require("truffle-expect");
const TruffleError = require("truffle-error");
const fse = require("fs-extra");
const path = require("path");
const EventEmitter = require("events");

class Console extends EventEmitter {
  constructor(tasks, options) {
    super();
    EventEmitter.call(this);

    expect.options(options, [
      "working_directory",
      "contracts_directory",
      "contracts_build_directory",
      "migrations_directory",
      "networks",
      "network",
      "network_id",
      "provider",
      "resolver",
      "build_directory"
    ]);

    this.options = options;

    this.repl = options.repl || new ReplManager(options);
    this.command = new Command(tasks);

    this.web3 = new Web3Shim({
      provider: options.provider,
      networkType: options.networks[options.network].type
    });

    // Bubble the ReplManager's exit event
    this.repl.on("exit", () => this.emit("exit"));
  }

  start(callback) {
    if (!this.repl) this.repl = new Repl(this.options);

    // TODO: This should probalby be elsewhere.
    // It's here to ensure the repl manager instance gets
    // passed down to commands.
    this.options.repl = this.repl;

    try {
      this.web3.eth.getAccounts().then(accounts => {
        const abstractions = this.provision();

        this.repl.start({
          prompt: "truffle(" + this.options.network + ")> ",
          context: {
            web3: this.web3,
            accounts: accounts
          },
          interpreter: this.interpret.bind(this),
          done: callback
        });

        this.resetContractsInConsoleContext(abstractions);
      });
    } catch (error) {
      this.options.logger.log(
        "Unexpected error: Cannot provision contracts while instantiating the console."
      );
      this.options.logger.log(error.stack || error.message || error);
    }
  }

  provision() {
    let files;
    try {
      files = fse.readdirSync(this.options.contracts_build_directory);
    } catch (error) {
      // Error reading the build directory? Must mean it doesn't exist or we don't have access to it.
      // Couldn't provision the contracts if we wanted. It's possible we're hiding very rare FS
      // errors, but that's better than showing the user error messages that will be "build folder
      // doesn't exist" 99.9% of the time.
    }

    let jsonBlobs = [];
    files = files || [];

    files.forEach(file => {
      try {
        const body = fse.readFileSync(
          path.join(this.options.contracts_build_directory, file),
          "utf8"
        );
        jsonBlobs.push(JSON.parse(body));
      } catch (error) {
        throw new Error(`Error parsing or reading ${file}: ${error.message}`);
      }
    });

    const abstractions = jsonBlobs.map(json => {
      const abstraction = contract(json);
      provision(abstraction, this.options);
      return abstraction;
    });

    this.resetContractsInConsoleContext(abstractions);

    return abstractions;
  }

  resetContractsInConsoleContext(abstractions) {
    abstractions = abstractions || [];

    const contextVars = {};

    abstractions.forEach(abstraction => {
      contextVars[abstraction.contract_name] = abstraction;
    });

    this.repl.setContextVars(contextVars);
  }

  interpret(cmd, context, filename, callback) {
    if (this.command.getCommand(cmd.trim(), this.options.noAliases) != null) {
      return this.command.run(cmd.trim(), this.options, error => {
        if (error) {
          // Perform error handling ourselves.
          if (error instanceof TruffleError) {
            console.log(error.message);
          } else {
            // Bubble up all other unexpected errors.
            console.log(error.stack || error.toString());
          }
          return callback();
        }

        // Reprovision after each command as it may change contracts.
        try {
          this.provision();
          callback();
        } catch (error) {
          // Don't pass abstractions to the callback if they're there or else
          // they'll get printed in the repl.
          callback(error);
        }
      });
    }

    // Much of the following code is from here, though spruced up:
    // https://github.com/nfcampos/await-outside

    /*
    - allow whitespace before everything else
    - optionally capture `var|let|const <varname> = `
      - varname only matches if it starts with a-Z or _ or $
        and if contains only those chars or numbers
      - this is overly restrictive but is easier to maintain
    - capture `await <anything that follows it>`
    */
    let includesAwait = /^\s*((?:(?:var|const|let)\s+)?[a-zA-Z_$][0-9a-zA-Z_$]*\s*=\s*)?(\(?\s*await[\s\S]*)/;

    const match = cmd.match(includesAwait);
    let source = cmd;
    let assignment = null;

    // If our code includes an await, add special processing to ensure it's evaluated properly.
    if (match) {
      let assign = match[1];
      const expression = match[2];

      const RESULT = "__await_outside_result";

      // Wrap the await inside an async function.
      // Strange indentation keeps column offset correct in stack traces
      source = `(async function() { try { ${
        assign ? `global.${RESULT} =` : "return"
      } (
  ${expression.trim()}
  ); } catch(e) { global.ERROR = e; throw e; } }())`;

      assignment = assign
        ? `${assign.trim()} global.${RESULT}; void delete global.${RESULT};`
        : null;
    }

    const runScript = script => {
      const options = {
        displayErrors: true,
        breakOnSigint: true,
        filename: filename
      };
      return script.runInContext(context, options);
    };

    let script;
    try {
      const options = { displayErrors: true, lineOffset: -1 };
      script = vm.createScript(source, options);
    } catch (error) {
      // If syntax error, or similar, bail.
      return callback(error);
    }

    // Ensure our script returns a promise whether we're using an
    // async function or not. If our script is an async function,
    // this will ensure the console waits until that await is finished.
    Promise.resolve(runScript(script))
      .then(value => {
        // If there's an assignment to run, run that.
        if (assignment) return runScript(vm.createScript(assignment));
        return value;
      })
      .then(value => {
        // All good? Return the value (e.g., eval'd script or assignment)
        callback(null, value);
      })
      .catch(callback);
  }
}

module.exports = Console;
<|endoftext|>