<commit_before>'use strict';

/**
 * Configuration options for a {@link Postgres}.
 * @typedef {Object} Postgres~Config
 * @property {String} [bin=postgres]
 * @property {String} [conf]
 * @property {(Number|String)} [port=5432]
 * @property {String} datadir
 */

/**
 * Invoked when an operation (i.e. {@link Postgres#open}) completes.
 * @callback Postgres~callback
 * @argument {Error} err
 */

/**
 * Emitted when a PostgreSQL server prints to stdout.
 * @event Postgres#stdout
 */

/**
 * Emitted when attempting to start a PostgreSQL server.
 * @event Postgres#opening
 */

/**
 * Emitted when a PostgreSQL server becomes ready to service requests.
 * @event Postgres#open
 */

/**
 * Emitted when attempting to stop a PostgreSQL server.
 * @event Postgres#closing
 */

/**
 * Emitted once a PostgreSQL server has stopped.
 * @event Postgres#close
 */

const childprocess = require('child_process');
const events = require('events');
const PromiseQueue = require('promise-queue');
const regExp = {
  terminalMessage: /ready\sto\saccept|already\sin\suse|denied|fatal|postgres/i,
  errorMessage: /^(?:fatal|postgres):\s+(.*)/i,
  singleWhiteSpace: /\s/g,
  multipleWhiteSpace: /\s\s+/g,
  newline: /\r?\n/
};

/**
 * Start and stop a local PostgreSQL server like a boss.
 * @class
 */
class Postgres extends events.EventEmitter {

  /**
   * Get a function that takes chunks of stdin data, aggregates it, and passes
   * it in complete lines, one by one, to a given {@link Postgres~callback}.
   * @argument {Postgres~callback} callback
   * @return {Function}
   */
  static getTextLineAggregator(callback) {
    let buffer = '';

    return (data) => {
      const fragments = data.toString().split(regExp.newline);
      const lines = fragments.slice(0, fragments.length - 1);

      // If there was an unended line in the previous dump, complete it by
      // the first section.
      lines[0] = buffer + lines[0];

      // If there is an unended line in this dump, store it to be completed by
      // the next. This assumes there will be a terminating newline character
      // at some point. Generally, this is a safe assumption.
      buffer = fragments[fragments.length - 1];

      for (let line of lines) {
        callback(line);
      }
    };
  }

  /**
   * Populate a given {@link Postgres~Config} with values from a
   * given {@link Postgres~Config}.
   * @protected
   * @argument {Postgres~Config} source
   * @argument {Postgres~Config} target
   * @return {Postgres~Config}
   */
  static parseConfig(source, target) {
    if (target == null) {
      target = Object.create(null);
    }

    if (typeof source === 'string') {
      target.datadir = source;

      return target;
    }

    if (source == null || typeof source !== 'object') {
      return target;
    }

    if (source.bin != null) {
      target.bin = source.bin;
    }

    if (source.conf != null) {
      target.conf = source.conf;

      return target;
    }

    if (source.datadir != null) {
      target.datadir = source.datadir;
    }

    if (source.port != null) {
      target.port = source.port;
    }

    return target;
  }

  /**
   * Parse process flags for PostgreSQL from a given {@link Postgres~Config}.
   * @protected
   * @argument {Postgres~Config} config
   * @return {Array.<String>}
   */
  static parseFlags(config) {
    if (config.conf != null) {
      return ['-c', `config_file=${config.conf}`];
    }

    const flags = [];

    if (config.datadir != null) {
      flags.push('-D', config.datadir);
    }

    if (config.port != null) {
      flags.push('-p', config.port);
    }

    return flags;
  }

  /**
   * Parse Redis server output for terminal messages.
   * @protected
   * @argument {String} string
   * @return {Object}
   */
  static parseData(string) {
    const matches = regExp.terminalMessage.exec(string);

    if (matches === null) {
      return null;
    }

    const result = {
      err: null,
      key: matches
      .pop()
      .replace(regExp.singleWhiteSpace, '')
      .toLowerCase()
    };

    switch (result.key) {
      case 'readytoaccept':
        break;

      case 'alreadyinuse':
        result.err = new Error('Address already in use');
        result.err.code = -1;

        break;

      case 'denied':
        result.err = new Error('Permission denied');
        result.err.code = -2;

        break;

      case 'postgres':
      case 'fatal': {
        const matches = regExp.errorMessage.exec(string);

        result.err = new Error(
          matches === null ? string : matches.pop()
        );
        result.err.code = -3;

        break;
      }
    }

    return result;
  }

  /**
   * Start a given {@linkcode server}.
   * @protected
   * @fires Postgres#stdout
   * @fires Postgres#opening
   * @fires Postgres#open
   * @fires Postgres#closing
   * @fires Postgres#close
   * @argument {Postgres} server
   * @return {Promise}
   */
  static open(server) {
    if (server.isOpening) {
      return server.openPromise;
    }

    server.isOpening = true;
    server.isClosing = false;
    server.openPromise = server.promiseQueue.add(() => {
      if (server.isClosing || server.isRunning) {
        server.isOpening = false;

        return Promise.resolve(null);
      }

      return new Promise((resolve, reject) => {
        /**
         * A listener for the current server process' stdout that resolves or
         * rejects the current {@link Promise} when done.
         * @see Postgres.getTextLineAggregator
         * @see Postgres.parseData
         * @argument {Buffer} buffer
         * @return {undefined}
         */
        const dataListener = Postgres.getTextLineAggregator((string) => {
          const result = Postgres.parseData(string);

          if (result === null) {
            return;
          }

          server.process.stdout.removeListener('data', dataListener);
          server.process.stderr.removeListener('data', dataListener);

          server.isOpening = false;

          if (result.err === null) {
            server.isRunning = true;

            server.emit('open');
            resolve(null);
          }
          else {
            server.isClosing = true;

            server.emit('closing');
            server.process.once('close', () => reject(result.err));
          }
        });

        /**
         * A listener to close the server when the current process exits.
         * @return {undefined}
         */
        const exitListener = () => {
          // istanbul ignore next
          server.close();
        };

        /**
         * Get a text line aggregator that emits a given {@linkcode event}
         * for the current server.
         * @see Postgres.getTextLineAggregator
         * @argument {String} event
         * @return {Function}
         */
        const getDataPropagator = (event) =>
          Postgres.getTextLineAggregator((line) => server.emit(event, line));

        server.emit('opening');

        server.process = childprocess.spawn(
          server.config.bin,
          Postgres.parseFlags(server.config)
        );

        server.process.stderr.on('data', dataListener);
        server.process.stderr.on('data', getDataPropagator('stderr'));
        server.process.stdout.on('data', dataListener);
        server.process.stdout.on('data', getDataPropagator('stdout'));
        server.process.on('close', () => {
          server.process = null;
          server.isRunning = false;
          server.isClosing = false;

          process.removeListener('exit', exitListener);
          server.emit('close');
        });
        process.on('exit', exitListener);
      });
    });

    return server.openPromise;
  }

  /**
   * Stop a given {@linkcode server}.
   * @protected
   * @fires Postgres#closing
   * @argument {Postgres} server
   * @return {Promise}
   */
  static close(server) {
    if (server.isClosing) {
      return server.closePromise;
    }

    server.isClosing = true;
    server.isOpening = false;
    server.closePromise = server.promiseQueue.add(() => {
      if (server.isOpening || !server.isRunning) {
        server.isClosing = false;

        return Promise.resolve(null);
      }

      return new Promise((resolve) => {
        server.emit('closing');
        server.process.once('close', () => resolve(null));
        server.process.kill();
      });
    });

    return server.closePromise;
  }

  /**
   * Construct a new {@link Postgres}.
   * @argument {(Number|String|Postgres~Config)} [configOrDataDir]
   * A number or string that is a port or an object for configuration.
   */
  constructor(configOrDataDir) {
    super();

    /**
     * Configuration options.
     * @protected
     * @type {Postgres~Config}
     */
    this.config = Postgres.parseConfig(configOrDataDir, {
      bin: 'postgres',
      conf: null,
      port: 5432,
      datadir: null
    });

    /**
     * The current process.
     * @protected
     * @type {ChildProcess}
     */
    this.process = null;

    /**
     * The last {@link Promise} returned by {@link Postgres#open}.
     * @protected
     * @type {Promise}
     */
    this.openPromise = Promise.resolve(null);

    /**
     * The last {@link Promise} returned by {@link Postgres#close}.
     * @protected
     * @type {Promise}
     */
    this.closePromise = Promise.resolve(null);

    /**
     * A serial queue of open and close promises.
     * @protected
     * @type {PromiseQueue}
     */
    this.promiseQueue = new PromiseQueue(1);

    /**
     * Determine if the instance is closing a PostgreSQL server; {@linkcode true}
     * while a process is being, or about to be, killed until the
     * contained PostgreSQL server either closes or errs.
     * @readonly
     * @type {Boolean}
     */
    this.isClosing = false;

    /**
     * Determine if the instance is starting a PostgreSQL server; {@linkcode true}
     * while a process is spawning, or about tobe spawned, until the
     * contained PostgreSQL server either starts or errs.
     * @readonly
     * @type {Boolean}
     */
    this.isRunning = false;

    /**
     * Determine if the instance is running a PostgreSQL server; {@linkcode true}
     * once a process has spawned and the contained PostgreSQL server is ready
     * to service requests.
     * @readonly
     * @type {Boolean}
     */
    this.isOpening = false;
  }

  /**
   * Open the server.
   * @argument {Postgres~callback} [callback]
   * @return {Promise}
   */
  open(callback) {
    const promise = Postgres.open(this);

    return typeof callback === 'function'
    ? promise
      .then((v) => callback(null, v))
      .catch((e) => callback(e, null))
    : promise;
  }

  /**
   * Close the server.
   * @argument {Postgres~callback} [callback]
   * @return {Promise}
   */
  close(callback) {
    const promise = Postgres.close(this);

    return typeof callback === 'function'
    ? promise
      .then((v) => callback(null, v))
      .catch((e) => callback(e, null))
    : promise;
  }
}

module.exports = exports = Postgres;
<commit_msg>Fix PostgreSQL lockfile permission error<commit_after>'use strict';

/**
 * Configuration options for a {@link Postgres}.
 * @typedef {Object} Postgres~Config
 * @property {String} [bin=postgres]
 * @property {String} [conf]
 * @property {(Number|String)} [port=5432]
 * @property {String} datadir
 */

/**
 * Invoked when an operation (i.e. {@link Postgres#open}) completes.
 * @callback Postgres~callback
 * @argument {Error} err
 */

/**
 * Emitted when a PostgreSQL server prints to stdout.
 * @event Postgres#stdout
 */

/**
 * Emitted when attempting to start a PostgreSQL server.
 * @event Postgres#opening
 */

/**
 * Emitted when a PostgreSQL server becomes ready to service requests.
 * @event Postgres#open
 */

/**
 * Emitted when attempting to stop a PostgreSQL server.
 * @event Postgres#closing
 */

/**
 * Emitted once a PostgreSQL server has stopped.
 * @event Postgres#close
 */

const childprocess = require('child_process');
const events = require('events');
const PromiseQueue = require('promise-queue');
const regExp = {
  terminalMessage: /ready\sto\saccept|already\sin\suse|denied|fatal|postgres/i,
  errorMessage: /^(?:fatal|postgres):\s+(.*)/i,
  singleWhiteSpace: /\s/g,
  multipleWhiteSpace: /\s\s+/g,
  newline: /\r?\n/
};

/**
 * Start and stop a local PostgreSQL server like a boss.
 * @class
 */
class Postgres extends events.EventEmitter {

  /**
   * Get a function that takes chunks of stdin data, aggregates it, and passes
   * it in complete lines, one by one, to a given {@link Postgres~callback}.
   * @argument {Postgres~callback} callback
   * @return {Function}
   */
  static getTextLineAggregator(callback) {
    let buffer = '';

    return (data) => {
      const fragments = data.toString().split(regExp.newline);
      const lines = fragments.slice(0, fragments.length - 1);

      // If there was an unended line in the previous dump, complete it by
      // the first section.
      lines[0] = buffer + lines[0];

      // If there is an unended line in this dump, store it to be completed by
      // the next. This assumes there will be a terminating newline character
      // at some point. Generally, this is a safe assumption.
      buffer = fragments[fragments.length - 1];

      for (let line of lines) {
        callback(line);
      }
    };
  }

  /**
   * Populate a given {@link Postgres~Config} with values from a
   * given {@link Postgres~Config}.
   * @protected
   * @argument {Postgres~Config} source
   * @argument {Postgres~Config} target
   * @return {Postgres~Config}
   */
  static parseConfig(source, target) {
    if (target == null) {
      target = Object.create(null);
    }

    if (typeof source === 'string') {
      target.datadir = source;

      return target;
    }

    if (source == null || typeof source !== 'object') {
      return target;
    }

    if (source.bin != null) {
      target.bin = source.bin;
    }

    if (source.conf != null) {
      target.conf = source.conf;

      return target;
    }

    if (source.datadir != null) {
      target.datadir = source.datadir;
    }

    if (source.port != null) {
      target.port = source.port;
    }

    return target;
  }

  /**
   * Parse process flags for PostgreSQL from a given {@link Postgres~Config}.
   * @protected
   * @argument {Postgres~Config} config
   * @return {Array.<String>}
   */
  static parseFlags(config) {
    if (config.conf != null) {
      return ['-c', `config_file=${config.conf}`];
    }

    const flags = [];

    if (config.datadir != null) {
      flags.push('-D', config.datadir);
    }

    if (config.port != null) {
      flags.push('-p', config.port);
    }

    return flags;
  }

  /**
   * Parse Redis server output for terminal messages.
   * @protected
   * @argument {String} string
   * @return {Object}
   */
  static parseData(string) {
    const matches = regExp.terminalMessage.exec(string);

    if (matches === null) {
      return null;
    }

    const result = {
      err: null,
      key: matches
      .pop()
      .replace(regExp.singleWhiteSpace, '')
      .toLowerCase()
    };

    switch (result.key) {
      case 'readytoaccept':
        break;

      case 'alreadyinuse':
        result.err = new Error('Address already in use');
        result.err.code = -1;

        break;

      case 'denied':
        result.err = new Error('Permission denied');
        result.err.code = -2;

        break;

      case 'postgres':
      case 'fatal': {
        const matches = regExp.errorMessage.exec(string);

        result.err = new Error(
          matches === null ? string : matches.pop()
        );
        result.err.code = -3;

        break;
      }
    }

    return result;
  }

  /**
   * Start a given {@linkcode server}.
   * @protected
   * @fires Postgres#stdout
   * @fires Postgres#opening
   * @fires Postgres#open
   * @fires Postgres#closing
   * @fires Postgres#close
   * @argument {Postgres} server
   * @return {Promise}
   */
  static open(server) {
    if (server.isOpening) {
      return server.openPromise;
    }

    server.isOpening = true;
    server.isClosing = false;
    server.openPromise = server.promiseQueue.add(() => {
      if (server.isClosing || server.isRunning) {
        server.isOpening = false;

        return Promise.resolve(null);
      }

      return new Promise((resolve, reject) => {
        /**
         * A listener for the current server process' stdout that resolves or
         * rejects the current {@link Promise} when done.
         * @see Postgres.getTextLineAggregator
         * @see Postgres.parseData
         * @argument {Buffer} buffer
         * @return {undefined}
         */
        const dataListener = Postgres.getTextLineAggregator((string) => {
          const result = Postgres.parseData(string);

          if (result === null) {
            return;
          }

          server.process.stdout.removeListener('data', dataListener);
          server.process.stderr.removeListener('data', dataListener);

          server.isOpening = false;

          if (result.err === null) {
            server.isRunning = true;

            server.emit('open');
            resolve(null);
          }
          else {
            server.isClosing = true;

            server.emit('closing');
            server.process.once('close', () => reject(result.err));
          }
        });

        /**
         * A listener to close the server when the current process exits.
         * @return {undefined}
         */
        const exitListener = () => {
          // istanbul ignore next
          server.close();
        };

        /**
         * Get a text line aggregator that emits a given {@linkcode event}
         * for the current server.
         * @see Postgres.getTextLineAggregator
         * @argument {String} event
         * @return {Function}
         */
        const getDataPropagator = (event) =>
          Postgres.getTextLineAggregator((line) => server.emit(event, line));

        server.emit('opening');

        const flags = Postgres.parseFlags(server.config);

        flags.push('-c', `unix_socket_directories=${__dirname}`);

        server.process = childprocess.spawn(server.config.bin, flags);

        server.process.stderr.on('data', dataListener);
        server.process.stderr.on('data', getDataPropagator('stderr'));
        server.process.stdout.on('data', dataListener);
        server.process.stdout.on('data', getDataPropagator('stdout'));
        server.process.on('close', () => {
          server.process = null;
          server.isRunning = false;
          server.isClosing = false;

          process.removeListener('exit', exitListener);
          server.emit('close');
        });
        process.on('exit', exitListener);
      });
    });

    return server.openPromise;
  }

  /**
   * Stop a given {@linkcode server}.
   * @protected
   * @fires Postgres#closing
   * @argument {Postgres} server
   * @return {Promise}
   */
  static close(server) {
    if (server.isClosing) {
      return server.closePromise;
    }

    server.isClosing = true;
    server.isOpening = false;
    server.closePromise = server.promiseQueue.add(() => {
      if (server.isOpening || !server.isRunning) {
        server.isClosing = false;

        return Promise.resolve(null);
      }

      return new Promise((resolve) => {
        server.emit('closing');
        server.process.once('close', () => resolve(null));
        server.process.kill();
      });
    });

    return server.closePromise;
  }

  /**
   * Construct a new {@link Postgres}.
   * @argument {(Number|String|Postgres~Config)} [configOrDataDir]
   * A number or string that is a port or an object for configuration.
   */
  constructor(configOrDataDir) {
    super();

    /**
     * Configuration options.
     * @protected
     * @type {Postgres~Config}
     */
    this.config = Postgres.parseConfig(configOrDataDir, {
      bin: 'postgres',
      conf: null,
      port: 5432,
      datadir: null
    });

    /**
     * The current process.
     * @protected
     * @type {ChildProcess}
     */
    this.process = null;

    /**
     * The last {@link Promise} returned by {@link Postgres#open}.
     * @protected
     * @type {Promise}
     */
    this.openPromise = Promise.resolve(null);

    /**
     * The last {@link Promise} returned by {@link Postgres#close}.
     * @protected
     * @type {Promise}
     */
    this.closePromise = Promise.resolve(null);

    /**
     * A serial queue of open and close promises.
     * @protected
     * @type {PromiseQueue}
     */
    this.promiseQueue = new PromiseQueue(1);

    /**
     * Determine if the instance is closing a PostgreSQL server; {@linkcode true}
     * while a process is being, or about to be, killed until the
     * contained PostgreSQL server either closes or errs.
     * @readonly
     * @type {Boolean}
     */
    this.isClosing = false;

    /**
     * Determine if the instance is starting a PostgreSQL server; {@linkcode true}
     * while a process is spawning, or about tobe spawned, until the
     * contained PostgreSQL server either starts or errs.
     * @readonly
     * @type {Boolean}
     */
    this.isRunning = false;

    /**
     * Determine if the instance is running a PostgreSQL server; {@linkcode true}
     * once a process has spawned and the contained PostgreSQL server is ready
     * to service requests.
     * @readonly
     * @type {Boolean}
     */
    this.isOpening = false;
  }

  /**
   * Open the server.
   * @argument {Postgres~callback} [callback]
   * @return {Promise}
   */
  open(callback) {
    const promise = Postgres.open(this);

    return typeof callback === 'function'
    ? promise
      .then((v) => callback(null, v))
      .catch((e) => callback(e, null))
    : promise;
  }

  /**
   * Close the server.
   * @argument {Postgres~callback} [callback]
   * @return {Promise}
   */
  close(callback) {
    const promise = Postgres.close(this);

    return typeof callback === 'function'
    ? promise
      .then((v) => callback(null, v))
      .catch((e) => callback(e, null))
    : promise;
  }
}

module.exports = exports = Postgres;
<|endoftext|>


<commit_before>var should = require('should'),
  tools = require('../../util/db-tools');

describe('DB Tools', function() {

  // TODO:
  // See about cloning the `struct` and `joins` to reduce code here.
  beforeEach(function (done) {
    struct = {
      "primary": "modelOne",
      "columns": {
        "modelOne": [
          "field1",
          "field2",
          "modelTwo_id"
        ],
        "modelTwo": [
          "id",
          "field1"
        ]
      }
    };

    struct2 = {
      "primary": "modelOne",
      "columns": {
        "modelOne": [
          "field1",
          "field2",
          "modelTwo_id"
        ],
        "modelTwo": [
          "id",
          "field1"
        ],
        "modelThree": [
          "id",
          "modelOne_id",
          "field1"
        ]
      }
    };

    joins = [{
      "relation": "modelTwo",
      "relation key": "id",
      "foreign key": "modelTwo_id"
    }];

    joins2 = [{
      "relation": "modelTwo",
      "relation key": "id",
      "foreign key": "modelTwo_id"
    },{
      "relation": "modelThree",
      "relation key": "ModelOne_id",
      "foreign key": "id"
    },];

    done();
  });

  it('should prepare a select statement without joins and without params',
      function(done) {
    var params = null;
    var expected = 'SELECT `modelOne`.`field1`, `modelOne`.`field2`, ' +
        '`modelOne`.`modelTwo_id`, `modelTwo`.`id`, `modelTwo`.`field1` ' +
        'FROM modelOne';
    var query = tools.prepareSelect(struct, params);
    query.should.equal(expected);
    done();
  });

  it('should prepare a select statement without joins and with params',
      function(done) {
    var params = { limit: 1};
    var expected = 'SELECT `modelOne`.`field1`, `modelOne`.`field2`, ' +
        '`modelOne`.`modelTwo_id`, `modelTwo`.`id`, `modelTwo`.`field1` ' +
        'FROM modelOne LIMIT 1';
    var query = tools.prepareSelect(struct, params);
    query.should.equal(expected);
    done();
  });

  it('should prepare a select statement with joins and without params',
      function(done) {

    var params = null;
    var expected = 'SELECT `modelOne`.`field1`, `modelOne`.`field2`, ' +
        '`modelOne`.`modelTwo_id`, `modelTwo`.`id`, `modelTwo`.`field1` ' +
        'FROM modelOne JOIN `modelTwo` ON `modelTwo`.`id` = ' +
        '`modelOne`.`modelTwo_id`';
    struct.joins = joins;
    var query = tools.prepareSelect(struct, params);
    query.should.equal(expected);
    done();
  });

  it('should prepare a select statement with joins and with params',
      function(done) {
    var params = { limit: 1};
    var expected = 'SELECT `modelOne`.`field1`, `modelOne`.`field2`, ' +
        '`modelOne`.`modelTwo_id`, `modelTwo`.`id`, `modelTwo`.`field1` ' +
        'FROM modelOne JOIN `modelTwo` ON `modelTwo`.`id` = ' +
        '`modelOne`.`modelTwo_id` LIMIT 1';
    struct.joins = joins;
    var query = tools.prepareSelect(struct, params);
    query.should.equal(expected);
    done();
  });

  it('should prepare a select statement with joins and with params',
      function(done) {
    var params = { limit: 1};
    var expected =  'SELECT `modelOne`.`field1`, `modelOne`.`field2`, ' +
        '`modelOne`.`modelTwo_id`, `modelTwo`.`id`, `modelTwo`.`field1`, ' +
        '`modelThree`.`id`, `modelThree`.`modelOne_id`, ' +
        '`modelThree`.`field1` FROM modelOne JOIN `modelTwo` ON ' +
        '`modelTwo`.`id` = `modelOne`.`modelTwo_id`,  ' +
        'JOIN `modelThree` ON `modelThree`.`ModelOne_id` = ' +
        '`modelOne`.`id` LIMIT 1';
    struct2.joins = joins2;
    var query = tools.prepareSelect(struct2, params);
    query.should.equal(expected);
    done();
  });

});<commit_msg>Added another test for the DB Tools.<commit_after>var should = require('should'),
  tools = require('../../util/db-tools');

describe('DB Tools', function() {
  beforeEach(function (done) {
    var modelOne = [
      "field1",
      "field2",
      "modelTwo_id"
    ];

    var modelTwo = [
      "id",
      "field1"
    ];

    var modelThree = [
      "id",
      "modelOne_id",
      "field1"
    ];

    struct_a = {
      "primary": "modelOne",
      "columns": {
        "modelOne": modelOne,
        "modelTwo": modelTwo
      }
    };

    struct_b = {
      "primary": "modelOne",
      "columns": {
        "modelOne": modelOne,
        "modelTwo": modelTwo,
        "modelThree": modelThree
      }
    };

    joins_a = [{
      "relation": "modelTwo",
      "relation key": "id",
      "foreign key": "modelTwo_id"
    }];

    joins_b = [{
      "relation": "modelTwo",
      "relation key": "id",
      "foreign key": "modelTwo_id"
    },{
      "relation": "modelThree",
      "relation key": "ModelOne_id",
      "foreign key": "id"
    },];

    done();
  });

  it('should prepare a select statement without joins and without params',
      function(done) {
    var params = null;
    var expected = 'SELECT `modelOne`.`field1`, `modelOne`.`field2`, ' +
        '`modelOne`.`modelTwo_id`, `modelTwo`.`id`, `modelTwo`.`field1` ' +
        'FROM modelOne';
    var query = tools.prepareSelect(struct_a, params);
    query.should.equal(expected);
    done();
  });

  it('should prepare a select statement without joins and with params',
      function(done) {
    var params = { limit: 1};
    var expected = 'SELECT `modelOne`.`field1`, `modelOne`.`field2`, ' +
        '`modelOne`.`modelTwo_id`, `modelTwo`.`id`, `modelTwo`.`field1` ' +
        'FROM modelOne LIMIT 1';
    var query = tools.prepareSelect(struct_a, params);
    query.should.equal(expected);
    done();
  });

  it('should prepare a select statement with joins and without params',
      function(done) {
    var params = null;
    var expected = 'SELECT `modelOne`.`field1`, `modelOne`.`field2`, ' +
        '`modelOne`.`modelTwo_id`, `modelTwo`.`id`, `modelTwo`.`field1` ' +
        'FROM modelOne JOIN `modelTwo` ON `modelTwo`.`id` = ' +
        '`modelOne`.`modelTwo_id`';
    struct_a.joins = joins_a;
    var query = tools.prepareSelect(struct_a, params);
    query.should.equal(expected);
    done();
  });

  it('should prepare a select statement with joins and with params',
      function(done) {
    var params = { limit: 1};
    var expected = 'SELECT `modelOne`.`field1`, `modelOne`.`field2`, ' +
        '`modelOne`.`modelTwo_id`, `modelTwo`.`id`, `modelTwo`.`field1` ' +
        'FROM modelOne JOIN `modelTwo` ON `modelTwo`.`id` = ' +
        '`modelOne`.`modelTwo_id` LIMIT 1';
    struct_a.joins = joins_a;
    var query = tools.prepareSelect(struct_a, params);
    query.should.equal(expected);
    done();
  });

  it('should prepare a select statement with multiple joins and without params',
      function(done) {
    var params = null;
    var expected =  'SELECT `modelOne`.`field1`, `modelOne`.`field2`, ' +
        '`modelOne`.`modelTwo_id`, `modelTwo`.`id`, `modelTwo`.`field1`, ' +
        '`modelThree`.`id`, `modelThree`.`modelOne_id`, ' +
        '`modelThree`.`field1` FROM modelOne JOIN `modelTwo` ON ' +
        '`modelTwo`.`id` = `modelOne`.`modelTwo_id`,  ' +
        'JOIN `modelThree` ON `modelThree`.`ModelOne_id` = ' +
        '`modelOne`.`id`';
    struct_b.joins = joins_b;
    var query = tools.prepareSelect(struct_b, params);
    query.should.equal(expected);
    done();
  });

  it('should prepare a select statement with multiple joins and with params',
      function(done) {
    var params = { limit: 1};
    var expected =  'SELECT `modelOne`.`field1`, `modelOne`.`field2`, ' +
        '`modelOne`.`modelTwo_id`, `modelTwo`.`id`, `modelTwo`.`field1`, ' +
        '`modelThree`.`id`, `modelThree`.`modelOne_id`, ' +
        '`modelThree`.`field1` FROM modelOne JOIN `modelTwo` ON ' +
        '`modelTwo`.`id` = `modelOne`.`modelTwo_id`,  ' +
        'JOIN `modelThree` ON `modelThree`.`ModelOne_id` = ' +
        '`modelOne`.`id` LIMIT 1';
    struct_b.joins = joins_b;
    var query = tools.prepareSelect(struct_b, params);
    query.should.equal(expected);
    done();
  });
});<|endoftext|>


<commit_before>angular.module('yds').directive('ydsHybrid', ['Data', '$http', '$stateParams', '$timeout',
	function(Data, $http, $stateParams, $timeout){
	return {
		restrict: 'E',
		scope: {},
		template: '
' +
					'
0">' +
							'' +
							'  Error: {{ydsAlert}}' +
					'
' +
				  '
',
		compile: function(tElem, tAttrs){
			return {
				pre: function(scope, element, iAttrs) {
					scope.ydsAlert = "";
					var embedCode = $stateParams.embedCode;

					var hybridContainer = angular.element(element[0].querySelector('.hybrid-container'));

					//create a random id for the element that will render the chart
					var elementId = "hybrid" + Data.createRandomId();
					hybridContainer[0].id = elementId;

					// Recover saved object from embed code and visualise it
					Data.recoverEmbedCode(embedCode)
					.then (function (response) {
						visualiseProject(response.embedding.project_id, response.embedding.type, response.embedding.view_type)
					}, function (error) {
						scope.ydsAlert = error.message;
						console.log('error during Data.recoverEmbedCode:', error);
					});

					/**
					 * Formats the server's response according to the chart type
					 * @param viewType	Chart type (pie, line, map etc.)
                     * @param response	Server's response
                     * @returns {{}}	Formatted response
                     */
					var formatResponseData = function(viewType, response) {
						var newData = {};					// Object to put formatted data in
						var responseData = response.data;	// Data from the response

						// If chart is not a map, get some data that is common for all of them
						if (viewType != "map") {
							// Get graph data
							newData.data = responseData.data;

							// Get view and find title from view
							var titleView = _.first(response.view);

							newData.title = responseData[titleView.attribute];
							if (_.isUndefined(newData.title)) {
								newData.title = Data.deepObjSearch(responseData, titleView.attribute);
							}
						}

						// Get remaining fields (depending on the view type)
						switch(viewType) {
							case "line":
							case "pie":
								// Get series name
								newData.series = responseData.series;
								break;
							case "bar":
								// Get categories
								newData.categories = responseData.categories;
								break;
							case "map":
								// Get route
								newData.route = _.first(responseData.routes).asWKT;
								break;
							default:
								console.error("Unknown chart type!");
						}

						// Return formatted data
						return newData;
					};

					//function to create a pie visualisation using data from the server
					var pieVisualisation = function (response) {
						var formattedData = formatResponseData("pie", response);

						var options = {
							chart: {
								plotBackgroundColor: null,
								plotBorderWidth: null,
								plotShadow: false,
								type: 'pie',
								renderTo: elementId
							},
							title: {
								text: formattedData.title
							},
							tooltip: {
								pointFormat: '{series.name}: {point.percentage:.1f}%'
							},
							plotOptions: {
								pie: {
									allowPointSelect: true,
									cursor: 'pointer',
									dataLabels: {
										enabled: false
									},
									showInLegend: true
								}
							},
							series: [{
								name: formattedData.series,
								colorByPoint: true,
								data: formattedData.data
							}],
							exporting: {
								enabled: false
							}
						};

						var chart = new Highcharts.Chart(options);
					};

					//function to create a bar visualisation using data from the server
					var barVisualisation = function (response) {
						var formattedData = formatResponseData("bar", response);

						var options = {
							chart: {
								type: 'column',
								renderTo: elementId
							},
							title: {
								text: formattedData.title
							},
							xAxis: {
								categories: formattedData.categories,
								crosshair: true
							},
							tooltip: {
								headerFormat: '{point.key}
',
								pointFormat: '
{series.name}: 	' +
								'{point.y:.1f}
',
								footerFormat: '
',
								shared: true,
								useHTML: true
							},
							plotOptions: {
								column: {
									pointPadding: 0.2,
									borderWidth: 0
								}
							},
							series: formattedData.data,
							exporting: {
								enabled: false
							}
						};

						var chart = new Highcharts.Chart(options);
					};

					//function to create a line visualisation using data from the server
					var lineVisualisation = function (response) {
						var formattedData = formatResponseData("line", response);

						var options = {
							chart: {
								renderTo: elementId
							},
							rangeSelector: {
								selected: 1
							},

							title: {
								text: formattedData.title
							},
							series: [{
								name: formattedData.series,
								data: formattedData.data,
								tooltip: {
									valueDecimals: 2
								}
							}],
							exporting: {
								enabled: false
							}
						};

						var chart = new Highcharts.StockChart(options);
					};

					//function to create a map visualisation using data from the server
					var mapVisualisation = function (response) {
						var formattedData = formatResponseData("map", response);

						var map = L.map(elementId, {
							center: [35.52, 23.80],
							zoom: 5
						});

						L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
							maxZoom: 18,
							attribution: 'Map data © OpenStreetMap contributors',
						}).addTo(map);

						var polyline = L.polyline([]).addTo(map);
						var route = angular.copy(formattedData.route);
						for (var i=0; i<route.length; i++){
							polyline.addLatLng([parseFloat(route[i].lng),parseFloat(route[i].lat)]);
						}

						// zoom the map to the polyline
						map.fitBounds(polyline.getBounds());
					};

					//function responsible for the visualisation of a project
					var visualiseProject = function (projectId, vizType, viewType) {
						vizType = vizType.toLowerCase();

						if (vizType == "pie") {
							Data.getProjectVis(vizType, projectId, viewType, "en")
							.then(function (response) {
								pieVisualisation(response);
							}, function (error) {
								console.log('error', error);
							});
						} else if (vizType == "bar") {
							Data.getProjectVis(vizType, projectId, viewType, "en")
							.then(function (response) {
								barVisualisation(response);
							}, function (error) {
								console.log('error', error);
							});
						} else if (vizType == "line") {
							Data.getProjectVis(vizType, projectId, viewType, "en")
							.then(function (response) {
								lineVisualisation(response);
							}, function (error) {
								console.log('error', error);
							});
						} else if (vizType == "map") {
							Data.getProjectVis(vizType, projectId, "default", "en")
							.then(function (response) {
								mapVisualisation(response);
							}, function (error) {
								console.log('error', error);
							});
						}
					};
				}
			}
		}
	}
}]);<commit_msg>Hybrid component uses language returned from Backend Utils<commit_after>angular.module('yds').directive('ydsHybrid', ['Data', '$http', '$stateParams', '$timeout',
	function(Data, $http, $stateParams, $timeout){
	return {
		restrict: 'E',
		scope: {},
		template: '
' +
					'
0">' +
							'' +
							'  Error: {{ydsAlert}}' +
					'
' +
				  '
',
		compile: function(tElem, tAttrs){
			return {
				pre: function(scope, element, iAttrs) {
					scope.ydsAlert = "";
					var embedCode = $stateParams.embedCode;

					var hybridContainer = angular.element(element[0].querySelector('.hybrid-container'));

					//create a random id for the element that will render the chart
					var elementId = "hybrid" + Data.createRandomId();
					hybridContainer[0].id = elementId;

					// Recover saved object from embed code and visualise it
					Data.recoverEmbedCode(embedCode)
					.then (function (response) {
						console.log("LANG!!!       " + response.embedding.lang);
						visualiseProject(response.embedding.project_id, response.embedding.type,
							response.embedding.view_type, response.embedding.lang);
					}, function (error) {
						scope.ydsAlert = error.message;
						console.log('error during Data.recoverEmbedCode:', error);
					});

					/**
					 * Formats the server's response according to the chart type
					 * @param viewType	Chart type (pie, line, map etc.)
                     * @param response	Server's response
                     * @returns {{}}	Formatted response
                     */
					var formatResponseData = function(viewType, response) {
						var newData = {};					// Object to put formatted data in
						var responseData = response.data;	// Data from the response

						// If chart is not a map, get some data that is common for all of them
						if (viewType != "map") {
							// Get graph data
							newData.data = responseData.data;

							// Get view and find title from view
							var titleView = _.first(response.view);

							newData.title = responseData[titleView.attribute];
							if (_.isUndefined(newData.title)) {
								newData.title = Data.deepObjSearch(responseData, titleView.attribute);
							}
						}

						// Get remaining fields (depending on the view type)
						switch(viewType) {
							case "line":
							case "pie":
								// Get series name
								newData.series = responseData.series;
								break;
							case "bar":
								// Get categories
								newData.categories = responseData.categories;
								break;
							case "map":
								// Get route
								newData.route = _.first(responseData.routes).asWKT;
								break;
							default:
								console.error("Unknown chart type!");
						}

						// Return formatted data
						return newData;
					};

					//function to create a pie visualisation using data from the server
					var pieVisualisation = function (response) {
						var formattedData = formatResponseData("pie", response);

						var options = {
							chart: {
								plotBackgroundColor: null,
								plotBorderWidth: null,
								plotShadow: false,
								type: 'pie',
								renderTo: elementId
							},
							title: {
								text: formattedData.title
							},
							tooltip: {
								pointFormat: '{series.name}: {point.percentage:.1f}%'
							},
							plotOptions: {
								pie: {
									allowPointSelect: true,
									cursor: 'pointer',
									dataLabels: {
										enabled: false
									},
									showInLegend: true
								}
							},
							series: [{
								name: formattedData.series,
								colorByPoint: true,
								data: formattedData.data
							}],
							exporting: {
								enabled: false
							}
						};

						var chart = new Highcharts.Chart(options);
					};

					//function to create a bar visualisation using data from the server
					var barVisualisation = function (response) {
						var formattedData = formatResponseData("bar", response);

						var options = {
							chart: {
								type: 'column',
								renderTo: elementId
							},
							title: {
								text: formattedData.title
							},
							xAxis: {
								categories: formattedData.categories,
								crosshair: true
							},
							tooltip: {
								headerFormat: '{point.key}
',
								pointFormat: '
{series.name}: 	' +
								'{point.y:.1f}
',
								footerFormat: '
',
								shared: true,
								useHTML: true
							},
							plotOptions: {
								column: {
									pointPadding: 0.2,
									borderWidth: 0
								}
							},
							series: formattedData.data,
							exporting: {
								enabled: false
							}
						};

						var chart = new Highcharts.Chart(options);
					};

					//function to create a line visualisation using data from the server
					var lineVisualisation = function (response) {
						var formattedData = formatResponseData("line", response);

						var options = {
							chart: {
								renderTo: elementId
							},
							rangeSelector: {
								selected: 1
							},

							title: {
								text: formattedData.title
							},
							series: [{
								name: formattedData.series,
								data: formattedData.data,
								tooltip: {
									valueDecimals: 2
								}
							}],
							exporting: {
								enabled: false
							}
						};

						var chart = new Highcharts.StockChart(options);
					};

					//function to create a map visualisation using data from the server
					var mapVisualisation = function (response) {
						var formattedData = formatResponseData("map", response);

						var map = L.map(elementId, {
							center: [35.52, 23.80],
							zoom: 5
						});

						L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
							maxZoom: 18,
							attribution: 'Map data © OpenStreetMap contributors',
						}).addTo(map);

						var polyline = L.polyline([]).addTo(map);
						var route = angular.copy(formattedData.route);
						for (var i=0; i<route.length; i++){
							polyline.addLatLng([parseFloat(route[i].lng),parseFloat(route[i].lat)]);
						}

						// zoom the map to the polyline
						map.fitBounds(polyline.getBounds());
					};

					//function responsible for the visualisation of a project
					var visualiseProject = function (projectId, vizType, viewType, lang) {
						vizType = vizType.toLowerCase();

						if (vizType == "pie") {
							Data.getProjectVis(vizType, projectId, viewType, lang)
							.then(function (response) {
								pieVisualisation(response);
							}, function (error) {
								console.log('error', error);
							});
						} else if (vizType == "bar") {
							Data.getProjectVis(vizType, projectId, viewType, lang)
							.then(function (response) {
								barVisualisation(response);
							}, function (error) {
								console.log('error', error);
							});
						} else if (vizType == "line") {
							Data.getProjectVis(vizType, projectId, viewType, lang)
							.then(function (response) {
								lineVisualisation(response);
							}, function (error) {
								console.log('error', error);
							});
						} else if (vizType == "map") {
							Data.getProjectVis(vizType, projectId, "default", lang)
							.then(function (response) {
								mapVisualisation(response);
							}, function (error) {
								console.log('error', error);
							});
						}
					};
				}
			}
		}
	}
}]);<|endoftext|>


<commit_before>import * as React from 'react';
import { withRouter } from 'react-router';
import RoutingConstants from 'config/routing-constants';
import AuthApi from './api';
import storage from 'helpers/storage';
import hasAdminAccess from './has-admin-access';

/**
 * A Higher-Order Component for fetching data about the current user.
 *
 * This component passes the following props to the wrapped component:
 * - **me**: an object containing the current user, or null if the user is not
 *    signed in (defaults to null).
 *
 * @param {Component} WrappedComponent  The component that should only be
 *  accessible to authenticated admin users.
 */
const Auth = (WrappedComponent) => withRouter(React.createClass({
  displayName: 'Auth',

  propTypes: {
    location: React.PropTypes.shape({
      pathname: React.PropTypes.string.isRequired,
      query: React.PropTypes.shape({
        iristoken: React.PropTypes.string,
      }).isRequired,
    }).isRequired,
    router: React.PropTypes.shape({
      push: React.PropTypes.func.isRequired,
      replace: React.PropTypes.func.isRequired,
    }).isRequired,
  },

  getInitialState: () => ({
    me: null,
    finishedLoading: false,
  }),

  componentWillMount() {
    if (this.props.location.pathname !== '/petitions') {
      storage.setItem('redirect-url', JSON.stringify(this.props.location));
    }
    AuthApi.whoami().then(response => {
      let state = {
        me: null,
        finishedLoading: true,
      };
      if (response.status === 'ok') {
        state = {
          me: response.data,
          finishedLoading: true,
        };
      }
      this.setState(state);
    }).catch(() => {
      this.setState({
        me: null,
        finishedLoading: true,
      });
    });
  },

  showLoginPage() {
    setTimeout(() => {
      this.props.router.push(RoutingConstants.LOGIN);
    }, 6);
  },

  render() {
    if (this.state.finishedLoading) {
      if (hasAdminAccess(this.state.me)) {
        return (
          <WrappedComponent {...this.props} me={this.state.me} />
        );
      }
      this.showLoginPage();
    }
    return <div />;
  }
}));

export default Auth;
<commit_msg>bug fix for appendix/cover pages<commit_after>import * as React from 'react';
import { withRouter } from 'react-router';
import RoutingConstants from 'config/routing-constants';
import AuthApi from './api';
import storage from 'helpers/storage';
import hasAdminAccess from './has-admin-access';

/**
 * A Higher-Order Component for fetching data about the current user.
 *
 * This component passes the following props to the wrapped component:
 * - **me**: an object containing the current user, or null if the user is not
 *    signed in (defaults to null).
 *
 * @param {Component} WrappedComponent  The component that should only be
 *  accessible to authenticated admin users.
 */
const Auth = (WrappedComponent) => withRouter(React.createClass({
  displayName: 'Auth',

  propTypes: {
    location: React.PropTypes.shape({
      pathname: React.PropTypes.string.isRequired,
      query: React.PropTypes.shape({
        iristoken: React.PropTypes.string,
      }).isRequired,
    }).isRequired,
    router: React.PropTypes.shape({
      push: React.PropTypes.func.isRequired,
      replace: React.PropTypes.func.isRequired,
    }).isRequired,
  },

  getInitialState: () => ({
    me: null,
    finishedLoading: false,
  }),

  componentWillMount() {
    const regExp = new RegExp(/petition\/\w*\/letter\/\w*/);
    if (this.props.location.pathname !== '/petitions' && !regExp.test(this.props.location.pathname)) {
      storage.setItem('redirect-url', JSON.stringify(this.props.location));
    }
    AuthApi.whoami().then(response => {
      let state = {
        me: null,
        finishedLoading: true,
      };
      if (response.status === 'ok') {
        state = {
          me: response.data,
          finishedLoading: true,
        };
      }
      this.setState(state);
    }).catch(() => {
      this.setState({
        me: null,
        finishedLoading: true,
      });
    });
  },

  showLoginPage() {
    setTimeout(() => {
      this.props.router.push(RoutingConstants.LOGIN);
    }, 6);
  },

  render() {
    if (this.state.finishedLoading) {
      if (hasAdminAccess(this.state.me)) {
        return (
          <WrappedComponent {...this.props} me={this.state.me} />
        );
      }
      this.showLoginPage();
    }
    return <div />;
  }
}));

export default Auth;
<|endoftext|>


<commit_before>pc.extend(pc, function () {
    /**
     * @name pc.PostEffectQueue
     * @description Create a new PostEffectQueue
     * @class Used to manage multiple post effects for a camera
     * @param {pc.Application} app The application
     * @param {pc.CameraComponent} camera The camera component
     */
    function PostEffectQueue(app, camera) {
        var self = this;

        this.app = app;
        this.camera = camera;
        // stores all of the post effects
        this.effects = [];
        // if the queue is enabled it will render all of its effects
        // otherwise it will not render anything
        this.enabled = false;

        // legacy
        this.depthTarget = null;

        this.renderTargetScale = 1;
        this.resizeTimeout = null;
        this.resizeLast = 0;

        this._resizeTimeoutCallback = function() {
            self.resizeRenderTargets();
        };

        camera.on('set_rect', this.onCameraRectChanged, this);
    }

    PostEffectQueue.prototype = {
        /**
         * @private
         * @function
         * @name pc.PostEffectQueue#_createOffscreenTarget
         * @description Creates a render target with the dimensions of the canvas, with an optional depth buffer
         * @param {Boolean} useDepth Set to true if you want to create a render target with a depth buffer
         * @param {Boolean} hdr Use HDR render target format
         * @returns {pc.RenderTarget} The render target
         */

        _createOffscreenTarget: function (useDepth, hdr) {
            var rect = this.camera.rect;

            var width = Math.floor(rect.z * this.app.graphicsDevice.width * this.renderTargetScale);
            var height = Math.floor(rect.w * this.app.graphicsDevice.height * this.renderTargetScale);

            var device = this.app.graphicsDevice;
            var format = hdr? device.getHdrFormat() : pc.PIXELFORMAT_R8_G8_B8_A8;

            var colorBuffer = new pc.Texture(device, {
                format: format,
                width: width,
                height: height
            });

            colorBuffer.minFilter = pc.FILTER_NEAREST;
            colorBuffer.magFilter = pc.FILTER_NEAREST;
            colorBuffer.addressU = pc.ADDRESS_CLAMP_TO_EDGE;
            colorBuffer.addressV = pc.ADDRESS_CLAMP_TO_EDGE;

            return new pc.RenderTarget(this.app.graphicsDevice, colorBuffer, { depth: useDepth });
        },

        _resizeOffscreenTarget: function (rt) {
            var rect = this.camera.rect;

            var width = Math.floor(rect.z * this.app.graphicsDevice.width * this.renderTargetScale);
            var height = Math.floor(rect.w * this.app.graphicsDevice.height * this.renderTargetScale);

            var device = this.app.graphicsDevice;
            var format = rt.colorBuffer.format;

            rt._colorBuffer.destroy();

            var colorBuffer = new pc.Texture(device, {
                format: format,
                width: width,
                height: height
            });

            colorBuffer.minFilter = pc.FILTER_NEAREST;
            colorBuffer.magFilter = pc.FILTER_NEAREST;
            colorBuffer.addressU = pc.ADDRESS_CLAMP_TO_EDGE;
            colorBuffer.addressV = pc.ADDRESS_CLAMP_TO_EDGE;

            rt._colorBuffer = colorBuffer;
            rt.destroy();
        },

        setRenderTargetScale: function (scale) {
            this.renderTargetScale = scale;
            this.resizeRenderTargets();
        },

        /**
         * @function
         * @name pc.PostEffectQueue#addEffect
         * @description Adds a post effect to the queue. If the queue is disabled adding a post effect will
         * automatically enable the queue.
         * @param {pc.PostEffect} effect The post effect to add to the queue.
         */
        addEffect: function (effect) {
            // first rendering of the scene requires depth buffer
            var isFirstEffect = this.effects.length === 0;

            var effects = this.effects;
            var newEntry = {
                effect: effect,
                inputTarget: this._createOffscreenTarget(isFirstEffect, effect.hdr),
                outputTarget: null
            };

            // #ifdef DEBUG
            console.warn("PostEffectQueue is deprecated. Use new post effects.");
            // #endif

            // legacy compatibility: create new layer
            if (!this.layer) {
                this.layer = new pc.Layer({
                    opaqueSortMode: pc.SORTMODE_NONE,
                    transparentSortMode: pc.SORTMODE_NONE,
                    passThrough: true,
                    name: "PostEffectQueue",
                    renderTarget: this.camera.renderTarget,
                    clear: false,
                    onPostRender: function() {
                        for(var i=0; i<this._commandList.length; i++) {
                            this._commandList[i].command();
                        }
                    }
                });
                // insert it after the last occurence of this camera
                var layerList = this.app.scene.layers.layerList;
                var order = 0;
                for(var i=layerList.length-1; i>=0; i--) {
                    if (layerList[i].cameras.indexOf(this.camera) >= 0) {
                        if (order === 0) {
                            order = i + 1;
                        } else {
                            layerList[i].renderTarget = newEntry.inputTarget;
                        }
                    }
                }
                this.app.scene.layers.insertOpaque(this.layer, order);
                this.layer._commandList = [];
                this.layer.isPostEffect = true;
            }

            effects.push(newEntry);

            var len = effects.length;
            if (len > 1) {
                // connect the effect with the previous effect if one exists
                effects[len - 2].outputTarget = newEntry.inputTarget;
            }

            this.enable();
        },

        /**
         * @function
         * @name pc.PostEffectQueue#removeEffect
         * @description Removes a post effect from the queue. If the queue becomes empty it will be disabled automatically.
         * @param {pc.PostEffect} effect The post effect to remove.
         */
        removeEffect: function (effect) {
            // find index of effect
            var index = -1;
            for (var i=0,len=this.effects.length; i<len; i++) {
                if (this.effects[i].effect === effect) {
                    index = i;
                    break;
                }
            }

            if (index >= 0) {
                if (index > 0)  {
                    // connect the previous effect with the effect after the one we're about to remove
                    this.effects[index-1].outputTarget = (index + 1) < this.effects.length ?
                                                         this.effects[index+1].inputTarget :
                                                         null;
                } else {
                    if (this.effects.length > 1) {
                        // if we removed the first effect then make sure that
                        // the input render target of the effect that will now become the first one
                        // has a depth buffer
                        if (!this.effects[1].inputTarget._depth) {
                            this.effects[1].inputTarget.destroy();
                            this.effects[1].inputTarget = this._createOffscreenTarget(true, this.effects[1].hdr);
                        }

                        this.camera.renderTarget = this.effects[1].inputTarget;
                    }
                }

                // release memory for removed effect
                this.effects[index].inputTarget.destroy();

                this.effects.splice(index, 1);
            }

            if (this.enabled) {
                if (effect.needsDepthBuffer) {
                    this.camera.releaseDepthMap();
                }
            }

            if (this.effects.length === 0) {
                this.disable();
            }
        },

        requestDepthMap: function () {
            for (var i=0,len=this.effects.length; i<len; i++) {
                var effect = this.effects[i].effect;
                if (effect.needsDepthBuffer) {
                    this.camera.camera.requestDepthMap();
                }
            }
        },

        releaseDepthMap: function () {
            for (var i=0,len=this.effects.length; i<len; i++) {
                var effect = this.effects[i].effect;
                if (effect.needsDepthBuffer) {
                    this.camera.releaseDepthMap();
                }
            }
        },

        /**
         * @function
         * @name pc.PostEffectQueue#destroy
         * @description Removes all the effects from the queue and disables it
         */
        destroy: function () {
            // release memory for all effects
            for (var i=0,len=this.effects.length; i<len; i++) {
                this.effects[i].inputTarget.destroy();
            }

            this.effects.length = 0;

            this.disable();
        },

        /**
         * @function
         * @name pc.PostEffectQueue#enable
         * @description Enables the queue and all of its effects. If there are no effects then the queue will not be enabled.
         */
        enable: function () {
            if (!this.enabled && this.effects.length) {
                this.enabled = true;

                var self = this;
                this.requestDepthMap();

                this.app.graphicsDevice.on('resizecanvas', this._onCanvasResized, this);

                // set the camera's rect to full screen. Set it directly to the
                // camera node instead of the component because we want to keep the old
                // rect set in the component for restoring the camera to its original settings
                // when the queue is disabled.
                //self.camera.camera.setRect(0, 0, 1, 1);

                // create a new command that renders all of the effects one after the other
                this.command = new pc.Command(pc.LAYER_FX, pc.BLEND_NONE, function () {
                    if (self.enabled) {
                        var rect = null;
                        var len = self.effects.length;
                        if (len) {
                            self.layer.renderTarget = self.effects[0].inputTarget;
                            //self.depthTarget = self.camera.camera._depthTarget;

                            for (var i=0; i<len; i++) {
                                var fx = self.effects[i];
                                //if (self.depthTarget) fx.effect.depthMap = self.depthTarget.colorBuffer;
                                if (i === len - 1) {
                                    rect = self.camera.rect;
                                }

                                fx.effect.render(fx.inputTarget, fx.outputTarget, rect);
                            }
                        }
                    }
                });

                this.layer._commandList.push(this.command);
            }
        },

        /**
         * @function
         * @name pc.PostEffectQueue#disable
         * @description Disables the queue and all of its effects.
         */
        disable: function () {
            if (this.enabled) {
                this.enabled = false;

                this.app.graphicsDevice.off('resizecanvas', this._onCanvasResized, this);

                this.camera.renderTarget = null;
                this.releaseDepthMap();

                var rect = this.camera.rect;
                //this.camera.camera.setRect(rect.x, rect.y, rect.z, rect.w);

                // remove the draw command
                var i = this.layer._commandList.indexOf(this.command);
                if (i >= 0) {
                    this.layer._commandList.splice(i, 1);
                }
            }
        },

        _onCanvasResized: function (width, height) {
            var rect = this.camera.rect;
            var device = this.app.graphicsDevice;
            this.camera.camera.aspectRatio = (device.width * rect.z) / (device.height * rect.w);

            // avoid resizing the render targets too often by using a timeout
            if (this.resizeTimeout)
                return;

            if ((pc.now() - this.resizeLast) > 100) {
                // allow resizing immediately if haven't been resized recently
                this.resizeRenderTargets();
            } else {
                // target to maximum at 10 resizes a second
                this.resizeTimeout = setTimeout(this._resizeTimeoutCallback, 100);
            }
        },

        resizeRenderTargets: function () {
            if (this.resizeTimeout) {
                clearTimeout(this.resizeTimeout);
                this.resizeTimeout = null;
            }

            this.resizeLast = pc.now();

            var rect = this.camera.rect;
            var desiredWidth = Math.floor(rect.z * this.app.graphicsDevice.width * this.renderTargetScale);
            var desiredHeight = Math.floor(rect.w * this.app.graphicsDevice.height * this.renderTargetScale);

            var effects = this.effects;

            for (var i=0,len=effects.length; i<len; i++) {
                var fx = effects[i];
                if (fx.inputTarget.width !== desiredWidth ||
                    fx.inputTarget.height !== desiredHeight)  {
                    this._resizeOffscreenTarget(fx.inputTarget);
                }
            }
        },

        onCameraRectChanged: function (name, oldValue, newValue) {
            if (this.enabled) {
                // reset the camera node's rect to full screen otherwise
                // post effect will not work correctly
                //this.camera.camera.setRect(0, 0, 1, 1);
                this.resizeRenderTargets();
            }
        }
    };

    return {
        PostEffectQueue: PostEffectQueue
    };
}());
<commit_msg>remove legacy pc.Command from the post effect queue<commit_after>pc.extend(pc, function () {
    /**
     * @name pc.PostEffectQueue
     * @description Create a new PostEffectQueue
     * @class Used to manage multiple post effects for a camera
     * @param {pc.Application} app The application
     * @param {pc.CameraComponent} camera The camera component
     */
    function PostEffectQueue(app, camera) {
        var self = this;

        this.app = app;
        this.camera = camera;
        // stores all of the post effects
        this.effects = [];
        // if the queue is enabled it will render all of its effects
        // otherwise it will not render anything
        this.enabled = false;

        // legacy
        this.depthTarget = null;

        this.renderTargetScale = 1;
        this.resizeTimeout = null;
        this.resizeLast = 0;

        this._resizeTimeoutCallback = function() {
            self.resizeRenderTargets();
        };

        camera.on('set_rect', this.onCameraRectChanged, this);
    }

    PostEffectQueue.prototype = {
        /**
         * @private
         * @function
         * @name pc.PostEffectQueue#_createOffscreenTarget
         * @description Creates a render target with the dimensions of the canvas, with an optional depth buffer
         * @param {Boolean} useDepth Set to true if you want to create a render target with a depth buffer
         * @param {Boolean} hdr Use HDR render target format
         * @returns {pc.RenderTarget} The render target
         */

        _createOffscreenTarget: function (useDepth, hdr) {
            var rect = this.camera.rect;

            var width = Math.floor(rect.z * this.app.graphicsDevice.width * this.renderTargetScale);
            var height = Math.floor(rect.w * this.app.graphicsDevice.height * this.renderTargetScale);

            var device = this.app.graphicsDevice;
            var format = hdr? device.getHdrFormat() : pc.PIXELFORMAT_R8_G8_B8_A8;

            var colorBuffer = new pc.Texture(device, {
                format: format,
                width: width,
                height: height
            });

            colorBuffer.minFilter = pc.FILTER_NEAREST;
            colorBuffer.magFilter = pc.FILTER_NEAREST;
            colorBuffer.addressU = pc.ADDRESS_CLAMP_TO_EDGE;
            colorBuffer.addressV = pc.ADDRESS_CLAMP_TO_EDGE;

            return new pc.RenderTarget(this.app.graphicsDevice, colorBuffer, { depth: useDepth });
        },

        _resizeOffscreenTarget: function (rt) {
            var rect = this.camera.rect;

            var width = Math.floor(rect.z * this.app.graphicsDevice.width * this.renderTargetScale);
            var height = Math.floor(rect.w * this.app.graphicsDevice.height * this.renderTargetScale);

            var device = this.app.graphicsDevice;
            var format = rt.colorBuffer.format;

            rt._colorBuffer.destroy();

            var colorBuffer = new pc.Texture(device, {
                format: format,
                width: width,
                height: height
            });

            colorBuffer.minFilter = pc.FILTER_NEAREST;
            colorBuffer.magFilter = pc.FILTER_NEAREST;
            colorBuffer.addressU = pc.ADDRESS_CLAMP_TO_EDGE;
            colorBuffer.addressV = pc.ADDRESS_CLAMP_TO_EDGE;

            rt._colorBuffer = colorBuffer;
            rt.destroy();
        },

        setRenderTargetScale: function (scale) {
            this.renderTargetScale = scale;
            this.resizeRenderTargets();
        },

        /**
         * @function
         * @name pc.PostEffectQueue#addEffect
         * @description Adds a post effect to the queue. If the queue is disabled adding a post effect will
         * automatically enable the queue.
         * @param {pc.PostEffect} effect The post effect to add to the queue.
         */
        addEffect: function (effect) {
            // first rendering of the scene requires depth buffer
            var isFirstEffect = this.effects.length === 0;

            var effects = this.effects;
            var newEntry = {
                effect: effect,
                inputTarget: this._createOffscreenTarget(isFirstEffect, effect.hdr),
                outputTarget: null
            };

            // #ifdef DEBUG
            console.warn("PostEffectQueue is deprecated. Use new post effects.");
            // #endif

            // legacy compatibility: create new layer
            if (!this.layer) {
                this.layer = new pc.Layer({
                    opaqueSortMode: pc.SORTMODE_NONE,
                    transparentSortMode: pc.SORTMODE_NONE,
                    passThrough: true,
                    name: "PostEffectQueue",
                    renderTarget: this.camera.renderTarget,
                    clear: false,
                    onPostRender: function() {
                        for(var i=0; i<this._commandList.length; i++) {
                            this._commandList[i]();
                        }
                    }
                });
                // insert it after the last occurence of this camera
                var layerList = this.app.scene.layers.layerList;
                var order = 0;
                for(var i=layerList.length-1; i>=0; i--) {
                    if (layerList[i].cameras.indexOf(this.camera) >= 0) {
                        if (order === 0) {
                            order = i + 1;
                        } else {
                            layerList[i].renderTarget = newEntry.inputTarget;
                        }
                    }
                }
                this.app.scene.layers.insertOpaque(this.layer, order);
                this.layer._commandList = [];
                this.layer.isPostEffect = true;
            }

            effects.push(newEntry);

            var len = effects.length;
            if (len > 1) {
                // connect the effect with the previous effect if one exists
                effects[len - 2].outputTarget = newEntry.inputTarget;
            }

            this.enable();
        },

        /**
         * @function
         * @name pc.PostEffectQueue#removeEffect
         * @description Removes a post effect from the queue. If the queue becomes empty it will be disabled automatically.
         * @param {pc.PostEffect} effect The post effect to remove.
         */
        removeEffect: function (effect) {
            // find index of effect
            var index = -1;
            for (var i=0,len=this.effects.length; i<len; i++) {
                if (this.effects[i].effect === effect) {
                    index = i;
                    break;
                }
            }

            if (index >= 0) {
                if (index > 0)  {
                    // connect the previous effect with the effect after the one we're about to remove
                    this.effects[index-1].outputTarget = (index + 1) < this.effects.length ?
                                                         this.effects[index+1].inputTarget :
                                                         null;
                } else {
                    if (this.effects.length > 1) {
                        // if we removed the first effect then make sure that
                        // the input render target of the effect that will now become the first one
                        // has a depth buffer
                        if (!this.effects[1].inputTarget._depth) {
                            this.effects[1].inputTarget.destroy();
                            this.effects[1].inputTarget = this._createOffscreenTarget(true, this.effects[1].hdr);
                        }

                        this.camera.renderTarget = this.effects[1].inputTarget;
                    }
                }

                // release memory for removed effect
                this.effects[index].inputTarget.destroy();

                this.effects.splice(index, 1);
            }

            if (this.enabled) {
                if (effect.needsDepthBuffer) {
                    this.camera.releaseDepthMap();
                }
            }

            if (this.effects.length === 0) {
                this.disable();
            }
        },

        requestDepthMap: function () {
            for (var i=0,len=this.effects.length; i<len; i++) {
                var effect = this.effects[i].effect;
                if (effect.needsDepthBuffer) {
                    this.camera.camera.requestDepthMap();
                }
            }
        },

        releaseDepthMap: function () {
            for (var i=0,len=this.effects.length; i<len; i++) {
                var effect = this.effects[i].effect;
                if (effect.needsDepthBuffer) {
                    this.camera.releaseDepthMap();
                }
            }
        },

        /**
         * @function
         * @name pc.PostEffectQueue#destroy
         * @description Removes all the effects from the queue and disables it
         */
        destroy: function () {
            // release memory for all effects
            for (var i=0,len=this.effects.length; i<len; i++) {
                this.effects[i].inputTarget.destroy();
            }

            this.effects.length = 0;

            this.disable();
        },

        /**
         * @function
         * @name pc.PostEffectQueue#enable
         * @description Enables the queue and all of its effects. If there are no effects then the queue will not be enabled.
         */
        enable: function () {
            if (!this.enabled && this.effects.length) {
                this.enabled = true;

                var self = this;
                this.requestDepthMap();

                this.app.graphicsDevice.on('resizecanvas', this._onCanvasResized, this);

                // set the camera's rect to full screen. Set it directly to the
                // camera node instead of the component because we want to keep the old
                // rect set in the component for restoring the camera to its original settings
                // when the queue is disabled.
                //self.camera.camera.setRect(0, 0, 1, 1);

                // create a new command that renders all of the effects one after the other
                this.command = function () {
                    if (self.enabled) {
                        var rect = null;
                        var len = self.effects.length;
                        if (len) {
                            self.layer.renderTarget = self.effects[0].inputTarget;
                            //self.depthTarget = self.camera.camera._depthTarget;

                            for (var i=0; i<len; i++) {
                                var fx = self.effects[i];
                                //if (self.depthTarget) fx.effect.depthMap = self.depthTarget.colorBuffer;
                                if (i === len - 1) {
                                    rect = self.camera.rect;
                                }

                                fx.effect.render(fx.inputTarget, fx.outputTarget, rect);
                            }
                        }
                    }
                };

                this.layer._commandList.push(this.command);
            }
        },

        /**
         * @function
         * @name pc.PostEffectQueue#disable
         * @description Disables the queue and all of its effects.
         */
        disable: function () {
            if (this.enabled) {
                this.enabled = false;

                this.app.graphicsDevice.off('resizecanvas', this._onCanvasResized, this);

                this.camera.renderTarget = null;
                this.releaseDepthMap();

                var rect = this.camera.rect;
                //this.camera.camera.setRect(rect.x, rect.y, rect.z, rect.w);

                // remove the draw command
                var i = this.layer._commandList.indexOf(this.command);
                if (i >= 0) {
                    this.layer._commandList.splice(i, 1);
                }
            }
        },

        _onCanvasResized: function (width, height) {
            var rect = this.camera.rect;
            var device = this.app.graphicsDevice;
            this.camera.camera.aspectRatio = (device.width * rect.z) / (device.height * rect.w);

            // avoid resizing the render targets too often by using a timeout
            if (this.resizeTimeout)
                return;

            if ((pc.now() - this.resizeLast) > 100) {
                // allow resizing immediately if haven't been resized recently
                this.resizeRenderTargets();
            } else {
                // target to maximum at 10 resizes a second
                this.resizeTimeout = setTimeout(this._resizeTimeoutCallback, 100);
            }
        },

        resizeRenderTargets: function () {
            if (this.resizeTimeout) {
                clearTimeout(this.resizeTimeout);
                this.resizeTimeout = null;
            }

            this.resizeLast = pc.now();

            var rect = this.camera.rect;
            var desiredWidth = Math.floor(rect.z * this.app.graphicsDevice.width * this.renderTargetScale);
            var desiredHeight = Math.floor(rect.w * this.app.graphicsDevice.height * this.renderTargetScale);

            var effects = this.effects;

            for (var i=0,len=effects.length; i<len; i++) {
                var fx = effects[i];
                if (fx.inputTarget.width !== desiredWidth ||
                    fx.inputTarget.height !== desiredHeight)  {
                    this._resizeOffscreenTarget(fx.inputTarget);
                }
            }
        },

        onCameraRectChanged: function (name, oldValue, newValue) {
            if (this.enabled) {
                // reset the camera node's rect to full screen otherwise
                // post effect will not work correctly
                //this.camera.camera.setRect(0, 0, 1, 1);
                this.resizeRenderTargets();
            }
        }
    };

    return {
        PostEffectQueue: PostEffectQueue
    };
}());
<|endoftext|>


<commit_before>//amaze
$(document).ready(function () {

    $(document).on("click", ".show-setter", function (e) {
        var $property = $(this).closest("[data-node-property]");
        var $setter = $property.find("> [data-node-setter]");
        var $getter = $property.find("> [data-node-getter]");
        $getter.fadeOut(function () {
            $setter.fadeIn(function () {
                $setter.find('input,textarea,select').filter(':enabled:visible:not([readonly="readonly"]):not([type="hidden"])').first().focus();

                $.validator.unobtrusive.parseDynamicContent($setter);
            });
        });
        e.preventDefault();
        return false;
    });
    $(document).on("click", ".hide-setter", function (e) {
        var $property = $(this).closest("[data-node-property]");
        var $setter = $property.find("> [data-node-setter]");
        var $getter = $property.find("> [data-node-getter]");
        $setter.fadeOut(function () {
            $getter.fadeIn();
        });

        e.preventDefault();
        return false;
    });

    $(".popover .close").live('click', function (e) { $(this).closest(".popover").hide(); });

    $(".nodeMethodsMenuLink").live('click', function (e) {
        var $link = $(this);
        if (e.target != this) return false;
        var methodsPanelId = "methods-" + $link.attr("data-nodeid");
        if ($("#" + methodsPanelId).length == false) {
            ensureLazyElement(
                methodsPanelId,
                $link.attr("href"),
                function () { showMethodsMenu($link); }
            );
        }
        return false;
    });

    function modalHtml() {
        return '

×

';
    }

    var showMethodsMenu = function ($link) {
        var methodsNodeId = "methods-" + $link.attr("data-nodeid");
        $link.attr("data-content", $("#" + methodsNodeId).html());
        $link.popover({
            trigger: "manual",
            placement: ($link.offset().left > ($(window).width() / 2)) ? "left" : "right",
            offset: 0,
            html: true,
            delayOut: 500,
            title: function () { return "Actions×"; }
        }).click(function () {
            $(".popover").hide();
            $link.popover('show');
            //            var methodName = $link.attr("data-methodname");
            //            if (methodName) {
            //                $("#" + nodeId + "_" + methodName + "_methodlink").click();
            //            }
        }).click();
    };

    function ensureLazyElement(id, path, callback, transform) {
        if ($("#" + id).length) {
            callback();
            return true;
        }
        $.get(path, {}, function (html) {
            var $html;
            if (transform) {
                $html = transform(html);
            } else {
                $html = $(html);
            }
            $('
').attr("id", id).css("display", "none").append($html).appendTo($("body"));
            callback();
        });
        return true;
    }

    $(".nodeMethodLink").live('click', function (e) {
        e.preventDefault();
        var $link = $(this);
        if ($link.attr("data-custom-method-handler")) {
            return false;
        }
        $link.closest(".popover").hide();
        //if (e.target != this) return false; //why???
        var methodsPanelId = "method-" + $link.attr("data-nodeid");
        if ($("#" + methodsPanelId).length == false) {
            ensureLazyElement(
                methodsPanelId,
                $link.attr("href"),
                function () { showMethodForm($link); },
                function (body) {
                    var $formHtml = $(body);
                    var $modal = $(modalHtml());
                    $modal.find(".title").append($link.html());
                    $modal.find(".modal-footer").append($formHtml.find("button").remove());
                    $modal.find(".modal-body").append($formHtml);
                    return $modal;
                });
        } else {
            showMethodForm($link);
        }
        return false;
    });
    var showMethodForm = function ($link) {

        var methodsPanelId = "method-" + $link.attr("data-nodeid");
        var $method = $("#" + methodsPanelId);
        $method.modal({ show: true, backdrop: true });
        $method.find(":input:visible:enabled:first").focus();

        $.validator.unobtrusive.parseDynamicContent($method);

    };
    $(document).on("click", ".submitMethod", function (e) {
        var $container = $(this).closest(".nodeMethod");
        var $form = $container.is("form") ? $container : $container.find("form");
        var formdata = false;
        if (window.FormData) {
            formdata = new FormData($form[0]);
        };

        var formAction = $form.attr('action');
        var ajaxOptions = {
            url: formAction,
            data: formdata ? formdata : $form.serialize(),
            cache: false,
            contentType: false,
            processData: false,
            type: 'POST',
            success: function (data, textStatus, jqXHR) {
                //if ($link) {
                //    var $table = $link.closest(".dataTable");
                //    if ($table.length) {
                //        $table.dataTable().fnDraw(false);
                //        $method.modal("hide");
                //        $method.remove();
                //        return;
                //    }
                //}
                window.location.reload();
            },
            error: function (jqXhr, textStatus, errorThrown) {
                if (errorThrown == "Conflict") {
                    var $html = $(jqXhr.responseText);
                    if ($container.hasClass("modal")) {
                        var $buttons = $html.find("button").remove();
                        $container.find(".modal-footer").empty().append($buttons);
                    } else if ($form.hasClass("form-inline")) {
                        $html.find("div.controls").css("display", "inline");
                        $html.find("div.control-group").css("display", "inline");
                        $html.addClass("form-inline");
                    }
                    $form.replaceWith($html);
                    $.validator.unobtrusive.parseDynamicContent($form);

                }
            },
            complete: function () {
                //$("#ProgressDialog").dialog("close");
            }
        };
        $.ajax(ajaxOptions);
        return false;
    });
});<commit_msg>Fix for hiding setter<commit_after>//amaze
$(document).ready(function () {

    $(document).on("click", ".show-setter", function (e) {
        var $property = $(this).closest("[data-node-property]");
        var $setter = $property.find("> [data-node-setter]");
        var $getter = $property.find("> [data-node-getter]");
        $getter.fadeOut(function () {
            $setter.fadeIn(function () {
                $setter.find('input,textarea,select').filter(':enabled:visible:not([readonly="readonly"]):not([type="hidden"])').first().focus();

                $.validator.unobtrusive.parseDynamicContent($setter.find("input,textarea,select"));
            });
        });
        e.preventDefault();
        return false;
    });
    $(document).on("click", ".hide-setter", function (e) {
        var $property = $(this).closest("[data-node-property]");
        var $setter = $property.find("> [data-node-setter]");
        var $getter = $property.find("> [data-node-getter]");
        $setter.fadeOut(function () {
            $getter.fadeIn();
        });

        e.preventDefault();
        return false;
    });

    $(".popover .close").live('click', function (e) { $(this).closest(".popover").hide(); });

    $(".nodeMethodsMenuLink").live('click', function (e) {
        var $link = $(this);
        if (e.target != this) return false;
        var methodsPanelId = "methods-" + $link.attr("data-nodeid");
        if ($("#" + methodsPanelId).length == false) {
            ensureLazyElement(
                methodsPanelId,
                $link.attr("href"),
                function () { showMethodsMenu($link); }
            );
        }
        return false;
    });

    function modalHtml() {
        return '

×

';
    }

    var showMethodsMenu = function ($link) {
        var methodsNodeId = "methods-" + $link.attr("data-nodeid");
        $link.attr("data-content", $("#" + methodsNodeId).html());
        $link.popover({
            trigger: "manual",
            placement: ($link.offset().left > ($(window).width() / 2)) ? "left" : "right",
            offset: 0,
            html: true,
            delayOut: 500,
            title: function () { return "Actions×"; }
        }).click(function () {
            $(".popover").hide();
            $link.popover('show');
            //            var methodName = $link.attr("data-methodname");
            //            if (methodName) {
            //                $("#" + nodeId + "_" + methodName + "_methodlink").click();
            //            }
        }).click();
    };

    function ensureLazyElement(id, path, callback, transform) {
        if ($("#" + id).length) {
            callback();
            return true;
        }
        $.get(path, {}, function (html) {
            var $html;
            if (transform) {
                $html = transform(html);
            } else {
                $html = $(html);
            }
            $('
').attr("id", id).css("display", "none").append($html).appendTo($("body"));
            callback();
        });
        return true;
    }

    $(".nodeMethodLink").live('click', function (e) {
        e.preventDefault();
        var $link = $(this);
        if ($link.attr("data-custom-method-handler")) {
            return false;
        }
        $link.closest(".popover").hide();
        //if (e.target != this) return false; //why???
        var methodsPanelId = "method-" + $link.attr("data-nodeid");
        if ($("#" + methodsPanelId).length == false) {
            ensureLazyElement(
                methodsPanelId,
                $link.attr("href"),
                function () { showMethodForm($link); },
                function (body) {
                    var $formHtml = $(body);
                    var $modal = $(modalHtml());
                    $modal.find(".title").append($link.html());
                    $modal.find(".modal-footer").append($formHtml.find("button").remove());
                    $modal.find(".modal-body").append($formHtml);
                    return $modal;
                });
        } else {
            showMethodForm($link);
        }
        return false;
    });
    var showMethodForm = function ($link) {

        var methodsPanelId = "method-" + $link.attr("data-nodeid");
        var $method = $("#" + methodsPanelId);
        $method.modal({ show: true, backdrop: true });
        $method.find(":input:visible:enabled:first").focus();

        $.validator.unobtrusive.parseDynamicContent($method.find("form").find("input,textarea,select").first());

    };
    $(document).on("click", ".submitMethod", function (e) {
        var $container = $(this).closest(".nodeMethod");
        var $form = $container.is("form") ? $container : $container.find("form");
        var formdata = false;
        if (window.FormData) {
            formdata = new FormData($form[0]);
        };

        var formAction = $form.attr('action');
        var ajaxOptions = {
            url: formAction,
            data: formdata ? formdata : $form.serialize(),
            cache: false,
            contentType: false,
            processData: false,
            type: 'POST',
            success: function (data, textStatus, jqXHR) {
                //if ($link) {
                //    var $table = $link.closest(".dataTable");
                //    if ($table.length) {
                //        $table.dataTable().fnDraw(false);
                //        $method.modal("hide");
                //        $method.remove();
                //        return;
                //    }
                //}
                window.location.reload();
            },
            error: function (jqXhr, textStatus, errorThrown) {
                if (errorThrown == "Conflict") {
                    var $html = $(jqXhr.responseText);
                    if ($container.hasClass("modal")) {
                        var $buttons = $html.find("button").remove();
                        $container.find(".modal-footer").empty().append($buttons);
                    } else if ($form.hasClass("form-inline")) {
                        $html.find("div.controls").css("display", "inline");
                        $html.find("div.control-group").css("display", "inline");
                        $html.addClass("form-inline");
                    }
                    $form.replaceWith($html);
                    $.validator.unobtrusive.parseDynamicContent($form.find("input,textarea,select").first());

                }
            },
            complete: function () {
                //$("#ProgressDialog").dialog("close");
            }
        };
        $.ajax(ajaxOptions);
        return false;
    });
});<|endoftext|>


<commit_before>define((require, exports, module) => {
  return (store) => {
    store.subscribe((mutation, state) => {
      if (state.config.evolution_notice == true) {
        if (mutation.type === 'evolution/updateEvolution') {
          let serial_id = mutation.payload.updateData.back[0].serial_id
          let finished_at = moment(parseValues(mutation.payload.updateData.back[0].finished_at))
          let sword_id = _.get(state, ['swords', 'serial', serial_id, 'sword_id'], 0)
          let sword_name = _.get(state, ['swords', 'serial', serial_id, 'name'], '-')
          if(state.evolution.back[0].isIntervalSet == false || state.evolution.back[0].isIntervalSet == null){
            let check = setInterval(function isEvolutionFinished(){
              state.evolution.back[0].isIntervalSet = true
              if(finished_at != null) {
                if(finished_at.isBefore(Date.now())){
                  store.dispatch('notice/addNotice', {
                    title: `${sword_name}修行结束！`,
                    message: `结束时间：${finished_at.format('HH:mm:ss')}`,
                    context: '记得及时刷新游戏接他回本丸哦！',
                    renotify: true,
                    disableAutoClose: true,
                    swordBaseId: sword_id,
                    icon: `static/sword/${sword_id}.png`
                  })
                  clearInterval(check)
                  state.evolution.back[0].isIntervalSet = false
                } else if (finished_at.add(-1,'days').isBefore(Date.now())) {
                  store.dispatch('notice/addNotice', {
                    title: `${sword_name}的第三封修行书信已经收到！`,
                    message: `收取时间：${finished_at.format('HH:mm:ss')}`,
                    context: '记得及时刷新游戏阅读哦！',
                    renotify: true,
                    disableAutoClose: true,
                    swordBaseId: sword_id,
                    icon: `static/sword/${sword_id}.png`
                  })
                } else if (finished_at.add(-2,'days').isBefore(Date.now())) {
                  store.dispatch('notice/addNotice', {
                    title: `${sword_name}的第二封修行书信已经收到！`,
                    message: `收取时间：${finished_at.format('HH:mm:ss')}`,
                    context: '记得及时刷新游戏阅读哦！',
                    renotify: true,
                    disableAutoClose: true,
                    swordBaseId: sword_id,
                    icon: `static/sword/${sword_id}.png`
                  })
                } else if (finished_at.add(-3,'days').isBefore(Date.now())) {
                  store.dispatch('notice/addNotice', {
                    title: `${sword_name}的第一封修行书信已经收到！`,
                    message: `收取时间：${finished_at.format('HH:mm:ss')}`,
                    context: '记得及时刷新游戏阅读哦！',
                    renotify: true,
                    disableAutoClose: true,
                    swordBaseId: sword_id,
                    icon: `static/sword/${sword_id}.png`
                  })
                }
              } else {
                clearInterval(check)
                state.evolution.back[0].isIntervalSet = false
              }
            }, 1000)
          }
        }
      }
    })
  }
})<commit_msg>add bug(x<commit_after>define((require, exports, module) => {
  return (store) => {
    store.subscribe((mutation, state) => {
      if (state.config.evolution_notice == true) {
        if (mutation.type === 'evolution/updateEvolution') {
          let serial_id = mutation.payload.updateData.back[0].serial_id
          let finished_at = moment(parseValues(mutation.payload.updateData.back[0].finished_at))
          let sword_id = _.get(state, ['swords', 'serial', serial_id, 'sword_id'], 0)
          let sword_name = _.get(state, ['swords', 'serial', serial_id, 'name'], '-')
          if(state.evolution.back[0].isIntervalSet == false || state.evolution.back[0].isIntervalSet == null){
            let check = setInterval(function isEvolutionFinished(){
              state.evolution.back[0].isIntervalSet = true
              if(finished_at != null) {
                if(finished_at.isBefore(Date.now())){
                  store.dispatch('notice/addNotice', {
                    title: `${sword_name}修行结束！`,
                    message: `结束时间：${finished_at.format('HH:mm:ss')}`,
                    context: '记得及时刷新游戏接他回本丸哦！',
                    renotify: true,
                    disableAutoClose: true,
                    swordBaseId: sword_id,
                    icon: `static/sword/${sword_id}.png`
                  })
                  clearInterval(check)
                  state.evolution.back[0].isIntervalSet = false
                } else if (finished_at.add(-1,'days').isBefore(Date.now())) {
                  store.dispatch('notice/addNotice', {
                    title: `${sword_name}的第三封修行书信已经收到！`,
                    message: `收取时间：${finished_at.format('HH:mm:ss')}`,
                    context: '记得及时阅读哦！',
                    renotify: true,
                    disableAutoClose: true,
                    swordBaseId: sword_id,
                    icon: `static/sword/${sword_id}.png`
                  })
                } else if (finished_at.add(-2,'days').isBefore(Date.now())) {
                  store.dispatch('notice/addNotice', {
                    title: `${sword_name}的第二封修行书信已经收到！`,
                    message: `收取时间：${finished_at.format('HH:mm:ss')}`,
                    context: '记得及时阅读哦！',
                    renotify: true,
                    disableAutoClose: true,
                    swordBaseId: sword_id,
                    icon: `static/sword/${sword_id}.png`
                  })
                } else if (finished_at.add(-3,'days').isBefore(Date.now())) {
                  store.dispatch('notice/addNotice', {
                    title: `${sword_name}的第一封修行书信已经收到！`,
                    message: `收取时间：${finished_at.format('HH:mm:ss')}`,
                    context: '记得及时阅读哦！',
                    renotify: true,
                    disableAutoClose: true,
                    swordBaseId: sword_id,
                    icon: `static/sword/${sword_id}.png`
                  })
                }
              } else {
                clearInterval(check)
                state.evolution.back[0].isIntervalSet = false
              }
            }, 1000)
          }
        }
      }
    })
  }
})<|endoftext|>