<commit_before>/**
 * rowcol - a library for row-column data manipulation
 * @module rowcol
 * @author Samuel Rouse <samuel.rouse@gmail.com>
 * @license MIT http://opensource.org/licenses/MIT
 */

/*--------------------------------------------------------------------------*/
/*
 * Constants and basic settings
 * Structure and code from Lo-Dash 2.4.1 <http://lodash.com/>
 */

const version = '3.0.0';

/*--------------------------------------------------------------------------*/

/* Functions */
const { has } = require('./util');

/**
 * Indicates if the passed object is an array
 * @param {*} obj
 * @returns {boolean}
 */
function isArray(obj) {
  return Object.prototype.toString.call(obj) === '[object Array]';
}

function getIndexesWithFunction(array, filter) {
  const indexes = [];
  const len = array.length;
  let i;

  for (i = 0; i < len; i += 1) {
    if (filter(array[i], i)) {
      indexes.push(i);
    }
  }

  return indexes;
}

function getIndexesWithValue(array, filter) {
  const indexes = [];
  const len = array.length;
  let i;

  for (i = 0; i < len; i += 1) {
    if (array[i] === filter) {
      indexes.push(i);
    }
  }

  return indexes;
}

/**
 * Returns an array of indexes that match a filter string or function.
 * @param {Array} array Array of values to match
 * @param {Function|String} filter Function or String to match array values.
 * @returns {Array} Array of indexes that match the filter.
 */
function getIndexes(array, filter) {
  if (typeof filter === 'function') {
    return getIndexesWithFunction(array, filter);
  }
  return getIndexesWithValue(array, filter);
}

/**
 * Returns the elements of an array using a list of selected indexes.
 * @param {Array} array
 * @param {Array} indexes
 * @returns {Array}
 */
function getByIndexes(array, indexes) {
  let i;
  const len = indexes.length;
  const data = [];

  for (i = 0; i < len; i += 1) {
    data.push(array[indexes[i]]);
  }
  return data;
}

/**
 * Performs the rotation for an unlimited (regular) rotation
 * @param {Array} arr
 * @param {Object} result
 * @returns {Object}
 */
function arrayRotateUnlimited(arr, result) {
  return arr.reduce((res, obj, i) => Object.entries(obj).reduce((acc, [key, val]) => {
    if (acc[key] === undefined) {
      acc[key] = [];
    }
    acc[key][i] = val;
    return acc;
  }, res), result);
}

/**
 * Performs the rotation for a limited (partial) rotation
 * @param {Array} arr
 * @param {Object} result
 * @param {Array|boolean} limited
 * @returns {Object}
 */
function arrayRotateLimited(arr, result, limited) {
  // If this is limited to the list of keys, get the keys for limited rotate
  // One pass to eliminate any unusable keys and create missing arrays
  const objKeys = (isArray(limited) ? limited : Object.keys(result)).filter((key) => {
    // Create arrays for missing keys
    if (undefined === result[key]) {
      result[key] = [];
      return true;
    }

    // Check if existing keys are arrays
    return isArray(result[key]);
  });

  arr.forEach((obj, i) => objKeys.reduce((acc, att) => {
    acc[att][i] = obj[att];
    return acc;
  }, result));

  return result;
}

/**
 * Rotate an array of row-data into a column-data object
 * @param {Array} arr
 * @param {Object} [result] Result object to use
 * @param {boolean} [limited] Only update keys on the result object; a limited rotation
 * @returns {Object}
 */
function arrayRotate(arr, result, limited = false) {
  const data = result || {};

  // Not an array? Send it back.
  if (!isArray(arr)) {
    throw new TypeError('RC: Argument is not an array');
  }

  if (limited && (typeof result !== 'object' || result === undefined)) {
    throw new TypeError("RC: Must pass a result object when 'limited' is true");
  }

  // Explicit check: you could pass [0], limited could be falsy.
  if (limited) {
    return arrayRotateLimited(arr, data, limited);
  }

  return arrayRotateUnlimited(arr, data);
}

/**
 *
 * @param obj
 * @param field
 * @param filter
 * @returns {Array}
 */
function filterIndexes(obj, field, filter) {
  let indexes = [];

  if (has(obj, field)) {
    indexes = getIndexes(obj[field], filter);
  }

  return indexes;
}

/**
 * Filters all properties in a column-data object using an array of indexes.
 * @param {Object} obj
 * @param {Array} indexes
 * @returns {Object}
 */
function filterMerge(obj, indexes) {
  // Don't waste time if there are no indexes to merge.
  if (indexes === undefined || !indexes.length) {
    return {};
  }

  return Object.entries(obj).reduce((acc, [key, val]) => {
    acc[key] = getByIndexes(val, indexes);
    return acc;
  }, {});
}

/**
 * Filter a column-data object for a particular field.
 * @param {Object} obj
 * @param {String} field
 * @param {Function|String} filter
 * @returns {*}
 */
function objectFilter(obj, field, filter) {
  return filterMerge(obj, filterIndexes(obj, field, filter));
}

/**
 * Finds the longest length of any property in a column-data object.
 * @param {Object} obj
 * @returns {number}
 */
function objectLength(obj) {
  return Math.max(0, ...Object.values(obj).map(arr => arr.length));
}

/**
 * Error throwing for object rotate
 * @param {Object} obj
 * @param result
 */
function objectRotateErrors(obj, result) {
  // Not an array? Send it back.
  if (typeof obj !== 'object') {
    throw new TypeError('RC: Argument is not an object');
  }

  // If a result is passed and isn't an array, send it back.
  if (undefined !== result && !isArray(result)) {
    throw new TypeError('RC: Result argument is not an array');
  }
}

/**
 * Create an object from the specified column index
 * @param {Object} obj
 * @param {number} index
 * @param {Boolean} [clearUndef]
 * @param {Object} [result]
 * @returns {*}
 */
function objFromIndex(obj, index, clearUndef, result = {}) {
  return Object.entries(obj).reduce((acc, [key, val]) => {
    if (!clearUndef || val[index] !== undefined) {
      acc[key] = val[index];
    }
    return acc;
  }, result);
}

/**
 * Rotates column-data to row-data and optionally adds it to an existing array.
 * @param {Object} obj
 * @param {Array} [result] Optional array init which we should place rotated data.
 * @param {Boolean} [clearUndef]
 * @returns {Array}
 */
function objectRotate(obj, result, clearUndef) {
  // Call out for errors
  objectRotateErrors(obj, result);

  // Don't replace parameters to avoid optimization issues.
  const output = result || [];

  // Get the existing result array length
  const resultOffset = output.length;

  const len = objectLength(obj);

  for (let i = 0; i < len; i += 1) {
    output[i + resultOffset] = objFromIndex(obj, i, clearUndef);
  }

  return output;
}

/**
 * A generic rotate function that accepts row or column data to rotate.
 * @param {Object|Array} obj
 * @param {...*} [args]
 * @returns {Array|Object}
 */
function rotate(obj, ...args) {
  // Generic, which directs to the appropriate array/object rotate
  if (typeof obj !== 'object') throw new TypeError('RC: rotate requires an object or an array');

  if (isArray(obj)) {
    return arrayRotate(obj, ...args);
  }
  return objectRotate(obj, ...args);
}

/**
 * Increment over column-data like it was array data, making a "read-only" object for each index.
 * Does not prevent modification of values with complex data types (objects, arrays).
 * @param {Object} obj
 * @param {Function} fn
 */
function readEach(obj, fn) {
  const len = objectLength(obj);

  for (let i = 0; i < len; i += 1) {
    fn(objFromIndex(obj, i));
  }
}

/**
 * Create an object and add a .commit() prototype to place the values back into the original.
 * Useful for moving simple data types around. Objects and arrays will be automatically referenced.
 * @constructor
 * @param {Object} obj - A columnar object
 * @param {number} index - The index to convert into a row-based object
 * @param {Boolean} [clearUndef] - Clear undefined attributes when copying the object.
 * @example
 * var proxyRow = new RowcolProxy(colData, index);
 */
const RCProxy = (function () {
  // TODO - swap out for ES6 proxy object
  const proxyObj = [];
  const proxyIdx = [];
  const proxyClearFlag = [];
  const proxyThis = [];
  let guid = 0;

  /**
   * Uses a private array to locate the same object,
   * so the proxy doesn't expose itself to modification.
   * We compare the passed object against an array of existing RowcolProxy objects;
   * basically pointer comparison.
   * @param {RowcolProxy} proxy
   * @returns {number} The proxy ID to allow commit and destroy to locate the correct element
   * @private
   */
  function proxyGetId(proxy) {
    let pid;

    // Most likely to be at the end, so start there
    for (let i = proxyThis.length; i;) {
      i -= 1;
      if (proxyThis[i] === proxy) {
        pid = i;
        break;
      }
    }

    if (pid === undefined) {
      throw new ReferenceError('Proxy is finalized and cannot be used again.');
    }
    return pid;
  }

  /**
   * The actual RowcolProxy constructor.
   * @param {Object} obj
   * @param {number} index
   * @param {Boolean} [clearUndef]
   * @constructor
   */
  function RowcolProxy(obj, index, clearUndef) {
    // Increment the guid
    guid += 1;

    // Set the private data.
    proxyObj[guid] = obj;
    proxyIdx[guid] = index;
    proxyClearFlag[guid] = clearUndef;
    proxyThis[guid] = this;

    // Call reusable code for objFromIndex
    objFromIndex(obj, index, clearUndef, this);
  }

  // Defined inside the constructor to give it access to the originating object and parameters

  /**
   * Commits the changes from the proxy to the original.
   */
  RowcolProxy.prototype.commit = function commit() {
    const pid = proxyGetId(this);
    const obj = proxyObj[pid];
    const index = proxyIdx[pid];
    const clearUndef = proxyClearFlag[pid];

    Object.entries(this).reduce((acc, [key, val]) => {
      if (key === '__rcProxyId') {
        return acc;
      }
      if (!clearUndef || val !== undefined) {
        acc[key][index] = val;
      }
      return acc;
    }, obj);
  };

  /**
   * Removes the proxy to prevent memory leaks
   */
  RowcolProxy.prototype.destroy = function destroy() {
    const pid = proxyGetId(this);
    delete proxyThis[pid];
    delete proxyObj[pid];
    delete proxyIdx[pid];
    delete proxyClearFlag[pid];
  };

  /**
   * Commit and destroy the proxy, as a single step
   */
  RowcolProxy.prototype.finalize = function finalize() {
    this.commit();
    this.destroy();
  };

  return RowcolProxy;
}());

/**
 * Create a proxy
 * @param obj
 * @param index
 * @param clearUndef
 */
function proxyFromIndex(obj, index, clearUndef) {
  return new RCProxy(obj, index, clearUndef);
}

/**
 * Increment over column-data like it was array data, making a proxy object for each index.
 * @param {Object} obj
 * @param {Function} fn
 */
function objEach(obj, fn) {
  const len = objectLength(obj);
  let i = 0;
  let prox;

  for (; i < len; i += 1) {
    prox = new RCProxy(obj, i);

    fn(prox, i);

    prox.finalize();
  }
}

/*--------------------------------------------------------------------------*/

const rowcol = {
  rotate,
  proxy: proxyFromIndex,
  array: {
    getIndexes,
    getByIndexes,
    rotate: arrayRotate,
  },
  object: {
    filter: objectFilter,
    filterIndexes,
    filterMerge,
    rotate: objectRotate,
    objFromIndex,
    proxy: proxyFromIndex,
    readEach,
    each: objEach,
    objLength: objectLength,
  },
  VERSION: version,
  test: {
    isArray,
  },
};

/*--------------------------------------------------------------------------*/

module.exports = rowcol;
<commit_msg>CodeClimate cleanup<commit_after>/**
 * rowcol - a library for row-column data manipulation
 * @module rowcol
 * @author Samuel Rouse <samuel.rouse@gmail.com>
 * @license MIT http://opensource.org/licenses/MIT
 */

/*--------------------------------------------------------------------------*/
/*
 * Constants and basic settings
 * Structure and code from Lo-Dash 2.4.1 <http://lodash.com/>
 */

const version = '3.0.0';

/*--------------------------------------------------------------------------*/

/* Functions */
const { has } = require('./util');

/**
 * Indicates if the passed object is an array
 * @param {*} obj
 * @returns {boolean}
 */
function isArray(obj) {
  return Object.prototype.toString.call(obj) === '[object Array]';
}

function getIndexesWithFunction(array, filter) {
  const indexes = [];
  const len = array.length;
  let i;

  for (i = 0; i < len; i += 1) {
    if (filter(array[i], i)) {
      indexes.push(i);
    }
  }

  return indexes;
}

function getIndexesWithValue(array, filter) {
  const indexes = [];
  const len = array.length;
  let i;

  for (i = 0; i < len; i += 1) {
    if (array[i] === filter) {
      indexes.push(i);
    }
  }

  return indexes;
}

/**
 * Returns an array of indexes that match a filter string or function.
 * @param {Array} array Array of values to match
 * @param {Function|String} filter Function or String to match array values.
 * @returns {Array} Array of indexes that match the filter.
 */
function getIndexes(array, filter) {
  if (typeof filter === 'function') {
    return getIndexesWithFunction(array, filter);
  }
  return getIndexesWithValue(array, filter);
}

/**
 * Returns the elements of an array using a list of selected indexes.
 * @param {Array} array
 * @param {Array} indexes
 * @returns {Array}
 */
function getByIndexes(array, indexes) {
  let i;
  const len = indexes.length;
  const data = [];

  for (i = 0; i < len; i += 1) {
    data.push(array[indexes[i]]);
  }
  return data;
}

/**
 * Performs the rotation for an unlimited (regular) rotation
 * @param {Array} arr
 * @param {Object} result
 * @returns {Object}
 */
function arrayRotateUnlimited(arr, result) {
  return arr.reduce((res, obj, i) => Object.entries(obj).reduce((acc, [key, val]) => {
    if (acc[key] === undefined) {
      acc[key] = [];
    }
    acc[key][i] = val;
    return acc;
  }, res), result);
}

/**
 * Performs the rotation for a limited (partial) rotation
 * @param {Array} arr
 * @param {Object} result
 * @param {Array|boolean} limited
 * @returns {Object}
 */
function arrayRotateLimited(arr, result, limited) {
  // If this is limited to the list of keys, get the keys for limited rotate
  // One pass to eliminate any unusable keys and create missing arrays
  const objKeys = (isArray(limited) ? limited : Object.keys(result)).filter((key) => {
    // Create arrays for missing keys
    if (undefined === result[key]) {
      result[key] = [];
      return true;
    }

    // Check if existing keys are arrays
    return isArray(result[key]);
  });

  arr.forEach((obj, i) => objKeys.reduce((acc, att) => {
    acc[att][i] = obj[att];
    return acc;
  }, result));

  return result;
}

/**
 * Error checking (and throwing) for the arrayRotate method
 * @param {Array} arr
 * @param {Object} [result] Result object to use
 * @param {boolean} [limited] Only update keys on the result object; a limited rotation
 */
function arrayRotateErrors(arr, result, limited) {
  // Not an array? Send it back.
  if (!isArray(arr)) {
    throw new TypeError('RC: Argument is not an array');
  }

  if (limited && (typeof result !== 'object' || result === undefined)) {
    throw new TypeError("RC: Must pass a result object when 'limited' is true");
  }
}

/**
 * Rotate an array of row-data into a column-data object
 * @param {Array} arr
 * @param {Object} [result] Result object to use
 * @param {boolean} [limited] Only update keys on the result object; a limited rotation
 * @returns {Object}
 */
function arrayRotate(arr, result, limited = false) {
  arrayRotateErrors(arr, result, limited);

  const data = result || {};

  // Explicit check: you could pass [0], limited could be falsy.
  if (limited) {
    return arrayRotateLimited(arr, data, limited);
  }

  return arrayRotateUnlimited(arr, data);
}

/**
 *
 * @param obj
 * @param field
 * @param filter
 * @returns {Array}
 */
function filterIndexes(obj, field, filter) {
  let indexes = [];

  if (has(obj, field)) {
    indexes = getIndexes(obj[field], filter);
  }

  return indexes;
}

/**
 * Filters all properties in a column-data object using an array of indexes.
 * @param {Object} obj
 * @param {Array} indexes
 * @returns {Object}
 */
function filterMerge(obj, indexes) {
  // Don't waste time if there are no indexes to merge.
  if (indexes === undefined || !indexes.length) {
    return {};
  }

  return Object.entries(obj).reduce((acc, [key, val]) => {
    acc[key] = getByIndexes(val, indexes);
    return acc;
  }, {});
}

/**
 * Filter a column-data object for a particular field.
 * @param {Object} obj
 * @param {String} field
 * @param {Function|String} filter
 * @returns {*}
 */
function objectFilter(obj, field, filter) {
  return filterMerge(obj, filterIndexes(obj, field, filter));
}

/**
 * Finds the longest length of any property in a column-data object.
 * @param {Object} obj
 * @returns {number}
 */
function objectLength(obj) {
  return Math.max(0, ...Object.values(obj).map(arr => arr.length));
}

/**
 * Error throwing for object rotate
 * @param {Object} obj
 * @param result
 */
function objectRotateErrors(obj, result) {
  // Not an array? Send it back.
  if (typeof obj !== 'object') {
    throw new TypeError('RC: Argument is not an object');
  }

  // If a result is passed and isn't an array, send it back.
  if (undefined !== result && !isArray(result)) {
    throw new TypeError('RC: Result argument is not an array');
  }
}

/**
 * Create an object from the specified column index
 * @param {Object} obj
 * @param {number} index
 * @param {Boolean} [clearUndef]
 * @param {Object} [result]
 * @returns {*}
 */
function objFromIndex(obj, index, clearUndef, result = {}) {
  return Object.entries(obj).reduce((acc, [key, val]) => {
    if (!clearUndef || val[index] !== undefined) {
      acc[key] = val[index];
    }
    return acc;
  }, result);
}

/**
 * Rotates column-data to row-data and optionally adds it to an existing array.
 * @param {Object} obj
 * @param {Array} [result] Optional array init which we should place rotated data.
 * @param {Boolean} [clearUndef]
 * @returns {Array}
 */
function objectRotate(obj, result, clearUndef) {
  // Call out for errors
  objectRotateErrors(obj, result);

  // Don't replace parameters to avoid optimization issues.
  const output = result || [];

  // Get the existing result array length
  const resultOffset = output.length;

  const len = objectLength(obj);

  for (let i = 0; i < len; i += 1) {
    output[i + resultOffset] = objFromIndex(obj, i, clearUndef);
  }

  return output;
}

/**
 * A generic rotate function that accepts row or column data to rotate.
 * @param {Object|Array} obj
 * @param {...*} [args]
 * @returns {Array|Object}
 */
function rotate(obj, ...args) {
  // Generic, which directs to the appropriate array/object rotate
  if (typeof obj !== 'object') throw new TypeError('RC: rotate requires an object or an array');

  if (isArray(obj)) {
    return arrayRotate(obj, ...args);
  }
  return objectRotate(obj, ...args);
}

/**
 * Increment over column-data like it was array data, making a "read-only" object for each index.
 * Does not prevent modification of values with complex data types (objects, arrays).
 * @param {Object} obj
 * @param {Function} fn
 */
function readEach(obj, fn) {
  const len = objectLength(obj);

  for (let i = 0; i < len; i += 1) {
    fn(objFromIndex(obj, i));
  }
}

/**
 * Create an object and add a .commit() prototype to place the values back into the original.
 * Useful for moving simple data types around. Objects and arrays will be automatically referenced.
 * @constructor
 * @param {Object} obj - A columnar object
 * @param {number} index - The index to convert into a row-based object
 * @param {Boolean} [clearUndef] - Clear undefined attributes when copying the object.
 * @example
 * var proxyRow = new RowcolProxy(colData, index);
 */
const RCProxy = (function () {
  // TODO - swap out for ES6 proxy object
  const proxyObj = [];
  const proxyIdx = [];
  const proxyClearFlag = [];
  const proxyThis = [];
  let guid = 0;

  /**
   * Uses a private array to locate the same object,
   * so the proxy doesn't expose itself to modification.
   * We compare the passed object against an array of existing RowcolProxy objects;
   * basically pointer comparison.
   * @param {RowcolProxy} proxy
   * @returns {number} The proxy ID to allow commit and destroy to locate the correct element
   * @private
   */
  function proxyGetId(proxy) {
    let pid;

    // Most likely to be at the end, so start there
    for (let i = proxyThis.length; i;) {
      i -= 1;
      if (proxyThis[i] === proxy) {
        pid = i;
        break;
      }
    }

    if (pid === undefined) {
      throw new ReferenceError('Proxy is finalized and cannot be used again.');
    }
    return pid;
  }

  /**
   * The actual RowcolProxy constructor.
   * @param {Object} obj
   * @param {number} index
   * @param {Boolean} [clearUndef]
   * @constructor
   */
  function RowcolProxy(obj, index, clearUndef) {
    // Increment the guid
    guid += 1;

    // Set the private data.
    proxyObj[guid] = obj;
    proxyIdx[guid] = index;
    proxyClearFlag[guid] = clearUndef;
    proxyThis[guid] = this;

    // Call reusable code for objFromIndex
    objFromIndex(obj, index, clearUndef, this);
  }

  // Defined inside the constructor to give it access to the originating object and parameters

  /**
   * Commits the changes from the proxy to the original.
   */
  RowcolProxy.prototype.commit = function commit() {
    const pid = proxyGetId(this);
    const obj = proxyObj[pid];
    const index = proxyIdx[pid];
    const clearUndef = proxyClearFlag[pid];

    Object.entries(this).reduce((acc, [key, val]) => {
      if (key === '__rcProxyId') {
        return acc;
      }
      if (!clearUndef || val !== undefined) {
        acc[key][index] = val;
      }
      return acc;
    }, obj);
  };

  /**
   * Removes the proxy to prevent memory leaks
   */
  RowcolProxy.prototype.destroy = function destroy() {
    const pid = proxyGetId(this);
    delete proxyThis[pid];
    delete proxyObj[pid];
    delete proxyIdx[pid];
    delete proxyClearFlag[pid];
  };

  /**
   * Commit and destroy the proxy, as a single step
   */
  RowcolProxy.prototype.finalize = function finalize() {
    this.commit();
    this.destroy();
  };

  return RowcolProxy;
}());

/**
 * Create a proxy
 * @param obj
 * @param index
 * @param clearUndef
 */
function proxyFromIndex(obj, index, clearUndef) {
  return new RCProxy(obj, index, clearUndef);
}

/**
 * Increment over column-data like it was array data, making a proxy object for each index.
 * @param {Object} obj
 * @param {Function} fn
 */
function objEach(obj, fn) {
  const len = objectLength(obj);
  let i = 0;
  let prox;

  for (; i < len; i += 1) {
    prox = new RCProxy(obj, i);

    fn(prox, i);

    prox.finalize();
  }
}

/*--------------------------------------------------------------------------*/

const rowcol = {
  rotate,
  proxy: proxyFromIndex,
  array: {
    getIndexes,
    getByIndexes,
    rotate: arrayRotate,
  },
  object: {
    filter: objectFilter,
    filterIndexes,
    filterMerge,
    rotate: objectRotate,
    objFromIndex,
    proxy: proxyFromIndex,
    readEach,
    each: objEach,
    objLength: objectLength,
  },
  VERSION: version,
  test: {
    isArray,
  },
};

/*--------------------------------------------------------------------------*/

module.exports = rowcol;
<|endoftext|>


<commit_before>goog.provide('gmf-editfeature');

goog.require('ngeo.proj.EPSG21781');
goog.require('gmf.authenticationDirective');
goog.require('gmf.mapDirective');
goog.require('gmf.EditFeature');
goog.require('goog.asserts');
goog.require('ol.Feature');
goog.require('ol.Map');
goog.require('ol.View');
goog.require('ol.extent');
goog.require('ol.geom.Point');
goog.require('ol.layer.Tile');
goog.require('ol.source.OSM');

/** @const **/
var app = {};

/** @type {!angular.Module} **/
app.module = angular.module('app', ['gmf']);

app.module.constant(
    'authenticationBaseUrl',
    'https://geomapfish-demo.camptocamp.net/2.1/wsgi');

app.module.constant('gmfLayersUrl',
    'https://geomapfish-demo.camptocamp.net/2.1/wsgi/layers/');

/**
 * @param {!angular.Scope} $scope Angular scope.
 * @param {gmf.EditFeature} gmfEditFeature Gmf edit feature service.
 * @param {gmfx.User} gmfUser User.
 * @constructor
 */
app.MainController = function($scope, gmfEditFeature, gmfUser) {

  /**
   * @type {!angular.Scope}
   * @private
   */
  this.scope_ = $scope;

  /**
   * @type {gmf.EditFeature}
   * @export
   */
  this.editFeature_ = gmfEditFeature;

  /**
   * @type {gmfx.User}
   * @export
   */
  this.gmfUser = gmfUser;

  var projection = ol.proj.get('EPSG:21781');
  projection.setExtent([485869.5728, 76443.1884, 837076.5648, 299941.7864]);

  /**
   * @type {ol.source.ImageWMS}
   * @private
   */
  this.wmsSource_ = new ol.source.ImageWMS({
    url: 'https://geomapfish-demo.camptocamp.net/2.1/wsgi/mapserv_proxy',
    params: {'LAYERS': 'point'}
  });

  /**
   * @type {ol.layer.Image}
   * @private
   */
  this.wmsLayer_ = new ol.layer.Image({
    source: this.wmsSource_
  });

  /**
   * @type {number}
   * @private
   */
  this.pixelBuffer_ = 10;

  /**
   * @type {number}
   * @private
   */
  this.layerId_ = 113;

  /**
   * @type {ol.Feature}
   * @export
   */
  this.feature = null;

  /**
   * @type {boolean}
   * @export
   */
  this.pending = false;

  /**
   * @type {ol.Map}
   * @export
   */
  this.map = new ol.Map({
    layers: [
      new ol.layer.Tile({
        source: new ol.source.OSM()
      }),
      this.wmsLayer_
    ],
    view: new ol.View({
      projection: projection,
      resolutions: [200, 100, 50, 20, 10, 5, 2.5, 2, 1, 0.5],
      center: [537635, 152640],
      zoom: 2
    })
  });

  this.map.on('singleclick', this.handleMapSingleClick_, this);

  // initialize tooltips
  $('[data-toggle="tooltip"]').tooltip({
    container: 'body',
    trigger: 'hover'
  });
};

/**
 * @param {ol.MapBrowserEvent} evt MapBrowser event
 * @private
 */
app.MainController.prototype.handleMapSingleClick_ = function(evt) {

  // (1) Launch query to fetch new features
  var coordinate = evt.coordinate;
  var map = this.map;
  var view = map.getView();
  var resolution = view.getResolution();
  var buffer = resolution * this.pixelBuffer_;
  var extent = ol.extent.buffer(
    [coordinate[0], coordinate[1], coordinate[0], coordinate[1]],
    buffer
  );

  this.editFeature_.getFeatures([this.layerId_], extent).then(
    this.handleGetFeatures_.bind(this));

  // (2) Clear any previously selected feature
  this.feature = null;

  // (3) Pending
  this.pending = true;

  this.scope_.$apply();
};

/**
 * @param {Array.<ol.Feature>} features Features.
 * @private
 */
app.MainController.prototype.handleGetFeatures_ = function(features) {
  this.pending = false;

  if (features.length) {
    this.feature = features[0];
  }
};

/**
 * Insert a new feature at a random location.
 * @export
 */
app.MainController.prototype.insertFeature = function() {

  this.pending = true;

  // (1) Create a randomly located feature
  var map = this.map;
  var view = map.getView();
  var resolution = view.getResolution();
  var buffer = resolution * -50; // 50 pixel buffer inside the extent
  var size = map.getSize();
  goog.asserts.assert(size);
  var extent = ol.extent.buffer(
    view.calculateExtent(size),
    buffer
  );
  var bottomLeft = ol.extent.getBottomLeft(extent);
  var topRight = ol.extent.getTopRight(extent);
  var left = bottomLeft[0];
  var bottom = bottomLeft[1];
  var right = topRight[0];
  var top = topRight[1];
  var deltaX = right - left;
  var deltaY = top - bottom;
  var coordinate = [
    left + Math.random() * deltaX,
    right + Math.random() * deltaY
  ];

  var feature = new ol.Feature({
    'geometry': new ol.geom.Point(coordinate),
    'name': 'New point'
  });

  // (2) Launch request
  this.editFeature_.insertFeatures(
    this.layerId_,
    [feature]
  ).then(
    this.handleEditFeature_.bind(this)
  );
};

/**
 * Update the currently selected feature with a new name.
 * @export
 */
app.MainController.prototype.updateFeature = function() {

  goog.asserts.assert(this.feature);

  this.pending = true;

  // (1) Update name
  this.feature.set('name', 'Updated name');

  // (2) Launch request
  this.editFeature_.updateFeature(
    this.layerId_,
    this.feature
  ).then(
    this.handleEditFeature_.bind(this)
  );

  // (3) Reset selected feature
  this.feature = null;
};

/**
 * Delete currently selected feature.
 * @export
 */
app.MainController.prototype.deleteFeature = function() {

  goog.asserts.assert(this.feature);

  // (1) Launch request
  this.editFeature_.deleteFeature(
    this.layerId_,
    this.feature
  ).then(
    this.handleEditFeature_.bind(this)
  );

  // (2) Reset selected feature
  this.feature = null;
};

/**
 * Called after an insert, update or delete request.
 * @param {angular.$http.Response} resp Ajax response.
 * @private
 */
app.MainController.prototype.handleEditFeature_ = function(resp) {
  this.pending = false;
  this.refreshWMSLayer_();
};

/**
 * @private
 */
app.MainController.prototype.refreshWMSLayer_ = function() {
  this.wmsSource_.updateParams({
    'random': Math.random()
  });
};

app.module.controller('MainController', app.MainController);
<commit_msg>Use console.assert instead of goog.asserts.assert in editfeature example<commit_after>goog.provide('gmf-editfeature');

goog.require('ngeo.proj.EPSG21781');
goog.require('gmf.authenticationDirective');
goog.require('gmf.mapDirective');
goog.require('gmf.EditFeature');
goog.require('ol.Feature');
goog.require('ol.Map');
goog.require('ol.View');
goog.require('ol.extent');
goog.require('ol.geom.Point');
goog.require('ol.layer.Tile');
goog.require('ol.source.OSM');

/** @const **/
var app = {};

/** @type {!angular.Module} **/
app.module = angular.module('app', ['gmf']);

app.module.constant(
    'authenticationBaseUrl',
    'https://geomapfish-demo.camptocamp.net/2.1/wsgi');

app.module.constant('gmfLayersUrl',
    'https://geomapfish-demo.camptocamp.net/2.1/wsgi/layers/');

/**
 * @param {!angular.Scope} $scope Angular scope.
 * @param {gmf.EditFeature} gmfEditFeature Gmf edit feature service.
 * @param {gmfx.User} gmfUser User.
 * @constructor
 */
app.MainController = function($scope, gmfEditFeature, gmfUser) {

  /**
   * @type {!angular.Scope}
   * @private
   */
  this.scope_ = $scope;

  /**
   * @type {gmf.EditFeature}
   * @export
   */
  this.editFeature_ = gmfEditFeature;

  /**
   * @type {gmfx.User}
   * @export
   */
  this.gmfUser = gmfUser;

  var projection = ol.proj.get('EPSG:21781');
  projection.setExtent([485869.5728, 76443.1884, 837076.5648, 299941.7864]);

  /**
   * @type {ol.source.ImageWMS}
   * @private
   */
  this.wmsSource_ = new ol.source.ImageWMS({
    url: 'https://geomapfish-demo.camptocamp.net/2.1/wsgi/mapserv_proxy',
    params: {'LAYERS': 'point'}
  });

  /**
   * @type {ol.layer.Image}
   * @private
   */
  this.wmsLayer_ = new ol.layer.Image({
    source: this.wmsSource_
  });

  /**
   * @type {number}
   * @private
   */
  this.pixelBuffer_ = 10;

  /**
   * @type {number}
   * @private
   */
  this.layerId_ = 113;

  /**
   * @type {ol.Feature}
   * @export
   */
  this.feature = null;

  /**
   * @type {boolean}
   * @export
   */
  this.pending = false;

  /**
   * @type {ol.Map}
   * @export
   */
  this.map = new ol.Map({
    layers: [
      new ol.layer.Tile({
        source: new ol.source.OSM()
      }),
      this.wmsLayer_
    ],
    view: new ol.View({
      projection: projection,
      resolutions: [200, 100, 50, 20, 10, 5, 2.5, 2, 1, 0.5],
      center: [537635, 152640],
      zoom: 2
    })
  });

  this.map.on('singleclick', this.handleMapSingleClick_, this);

  // initialize tooltips
  $('[data-toggle="tooltip"]').tooltip({
    container: 'body',
    trigger: 'hover'
  });
};

/**
 * @param {ol.MapBrowserEvent} evt MapBrowser event
 * @private
 */
app.MainController.prototype.handleMapSingleClick_ = function(evt) {

  // (1) Launch query to fetch new features
  var coordinate = evt.coordinate;
  var map = this.map;
  var view = map.getView();
  var resolution = view.getResolution();
  var buffer = resolution * this.pixelBuffer_;
  var extent = ol.extent.buffer(
    [coordinate[0], coordinate[1], coordinate[0], coordinate[1]],
    buffer
  );

  this.editFeature_.getFeatures([this.layerId_], extent).then(
    this.handleGetFeatures_.bind(this));

  // (2) Clear any previously selected feature
  this.feature = null;

  // (3) Pending
  this.pending = true;

  this.scope_.$apply();
};

/**
 * @param {Array.<ol.Feature>} features Features.
 * @private
 */
app.MainController.prototype.handleGetFeatures_ = function(features) {
  this.pending = false;

  if (features.length) {
    this.feature = features[0];
  }
};

/**
 * Insert a new feature at a random location.
 * @export
 */
app.MainController.prototype.insertFeature = function() {

  this.pending = true;

  // (1) Create a randomly located feature
  var map = this.map;
  var view = map.getView();
  var resolution = view.getResolution();
  var buffer = resolution * -50; // 50 pixel buffer inside the extent
  var size = /** @type {!Array.<number>} */ (map.getSize());
  var extent = ol.extent.buffer(
    view.calculateExtent(size),
    buffer
  );
  var bottomLeft = ol.extent.getBottomLeft(extent);
  var topRight = ol.extent.getTopRight(extent);
  var left = bottomLeft[0];
  var bottom = bottomLeft[1];
  var right = topRight[0];
  var top = topRight[1];
  var deltaX = right - left;
  var deltaY = top - bottom;
  var coordinate = [
    left + Math.random() * deltaX,
    right + Math.random() * deltaY
  ];

  var feature = new ol.Feature({
    'geometry': new ol.geom.Point(coordinate),
    'name': 'New point'
  });

  // (2) Launch request
  this.editFeature_.insertFeatures(
    this.layerId_,
    [feature]
  ).then(
    this.handleEditFeature_.bind(this)
  );
};

/**
 * Update the currently selected feature with a new name.
 * @export
 */
app.MainController.prototype.updateFeature = function() {

  console.assert(this.feature);

  this.pending = true;

  // (1) Update name
  this.feature.set('name', 'Updated name');

  // (2) Launch request
  this.editFeature_.updateFeature(
    this.layerId_,
    this.feature
  ).then(
    this.handleEditFeature_.bind(this)
  );

  // (3) Reset selected feature
  this.feature = null;
};

/**
 * Delete currently selected feature.
 * @export
 */
app.MainController.prototype.deleteFeature = function() {

  console.assert(this.feature);

  // (1) Launch request
  this.editFeature_.deleteFeature(
    this.layerId_,
    this.feature
  ).then(
    this.handleEditFeature_.bind(this)
  );

  // (2) Reset selected feature
  this.feature = null;
};

/**
 * Called after an insert, update or delete request.
 * @param {angular.$http.Response} resp Ajax response.
 * @private
 */
app.MainController.prototype.handleEditFeature_ = function(resp) {
  this.pending = false;
  this.refreshWMSLayer_();
};

/**
 * @private
 */
app.MainController.prototype.refreshWMSLayer_ = function() {
  this.wmsSource_.updateParams({
    'random': Math.random()
  });
};

app.module.controller('MainController', app.MainController);
<|endoftext|>


<commit_before>dojo.provide("dojox.form._HasDropDown");

dojo.require("dijit._Widget");

dojo.declare("dojox.form._HasDropDown",
	null,
	{
		//	summary:
		//		Mixin for widgets that need drop down ability.
		//
		//	dropDownNode: DomNode
		//		responds to click events.  "dropDownNode" can be set via
		//		a dojoAttachPoint assignment.  If missing, then either focusNode
		//		or domNode (if focusNode is also missing) will be used.
		dropDownNode: null,

		//	popupStateNode: DomNode
		//		the node to set the popupActive class on.  If missing, then
		//		focusNode or dropDownNode (if focusNode is missing) will be used.
		popupStateNode: null,

		//	aroundNode: DomNode
		//		the node to display the popup around.  If missing, then
		//		domNode will be used.
		aroundNode: null,

		//	dropDown: Widget
		//		the widget to display in the popup.  This widget *must* be
		//		defined before the startup function is called.
		dropDown: null,

		//	autoWidth: Boolean
		//		Set to true to make the drop down at least as wide as this
		//		widget.  Set to false if the drop down should just be its
		//		default width
		autoWidth: true,

		//	_stopClickEvents: Boolean
		//		When set to false, the click events will not be stopped, in
		//		case you want to use them in your subwidget
		_stopClickEvents: true,

		_onMenuMouseup: function(/*Event*/ e){
			// summary: called with the mouseup event if the mouseup occurred
			//			over the menu.  You can try and use this event in
			//			order to automatically execute your dropdown (as
			//			if it were clicked).  You mightwant to close your menu
			//			as a part of this function.
		},

		_onDropDownMouse: function(/*Event*/ e){
			// summary: callback when the user mouse clicks on menu popup node
			//
			// We handle mouse events using onmousedown in order to allow for
			// selecting via a drag.  So, our click is already handled, unless
			// we are executed via keypress - in which case, this._seenKeydown
			// will be set to true.
			if(e.type == "click" && !this._seenKeydown){ return; }
			this._seenKeydown = false;

			// If we are a mouse event, set up the mouseup handler
			if(e.type == "mousedown"){
				this._docHandler = this.connect(dojo.doc, "onmouseup", "_onDropDownMouseup");
			}
			if(this.disabled || this.readOnly){ return; }
			if(this._stopClickEvents){
				dojo.stopEvent(e);
			}
			this.toggleDropDown();

			// If we are a click, then we'll pretend we did a mouse up
			if(e.type == "click" || e.type == "keypress"){
				this._onDropDownMouseup();
			}
		},

		_onDropDownMouseup: function(/*Event?*/ e){
			// summary: callback when the user lifts their mouse - if we are
			//			over the menu, we execute it, otherwise, we focus our
			//			dropDown node.  If the event is missing, then we are not
			//			a mouseup event.
			if(e && this._docHandler){
				this.disconnect(this._docHandler);
			}
			var dropDown = this.dropDown, overMenu = false;

			if(e && this._opened){
				// Find out if our target is somewhere in our dropdown widget
				var t = e.target;
				while(t && !overMenu){
					if(dojo.hasClass(t, "dijitPopup")){
						overMenu = true;
					}else{
						t = t.parentNode;
					}
				}
				if(overMenu){
					this._onMenuMouseup(e);
					return;
				}
			}
			if(this._opened && dropDown.focus){
				// delay so that we don't steal our own focus.
				window.setTimeout(dojo.hitch(dropDown, "focus"), 1);
			}else{
				dijit.focus(this.popupStateNode);
			}
		},

		_setupDropdown: function(){
			//	summary:
			//		set up nodes and connect our mouse and keypress events
			this.dropDownNode = this.dropDownNode || this.focusNode || this.domNode;
			this.popupStateNode = this.popupStateNode || this.focusNode || this.dropDownNode;
			this.aroundNode = this.aroundNode || this.domNode;
			this.connect(this.dropDownNode, "onmousedown", "_onDropDownMouse");
			this.connect(this.dropDownNode, "onclick", "_onDropDownMouse");
			this.connect(this.dropDownNode, "onkeydown", "_onDropDownKeydown");
			this.connect(this.dropDownNode, "onblur", "_onDropDownBlur");
			this.connect(this.dropDownNode, "onkeypress", "_onKey");

			// If we have a _setStateClass function (which happens when
			// we are a form widget), then we need to connect our open/close
			// functions to it
			if(this._setStateClass){
				this.connect(this, "openDropDown", "_setStateClass");
				this.connect(this, "closeDropDown", "_setStateClass");
			}
		},

		postCreate: function(){
			this._setupDropdown();
			this.inherited("postCreate", arguments);
		},

		startup: function(){
			dijit.popup.prepare(this.dropDown.domNode);
			this.inherited("startup", arguments);
		},

		destroyDescendants: function(){
			if(this.dropDown){
				this.dropDown.destroyRecursive();
				delete this.dropDown;
			}
			this.inherited("destroyDescendants", arguments);
		},

		_onDropDownKeydown: function(/*Event*/ e){
			this._seenKeydown = true;
		},

		_onKeyPress: function(/*Event*/ e){
			if(this._opened && e.charOrCode == dojo.keys.ESCAPE && !e.shiftKey && !e.ctrlKey && !e.altKey){
				this.toggleDropDown();
				dojo.stopEvent(e);
				return;
			}
			this.inherited(arguments);
		},

		_onDropDownBlur: function(/*Event*/ e){
			this._seenKeydown = false;
		},

		_onKey: function(/*Event*/ e){
			// summary: callback when the user presses a key on menu popup node
			if(this.disabled || this.readOnly){ return; }
			var d = this.dropDown;
			if(d && this._opened && d.handleKey){
				if(d.handleKey(e) === false){ return; }
			}
			if(d && this._opened && e.keyCode == dojo.keys.ESCAPE){
				this.toggleDropDown();
				return;
			}
			if(e.keyCode == dojo.keys.DOWN_ARROW){
				this._onDropDownMouse(e);
			}
		},

		_onBlur: function(){
			// summary: called magically when focus has shifted away from this widget and it's dropdown
			this.closeDropDown();
			// don't focus on button.  the user has explicitly focused on something else.
			this.inherited("_onBlur", arguments);
		},

		isLoaded: function(){
			// summary: returns whether or not the dropdown is loaded.  This can
			//		be overridden in order to force a call to loadDropDown().
			return true;
		},

		loadDropDown: function(/* Function */ loadCallback){
			// summary: loads the data for the dropdown, and at some point, calls
			//		the given callback
			loadCallback();
		},

		toggleDropDown: function(){
			// summary: toggle the drop-down widget; if it is up, close it, if not, open it
			if(this.disabled || this.readOnly){ return; }
			this.focus();
			var dropDown = this.dropDown;
			if(!dropDown){ return; }
			if(!this._opened){
				// If we aren't loaded, load it first so there isn't a flicker
				if(!this.isLoaded()){
					this.loadDropDown(dojo.hitch(this, "openDropDown"));
					return;
				}else{
					this.openDropDown();
				}
			}else{
				this.closeDropDown();
			}
		},

		openDropDown: function(){
			// summary: opens the dropdown for this widget - it returns the
			//			return value of dijit.popup.open
			var dropDown = this.dropDown;
			var oldWidth=dropDown.domNode.style.width;
			var self = this;

			var retVal = dijit.popup.open({
				parent: this,
				popup: dropDown,
				around: this.aroundNode,
				orient:
					// TODO: add user-defined positioning option, like in Tooltip.js
					this.isLeftToRight() ? {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'}
					: {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'},
				onExecute: function(){
					self.closeDropDown(true);
				},
				onCancel: function(){
					self.closeDropDown(true);
				},
				onClose: function(){
					dropDown.domNode.style.width = oldWidth;
					dojo.attr(self.popupStateNode, "popupActive", false);
					self.popupStateNode.removeAttribute("popupActive");
					self._opened = false;
					self.state = "";
				}
			});
			if(this.autoWidth && this.domNode.offsetWidth > dropDown.domNode.offsetWidth){
				var adjustNode = null;
				if(!this.isLeftToRight()){
					adjustNode = dropDown.domNode.parentNode;
					var oldRight = adjustNode.offsetLeft + adjustNode.offsetWidth;
				}
				// make menu at least as wide as the node
				if(dropDown.resize){
					dropDown.resize({w: this.domNode.offsetWidth});
				}else{
					dojo.marginBox(dropDown.domNode, {w: this.domNode.offsetWidth});
				}
				if(adjustNode){
					adjustNode.style.left = oldRight - this.domNode.offsetWidth + "px";
				}
			}
			dojo.attr(this.popupStateNode, "popupActive", "true");
			this._opened=true;
			this.state="Opened";
			if(dropDown.focus){
				dropDown.focus();
			}
			// TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
			return retVal;
		},

		closeDropDown: function(/*Boolean*/ focus){
			// summary: Closes the drop down on this widget
			if(this._opened){
				dijit.popup.close(this.dropDown);
				if(focus){ this.focus(); }
				this._opened = false;
				this.state = "";
			}
		}

	}
);<commit_msg>Fixes #7457 - Style the file picker text box more like a combo box - but use a folder instead of an arrow ;)<commit_after>dojo.provide("dojox.form._HasDropDown");

dojo.require("dijit._Widget");

dojo.declare("dojox.form._HasDropDown",
	null,
	{
		//	summary:
		//		Mixin for widgets that need drop down ability.
		//
		//	dropDownNode: DomNode
		//		responds to click events.  "dropDownNode" can be set via
		//		a dojoAttachPoint assignment.  If missing, then either focusNode
		//		or domNode (if focusNode is also missing) will be used.
		dropDownNode: null,

		//	popupStateNode: DomNode
		//		the node to set the popupActive class on.  If missing, then
		//		focusNode or dropDownNode (if focusNode is missing) will be used.
		popupStateNode: null,

		//	aroundNode: DomNode
		//		the node to display the popup around.  If missing, then
		//		domNode will be used.
		aroundNode: null,

		//	dropDown: Widget
		//		the widget to display in the popup.  This widget *must* be
		//		defined before the startup function is called.
		dropDown: null,

		//	autoWidth: Boolean
		//		Set to true to make the drop down at least as wide as this
		//		widget.  Set to false if the drop down should just be its
		//		default width
		autoWidth: true,

		//	_stopClickEvents: Boolean
		//		When set to false, the click events will not be stopped, in
		//		case you want to use them in your subwidget
		_stopClickEvents: true,

		_onMenuMouseup: function(/*Event*/ e){
			// summary: called with the mouseup event if the mouseup occurred
			//			over the menu.  You can try and use this event in
			//			order to automatically execute your dropdown (as
			//			if it were clicked).  You mightwant to close your menu
			//			as a part of this function.
		},

		_onDropDownMouse: function(/*Event*/ e){
			// summary: callback when the user mouse clicks on menu popup node
			//
			// We handle mouse events using onmousedown in order to allow for
			// selecting via a drag.  So, our click is already handled, unless
			// we are executed via keypress - in which case, this._seenKeydown
			// will be set to true.
			if(e.type == "click" && !this._seenKeydown){ return; }
			this._seenKeydown = false;

			// If we are a mouse event, set up the mouseup handler
			if(e.type == "mousedown"){
				this._docHandler = this.connect(dojo.doc, "onmouseup", "_onDropDownMouseup");
			}
			if(this.disabled || this.readOnly){ return; }
			if(this._stopClickEvents){
				dojo.stopEvent(e);
			}
			this.toggleDropDown();

			// If we are a click, then we'll pretend we did a mouse up
			if(e.type == "click" || e.type == "keypress"){
				this._onDropDownMouseup();
			}
		},

		_onDropDownMouseup: function(/*Event?*/ e){
			// summary: callback when the user lifts their mouse - if we are
			//			over the menu, we execute it, otherwise, we focus our
			//			dropDown node.  If the event is missing, then we are not
			//			a mouseup event.
			if(e && this._docHandler){
				this.disconnect(this._docHandler);
			}
			var dropDown = this.dropDown, overMenu = false;

			if(e && this._opened){
				// Find out if our target is somewhere in our dropdown widget
				var t = e.target;
				while(t && !overMenu){
					if(dojo.hasClass(t, "dijitPopup")){
						overMenu = true;
					}else{
						t = t.parentNode;
					}
				}
				if(overMenu){
					this._onMenuMouseup(e);
					return;
				}
			}
			if(this._opened && dropDown.focus){
				// delay so that we don't steal our own focus.
				window.setTimeout(dojo.hitch(dropDown, "focus"), 1);
			}else{
				dijit.focus(this.focusNode);
			}
		},

		_setupDropdown: function(){
			//	summary:
			//		set up nodes and connect our mouse and keypress events
			this.dropDownNode = this.dropDownNode || this.focusNode || this.domNode;
			this.popupStateNode = this.popupStateNode || this.focusNode || this.dropDownNode;
			this.aroundNode = this.aroundNode || this.domNode;
			this.connect(this.dropDownNode, "onmousedown", "_onDropDownMouse");
			this.connect(this.dropDownNode, "onclick", "_onDropDownMouse");
			this.connect(this.dropDownNode, "onkeydown", "_onDropDownKeydown");
			this.connect(this.dropDownNode, "onblur", "_onDropDownBlur");
			this.connect(this.dropDownNode, "onkeypress", "_onKey");

			// If we have a _setStateClass function (which happens when
			// we are a form widget), then we need to connect our open/close
			// functions to it
			if(this._setStateClass){
				this.connect(this, "openDropDown", "_setStateClass");
				this.connect(this, "closeDropDown", "_setStateClass");
			}
		},

		postCreate: function(){
			this._setupDropdown();
			this.inherited("postCreate", arguments);
		},

		startup: function(){
			dijit.popup.prepare(this.dropDown.domNode);
			this.inherited("startup", arguments);
		},

		destroyDescendants: function(){
			if(this.dropDown){
				this.dropDown.destroyRecursive();
				delete this.dropDown;
			}
			this.inherited("destroyDescendants", arguments);
		},

		_onDropDownKeydown: function(/*Event*/ e){
			this._seenKeydown = true;
		},

		_onKeyPress: function(/*Event*/ e){
			if(this._opened && e.charOrCode == dojo.keys.ESCAPE && !e.shiftKey && !e.ctrlKey && !e.altKey){
				this.toggleDropDown();
				dojo.stopEvent(e);
				return;
			}
			this.inherited(arguments);
		},

		_onDropDownBlur: function(/*Event*/ e){
			this._seenKeydown = false;
		},

		_onKey: function(/*Event*/ e){
			// summary: callback when the user presses a key on menu popup node
			if(this.disabled || this.readOnly){ return; }
			var d = this.dropDown;
			if(d && this._opened && d.handleKey){
				if(d.handleKey(e) === false){ return; }
			}
			if(d && this._opened && e.keyCode == dojo.keys.ESCAPE){
				this.toggleDropDown();
				return;
			}
			if(e.keyCode == dojo.keys.DOWN_ARROW){
				this._onDropDownMouse(e);
			}
		},

		_onBlur: function(){
			// summary: called magically when focus has shifted away from this widget and it's dropdown
			this.closeDropDown();
			// don't focus on button.  the user has explicitly focused on something else.
			this.inherited("_onBlur", arguments);
		},

		isLoaded: function(){
			// summary: returns whether or not the dropdown is loaded.  This can
			//		be overridden in order to force a call to loadDropDown().
			return true;
		},

		loadDropDown: function(/* Function */ loadCallback){
			// summary: loads the data for the dropdown, and at some point, calls
			//		the given callback
			loadCallback();
		},

		toggleDropDown: function(){
			// summary: toggle the drop-down widget; if it is up, close it, if not, open it
			if(this.disabled || this.readOnly){ return; }
			this.focus();
			var dropDown = this.dropDown;
			if(!dropDown){ return; }
			if(!this._opened){
				// If we aren't loaded, load it first so there isn't a flicker
				if(!this.isLoaded()){
					this.loadDropDown(dojo.hitch(this, "openDropDown"));
					return;
				}else{
					this.openDropDown();
				}
			}else{
				this.closeDropDown();
			}
		},

		openDropDown: function(){
			// summary: opens the dropdown for this widget - it returns the
			//			return value of dijit.popup.open
			var dropDown = this.dropDown;
			var oldWidth=dropDown.domNode.style.width;
			var self = this;

			var retVal = dijit.popup.open({
				parent: this,
				popup: dropDown,
				around: this.aroundNode,
				orient:
					// TODO: add user-defined positioning option, like in Tooltip.js
					this.isLeftToRight() ? {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'}
					: {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'},
				onExecute: function(){
					self.closeDropDown(true);
				},
				onCancel: function(){
					self.closeDropDown(true);
				},
				onClose: function(){
					dropDown.domNode.style.width = oldWidth;
					dojo.attr(self.popupStateNode, "popupActive", false);
					dojo.removeClass(self.popupStateNode, "dojoxHasDropDownOpen");
					self._opened = false;
					self.state = "";
				}
			});
			if(this.autoWidth && this.domNode.offsetWidth > dropDown.domNode.offsetWidth){
				var adjustNode = null;
				if(!this.isLeftToRight()){
					adjustNode = dropDown.domNode.parentNode;
					var oldRight = adjustNode.offsetLeft + adjustNode.offsetWidth;
				}
				// make menu at least as wide as the node
				if(dropDown.resize){
					dropDown.resize({w: this.domNode.offsetWidth});
				}else{
					dojo.marginBox(dropDown.domNode, {w: this.domNode.offsetWidth});
				}
				if(adjustNode){
					adjustNode.style.left = oldRight - this.domNode.offsetWidth + "px";
				}
			}
			dojo.attr(this.popupStateNode, "popupActive", "true");
			dojo.addClass(self.popupStateNode, "dojoxHasDropDownOpen");
			this._opened=true;
			this.state="Opened";
			if(dropDown.focus){
				dropDown.focus();
			}
			// TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
			return retVal;
		},

		closeDropDown: function(/*Boolean*/ focus){
			// summary: Closes the drop down on this widget
			if(this._opened){
				dijit.popup.close(this.dropDown);
				if(focus){ this.focus(); }
				this._opened = false;
				this.state = "";
			}
		}

	}
);<|endoftext|>


<commit_before>var transforms=[{
	name:'Continuous-time Fourier transform (CTFT)', // angular frequency, non-unitary
	wikipedia:'http://en.wikipedia.org/wiki/Fourier_transform',
	timeVar:'t',
	freqVar:'\\omega',
	timeFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'x'+o.fnConj+'('+arg+')';
	},
	freqFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'X'+o.fnConj+'('+o.argSign+'j'+o.argRest+')';
	},
	sections:{
		definitions:function(x,X,t,T){return{
			time:[
				{formula:{
					item:x(t)+' = \\frac{1}{2\\pi} \\int\\limits_{-\\infty}^{+\\infty} '+X(T)+' e^{j'+T+' '+t+'} \\,\\mathrm{d}'+T,
					notes:{b:'function \\('+x(t)+'\\) of continuous variable \\('+t+'\\)'}
				}}
			],
			freq:[
				{formula:{
					item:X(T)+' = \\int\\limits_{-\\infty}^{+\\infty} '+x(t)+' e^{-j'+T+' '+t+'} \\,\\mathrm{d}'+t,
					notes:{b:'function \\('+X(T)+'\\) of continuous variable \\('+T+'\\)'}
				}}
			]
		}},
		duality:function(x,X,t,T){return{
			time:[
				{formula:{item:'\\frac{1}{2\\pi} '+X('-'+t)}},
				{formula:{item:x(t)}},
				{formula:{item:X(t)}}
			],
			freq:[
				{formula:{item:x(T)}},
				{formula:{item:X(T)}},
				{formula:{item:'2\\pi '+x('-'+T)}}
			]
		}},
		conjrev:function(x,X,t,T){return{
		}},
		modshift:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t+'+'+t+'_0')}},
				{formula:{item:'e^{-j'+T+'_0 '+t+'}'+x(t)}},
				{formula:{item:x(t)}},
				{formula:{item:'e^{j'+T+'_0 '+t+'}'+x(t)}},
				{formula:{item:x(t+'-'+t+'_0')}}
			],
			freq:[
				{formula:{item:'e^{j'+T+' '+t+'_0}'+X(T)}},
				{formula:{item:X(''+T+'+'+T+'_0')}},
				{formula:{item:X(T)}},
				{formula:{item:X(''+T+'-'+T+'_0')}},
				{formula:{item:'e^{-j'+T+' '+t+'_0}'+X(T)}}
			]
		}},
		intdiff:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t)}},
				{formula:{item:'\\frac{\\mathrm{d}}{\\mathrm{d}'+t+'} '+x(t)}},
				{formula:{item:'-j '+t+' '+x(t)}}
			],
			freq:[
				{formula:{item:X(T)}},
				{formula:{item:'j'+T+' '+X(T)}},
				{formula:{item:'\\frac{\\mathrm{d}}{\\mathrm{d}'+T+'} '+X(T)}}
			]
		}}
	}
},{
	name:'Continuous-time Fourier series (CTFS)',
	wikipedia:'http://en.wikipedia.org/wiki/Fourier_series',
	timeVar:'t',
	freqVar:'k',
	timeFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'x'+o.fnConj+'('+arg+')';
	},
	freqFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'a'+o.fnConj+'_{'+arg+'}';
	},
	sections:{
		definitions:function(x,X,t,T){return{
			time:[
				{formula:{
					item:x(t)+' = \\sum_{'+T+'=-\\infty}^{+\\infty} '+X(T)+' e^{j'+T+' \\omega_0 '+t+'}',
					notes:{b:'periodic function \\('+x(t)+'\\) of continuous variable \\('+t+'\\)
 with period \\(T\\)
 \\(\\omega_0=\\frac{2\\pi}{T}\\)'}
				}}
			],
			freq:[
				{formula:{
					item:X(T)+' = \\frac{1}{T} \\int\\limits_{\\langle T \\rangle} '+x(t)+' e^{-j'+T+' \\omega_0 '+t+'} \\,\\mathrm{d}'+t,
					notes:{b:'function \\('+X(T)+'\\) of discrete variable \\('+T+'\\)
 \\(\\omega_0=\\frac{2\\pi}{T}\\)'}
				}}
			]
		}},
		conjrev:function(x,X,t,T){return{
		}},
		modshift:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t+'+'+t+'_0')}},
				{formula:{item:'e^{-j'+T+'_0 \\omega_0 '+t+'}'+x(t)}},
				{formula:{item:x(t)}},
				{formula:{item:'e^{j'+T+'_0 \\omega_0 '+t+'}'+x(t)}},
				{formula:{item:x(t+'-'+t+'_0')}}
			],
			freq:[
				{formula:{item:'e^{j'+T+'\\omega_0 '+t+'_0}'+X(T)}},
				{formula:{item:X(''+T+'+'+T+'_0')}},
				{formula:{item:X(T)}},
				{formula:{item:X(''+T+'-'+T+'_0')}},
				{formula:{item:'e^{-j'+T+'\\omega_0 '+t+'_0}'+X(T)}}
			]
		}}
	}
},{
	name:'Discrete-time Fourier transform (DTFT)',
	wikipedia:'http://en.wikipedia.org/wiki/Discrete-time_Fourier_transform',
	timeVar:'n',
	freqVar:'\\omega',
	timeFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'x'+o.fnConj+'['+arg+']';
	},
	freqFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'X'+o.fnConj+'(e^{'+o.argSign+'j'+o.argRest+'})';
	},
	sections:{
		definitions:function(x,X,t,T){return{
			time:[
				{formula:{
					item:x(t)+' = \\frac{1}{2\\pi} \\int\\limits_{\\langle 2\\pi \\rangle} '+X(T)+' e^{j'+T+' '+t+'} \\,\\mathrm{d}'+T,
					notes:{b:'function \\('+x(t)+'\\) of discrete variable \\('+t+'\\)'}
				}}
			],
			freq:[
				{formula:{
					item:X(T)+' = \\sum_{'+t+'=-\\infty}^{+\\infty} '+x(t)+' e^{-j'+T+' '+t+'}',
					notes:{b:'periodic function \\('+X(T)+'\\) of continuous variable \\('+T+'\\)
 with period \\(2\\pi\\)'}
				}}
			]
		}},
		conjrev:function(x,X,t,T){return{
		}},
		modshift:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t+'+'+t+'_0')}},
				{formula:{item:'e^{-j'+T+'_0 '+t+'}'+x(t)}},
				{formula:{item:x(t)}},
				{formula:{item:'e^{j'+T+'_0 '+t+'}'+x(t)}},
				{formula:{item:x(t+'-'+t+'_0')}}
			],
			freq:[
				{formula:{item:'e^{j'+T+' '+t+'_0}'+X(T)}},
				{formula:{item:X(''+T+'+'+T+'_0')}},
				{formula:{item:X(T)}},
				{formula:{item:X(''+T+'-'+T+'_0')}},
				{formula:{item:'e^{-j'+T+' '+t+'_0}'+X(T)}}
			]
		}},
		intdiff:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t)}},
				{formula:{item:x(t)+'-'+x(t+'-1')}},
				{formula:{item:'-j '+t+' '+x(t)}}
			],
			freq:[
				{formula:{item:X(T)}},
				{formula:{item:'(1-e^{-j'+T+'}) '+X(T)}},
				{formula:{item:'\\frac{\\mathrm{d}}{\\mathrm{d}'+T+'} '+X(T)}}
			]
		}}
	}
},{
	name:'Discrete Fourier transform (DFT)',
	wikipedia:'http://en.wikipedia.org/wiki/Discrete_Fourier_transform',
	timeVar:'n',
	freqVar:'k',
	timeFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'x'+o.fnConj+'['+arg+']';
	},
	freqFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'X'+o.fnConj+'['+arg+']';
	},
	sections:{
		definitions:function(x,X,t,T){return{
			time:[
				{formula:{
					item:x(t)+' = \\frac 1 N \\sum_{'+T+'=0}^{N-1} '+X(T)+' W_N^{-'+T+' '+t+'}',
					notes:{b:'function \\('+x(t)+'\\) of discrete variable \\('+t+'\\)
 with support \\([0;N-1]\\);
 variable is interpreted modulo \\(N\\);
 \\(W_N = e^{-j \\frac{2\\pi}{N}}\\)'}
				}}
			],
			freq:[
				{formula:{
					item:X(T)+' = \\sum_{'+t+'=0}^{N-1} '+x(t)+' W_N^{'+T+' '+t+'}',
					notes:{b:'function \\('+X(T)+'\\) of discrete variable \\('+T+'\\)
 with support \\([0;N-1]\\);
 variable is interpreted modulo \\(N\\);
 \\(W_N = e^{-j \\frac{2\\pi}{N}}\\)'}
				}}
			]
		}},
		duality:function(x,X,t,T){return{
			time:[
				{formula:{item:'\\frac{1}{N} '+X('-'+t)}},
				{formula:{item:x(t)}},
				{formula:{item:X(t)}}
			],
			freq:[
				{formula:{item:x(T)}},
				{formula:{item:X(T)}},
				{formula:{item:'N '+x('-'+T)}}
			]
		}},
		conjrev:function(x,X,t,T){return{
		}},
		modshift:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t+'+'+t+'_0')}},
				{formula:{item:'W_N^{'+T+'_0 n}'+x(t)}},
				{formula:{item:x(t)}},
				{formula:{item:'W_N^{-'+T+'_0 n}'+x(t)}},
				{formula:{item:x(t+'-'+t+'_0')}}
			],
			freq:[
				{formula:{item:'W_N^{-'+T+' '+t+'_0}'+X(T)}},
				{formula:{item:X(T+'+'+T+'_0')}},
				{formula:{item:X(T)}},
				{formula:{item:X(T+'-'+T+'_0')}},
				{formula:{item:'W_N^{'+T+' '+t+'_0}'+X(T)}}
			]
		}},
		intdiff:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t)}},
				{formula:{item:x(t)+'-'+x(t+'-1')}},
				{formula:{item:'(1-W_N^{-'+t+'}) '+x(t)}}
			],
			freq:[
				{formula:{item:X(T)}},
				{formula:{item:'(1-W_N^'+T+') '+X(T)}},
				{formula:{item:X(T)+'-'+X(T+'-1')}}
			]
		}}
	}
}];
<commit_msg>CTFS intdiff props<commit_after>var transforms=[{
	name:'Continuous-time Fourier transform (CTFT)', // angular frequency, non-unitary
	wikipedia:'http://en.wikipedia.org/wiki/Fourier_transform',
	timeVar:'t',
	freqVar:'\\omega',
	timeFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'x'+o.fnConj+'('+arg+')';
	},
	freqFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'X'+o.fnConj+'('+o.argSign+'j'+o.argRest+')';
	},
	sections:{
		definitions:function(x,X,t,T){return{
			time:[
				{formula:{
					item:x(t)+' = \\frac{1}{2\\pi} \\int\\limits_{-\\infty}^{+\\infty} '+X(T)+' e^{j'+T+' '+t+'} \\,\\mathrm{d}'+T,
					notes:{b:'function \\('+x(t)+'\\) of continuous variable \\('+t+'\\)'}
				}}
			],
			freq:[
				{formula:{
					item:X(T)+' = \\int\\limits_{-\\infty}^{+\\infty} '+x(t)+' e^{-j'+T+' '+t+'} \\,\\mathrm{d}'+t,
					notes:{b:'function \\('+X(T)+'\\) of continuous variable \\('+T+'\\)'}
				}}
			]
		}},
		duality:function(x,X,t,T){return{
			time:[
				{formula:{item:'\\frac{1}{2\\pi} '+X('-'+t)}},
				{formula:{item:x(t)}},
				{formula:{item:X(t)}}
			],
			freq:[
				{formula:{item:x(T)}},
				{formula:{item:X(T)}},
				{formula:{item:'2\\pi '+x('-'+T)}}
			]
		}},
		conjrev:function(x,X,t,T){return{
		}},
		modshift:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t+'+'+t+'_0')}},
				{formula:{item:'e^{-j'+T+'_0 '+t+'}'+x(t)}},
				{formula:{item:x(t)}},
				{formula:{item:'e^{j'+T+'_0 '+t+'}'+x(t)}},
				{formula:{item:x(t+'-'+t+'_0')}}
			],
			freq:[
				{formula:{item:'e^{j'+T+' '+t+'_0}'+X(T)}},
				{formula:{item:X(''+T+'+'+T+'_0')}},
				{formula:{item:X(T)}},
				{formula:{item:X(''+T+'-'+T+'_0')}},
				{formula:{item:'e^{-j'+T+' '+t+'_0}'+X(T)}}
			]
		}},
		intdiff:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t)}},
				{formula:{item:'\\frac{\\mathrm{d}}{\\mathrm{d}'+t+'} '+x(t)}},
				{formula:{item:'-j '+t+' '+x(t)}}
			],
			freq:[
				{formula:{item:X(T)}},
				{formula:{item:'j'+T+' '+X(T)}},
				{formula:{item:'\\frac{\\mathrm{d}}{\\mathrm{d}'+T+'} '+X(T)}}
			]
		}}
	}
},{
	name:'Continuous-time Fourier series (CTFS)',
	wikipedia:'http://en.wikipedia.org/wiki/Fourier_series',
	timeVar:'t',
	freqVar:'k',
	timeFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'x'+o.fnConj+'('+arg+')';
	},
	freqFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'a'+o.fnConj+'_{'+arg+'}';
	},
	sections:{
		definitions:function(x,X,t,T){return{
			time:[
				{formula:{
					item:x(t)+' = \\sum_{'+T+'=-\\infty}^{+\\infty} '+X(T)+' e^{j'+T+' \\omega_0 '+t+'}',
					notes:{b:'periodic function \\('+x(t)+'\\) of continuous variable \\('+t+'\\)
 with period \\(T\\)
 \\(\\omega_0=\\frac{2\\pi}{T}\\)'}
				}}
			],
			freq:[
				{formula:{
					item:X(T)+' = \\frac{1}{T} \\int\\limits_{\\langle T \\rangle} '+x(t)+' e^{-j'+T+' \\omega_0 '+t+'} \\,\\mathrm{d}'+t,
					notes:{b:'function \\('+X(T)+'\\) of discrete variable \\('+T+'\\)
 \\(\\omega_0=\\frac{2\\pi}{T}\\)'}
				}}
			]
		}},
		conjrev:function(x,X,t,T){return{
		}},
		modshift:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t+'+'+t+'_0')}},
				{formula:{item:'e^{-j'+T+'_0 \\omega_0 '+t+'}'+x(t)}},
				{formula:{item:x(t)}},
				{formula:{item:'e^{j'+T+'_0 \\omega_0 '+t+'}'+x(t)}},
				{formula:{item:x(t+'-'+t+'_0')}}
			],
			freq:[
				{formula:{item:'e^{j'+T+'\\omega_0 '+t+'_0}'+X(T)}},
				{formula:{item:X(''+T+'+'+T+'_0')}},
				{formula:{item:X(T)}},
				{formula:{item:X(''+T+'-'+T+'_0')}},
				{formula:{item:'e^{-j'+T+'\\omega_0 '+t+'_0}'+X(T)}}
			]
		}},
		intdiff:function(x,X,t,T){return{
			cells:[
				'.|+|+',
				'.|.|.'
			],
			time:[
				{formula:{item:x(t)}},
				{formula:{item:'\\frac{\\mathrm{d}}{\\mathrm{d}'+t+'} '+x(t)}},
			],
			freq:[
				{formula:{item:X(T)}},
				{formula:{item:'j'+T+' \\omega_0 '+X(T)}},
			]
		}}
	}
},{
	name:'Discrete-time Fourier transform (DTFT)',
	wikipedia:'http://en.wikipedia.org/wiki/Discrete-time_Fourier_transform',
	timeVar:'n',
	freqVar:'\\omega',
	timeFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'x'+o.fnConj+'['+arg+']';
	},
	freqFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'X'+o.fnConj+'(e^{'+o.argSign+'j'+o.argRest+'})';
	},
	sections:{
		definitions:function(x,X,t,T){return{
			time:[
				{formula:{
					item:x(t)+' = \\frac{1}{2\\pi} \\int\\limits_{\\langle 2\\pi \\rangle} '+X(T)+' e^{j'+T+' '+t+'} \\,\\mathrm{d}'+T,
					notes:{b:'function \\('+x(t)+'\\) of discrete variable \\('+t+'\\)'}
				}}
			],
			freq:[
				{formula:{
					item:X(T)+' = \\sum_{'+t+'=-\\infty}^{+\\infty} '+x(t)+' e^{-j'+T+' '+t+'}',
					notes:{b:'periodic function \\('+X(T)+'\\) of continuous variable \\('+T+'\\)
 with period \\(2\\pi\\)'}
				}}
			]
		}},
		conjrev:function(x,X,t,T){return{
		}},
		modshift:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t+'+'+t+'_0')}},
				{formula:{item:'e^{-j'+T+'_0 '+t+'}'+x(t)}},
				{formula:{item:x(t)}},
				{formula:{item:'e^{j'+T+'_0 '+t+'}'+x(t)}},
				{formula:{item:x(t+'-'+t+'_0')}}
			],
			freq:[
				{formula:{item:'e^{j'+T+' '+t+'_0}'+X(T)}},
				{formula:{item:X(''+T+'+'+T+'_0')}},
				{formula:{item:X(T)}},
				{formula:{item:X(''+T+'-'+T+'_0')}},
				{formula:{item:'e^{-j'+T+' '+t+'_0}'+X(T)}}
			]
		}},
		intdiff:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t)}},
				{formula:{item:x(t)+'-'+x(t+'-1')}},
				{formula:{item:'-j '+t+' '+x(t)}}
			],
			freq:[
				{formula:{item:X(T)}},
				{formula:{item:'(1-e^{-j'+T+'}) '+X(T)}},
				{formula:{item:'\\frac{\\mathrm{d}}{\\mathrm{d}'+T+'} '+X(T)}}
			]
		}}
	}
},{
	name:'Discrete Fourier transform (DFT)',
	wikipedia:'http://en.wikipedia.org/wiki/Discrete_Fourier_transform',
	timeVar:'n',
	freqVar:'k',
	timeFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'x'+o.fnConj+'['+arg+']';
	},
	freqFn:function(arg,opts){
		var o=parseFunctionOptions(arg,opts);
		return 'X'+o.fnConj+'['+arg+']';
	},
	sections:{
		definitions:function(x,X,t,T){return{
			time:[
				{formula:{
					item:x(t)+' = \\frac 1 N \\sum_{'+T+'=0}^{N-1} '+X(T)+' W_N^{-'+T+' '+t+'}',
					notes:{b:'function \\('+x(t)+'\\) of discrete variable \\('+t+'\\)
 with support \\([0;N-1]\\);
 variable is interpreted modulo \\(N\\);
 \\(W_N = e^{-j \\frac{2\\pi}{N}}\\)'}
				}}
			],
			freq:[
				{formula:{
					item:X(T)+' = \\sum_{'+t+'=0}^{N-1} '+x(t)+' W_N^{'+T+' '+t+'}',
					notes:{b:'function \\('+X(T)+'\\) of discrete variable \\('+T+'\\)
 with support \\([0;N-1]\\);
 variable is interpreted modulo \\(N\\);
 \\(W_N = e^{-j \\frac{2\\pi}{N}}\\)'}
				}}
			]
		}},
		duality:function(x,X,t,T){return{
			time:[
				{formula:{item:'\\frac{1}{N} '+X('-'+t)}},
				{formula:{item:x(t)}},
				{formula:{item:X(t)}}
			],
			freq:[
				{formula:{item:x(T)}},
				{formula:{item:X(T)}},
				{formula:{item:'N '+x('-'+T)}}
			]
		}},
		conjrev:function(x,X,t,T){return{
		}},
		modshift:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t+'+'+t+'_0')}},
				{formula:{item:'W_N^{'+T+'_0 n}'+x(t)}},
				{formula:{item:x(t)}},
				{formula:{item:'W_N^{-'+T+'_0 n}'+x(t)}},
				{formula:{item:x(t+'-'+t+'_0')}}
			],
			freq:[
				{formula:{item:'W_N^{-'+T+' '+t+'_0}'+X(T)}},
				{formula:{item:X(T+'+'+T+'_0')}},
				{formula:{item:X(T)}},
				{formula:{item:X(T+'-'+T+'_0')}},
				{formula:{item:'W_N^{'+T+' '+t+'_0}'+X(T)}}
			]
		}},
		intdiff:function(x,X,t,T){return{
			time:[
				{formula:{item:x(t)}},
				{formula:{item:x(t)+'-'+x(t+'-1')}},
				{formula:{item:'(1-W_N^{-'+t+'}) '+x(t)}}
			],
			freq:[
				{formula:{item:X(T)}},
				{formula:{item:'(1-W_N^'+T+') '+X(T)}},
				{formula:{item:X(T)+'-'+X(T+'-1')}}
			]
		}}
	}
}];
<|endoftext|>


<commit_before>"use strict";
var Promise = require('mpromise');
var _ = require('lodash');
var app = require('../app');
var kodiService = require(app.dir + '/api/kodi-service.js').create({ host : process.env.KODI_HOST});

function alexaPostRequest(req,response,done){
    console.log(req.body);
    var intent = req.body.request.intent.name;

    if (intent == "PLAY"){
        kodiService.play().then(function(x){
            sendMessage(response, x.message);
        });
    }
    else if (intent == "PAUSE"){
        kodiService.pause().then(function(x){
            sendMessage(response, x.message);
        });
    }
    else if (intent == "STOP"){
        kodiService.stop().then(function(x){
            sendMessage(response, x.message);
        });
    }
    else if (intent == "PLAYING"){
        kodiService.info().then(function(x){
            sendMessage(response, x.currentlyPlaying);
        });
    }
    else if (intent === "PLAYRANDOM"){
        console.log(req.body.request.intent.slots[0].Show.value);
        kodiService.searchTVShows({label : req.body.request.intent.slots[0].Show.value})
            .then(function(x){
                kodiService.getTVShowEpisodes(x[0].tvshowid)
                    .then(function(y){
                        var randIdx = _.random(0,y.result.episodes.length);
                        kodiService.playEpisode(y.result.episodes[randIdx].episodeid).then(function(z){
                            sendMessage(response,"Playing episode " + y.result.episodes[randIdx].label + " of " + x.label);
                        })
                    })
            });
    }
    else{
        sendMessage(response, "So sorry");
    }
}

function sendMessage(response,text){
    var obj = {
        "version": "1.0",
        "sessionAttributes": {
        },
        "response": {
            "outputSpeech": {
                "type": "PlainText",
                "text": text
            },
            "card": {
                "type": "Simple",
                "title": "Kodi",
                "content": text
            },
            "shouldEndSession": true
        }
    };
    response.send(obj);
}

module.exports = exports;
exports.alexaPostRequest = alexaPostRequest;<commit_msg>endpoint<commit_after>"use strict";
var Promise = require('mpromise');
var _ = require('lodash');
var app = require('../app');
var kodiService = require(app.dir + '/api/kodi-service.js').create({ host : process.env.KODI_HOST});

function alexaPostRequest(req,response,done){
    console.log(req.body);
    var intent = req.body.request.intent.name;

    if (intent == "PLAY"){
        kodiService.play().then(function(x){
            sendMessage(response, x.message);
        });
    }
    else if (intent == "PAUSE"){
        kodiService.pause().then(function(x){
            sendMessage(response, x.message);
        });
    }
    else if (intent == "STOP"){
        kodiService.stop().then(function(x){
            sendMessage(response, x.message);
        });
    }
    else if (intent == "PLAYING"){
        kodiService.info().then(function(x){
            sendMessage(response, x.currentlyPlaying);
        });
    }
    else if (intent === "PLAYRANDOM"){
        console.log(req.body.request.intent.slots.Show.value);
        kodiService.searchTVShows({label : req.body.request.intent.slots.Show.value})
            .then(function(x){
                kodiService.getTVShowEpisodes(x[0].tvshowid)
                    .then(function(y){
                        var randIdx = _.random(0,y.result.episodes.length);
                        kodiService.playEpisode(y.result.episodes[randIdx].episodeid).then(function(z){
                            sendMessage(response,"Playing episode " + y.result.episodes[randIdx].label + " of " + x.label);
                        }).onReject(function(xx){
                            console.log(xx);
                            sendMessage(response,"So sorry");
                        });
                    }).onReject(function(xxx){
                        console.log(xxx);
                        sendMessage(response,"So sorry");
                    });
            }).onReject(function(x){
                console.log(x);
                sendMessage(response,"So sorry");
            });
    }
    else{
        sendMessage(response, "So sorry");
    }
}

function sendMessage(response,text){
    var obj = {
        "version": "1.0",
        "sessionAttributes": {
        },
        "response": {
            "outputSpeech": {
                "type": "PlainText",
                "text": text
            },
            "card": {
                "type": "Simple",
                "title": "Kodi",
                "content": text
            },
            "shouldEndSession": true
        }
    };
    response.send(obj);
}

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


<commit_before>'use strict';

const firebase = require('firebase');
const config = require('.././config');
const db = firebase.database();
const refIdea = db.ref().child('idea');
const refUser = db.ref().child('usuario');
const idea = require('.././models/modelIdea');
const modelIdea = require('.././models/modelResponse');
const refUsuarioCategoria = db.ref().child('usuarioxcategoria');

/**
 * Metodo para listar ideas
 * @param {http | https} req - Peticion
 * @param {http | https} res  - Respuesta
 */
function listarIdeas (req, res) {
	let data = idea.listaDeIdeas();
	res.status(200).send(modelIdea.modelResponse(
			null,
			null,
			null,
			true,
			`Se listaron las ideas`,
			data.length,
			data)
	);
}

/**
 * Metodo para crear una idea
 * @param {http | https} req - Peticion
 * @param {http | https} res  - Respuesta
 */
function crearIdea (req, res) {
	let nuevaIdea = refIdea.push();
	let key = nuevaIdea.toString().split('/idea/')[1];
	let num = req.body.numero;
	let ben = req. body.beneficios;
	let des = req.body.descripcion;
	let dis = req.body.disposicionesFinales;
	let efe = req.body.efectos;
	let fac = req.body.fechaActualizacion;
	let fel = req.body.fechaElaboracion;
	let form = req.body.formulaLegal;
	let mot = req.body.motivos;
	let tit = req.body.titular;
	let cat = req.body.categoria;

	nuevaIdea.set(idea.modelIdea(num, ben, des, dis, efe, fac, fel, form, mot, tit, cat));

	res.status(200).send(
		modelIdea.modelResponse(
			null,
			null,
			null,
			true,
			`Se registro la idea ${key}`,
			1,
			idea.modelIdea(num, ben, des, dis, efe, fac, fel, form, mot, tit, cat))
	);
}

function ideasPorCategoriaDelUsuario(req, res) {
	let email = req.user;
	let token = req.token;

	refUsuarioCategoria.on('value', (snap) => {
		let lista = snap.val();
		let user = null;
		let keyUser =null;

		for(let key in lista){
			console.log(lista[key].correo + ' < > '+ email);
			if (lista[key].correo == email) {
				user = lista[key];
				keyUser = key;
				console.log(user);
				break;
			}
		}

		if(user != null){
			refIdea.on('value', (snap) => {
				let listaIdea = snap.val();
				let ideasDelUsuario = [];
				let contador = 0;
				let arrayNombre = ['hola','Reduccion del peaje','Libros Virtuales','Reduccion medio pasaje','Matrimonio homosexual','Reduccion pension cibertec'];

				for(let key in listaIdea){
					contador++
					if (listaIdea[key].categoria == user.categoria) {
						let ideaUsu = {
							id : key,
							nombre :arrayNombre[listaIdea[key].numero],
							descripcion : listaIdea[key].descripcion,
							imagen : ['https://randomuser.me/api/portraits/women/10.jpg','https://randomuser.me/api/portraits/women/2.jpg','https://randomuser.me/api/portraits/women/6.jpg','https://randomuser.me/api/portraits/women/9.jpg'],
							like : 40,
							cantContrib : 50,
							idCreador : keyUser,
							nombreCreador : user.nombre,
							fotoCreador : '.img/fotoUser.jpg',
							fotoContribuidor : ['https://randomuser.me/api/portraits/women/10.jpg','https://randomuser.me/api/portraits/women/2.jpg','https://randomuser.me/api/portraits/women/6.jpg','https://randomuser.me/api/portraits/women/9.jpg'],
							sigueAlCreador : false
						}
						//ideasDelUsuario.push(listaIdea[key]);
						ideasDelUsuario.push(ideaUsu);
						console.log(user + ' <> ' + contador);
					}
				}

				res.status(200).send(modelIdea.modelResponse(token, null, null, true, 'Se listaron las ideas del usuario', ideasDelUsuario.length, ideasDelUsuario));

			});
		}else{
			res.status(404).send(modelIdea.modelResponse(token,null,null,false, 'El usuario no existe', 0, email));
		}
	});
}

function ideasSeguidas(req, res){

}

module.exports = {
	listarIdeas,
	crearIdea,
	ideasPorCategoriaDelUsuario
};

<commit_msg>Cambio<commit_after>'use strict';

const firebase = require('firebase');
const config = require('.././config');
const db = firebase.database();
const refIdea = db.ref().child('idea');
const refUser = db.ref().child('usuario');
const idea = require('.././models/modelIdea');
const modelIdea = require('.././models/modelResponse');
const refUsuarioCategoria = db.ref().child('usuarioxcategoria');

/**
 * Metodo para listar ideas
 * @param {http | https} req - Peticion
 * @param {http | https} res  - Respuesta
 */
function listarIdeas (req, res) {
	let data = idea.listaDeIdeas();
	res.status(200).send(modelIdea.modelResponse(
			null,
			null,
			null,
			true,
			`Se listaron las ideas`,
			data.length,
			data)
	);
}

/**
 * Metodo para crear una idea
 * @param {http | https} req - Peticion
 * @param {http | https} res  - Respuesta
 */
function crearIdea (req, res) {
	let nuevaIdea = refIdea.push();
	let key = nuevaIdea.toString().split('/idea/')[1];
	let num = req.body.numero;
	let ben = req. body.beneficios;
	let des = req.body.descripcion;
	let dis = req.body.disposicionesFinales;
	let efe = req.body.efectos;
	let fac = req.body.fechaActualizacion;
	let fel = req.body.fechaElaboracion;
	let form = req.body.formulaLegal;
	let mot = req.body.motivos;
	let tit = req.body.titular;
	let cat = req.body.categoria;

	nuevaIdea.set(idea.modelIdea(num, ben, des, dis, efe, fac, fel, form, mot, tit, cat));

	res.status(200).send(
		modelIdea.modelResponse(
			null,
			null,
			null,
			true,
			`Se registro la idea ${key}`,
			1,
			idea.modelIdea(num, ben, des, dis, efe, fac, fel, form, mot, tit, cat))
	);
}

function ideasPorCategoriaDelUsuario(req, res) {
	let email = req.user;
	let token = req.token;

	refUsuarioCategoria.on('value', (snap) => {
		let lista = snap.val();
		let user = null;
		let keyUser =null;

		for(let key in lista){
			console.log(lista[key].correo + ' < > '+ email);
			if (lista[key].correo == email) {
				user = lista[key];
				keyUser = key;
				console.log(user);
				break;
			}
		}

		if(user != null){
			refIdea.on('value', (snap) => {
				let listaIdea = snap.val();
				let ideasDelUsuario = [];
				///
				let nombres = ['','Yefrin Laura Chavez','Alonso Moreno Postigo','Bruno Landacay','Eduardo Leandro Pretel',];
				let cantidadUsuarios  = ['',50,45,80,102,87];
				let cantidadContribuyentes  = ['',98,14,50,6,98];
				let sigue = [true,true,true,false,true, false, false];
				let contador = 0;
				let fotos = ['',
					'https://randomuser.me/api/portraits/men/0.jpg',
					'https://randomuser.me/api/portraits/men/1.jpg',
					'https://randomuser.me/api/portraits/men/2.jpg',
					'https://randomuser.me/api/portraits/men/3.jpg',
					'https://randomuser.me/api/portraits/men/4.jpg',
					'https://randomuser.me/api/portraits/men/5.jpg'];
				let arrayNombre = ['hola','Reduccion del peaje','Libros Virtuales','Reduccion medio pasaje','Matrimonio homosexual','Reduccion pension cibertec'];

				for(let key in listaIdea){
					contador++
					if (listaIdea[key].categoria == user.categoria) {
						let ideaUsu = {
							id : key,
							nombre :arrayNombre[listaIdea[key].numero],
							descripcion : listaIdea[key].descripcion,
							imagen : ['https://randomuser.me/api/portraits/women/10.jpg','https://randomuser.me/api/portraits/women/2.jpg','https://randomuser.me/api/portraits/women/6.jpg','https://randomuser.me/api/portraits/women/9.jpg'],
							like : 40,
							cantContrib : cantidadContribuyentes[listaIdea[key].numero],
							idCreador : cantidadUsuarios[listaIdea[key].numero],
							nombreCreador : nombres[listaIdea[key].numero],
							fotoCreador : fotos[listaIdea[key].numero],
							fotoContribuidor : ['https://randomuser.me/api/portraits/women/10.jpg','https://randomuser.me/api/portraits/women/2.jpg','https://randomuser.me/api/portraits/women/6.jpg','https://randomuser.me/api/portraits/women/9.jpg'],
							sigueAlCreador : sigue[listaIdea[key].numero]
						}
						//ideasDelUsuario.push(listaIdea[key]);
						ideasDelUsuario.push(ideaUsu);
						console.log(user + ' <> ' + contador);
					}
				}

				res.status(200).send(modelIdea.modelResponse(token, null, null, true, 'Se listaron las ideas del usuario', ideasDelUsuario.length, ideasDelUsuario));

			});
		}else{
			res.status(404).send(modelIdea.modelResponse(token,null,null,false, 'El usuario no existe', 0, email));
		}
	});
}

function ideasSeguidas(req, res){

}

module.exports = {
	listarIdeas,
	crearIdea,
	ideasPorCategoriaDelUsuario
};

<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const moment = require('moment');

const prAmount = 5;

const statements = [
    'It\'s not too late to start!',
    'Off to a great start, keep going!',
    'Keep it up!',
    'Nice! Now, don\'t stop!',
    'So close!',
    'Way to go!',
    'Now you\'re just showing off!'
];

const errors = {
    notUser: 'Username must belong to a user account.'
};

const errorCodes = {
    notUser: 400
};

/**
 * GET /
 */
exports.index = (req, res) => {
    const github = req.app.get('github');
    const username = req.query.username;
    const statsLink = '/me';

    var hostname = `${req.protocol}://${req.headers.host}`;
    var today = new Date();
    var curmonth = today.getMonth();
    var timeleft = 31 - today.getDate();
    var timemessage = '';
    if (curmonth === 9) {
        if (timeleft === 0) {
            timemessage = 'It\'s the very last day! Get your last PRs in!';
        } else if (timeleft === 1) {
            timemessage = 'One more day, keep it going!';
        } else if (timeleft < 10) {
            timemessage = 'There\'s only ' + timeleft + ' days left! You can do it!';
        } else {
            timemessage = 'There\'s ' + timeleft + ' days remaining!';
        }
    }

    // in a reverse proxy situation we have to use the referer to retrieve
    // the correct protocol, hostname, and path
    // unfortunately this won't work, when accessng the page directly:
    // e.g.: http://example.com/hacktoberfest/?username=XXX
    // in such a case we set hostname to an empty string and create the link
    // with js after the page has loaded
    if (req.headers['x-forwarded-for']) {
        const referer = req.headers.referer;
        if (referer) {
            hostname = referer.split('?')[0].slice(0, -1);
            if (hostname.endsWith(statsLink.slice(0, -1))) {
                hostname = hostname.slice(0, -1*(statsLink.slice(0, -1).length));
            }
        } else {
            hostname = '';
        }
    }

    if (!username) {
        if (req.xhr) {
            return res.render('partials/error', { layout: false });
        }

        return res.render('index', {hostname: hostname, timemessage: timemessage});
    }
    function getStatement(prs) {
        if (curmonth < 9) {
            return 'Last year\'s result.';
        } else if (curmonth === 9) {
            return statements[prs.length < prAmount+1 ? prs.length : prAmount+1 ];
        } else {
            return 'This year\'s result.';
        }
    }

    Promise.all([
        findPrs(github, username),
        github.users.getForUser({username})
            .then(logCallsRemaining)
    ])
        .then(([prs, user]) => {
            if (user.data.type !== 'User') {
                return Promise.reject('notUser');
            }

            const data = {
                prs,
                isNotComplete: prs.length < prAmount,
                statement: getStatement(prs),
                username,
                userImage: user.data.avatar_url,
                hostname: hostname,
                prAmount
            };

            if (req.query['plain-data']) {
                res.render('partials/prs', _.assign(data, {layout: false}));
            } else {
                res.render('index', data);
            }
        }).catch((err) => {
            console.log(err);
            if (req.xhr) {
                const code = errorCodes[err] || 404;
                res.status(code).render('partials/error', {
                    layout: false, errorMsg: errors[err]
                });
            } else {
                res.render('index', {
                    error: true, errorMsg: errors[err], username
                });
            }
        });
};

let pullRequestData = [];

function getNextPage(response, github) {
    const promise = new Promise(function(resolve, reject) {
        github.getNextPage(response, function(err, res) {
            if (err) {
                reject();
                return false;
            }

            pullRequestData = pullRequestData.concat(res['data'].items);
            if (github.hasNextPage(res)) {
                getNextPage(res, github).then(function () {
                    resolve();
                });
            } else {
                if (process.env.NODE_ENV !== 'production') {
                    console.log('Found ' + pullRequestData.length + ' pull requests.');
                }
                resolve();
            }
        });
    });
    return promise;
}

function loadPrs(github, username) {
    const promise = new Promise(function(resolve, reject) {
        var today = new Date();
        var curmonth = today.getMonth();
        var curyear = today.getFullYear();
        var searchyear = curyear;
        if (curmonth < 9) {
            searchyear = curyear - 1;
        }
        github.search.issues({
            q: `-label:invalid+created:${searchyear}-09-30T00:00:00-12:00..${searchyear}-10-31T23:59:59-12:00+type:pr+is:public+author:${username}`,
            per_page: 100  // 30 is the default but this makes it clearer/allows it to be tweaked
        }, function(err, res) {
            if (err) {
                reject();
                return false;
            }

            pullRequestData = pullRequestData.concat(res['data'].items);
            if (github.hasNextPage(res)) {
                getNextPage(res, github).then(function () {
                    resolve();
                });
            } else {
                if (process.env.NODE_ENV !== 'production') {
                    console.log('Found ' + pullRequestData.length + ' pull requests.');
                }
                resolve();
            }
        });
    });

    return promise;
}

function findPrs(github, username) {
    pullRequestData = [];
    return loadPrs(github, username, pullRequestData)
        .then(function() {
            pullRequestData = _.map(pullRequestData, event => {
                const repo = event.pull_request.html_url.substring(0, event.pull_request.html_url.search('/pull/'));
                const hacktoberFestLabels = _.some(event.labels, label => label.name.toLowerCase() === 'hacktoberfest');

                return {
                    has_hacktoberfest_label: hacktoberFestLabels,
                    number: event.number,
                    open: event.state === 'open',
                    repo_name: repo.replace('https://github.com/', ''),
                    title: event.title,
                    url: event.html_url,
                    created_at: moment(event.created_at).format('MMMM Do YYYY'),
                    user: {
                        login: event.user.login,
                        url: event.user.html_url
                    },
                };
            });
            return Promise.resolve(pullRequestData);
        }).then(prs => {
            const checkMergeStatus = _.map(prs, pr => {
                const repoDetails = pr.repo_name.split('/');
                const pullDetails = {
                    owner: repoDetails[0],
                    repo: repoDetails[1],
                    number: pr.number
                };

                return github.pullRequests.checkMerged(pullDetails)
                    .then(logCallsRemaining)
                    .then(res => res.meta.status === '204 No Content')
                    .catch(err => {
                        // 404 means there wasn't a merge
                        if (err.code === 404) {
                            return false;
                        }

                        throw err;
                    });
            });

            return Promise
                .all(checkMergeStatus)
                .then(mergeStatus => Promise.resolve(_.zipWith(prs, mergeStatus, (pr, merged) => _.assign(pr, {merged}))));
        });
}

const logCallsRemaining = res => {
    var callsRemaining = res.meta['x-ratelimit-remaining'];
    if (process.env.NODE_ENV !== 'production') {
        console.log('API calls remaining: ' + callsRemaining);
    } else if (callsRemaining < 100) {
        console.log('API calls remaining: ' + callsRemaining);
    }
    return res;
};

exports.me = (req, res) => {
    res.render('me');
};

exports.notfound = (req, res) => {
    res.render('404');
};
<commit_msg>Ensure hostname is passed through<commit_after>'use strict';

const _ = require('lodash');
const moment = require('moment');

const prAmount = 5;

const statements = [
    'It\'s not too late to start!',
    'Off to a great start, keep going!',
    'Keep it up!',
    'Nice! Now, don\'t stop!',
    'So close!',
    'Way to go!',
    'Now you\'re just showing off!'
];

const errors = {
    notUser: 'Username must belong to a user account.'
};

const errorCodes = {
    notUser: 400
};

/**
 * GET /
 */
exports.index = (req, res) => {
    const github = req.app.get('github');
    const username = req.query.username;
    const statsLink = '/me';

    var hostname = `${req.protocol}://${req.headers.host}`;
    var today = new Date();
    var curmonth = today.getMonth();
    var timeleft = 31 - today.getDate();
    var timemessage = '';
    if (curmonth === 9) {
        if (timeleft === 0) {
            timemessage = 'It\'s the very last day! Get your last PRs in!';
        } else if (timeleft === 1) {
            timemessage = 'One more day, keep it going!';
        } else if (timeleft < 10) {
            timemessage = 'There\'s only ' + timeleft + ' days left! You can do it!';
        } else {
            timemessage = 'There\'s ' + timeleft + ' days remaining!';
        }
    }

    // in a reverse proxy situation we have to use the referer to retrieve
    // the correct protocol, hostname, and path
    // unfortunately this won't work, when accessng the page directly:
    // e.g.: http://example.com/hacktoberfest/?username=XXX
    // in such a case we set hostname to an empty string and create the link
    // with js after the page has loaded
    if (req.headers['x-forwarded-for']) {
        const referer = req.headers.referer;
        if (referer) {
            hostname = referer.split('?')[0].slice(0, -1);
            if (hostname.endsWith(statsLink.slice(0, -1))) {
                hostname = hostname.slice(0, -1*(statsLink.slice(0, -1).length));
            }
        } else {
            hostname = '';
        }
    }

    if (!username) {
        if (req.xhr) {
            return res.render('partials/error', {hostname: hostname, layout: false});
        }

        return res.render('index', {hostname: hostname, timemessage: timemessage});
    }
    function getStatement(prs) {
        if (curmonth < 9) {
            return 'Last year\'s result.';
        } else if (curmonth === 9) {
            return statements[prs.length < prAmount+1 ? prs.length : prAmount+1 ];
        } else {
            return 'This year\'s result.';
        }
    }

    Promise.all([
        findPrs(github, username),
        github.users.getForUser({username})
            .then(logCallsRemaining)
    ])
        .then(([prs, user]) => {
            if (user.data.type !== 'User') {
                return Promise.reject('notUser');
            }

            const data = {
                prs,
                isNotComplete: prs.length < prAmount,
                statement: getStatement(prs),
                username,
                userImage: user.data.avatar_url,
                hostname: hostname,
                prAmount
            };

            if (req.query['plain-data']) {
                res.render('partials/prs', _.assign(data, {layout: false}));
            } else {
                res.render('index', data);
            }
        }).catch((err) => {
            console.log(err);
            if (req.xhr) {
                const code = errorCodes[err] || 404;
                res.status(code).render('partials/error', {
                    hostname: hostname,
                    layout: false,
                    errorMsg: errors[err]
                });
            } else {
                res.render('index', {
                    hostname: hostname,
                    error: true,
                    errorMsg: errors[err],
                    username
                });
            }
        });
};

let pullRequestData = [];

function getNextPage(response, github) {
    const promise = new Promise(function(resolve, reject) {
        github.getNextPage(response, function(err, res) {
            if (err) {
                reject();
                return false;
            }

            pullRequestData = pullRequestData.concat(res['data'].items);
            if (github.hasNextPage(res)) {
                getNextPage(res, github).then(function () {
                    resolve();
                });
            } else {
                if (process.env.NODE_ENV !== 'production') {
                    console.log('Found ' + pullRequestData.length + ' pull requests.');
                }
                resolve();
            }
        });
    });
    return promise;
}

function loadPrs(github, username) {
    const promise = new Promise(function(resolve, reject) {
        var today = new Date();
        var curmonth = today.getMonth();
        var curyear = today.getFullYear();
        var searchyear = curyear;
        if (curmonth < 9) {
            searchyear = curyear - 1;
        }
        github.search.issues({
            q: `-label:invalid+created:${searchyear}-09-30T00:00:00-12:00..${searchyear}-10-31T23:59:59-12:00+type:pr+is:public+author:${username}`,
            per_page: 100  // 30 is the default but this makes it clearer/allows it to be tweaked
        }, function(err, res) {
            if (err) {
                reject();
                return false;
            }

            pullRequestData = pullRequestData.concat(res['data'].items);
            if (github.hasNextPage(res)) {
                getNextPage(res, github).then(function () {
                    resolve();
                });
            } else {
                if (process.env.NODE_ENV !== 'production') {
                    console.log('Found ' + pullRequestData.length + ' pull requests.');
                }
                resolve();
            }
        });
    });

    return promise;
}

function findPrs(github, username) {
    pullRequestData = [];
    return loadPrs(github, username, pullRequestData)
        .then(function() {
            pullRequestData = _.map(pullRequestData, event => {
                const repo = event.pull_request.html_url.substring(0, event.pull_request.html_url.search('/pull/'));
                const hacktoberFestLabels = _.some(event.labels, label => label.name.toLowerCase() === 'hacktoberfest');

                return {
                    has_hacktoberfest_label: hacktoberFestLabels,
                    number: event.number,
                    open: event.state === 'open',
                    repo_name: repo.replace('https://github.com/', ''),
                    title: event.title,
                    url: event.html_url,
                    created_at: moment(event.created_at).format('MMMM Do YYYY'),
                    user: {
                        login: event.user.login,
                        url: event.user.html_url
                    },
                };
            });
            return Promise.resolve(pullRequestData);
        }).then(prs => {
            const checkMergeStatus = _.map(prs, pr => {
                const repoDetails = pr.repo_name.split('/');
                const pullDetails = {
                    owner: repoDetails[0],
                    repo: repoDetails[1],
                    number: pr.number
                };

                return github.pullRequests.checkMerged(pullDetails)
                    .then(logCallsRemaining)
                    .then(res => res.meta.status === '204 No Content')
                    .catch(err => {
                        // 404 means there wasn't a merge
                        if (err.code === 404) {
                            return false;
                        }

                        throw err;
                    });
            });

            return Promise
                .all(checkMergeStatus)
                .then(mergeStatus => Promise.resolve(_.zipWith(prs, mergeStatus, (pr, merged) => _.assign(pr, {merged}))));
        });
}

const logCallsRemaining = res => {
    var callsRemaining = res.meta['x-ratelimit-remaining'];
    if (process.env.NODE_ENV !== 'production') {
        console.log('API calls remaining: ' + callsRemaining);
    } else if (callsRemaining < 100) {
        console.log('API calls remaining: ' + callsRemaining);
    }
    return res;
};

exports.me = (req, res) => {
    var hostname = `${req.protocol}://${req.headers.host}`;
    res.render('me', {hostname: hostname});
};

exports.notfound = (req, res) => {
    var hostname = `${req.protocol}://${req.headers.host}`;
    res.render('404', {hostname: hostname});
};
<|endoftext|>