<commit_before>import {EventEmitter} from 'events';
import {Readable} from 'stream';
import AWS from 'aws-sdk';
import Promise from 'bluebird';
import _ from 'lodash';
import uuid from 'node-uuid';
import type from 'type-of';
import CustomError from './helpers/CustomError';

class BloodySimpleSQS extends EventEmitter {

  /**
   * Constructs and returns a new bloody simple SQS client.
   * @param {Object} options SQS client options.
   * @param {String} options.queueName the name of the queue as provided by AWS.
   * @param {String} options.accessKeyId your AWS access key ID.
   * @param {String} options.secretAccessKey your AWS secret access key.
   * @param {String} [options.region=us-east-1] the region of the queue as provided by AWS.
   * @constructor
   */
  constructor(options) {
    // init event emitter
    super();
    this.setMaxListeners(999);

    // validate arguments
    if (!_.isPlainObject(options)) {
      throw new CustomError(`Invalid options param; expected object, received ${type(options)}`, 'InvalidArgument');
    }

    // set default options
    options = _.defaults(options, {
      region: 'us-east-1'
    });

    // validate options
    if (!_.isString(options.queueName)) {
      throw new CustomError(`Invalid queueName option; expected string, received ${type(options.queueName)}`, 'InvalidArgument');
    }

    if (!_.isString(options.accessKeyId)) {
      throw new CustomError(`Invalid accessKeyId option; expected string, received ${type(options.accessKeyId)}`, 'InvalidArgument');
    }

    if (!_.isString(options.secretAccessKey)) {
      throw new CustomError(`Invalid secretAccessKey option; expected string, received ${type(options.secretAccessKey)}`, 'InvalidArgument');
    }

    if (!_.isString(options.region)) {
      throw new CustomError(`Invalid region option; expected string, received ${type(options.region)}`, 'InvalidArgument');
    }

    // init internal aws-sdk client
    this.queueName = options.queueName;

    this.sqs = new AWS.SQS({
      queueName: options.queueName,
      accessKeyId: options.accessKeyId,
      secretAccessKey: options.secretAccessKey,
      region: options.region,
      apiVersion: '2012-11-05'
    });

    // retrieve queue URL and emit ready
    this.getUrl().then((url) => {
      this.queueUrl = url;
      this.isReady = true;
      this.emit('ready');
    });
  }

  /**
   * Constructs and returns a new Promise based on the designated resolver.
   * Delays the execution of the resolver until the queue emits ready.
   * @param {Function} resolver
   * @return {Promise}
   * @private
   */
  _contructPromise(resolver) {
    return new Promise((resolve, reject) => {
      if (this.isReady) {
        resolver(resolve, reject);
      } else {
        this.once('ready', () => resolver(resolve, reject));
      }
    });
  }

  /**
   * Retrieves the URL of the queue from AWS.
   * @param {Function} [callback] an optional callback function with (err, url) arguments.
   * @return {Promise}
   */
  getUrl(callback) {
    // check if queue URL is already set
    if (this.isReady) {
      return Promise.resolve(this.queueUrl).nodeify(callback);
    }

    let resolver = (resolve, reject) => {
      this.sqs.getQueueUrl({
        QueueName: this.queueName
      }, (err, response) => {
        if (err) return reject(err);

        resolve(response.QueueUrl);
      });
    };

    return new Promise(resolver).nodeify(callback);
  }

  /**
   * Returns the number of messages in the queue.
   * @param {Function} [callback] an optional callback function with (err, size) arguments.
   * @return {Promise}
   */
  size(callback) {
    let resolver = (resolve, reject) => {
      this.sqs.getQueueAttributes({
        QueueUrl: this.queueUrl,
        AttributeNames: ['ApproximateNumberOfMessages', 'ApproximateNumberOfMessagesNotVisible']
      }, (err, response) => {
        if (err) return reject(err);

        let messagesCount = parseInt(response.Attributes.ApproximateNumberOfMessages, 10) +
          parseInt(response.Attributes.ApproximateNumberOfMessagesNotVisible, 10);

        resolve(messagesCount);
      });
    };

    return this._contructPromise(resolver).nodeify(callback);
  }

  /**
   * Indicates whether the queue is empty.
   * @param {Function} [callback] an optional callback function (err, isEmpty) arguments.
   * @return {Promise}
   */
  isEmpty(callback) {
    return this.size()

      .then((messagesCount) => {
        return messagesCount === 0;
      })

      .nodeify(callback);
  }

  /**
   * Appends a new message, with the given payload, at the end of the queue.
   * @param {(Boolean|String|Number|Object|null)} payload the message payload.
   * @param {Function} [callback] an optional callback function with (err, response) arguments.
   * @return {Promise}
   */
  add(payload, callback) {
    // validate arguments
    if (
      !_.isNumber(payload) &&
      !_.isString(payload) &&
      !_.isPlainObject(payload) &&
      !_.isBoolean(payload) &&
      !_.isNull(payload)
    ) {
      return Promise.reject(new CustomError(`Invalid payload argument; expected number, string, boolean, object or null, received ${type(payload)}`, 'InvalidArgument'))
        .nodeify(callback);
    }

    // define promise resolver
    let resolver = (resolve, reject) => {
      this.sqs.sendMessage({
        QueueUrl: this.queueUrl,
        MessageBody: JSON.stringify(payload)
      }, (err, response) => {
        if (err) return reject(err);

        resolve({
          id: response.MessageId,
          body: payload,
          md5: response.MD5OfMessageBody
        });
      });
    };

    return this._contructPromise(resolver).nodeify(callback);
  }

  /**
   * Appends the elements of the specified array as messages to the queue.
   * @param {Array} arr an array of elements to append.
   * @param {Function} [callback] an optional callback function with (err, response) arguments.
   * @return {Promise}
   */
  addAll(arr, callback) {
    // validate arguments
    if (!_.isArray(arr)) {
      return Promise.reject(new CustomError(`Invalid arr argument; expected array, received ${type(arr)}`, 'InvalidArgument'))
        .nodeify(callback);
    }

    // check if array is empty
    if (arr.length === 0) {
      return Promise.resolve([]).nodeify(callback); // exit
    }

    // check if array contains more than 10 elements
    if (arr.length > 10) {
      // split in chunks
      return Promise.map(_.chunk(arr, 10), (chunk) => {
        return this.addAll(chunk);
      })
        .then(_.flatten)
        .nodeify(callback);
    }

    // define promise resolver
    let resolver = (resolve, reject) => {
      this.sqs.sendMessageBatch({
        QueueUrl: this.queueUrl,
        Entries: arr.map((e) => {
          return {
            Id: uuid.v4(),
            MessageBody: JSON.stringify(e)
          };
        })
      }, (err) => {
        if (err) return reject(err);
        resolve();
      });
    };

    return this._contructPromise(resolver).nodeify(callback);
  }

  /**
   * Retrieves, but does not remove, the head of the queue.
   * @param {Object} [options] optional request options.
   * @param {number} [options.timeout=0] number of seconds to wait until a message arrives in the queue; must be between 0 and 20.
   * @param {number} [options.limit=1] maximum number of messages to return.
   * @param {Function} [callback] an optional callback function with (err, message) arguments.
   * @return {Promise}
   */
  peek(options, callback) {
    // validate arguments
    if (_.isFunction(options)) {
      callback = options;
      options = {};
    } else if (_.isUndefined(options)) {
      options = {};
    } else if (!_.isPlainObject(options)) {
      return Promise.reject(new CustomError(`Invalid options argument; expected object, received ${type(options)}`, 'InvalidArgument'))
        .nodeify(callback);
    }

    // set default options
    options = _.defaults(options, {
      timeout: 0,
      limit: 1
    });

    // validate options
    if (!_.isNumber(options.timeout)) {
      return Promise.reject(new CustomError(`Invalid timeout option; expected number, received ${type(options.timeout)}`))
        .nodeify(callback);
    }

    if (!_.isNumber(options.limit)) {
      return Promise.reject(new CustomError(`Invalid limit option; expected number, received ${type(options.limit)}`))
        .nodeify(callback);
    }

    // check if array contains more than 10 elements
    if (options.limit > 10) {
      // split in chunks
      return Promise.map(_.chunk(new Array(options.limit), 10), (chunk) => {
        return this.peek({timeout: options.timeout, limit: chunk.length});
      })
        .then(_.flatten)
        .nodeify(callback);
    }

    // define promise resolver
    let resolver = (resolve, reject) => {
      this.sqs.receiveMessage({
        QueueUrl: this.queueUrl,
        MaxNumberOfMessages: options.limit,
        WaitTimeSeconds: options.timeout
      }, (err, response) => {
        if (err) return reject(err);

        let messages = (response.Messages || [])

          .map((obj) => {
            return {
              id: obj.MessageId,
              body: JSON.parse(obj.Body),
              md5: obj.MD5OfBody,
              receiptHandle: obj.ReceiptHandle
            };
          });

        if (options.limit === 1) {
          resolve(messages[0] || null); // return single message
          return;
        }

        resolve(messages);
      });
    };

    return this._contructPromise(resolver).nodeify(callback);
  }

  /**
   * Removes the designated message from queue.
   * @param {String} receiptHandle the message's receipt handle, as given on peek().
   * @param {Function} [callback] an optional callback function with (err) arguments.
   * @return {Promise}
   */
  remove(receiptHandle, callback) {
    // validate arguments
    if (!_.isString(receiptHandle)) {
      return Promise.reject(new CustomError(`Invalid receiptHandle argument; expected string, received ${type(receiptHandle)}`))
        .nodeify(callback);
    }

    // define promise resolver
    let resolver = (resolve, reject) => {
      this.sqs.deleteMessage({
        QueueUrl: this.queueUrl,
        ReceiptHandle: receiptHandle
      }, (err) => {
        if (err) return reject(err);
        resolve();
      });
    };

    return this._contructPromise(resolver).nodeify(callback);
  }

  /**
   * Retrieves and removes the head of the queue, or returns null if queue is empty.
   * @param {Object} [options] optional request options.
   * @param {Function} [callback] an optional callback function with (err, message) arguments.
   * @see {@link peek} for further information on the "options" param.
   * @return {Promise}
   */
  poll(options, callback) {
    return this.peek(options)

      .map((message) => {
        return this.remove(message.receiptHandle)
          .return(message);
      })

      .nodeify(callback);
  }

  /**
   * Removes all messages from queue.
   * @param {Function} [callback] an optional callback function with (err) arguments.
   * @return {Promise}
   */
  clear(callback) {
    let resolver = (resolve, reject) => {
      this.sqs.purgeQueue({QueueUrl: this.queueUrl}, (err) => {
        if (err) return reject(err);
        resolve();
      });
    };

    return this._contructPromise(resolver).nodeify(callback);
  }

  /**
   * Returns a new ReadStream object consuming the queue's messages.
   * @return {stream.Readable}
   */
  createReadStream() {
    let rs = new Readable({
      highWaterMark: 1, // as little as possible
      objectMode: true,
      encoding: 'utf8'
    });

    rs._read = () => {
      this.poll()

        .then((message) => {
          if (message.length === 0) return rs.push(null); // end
          rs.push(message[0].body, 'utf8');
        })

        .catch((err) => {
          rs.emit('error', err);
        });
    };

    return rs;
  }
}

export default BloodySimpleSQS;
<commit_msg>Add #pollOne and #peekOne methods; #poll and #peek always return arrays for consistency<commit_after>import {EventEmitter} from 'events';
import {Readable} from 'stream';
import AWS from 'aws-sdk';
import Promise from 'bluebird';
import _ from 'lodash';
import uuid from 'node-uuid';
import type from 'type-of';
import CustomError from './helpers/CustomError';

class BloodySimpleSQS extends EventEmitter {

  /**
   * Constructs and returns a new bloody simple SQS client.
   * @param {Object} options SQS client options.
   * @param {String} options.queueName the name of the queue as provided by AWS.
   * @param {String} options.accessKeyId your AWS access key ID.
   * @param {String} options.secretAccessKey your AWS secret access key.
   * @param {String} [options.region=us-east-1] the region of the queue as provided by AWS.
   * @constructor
   */
  constructor(options) {
    // init event emitter
    super();
    this.setMaxListeners(999);

    // validate arguments
    if (!_.isPlainObject(options)) {
      throw new CustomError(`Invalid options param; expected object, received ${type(options)}`, 'InvalidArgument');
    }

    // set default options
    options = _.defaults(options, {
      region: 'us-east-1'
    });

    // validate options
    if (!_.isString(options.queueName)) {
      throw new CustomError(`Invalid queueName option; expected string, received ${type(options.queueName)}`, 'InvalidArgument');
    }

    if (!_.isString(options.accessKeyId)) {
      throw new CustomError(`Invalid accessKeyId option; expected string, received ${type(options.accessKeyId)}`, 'InvalidArgument');
    }

    if (!_.isString(options.secretAccessKey)) {
      throw new CustomError(`Invalid secretAccessKey option; expected string, received ${type(options.secretAccessKey)}`, 'InvalidArgument');
    }

    if (!_.isString(options.region)) {
      throw new CustomError(`Invalid region option; expected string, received ${type(options.region)}`, 'InvalidArgument');
    }

    // init internal aws-sdk client
    this.queueName = options.queueName;

    this.sqs = new AWS.SQS({
      queueName: options.queueName,
      accessKeyId: options.accessKeyId,
      secretAccessKey: options.secretAccessKey,
      region: options.region,
      apiVersion: '2012-11-05'
    });

    // retrieve queue URL and emit ready
    this.getUrl().then((url) => {
      this.queueUrl = url;
      this.isReady = true;
      this.emit('ready');
    });
  }

  /**
   * Constructs and returns a new Promise based on the designated resolver.
   * Delays the execution of the resolver until the queue emits ready.
   * @param {Function} resolver
   * @return {Promise}
   * @private
   */
  _contructPromise(resolver) {
    return new Promise((resolve, reject) => {
      if (this.isReady) {
        resolver(resolve, reject);
      } else {
        this.once('ready', () => resolver(resolve, reject));
      }
    });
  }

  /**
   * Retrieves the URL of the queue from AWS.
   * @param {Function} [callback] an optional callback function with (err, url) arguments.
   * @return {Promise}
   */
  getUrl(callback) {
    // check if queue URL is already set
    if (this.isReady) {
      return Promise.resolve(this.queueUrl).nodeify(callback);
    }

    let resolver = (resolve, reject) => {
      this.sqs.getQueueUrl({
        QueueName: this.queueName
      }, (err, response) => {
        if (err) return reject(err);

        resolve(response.QueueUrl);
      });
    };

    return new Promise(resolver).nodeify(callback);
  }

  /**
   * Returns the number of messages in the queue.
   * @param {Function} [callback] an optional callback function with (err, size) arguments.
   * @return {Promise}
   */
  size(callback) {
    let resolver = (resolve, reject) => {
      this.sqs.getQueueAttributes({
        QueueUrl: this.queueUrl,
        AttributeNames: ['ApproximateNumberOfMessages', 'ApproximateNumberOfMessagesNotVisible']
      }, (err, response) => {
        if (err) return reject(err);

        let messagesCount = parseInt(response.Attributes.ApproximateNumberOfMessages, 10) +
          parseInt(response.Attributes.ApproximateNumberOfMessagesNotVisible, 10);

        resolve(messagesCount);
      });
    };

    return this._contructPromise(resolver).nodeify(callback);
  }

  /**
   * Indicates whether the queue is empty.
   * @param {Function} [callback] an optional callback function (err, isEmpty) arguments.
   * @return {Promise}
   */
  isEmpty(callback) {
    return this.size()

      .then((messagesCount) => {
        return messagesCount === 0;
      })

      .nodeify(callback);
  }

  /**
   * Appends a new message, with the given payload, at the end of the queue.
   * @param {(Boolean|String|Number|Object|null)} payload the message payload.
   * @param {Function} [callback] an optional callback function with (err, response) arguments.
   * @return {Promise}
   */
  add(payload, callback) {
    // validate arguments
    if (
      !_.isNumber(payload) &&
      !_.isString(payload) &&
      !_.isPlainObject(payload) &&
      !_.isBoolean(payload) &&
      !_.isNull(payload)
    ) {
      return Promise.reject(new CustomError(`Invalid payload argument; expected number, string, boolean, object or null, received ${type(payload)}`, 'InvalidArgument'))
        .nodeify(callback);
    }

    // define promise resolver
    let resolver = (resolve, reject) => {
      this.sqs.sendMessage({
        QueueUrl: this.queueUrl,
        MessageBody: JSON.stringify(payload)
      }, (err, response) => {
        if (err) return reject(err);

        resolve({
          id: response.MessageId,
          body: payload,
          md5: response.MD5OfMessageBody
        });
      });
    };

    return this._contructPromise(resolver).nodeify(callback);
  }

  /**
   * Appends the elements of the specified array as messages to the queue.
   * @param {Array} arr an array of elements to append.
   * @param {Function} [callback] an optional callback function with (err, response) arguments.
   * @return {Promise}
   */
  addAll(arr, callback) {
    // validate arguments
    if (!_.isArray(arr)) {
      return Promise.reject(new CustomError(`Invalid arr argument; expected array, received ${type(arr)}`, 'InvalidArgument'))
        .nodeify(callback);
    }

    // check if array is empty
    if (arr.length === 0) {
      return Promise.resolve([]).nodeify(callback); // exit
    }

    // check if array contains more than 10 elements
    if (arr.length > 10) {
      // split in chunks
      return Promise.map(_.chunk(arr, 10), (chunk) => {
        return this.addAll(chunk);
      })
        .then(_.flatten)
        .nodeify(callback);
    }

    // define promise resolver
    let resolver = (resolve, reject) => {
      this.sqs.sendMessageBatch({
        QueueUrl: this.queueUrl,
        Entries: arr.map((e) => {
          return {
            Id: uuid.v4(),
            MessageBody: JSON.stringify(e)
          };
        })
      }, (err) => {
        if (err) return reject(err);
        resolve();
      });
    };

    return this._contructPromise(resolver).nodeify(callback);
  }

  /**
   * Retrieves, but does not remove, the specified number of messages from the head of the queue.
   * @param {Object} [options] optional request options.
   * @param {number} [options.timeout=0] number of seconds to wait until a message arrives in the queue; must be between 0 and 20.
   * @param {number} [options.limit=1] maximum number of messages to return.
   * @param {Function} [callback] an optional callback function with (err, messages) arguments.
   * @return {Promise}
   */
  peek(options, callback) {
    // validate arguments
    if (_.isFunction(options)) {
      callback = options;
      options = {};
    } else if (_.isUndefined(options)) {
      options = {};
    } else if (!_.isPlainObject(options)) {
      return Promise.reject(new CustomError(`Invalid options argument; expected object, received ${type(options)}`, 'InvalidArgument'))
        .nodeify(callback);
    }

    // set default options
    options = _.defaults(options, {
      timeout: 0,
      limit: 1
    });

    // validate options
    if (!_.isNumber(options.timeout)) {
      return Promise.reject(new CustomError(`Invalid timeout option; expected number, received ${type(options.timeout)}`))
        .nodeify(callback);
    }

    if (!_.isNumber(options.limit)) {
      return Promise.reject(new CustomError(`Invalid limit option; expected number, received ${type(options.limit)}`))
        .nodeify(callback);
    }

    // check if array contains more than 10 elements
    if (options.limit > 10) {
      // split in chunks
      return Promise.map(_.chunk(new Array(options.limit), 10), (chunk) => {
        return this.peek({timeout: options.timeout, limit: chunk.length});
      })
        .then(_.flatten)
        .nodeify(callback);
    }

    // define promise resolver
    let resolver = (resolve, reject) => {
      this.sqs.receiveMessage({
        QueueUrl: this.queueUrl,
        MaxNumberOfMessages: options.limit,
        WaitTimeSeconds: options.timeout
      }, (err, response) => {
        if (err) return reject(err);

        let messages = (response.Messages || [])

          .map((obj) => {
            return {
              id: obj.MessageId,
              body: JSON.parse(obj.Body),
              md5: obj.MD5OfBody,
              receiptHandle: obj.ReceiptHandle
            };
          });

        resolve(messages);
      });
    };

    return this._contructPromise(resolver).nodeify(callback);
  }

  /**
   * Retrieves, but does not remove, the first message from the head of the queue.
   * @param {Object} [options] optional request options.
   * @param {number} [options.timeout=0] number of seconds to wait until a message arrives in the queue; must be between 0 and 20.
   * @param {Function} [callback] an optional callback function with (err, messages) arguments.
   * @return {Promise}
   */
  peekOne(options, callback) {
    // validate arguments
    if (_.isFunction(options)) {
      callback = options;
      options = {};
    } else if (_.isUndefined(options)) {
      options = {};
    } else if (!_.isPlainObject(options)) {
      return Promise.reject(new CustomError(`Invalid options argument; expected object, received ${type(options)}`, 'InvalidArgument'))
        .nodeify(callback);
    }

    // set default options
    options = _.defaults(options, {
      timeout: 0
    });

    // set hard limit to 1
    options.limit = 1;

    // peek and unpack returned messages
    return this.peek(options)
      .then((messages) => {
        if (messages.length === 0) return null;
        return messages[0];
      })
      .nodeify(callback);
  }

  /**
   * Removes the designated message from queue.
   * @param {String} receiptHandle the message's receipt handle, as given on peek().
   * @param {Function} [callback] an optional callback function with (err) arguments.
   * @return {Promise}
   */
  remove(receiptHandle, callback) {
    // validate arguments
    if (!_.isString(receiptHandle)) {
      return Promise.reject(new CustomError(`Invalid receiptHandle argument; expected string, received ${type(receiptHandle)}`))
        .nodeify(callback);
    }

    // define promise resolver
    let resolver = (resolve, reject) => {
      this.sqs.deleteMessage({
        QueueUrl: this.queueUrl,
        ReceiptHandle: receiptHandle
      }, (err) => {
        if (err) return reject(err);
        resolve();
      });
    };

    return this._contructPromise(resolver).nodeify(callback);
  }

  /**
   * Retrieves and removes the specified number of messages from the head of the queue.
   * @param {Object} [options] optional request options.
   * @param {number} [options.timeout=0] number of seconds to wait until a message arrives in the queue; must be between 0 and 20.
   * @param {number} [options.limit=1] maximum number of messages to return.
   * @param {Function} [callback] an optional callback function with (err, messages) arguments.
   * @return {Promise}
   */
  poll(options, callback) {
    return this.peek(options)

      .map((message) => {
        return this.remove(message.receiptHandle)
          .return(message);
      })

      .nodeify(callback);
  }

  /**
   * Retrieves and removes the first message from the head of the queue.
   * @param {Object} [options] optional request options.
   * @param {number} [options.timeout=0] number of seconds to wait until a message arrives in the queue; must be between 0 and 20.
   * @param {Function} [callback] an optional callback function with (err, messages) arguments.
   * @return {Promise}
   */
  pollOne(options, callback) {
    return this.peekOne(options)

      .then((message) => {
        if (!message) return null;

        return this.remove(message.receiptHandle)
          .return(message);
      })

      .nodeify(callback);
  }

  /**
   * Removes all messages from queue.
   * @param {Function} [callback] an optional callback function with (err) arguments.
   * @return {Promise}
   */
  clear(callback) {
    let resolver = (resolve, reject) => {
      this.sqs.purgeQueue({QueueUrl: this.queueUrl}, (err) => {
        if (err) return reject(err);
        resolve();
      });
    };

    return this._contructPromise(resolver).nodeify(callback);
  }

  /**
   * Returns a new ReadStream object consuming the queue's messages.
   * @return {stream.Readable}
   */
  createReadStream() {
    let rs = new Readable({
      highWaterMark: 1, // as little as possible
      objectMode: true,
      encoding: 'utf8'
    });

    rs._read = () => {
      this.pollOne()

        .then((message) => {
          if (!message) {
            return rs.push(null); // exit
          }

          rs.push(message.body, 'utf8');
        })

        .catch((err) => {
          rs.emit('error', err);
        });
    };

    return rs;
  }
}

export default BloodySimpleSQS;
<|endoftext|>


<commit_before>/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
 * full list of contributors). Published under the 2-clause BSD license.
 * See license.txt in the OpenLayers distribution or repository for the
 * full text of the license. */

/**
 * @requires OpenLayers/Layer/Google.js
 */

/**
 * Constant: OpenLayers.Layer.Google.v3
 *
 * Mixin providing functionality specific to the Google Maps API v3 <= v3.6.
 * Note that this layer configures the google.maps.map object with the
 * "disableDefaultUI" option set to true. Using UI controls that the Google
 * Maps API provides is not supported by the OpenLayers API. To use this layer,
 * you must include the GMaps API (<= v3.6) in your html:
 *
 * (code)
 * <script src="http://maps.google.com/maps/api/js?v=3.6&sensor=false"></script>
 * (end)
 */
OpenLayers.Layer.Google.v3 = {

    /**
     * Constant: DEFAULTS
     * {Object} It is not recommended to change the properties set here. Note
     * that Google.v3 layers only work when sphericalMercator is set to true.
     *
     * (code)
     * {
     *     sphericalMercator: true,
     *     projection: "EPSG:900913"
     * }
     * (end)
     */
    DEFAULTS: {
        sphericalMercator: true,
        projection: "EPSG:900913"
    },

    /**
     * APIProperty: animationEnabled
     * {Boolean} If set to true, the transition between zoom levels will be
     *     animated (if supported by the GMaps API for the device used). Set to
     *     false to match the zooming experience of other layer types. Default
     *     is true. Note that the GMaps API does not give us control over zoom
     *     animation, so if set to false, when zooming, this will make the
     *     layer temporarily invisible, wait until GMaps reports the map being
     *     idle, and make it visible again. The result will be a blank layer
     *     for a few moments while zooming.
     */
    animationEnabled: true,

    /**
     * Method: loadMapObject
     * Load the GMap and register appropriate event listeners. If we can't
     *     load GMap2, then display a warning message.
     */
    loadMapObject:function() {
        if (!this.type) {
            this.type = google.maps.MapTypeId.ROADMAP;
        }
        var mapObject;
        var cache = OpenLayers.Layer.Google.cache[this.map.id];
        if (cache) {
            // there are already Google layers added to this map
            mapObject = cache.mapObject;
            // increment the layer count
            ++cache.count;
        } else {
            // this is the first Google layer for this map

            var container = this.map.viewPortDiv;
            var div = document.createElement("div");
            div.id = this.map.id + "_GMapContainer";
            div.style.position = "absolute";
            div.style.width = "100%";
            div.style.height = "100%";
            container.appendChild(div);

            // create GMap and shuffle elements
            var center = this.map.getCenter();
            mapObject = new google.maps.Map(div, {
                center: center ?
                    new google.maps.LatLng(center.lat, center.lon) :
                    new google.maps.LatLng(0, 0),
                zoom: this.map.getZoom() || 0,
                mapTypeId: this.type,
                disableDefaultUI: true,
                keyboardShortcuts: false,
                draggable: false,
                disableDoubleClickZoom: true,
                scrollwheel: false,
                streetViewControl: false
            });

            // cache elements for use by any other google layers added to
            // this same map
            cache = {
                mapObject: mapObject,
                count: 1
            };
            OpenLayers.Layer.Google.cache[this.map.id] = cache;
            this.repositionListener = google.maps.event.addListenerOnce(
                mapObject,
                "center_changed",
                OpenLayers.Function.bind(this.repositionMapElements, this)
            );
        }
        this.mapObject = mapObject;
        this.setGMapVisibility(this.visibility);
    },

    /**
     * Method: repositionMapElements
     *
     * Waits until powered by and terms of use elements are available and then
     * moves them so they are clickable.
     */
    repositionMapElements: function() {

        // This is the first time any Google layer in this mapObject has been
        // made visible.  The mapObject needs to know the container size.
        google.maps.event.trigger(this.mapObject, "resize");

        var div = this.mapObject.getDiv().firstChild;
        if (!div || div.childNodes.length < 3) {
            this.repositionTimer = window.setTimeout(
                OpenLayers.Function.bind(this.repositionMapElements, this),
                250
            );
            return false;
        }

        var cache = OpenLayers.Layer.Google.cache[this.map.id];
        var container = this.map.viewPortDiv;

        // move the Map Data popup to the container, if any
        while (div.lastChild.style.display == "none") {
            container.appendChild(div.lastChild);
        }

        // move the ToS and branding stuff up to the container div
        var termsOfUse = div.lastChild;
        container.appendChild(termsOfUse);
        termsOfUse.style.zIndex = "1100";
        termsOfUse.style.bottom = "";
        termsOfUse.className = "olLayerGoogleCopyright olLayerGoogleV3";
        termsOfUse.style.display = "";
        cache.termsOfUse = termsOfUse;

        var poweredBy = div.lastChild;
        container.appendChild(poweredBy);
        poweredBy.style.zIndex = "1100";
        poweredBy.style.bottom = "";
        poweredBy.className = "olLayerGooglePoweredBy olLayerGoogleV3 gmnoprint";
        poweredBy.style.display = "";
        cache.poweredBy = poweredBy;

        this.setGMapVisibility(this.visibility);

    },

    /**
     * APIMethod: onMapResize
     */
    onMapResize: function() {
        if (this.visibility) {
            google.maps.event.trigger(this.mapObject, "resize");
        } else {
            var cache = OpenLayers.Layer.Google.cache[this.map.id];
            if (!cache.resized) {
                var layer = this;
                google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function() {
                    google.maps.event.trigger(layer.mapObject, "resize");
                    layer.moveTo(layer.map.getCenter(), layer.map.getZoom());
                    delete cache.resized;
                });
            }
            cache.resized = true;
        }
    },

    /**
     * Method: setGMapVisibility
     * Display the GMap container and associated elements.
     *
     * Parameters:
     * visible - {Boolean} Display the GMap elements.
     */
    setGMapVisibility: function(visible) {
        var cache = OpenLayers.Layer.Google.cache[this.map.id];
        if (cache) {
            var type = this.type;
            var layers = this.map.layers;
            var layer;
            for (var i=layers.length-1; i>=0; --i) {
                layer = layers[i];
                if (layer instanceof OpenLayers.Layer.Google &&
                            layer.visibility === true && layer.inRange === true) {
                    type = layer.type;
                    visible = true;
                    break;
                }
            }
            var container = this.mapObject.getDiv();
            if (visible === true) {
                this.mapObject.setMapTypeId(type);
                container.style.left = "";
                if (cache.termsOfUse && cache.termsOfUse.style) {
                    cache.termsOfUse.style.left = "";
                    cache.termsOfUse.style.display = "";
                    cache.poweredBy.style.display = "";
                }
                cache.displayed = this.id;
            } else {
                delete cache.displayed;
                container.style.left = "-9999px";
                if (cache.termsOfUse && cache.termsOfUse.style) {
                    cache.termsOfUse.style.display = "none";
                    // move ToU far to the left in addition to setting
                    // display to "none", because at the end of the GMap
                    // load sequence, display: none will be unset and ToU
                    // would be visible after loading a map with a google
                    // layer that is initially hidden.
                    cache.termsOfUse.style.left = "-9999px";
                    cache.poweredBy.style.display = "none";
                }
            }
        }
    },

    /**
     * Method: getMapContainer
     *
     * Returns:
     * {DOMElement} the GMap container's div
     */
    getMapContainer: function() {
        return this.mapObject.getDiv();
    },

  //
  // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
  //

    /**
     * APIMethod: getMapObjectBoundsFromOLBounds
     *
     * Parameters:
     * olBounds - {<OpenLayers.Bounds>}
     *
     * Returns:
     * {Object} A MapObject Bounds, translated from olBounds
     *          Returns null if null value is passed in
     */
    getMapObjectBoundsFromOLBounds: function(olBounds) {
        var moBounds = null;
        if (olBounds != null) {
            var sw = this.sphericalMercator ?
              this.inverseMercator(olBounds.bottom, olBounds.left) :
              new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
            var ne = this.sphericalMercator ?
              this.inverseMercator(olBounds.top, olBounds.right) :
              new OpenLayers.LonLat(olBounds.top, olBounds.right);
            moBounds = new google.maps.LatLngBounds(
                new google.maps.LatLng(sw.lat, sw.lon),
                new google.maps.LatLng(ne.lat, ne.lon)
            );
        }
        return moBounds;
    },

    /************************************
     *                                  *
     *   MapObject Interface Controls   *
     *                                  *
     ************************************/

  // LonLat - Pixel Translation

    /**
     * APIMethod: getMapObjectLonLatFromMapObjectPixel
     *
     * Parameters:
     * moPixel - {Object} MapObject Pixel format
     *
     * Returns:
     * {Object} MapObject LonLat translated from MapObject Pixel
     */
    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
        var size = this.map.getSize();
        var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);
        var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);
        var res = this.map.getResolution();

        var delta_x = moPixel.x - (size.w / 2);
        var delta_y = moPixel.y - (size.h / 2);

        var lonlat = new OpenLayers.LonLat(
            lon + delta_x * res,
            lat - delta_y * res
        );

        if (this.wrapDateLine) {
            lonlat = lonlat.wrapDateLine(this.maxExtent);
        }
        return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);
    },

    /**
     * APIMethod: getMapObjectPixelFromMapObjectLonLat
     *
     * Parameters:
     * moLonLat - {Object} MapObject LonLat format
     *
     * Returns:
     * {Object} MapObject Pixel transtlated from MapObject LonLat
     */
    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
        var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
        var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
        var res = this.map.getResolution();
        var extent = this.map.getExtent();
        return this.getMapObjectPixelFromXY((1/res * (lon - extent.left)),
                                            (1/res * (extent.top - lat)));
    },

    /**
     * APIMethod: setMapObjectCenter
     * Set the mapObject to the specified center and zoom
     *
     * Parameters:
     * center - {Object} MapObject LonLat format
     * zoom - {int} MapObject zoom format
     */
    setMapObjectCenter: function(center, zoom) {
        if (this.animationEnabled === false && zoom != this.mapObject.zoom) {
            var mapContainer = this.getMapContainer();
            google.maps.event.addListenerOnce(
                this.mapObject,
                "idle",
                function() {
                    mapContainer.style.visibility = "";
                }
            );
            mapContainer.style.visibility = "hidden";
        }
        this.mapObject.setOptions({
            center: center,
            zoom: zoom
        });
    },

  // Bounds

    /**
     * APIMethod: getMapObjectZoomFromMapObjectBounds
     *
     * Parameters:
     * moBounds - {Object} MapObject Bounds format
     *
     * Returns:
     * {Object} MapObject Zoom for specified MapObject Bounds
     */
    getMapObjectZoomFromMapObjectBounds: function(moBounds) {
        return this.mapObject.getBoundsZoomLevel(moBounds);
    },

    /************************************
     *                                  *
     *       MapObject Primitives       *
     *                                  *
     ************************************/

  // LonLat

    /**
     * APIMethod: getMapObjectLonLatFromLonLat
     *
     * Parameters:
     * lon - {Float}
     * lat - {Float}
     *
     * Returns:
     * {Object} MapObject LonLat built from lon and lat params
     */
    getMapObjectLonLatFromLonLat: function(lon, lat) {
        var gLatLng;
        if(this.sphericalMercator) {
            var lonlat = this.inverseMercator(lon, lat);
            gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);
        } else {
            gLatLng = new google.maps.LatLng(lat, lon);
        }
        return gLatLng;
    },

  // Pixel

    /**
     * APIMethod: getMapObjectPixelFromXY
     *
     * Parameters:
     * x - {Integer}
     * y - {Integer}
     *
     * Returns:
     * {Object} MapObject Pixel from x and y parameters
     */
    getMapObjectPixelFromXY: function(x, y) {
        return new google.maps.Point(x, y);
    },

    /**
     * APIMethod: destroy
     * Clean up this layer.
     */
    destroy: function() {
        if (this.repositionListener) {
            google.maps.event.removeListener(this.repositionListener);
        }
        if (this.repositionTimer) {
            window.clearTimeout(this.repositionTimer);
        }
        OpenLayers.Layer.Google.prototype.destroy.apply(this, arguments);
    }

};
<commit_msg>Fix Google terms/poweredby/popup<commit_after>/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
 * full list of contributors). Published under the 2-clause BSD license.
 * See license.txt in the OpenLayers distribution or repository for the
 * full text of the license. */

/**
 * @requires OpenLayers/Layer/Google.js
 */

/**
 * Constant: OpenLayers.Layer.Google.v3
 *
 * Mixin providing functionality specific to the Google Maps API v3 <= v3.6.
 * Note that this layer configures the google.maps.map object with the
 * "disableDefaultUI" option set to true. Using UI controls that the Google
 * Maps API provides is not supported by the OpenLayers API. To use this layer,
 * you must include the GMaps API (<= v3.6) in your html:
 *
 * (code)
 * <script src="http://maps.google.com/maps/api/js?v=3.6&sensor=false"></script>
 * (end)
 */
OpenLayers.Layer.Google.v3 = {

    /**
     * Constant: DEFAULTS
     * {Object} It is not recommended to change the properties set here. Note
     * that Google.v3 layers only work when sphericalMercator is set to true.
     *
     * (code)
     * {
     *     sphericalMercator: true,
     *     projection: "EPSG:900913"
     * }
     * (end)
     */
    DEFAULTS: {
        sphericalMercator: true,
        projection: "EPSG:900913"
    },

    /**
     * APIProperty: animationEnabled
     * {Boolean} If set to true, the transition between zoom levels will be
     *     animated (if supported by the GMaps API for the device used). Set to
     *     false to match the zooming experience of other layer types. Default
     *     is true. Note that the GMaps API does not give us control over zoom
     *     animation, so if set to false, when zooming, this will make the
     *     layer temporarily invisible, wait until GMaps reports the map being
     *     idle, and make it visible again. The result will be a blank layer
     *     for a few moments while zooming.
     */
    animationEnabled: true,

    /**
     * Method: loadMapObject
     * Load the GMap and register appropriate event listeners. If we can't
     *     load GMap2, then display a warning message.
     */
    loadMapObject:function() {
        if (!this.type) {
            this.type = google.maps.MapTypeId.ROADMAP;
        }
        var mapObject;
        var cache = OpenLayers.Layer.Google.cache[this.map.id];
        if (cache) {
            // there are already Google layers added to this map
            mapObject = cache.mapObject;
            // increment the layer count
            ++cache.count;
        } else {
            // this is the first Google layer for this map

            var container = this.map.viewPortDiv;
            var div = document.createElement("div");
            div.id = this.map.id + "_GMapContainer";
            div.style.position = "absolute";
            div.style.width = "100%";
            div.style.height = "100%";
            container.appendChild(div);

            // create GMap and shuffle elements
            var center = this.map.getCenter();
            mapObject = new google.maps.Map(div, {
                center: center ?
                    new google.maps.LatLng(center.lat, center.lon) :
                    new google.maps.LatLng(0, 0),
                zoom: this.map.getZoom() || 0,
                mapTypeId: this.type,
                disableDefaultUI: true,
                keyboardShortcuts: false,
                draggable: false,
                disableDoubleClickZoom: true,
                scrollwheel: false,
                streetViewControl: false
            });

            // cache elements for use by any other google layers added to
            // this same map
            cache = {
                mapObject: mapObject,
                count: 1
            };
            OpenLayers.Layer.Google.cache[this.map.id] = cache;
            this.repositionListener = google.maps.event.addListenerOnce(
                mapObject,
                "center_changed",
                OpenLayers.Function.bind(this.repositionMapElements, this)
            );
        }
        this.mapObject = mapObject;
        this.setGMapVisibility(this.visibility);
    },

    /**
     * Method: repositionMapElements
     *
     * Waits until powered by and terms of use elements are available and then
     * moves them so they are clickable.
     */
    repositionMapElements: function() {

        // This is the first time any Google layer in this mapObject has been
        // made visible.  The mapObject needs to know the container size.
        google.maps.event.trigger(this.mapObject, "resize");

        var div = this.mapObject.getDiv().firstChild;
        if (!div || div.childNodes.length < 3) {
            this.repositionTimer = window.setTimeout(
                OpenLayers.Function.bind(this.repositionMapElements, this),
                250
            );
            return false;
        }

        var cache = OpenLayers.Layer.Google.cache[this.map.id];
        var container = this.map.viewPortDiv;

        // move the ToS and branding stuff up to the container div
        // depends on value of zIndex, which is not robust
        for (var i=div.children.length-1; i>=0; --i) {
            if (div.children[i].style.zIndex == 1000001) {
                var termsOfUse = div.children[i];
                container.appendChild(termsOfUse);
                termsOfUse.style.zIndex = "1100";
                termsOfUse.style.bottom = "";
                termsOfUse.className = "olLayerGoogleCopyright olLayerGoogleV3";
                termsOfUse.style.display = "";
                cache.termsOfUse = termsOfUse;
            }
            if (div.children[i].style.zIndex == 1000000) {
                var poweredBy = div.children[i];
                container.appendChild(poweredBy);
                poweredBy.style.zIndex = "1100";
                poweredBy.style.bottom = "";
                poweredBy.className = "olLayerGooglePoweredBy olLayerGoogleV3 gmnoprint";
                poweredBy.style.display = "";
                cache.poweredBy = poweredBy;
            }
            if (div.children[i].style.zIndex == 10000002) {
                container.appendChild(div.children[i]);
            }
        }

        this.setGMapVisibility(this.visibility);

    },

    /**
     * APIMethod: onMapResize
     */
    onMapResize: function() {
        if (this.visibility) {
            google.maps.event.trigger(this.mapObject, "resize");
        } else {
            var cache = OpenLayers.Layer.Google.cache[this.map.id];
            if (!cache.resized) {
                var layer = this;
                google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function() {
                    google.maps.event.trigger(layer.mapObject, "resize");
                    layer.moveTo(layer.map.getCenter(), layer.map.getZoom());
                    delete cache.resized;
                });
            }
            cache.resized = true;
        }
    },

    /**
     * Method: setGMapVisibility
     * Display the GMap container and associated elements.
     *
     * Parameters:
     * visible - {Boolean} Display the GMap elements.
     */
    setGMapVisibility: function(visible) {
        var cache = OpenLayers.Layer.Google.cache[this.map.id];
        if (cache) {
            var type = this.type;
            var layers = this.map.layers;
            var layer;
            for (var i=layers.length-1; i>=0; --i) {
                layer = layers[i];
                if (layer instanceof OpenLayers.Layer.Google &&
                            layer.visibility === true && layer.inRange === true) {
                    type = layer.type;
                    visible = true;
                    break;
                }
            }
            var container = this.mapObject.getDiv();
            if (visible === true) {
                this.mapObject.setMapTypeId(type);
                container.style.left = "";
                if (cache.termsOfUse && cache.termsOfUse.style) {
                    cache.termsOfUse.style.left = "";
                    cache.termsOfUse.style.display = "";
                    cache.poweredBy.style.display = "";
                }
                cache.displayed = this.id;
            } else {
                delete cache.displayed;
                container.style.left = "-9999px";
                if (cache.termsOfUse && cache.termsOfUse.style) {
                    cache.termsOfUse.style.display = "none";
                    // move ToU far to the left in addition to setting
                    // display to "none", because at the end of the GMap
                    // load sequence, display: none will be unset and ToU
                    // would be visible after loading a map with a google
                    // layer that is initially hidden.
                    cache.termsOfUse.style.left = "-9999px";
                    cache.poweredBy.style.display = "none";
                }
            }
        }
    },

    /**
     * Method: getMapContainer
     *
     * Returns:
     * {DOMElement} the GMap container's div
     */
    getMapContainer: function() {
        return this.mapObject.getDiv();
    },

  //
  // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
  //

    /**
     * APIMethod: getMapObjectBoundsFromOLBounds
     *
     * Parameters:
     * olBounds - {<OpenLayers.Bounds>}
     *
     * Returns:
     * {Object} A MapObject Bounds, translated from olBounds
     *          Returns null if null value is passed in
     */
    getMapObjectBoundsFromOLBounds: function(olBounds) {
        var moBounds = null;
        if (olBounds != null) {
            var sw = this.sphericalMercator ?
              this.inverseMercator(olBounds.bottom, olBounds.left) :
              new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
            var ne = this.sphericalMercator ?
              this.inverseMercator(olBounds.top, olBounds.right) :
              new OpenLayers.LonLat(olBounds.top, olBounds.right);
            moBounds = new google.maps.LatLngBounds(
                new google.maps.LatLng(sw.lat, sw.lon),
                new google.maps.LatLng(ne.lat, ne.lon)
            );
        }
        return moBounds;
    },

    /************************************
     *                                  *
     *   MapObject Interface Controls   *
     *                                  *
     ************************************/

  // LonLat - Pixel Translation

    /**
     * APIMethod: getMapObjectLonLatFromMapObjectPixel
     *
     * Parameters:
     * moPixel - {Object} MapObject Pixel format
     *
     * Returns:
     * {Object} MapObject LonLat translated from MapObject Pixel
     */
    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
        var size = this.map.getSize();
        var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);
        var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);
        var res = this.map.getResolution();

        var delta_x = moPixel.x - (size.w / 2);
        var delta_y = moPixel.y - (size.h / 2);

        var lonlat = new OpenLayers.LonLat(
            lon + delta_x * res,
            lat - delta_y * res
        );

        if (this.wrapDateLine) {
            lonlat = lonlat.wrapDateLine(this.maxExtent);
        }
        return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);
    },

    /**
     * APIMethod: getMapObjectPixelFromMapObjectLonLat
     *
     * Parameters:
     * moLonLat - {Object} MapObject LonLat format
     *
     * Returns:
     * {Object} MapObject Pixel transtlated from MapObject LonLat
     */
    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
        var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
        var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
        var res = this.map.getResolution();
        var extent = this.map.getExtent();
        return this.getMapObjectPixelFromXY((1/res * (lon - extent.left)),
                                            (1/res * (extent.top - lat)));
    },

    /**
     * APIMethod: setMapObjectCenter
     * Set the mapObject to the specified center and zoom
     *
     * Parameters:
     * center - {Object} MapObject LonLat format
     * zoom - {int} MapObject zoom format
     */
    setMapObjectCenter: function(center, zoom) {
        if (this.animationEnabled === false && zoom != this.mapObject.zoom) {
            var mapContainer = this.getMapContainer();
            google.maps.event.addListenerOnce(
                this.mapObject,
                "idle",
                function() {
                    mapContainer.style.visibility = "";
                }
            );
            mapContainer.style.visibility = "hidden";
        }
        this.mapObject.setOptions({
            center: center,
            zoom: zoom
        });
    },

  // Bounds

    /**
     * APIMethod: getMapObjectZoomFromMapObjectBounds
     *
     * Parameters:
     * moBounds - {Object} MapObject Bounds format
     *
     * Returns:
     * {Object} MapObject Zoom for specified MapObject Bounds
     */
    getMapObjectZoomFromMapObjectBounds: function(moBounds) {
        return this.mapObject.getBoundsZoomLevel(moBounds);
    },

    /************************************
     *                                  *
     *       MapObject Primitives       *
     *                                  *
     ************************************/

  // LonLat

    /**
     * APIMethod: getMapObjectLonLatFromLonLat
     *
     * Parameters:
     * lon - {Float}
     * lat - {Float}
     *
     * Returns:
     * {Object} MapObject LonLat built from lon and lat params
     */
    getMapObjectLonLatFromLonLat: function(lon, lat) {
        var gLatLng;
        if(this.sphericalMercator) {
            var lonlat = this.inverseMercator(lon, lat);
            gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);
        } else {
            gLatLng = new google.maps.LatLng(lat, lon);
        }
        return gLatLng;
    },

  // Pixel

    /**
     * APIMethod: getMapObjectPixelFromXY
     *
     * Parameters:
     * x - {Integer}
     * y - {Integer}
     *
     * Returns:
     * {Object} MapObject Pixel from x and y parameters
     */
    getMapObjectPixelFromXY: function(x, y) {
        return new google.maps.Point(x, y);
    },

    /**
     * APIMethod: destroy
     * Clean up this layer.
     */
    destroy: function() {
        if (this.repositionListener) {
            google.maps.event.removeListener(this.repositionListener);
        }
        if (this.repositionTimer) {
            window.clearTimeout(this.repositionTimer);
        }
        OpenLayers.Layer.Google.prototype.destroy.apply(this, arguments);
    }

};
<|endoftext|>


<commit_before>/* global THREE, dat, modifiers */
(function() {
  'use strict';

  var container;

  var scene, camera, renderer;
  var views;

  var baseGeometry;
  var geometry, material, mesh;
  var wireframe;

  var textarea = document.getElementById( 'textarea' );
  var viewports = document.getElementById( 'viewports' );

  var config = {
    widthSegments: 8,
    heightSegments: 8,
    depthSegments: 8
  };

  var transformVertex = function() {};

  function createWireframe() {
    if ( wireframe && wireframe.parent ) {
      wireframe.parent.remove( wireframe );
    }

    wireframe = new THREE.WireframeHelper( mesh );
    scene.add( wireframe );
  }

  function transformGeometry() {
    var _geometry = new THREE.Geometry().copy( baseGeometry );
    _geometry.vertices.forEach( transformVertex );
    _geometry.computeFaceNormals();
    _geometry.computeVertexNormals();

    geometry = _geometry;
    mesh.geometry = geometry;
    mesh.needsUpdate = true;

    createWireframe( mesh );

    render();
  }

  function onInput() {
    var lines = textarea.value.split( '\n' );

    var x = lines[0] || 'x';
    var y = lines[1] || 'y';
    var z = lines[2] || 'z';

    try {
      var fn = new Function(
        [ 'x', 'y', 'z', 'xt', 'yt', 'zt' ],
        'return [' + x + ', ' + y + ', ' + z + '];'
      );

      transformVertex = modifiers.parametric( baseGeometry, function( vector, xt, yt, zt ) {
        vector.fromArray( fn( vector.x, vector.y, vector.z, xt, yt, zt ) );
      });

      transformGeometry();
    } catch ( error ) {
      console.error( error );
    }
  }

  function init() {
    container = document.createElement( 'div' );
    document.body.appendChild( container );

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    container.appendChild( renderer.domElement );

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight );
    camera.position.set( 0, 1, 2 );

    views = [ 'x', 'y', 'z' ].reduce(function( views, axis ) {
      var _renderer = new THREE.WebGLRenderer({ antialias: true });
      _renderer.setPixelRatio( window.devicePixelRatio );
      _renderer.setSize( 128, 128 );
      viewports.appendChild( _renderer.domElement );

      var size = 1;
      var _camera = new THREE.OrthographicCamera( -size, size, size, -size );
      _camera.position[ axis ] = 2;
      _camera.lookAt( new THREE.Vector3() );

      views[ axis ] = {
        renderer: _renderer,
        camera: _camera
      };

      return views;
    }, {} );

    var controls = new THREE.OrbitControls( camera, renderer.domElement );
    controls.addEventListener( 'change', render );

    baseGeometry = new THREE.BoxGeometry(
      1, 1, 1,
      config.widthSegments, config.heightSegments, config.depthSegments
    );

    geometry = baseGeometry;
    material = new THREE.MeshStandardMaterial({ shading: THREE.FlatShading });
    mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );

    createWireframe( mesh );

    var light = new THREE.DirectionalLight();
    light.position.set( 8, 8, 0 );
    scene.add( light );

    scene.add( new THREE.AmbientLight( '#333' ) );

    textarea.addEventListener( 'input', onInput );

    // Prevent input from toggling dat.gui via hide shortcut ('h').
    textarea.addEventListener( 'keydown', function( event ) {
      event.stopPropagation();
    });

    var gui = new dat.GUI();

    [ 'widthSegments', 'heightSegments', 'depthSegments' ].forEach(function( segmentCount ) {
      gui.add( config, segmentCount, 1, 16 )
        .step( 1 )
        .listen()
        .onChange(function( count ) {
          config[ segmentCount ] = count;

          baseGeometry = new THREE.BoxGeometry(
            1, 1, 1,
            config.widthSegments, config.heightSegments, config.depthSegments
          );

          transformGeometry();
        });
    })
  }

  function render() {
    renderer.render( scene, camera );

    Object.keys( views ).forEach(function( key ) {
      var view = views[ key ];
      view.renderer.render( scene, view.camera );
    });
  }

  init();
  render();

  window.addEventListener( 'resize', function() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize( window.innerWidth, window.innerHeight );
    render();
  });

})();
<commit_msg>lathe[modifiers]: Sync window.location.hash with input.<commit_after>/* global THREE, dat, modifiers */
(function() {
  'use strict';

  var container;

  var scene, camera, renderer;
  var views;

  var baseGeometry;
  var geometry, material, mesh;
  var wireframe;

  var textarea = document.getElementById( 'textarea' );
  var viewports = document.getElementById( 'viewports' );

  var config = {
    widthSegments: 8,
    heightSegments: 8,
    depthSegments: 8
  };

  var transformVertex = function() {};

  // Set from window.location.hash.
  var hash = decodeURIComponent( window.location.hash ).slice( 1 );
  textarea.value = hash || '';
  onInput();

  function createWireframe() {
    if ( wireframe && wireframe.parent ) {
      wireframe.parent.remove( wireframe );
    }

    wireframe = new THREE.WireframeHelper( mesh );
    scene.add( wireframe );
  }

  function transformGeometry() {
    var _geometry = new THREE.Geometry().copy( baseGeometry );
    _geometry.vertices.forEach( transformVertex );
    _geometry.computeFaceNormals();
    _geometry.computeVertexNormals();

    geometry = _geometry;
    mesh.geometry = geometry;
    mesh.needsUpdate = true;

    createWireframe( mesh );

    render();
  }

  function onInput() {
    var lines = textarea.value.split( '\n' );

    var x = lines[0] || 'x';
    var y = lines[1] || 'y';
    var z = lines[2] || 'z';

    try {
      var fn = new Function(
        [ 'x', 'y', 'z', 'xt', 'yt', 'zt' ],
        'return [' + x + ', ' + y + ', ' + z + '];'
      );

      transformVertex = modifiers.parametric( baseGeometry, function( vector, xt, yt, zt ) {
        vector.fromArray( fn( vector.x, vector.y, vector.z, xt, yt, zt ) );
      });

      transformGeometry();
    } catch ( error ) {
      console.error( error );
      return;
    }

    // Update location.
    var hash = (
      window.location.origin +
      window.location.pathname +
      '#' + encodeURIComponent( textarea.value )
    );

    window.history.replaceState( '', '', hash );
  }

  function init() {
    container = document.createElement( 'div' );
    document.body.appendChild( container );

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    container.appendChild( renderer.domElement );

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight );
    camera.position.set( 0, 1, 2 );

    views = [ 'x', 'y', 'z' ].reduce(function( views, axis ) {
      var _renderer = new THREE.WebGLRenderer({ antialias: true });
      _renderer.setPixelRatio( window.devicePixelRatio );
      _renderer.setSize( 128, 128 );
      viewports.appendChild( _renderer.domElement );

      var size = 1;
      var _camera = new THREE.OrthographicCamera( -size, size, size, -size );
      _camera.position[ axis ] = 2;
      _camera.lookAt( new THREE.Vector3() );

      views[ axis ] = {
        renderer: _renderer,
        camera: _camera
      };

      return views;
    }, {} );

    var controls = new THREE.OrbitControls( camera, renderer.domElement );
    controls.addEventListener( 'change', render );

    baseGeometry = new THREE.BoxGeometry(
      1, 1, 1,
      config.widthSegments, config.heightSegments, config.depthSegments
    );

    geometry = baseGeometry;
    material = new THREE.MeshStandardMaterial({ shading: THREE.FlatShading });
    mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );

    createWireframe( mesh );

    var light = new THREE.DirectionalLight();
    light.position.set( 8, 8, 0 );
    scene.add( light );

    scene.add( new THREE.AmbientLight( '#333' ) );

    textarea.addEventListener( 'input', onInput );

    // Prevent input from toggling dat.gui via hide shortcut ('h').
    textarea.addEventListener( 'keydown', function( event ) {
      event.stopPropagation();
    });

    var gui = new dat.GUI();

    [ 'widthSegments', 'heightSegments', 'depthSegments' ].forEach(function( segmentCount ) {
      gui.add( config, segmentCount, 1, 16 )
        .step( 1 )
        .listen()
        .onChange(function( count ) {
          config[ segmentCount ] = count;

          baseGeometry = new THREE.BoxGeometry(
            1, 1, 1,
            config.widthSegments, config.heightSegments, config.depthSegments
          );

          transformGeometry();
        });
    })
  }

  function render() {
    renderer.render( scene, camera );

    Object.keys( views ).forEach(function( key ) {
      var view = views[ key ];
      view.renderer.render( scene, view.camera );
    });
  }

  init();
  render();

  window.addEventListener( 'resize', function() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize( window.innerWidth, window.innerHeight );
    render();
  });

})();
<|endoftext|>


<commit_before>export default class Listeners {

  constructor(server, port) {

    this.server = server;

    this.port = port;

    this.init();
  }

  /**
   * Initialization
   * @return {none}
   */
  init() {

    if (!this.server || !this.port) {

      console.error('Listeners class requireвd server and port params');
      return;
    }
  }

  /**
   * On error callback
   * @param  {Object} error
   * @return {none}
   */
  onError(error) {
    if (error.syscall !== 'listen') {
      throw error;
    }

    let bind = (typeof this.port === 'string')
      ? 'Pipe ' + this.port
      : 'Port ' + this.port;

    /**
     * Handle specific listen
     * errors with friendly messages
     */
    switch (error.code) {
      case 'EACCES':
      {
        console.error(bind + ' requires elevated privileges');
        process.exit(1);
        break;
      }
      case 'EADDRINUSE':
      {
        console.error(bind + ' is already in use');
        process.exit(1);
        break;
      }
      default:
        throw error;
    }
  }

  /**
   * Event listener for HTTP
   * server "listening" event.
   */
  onListening() {
    let addr = this.server.address();
    let bind = (typeof addr === 'string')
      ? 'pipe ' + addr
      : 'port ' + addr.port;

    console.log('Listening on ' + bind);
  }
}
<commit_msg>Fixed lints errors<commit_after>export default class Listeners {

  constructor(server, port) {

    this.server = server;

    this.port = port;

    this.init();
  }

  /**
   * Initialization
   * @return {none}
   */
  init() {

    if (!this.server || !this.port) {

      console.error('Listeners class requireвd server and port params');
      return;
    }
  }

  /**
   * On error callback
   * @param  {Object} error
   * @return {none}
   */
  onError(error) {
    if (error.syscall !== 'listen') {
      throw error;
    }

    let bind = (typeof this.port === 'string') ? 'Pipe ' + this.port : 'Port ' + this.port;

    /**
     * Handle specific listen
     * errors with friendly messages
     */
    switch (error.code) {
      case 'EACCES':
      {
        console.error(bind + ' requires elevated privileges');
        process.exit(1);
        break;
      }
      case 'EADDRINUSE':
      {
        console.error(bind + ' is already in use');
        process.exit(1);
        break;
      }
      default:
        throw error;
    }
  }

  /**
   * Event listener for HTTP
   * server "listening" event.
   */
  onListening() {
    let addr = this.server.address();
    let bind = (typeof addr === 'string') ? 'pipe ' + addr : 'port ' + addr.port;

    console.log('Listening on ' + bind);
  }
}
<|endoftext|>


<commit_before>import Stack from './stack';
import Card from './card';

global.gajus = global.gajus || {};

global.gajus.Swing = {
    Stack,
    Card
};

export {
    Stack,
    Card
};
<commit_msg>Do not export anything to global environment.<commit_after>import Stack from './stack';
import Card from './card';

export {
    Stack,
    Card
};
<|endoftext|>


<commit_before>const { app } = require('electron');
const { BrowserWindow } = require('electron');
const { protocol } = require('electron');
const path = require('path');
const cProcess = require('child_process').spawn;
const portscanner = require('portscanner');
const imageSize = require('image-size');
let io, server, browserWindows, ipc, apiProcess, loadURL;
let appApi, menu, dialogApi, notification, tray, webContents;
let globalShortcut, shellApi, screen, clipboard, autoUpdater;
let commandLine, browserView;
let powerMonitor;
let splashScreen, hostHook;
let mainWindowId, nativeTheme;
let dock;
let launchFile;
let launchUrl;

let manifestJsonFileName = 'electron.manifest.json';
let watchable = false;
if (app.commandLine.hasSwitch('manifest')) {
    manifestJsonFileName = app.commandLine.getSwitchValue('manifest');
};

if (app.commandLine.hasSwitch('watch')) {
    watchable = true;
};

let currentBinPath = path.join(__dirname.replace('app.asar', ''), 'bin');
let manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);

// if watch is enabled lets change the path
if (watchable) {
    currentBinPath = path.join(__dirname, '../../'); // go to project directory
    manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);
}

//  handle macOS events for opening the app with a file, etc
app.on('will-finish-launching', () => {
    app.on('open-file', (evt, file) => {
        evt.preventDefault();
        launchFile = file;
    })
    app.on('open-url', (evt, url) => {
        evt.preventDefault();
        launchUrl = url;
    })
});

const manifestJsonFile = require(manifestJsonFilePath);
if (manifestJsonFile.singleInstance || manifestJsonFile.aspCoreBackendPort) {
    const mainInstance = app.requestSingleInstanceLock();
    app.on('second-instance', () => {
        const windows = BrowserWindow.getAllWindows();
        if (windows.length) {
            if (windows[0].isMinimized()) {
                windows[0].restore();
            }
            windows[0].focus();
        }
    });

    if (!mainInstance) {
        app.quit();
    }
}

app.on('ready', () => {

    // Fix ERR_UNKNOWN_URL_SCHEME using file protocol
    // https://github.com/electron/electron/issues/23757
    protocol.registerFileProtocol('file', (request, callback) => {
        const pathname = request.url.replace('file:///', '');
        callback(pathname);
    });

    if (isSplashScreenEnabled()) {
        startSplashScreen();
    }
    // Added default port as configurable for port restricted environments.
    let defaultElectronPort = 8000;
    if (manifestJsonFile.electronPort) {
        defaultElectronPort = (manifestJsonFile.electronPort)
    }
    // hostname needs to be localhost, otherwise Windows Firewall will be triggered.
    portscanner.findAPortNotInUse(defaultElectronPort, 65535, 'localhost', function (error, port) {
        console.log('Electron Socket IO Port: ' + port);
        startSocketApiBridge(port);
    });
});

app.on('quit', async (event, exitCode) => {
    await server.close();
    apiProcess.kill();
});

function isSplashScreenEnabled() {
    if (manifestJsonFile.hasOwnProperty('splashscreen')) {
        if (manifestJsonFile.splashscreen.hasOwnProperty('imageFile')) {
            return Boolean(manifestJsonFile.splashscreen.imageFile);
        }
    }

    return false;
}

function startSplashScreen() {
    let imageFile = path.join(currentBinPath, manifestJsonFile.splashscreen.imageFile);
    imageSize(imageFile, (error, dimensions) => {
        if (error) {
            console.log(`load splashscreen error:`);
            console.error(error);

            throw new Error(error.message);
        }

        splashScreen = new BrowserWindow({
            width: dimensions.width,
            height: dimensions.height,
            transparent: true,
            center: true,
            frame: false,
            closable: false,
            resizable: false,
            skipTaskbar: true,
            show: true
        });

        app.once('browser-window-created', () => {
            splashScreen.destroy();
        });

        const loadSplashscreenUrl = path.join(__dirname, 'splashscreen', 'index.html') + '?imgPath=' + imageFile;
        splashScreen.loadURL('file://' + loadSplashscreenUrl);

        splashScreen.once('closed', () => {
            splashScreen = null;
        });
    });
}

function startSocketApiBridge(port) {

    // instead of 'require('socket.io')(port);' we need to use this workaround
    // otherwise the Windows Firewall will be triggered
    server = require('http').createServer();
    io = require('socket.io')();
    io.attach(server);

    server.listen(port, 'localhost');
    server.on('listening', function () {
        console.log('Electron Socket started on port %s at %s', server.address().port, server.address().address);
        // Now that socket connection is established, we can guarantee port will not be open for portscanner
        if (watchable) {
            startAspCoreBackendWithWatch(port);
        } else {
            startAspCoreBackend(port);
        }
    });

    // prototype
    app['mainWindowURL'] = "";
    app['mainWindow'] = null;

    io.on('connection', (socket) => {

        socket.on('disconnect', function (reason) {
            console.log('Got disconnect! Reason: ' + reason);
            try {
                if (hostHook) {
                    const hostHookScriptFilePath = path.join(__dirname, 'ElectronHostHook', 'index.js');
                    delete require.cache[require.resolve(hostHookScriptFilePath)];
                    hostHook = undefined;
                }

            } catch (error) {
                console.error(error.message);
            }
        });

        if (global['electronsocket'] === undefined) {
            global['electronsocket'] = socket;
            global['electronsocket'].setMaxListeners(0);
        }

        console.log('ASP.NET Core Application connected...', 'global.electronsocket', global['electronsocket'].id, new Date());

        if (appApi === undefined) appApi = require('./api/app')(socket, app);
        if (browserWindows === undefined) browserWindows = require('./api/browserWindows')(socket, app);
        if (commandLine === undefined) commandLine = require('./api/commandLine')(socket, app);
        if (autoUpdater === undefined) autoUpdater = require('./api/autoUpdater')(socket);
        if (ipc === undefined) ipc = require('./api/ipc')(socket);
        if (menu === undefined) menu = require('./api/menu')(socket);
        if (dialogApi === undefined) dialogApi = require('./api/dialog')(socket);
        if (notification === undefined) notification = require('./api/notification')(socket);
        if (tray === undefined) tray = require('./api/tray')(socket);
        if (webContents === undefined) webContents = require('./api/webContents')(socket);
        if (globalShortcut === undefined) globalShortcut = require('./api/globalShortcut')(socket);
        if (shellApi === undefined) shellApi = require('./api/shell')(socket);
        if (screen === undefined) screen = require('./api/screen')(socket);
        if (clipboard === undefined) clipboard = require('./api/clipboard')(socket);
        if (browserView === undefined) browserView = require('./api/browserView').browserViewApi(socket);
        if (powerMonitor === undefined) powerMonitor = require('./api/powerMonitor')(socket);
        if (nativeTheme === undefined) nativeTheme = require('./api/nativeTheme')(socket);
        if (dock === undefined) dock = require('./api/dock')(socket);

        socket.on('register-app-open-file-event', (id) => {
            electronSocket = socket;

            app.on('open-file', (event, file) => {
                event.preventDefault();

                electronSocket.emit('app-open-file' + id, file);
            });

            if (launchFile) {
                electronSocket.emit('app-open-file' + id, launchFile);
            }
        });

        socket.on('register-app-open-url-event', (id) => {
            electronSocket = socket;

            app.on('open-url', (event, url) => {
                event.preventDefault();

                electronSocket.emit('app-open-url' + id, url);
            });

            if (launchUrl) {
                electronSocket.emit('app-open-url' + id, launchUrl);
            }
        });

        try {
            const hostHookScriptFilePath = path.join(__dirname, 'ElectronHostHook', 'index.js');

            if (isModuleAvailable(hostHookScriptFilePath) && hostHook === undefined) {
                const { HookService } = require(hostHookScriptFilePath);
                hostHook = new HookService(socket, app);
                hostHook.onHostReady();
            }
        } catch (error) {
            console.error(error.message);
        }
    });
}

function isModuleAvailable(name) {
    try {
        require.resolve(name);
        return true;
    } catch (e) { }
    return false;
}

function startAspCoreBackend(electronPort) {
    if (manifestJsonFile.aspCoreBackendPort) {
        startBackend(manifestJsonFile.aspCoreBackendPort)
    } else {
        // hostname needs to be localhost, otherwise Windows Firewall will be triggered.
        portscanner.findAPortNotInUse(electronPort + 1, 65535, 'localhost', function (error, electronWebPort) {
            startBackend(electronWebPort);
        });
    }

    function startBackend(aspCoreBackendPort) {
        console.log('ASP.NET Core Port: ' + aspCoreBackendPort);
        loadURL = `http://localhost:${aspCoreBackendPort}`;
        const parameters = [getEnvironmentParameter(), `/electronPort=${electronPort}`, `/electronWebPort=${aspCoreBackendPort}`];
        let binaryFile = manifestJsonFile.executable;

        const os = require('os');
        if (os.platform() === 'win32') {
            binaryFile = binaryFile + '.exe';
        }

        let binFilePath = path.join(currentBinPath, binaryFile);
        var options = { cwd: currentBinPath };
        apiProcess = cProcess(binFilePath, parameters, options);

        apiProcess.stdout.on('data', (data) => {
            console.log(`stdout: ${data.toString()}`);
        });
    }
}

function startAspCoreBackendWithWatch(electronPort) {
    if (manifestJsonFile.aspCoreBackendPort) {
        startBackend(manifestJsonFile.aspCoreBackendPort)
    } else {
        // hostname needs to be localhost, otherwise Windows Firewall will be triggered.
        portscanner.findAPortNotInUse(electronPort + 1, 65535, 'localhost', function (error, electronWebPort) {
            startBackend(electronWebPort);
        });
    }

    function startBackend(aspCoreBackendPort) {
        console.log('ASP.NET Core Watch Port: ' + aspCoreBackendPort);
        loadURL = `http://localhost:${aspCoreBackendPort}`;
        const parameters = ['watch', 'run', getEnvironmentParameter(), `/electronPort=${electronPort}`, `/electronWebPort=${aspCoreBackendPort}`];

        var options = {
            cwd: currentBinPath,
            env: process.env,
        };
        apiProcess = cProcess('dotnet', parameters, options);

        apiProcess.stdout.on('data', (data) => {
            console.log(`stdout: ${data.toString()}`);
        });
    }
}

function getEnvironmentParameter() {
    if (manifestJsonFile.environment) {
        return '--environment=' + manifestJsonFile.environment;
    }

    return '';
}
<commit_msg>Fully ignore mouse events on splash screen (avoids drag + drop file into window)<commit_after>const { app } = require('electron');
const { BrowserWindow } = require('electron');
const { protocol } = require('electron');
const path = require('path');
const cProcess = require('child_process').spawn;
const portscanner = require('portscanner');
const imageSize = require('image-size');
let io, server, browserWindows, ipc, apiProcess, loadURL;
let appApi, menu, dialogApi, notification, tray, webContents;
let globalShortcut, shellApi, screen, clipboard, autoUpdater;
let commandLine, browserView;
let powerMonitor;
let splashScreen, hostHook;
let mainWindowId, nativeTheme;
let dock;
let launchFile;
let launchUrl;

let manifestJsonFileName = 'electron.manifest.json';
let watchable = false;
if (app.commandLine.hasSwitch('manifest')) {
    manifestJsonFileName = app.commandLine.getSwitchValue('manifest');
};

if (app.commandLine.hasSwitch('watch')) {
    watchable = true;
};

let currentBinPath = path.join(__dirname.replace('app.asar', ''), 'bin');
let manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);

// if watch is enabled lets change the path
if (watchable) {
    currentBinPath = path.join(__dirname, '../../'); // go to project directory
    manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);
}

//  handle macOS events for opening the app with a file, etc
app.on('will-finish-launching', () => {
    app.on('open-file', (evt, file) => {
        evt.preventDefault();
        launchFile = file;
    })
    app.on('open-url', (evt, url) => {
        evt.preventDefault();
        launchUrl = url;
    })
});

const manifestJsonFile = require(manifestJsonFilePath);
if (manifestJsonFile.singleInstance || manifestJsonFile.aspCoreBackendPort) {
    const mainInstance = app.requestSingleInstanceLock();
    app.on('second-instance', () => {
        const windows = BrowserWindow.getAllWindows();
        if (windows.length) {
            if (windows[0].isMinimized()) {
                windows[0].restore();
            }
            windows[0].focus();
        }
    });

    if (!mainInstance) {
        app.quit();
    }
}

app.on('ready', () => {

    // Fix ERR_UNKNOWN_URL_SCHEME using file protocol
    // https://github.com/electron/electron/issues/23757
    protocol.registerFileProtocol('file', (request, callback) => {
        const pathname = request.url.replace('file:///', '');
        callback(pathname);
    });

    if (isSplashScreenEnabled()) {
        startSplashScreen();
    }
    // Added default port as configurable for port restricted environments.
    let defaultElectronPort = 8000;
    if (manifestJsonFile.electronPort) {
        defaultElectronPort = (manifestJsonFile.electronPort)
    }
    // hostname needs to be localhost, otherwise Windows Firewall will be triggered.
    portscanner.findAPortNotInUse(defaultElectronPort, 65535, 'localhost', function (error, port) {
        console.log('Electron Socket IO Port: ' + port);
        startSocketApiBridge(port);
    });
});

app.on('quit', async (event, exitCode) => {
    await server.close();
    apiProcess.kill();
});

function isSplashScreenEnabled() {
    if (manifestJsonFile.hasOwnProperty('splashscreen')) {
        if (manifestJsonFile.splashscreen.hasOwnProperty('imageFile')) {
            return Boolean(manifestJsonFile.splashscreen.imageFile);
        }
    }

    return false;
}

function startSplashScreen() {
    let imageFile = path.join(currentBinPath, manifestJsonFile.splashscreen.imageFile);
    imageSize(imageFile, (error, dimensions) => {
        if (error) {
            console.log(`load splashscreen error:`);
            console.error(error);

            throw new Error(error.message);
        }

        splashScreen = new BrowserWindow({
            width: dimensions.width,
            height: dimensions.height,
            transparent: true,
            center: true,
            frame: false,
            closable: false,
            resizable: false,
            skipTaskbar: true,
            show: true
        });
        splashScreen.setIgnoreMouseEvents(true);

        app.once('browser-window-created', () => {
            splashScreen.destroy();
        });

        const loadSplashscreenUrl = path.join(__dirname, 'splashscreen', 'index.html') + '?imgPath=' + imageFile;
        splashScreen.loadURL('file://' + loadSplashscreenUrl);

        splashScreen.once('closed', () => {
            splashScreen = null;
        });
    });
}

function startSocketApiBridge(port) {

    // instead of 'require('socket.io')(port);' we need to use this workaround
    // otherwise the Windows Firewall will be triggered
    server = require('http').createServer();
    io = require('socket.io')();
    io.attach(server);

    server.listen(port, 'localhost');
    server.on('listening', function () {
        console.log('Electron Socket started on port %s at %s', server.address().port, server.address().address);
        // Now that socket connection is established, we can guarantee port will not be open for portscanner
        if (watchable) {
            startAspCoreBackendWithWatch(port);
        } else {
            startAspCoreBackend(port);
        }
    });

    // prototype
    app['mainWindowURL'] = "";
    app['mainWindow'] = null;

    io.on('connection', (socket) => {

        socket.on('disconnect', function (reason) {
            console.log('Got disconnect! Reason: ' + reason);
            try {
                if (hostHook) {
                    const hostHookScriptFilePath = path.join(__dirname, 'ElectronHostHook', 'index.js');
                    delete require.cache[require.resolve(hostHookScriptFilePath)];
                    hostHook = undefined;
                }

            } catch (error) {
                console.error(error.message);
            }
        });

        if (global['electronsocket'] === undefined) {
            global['electronsocket'] = socket;
            global['electronsocket'].setMaxListeners(0);
        }

        console.log('ASP.NET Core Application connected...', 'global.electronsocket', global['electronsocket'].id, new Date());

        if (appApi === undefined) appApi = require('./api/app')(socket, app);
        if (browserWindows === undefined) browserWindows = require('./api/browserWindows')(socket, app);
        if (commandLine === undefined) commandLine = require('./api/commandLine')(socket, app);
        if (autoUpdater === undefined) autoUpdater = require('./api/autoUpdater')(socket);
        if (ipc === undefined) ipc = require('./api/ipc')(socket);
        if (menu === undefined) menu = require('./api/menu')(socket);
        if (dialogApi === undefined) dialogApi = require('./api/dialog')(socket);
        if (notification === undefined) notification = require('./api/notification')(socket);
        if (tray === undefined) tray = require('./api/tray')(socket);
        if (webContents === undefined) webContents = require('./api/webContents')(socket);
        if (globalShortcut === undefined) globalShortcut = require('./api/globalShortcut')(socket);
        if (shellApi === undefined) shellApi = require('./api/shell')(socket);
        if (screen === undefined) screen = require('./api/screen')(socket);
        if (clipboard === undefined) clipboard = require('./api/clipboard')(socket);
        if (browserView === undefined) browserView = require('./api/browserView').browserViewApi(socket);
        if (powerMonitor === undefined) powerMonitor = require('./api/powerMonitor')(socket);
        if (nativeTheme === undefined) nativeTheme = require('./api/nativeTheme')(socket);
        if (dock === undefined) dock = require('./api/dock')(socket);

        socket.on('register-app-open-file-event', (id) => {
            electronSocket = socket;

            app.on('open-file', (event, file) => {
                event.preventDefault();

                electronSocket.emit('app-open-file' + id, file);
            });

            if (launchFile) {
                electronSocket.emit('app-open-file' + id, launchFile);
            }
        });

        socket.on('register-app-open-url-event', (id) => {
            electronSocket = socket;

            app.on('open-url', (event, url) => {
                event.preventDefault();

                electronSocket.emit('app-open-url' + id, url);
            });

            if (launchUrl) {
                electronSocket.emit('app-open-url' + id, launchUrl);
            }
        });

        try {
            const hostHookScriptFilePath = path.join(__dirname, 'ElectronHostHook', 'index.js');

            if (isModuleAvailable(hostHookScriptFilePath) && hostHook === undefined) {
                const { HookService } = require(hostHookScriptFilePath);
                hostHook = new HookService(socket, app);
                hostHook.onHostReady();
            }
        } catch (error) {
            console.error(error.message);
        }
    });
}

function isModuleAvailable(name) {
    try {
        require.resolve(name);
        return true;
    } catch (e) { }
    return false;
}

function startAspCoreBackend(electronPort) {
    if (manifestJsonFile.aspCoreBackendPort) {
        startBackend(manifestJsonFile.aspCoreBackendPort)
    } else {
        // hostname needs to be localhost, otherwise Windows Firewall will be triggered.
        portscanner.findAPortNotInUse(electronPort + 1, 65535, 'localhost', function (error, electronWebPort) {
            startBackend(electronWebPort);
        });
    }

    function startBackend(aspCoreBackendPort) {
        console.log('ASP.NET Core Port: ' + aspCoreBackendPort);
        loadURL = `http://localhost:${aspCoreBackendPort}`;
        const parameters = [getEnvironmentParameter(), `/electronPort=${electronPort}`, `/electronWebPort=${aspCoreBackendPort}`];
        let binaryFile = manifestJsonFile.executable;

        const os = require('os');
        if (os.platform() === 'win32') {
            binaryFile = binaryFile + '.exe';
        }

        let binFilePath = path.join(currentBinPath, binaryFile);
        var options = { cwd: currentBinPath };
        apiProcess = cProcess(binFilePath, parameters, options);

        apiProcess.stdout.on('data', (data) => {
            console.log(`stdout: ${data.toString()}`);
        });
    }
}

function startAspCoreBackendWithWatch(electronPort) {
    if (manifestJsonFile.aspCoreBackendPort) {
        startBackend(manifestJsonFile.aspCoreBackendPort)
    } else {
        // hostname needs to be localhost, otherwise Windows Firewall will be triggered.
        portscanner.findAPortNotInUse(electronPort + 1, 65535, 'localhost', function (error, electronWebPort) {
            startBackend(electronWebPort);
        });
    }

    function startBackend(aspCoreBackendPort) {
        console.log('ASP.NET Core Watch Port: ' + aspCoreBackendPort);
        loadURL = `http://localhost:${aspCoreBackendPort}`;
        const parameters = ['watch', 'run', getEnvironmentParameter(), `/electronPort=${electronPort}`, `/electronWebPort=${aspCoreBackendPort}`];

        var options = {
            cwd: currentBinPath,
            env: process.env,
        };
        apiProcess = cProcess('dotnet', parameters, options);

        apiProcess.stdout.on('data', (data) => {
            console.log(`stdout: ${data.toString()}`);
        });
    }
}

function getEnvironmentParameter() {
    if (manifestJsonFile.environment) {
        return '--environment=' + manifestJsonFile.environment;
    }

    return '';
}
<|endoftext|>


<commit_before>(function () {
	var newApp = angular.module('newApp', ['ngRoute']);

	newApp.config(function ($routeProvider) {
		$routeProvider

		.when('/', {
			templateUrl: 'pages/index.html',
			controller: 'mainControl'
		})

		.when('/about', {
			templateUrl: 'pages/about.html',
			controller: 'aboutControl'
		})
	});

	newApp.controller('TabController', function() {
		this.tab = 1;

	 	this.setTab = function(newValue){
			this.tab = newValue;
	 	};

	 	this.isSet = function(tabName){
			return this.tab === tabName;
	 	};
	});

	newApp.controller('GalleryController', function() {
	 	this.current = 0;
	 	this.setCurrent = function(newGallery){
			this.current = newGallery || 0;
	 	};
	});

	newApp.controller('ReviewController', function () {
		this.review = {};

		this.addReview = function (prd) {
			prd.reviews.push(this.review);
			this.review = {};// create a brand new review object. This clear out form and hide live preview
		};
	});

	newApp.controller('StoreController', ['$http', function ($http) {
		var store = this;

		store.products = [];

		$http.get('server/products.json').success(function (data) {
			store.products = data;
		});
	}]);
})();
<commit_msg>Update app.js<commit_after>(function () {
	var newApp = angular.module('newApp', ['ngRoute']);

	newApp.config(function ($routeProvider) {
		$routeProvider

		.when('/', {
			templateUrl: 'pages/index.html',
			controller: 'mainControl'
		})

		.when('/about', {
			templateUrl: 'pages/about.html',
			controller: 'aboutControl'
		})
	});

	newApp.controller('TabController', function() {
		this.tab = 1;

	 	this.setTab = function(newValue){
			this.tab = newValue;
	 	};

	 	this.isSet = function(tabName){
			return this.tab === tabName;
	 	};
	});

	newApp.controller('GalleryController', function() {
	 	this.current = 0;
	 	this.setCurrent = function(newGallery){
			this.current = newGallery || 0;
	 	};
	});

	newApp.controller('ReviewController', function () {
		this.review = {};

		this.addReview = function (prd) {
			prd.reviews.push(this.review);
			this.review = {};// create a brand new review object. This clear out form and hide live preview
		};
	});

	// connect with file
	newApp.controller('StoreController', ['$http', function ($http) {
		var store = this;

		store.products = [];

		$http.get('server/products.json').success(function (data) {
			store.products = data;
		});
	}]);
})();
<|endoftext|>