<commit_before>var util = require('util');
var Connector = require('../connector');
var geo = require('../geo');
var utils = require('../utils');
var fs = require('fs');
var async = require('async');

/**
 * Initialize the Oracle connector against the given data source
 *
 * @param {DataSource} dataSource The loopback-datasource-juggler dataSource
 * @param {Function} [callback] The callback function
 */
exports.initialize = function initializeDataSource(dataSource, callback) {
  dataSource.connector = new Memory(null, dataSource.settings);
  dataSource.connector.connect(callback);
};

exports.Memory = Memory;

function Memory(m, settings) {
  if (m instanceof Memory) {
    this.isTransaction = true;
    this.cache = m.cache;
    this.ids = m.ids;
    this.constructor.super_.call(this, 'memory', settings);
    this._models = m._models;
  } else {
    this.isTransaction = false;
    this.cache = {};
    this.ids = {};
    this.constructor.super_.call(this, 'memory', settings);
  }
}

util.inherits(Memory, Connector);

Memory.prototype.getDefaultIdType = function() {
  return Number;
};

Memory.prototype.getTypes = function() {
  return ['db', 'nosql', 'memory'];
};

Memory.prototype.connect = function (callback) {
  if (this.isTransaction) {
    this.onTransactionExec = callback;
  } else {
    this.loadFromFile(callback);
  }
};

function serialize(obj) {
  if(obj === null || obj === undefined) {
    return obj;
  }
  return JSON.stringify(obj);
}

function deserialize(dbObj) {
  if(dbObj === null || dbObj === undefined) {
    return dbObj;
  }
  if(typeof dbObj === 'string') {
    return JSON.parse(dbObj);
  } else {
    return dbObj;
  }
}

Memory.prototype.loadFromFile = function(callback) {
  var self = this;
  if (self.settings.file) {
    fs.readFile(self.settings.file, {encoding: 'utf8', flag: 'r'}, function (err, data) {
      if (err && err.code !== 'ENOENT') {
        callback && callback(err);
      } else {
        if (data) {
          data = JSON.parse(data.toString());
          self.ids = data.ids || {};
          self.cache = data.models || {};
        } else {
          if(!self.cache) {
            self.ids = {};
            self.cache = {};
          }
        }
        callback && callback();
      }
    });
  } else {
    process.nextTick(callback);
  }
};

/*!
 * Flush the cache into the json file if necessary
 * @param {Function} callback
 */
Memory.prototype.saveToFile = function (result, callback) {
  var self = this;
  if (this.settings.file) {
    if(!self.writeQueue) {
      // Create a queue for writes
      self.writeQueue = async.queue(function (task, cb) {
        // Flush out the models/ids
        var data = JSON.stringify({
          ids: self.ids,
          models: self.cache
        }, null, '  ');

        fs.writeFile(self.settings.file, data, function (err) {
          cb(err);
          task.callback && task.callback(err, task.data);
        });
      }, 1);
    }
    // Enqueue the write
    self.writeQueue.push({
      data: result,
      callback: callback
    });
  } else {
    process.nextTick(function () {
      callback && callback(null, result);
    });
  }
};

Memory.prototype.define = function defineModel(definition) {
  this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
  var m = definition.model.modelName;
  if(!this.cache[m]) {
    this.cache[m] = {};
    this.ids[m] = 1;
  }
};

Memory.prototype.create = function create(model, data, callback) {
  // FIXME: [rfeng] We need to generate unique ids based on the id type
  // FIXME: [rfeng] We don't support composite ids yet
  var currentId = this.ids[model];
  if (currentId === undefined) {
    // First time
    this.ids[model] = 1;
    currentId = 1;
  }
  var id = this.getIdValue(model, data) || currentId;
  if (id > currentId) {
    // If the id is passed in and the value is greater than the current id
    currentId = id;
  }
  this.ids[model] = Number(currentId) + 1;

  var props = this._models[model].properties;
  var idName = this.idName(model);
  id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
  this.setIdValue(model, data, id);
  if(!this.cache[model]) {
    this.cache[model] = {};
  }
  this.cache[model][id] = serialize(data);
  this.saveToFile(id, callback);
};

Memory.prototype.updateOrCreate = function (model, data, callback) {
  var self = this;
  this.exists(model, self.getIdValue(model, data), function (err, exists) {
    if (exists) {
      self.save(model, data, callback);
    } else {
      self.create(model, data, function (err, id) {
        self.setIdValue(model, data, id);
        callback(err, data);
      });
    }
  });
};

Memory.prototype.save = function save(model, data, callback) {
  this.cache[model][this.getIdValue(model, data)] = serialize(data);
  this.saveToFile(data, callback);
};

Memory.prototype.exists = function exists(model, id, callback) {
  process.nextTick(function () {
    callback(null, this.cache[model] && this.cache[model].hasOwnProperty(id));
  }.bind(this));
};

Memory.prototype.find = function find(model, id, callback) {
  var self = this;
  process.nextTick(function () {
    callback(null, id in this.cache[model] && this.fromDb(model, this.cache[model][id]));
  }.bind(this));
};

Memory.prototype.destroy = function destroy(model, id, callback) {
  delete this.cache[model][id];
  this.saveToFile(null, callback);
};

Memory.prototype.fromDb = function (model, data) {
  if (!data) return null;
  data = deserialize(data);
  var props = this._models[model].properties;
  for (var key in data) {
    var val = data[key];
    if (val === undefined || val === null) {
      continue;
    }
    if (props[key]) {
      switch (props[key].type.name) {
        case 'Date':
          val = new Date(val.toString().replace(/GMT.*$/, 'GMT'));
          break;
        case 'Boolean':
          val = Boolean(val);
          break;
        case 'Number':
          val = Number(val);
          break;
      }
    }
    data[key] = val;
  }
  return data;
};

Memory.prototype.all = function all(model, filter, callback) {
  var self = this;
  var nodes = Object.keys(this.cache[model]).map(function (key) {
    return this.fromDb(model, this.cache[model][key]);
  }.bind(this));

  if (filter) {
    // do we need some sorting?
    if (filter.order) {
      var orders = filter.order;
      if (typeof filter.order === "string") {
        orders = [filter.order];
      }
      orders.forEach(function (key, i) {
        var reverse = 1;
        var m = key.match(/\s+(A|DE)SC$/i);
        if (m) {
          key = key.replace(/\s+(A|DE)SC/i, '');
          if (m[1].toLowerCase() === 'de') reverse = -1;
        }
        orders[i] = {"key": key, "reverse": reverse};
      });
      nodes = nodes.sort(sorting.bind(orders));
    }

    var nearFilter = geo.nearFilter(filter.where);

    // geo sorting
    if (nearFilter) {
      nodes = geo.filter(nodes, nearFilter);
    }

    // do we need some filtration?
    if (filter.where) {
      nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
    }

    // field selection
    if (filter.fields) {
      nodes = nodes.map(utils.selectFields(filter.fields));
    }

    // limit/skip
    filter.skip = filter.skip || 0;
    filter.limit = filter.limit || nodes.length;
    nodes = nodes.slice(filter.skip, filter.skip + filter.limit);
  }

  process.nextTick(function () {
    if (filter && filter.include) {
      self._models[model].model.include(nodes, filter.include, callback);
    } else {
      callback(null, nodes);
    }
  });

  function sorting(a, b) {
    for (var i = 0, l = this.length; i < l; i++) {
      if (a[this[i].key] > b[this[i].key]) {
        return 1 * this[i].reverse;
      } else if (a[this[i].key] < b[this[i].key]) {
        return -1 * this[i].reverse;
      }
    }
    return 0;
  }
};

function applyFilter(filter) {
  if (typeof filter.where === 'function') {
    return filter.where;
  }
  var keys = Object.keys(filter.where);
  return function (obj) {
    var pass = true;
    keys.forEach(function (key) {
      if (!test(filter.where[key], obj && obj[key])) {
        pass = false;
      }
    });
    return pass;
  }

  function test(example, value) {
    if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
      return value.match(example);
    }
    if (typeof example === 'undefined') return undefined;
    if (typeof value === 'undefined') return undefined;
    if (typeof example === 'object') {
      // ignore geo near filter
      if (example.near) return true;

      if (example.inq) {
        if (!value) return false;
        for (var i = 0; i < example.inq.length; i += 1) {
          if (example.inq[i] == value) return true;
        }
        return false;
      }

      if (isNum(example.gt) && example.gt < value) return true;
      if (isNum(example.gte) && example.gte <= value) return true;
      if (isNum(example.lt) && example.lt > value) return true;
      if (isNum(example.lte) && example.lte >= value) return true;
    }
    // not strict equality
    return (example !== null ? example.toString() : example) == (value !== null ? value.toString() : value);
  }

  function isNum(n) {
    return typeof n === 'number';
  }
}

Memory.prototype.destroyAll = function destroyAll(model, where, callback) {
  if (!callback && 'function' === typeof where) {
    callback = where;
    where = undefined;
  }
  var cache = this.cache[model];
  var filter = null;
  if (where) {
    filter = applyFilter({where: where});
  }
  Object.keys(cache).forEach(function (id) {
    if (!filter || filter(this.fromDb(model, cache[id]))) {
      delete cache[id];
    }
  }.bind(this));
  if (!where) {
    this.cache[model] = {};
  }
  this.saveToFile(null, callback);
};

Memory.prototype.count = function count(model, callback, where) {
  var cache = this.cache[model];
  var data = Object.keys(cache);
  if (where) {
    var filter = {where: where};
    data = data.map(function (id) {
      return this.fromDb(model, cache[id]);
    }.bind(this));
    data = data.filter(applyFilter(filter));
  }
  process.nextTick(function () {
    callback(null, data.length);
  });
};

Memory.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
  if (!id) {
    var err = new Error('You must provide an id when updating attributes!');
    if (cb) {
      return cb(err);
    } else {
      throw err;
    }
  }

  this.setIdValue(model, data, id);

  var cachedModels = this.cache[model];
  var modelData = cachedModels && this.cache[model][id];
  modelData = modelData && deserialize(modelData);

  if (modelData) {
    this.save(model, merge(modelData, data), cb);
  } else {
    cb(new Error('Could not update attributes. Object with id ' + id + ' does not exist!'));
  }
};

Memory.prototype.transaction = function () {
  return new Memory(this);
};

Memory.prototype.exec = function (callback) {
  this.onTransactionExec();
  setTimeout(callback, 50);
};

Memory.prototype.buildNearFilter = function (filter) {
  // noop
}

function merge(base, update) {
  if (!base) return update;
  Object.keys(update).forEach(function (key) {
    base[key] = update[key];
  });
  return base;
}<commit_msg>Local Storage<commit_after>var util = require('util');
var Connector = require('../connector');
var geo = require('../geo');
var utils = require('../utils');
var fs = require('fs');
var async = require('async');

/**
 * Initialize the Oracle connector against the given data source
 *
 * @param {DataSource} dataSource The loopback-datasource-juggler dataSource
 * @param {Function} [callback] The callback function
 */
exports.initialize = function initializeDataSource(dataSource, callback) {
  dataSource.connector = new Memory(null, dataSource.settings);
  dataSource.connector.connect(callback);
};

exports.Memory = Memory;

function Memory(m, settings) {
  if (m instanceof Memory) {
    this.isTransaction = true;
    this.cache = m.cache;
    this.ids = m.ids;
    this.constructor.super_.call(this, 'memory', settings);
    this._models = m._models;
  } else {
    this.isTransaction = false;
    this.cache = {};
    this.ids = {};
    this.constructor.super_.call(this, 'memory', settings);
  }
}

util.inherits(Memory, Connector);

Memory.prototype.getDefaultIdType = function() {
  return Number;
};

Memory.prototype.getTypes = function() {
  return ['db', 'nosql', 'memory'];
};

Memory.prototype.connect = function (callback) {
  if (this.isTransaction) {
    this.onTransactionExec = callback;
  } else {
    this.loadFromFile(callback);
  }
};

function serialize(obj) {
  if(obj === null || obj === undefined) {
    return obj;
  }
  return JSON.stringify(obj);
}

function deserialize(dbObj) {
  if(dbObj === null || dbObj === undefined) {
    return dbObj;
  }
  if(typeof dbObj === 'string') {
    return JSON.parse(dbObj);
  } else {
    return dbObj;
  }
}

Memory.prototype.loadFromFile = function(callback) {
  var self = this;
  var hasLocalStorage = typeof window !== 'undefined' && window.localStorage;
  var localStorage = hasLocalStorage && this.settings.localStorage;

  if (self.settings.file) {
    fs.readFile(self.settings.file, {encoding: 'utf8', flag: 'r'}, function (err, data) {
      if (err && err.code !== 'ENOENT') {
        callback && callback(err);
      } else {
        parseAndLoad(data);
      }
    });
  } else if(localStorage) {
    var data = window.localStorage.getItem(localStorage);
    data = data || '{}';
    parseAndLoad(data);
  } else {
    process.nextTick(callback);
  }

  function parseAndLoad(data) {
    if (data) {
      try {
        data = JSON.parse(data.toString());
      } catch(e) {
        return callback(e);
      }

      self.ids = data.ids || {};
      self.cache = data.models || {};
    } else {
      if(!self.cache) {
        self.ids = {};
        self.cache = {};
      }
    }
    callback && callback();
  }
};

/*!
 * Flush the cache into the json file if necessary
 * @param {Function} callback
 */
Memory.prototype.saveToFile = function (result, callback) {
  var self = this;
  var file = this.settings.file;
  var hasLocalStorage = typeof window !== 'undefined' && window.localStorage;
  var localStorage = hasLocalStorage && this.settings.localStorage;
  if (file) {
    if(!self.writeQueue) {
      // Create a queue for writes
      self.writeQueue = async.queue(function (task, cb) {
        // Flush out the models/ids
        var data = JSON.stringify({
          ids: self.ids,
          models: self.cache
        }, null, '  ');

        fs.writeFile(self.settings.file, data, function (err) {
          cb(err);
          task.callback && task.callback(err, task.data);
        });
      }, 1);
    }
    // Enqueue the write
    self.writeQueue.push({
      data: result,
      callback: callback
    });
  } else if (localStorage) {
    // Flush out the models/ids
    var data = JSON.stringify({
      ids: self.ids,
      models: self.cache
    }, null, '  ');
    window.localStorage.setItem(localStorage, data);
    process.nextTick(function () {
      callback && callback(null, result);
    });
  } else {
    process.nextTick(function () {
      callback && callback(null, result);
    });
  }
};

Memory.prototype.define = function defineModel(definition) {
  this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
  var m = definition.model.modelName;
  if(!this.cache[m]) {
    this.cache[m] = {};
    this.ids[m] = 1;
  }
};

Memory.prototype.create = function create(model, data, callback) {
  // FIXME: [rfeng] We need to generate unique ids based on the id type
  // FIXME: [rfeng] We don't support composite ids yet
  var currentId = this.ids[model];
  if (currentId === undefined) {
    // First time
    this.ids[model] = 1;
    currentId = 1;
  }
  var id = this.getIdValue(model, data) || currentId;
  if (id > currentId) {
    // If the id is passed in and the value is greater than the current id
    currentId = id;
  }
  this.ids[model] = Number(currentId) + 1;

  var props = this._models[model].properties;
  var idName = this.idName(model);
  id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
  this.setIdValue(model, data, id);
  if(!this.cache[model]) {
    this.cache[model] = {};
  }
  this.cache[model][id] = serialize(data);
  this.saveToFile(id, callback);
};

Memory.prototype.updateOrCreate = function (model, data, callback) {
  var self = this;
  this.exists(model, self.getIdValue(model, data), function (err, exists) {
    if (exists) {
      self.save(model, data, callback);
    } else {
      self.create(model, data, function (err, id) {
        self.setIdValue(model, data, id);
        callback(err, data);
      });
    }
  });
};

Memory.prototype.save = function save(model, data, callback) {
  this.cache[model][this.getIdValue(model, data)] = serialize(data);
  this.saveToFile(data, callback);
};

Memory.prototype.exists = function exists(model, id, callback) {
  process.nextTick(function () {
    callback(null, this.cache[model] && this.cache[model].hasOwnProperty(id));
  }.bind(this));
};

Memory.prototype.find = function find(model, id, callback) {
  var self = this;
  process.nextTick(function () {
    callback(null, id in this.cache[model] && this.fromDb(model, this.cache[model][id]));
  }.bind(this));
};

Memory.prototype.destroy = function destroy(model, id, callback) {
  delete this.cache[model][id];
  this.saveToFile(null, callback);
};

Memory.prototype.fromDb = function (model, data) {
  if (!data) return null;
  data = deserialize(data);
  var props = this._models[model].properties;
  for (var key in data) {
    var val = data[key];
    if (val === undefined || val === null) {
      continue;
    }
    if (props[key]) {
      switch (props[key].type.name) {
        case 'Date':
          val = new Date(val.toString().replace(/GMT.*$/, 'GMT'));
          break;
        case 'Boolean':
          val = Boolean(val);
          break;
        case 'Number':
          val = Number(val);
          break;
      }
    }
    data[key] = val;
  }
  return data;
};

Memory.prototype.all = function all(model, filter, callback) {
  var self = this;
  var nodes = Object.keys(this.cache[model]).map(function (key) {
    return this.fromDb(model, this.cache[model][key]);
  }.bind(this));

  if (filter) {
    // do we need some sorting?
    if (filter.order) {
      var orders = filter.order;
      if (typeof filter.order === "string") {
        orders = [filter.order];
      }
      orders.forEach(function (key, i) {
        var reverse = 1;
        var m = key.match(/\s+(A|DE)SC$/i);
        if (m) {
          key = key.replace(/\s+(A|DE)SC/i, '');
          if (m[1].toLowerCase() === 'de') reverse = -1;
        }
        orders[i] = {"key": key, "reverse": reverse};
      });
      nodes = nodes.sort(sorting.bind(orders));
    }

    var nearFilter = geo.nearFilter(filter.where);

    // geo sorting
    if (nearFilter) {
      nodes = geo.filter(nodes, nearFilter);
    }

    // do we need some filtration?
    if (filter.where) {
      nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
    }

    // field selection
    if (filter.fields) {
      nodes = nodes.map(utils.selectFields(filter.fields));
    }

    // limit/skip
    filter.skip = filter.skip || 0;
    filter.limit = filter.limit || nodes.length;
    nodes = nodes.slice(filter.skip, filter.skip + filter.limit);
  }

  process.nextTick(function () {
    if (filter && filter.include) {
      self._models[model].model.include(nodes, filter.include, callback);
    } else {
      callback(null, nodes);
    }
  });

  function sorting(a, b) {
    for (var i = 0, l = this.length; i < l; i++) {
      if (a[this[i].key] > b[this[i].key]) {
        return 1 * this[i].reverse;
      } else if (a[this[i].key] < b[this[i].key]) {
        return -1 * this[i].reverse;
      }
    }
    return 0;
  }
};

function applyFilter(filter) {
  if (typeof filter.where === 'function') {
    return filter.where;
  }
  var keys = Object.keys(filter.where);
  return function (obj) {
    var pass = true;
    keys.forEach(function (key) {
      if (!test(filter.where[key], obj && obj[key])) {
        pass = false;
      }
    });
    return pass;
  }

  function test(example, value) {
    if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
      return value.match(example);
    }
    if (typeof example === 'undefined') return undefined;
    if (typeof value === 'undefined') return undefined;
    if (typeof example === 'object') {
      // ignore geo near filter
      if (example.near) return true;

      if (example.inq) {
        if (!value) return false;
        for (var i = 0; i < example.inq.length; i += 1) {
          if (example.inq[i] == value) return true;
        }
        return false;
      }

      if (isNum(example.gt) && example.gt < value) return true;
      if (isNum(example.gte) && example.gte <= value) return true;
      if (isNum(example.lt) && example.lt > value) return true;
      if (isNum(example.lte) && example.lte >= value) return true;
    }
    // not strict equality
    return (example !== null ? example.toString() : example) == (value !== null ? value.toString() : value);
  }

  function isNum(n) {
    return typeof n === 'number';
  }
}

Memory.prototype.destroyAll = function destroyAll(model, where, callback) {
  if (!callback && 'function' === typeof where) {
    callback = where;
    where = undefined;
  }
  var cache = this.cache[model];
  var filter = null;
  if (where) {
    filter = applyFilter({where: where});
  }
  Object.keys(cache).forEach(function (id) {
    if (!filter || filter(this.fromDb(model, cache[id]))) {
      delete cache[id];
    }
  }.bind(this));
  if (!where) {
    this.cache[model] = {};
  }
  this.saveToFile(null, callback);
};

Memory.prototype.count = function count(model, callback, where) {
  var cache = this.cache[model];
  var data = Object.keys(cache);
  if (where) {
    var filter = {where: where};
    data = data.map(function (id) {
      return this.fromDb(model, cache[id]);
    }.bind(this));
    data = data.filter(applyFilter(filter));
  }
  process.nextTick(function () {
    callback(null, data.length);
  });
};

Memory.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
  if (!id) {
    var err = new Error('You must provide an id when updating attributes!');
    if (cb) {
      return cb(err);
    } else {
      throw err;
    }
  }

  this.setIdValue(model, data, id);

  var cachedModels = this.cache[model];
  var modelData = cachedModels && this.cache[model][id];
  modelData = modelData && deserialize(modelData);

  if (modelData) {
    this.save(model, merge(modelData, data), cb);
  } else {
    cb(new Error('Could not update attributes. Object with id ' + id + ' does not exist!'));
  }
};

Memory.prototype.transaction = function () {
  return new Memory(this);
};

Memory.prototype.exec = function (callback) {
  this.onTransactionExec();
  setTimeout(callback, 50);
};

Memory.prototype.buildNearFilter = function (filter) {
  // noop
}

function merge(base, update) {
  if (!base) return update;
  Object.keys(update).forEach(function (key) {
    base[key] = update[key];
  });
  return base;
}
<|endoftext|>


<commit_before>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2014 Laura Schlimmer
 *   Copyright (c) 2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
var DropDownComponent = function() {

  this.createdCallback = function() {
    var base = this;

    // dropdown items
    var items = this.querySelector("z-dropdown-items");
    if (items) {
      var cloned = items.cloneNode(true);
      this.setDropdownItems(cloned.children);
    }

    // header
    var header_elem = this.querySelector("z-dropdown-header");
    if (!header_elem) {
      header_elem = document.createElement("z-dropdown-header");
      this.insertBefore(header_elem, this.firstChild);
    }

    //render header value
    if (!header_elem.querySelector("z-drodpown-header-value")) {
      header_elem.appendChild(document.createElement(
        "z-dropdown-header-value"));
    }

    //render header icon
    if (!header_elem.querySelector("z-dropdown-header-icon")) {
      var icon = document.createElement("z-dropdown-header-icon");
      icon.innerHTML = "";
      header_elem.appendChild(icon);
    }

    header_elem.addEventListener('click', function(e) {
      e.stopPropagation();
      base.toggleDropdown();
    }, false);

    if (this.hasAttribute('data-hover')) {
      this.addEventListener(
        'mouseover',
        base.showDropdown.bind(base),
        false);

      this.addEventListener(
        'mouseout',
        base.hideDropdown.bind(base),
        false);
    }

    //close dropdown only if click event is outside
    this.addEventListener('click', function(e) {
      e.stopPropagation();
    }, false);

    this.__applyAttributes();
  };

  this.detachedCallback = function() {
    this.hideDropdown();
  }

  this.attributeChangedCallback = function(attr, old_val, new_val) {
    this.__applyAttributes();
  };

  /**
    * Returns either the selected items data-value attr or text content
    *
    * @return {String} csv
    */
  this.getValue = function() {
    var items = this.querySelectorAll("z-dropdown-item[data-selected]");
    var value = [];

    for (var i = 0; i < items.length; i++) {
      if (items[i].hasAttribute('data-value')) {
        value.push(items[i].getAttribute('data-value'));
      } else {
        value.push(items[i].textContent);
      }
    }

    return value.join(",");
  };

  /**
    * Marks for each value the matching dropdown-item as selected
    *
    * @param {Array} values
    */
  this.setValue = function(values) {
    var _this = this;

    values.forEach(function(v) {
      var item = _this.querySelector("z-dropdown-item[data-value='" + v + "']");

      if (item) {
        item.setAttribute('data-selected', 'selected');

        var checkbox = item.querySelector("z-checkbox");

        if (checkbox) {
          checkbox.setAttribute('data-active', 'active');
        }
      }
    });

    this.__setHeaderValue();
  }

  this.setDropdownItems = function(items) {
    var base = this;

    var elem = this.querySelector("z-dropdown-items");
    elem.innerHTML = "";

    for (var i = 0; i < items.length; i++) {
      items[i].addEventListener('click', function(e) {
        e.stopPropagation;
        base.__onItemClick(this);
      }, false);

      elem.appendChild(items[i]);
    }
  };

  this.toggleDropdown = function() {
    if (this.hasAttribute('data-active')) {
      this.hideDropdown();
    } else {
      this.showDropdown();
    }
  };

  this.showDropdown = function() {
    if (this.classList.contains("disabled")) {
      return;
    }

    if (this.getAttribute('data-active') == 'active') {
      return;
    }

    this.closeAllDropdowns();
    this.setAttribute('data-active', 'active');
    //this.setXValue();

    this.__setKeyNavigation();
  };

  this.hideDropdown = function() {
    if (this.getAttribute('data-active') != 'active') {
      return;
    }

    this.__unsetKeyNavigation();
    this.removeAttribute('data-active');

    var ev = new CustomEvent('z-dropdown-close', {
      bubbles: true,
      cancelable: true
    });

    this.dispatchEvent(ev);
  };

  /**
    * Close all open dropdowns
    */
  this.closeAllDropdowns = function(callback) {
    var active_items = document.querySelectorAll("z-dropdown[data-active]");

    for (var i = 0; i < active_items.length; i++) {
      if (typeof active_items[i].hideDropdown == 'function') {
        active_items[i].hideDropdown();
      }
    }

    if (callback) callback();
  };

  this.findItemByValue = function(value) {
    return (this.querySelector("z-dropdown-item[data-value='" + value + "']"));
  };

  /*************************** PRIVATE **********************************/

  this.__applyAttributes = function() {
    this.__setHeaderValue();
  }

  // Copies the selected items html to the header_elem
  this.__setHeaderValue = function() {
    var header = this.querySelector("z-dropdown-header-value");

    if (header.hasAttribute('data-static')) {
      return;
    }

    var innerHTML = "";

    //multi selectable dropdown
    if (this.classList.contains('checkbox')) {
      var selected_items = this.querySelectorAll("z-dropdown-item[data-selected]");

      for (var i = 0; i < selected_items.length; i++) {
        if (i > 0) {
          innerHTML += ", ";
        }

        innerHTML += selected_items[i].innerText || selected_items[i].textContent;
      }

    } else {

      var selected_item = this.querySelector("z-dropdown-item[data-selected]");
      if (!selected_item) {

        var preselection = this.getAttribute('data-preselected');
        //set preselected item
        if (preselection) {
          var preselected_item = this.findItemByValue(preselection);

          preselected_item.setAttribute('data-selected', 'selected');
          innerHTML = preselected_item.innerHTML;

        } else {
          //z-dropdown has initial not selectable header
          if (header.innerHTML.length > 0) {
            return;
          }

          //set first z-dropdown-item as header value
          var first_item = this.querySelector("z-dropdown-item");
          if (first_item) {
            innerHTML = first_item.innerHTML;
          }
        }
      } else {
        //set items input value as html header
        if (selected_item.hasAttribute('data-input-select')) {
          var input = selected_item.querySelector("input");
          innerHTML = input.value;

        } else {
          //set selected_item's html as header (standard case)
          innerHTML = selected_item.innerHTML;
        }
      }
    }

    header.innerHTML = innerHTML;
  };

  this.__onItemClick = function(item) {
    if (!item) {
      return;
    }

    var input_elem = this.querySelector("z-input");
    var selected = true;

    //multi-selectable dropdown with checkboxes
    if (this.classList.contains('checkbox')) {
      var checkbox = item.querySelector("z-checkbox") || item.querySelector("[data-check]");

      if (item.hasAttribute('data-selected')) {
        selected = false;
        item.removeAttribute('data-selected');
        checkbox.removeAttribute('data-active');

        //not completely unselectable dropdown
        if (this.hasAttribute('data-force-select') &&
            !this.querySelector('z-dropdown-item[data-selected]')) {

          var default_select = this.querySelector('z-dropdown-item[data-default]');

          if (default_select) {
            default_select.setAttribute('data-selected', 'selected');
            checkbox = default_select.querySelector('z-checkbox');

            if (checkbox) {
              checkbox.setAttribute('data-active', 'active');
            }
          }
        }
      } else {
        checkbox.setAttribute('data-active', 'active');
      }

    } else {
      var prev_item = this.querySelector("z-dropdown-item[data-selected]");

      if (prev_item) {
        prev_item.removeAttribute('data-selected');
      }
    }

    if (selected) {
      item.setAttribute("data-selected", 'selected');
    }

    if (input_elem) {
      input_elem.setAttribute('data-value', item.innerHTML);
    } else {
      this.__setHeaderValue();
    }

    var click_ev = new CustomEvent(
        (selected) ? "z-dropdown-item-click" : "z-dropdown-item-unselect",
        {
            detail : {'text' : item.textContent},
            bubbles: true,
            cancelable: true
        });

    item.dispatchEvent(click_ev);

    // FIXME: abort if this is a multiselect
    this.hideDropdown();

    var change_ev = new CustomEvent(
        "change",
        {
          detail : {
            value: this.getValue()
          },
          bubbles: true,
          cancelable: true
        });

    this.dispatchEvent(change_ev);
  };

  this.__setKeyNavigation = function() {
    var base = this;
    this.__unsetKeyNavigation();

    this.keydown_handler = function(e) {
      switch (e.keyCode) {
        case 38:
          base.__keyNavigation('up');
          e.preventDefault();
          return false;

        case 40:
          base.__keyNavigation('down');
          e.preventDefault();
          return false;

        case 13:
          base.__onItemClick(base.querySelector("z-dropdown-item.hover"), true);
          e.preventDefault();
          return false;

        case 27:
          base.hideDropdown();
          e.preventDefault();
          return false;

        default:
          return true;
      }
    };

    window.addEventListener('keydown', this.keydown_handler, false);
  };

  this.__unsetKeyNavigation = function() {
    window.removeEventListener('keydown', this.keydown_handler, false);
  };

  this.__keyNavigation = function(direction) {
    var items = this.querySelector("z-dropdown-items");
    var visibleScrollbar = items.scrollHeight > items.offsetHeight;

    var hover_elem = this.querySelector("z-dropdown-item.hover");
    var ref_elem;
    var elem = null;
    var items = this.querySelectorAll("z-dropdown-item");

    if (!hover_elem) {
      if (direction == 'down') {
        //filtered Dropdown can contain hidden items
        if (items[0].classList.contains('hidden')) {
          hover_elem = items[0];
        } else {
          items[0].classList.add('hover');
          return;
        }
      }
    }

    ref_elem = hover_elem;
    for (var i = 0; i < items.length; i++) {

      //down
      if (direction == 'down') {
        elem = ref_elem.nextElementSibling;

        //wrap around and mark first item
        if (!elem) {
          elem = items[0];

          if (visibleScrollbar) {
            var d_items = this.querySelector("z-dropdown-items");
            d_items.scrollTop = 0;
          }

        } else {

          if (visibleScrollbar) {
            var d_items = this.querySelector("z-dropdown-items");
            d_items.scrollTop = d_items.scrollTop + (d_items.offsetHeight / items.length);
          }
        }

      //up
      } else {
        elem = ref_elem.previousElementSibling;

        //wrap around and mark last item
        if (!elem) {
          elem = items[items.length - 1];

          if (visibleScrollbar) {
            var d_items = this.querySelector("z-dropdown-items");
            d_items.scrollTop = d_items.scrollHeight;
          }

        } else {

          if (visibleScrollbar) {
            var d_items = this.querySelector("z-dropdown-items");
            d_items.scrollTop = d_items.scrollTop - (d_items.offsetHeight / items.length);
          }

        }
      }

      if (elem.classList.contains('hidden')) {
        ref_elem = elem;
      } else {
        elem.classList.add('hover');
        hover_elem.classList.remove('hover');
        return;
      }
    }
  };

  /******************** Searchable Dropdown *************************/

  //this.renderSearchable = function() {
  //  var dropdown_input = this.querySelector("z-dropdown-input");
  //  if (!dropdown_input) {
  //    return;
  //  }

  //  var base = this;
  //  var input = document.createElement("z-input");
  //  var placeholder = dropdown_input.getAttribute('data-placeholder');
  //  var selected_item = this.querySelector("z-dropdown-item[data-selected");

  //  if (placeholder) {
  //    input.setAttribute('data-placeholder', placeholder);
  //  }

  //  if (selected_item) {
  //    input.setAttribute('data-value', selected_item.textContent);
  //  }

  //  dropdown_input.appendChild(input);
  //  input.addEventListener('click', function(e) {
  //    e.stopPropagation();
  //    this.setAttribute('data-value', "");
  //    base.showDropdown();
  //  }, false);

  //  this.setKeyNavigation(input);
  //};

  //this.showFilteredDropdownItems = function(value) {
  //  var items = this.querySelectorAll("z-dropdown-item");
  //  var value = value.toLowerCase();

  //  for (var i = 0; i < items.length; i++) {
  //    if (items[i].textContent.toLowerCase().indexOf(value) > -1) {
  //      items[i].classList.remove('hidden');
  //    } else {
  //      items[i].classList.add('hidden');
  //    }
  //  }

  //  this.showDropdown();
  //};

  //this.setXValue = function() {
  //  var items = this.querySelector("z-dropdown-items");
  //  var pos = items.getBoundingClientRect();
  //  var margin;

  //  if (document.body.offsetWidth < pos.right) {
  //    margin = pos.right - document.body.offsetWidth;

  //    //visible y-scrollbar
  //    if (items.scrollHeight > pos.height) {
  //      margin += 17;
  //    }

  //    items.style.marginLeft = "-" + margin + "px";
  //  }
  //}
};

var proto = Object.create(HTMLElement.prototype);
DropDownComponent.apply(proto);
document.registerElement("z-dropdown", { prototype: proto });
<commit_msg>close dropdown on background click<commit_after>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2014 Laura Schlimmer
 *   Copyright (c) 2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
var DropDownComponent = function() {

  this.createdCallback = function() {
    var base = this;

    // dropdown items
    var items = this.querySelector("z-dropdown-items");
    if (items) {
      var cloned = items.cloneNode(true);
      this.setDropdownItems(cloned.children);
    }

    // header
    var header_elem = this.querySelector("z-dropdown-header");
    if (!header_elem) {
      header_elem = document.createElement("z-dropdown-header");
      this.insertBefore(header_elem, this.firstChild);
    }

    //render header value
    if (!header_elem.querySelector("z-drodpown-header-value")) {
      header_elem.appendChild(document.createElement(
        "z-dropdown-header-value"));
    }

    //render header icon
    if (!header_elem.querySelector("z-dropdown-header-icon")) {
      var icon = document.createElement("z-dropdown-header-icon");
      icon.innerHTML = "";
      header_elem.appendChild(icon);
    }

    header_elem.addEventListener('click', function(e) {
      e.stopPropagation();
      base.toggleDropdown();
    }, false);

    if (this.hasAttribute('data-hover')) {
      this.addEventListener(
        'mouseover',
        base.showDropdown.bind(base),
        false);

      this.addEventListener(
        'mouseout',
        base.hideDropdown.bind(base),
        false);
    }

    //close dropdown only if click event is outside
    this.addEventListener('click', function(e) {
      e.stopPropagation();
    }, false);

    this.__applyAttributes();
  };

  this.detachedCallback = function() {
    this.hideDropdown();
  }

  this.attributeChangedCallback = function(attr, old_val, new_val) {
    this.__applyAttributes();
  };

  /**
    * Returns either the selected items data-value attr or text content
    *
    * @return {String} csv
    */
  this.getValue = function() {
    var items = this.querySelectorAll("z-dropdown-item[data-selected]");
    var value = [];

    for (var i = 0; i < items.length; i++) {
      if (items[i].hasAttribute('data-value')) {
        value.push(items[i].getAttribute('data-value'));
      } else {
        value.push(items[i].textContent);
      }
    }

    return value.join(",");
  };

  /**
    * Marks for each value the matching dropdown-item as selected
    *
    * @param {Array} values
    */
  this.setValue = function(values) {
    var _this = this;

    values.forEach(function(v) {
      var item = _this.querySelector("z-dropdown-item[data-value='" + v + "']");

      if (item) {
        item.setAttribute('data-selected', 'selected');

        var checkbox = item.querySelector("z-checkbox");

        if (checkbox) {
          checkbox.setAttribute('data-active', 'active');
        }
      }
    });

    this.__setHeaderValue();
  }

  this.setDropdownItems = function(items) {
    var base = this;

    var elem = this.querySelector("z-dropdown-items");
    elem.innerHTML = "";

    for (var i = 0; i < items.length; i++) {
      items[i].addEventListener('click', function(e) {
        e.stopPropagation;
        base.__onItemClick(this);
      }, false);

      elem.appendChild(items[i]);
    }
  };

  this.toggleDropdown = function() {
    if (this.hasAttribute('data-active')) {
      this.hideDropdown();
    } else {
      this.showDropdown();
    }
  };

  this.showDropdown = function() {
    if (this.classList.contains("disabled")) {
      return;
    }

    if (this.getAttribute('data-active') == 'active') {
      return;
    }

    this.closeAllDropdowns();
    this.setAttribute('data-active', 'active');
    //this.setXValue();

    this.__setKeyNavigation();
  };

  this.hideDropdown = function() {
    if (this.getAttribute('data-active') != 'active') {
      return;
    }

    this.__unsetKeyNavigation();
    this.removeAttribute('data-active');

    var ev = new CustomEvent('z-dropdown-close', {
      bubbles: true,
      cancelable: true
    });

    this.dispatchEvent(ev);
  };

  /**
    * Close all open dropdowns
    */
  this.closeAllDropdowns = function(callback) {
    var active_items = document.querySelectorAll("z-dropdown[data-active]");

    for (var i = 0; i < active_items.length; i++) {
      if (typeof active_items[i].hideDropdown == 'function') {
        active_items[i].hideDropdown();
      }
    }

    if (callback) callback();
  };

  this.findItemByValue = function(value) {
    return (this.querySelector("z-dropdown-item[data-value='" + value + "']"));
  };

  /*************************** PRIVATE **********************************/

  this.__applyAttributes = function() {
    this.__setHeaderValue();
  }

  // Copies the selected items html to the header_elem
  this.__setHeaderValue = function() {
    var header = this.querySelector("z-dropdown-header-value");

    if (header.hasAttribute('data-static')) {
      return;
    }

    var innerHTML = "";

    //multi selectable dropdown
    if (this.classList.contains('checkbox')) {
      var selected_items = this.querySelectorAll("z-dropdown-item[data-selected]");

      for (var i = 0; i < selected_items.length; i++) {
        if (i > 0) {
          innerHTML += ", ";
        }

        innerHTML += selected_items[i].innerText || selected_items[i].textContent;
      }

    } else {

      var selected_item = this.querySelector("z-dropdown-item[data-selected]");
      if (!selected_item) {

        var preselection = this.getAttribute('data-preselected');
        //set preselected item
        if (preselection) {
          var preselected_item = this.findItemByValue(preselection);

          preselected_item.setAttribute('data-selected', 'selected');
          innerHTML = preselected_item.innerHTML;

        } else {
          //z-dropdown has initial not selectable header
          if (header.innerHTML.length > 0) {
            return;
          }

          //set first z-dropdown-item as header value
          var first_item = this.querySelector("z-dropdown-item");
          if (first_item) {
            innerHTML = first_item.innerHTML;
          }
        }
      } else {
        //set items input value as html header
        if (selected_item.hasAttribute('data-input-select')) {
          var input = selected_item.querySelector("input");
          innerHTML = input.value;

        } else {
          //set selected_item's html as header (standard case)
          innerHTML = selected_item.innerHTML;
        }
      }
    }

    header.innerHTML = innerHTML;
  };

  this.__onItemClick = function(item) {
    if (!item) {
      return;
    }

    var input_elem = this.querySelector("z-input");
    var selected = true;

    //multi-selectable dropdown with checkboxes
    if (this.classList.contains('checkbox')) {
      var checkbox = item.querySelector("z-checkbox") || item.querySelector("[data-check]");

      if (item.hasAttribute('data-selected')) {
        selected = false;
        item.removeAttribute('data-selected');
        checkbox.removeAttribute('data-active');

        //not completely unselectable dropdown
        if (this.hasAttribute('data-force-select') &&
            !this.querySelector('z-dropdown-item[data-selected]')) {

          var default_select = this.querySelector('z-dropdown-item[data-default]');

          if (default_select) {
            default_select.setAttribute('data-selected', 'selected');
            checkbox = default_select.querySelector('z-checkbox');

            if (checkbox) {
              checkbox.setAttribute('data-active', 'active');
            }
          }
        }
      } else {
        checkbox.setAttribute('data-active', 'active');
      }

    } else {
      var prev_item = this.querySelector("z-dropdown-item[data-selected]");

      if (prev_item) {
        prev_item.removeAttribute('data-selected');
      }
    }

    if (selected) {
      item.setAttribute("data-selected", 'selected');
    }

    if (input_elem) {
      input_elem.setAttribute('data-value', item.innerHTML);
    } else {
      this.__setHeaderValue();
    }

    var click_ev = new CustomEvent(
        (selected) ? "z-dropdown-item-click" : "z-dropdown-item-unselect",
        {
            detail : {'text' : item.textContent},
            bubbles: true,
            cancelable: true
        });

    item.dispatchEvent(click_ev);

    // FIXME: abort if this is a multiselect
    this.hideDropdown();

    var change_ev = new CustomEvent(
        "change",
        {
          detail : {
            value: this.getValue()
          },
          bubbles: true,
          cancelable: true
        });

    this.dispatchEvent(change_ev);
  };

  this.__setKeyNavigation = function() {
    var base = this;
    this.__unsetKeyNavigation();

    this.keydown_handler = function(e) {
      switch (e.keyCode) {
        case 38:
          base.__keyNavigation('up');
          e.preventDefault();
          return false;

        case 40:
          base.__keyNavigation('down');
          e.preventDefault();
          return false;

        case 13:
          base.__onItemClick(base.querySelector("z-dropdown-item.hover"), true);
          e.preventDefault();
          return false;

        case 27:
          base.hideDropdown();
          e.preventDefault();
          return false;

        default:
          return true;
      }
    };

    window.addEventListener('keydown', this.keydown_handler, false);

    this.click_handler = function(e) {
      base.hideDropdown();
    };

    window.addEventListener('click', this.click_handler, false);
  };

  this.__unsetKeyNavigation = function() {
    window.removeEventListener('keydown', this.keydown_handler, false);
    window.removeEventListener('click', this.click_handler, false);
  };

  this.__keyNavigation = function(direction) {
    var items = this.querySelector("z-dropdown-items");
    var visibleScrollbar = items.scrollHeight > items.offsetHeight;

    var hover_elem = this.querySelector("z-dropdown-item.hover");
    var ref_elem;
    var elem = null;
    var items = this.querySelectorAll("z-dropdown-item");

    if (!hover_elem) {
      if (direction == 'down') {
        //filtered Dropdown can contain hidden items
        if (items[0].classList.contains('hidden')) {
          hover_elem = items[0];
        } else {
          items[0].classList.add('hover');
          return;
        }
      }
    }

    ref_elem = hover_elem;
    for (var i = 0; i < items.length; i++) {

      //down
      if (direction == 'down') {
        elem = ref_elem.nextElementSibling;

        //wrap around and mark first item
        if (!elem) {
          elem = items[0];

          if (visibleScrollbar) {
            var d_items = this.querySelector("z-dropdown-items");
            d_items.scrollTop = 0;
          }

        } else {

          if (visibleScrollbar) {
            var d_items = this.querySelector("z-dropdown-items");
            d_items.scrollTop = d_items.scrollTop + (d_items.offsetHeight / items.length);
          }
        }

      //up
      } else {
        elem = ref_elem.previousElementSibling;

        //wrap around and mark last item
        if (!elem) {
          elem = items[items.length - 1];

          if (visibleScrollbar) {
            var d_items = this.querySelector("z-dropdown-items");
            d_items.scrollTop = d_items.scrollHeight;
          }

        } else {

          if (visibleScrollbar) {
            var d_items = this.querySelector("z-dropdown-items");
            d_items.scrollTop = d_items.scrollTop - (d_items.offsetHeight / items.length);
          }

        }
      }

      if (elem.classList.contains('hidden')) {
        ref_elem = elem;
      } else {
        elem.classList.add('hover');
        hover_elem.classList.remove('hover');
        return;
      }
    }
  };

  /******************** Searchable Dropdown *************************/

  //this.renderSearchable = function() {
  //  var dropdown_input = this.querySelector("z-dropdown-input");
  //  if (!dropdown_input) {
  //    return;
  //  }

  //  var base = this;
  //  var input = document.createElement("z-input");
  //  var placeholder = dropdown_input.getAttribute('data-placeholder');
  //  var selected_item = this.querySelector("z-dropdown-item[data-selected");

  //  if (placeholder) {
  //    input.setAttribute('data-placeholder', placeholder);
  //  }

  //  if (selected_item) {
  //    input.setAttribute('data-value', selected_item.textContent);
  //  }

  //  dropdown_input.appendChild(input);
  //  input.addEventListener('click', function(e) {
  //    e.stopPropagation();
  //    this.setAttribute('data-value', "");
  //    base.showDropdown();
  //  }, false);

  //  this.setKeyNavigation(input);
  //};

  //this.showFilteredDropdownItems = function(value) {
  //  var items = this.querySelectorAll("z-dropdown-item");
  //  var value = value.toLowerCase();

  //  for (var i = 0; i < items.length; i++) {
  //    if (items[i].textContent.toLowerCase().indexOf(value) > -1) {
  //      items[i].classList.remove('hidden');
  //    } else {
  //      items[i].classList.add('hidden');
  //    }
  //  }

  //  this.showDropdown();
  //};

  //this.setXValue = function() {
  //  var items = this.querySelector("z-dropdown-items");
  //  var pos = items.getBoundingClientRect();
  //  var margin;

  //  if (document.body.offsetWidth < pos.right) {
  //    margin = pos.right - document.body.offsetWidth;

  //    //visible y-scrollbar
  //    if (items.scrollHeight > pos.height) {
  //      margin += 17;
  //    }

  //    items.style.marginLeft = "-" + margin + "px";
  //  }
  //}
};

var proto = Object.create(HTMLElement.prototype);
DropDownComponent.apply(proto);
document.registerElement("z-dropdown", { prototype: proto });
<|endoftext|>


<commit_before>(function(T, doc) {

	T.FlowHandler = Object.augment(function() {

		this.activate = function() {
			T.utils.getEvents().start.forEach(function(event) {
				this.on(this.element, event);
			}, this);
		};

		this.deactivate = function() {
			T.utils.getEvents().start.forEach(function(event) {
				this.off(this.element, event);
			}, this);
			this.bindDoc(false);
			this.resetFlowTypes();
			T.cache.remove(this.element);
		};

		this.on = function(element, event) {
			if(T.utils.msPointer) {
				if(element.style && element.style.msTouchAction) {
					this.msTouchAction = element.style.msTouchAction;
				} else if (!element.style) {
					element.style = '';
				}
				element.style.msTouchAction = 'none';
			}
			element.addEventListener(event, this, false);
		};

		this.off = function(element, event) {
			element.removeEventListener(event, this, false);
			if(T.utils.msPointer) {
				if(element.style) {
					element.style.msTouchAction = this.msTouchAction;
				}
			}
		};

		this.bindDoc = function(on, type) {
			var flowTypes = T.utils.getFlowTypes(type),
				events;
			flowTypes.forEach(function(flowType){
				events = [T.utils.getEvent('update', flowType), T.utils.getEvent('end', flowType), T.utils.getEvent('cancel', flowType)];
				events.forEach(function(event) {
					if(event) {
						this[on ? 'on' : 'off'](doc, event);
					}
				}, this);
			}, this);
		};

		/**
		 * Help function for detectTapOnScrollbar. Returns the width of the borders.
		 * @param {Element} element The element to get the border width from
		 * @returns {Object} An object with the following properties:
		 *                     - {Number} left		The left border width
		 *                     - {Number} top		The top border width
		 *                     - {Number} right		The right border width
		 *                     - {Number} bottom	The bottom border width
		 */
		function getWidthOfBorders( element ) {
			var borderWidths = {};

			function computeStyle( property ) {
				return parseInt( window.getComputedStyle( element ).getPropertyValue( property ), 10 );
			}

			borderWidths.left = computeStyle( 'border-left-width' );
			borderWidths.top = computeStyle( 'border-top-width' );
			borderWidths.right = computeStyle( 'border-right-width' );
			borderWidths.bottom = computeStyle( 'border-bottom-width' );

			return borderWidths;
		}

		/**
		 * Help function for the detectTapOnScrollbar function. It returns the tap point
		 * relative the element that was tapped.
		 * @param {Number} pageX The pageX property from either the event or a touch object in the event
		 * @param {Number} pageY The pageY property from either the event or a touch object in the event
		 * @param {Element} element The element that was tapped
		 * @return {Object} An object with the following properties:
		 *                    - {Number} x The x position of the tap relative the element
		 *                    - {Number} y The y position of the tap relative the element
		 */
		function getTapPointInElement( pageX, pageY, element ) {
			return {
				x: pageX - element.offsetLeft,
				y: pageY - element.offsetTop
			};
		}

		/**
		 * Detects tapping on a scrollbar. If there are more than one touch in the
		 * event it will return true if at least one touch is on a scrollbar.
		 * @param {Event} event The event that was triggered by the tap
		 * @return {Boolean} true if a scrollbar was tapped else false
		 */
		function detectTapOnScrollbar(event) {
			if (event.target.clientHeight === 0 || event.target.clientWidth === 0) {
				// No dimensions of the element so there is no scrollbar
				return false;
			}

			var borders = getWidthOfBorders( event.target ),
				offsetWidth = event.target.offsetWidth - borders.left - borders.right,
				clientWidth = event.target.clientWidth,
				offsetHeight = event.target.offsetHeight - borders.top - borders.bottom,
				clientHeight = event.target.clientHeight,
				tapOnVerticalScrollbar = false,
				tapOnHorizontalScrollbar = false,
				offset;

			if (event.touches) {
				// There could be multiple touches so we check each of them
				for ( var i=0; i<event.touches.length; i++ ) {
					offset = getTapPointInElement( event.touches[i].pageX, event.touches[i].pageY, event.target );
					tapOnVerticalScrollbar = tapOnVerticalScrollbar || ( offsetWidth > clientWidth && offset.x > clientWidth );
					tapOnHorizontalScrollbar = tapOnHorizontalScrollbar || ( offsetHeight > clientHeight && offset.y > clientHeight );
				}
			} else {
				offset = getTapPointInElement( event.pageX, event.pageY, event.target );
				tapOnVerticalScrollbar = offsetWidth > clientWidth && offset.x > clientWidth;
				tapOnHorizontalScrollbar = offsetHeight > clientHeight && offset.y > clientHeight;
			}

			return tapOnVerticalScrollbar || tapOnHorizontalScrollbar;
		}

		this.handleEvent = function(event) {
			switch(event.type) {
				case "mousedown":
				case "touchstart":
				case "MSPointerDown":
				case "pointerstart":
					// Ignore the event if it is triggered on a scrollbar
					// This is a workaround to the issue that IE does not trigger mouseup when clicking on a scrollbar:
					// http://social.msdn.microsoft.com/Forums/vstudio/en-US/3749b8a1-53ef-48fe-be81-b2df39d6154f/mouseup-event-of-vertical-scroll-bar?forum=netfxjscript
					if ( !detectTapOnScrollbar( event ) ) {
						this.onStart( event );
					}
					break;
				case "mousemove":
				case "touchmove":
				case "MSPointerMove":
				case "pointermove":
					this.onUpdate(event);
					break;
				case "mouseup":
				case "touchend":
				case "MSPointerUp":
				case "pointerup":
					this.onEnd(event);
					break;
				case "touchcancel":
				case "MSPointerCancel":
				case "pointercancel":
					this.onCancel(event);
					break;
			}
		};

		this.onStart = function() {};
		this.start = function() {};
		this.onUpdate = function() {};
		this.update = function() {};
		this.onEnd = function() {};
		this.end = function() {};
		this.onCancel = function() {};
		this.cancel = function() {};

		function FlowHandler(element) {
			this.element = element;
		}
		return FlowHandler;
	});

})(window.Touche, window.document);
<commit_msg>Fix tap offset calculation in element when detecting click on scrollbar<commit_after>(function(T, doc) {

	T.FlowHandler = Object.augment(function() {

		this.activate = function() {
			T.utils.getEvents().start.forEach(function(event) {
				this.on(this.element, event);
			}, this);
		};

		this.deactivate = function() {
			T.utils.getEvents().start.forEach(function(event) {
				this.off(this.element, event);
			}, this);
			this.bindDoc(false);
			this.resetFlowTypes();
			T.cache.remove(this.element);
		};

		this.on = function(element, event) {
			if(T.utils.msPointer) {
				if(element.style && element.style.msTouchAction) {
					this.msTouchAction = element.style.msTouchAction;
				} else if (!element.style) {
					element.style = '';
				}
				element.style.msTouchAction = 'none';
			}
			element.addEventListener(event, this, false);
		};

		this.off = function(element, event) {
			element.removeEventListener(event, this, false);
			if(T.utils.msPointer) {
				if(element.style) {
					element.style.msTouchAction = this.msTouchAction;
				}
			}
		};

		this.bindDoc = function(on, type) {
			var flowTypes = T.utils.getFlowTypes(type),
				events;
			flowTypes.forEach(function(flowType){
				events = [T.utils.getEvent('update', flowType), T.utils.getEvent('end', flowType), T.utils.getEvent('cancel', flowType)];
				events.forEach(function(event) {
					if(event) {
						this[on ? 'on' : 'off'](doc, event);
					}
				}, this);
			}, this);
		};

		/**
		 * Help function for detectTapOnScrollbar. Returns the width of the borders.
		 * @param {Element} element The element to get the border width from
		 * @returns {Object} An object with the following properties:
		 *                     - {Number} left		The left border width
		 *                     - {Number} top		The top border width
		 *                     - {Number} right		The right border width
		 *                     - {Number} bottom	The bottom border width
		 */
		function getWidthOfBorders( element ) {
			var borderWidths = {};

			function computeStyle( property ) {
				return parseInt( window.getComputedStyle( element ).getPropertyValue( property ), 10 );
			}

			borderWidths.left = computeStyle( 'border-left-width' );
			borderWidths.top = computeStyle( 'border-top-width' );
			borderWidths.right = computeStyle( 'border-right-width' );
			borderWidths.bottom = computeStyle( 'border-bottom-width' );

			return borderWidths;
		}

		/**
		 * Help function for the detectTapOnScrollbar function. It returns the tap point
		 * relative the element that was tapped.
		 * @param {Number} pageX The pageX property from either the event or a touch object in the event
		 * @param {Number} pageY The pageY property from either the event or a touch object in the event
		 * @param {Element} element The element that was tapped
		 * @return {Object} An object with the following properties:
		 *                    - {Number} x The x position of the tap relative the element
		 *                    - {Number} y The y position of the tap relative the element
		 */
		function getTapPointInElement( pageX, pageY, element ) {
			var bbox = element.getBoundingClientRect();

			return {
				x: pageX - bbox.left,
				y: pageY - bbox.top
			};
		}

		/**
		 * Detects tapping on a scrollbar. If there are more than one touch in the
		 * event it will return true if at least one touch is on a scrollbar.
		 * @param {Event} event The event that was triggered by the tap
		 * @return {Boolean} true if a scrollbar was tapped else false
		 */
		function detectTapOnScrollbar(event) {
			if (event.target.clientHeight === 0 || event.target.clientWidth === 0) {
				// No dimensions of the element so there is no scrollbar
				return false;
			}

			var borders = getWidthOfBorders( event.target ),
				offsetWidth = event.target.offsetWidth - borders.left - borders.right,
				clientWidth = event.target.clientWidth,
				offsetHeight = event.target.offsetHeight - borders.top - borders.bottom,
				clientHeight = event.target.clientHeight,
				tapOnVerticalScrollbar = false,
				tapOnHorizontalScrollbar = false,
				offset;

			if (event.touches) {
				// There could be multiple touches so we check each of them
				for ( var i=0; i<event.touches.length; i++ ) {
					offset = getTapPointInElement( event.touches[i].pageX, event.touches[i].pageY, event.target );
					tapOnVerticalScrollbar = tapOnVerticalScrollbar || ( offsetWidth > clientWidth && offset.x > clientWidth );
					tapOnHorizontalScrollbar = tapOnHorizontalScrollbar || ( offsetHeight > clientHeight && offset.y > clientHeight );
				}
			} else {
				offset = getTapPointInElement( event.pageX, event.pageY, event.target );
				tapOnVerticalScrollbar = offsetWidth > clientWidth && offset.x > clientWidth;
				tapOnHorizontalScrollbar = offsetHeight > clientHeight && offset.y > clientHeight;
			}

			return tapOnVerticalScrollbar || tapOnHorizontalScrollbar;
		}

		this.handleEvent = function(event) {
			switch(event.type) {
				case "mousedown":
				case "touchstart":
				case "MSPointerDown":
				case "pointerstart":
					// Ignore the event if it is triggered on a scrollbar
					// This is a workaround to the issue that IE does not trigger mouseup when clicking on a scrollbar:
					// http://social.msdn.microsoft.com/Forums/vstudio/en-US/3749b8a1-53ef-48fe-be81-b2df39d6154f/mouseup-event-of-vertical-scroll-bar?forum=netfxjscript
					if ( !detectTapOnScrollbar( event ) ) {
						this.onStart( event );
					}
					break;
				case "mousemove":
				case "touchmove":
				case "MSPointerMove":
				case "pointermove":
					this.onUpdate(event);
					break;
				case "mouseup":
				case "touchend":
				case "MSPointerUp":
				case "pointerup":
					this.onEnd(event);
					break;
				case "touchcancel":
				case "MSPointerCancel":
				case "pointercancel":
					this.onCancel(event);
					break;
			}
		};

		this.onStart = function() {};
		this.start = function() {};
		this.onUpdate = function() {};
		this.update = function() {};
		this.onEnd = function() {};
		this.end = function() {};
		this.onCancel = function() {};
		this.cancel = function() {};

		function FlowHandler(element) {
			this.element = element;
		}
		return FlowHandler;
	});

})(window.Touche, window.document);
<|endoftext|>


<commit_before>(function () {
    "use strict";

    var app = angular.module('viewCustom', ['angularLoad']);

    //record GA page view event to new primo tracker GA ID
    app.run(['$rootScope', '$location', '$window', function($rootScope, $location, $window){

        // remove favourite element from nav-bar because it's included in user area overwrite
        $rootScope.$on("$stateChangeSuccess", function() {
            // Primo keeps adding favourites element every time user clicks on My Account
            // Keep removing fav element every time it's added
            var favouritesElement = document.querySelector('.top-nav-bar > prm-search-bookmark-filter');
            if (favouritesElement !== null && typeof(favouritesElement) !== 'undefined') {
                favouritesElement.parentNode.removeChild(favouritesElement);
            }
        });

        $rootScope.$on('$locationChangeSuccess', function(event){
            //temporarily create a tracker specifically to new Primo
            if ($window.ga) {
                $window.ga(function () {
                    $window.ga('create', 'UA-4365437-14', 'search.library.uq.edu.au', 'NewPrimoTracker');
                });
                $window.ga('NewPrimoTracker.send', 'pageview', {location: $location.url()});
            }
        });
    }]);

    app.component('prmTopBarBefore', {
        template: '
<uqlibrary-alerts></uqlibrary-alerts>
' +
        '
<uq-minimal-header show-login-button="false"></uq-minimal-header>
'
    });

    var template = 'components/search/topbar/userArea/user-area.html';
    var newUserArea = '
' +
        '{{$ctrl.userName()}}' +
        '<prm-search-bookmark-filter></prm-search-bookmark-filter>' +
        '<prm-library-card-menu ng-show="$ctrl.userName().length > 0"></prm-library-card-menu>' +
        '<prm-authentication layout="flex" [is-logged-in]="$ctrl.userName().length > 0"></prm-authentication>' +
        '
';

    app.component('prmUserAreaAfter', {
        bindings: {
            parentCtrl: '<'
        },
        controller: function($compile, $scope, $templateCache, $element) {
            $templateCache.put(template, newUserArea);
            $compile($element.parent())($scope);
        }
    });

    /****************************************************************************************************/

    /*In case of CENTRAL_PACKAGE - comment out the below line to replace the other module definition*/

    /*var app = angular.module('centralCustom', ['angularLoad']);*/

    /****************************************************************************************************/

    function insertScript(url) {
        var script = document.querySelector("script[src*='"+url+"']");
        if (!script) {
            var heads = document.getElementsByTagName("head");
            if (heads && heads.length) {
                var head = heads[0];
                if (head) {
                    script = document.createElement('script');
                    script.setAttribute('src', url);
                    script.setAttribute('type', 'text/javascript');
                    head.appendChild(script);
                }
            }
        }
    }

    function insertLink(link) {
        var linkTag = document.querySelector("link[href*='"+link.href+"']");
        if (!linkTag) {
            var heads = document.getElementsByTagName("head");
            if (heads && heads.length) {
                var head = heads[0];
                if (head) {
                    linkTag = document.createElement('link');
                    linkTag.setAttribute('href', link.href);
                    linkTag.setAttribute('rel', link.rel);
                    head.appendChild(linkTag);
                }
            }
        }
    }

    var scripts = [
        '//assets.library.uq.edu.au/reusable-components/webcomponentsjs/webcomponents-lite.min.js',
        '//assets.library.uq.edu.au/reusable-components/resources/preloader.js',
        '//assets.library.uq.edu.au/reusable-components/primo2/load.js'
    ];

    var links = [
        { rel: 'import', href: '//assets.library.uq.edu.au/reusable-components/elements.vulcanized.html'},
        { rel: 'stylesheet', href: '//assets.library.uq.edu.au/reusable-components/primo2/custom-styles.css'}
    ];

    insertLink(links[0]);
    insertLink(links[1]);

    insertScript(scripts[0]);
    insertScript(scripts[1]);
    insertScript(scripts[2]);

})();

var fixUserArea = setInterval(function() {
    //wait for header to be inserted by angular
    if (document.querySelector('uq-minimal-header') !== null && typeof(document.querySelector('uq-minimal-header')) !== 'undefined') {
        clearInterval(fixUserArea);

        //remove unused favourites element
        var favouritesElement = document.querySelector('.top-nav-bar > prm-search-bookmark-filter');
        if (favouritesElement !== null && typeof(favouritesElement) !== 'undefined') {
            favouritesElement.parentNode.removeChild(favouritesElement);
        }
    }
}, 100);

// enable GTM
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-W4KK37');

<commit_msg>reverting changes<commit_after>var fixUserArea = setInterval(function() {
    //wait for header to be inserted by angular
    if (document.querySelector('uq-minimal-header') !== null && typeof(document.querySelector('uq-minimal-header')) !== 'undefined') {
        clearInterval(fixUserArea);

        //remove unused favourites element
        var favouritesElement = document.querySelector('.top-nav-bar > prm-search-bookmark-filter');
        if (favouritesElement !== null && typeof(favouritesElement) !== 'undefined') {
            favouritesElement.parentNode.removeChild(favouritesElement);
        }
    }
}, 100);

// enable GTM
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-W4KK37');

<|endoftext|>


<commit_before>import Octokat from 'octokat';
import { decryptData } from '../utils/cryptData';
import { fetchAll } from '../utils/octokat-helper';
export const TYPE_PULL = 'TYPE_PULL';
export const TYPE_ISSUE = 'TYPE_ISSUE';

function mixRepos(...repos) {
  return Object.assign([], ...repos);
}

function mixWatchedAndNotUserRepos(...repos) {
  repos;
  return [];
}

function fetchUserReposPromise({octo, since} = {}) {
  return fetchAll(octo.user.repos.fetch, {
    per_page: 100,
    sort: 'pushed',
    direction: 'desc',
  }).then((value) => {
    return value.filter((repo) => {
      return (repo.pushedAt && repo.pushedAt >= since) || (repo.updatedAt && repo.updatedAt >= since);
    });
  });
}

function fetchWatchedReposPromise({octo, since} = {}) {
  octo;
  since;
  return Promise.resolve([]);
}

function fetchUserIssuesPromise({octo, since} = {}) {
  return fetchAll(octo.issues.fetch, {
    per_page: 100,
    filter: 'all',
    state: 'all',
    sort: 'updated',
    since: since.toISOString(),
  });
}

function fetchReposIssuesPromise({repos, since} = {}) {
  repos;
  since;
  return Promise.resolve([]);
}

function fetchReposBranchesPromise({repos} = {}) {
  return Promise.all(
    repos.map((repo) => {
      return fetchAll(repo.branches.fetch, {
        per_page: 100,
      }).then((value) => {
        return {
          branches: value,
          repo: repo,
        };
      });
    })
  );
}

function appendPullRequestPromiseArray(data = []) {
  return data.map((story) => {
    if (!story.issue.pullRequest) {
      return Promise.resolve(Object.assign({}, story, { type: TYPE_ISSUE }));
    }
    return story.issue.pullRequest.fetch({
      per_page: 100,
    }).then((pull) => {
      return Object.assign({}, story, { pull: pull, type: TYPE_PULL });
    });
  });
}

function appendStatusPromiseArray(data = []) {
  return data.map((story) => {
    if (!story.pull) {
      return Promise.resolve(story);
    }
    return story.repo.commits(story.pull.head.sha).status.fetch({
      per_page: 100,
    }).then((status) => {
      return Object.assign({}, story, { status: status });
    });
  });
}

function appendBranchPromiseArray({stories, reposBranches} = {}) {
  return stories.map((story) => {
    if (!story.pull) {
      return Promise.resolve(story);
    }
    const repoBranches = Array.find(reposBranches, (repo) => {
      return repo.repo.id === story.repo.id;
    });
    if (!repoBranches) {
      return Promise.resolve(story);
    }
    const targetBranch = Array.find(repoBranches.branches, (branch) => {
      return branch.name === story.pull.head.ref;
    });
    if (!targetBranch) {
      return Promise.resolve(story);
    }
    return Promise.resolve(
      Object.assign({}, story, { branch: targetBranch })
    );
  });
}

export function fetchCraftedStories(settings = {}) {
  return new Promise((resolve1) => {
    const now = new Date();
    const since = new Date(now - 5 * 24 * 60 * 60 * 1000);
    const token = decryptData(settings.token);
    const octo = new Octokat({
      token: token,
      rootURL: settings.apiendpoint,
      acceptHeader: 'application/vnd.github.v3.text+json',
    });
    let repos;
    let userRepos;
    let watchedRepos;
    let watchedAndNotUserRepos;
    let reposBranches;
    Promise.all([
      fetchUserReposPromise({octo: octo, since: since}),
      fetchWatchedReposPromise({octo: octo, since: since}),
    ]).then((value) => {
      [ userRepos, watchedRepos ] = value;
      repos = mixRepos(userRepos, watchedRepos);
      watchedAndNotUserRepos = mixWatchedAndNotUserRepos(watchedRepos, userRepos);
      return Promise.all([
        fetchUserIssuesPromise({octo: octo, since: since}),
        fetchReposIssuesPromise({repos: watchedAndNotUserRepos, since: since}),
        fetchReposBranchesPromise({repos: repos}),
      ]).then((value2) => {
        const [userIssues, reposIssues, branches] = value2;
        reposBranches = branches;
        // FIXME: single issue does not have repository
        return userIssues.concat(reposIssues).map((issue) => {
          return {
            id: `issue:${issue.id}`,
            issue: issue,
            repo: issue.repository,
          };
        });
      });
    }).then((value) => {
      return Promise.all(appendPullRequestPromiseArray(value));
    }).then((value) => {
      return Promise.all(appendStatusPromiseArray(value));
    }).then((value) => {
      return Promise.all(appendBranchPromiseArray({
        stories: value,
        reposBranches: reposBranches,
      }));
    }).then((value) => {
      resolve1(value);
    });
  });
}

function fetchReposPromiseByIdentifiers({octo, identifiers = []} = {}) {
  return Promise.all(
    identifiers.map((identifier) => {
      return octo.repos(identifier.owner, identifier.repo).fetch();
    })
  );
}

function fetchIssuesPromiseByIdentifiers({octo, identifiers = []} = {}) {
  return Promise.all(
    identifiers.map((identifier) => {
      return Promise.all([
        octo.repos(identifier.owner, identifier.repo)
          .issues(identifier.number)
          .fetch(),
        octo.repos(identifier.owner, identifier.repo).fetch(),
      ]);
    })
  );
}

function toggleIssueState(state) {
  return (state === 'open') ? 'closed' : 'open';
}

function closeIssuesPromiseByIdentifiers({octo, identifiers = []} = {}) {
  return Promise.all(
    identifiers.map((identifier) => {
      return octo.repos(identifier.owner, identifier.repo)
        .issues(identifier.number)
        .fetch()
        .then((value) => {
        const issue = value;
        return issue.update({
          state: toggleIssueState(issue.state),
        });
      });
    })
  );
}

export function fetchCraftedStoriesByIssue({settings = {}, identifiers = []} = {}) {
  return new Promise((resolve1) => {
    const token = decryptData(settings.token);
    const octo = new Octokat({
      token: token,
      rootURL: settings.apiendpoint,
      acceptHeader: 'application/vnd.github.v3.text+json',
    });
    let repos;
    let reposBranches;
    fetchReposPromiseByIdentifiers({
      octo: octo,
      identifiers: identifiers,
    }).then((value) => {
      repos = value;
      return fetchReposBranchesPromise({
        repos: repos,
      });
    }).then((value) => {
      reposBranches = value;
      return fetchIssuesPromiseByIdentifiers({
        octo: octo,
        identifiers: identifiers,
      });
    }).then((value) => {
      return value.map((value2) => {
        const [issue, repository] = value2;
        return {
          id: `issue:${issue.id}`,
          issue: issue,
          repo: repository,
        };
      });
    }).then((value) => {
      return Promise.all(appendPullRequestPromiseArray(value));
    }).then((value) => {
      return Promise.all(appendStatusPromiseArray(value));
    }).then((value) => {
      return Promise.all(appendBranchPromiseArray({
        stories: value,
        reposBranches: reposBranches,
      }));
    }).then((value) => {
      resolve1(value);
    });
  });
}

export function closeIssues({settings = {}, identifiers = []} = {}) {
  return new Promise((resolve1) => {
    const token = decryptData(settings.token);
    const octo = new Octokat({
      token: token,
      rootURL: settings.apiendpoint,
      acceptHeader: 'application/vnd.github.v3.text+json',
    });
    closeIssuesPromiseByIdentifiers({
      octo: octo,
      identifiers: identifiers,
    }).then((value) => {
      resolve1(value);
    });
  });
}
<commit_msg>chore(story): reload after close issue<commit_after>import Octokat from 'octokat';
import { decryptData } from '../utils/cryptData';
import { fetchAll } from '../utils/octokat-helper';
export const TYPE_PULL = 'TYPE_PULL';
export const TYPE_ISSUE = 'TYPE_ISSUE';

function mixRepos(...repos) {
  return Object.assign([], ...repos);
}

function mixWatchedAndNotUserRepos(...repos) {
  repos;
  return [];
}

function fetchUserReposPromise({octo, since} = {}) {
  return fetchAll(octo.user.repos.fetch, {
    per_page: 100,
    sort: 'pushed',
    direction: 'desc',
  }).then((value) => {
    return value.filter((repo) => {
      return (repo.pushedAt && repo.pushedAt >= since) || (repo.updatedAt && repo.updatedAt >= since);
    });
  });
}

function fetchWatchedReposPromise({octo, since} = {}) {
  octo;
  since;
  return Promise.resolve([]);
}

function fetchUserIssuesPromise({octo, since} = {}) {
  return fetchAll(octo.issues.fetch, {
    per_page: 100,
    filter: 'all',
    state: 'all',
    sort: 'updated',
    since: since.toISOString(),
  });
}

function fetchReposIssuesPromise({repos, since} = {}) {
  repos;
  since;
  return Promise.resolve([]);
}

function fetchReposBranchesPromise({repos} = {}) {
  return Promise.all(
    repos.map((repo) => {
      return fetchAll(repo.branches.fetch, {
        per_page: 100,
      }).then((value) => {
        return {
          branches: value,
          repo: repo,
        };
      });
    })
  );
}

function appendPullRequestPromiseArray(data = []) {
  return data.map((story) => {
    if (!story.issue.pullRequest) {
      return Promise.resolve(Object.assign({}, story, { type: TYPE_ISSUE }));
    }
    return story.issue.pullRequest.fetch({
      per_page: 100,
    }).then((pull) => {
      return Object.assign({}, story, { pull: pull, type: TYPE_PULL });
    });
  });
}

function appendStatusPromiseArray(data = []) {
  return data.map((story) => {
    if (!story.pull) {
      return Promise.resolve(story);
    }
    return story.repo.commits(story.pull.head.sha).status.fetch({
      per_page: 100,
    }).then((status) => {
      return Object.assign({}, story, { status: status });
    });
  });
}

function appendBranchPromiseArray({stories, reposBranches} = {}) {
  return stories.map((story) => {
    if (!story.pull) {
      return Promise.resolve(story);
    }
    const repoBranches = Array.find(reposBranches, (repo) => {
      return repo.repo.id === story.repo.id;
    });
    if (!repoBranches) {
      return Promise.resolve(story);
    }
    const targetBranch = Array.find(repoBranches.branches, (branch) => {
      return branch.name === story.pull.head.ref;
    });
    if (!targetBranch) {
      return Promise.resolve(story);
    }
    return Promise.resolve(
      Object.assign({}, story, { branch: targetBranch })
    );
  });
}

export function fetchCraftedStories(settings = {}) {
  return new Promise((resolve1) => {
    const now = new Date();
    const since = new Date(now - 5 * 24 * 60 * 60 * 1000);
    const token = decryptData(settings.token);
    const octo = new Octokat({
      token: token,
      rootURL: settings.apiendpoint,
      acceptHeader: 'application/vnd.github.v3.text+json',
    });
    let repos;
    let userRepos;
    let watchedRepos;
    let watchedAndNotUserRepos;
    let reposBranches;
    Promise.all([
      fetchUserReposPromise({octo: octo, since: since}),
      fetchWatchedReposPromise({octo: octo, since: since}),
    ]).then((value) => {
      [ userRepos, watchedRepos ] = value;
      repos = mixRepos(userRepos, watchedRepos);
      watchedAndNotUserRepos = mixWatchedAndNotUserRepos(watchedRepos, userRepos);
      return Promise.all([
        fetchUserIssuesPromise({octo: octo, since: since}),
        fetchReposIssuesPromise({repos: watchedAndNotUserRepos, since: since}),
        fetchReposBranchesPromise({repos: repos}),
      ]).then((value2) => {
        const [userIssues, reposIssues, branches] = value2;
        reposBranches = branches;
        // FIXME: single issue does not have repository
        return userIssues.concat(reposIssues).map((issue) => {
          return {
            id: `issue:${issue.id}`,
            issue: issue,
            repo: issue.repository,
          };
        });
      });
    }).then((value) => {
      return Promise.all(appendPullRequestPromiseArray(value));
    }).then((value) => {
      return Promise.all(appendStatusPromiseArray(value));
    }).then((value) => {
      return Promise.all(appendBranchPromiseArray({
        stories: value,
        reposBranches: reposBranches,
      }));
    }).then((value) => {
      resolve1(value);
    });
  });
}

function fetchReposPromiseByIdentifiers({octo, identifiers = []} = {}) {
  return Promise.all(
    identifiers.map((identifier) => {
      return octo.repos(identifier.owner, identifier.repo).fetch();
    })
  );
}

function fetchIssuesPromiseByIdentifiers({octo, identifiers = []} = {}) {
  return Promise.all(
    identifiers.map((identifier) => {
      return Promise.all([
        octo.repos(identifier.owner, identifier.repo)
          .issues(identifier.number)
          .fetch(),
        octo.repos(identifier.owner, identifier.repo).fetch(),
      ]);
    })
  );
}

function toggleIssueState(state) {
  return (state === 'open') ? 'closed' : 'open';
}

function closeIssuesPromiseByIdentifiers({octo, identifiers = []} = {}) {
  return Promise.all(
    identifiers.map((identifier) => {
      return octo.repos(identifier.owner, identifier.repo)
        .issues(identifier.number)
        .fetch()
        .then((value) => {
        const issue = value;
        return issue.update({
          state: toggleIssueState(issue.state),
        });
      });
    })
  );
}

export function fetchCraftedStoriesByIssue({settings = {}, identifiers = []} = {}) {
  return new Promise((resolve1) => {
    const token = decryptData(settings.token);
    const octo = new Octokat({
      token: token,
      rootURL: settings.apiendpoint,
      acceptHeader: 'application/vnd.github.v3.text+json',
    });
    let repos;
    let reposBranches;
    fetchReposPromiseByIdentifiers({
      octo: octo,
      identifiers: identifiers,
    }).then((value) => {
      repos = value;
      return fetchReposBranchesPromise({
        repos: repos,
      });
    }).then((value) => {
      reposBranches = value;
      return fetchIssuesPromiseByIdentifiers({
        octo: octo,
        identifiers: identifiers,
      });
    }).then((value) => {
      return value.map((value2) => {
        const [issue, repository] = value2;
        return {
          id: `issue:${issue.id}`,
          issue: issue,
          repo: repository,
        };
      });
    }).then((value) => {
      return Promise.all(appendPullRequestPromiseArray(value));
    }).then((value) => {
      return Promise.all(appendStatusPromiseArray(value));
    }).then((value) => {
      return Promise.all(appendBranchPromiseArray({
        stories: value,
        reposBranches: reposBranches,
      }));
    }).then((value) => {
      resolve1(value);
    });
  });
}

export function closeIssues({settings = {}, identifiers = []} = {}) {
  return new Promise((resolve1) => {
    const token = decryptData(settings.token);
    const octo = new Octokat({
      token: token,
      rootURL: settings.apiendpoint,
      acceptHeader: 'application/vnd.github.v3.text+json',
    });
    closeIssuesPromiseByIdentifiers({
      octo: octo,
      identifiers: identifiers,
    }).then(() => {
      return fetchCraftedStoriesByIssue({
        settings: settings,
        identifiers: identifiers,
      });
    }).then((value) => {
      resolve1(value);
    });
  });
}
<|endoftext|>


<commit_before>function(keys, values, rereduce) {
    if (rereduce) {
        var result = {
            'sum': values.reduce(function(a, b) { return a + b.sum; }, 0),
            'min': values.reduce(function(a, b) { return Math.min(a, b.min); }, Infinity),
            'max': values.reduce(function(a, b) { return Math.max(a, b.max); }, -Infinity),
            'count': values.reduce(function(a, b) { return a + b.count; }, 0)
        };

        result.avg = result.sum / result.count;

        return result;

    } else {
        return {
            'sum': sum(values),
            'min': Math.min.apply(null, values),
            'max': Math.max.apply(null, values),
            'count': values.length,
            'avg': sum(values) / values.length
        };
    }
}
<commit_msg>Round avg to 1 decimal point<commit_after>function(keys, values, rereduce) {
    if (rereduce) {
        var result = {
            'sum': values.reduce(function(a, b) { return a + b.sum; }, 0),
            'min': values.reduce(function(a, b) { return Math.min(a, b.min); }, Infinity),
            'max': values.reduce(function(a, b) { return Math.max(a, b.max); }, -Infinity),
            'count': values.reduce(function(a, b) { return a + b.count; }, 0)
        };

        result.avg = Math.round((result.sum / result.count)*10)/10; // Round to 1 decimal point.

        return result;

    } else {
        return {
            'sum': sum(values),
            'min': Math.min.apply(null, values),
            'max': Math.max.apply(null, values),
            'count': values.length,
            'avg': Math.round((sum(values) / values.length)*10)/10 // Round to 1 decimal point.
        };
    }
}
<|endoftext|>


<commit_before>// Restrict output in a codecell to a maximum length

define([
    'notebook/js/outputarea',
    'notebook/js/codecell',
    'services/config',
    'base/js/utils'
], function(oa, cc, configmod, utils) {
    "use strict";

    var base_url = utils.get_body_data("baseUrl");
    var config = new configmod.ConfigSection('notebook', {base_url: base_url});

    // define default values for config parameters
    var params = {
        // maximum number of characters the output area is allowed to print
        limit_output : 10000,
        // message to print when output is limited
        limit_output_message : 'limit_output extension: Maximum message size of {limit_output_length} exceeded with {output_length} characters'
    };

    // to be called once config is loaded, this updates default config vals
    // with the ones specified by the server's config file
    var update_params = function() {
        for (var key in params) {
            if (config.data.hasOwnProperty(key) ){
                params[key] = config.data[key];
            }
        }
    };

    config.loaded.then(function() {
        update_params();
        var MAX_CHARACTERS = params.limit_output;

        oa.OutputArea.prototype._handle_output = oa.OutputArea.prototype.handle_output;
        oa.OutputArea.prototype.handle_output = function (msg) {
            if (msg.header.msg_type.match("stream|execute_result|display_data")) {
                var count = 0;
                if (msg.header.msg_type === "stream") {
                    count = String(msg.content.text).length;
                } else {
                    count = Math.max(
                        (msg.content.data['text/plain'] === undefined) ? 0 : String(msg.content.data['text/plain']).length,
                        (msg.content.data['text/html'] === undefined) ? 0 : String(msg.content.data['text/html']).length )
                }
                if (count > MAX_CHARACTERS) {
                    console.log("limit_output: output", count, "exceeded", MAX_CHARACTERS, "characters. Further output muted.");
                    if (msg.header.msg_type === "stream") {
                        msg.content.text = msg.content.text.substr(0, MAX_CHARACTERS)
                    } else {
                        if (msg.content.data['text/plain'] !== undefined) {
                            msg.content.data['text/plain'] = msg.content.data['text/plain'].substr(0, MAX_CHARACTERS);
                        }
                        if (msg.content.data['text/html'] !== undefined) {
                            msg.content.data['text/html'] = msg.content.data['text/html'].substr(0, MAX_CHARACTERS);
                        }
                    }
                    var limitmsg = {};
                    limitmsg.data = [];
                    // allow simple substitutions for output length for quick debugging
                    limitmsg.data['text/html'] = params.limit_output_message.replace("{limit_output_length}", MAX_CHARACTERS)
                                                                            .replace("{output_length}", count);
                    this._handle_output(msg);
                    return this.append_display_data(limitmsg);
                }
            }
            return this._handle_output(msg);
        };

        cc.CodeCell.prototype._execute = cc.CodeCell.prototype.execute;
        cc.CodeCell.prototype.execute = function() {
            // reset counter on execution.
            this.output_area.count = 0;
            this.output_area.drop  = false;
            return this._execute();
        };
    });

    var load_ipython_extension = function() {
        config.load();
    };

    return {
        load_ipython_extension : load_ipython_extension
    };
});
<commit_msg>[limit_output] add missing semicolons<commit_after>// Restrict output in a codecell to a maximum length

define([
    'notebook/js/outputarea',
    'notebook/js/codecell',
    'services/config',
    'base/js/utils'
], function(oa, cc, configmod, utils) {
    "use strict";

    var base_url = utils.get_body_data("baseUrl");
    var config = new configmod.ConfigSection('notebook', {base_url: base_url});

    // define default values for config parameters
    var params = {
        // maximum number of characters the output area is allowed to print
        limit_output : 10000,
        // message to print when output is limited
        limit_output_message : 'limit_output extension: Maximum message size of {limit_output_length} exceeded with {output_length} characters'
    };

    // to be called once config is loaded, this updates default config vals
    // with the ones specified by the server's config file
    var update_params = function() {
        for (var key in params) {
            if (config.data.hasOwnProperty(key) ){
                params[key] = config.data[key];
            }
        }
    };

    config.loaded.then(function() {
        update_params();
        var MAX_CHARACTERS = params.limit_output;

        oa.OutputArea.prototype._handle_output = oa.OutputArea.prototype.handle_output;
        oa.OutputArea.prototype.handle_output = function (msg) {
            if (msg.header.msg_type.match("stream|execute_result|display_data")) {
                var count = 0;
                if (msg.header.msg_type === "stream") {
                    count = String(msg.content.text).length;
                } else {
                    count = Math.max(
                        (msg.content.data['text/plain'] === undefined) ? 0 : String(msg.content.data['text/plain']).length,
                        (msg.content.data['text/html'] === undefined) ? 0 : String(msg.content.data['text/html']).length );
                }
                if (count > MAX_CHARACTERS) {
                    console.log("limit_output: output", count, "exceeded", MAX_CHARACTERS, "characters. Further output muted.");
                    if (msg.header.msg_type === "stream") {
                        msg.content.text = msg.content.text.substr(0, MAX_CHARACTERS);
                    } else {
                        if (msg.content.data['text/plain'] !== undefined) {
                            msg.content.data['text/plain'] = msg.content.data['text/plain'].substr(0, MAX_CHARACTERS);
                        }
                        if (msg.content.data['text/html'] !== undefined) {
                            msg.content.data['text/html'] = msg.content.data['text/html'].substr(0, MAX_CHARACTERS);
                        }
                    }
                    var limitmsg = {};
                    limitmsg.data = [];
                    // allow simple substitutions for output length for quick debugging
                    limitmsg.data['text/html'] = params.limit_output_message.replace("{limit_output_length}", MAX_CHARACTERS)
                                                                            .replace("{output_length}", count);
                    this._handle_output(msg);
                    return this.append_display_data(limitmsg);
                }
            }
            return this._handle_output(msg);
        };

        cc.CodeCell.prototype._execute = cc.CodeCell.prototype.execute;
        cc.CodeCell.prototype.execute = function() {
            // reset counter on execution.
            this.output_area.count = 0;
            this.output_area.drop  = false;
            return this._execute();
        };
    });

    var load_ipython_extension = function() {
        config.load();
    };

    return {
        load_ipython_extension : load_ipython_extension
    };
});
<|endoftext|>


<commit_before>var utils = require('../core/utils.js');
var RunCode = require('../core/runCode.js');

var Console = function(options) {
  this.plugins = options.plugins;
  this.version = options.version;
};

Console.prototype.runCode = function(code) {
  RunCode.doEval(code); // jshint ignore:line
};

Console.prototype.executeCmd = function(cmd, callback) {
  var plugin, pluginOutput;
  var plugins = this.plugins.getPluginsFor('console');
  for (var i = 0; i < plugins.length; i++) {
    plugin = plugins[i];
    pluginOutput = plugin.runCommands(cmd, {});
    if (pluginOutput !== false && pluginOutput !== 'false') return callback(pluginOutput);
  }

  if (cmd === 'help') {
    var helpText = [
      'Welcome to Embark ' + this.version,
      '',
      'possible commands are:',
      // TODO: only if the blockchain is actually active!
      // will need to pass te current embark state here
      'web3 - instantiated web3.js object configured to the current environment',
      'quit - to immediatly exit',
      '',
      'The web3 object and the interfaces for the deployed contrats and their methods are also available'
    ];
    return callback(helpText.join('\n'));
  } else if (cmd === 'quit') {
    utils.exit();
  }

  try {
    var result = RunCode.doEval(cmd);
    return callback(result);
  }
  catch(e) {
    if (e.message.indexOf('not defined') > 0) {
      return callback(("error: " + e.message).red + ("\nType " + "help".bold + " to see the list of available commands").cyan);
    } else {
      return callback(e.message);
    }
  }
};

module.exports = Console;

<commit_msg>Minor typo in help message<commit_after>var utils = require('../core/utils.js');
var RunCode = require('../core/runCode.js');

var Console = function(options) {
  this.plugins = options.plugins;
  this.version = options.version;
};

Console.prototype.runCode = function(code) {
  RunCode.doEval(code); // jshint ignore:line
};

Console.prototype.executeCmd = function(cmd, callback) {
  var plugin, pluginOutput;
  var plugins = this.plugins.getPluginsFor('console');
  for (var i = 0; i < plugins.length; i++) {
    plugin = plugins[i];
    pluginOutput = plugin.runCommands(cmd, {});
    if (pluginOutput !== false && pluginOutput !== 'false') return callback(pluginOutput);
  }

  if (cmd === 'help') {
    var helpText = [
      'Welcome to Embark ' + this.version,
      '',
      'possible commands are:',
      // TODO: only if the blockchain is actually active!
      // will need to pass te current embark state here
      'web3 - instantiated web3.js object configured to the current environment',
      'quit - to immediatly exit',
      '',
      'The web3 object and the interfaces for the deployed contracts and their methods are also available'
    ];
    return callback(helpText.join('\n'));
  } else if (cmd === 'quit') {
    utils.exit();
  }

  try {
    var result = RunCode.doEval(cmd);
    return callback(result);
  }
  catch(e) {
    if (e.message.indexOf('not defined') > 0) {
      return callback(("error: " + e.message).red + ("\nType " + "help".bold + " to see the list of available commands").cyan);
    } else {
      return callback(e.message);
    }
  }
};

module.exports = Console;
<|endoftext|>