<commit_before>var fs = require('fs');
var path = require('path');

var async = require('async');
var fse = require('fs-extra');
var nomnom = require('nomnom');

var generateInfo = require('./generate-info');

/**
 * Read the symbols from info file.
 * @param {funciton(Error, Array.<string>, Array.<Object>)} callback Called
 *     with the patterns and symbols (or any error).
 */
function getInfo(callback) {
  generateInfo(function(err) {
    if (err) {
      callback(new Error('Trouble generating info: ' + err.message));
      return;
    }
    var typedefs = require('../build/info.json').typedefs;
    var symbols = require('../build/info.json').symbols;
    var externs = require('../build/info.json').externs;
    callback(null, typedefs, symbols, externs);
  });
}

/**
 * Generate externs code given a list symbols.
 * @param {Array.<Object>} typedefs List of typedefs.
 * @param {Array.<Object>} symbols List of symbols.
 * @param {Array.<Object>} externs List of externs.
 * @param {string|undefined} namespace Target object for exported symbols.
 * @return {string} Export code.
 */
function generateExterns(typedefs, symbols, externs) {
  var lines = [];
  var namespaces = {};
  var constructors = {};

  function addNamespaces(name) {
    var parts = name.split('.');
    parts.pop();
    var namespace = [];
    parts.forEach(function(part) {
      namespace.push(part);
      var partialNamespace = namespace.join('.');
      if (!(partialNamespace in namespaces ||
          partialNamespace in constructors)) {
        namespaces[partialNamespace] = true;
        lines.push('/**');
        lines.push(' * @type {Object}');
        lines.push(' */');
        lines.push(
            (namespace.length == 1 ? 'var ' : '') + partialNamespace + ';');
        lines.push('\n');
      }
    });
  }

  function processSymbol(symbol) {
    addNamespaces(symbol.name.split('#')[0]);

    var name = symbol.name;
    if (name.indexOf('#') > 0) {
      name = symbol.name.replace('#', '.prototype.');
      var constructor = symbol.name.split('#')[0];
      if (!(constructor in constructors)) {
        constructors[constructor] = true;
        lines.push('/**');
        lines.push(' * @constructor');
        lines.push(' */');
        lines.push(constructor + ' = function() {};');
        lines.push('\n');
      }
    }

    lines.push('/**');
    if (symbol.kind == 'class') {
      constructors[name] = true;
      lines.push(' * @constructor');
    }
    if (symbol.types) {
      lines.push(' * @type {' + symbol.types.join('|') + '}');
    }
    var args = [];
    if (symbol.params) {
      symbol.params.forEach(function(param) {
        args.push(param.name);
        lines.push(' * @param {' +
            (param.variable ? '...' : '') +
            param.types.join('|') +
            (param.optional ? '=' : '') +
            '} ' + param.name);
      });
    }
    if (symbol.returns) {
      lines.push(' * @return {' + symbol.returns.join('|') + '}');
    }
    if (symbol.template) {
      lines.push(' * @template ' + symbol.template);
    }
    lines.push(' */');
    if (symbol.kind == 'function' || symbol.kind == 'class') {
      lines.push(name + ' = function(' + args.join(', ') + ') {};');
    } else {
      lines.push(name + ';');
    }
    lines.push('\n');
  }

  typedefs.forEach(function(typedef) {
    addNamespaces(typedef.name);
    lines.push('/**');
    lines.push(' * @typedef {' + typedef.types.join('|') + '}');
    lines.push(' */');
    lines.push(typedef.name + ';');
    lines.push('\n');
  });

  externs.forEach(processSymbol);
  symbols.forEach(processSymbol);

  return lines.join('\n');
}

/**
 * Generate the exports code.
 *
 * @param {function(Error, string)} callback Called with the exports code or any
 *     error generating it.
 */
function main(callback) {
  async.waterfall([
    getInfo,
    function(typedefs, symbols, externs, done) {
      var code, err;
      try {
        code = generateExterns(typedefs, symbols, externs);
      } catch (e) {
        err = e;
      }
      done(err, code);
    }
  ], callback);
}

/**
 * If running this module directly, read the config file, call the main
 * function, and write the output file.
 */
if (require.main === module) {
  var options = nomnom.options({
    output: {
      position: 0,
      required: true,
      help: 'Output path for the generated externs file.'
    }
  }).parse();

  async.waterfall([
    main,
    fse.outputFile.bind(fse, options.output)
  ], function(err) {
    if (err) {
      console.error(err.message);
      process.exit(1);
    } else {
      process.exit(0);
    }
  });
}

/**
 * Export main function.
 */
module.exports = main;
<commit_msg>Prepend 'var ' for root symbols<commit_after>var fs = require('fs');
var path = require('path');

var async = require('async');
var fse = require('fs-extra');
var nomnom = require('nomnom');

var generateInfo = require('./generate-info');

/**
 * Read the symbols from info file.
 * @param {funciton(Error, Array.<string>, Array.<Object>)} callback Called
 *     with the patterns and symbols (or any error).
 */
function getInfo(callback) {
  generateInfo(function(err) {
    if (err) {
      callback(new Error('Trouble generating info: ' + err.message));
      return;
    }
    var typedefs = require('../build/info.json').typedefs;
    var symbols = require('../build/info.json').symbols;
    var externs = require('../build/info.json').externs;
    callback(null, typedefs, symbols, externs);
  });
}

/**
 * Generate externs code given a list symbols.
 * @param {Array.<Object>} typedefs List of typedefs.
 * @param {Array.<Object>} symbols List of symbols.
 * @param {Array.<Object>} externs List of externs.
 * @param {string|undefined} namespace Target object for exported symbols.
 * @return {string} Export code.
 */
function generateExterns(typedefs, symbols, externs) {
  var lines = [];
  var namespaces = {};
  var constructors = {};

  function addNamespaces(name) {
    var parts = name.split('.');
    parts.pop();
    var namespace = [];
    parts.forEach(function(part) {
      namespace.push(part);
      var partialNamespace = namespace.join('.');
      if (!(partialNamespace in namespaces ||
          partialNamespace in constructors)) {
        lines.push('/**');
        lines.push(' * @type {Object}');
        lines.push(' */');
        lines.push(nameToJS(partialNamespace) + ';');
        lines.push('\n');
      }
    });
  }

  function nameToJS(name) {
    if (name.indexOf('.') == -1) {
      namespaces[name] = true;
      name = 'var ' + name;
    }
    return name;
  }

  function processSymbol(symbol) {
    addNamespaces(symbol.name.split('#')[0]);

    var name = symbol.name;
    if (name.indexOf('#') > 0) {
      name = symbol.name.replace('#', '.prototype.');
      var constructor = symbol.name.split('#')[0];
      if (!(constructor in constructors)) {
        constructors[constructor] = true;
        lines.push('/**');
        lines.push(' * @constructor');
        lines.push(' */');
        lines.push(nameToJS(constructor) + ' = function() {};');
        lines.push('\n');
      }
    }

    lines.push('/**');
    if (symbol.kind == 'class') {
      constructors[name] = true;
      lines.push(' * @constructor');
    }
    if (symbol.types) {
      lines.push(' * @type {' + symbol.types.join('|') + '}');
    }
    var args = [];
    if (symbol.params) {
      symbol.params.forEach(function(param) {
        args.push(param.name);
        lines.push(' * @param {' +
            (param.variable ? '...' : '') +
            param.types.join('|') +
            (param.optional ? '=' : '') +
            '} ' + param.name);
      });
    }
    if (symbol.returns) {
      lines.push(' * @return {' + symbol.returns.join('|') + '}');
    }
    if (symbol.template) {
      lines.push(' * @template ' + symbol.template);
    }
    lines.push(' */');
    if (symbol.kind == 'function' || symbol.kind == 'class') {
      lines.push(nameToJS(name) + ' = function(' + args.join(', ') + ') {};');
    } else {
      lines.push(nameToJS(name) + ';');
    }
    lines.push('\n');
  }

  externs.forEach(processSymbol);

  typedefs.forEach(function(typedef) {
    addNamespaces(typedef.name);
    lines.push('/**');
    lines.push(' * @typedef {' + typedef.types.join('|') + '}');
    lines.push(' */');
    lines.push(nameToJS(typedef.name) + ';');
    lines.push('\n');
  });

  symbols.forEach(processSymbol);

  return lines.join('\n');
}

/**
 * Generate the exports code.
 *
 * @param {function(Error, string)} callback Called with the exports code or any
 *     error generating it.
 */
function main(callback) {
  async.waterfall([
    getInfo,
    function(typedefs, symbols, externs, done) {
      var code, err;
      try {
        code = generateExterns(typedefs, symbols, externs);
      } catch (e) {
        err = e;
      }
      done(err, code);
    }
  ], callback);
}

/**
 * If running this module directly, read the config file, call the main
 * function, and write the output file.
 */
if (require.main === module) {
  var options = nomnom.options({
    output: {
      position: 0,
      required: true,
      help: 'Output path for the generated externs file.'
    }
  }).parse();

  async.waterfall([
    main,
    fse.outputFile.bind(fse, options.output)
  ], function(err) {
    if (err) {
      console.error(err.message);
      process.exit(1);
    } else {
      process.exit(0);
    }
  });
}

/**
 * Export main function.
 */
module.exports = main;
<|endoftext|>


<commit_before>(function(document, window) {
  'use strict';

  /**
   * Helpers to make old ie work
   */
  var Helpers = {
    _eventMethods: (function() {
      var out;

      if(window.addEventListener !== 'undefined') {
        out = ['addEventListener', 'removeEventListener'];
      } else {
        out = ['attachEvent', 'detachEvent'];
      }

      return out;
    })(),

    /** shim addEventListener */
    addEvent: function(el, e, fn) {
      el[this._eventMethods[0]](e, fn, true);
    },

    removeEvent: function(el, e, fn) {
      el[this._eventMethods[1]](e, fn, true);
    },

    /** custom event cache */
    _events: {},

    createEvent: function(eventName) {
      var event;

      if (document.createEvent) {
        event = document.createEvent("HTMLEvents");
        event.initEvent(eventName, true, true);
      } else {
        event = document.createEventObject();
        event.eventType = eventName;
      }

      event.eventName = eventName;

      this._events[eventName] = event;

      return event;
    },

    /**
     * @param el {object} - dom node to recieve event
     * @param eventName {string} - name of event to fire
     */
    fireEvent: function(el, eventName) {
      var event = this._events[eventName] || this.createEvent(eventName);

      if (document.createEvent) {
        el.dispatchEvent(event);
      } else {
        el.fireEvent("on" + event.eventType, event);
      }
    },

    redraw: function(el) {
      return el.offsetHeight;
    }
  };

  var Range = function(el) {
    this.input = el;

    this.value = parseFloat(el.value);
    this.max = parseFloat(el.getAttribute('max')) || 100;
    this.min = parseFloat(el.getAttribute('min')) || 0;
    this.step = parseFloat(el.getAttribute('step')) || 1;

    this.mouseDown = false;
  }

  Range.prototype = {
    init: function() {

      this._render();
      this._bindEvents();
      this._setValue(this.value);

      return this;
    },

    _render: function() {
      this.input.style.opacity = 0;
      this.input.style.position = 'absolute';

      this.input.parentNode.insertBefore(this._template(), this.input.nextSibling);

      Helpers.redraw(this.pointer);

      this.pointerWidth = this.pointer.offsetWidth;

      var rect = this.el.getBoundingClientRect();

      this.xMin = rect.left;
      this.xMax = rect.right - this.xMin;
    },

    _template: function() {
      this.el = document.createElement('div');
      this.el.className = 'range-replacement';
      this.el.style.position = 'relative';

      this.pointer = document.createElement('div');
      this.pointer.className = 'point';
      this.pointer.style.position = 'absolute';

      this.el.appendChild(this.pointer)

      return this.el;
    },

    _bindEvents: function() {
      var that = this;

      Helpers.addEvent(this.el, 'mousedown', function(e) { that._onMouseDown(e); });
      Helpers.addEvent(this.el, 'mouseup', function(e) { that._onMouseUp(e); });
    },

    _onMouseDown: function(e) {
      this.oldValue = this.value;

      this._input(e);

      Helpers.fireEvent(this.input, 'mousedown');

      var that = this;

      var mouseUpdate = function(e) {
        that._input(e);
        that._change();

        if(that.mouseDown) {
          mouseUpdate(e);
        }
      };

      var onMove = function(e) {
        that._input(e);
      };

      var onUp = function(e) {
        that._change();
        Helpers.removeEvent(window, 'mousemove',  onMove);
        Helpers.removeEvent(window, 'mouseup', onUp);
      }

      Helpers.addEvent(window, 'mousemove',  onMove);
      Helpers.addEvent(window, 'mouseup', onUp);
    },

    _onMouseUp: function(e) {
      this._input(e);
      this._change();

      // stop updating
      Helpers.fireEvent(this.input, 'mouseup');
      Helpers.fireEvent(this.input, 'click');
    },

    _input: function(e) {
      var x = (window.Event) ? e.pageX : Event.clientX;

      var value = parseInt(this._scale(x - this.xMin, 0, this.xMax, this.min, this.max));

      this._setValue(value);

      if(this.oldValue !== this.value) {
        Helpers.fireEvent(this.input, 'input');
      }
    },

    _setValue: function(value) {
      // round to nearest step + min limit between min and max
      var rounded = this._round(value);
      var limited = this._limitToRange(rounded);
      this.newValue = limited;

      // set pointer position
      var maxLeft = this.xMax - this.pointerWidth;

      // TODO: should be correct number of steps. one per possible value
      var left = this._scale(limited, this.min, this.max, 0, maxLeft);

      this.pointer.style.left = left + 'px';
    },

    _change: function() {
      if(this.oldValue !== this.newValue) {
        this.value = this.newValue;

        this.input.value = this.oldValue = this.value;

        Helpers.fireEvent(this.input, 'change');
      }
    },

    _limitToRange: function(n) {
      return this._limit(n, this.max, this.min);
    },

    _limit: function(n, max, min) {
      return Math.min(Math.max(n, min), max);
    },

    /**
     * @param {number} value - number to be rounded
     * @param {number} srcLow - min value
     * @param {number} srcHigh - max value
     * @param {number} destLow - min output
     * @param {number} destHigh - max output
     * @returns {number} - value scaled between destLow, and destHigh
     */
    _scale: function(value, srcLow, srcHigh, destLow, destHigh) {
      return ((value - srcLow) / (srcHigh - srcLow)) * (destHigh - destLow) + destLow;
    },

    /**
     * @param {number} n - to be rounded
     * @returns {integer} - n rounded to nearest this.step
     */
    _round: function(n) {
      return Math.round(n / this.step) * this.step;
    }
  };

  /**
   * Apply Range to single range input
   *
   * @returns {object} - Range instance
   */
  Range.new = function(el) {
    return new Range(el).init();
  };

  /**
   * Apply to all range inputs
   *
   * @returns {array} - Range instances
   */
  Range.init = function(selector) {
    selector = selector || 'input[type=range]'
    var els = document.querySelectorAll(selector);
    var ranges = [];

    for(var i = 0, el; el = els[i]; i++) {
      ranges.push(Range.new(el));
    }

    return ranges;
  };

  // expose just what is needed
  var out = {
    new: Range.new,
    init: Range.init
  };

  var define = window['define'] || null;

  if(typeof define === 'function' && define['amd']) {
    define('range', [], function(){ return out; });
  } else {
    window.Range = out;
  }

})(document, window);

<commit_msg>float steps<commit_after>(function(document, window) {
  'use strict';

  /**
   * Helpers to make old ie work
   */
  var Helpers = {
    _eventMethods: (function() {
      var out;

      if(window.addEventListener !== 'undefined') {
        out = ['addEventListener', 'removeEventListener'];
      } else {
        out = ['attachEvent', 'detachEvent'];
      }

      return out;
    })(),

    /** shim addEventListener */
    addEvent: function(el, e, fn) {
      el[this._eventMethods[0]](e, fn, true);
    },

    removeEvent: function(el, e, fn) {
      el[this._eventMethods[1]](e, fn, true);
    },

    /** custom event cache */
    _events: {},

    createEvent: function(eventName) {
      var event;

      if (document.createEvent) {
        event = document.createEvent("HTMLEvents");
        event.initEvent(eventName, true, true);
      } else {
        event = document.createEventObject();
        event.eventType = eventName;
      }

      event.eventName = eventName;

      this._events[eventName] = event;

      return event;
    },

    /**
     * @param el {object} - dom node to recieve event
     * @param eventName {string} - name of event to fire
     */
    fireEvent: function(el, eventName) {
      var event = this._events[eventName] || this.createEvent(eventName);

      if (document.createEvent) {
        el.dispatchEvent(event);
      } else {
        el.fireEvent("on" + event.eventType, event);
      }
    },

    redraw: function(el) {
      return el.offsetHeight;
    }
  };

  var Range = function(el) {
    this.input = el;

    this.value = parseFloat(el.value);
    this.max = parseFloat(el.getAttribute('max')) || 100;
    this.min = parseFloat(el.getAttribute('min')) || 0;
    this.step = parseFloat(el.getAttribute('step')) || 1;

    this.mouseDown = false;
  }

  Range.prototype = {
    init: function() {

      this._render();
      this._bindEvents();
      this._setValue(this.value);

      return this;
    },

    _render: function() {
      this.input.style.opacity = 0;
      this.input.style.position = 'absolute';

      this.input.parentNode.insertBefore(this._template(), this.input.nextSibling);

      Helpers.redraw(this.pointer);

      this.pointerWidth = this.pointer.offsetWidth;

      var rect = this.el.getBoundingClientRect();

      this.xMin = rect.left;
      this.xMax = rect.right - this.xMin;
    },

    _template: function() {
      this.el = document.createElement('div');
      this.el.className = 'range-replacement';
      this.el.style.position = 'relative';

      this.pointer = document.createElement('div');
      this.pointer.className = 'point';
      this.pointer.style.position = 'absolute';

      this.el.appendChild(this.pointer)

      return this.el;
    },

    _bindEvents: function() {
      var that = this;

      Helpers.addEvent(this.el, 'mousedown', function(e) { that._onMouseDown(e); });
      Helpers.addEvent(this.el, 'mouseup', function(e) { that._onMouseUp(e); });
    },

    _onMouseDown: function(e) {
      this.oldValue = this.value;

      this._input(e);

      Helpers.fireEvent(this.input, 'mousedown');

      var that = this;

      var mouseUpdate = function(e) {
        that._input(e);
        that._change();

        if(that.mouseDown) {
          mouseUpdate(e);
        }
      };

      var onMove = function(e) {
        that._input(e);
      };

      var onUp = function(e) {
        that._change();
        Helpers.removeEvent(window, 'mousemove',  onMove);
        Helpers.removeEvent(window, 'mouseup', onUp);
      }

      Helpers.addEvent(window, 'mousemove',  onMove);
      Helpers.addEvent(window, 'mouseup', onUp);
    },

    _onMouseUp: function(e) {
      this._input(e);
      this._change();

      // stop updating
      Helpers.fireEvent(this.input, 'mouseup');
      Helpers.fireEvent(this.input, 'click');
    },

    _input: function(e) {
      var x = (window.Event) ? e.pageX : Event.clientX;

      var value = parseFloat(this._scale(x - this.xMin, 0, this.xMax, this.min, this.max));

      this._setValue(value);

      if(this.oldValue !== this.value) {
        Helpers.fireEvent(this.input, 'input');
      }
    },

    _setValue: function(value) {
      // round to nearest step + min limit between min and max
      var rounded = this._round(value);
      var limited = this._limitToRange(rounded);
      this.newValue = limited;

      // set pointer position
      var maxLeft = this.xMax - this.pointerWidth;

      // TODO: should be correct number of steps. one per possible value
      var left = this._scale(limited, this.min, this.max, 0, maxLeft);

      this.pointer.style.left = left + 'px';
    },

    _change: function() {
      if(this.oldValue !== this.newValue) {
        this.value = this.newValue;

        this.input.value = this.oldValue = this.value;

        Helpers.fireEvent(this.input, 'change');
      }
    },

    _limitToRange: function(n) {
      return this._limit(n, this.max, this.min);
    },

    _limit: function(n, max, min) {
      return Math.min(Math.max(n, min), max);
    },

    /**
     * @param {number} value - number to be rounded
     * @param {number} srcLow - min value
     * @param {number} srcHigh - max value
     * @param {number} destLow - min output
     * @param {number} destHigh - max output
     * @returns {number} - value scaled between destLow, and destHigh
     */
    _scale: function(value, srcLow, srcHigh, destLow, destHigh) {
      return ((value - srcLow) / (srcHigh - srcLow)) * (destHigh - destLow) + destLow;
    },

    /**
     * @param {number} n - to be rounded
     * @returns {integer} - n rounded to nearest this.step
     */
    _round: function(n) {
      return Math.round(n / this.step) * this.step;
    }
  };

  /**
   * Apply Range to single range input
   *
   * @returns {object} - Range instance
   */
  Range.new = function(el) {
    return new Range(el).init();
  };

  /**
   * Apply to all range inputs
   *
   * @returns {array} - Range instances
   */
  Range.init = function(selector) {
    selector = selector || 'input[type=range]'
    var els = document.querySelectorAll(selector);
    var ranges = [];

    for(var i = 0, el; el = els[i]; i++) {
      ranges.push(Range.new(el));
    }

    return ranges;
  };

  // expose just what is needed
  var out = {
    new: Range.new,
    init: Range.init
  };

  var define = window['define'] || null;

  if(typeof define === 'function' && define['amd']) {
    define('range', [], function(){ return out; });
  } else {
    window.Range = out;
  }

})(document, window);

<|endoftext|>


<commit_before>var ltgt = require('ltgt')

//compare two array items
function isArrayLike (a) {
  return Array.isArray(a) || Buffer.isBuffer(a)
}

function isPrimitive (a) {
  return 'string' === typeof a || 'number' === typeof a
}

function has(o, k) {
  return Object.hasOwnProperty.call(o, k)
}

function compare (a, b) {

  if(isArrayLike(a) && isArrayLike(b)) {
    var l = Math.min(a.length, b.length)
    for(var i = 0; i < l; i++) {
      var c = compare(a[i], b[i])
      if(c) return c
    }
    return a.length - b.length
  }
  if(isPrimitive(a) && isPrimitive(b))
    return a < b ? -1 : a > b ? 1 : 0

  throw new Error('items not comparable:'
    + JSON.stringify(a) + ' ' + JSON.stringify(b))
}

//this assumes that the prefix is of the form:
// [Array, string]

function prefix (a, b) {
  if(a.length > b.length) return false
  var l = a.length - 1
  var lastA = a[l]
  var lastB = b[l]

  if(typeof lastA !== typeof lastB)
    return false

  if('string' == typeof lastA
    && 0 != lastB.indexOf(lastA))
      return false

  //handle cas where there is no key prefix
  //(a hook on an entire sublevel)
  if(a.length == 1 && isArrayLike(lastA)) l ++

  while(l--) {
    if(compare(a[l], b[l])) return false
  }
  return true
}

exports = module.exports = function (range, key) {
  //handle prefix specially,
  //check that everything up to the last item is equal
  //then check the last item starts with
  if(isArrayLike(range)) return prefix(range, key)

//  return ltgt.contains(range, key, compare)

  if(range.lt  && compare(key, range.lt) >= 0) return false
  if(range.lte && compare(key, range.lte) > 0) return false
  if(range.gt  && compare(key, range.gt) <= 0) return false
  if(range.gte && compare(key, range.gte) < 0) return false

  return true
}

function addPrefix(prefix, range) {
  var r = {}
  if(has(range, 'lt')) r.lt = [prefix, range.lt]
  if(has(range, 'gt')) r.gt = [prefix, range.gt]
  if(has(range, 'lte')) r.lte = [prefix, range.lte]
  if(has(range, 'gte')) r.gte = [prefix, range.gte]
  r.reverse = !!range.reverse
  return r
}

exports.compare = compare
exports.prefix = prefix
exports.addPrefix = addPrefix
<commit_msg>handle other the various ways of describing a range<commit_after>var ltgt = require('ltgt')

//compare two array items
function isArrayLike (a) {
  return Array.isArray(a) || Buffer.isBuffer(a)
}

function isPrimitive (a) {
  return 'string' === typeof a || 'number' === typeof a
}

function has(o, k) {
  return Object.hasOwnProperty.call(o, k)
}

function compare (a, b) {

  if(isArrayLike(a) && isArrayLike(b)) {
    var l = Math.min(a.length, b.length)
    for(var i = 0; i < l; i++) {
      var c = compare(a[i], b[i])
      if(c) return c
    }
    return a.length - b.length
  }
  if(isPrimitive(a) && isPrimitive(b))
    return a < b ? -1 : a > b ? 1 : 0

  throw new Error('items not comparable:'
    + JSON.stringify(a) + ' ' + JSON.stringify(b))
}

//this assumes that the prefix is of the form:
// [Array, string]

function prefix (a, b) {
  if(a.length > b.length) return false
  var l = a.length - 1
  var lastA = a[l]
  var lastB = b[l]

  if(typeof lastA !== typeof lastB)
    return false

  if('string' == typeof lastA
    && 0 != lastB.indexOf(lastA))
      return false

  //handle cas where there is no key prefix
  //(a hook on an entire sublevel)
  if(a.length == 1 && isArrayLike(lastA)) l ++

  while(l--) {
    if(compare(a[l], b[l])) return false
  }
  return true
}

exports = module.exports = function (range, key) {
  //handle prefix specially,
  //check that everything up to the last item is equal
  //then check the last item starts with
  if(isArrayLike(range)) return prefix(range, key)

//  return ltgt.contains(range, key, compare)

  if(range.lt  && compare(key, range.lt) >= 0) return false
  if(range.lte && compare(key, range.lte) > 0) return false
  if(range.gt  && compare(key, range.gt) <= 0) return false
  if(range.gte && compare(key, range.gte) < 0) return false

  return true
}

function addPrefix(prefix, range) {
  var r = {}
  if(has(range, 'lt')) r.lt = [prefix, range.lt]
  if(has(range, 'gt')) r.gt = [prefix, range.gt]
  if(has(range, 'lte')) r.lte = [prefix, range.lte]
  if(has(range, 'gte')) r.gte = [prefix, range.gte]
  if(has(range, 'start')) {
    if(range.reverse)  r.lte = [prefix, range.start]
    else               r.gte = [prefix, range.start]
  }
  if(has(range, 'end')) {
    if(range.reverse)  r.gte = [prefix, range.end]
    else               r.lte = [prefix, range.end]
  }
  if(has(range, 'min')) r.gte = [prefix, range.min]
  if(has(range, 'max')) r.lte = [prefix, range.max]
  r.reverse = !!range.reverse
  return r
}

exports.compare = compare
exports.prefix = prefix
exports.addPrefix = addPrefix
<|endoftext|>


<commit_before>M.util = {
    /**
     * 将一个值对象转换成引用对象
     * @memberof M
     * @param {Object} value
     * @return {Object}
     */
    toObject: function(value) {
        var obj = value;
        switch(typeof value){
            case 'string':
                obj = new String(value);
                break;
            case 'number':
                obj = new Number(value);
                break;
            case 'boolean':
                obj = new Boolean(value);
                break;
        }
        return obj;
    },

    /**
     * 调用方法自身
     * @memberof M
     * @return {Function}
     */

    fn: function() {
        return arguments.callee.caller.apply(this, arguments);
    },

    /**
     * 将类Array对象转换成Array
     * @memberof M
     * @param  {Object} arrayLikeObj
     * @return {Array}
     */

    parseArray: function(arrayLikeObj) {
        return Array.prototype.slice.call(arrayLikeObj, 0);
    },

    /**
     * 声明一个枚举
     * @memberof M
     * @param {...String} names enumeration key
     * @example
     * var color = $enum("BLUE", "RED", "YELLOW");
     * color.BLUE
     * color.RED
     * color.YELLOW
     *
     * var color = $enum{{
     *  "BLUE": -1,
     *  "RED": 1
     * })
     */

    enumeration: function(names) {
        if(typeof names == "object") {
            return arguments[0];
        }

        var _enum = {};
        for(var i = 0, l = arguments.length; i < l; i++) {
            _enum[arguments[i]] = {};
        }

        return _enum;
    },

    /**
     * 将一个纯函数包装成指定对象的一个方法，并在此时设定纯函数的第一参数
     * @memberof M
     * @param  {Function} fn 纯函数
     * @param  {Object}   [firstParam=this] fn的第一个参数，如果没有getFirstParam的话
     * @param  {Function} [getFirstParam] 使用此函数获取fn的第一个参数，调用此函数时将把firstParam传递给它
     * @return {Function}
     */

    methodize: function(fn, firstParam, getFirstParam) {
        var slice = Array.prototype.slice;
        return function() {
            var obj = firstParam || this;
            var args = [getFirstParam ? getFirstParam(obj) : obj].concat(slice.call(arguments, 0));
            return fn.apply(this, args);
        };
    },

    /**
     * overwrite对象的方法，新方法将调用指定的overwriter并把原方法当作第一个参数传递给它
     * @param  {Object} obj 被覆盖的对象
     * @param  {string} funcName   对象被覆盖的方法名
     * @param  {Function} overwriter 实际替代原方法的函数
     */
    overwrite: function(obj, funcName, overwriter) {
        var _baseFn = obj[funcName].bind(obj);
        obj[funcName] = function() {
            var args = [_baseFn].concat(Array.prototype.slice.call(arguments, 0));
            return overwriter.apply(this, args);
        };
    },

    /**
     * 生成供eval()函数将指定变量成员声明为当前作用域内变量的代码
     * 导入后会导致对象的方法调用时this变化
     * @memberof M
     * @param  {String} [obj=this]
     * @return {String}
     * @example
     * var Calculator = {
     *     add: function(a, b){ return a + b },
     *     sub: function(a, b){ return a - b }
     * }
     *
     * eval(localize(Calculator));
     *
     * add(4, 6);
     * sub(10, 4);
     */

    dsl: function(obj, names) {
        obj = obj || this;

        var tempVarName =  "_temp" + Date.now() + Math.random().toString().substr(2);
        eval(tempVarName + "={value: {}}");

        var temp = eval(tempVarName);
        temp.value = obj;

        if(typeof names == "string" && names !== ""){
            names = names.split(" ").map(function(n){ return n.trim(); });
        }else if(!names){
            names = Object.keys(obj);
        }

        //generate members declare string
        // var keys = Object.keys(obj).filter(function(k) {
            // return obj.hasOwnProperty(k) && typeof obj[k] == "function";
        // });

        var members = names.map(function(name) {
            //可以加入检查是否已经定义的功能，如已定义则警告。
            return name + "=" + tempVarName + ".value" + "['" + name + "']";
        });

        return "var " + members.join(",") + ";delete " + tempVarName + ";";
    },

    /**
     * 运行一个方法，一个快捷的使用闭包来避免全局变量的方法
     * @param  {Function} fn [description]
     * @return {[type]}      [description]
     */
    run: function(fn) {
        if(arguments.length == 1) {
            return fn();
        } else {
            return fn.apply(this, Array.prototype.slice.call(arguments, 1));
        }
    }
}
<commit_msg>重构methodize()便更易读<commit_after>M.util = {
    /**
     * 将一个值对象转换成引用对象
     * @memberof M
     * @param {Object} value
     * @return {Object}
     */
    toObject: function(value) {
        var obj = value;
        switch(typeof value){
            case 'string':
                obj = new String(value);
                break;
            case 'number':
                obj = new Number(value);
                break;
            case 'boolean':
                obj = new Boolean(value);
                break;
        }
        return obj;
    },

    /**
     * 调用方法自身
     * @memberof M
     * @return {Function}
     */

    fn: function() {
        return arguments.callee.caller.apply(this, arguments);
    },

    /**
     * 将类Array对象转换成Array
     * @memberof M
     * @param  {Object} arrayLikeObj
     * @return {Array}
     */

    parseArray: function(arrayLikeObj) {
        return Array.prototype.slice.call(arrayLikeObj, 0);
    },

    /**
     * 声明一个枚举
     * @memberof M
     * @param {...String} names enumeration key
     * @example
     * var color = $enum("BLUE", "RED", "YELLOW");
     * color.BLUE
     * color.RED
     * color.YELLOW
     *
     * var color = $enum{{
     *  "BLUE": -1,
     *  "RED": 1
     * })
     */

    enumeration: function(names) {
        if(typeof names == "object") {
            return arguments[0];
        }

        var _enum = {};
        for(var i = 0, l = arguments.length; i < l; i++) {
            _enum[arguments[i]] = {};
        }

        return _enum;
    },

    /**
     * 将一个纯函数包装成指定对象的一个方法，并在此时设定纯函数的第一参数
     * @memberof M
     * @param  {Function} fn 纯函数
     * @param  {Object}   [firstParam=this] fn的第一个参数，如果没有getFirstParam的话
     * @param  {Function} [getFirstParam] 使用此函数获取fn的第一个参数，调用此函数时将把firstParam传递给它
     * @return {Function}
     */

    methodize: function(fn, firstParam, getFirstParam) {
        var slice = Array.prototype.slice;
        return function() {
            var obj = firstParam || this;
            var args = [getFirstParam ? getFirstParam(obj) : obj].concat(slice.call(arguments, 0));
            return fn.apply(this, args);
        };
    },

    methodize: function(fn, firstParam, getFirstParam) {
        return function() {
            //获取第一个参数
            var p1 = firstParam || this;
            if(getFirstParam) p1 = getFirstParam(p1);

            //把第一个参数和其他参数放在一起
            var slice = Array.prototype.slice;
            var args = [p1].concat(slice.call(arguments));

            return fn.apply(this, args);
        }
    },

    /**
     * overwrite对象的方法，新方法将调用指定的overwriter并把原方法当作第一个参数传递给它
     * @param  {Object} obj 被覆盖的对象
     * @param  {string} funcName   对象被覆盖的方法名
     * @param  {Function} overwriter 实际替代原方法的函数
     */
    overwrite: function(obj, funcName, overwriter) {
        var _baseFn = obj[funcName].bind(obj);
        obj[funcName] = function() {
            var args = [_baseFn].concat(Array.prototype.slice.call(arguments, 0));
            return overwriter.apply(this, args);
        };
    },

    /**
     * 生成供eval()函数将指定变量成员声明为当前作用域内变量的代码
     * 导入后会导致对象的方法调用时this变化
     * @memberof M
     * @param  {String} [obj=this]
     * @return {String}
     * @example
     * var Calculator = {
     *     add: function(a, b){ return a + b },
     *     sub: function(a, b){ return a - b }
     * }
     *
     * eval(dsl(Calculator));
     *
     * add(4, 6);
     * sub(10, 4);
     */

    dsl: function(obj, names) {
        obj = obj || this;

        var tempVarName =  "_temp" + Date.now() + Math.random().toString().substr(2);
        eval(tempVarName + "={value: {}}");

        var temp = eval(tempVarName);
        temp.value = obj;

        if(typeof names == "string" && names !== ""){
            names = names.split(" ").map(function(n){ return n.trim(); });
        }else if(!names){
            names = Object.keys(obj);
        }

        //generate members declare string
        // var keys = Object.keys(obj).filter(function(k) {
            // return obj.hasOwnProperty(k) && typeof obj[k] == "function";
        // });

        var members = names.map(function(name) {
            //可以加入检查是否已经定义的功能，如已定义则警告。
            return name + "=" + tempVarName + ".value" + "['" + name + "']";
        });

        return "var " + members.join(",") + ";delete " + tempVarName + ";";
    },

    /**
     * 运行一个方法，一个快捷的使用闭包来避免全局变量的方法
     * @param  {Function} fn [description]
     * @return {[type]}      [description]
     */
    run: function(fn) {
        if(arguments.length == 1) {
            return fn();
        } else {
            return fn.apply(this, Array.prototype.slice.call(arguments, 1));
        }
    }
}
<|endoftext|>


<commit_before>import csvStringify from 'csv-stringify';

function convertToCSV(data) {
  var sectionStack = [],
      rows = [];

  function traverse(items) {
    if (items.length > 0) {
      items.forEach(section => {
        // Keep traversing
        //
        sectionStack.push(section);
        traverse(section.items);
        sectionStack.pop();
      });

    } else {
      // "Leaf" node, output sectionStack to rows[]
      //
      let fullTitleArray = ['', '', '', '', '', ''],
          lastSection = sectionStack[sectionStack.length - 1],
          text, titleStack;

      if (!lastSection.hasTitle()) {
        // Last section was normal content.
        //
        text = lastSection.text;
        titleStack = sectionStack.slice(0, -1); // exclude last section from title
      } else {
        // Last section was a title as well.
        //
        text = '';
        titleStack = sectionStack;
      }

      // Fill up title0 ~ title5.
      // Leave blank ('') for titles that's too deep or too shallow.
      //
      titleStack.forEach(({text}, idx) => {
        fullTitleArray[idx] = text;
      });

      // Use "last title" as the coordinate if possible.
      //
      let outputCoord;
      if (titleStack.length) {
        outputCoord = titleStack[titleStack.length - 1].coord;
      } else {
        outputCoord = lastSection.coord;
      }

      // title0 ~ title5, page, coordinate, text
      rows.push([].concat(lastSection.errors.join(''), fullTitleArray, lastSection.page, outputCoord, text));
    }

  }

  traverse(data);

  return new Promise((resolve, reject) => {
    csvStringify(rows, (err, csv) => {
      if (err) {reject(err);} else {resolve(csv);}
    })
  });
}

export default {convertToCSV}
<commit_msg>Correctly aggregate all errors within the title stack.<commit_after>import csvStringify from 'csv-stringify';

function convertToCSV(data) {
  var sectionStack = [],
      rows = [];

  function traverse(items) {
    if (items.length > 0) {
      items.forEach(section => {
        // Keep traversing
        //
        sectionStack.push(section);
        traverse(section.items);
        sectionStack.pop();
      });

    } else {
      // "Leaf" node, output sectionStack to rows[]
      //
      let fullTitleArray = ['', '', '', '', '', ''],
          lastSection = sectionStack[sectionStack.length - 1],
          text, titleStack;

      if (!lastSection.hasTitle()) {
        // Last section was normal content.
        //
        text = lastSection.text;
        titleStack = sectionStack.slice(0, -1); // exclude last section from title
      } else {
        // Last section was a title as well.
        //
        text = '';
        titleStack = sectionStack;
      }

      // Fill up title0 ~ title5.
      // Leave blank ('') for titles that's too deep or too shallow.
      //
      titleStack.forEach(({text}, idx) => {
        fullTitleArray[idx] = text;
      });

      // Use "last title" as the coordinate if possible.
      //
      let outputCoord;
      if (titleStack.length) {
        outputCoord = titleStack[titleStack.length - 1].coord;
      } else {
        outputCoord = lastSection.coord;
      }

      // Aggregate errors in the stack.
      let aggregatedErrors = [];
      sectionStack.forEach((section) => {
        if (!section.__isErrorOutputted) {
          aggregatedErrors = aggregatedErrors.concat(section.errors);
          // Errors just need to be output once
          //
          section.__isErrorOutputted = true;
        }
      });

      // title0 ~ title5, page, coordinate, text
      rows.push([].concat(aggregatedErrors.join(''), fullTitleArray, lastSection.page, outputCoord, text));
    }

  }

  traverse(data);

  return new Promise((resolve, reject) => {
    csvStringify(rows, (err, csv) => {
      if (err) {reject(err);} else {resolve(csv);}
    })
  });
}

export default {convertToCSV}
<|endoftext|>


<commit_before>import { angle, onSegment } from '../../common/Geometry'
import Machine from './machine'
import Victor from 'victor'

export const traceCircle = (start, end, size) => {
  const startAngle = start.angle()
  const endAngle = end.angle()
  let resolution = (Math.PI*2.0) / 128.0 // 128 segments per circle. Enough?
  let deltaAngle = ((endAngle - startAngle) + 2.0 * Math.PI) % (2.0 * Math.PI)

  if (deltaAngle > Math.PI) {
    deltaAngle -= 2.0 * Math.PI
  }
  if (deltaAngle < 0.0) {
    resolution *= -1.0
  }

  let tracePoints = []
  for (let step = 0; step < (deltaAngle/resolution) ; step++) {
    tracePoints.push(Victor(size * Math.cos(resolution * step + startAngle),
                            size * Math.sin(resolution * step + startAngle)))
  }
  return tracePoints
}

export default class PolarMachine extends Machine {
  // vertices should be a Victor array
  constructor(vertices, settings) {
    super()
    this.vertices = vertices
    this.settings = settings
  }

  addEndpoints() {
    const maxRadius = this.settings.maxRadius

    if (this.settings.polarStartPoint !== 'none') {
      if (this.settings.polarStartPoint === 'center') {
        this.vertices.unshift(new Victor(0.0, 0.0))
      } else {
        const first = this.vertices[0]
        const scale = maxRadius / first.magnitude()
        const startPoint = first.multiply(new Victor(scale, scale))
        this.vertices.unshift(new Victor(startPoint.x, startPoint.y))
      }
    }

    if (this.settings.polarEndPoint !== 'none') {
      if (this.settings.polarEndPoint === 'center') {
        this.vertices.push(new Victor(0.0, 0.0))
      } else {
        const last = this.vertices[this.vertices.length-1]
        const scale = maxRadius / last.magnitude()
        const endPoint = last.multiply(new Victor(scale, scale))
        this.vertices.push(new Victor(endPoint.x, endPoint.y))
      }
    }

    return this
  }

  // walk the given vertices, clipping as needed along the circle perimeter
  enforceLimits() {
    let cleanVertices = []
    let previous = null

    for (let next=0; next<this.vertices.length; next++) {
      const vertex = this.vertices[next]

      if (previous) {
        const line = this.clipLine(previous, vertex)

        for (let pt=0; pt<line.length; pt++) {
          if (line[pt] !== previous) {
            cleanVertices.push(line[pt])
          }
        }
      } else {
        cleanVertices.push(this.nearestVertex(vertex))
      }

      previous = vertex
    }

    this.vertices = cleanVertices
    return this
  }

  // Finds the nearest vertex that is in the bounds of the circle. This will change the shape. i.e. this doesn't
  // care about the line segment, only about the point.
  nearestVertex(vertex) {
    const size = this.settings.maxRadius

    if ( vertex.length() > size) {
      const scale = size / vertex.length()
      return vertex.multiply(new Victor(scale, scale))
    } else {
      return vertex
    }
  }

  // returns the distance along the perimeter between two points
  perimeterDistance(v1, v2) {
    const startAngle = angle(v1)
    const endAngle = angle(v2)
    let deltaAngle = Math.abs(endAngle - startAngle)

    if (deltaAngle > Math.PI) {
      deltaAngle -= 2.0 * Math.PI
    }

    return Math.abs(deltaAngle)
  }

  // This method is the guts of logic for this limits enforcer. It will take a single line (defined by
  // start and end) and if the line goes out of bounds, returns the vertices around the outside edge
  // to follow around without messing up the shape of the vertices.
  //
  clipLine(start, end) {
    // Cases:
    // 1 - Entire line is inside
    //     return start, end
    // 2 - Entire line is outside
    //     trace from start to end
    // 3 - only start is inside
    //     find the intersection
    //     include start
    //     include intersection
    //     trace from intersection to closest to end point
    // 4 - only end is inside
    //     do reverse of 3
    // 4 - Neither end is inside, but there is some line segment inside
    //     find both intersections
    //     trace from start to first intersction
    //     trace from second intersection to end
    const size = this.settings.maxRadius
    const radStart = start.magnitude()
    const radEnd = end.magnitude()

    if (radStart <= size && radEnd <= size) {
      // The whole segment is inside
      return [start, end]
    }

    // Check for the odd case of coincident points
    if (start.distance(end) < 0.00001) {
       return [this.nearestVertex(start)]
    }

    let intersections = this.getIntersections(start, end)
    if (!intersections.intersection) {
      // The whole line is outside, just trace.
      return this.tracePerimeter(start, end)
    }

    // if neither point is on the segment, then it should just be a trace
    if (!intersections.points[0].on && ! intersections.points[1].on) {
      return this.tracePerimeter(start, end)
    }

    // If both points are outside, but there's an intersection
    if (radStart > size + 1.0e-9 && radEnd > size + 1.0e-9) {
      let point = intersections.points[0].point
      let otherPoint = intersections.points[1].point

      return [
        ...this.tracePerimeter(start, point),
        point,
        ...this.tracePerimeter(otherPoint, end)
      ]
    }

    // If we're here, then one point is still in the circle.
    if (radStart <= size) {
      let point1 = (intersections.points[0].on && Math.abs(intersections.points[0].point - start) > 0.0001) ? intersections.points[0].point : intersections.points[1].point

      return [
        start,
        ...this.tracePerimeter(point1, end),
        end
      ]
    } else {
      const point1 = intersections.points[0].on ? intersections.points[0].point : intersections.points[1].point

      return [
        ...this.tracePerimeter(start, point1),
        point1,
        end
      ]
    }
  }

  // Returns points along the circle from the start to the end, tracing a circle of radius size.
  tracePerimeter(start, end) {
    return traceCircle(start, end, this.settings.maxRadius)
  }

  getIntersections(start, end) {
    const size = this.settings.maxRadius
    let direction = end.clone().subtract(start).clone().normalize()
    let t = direction.x * -1.0 * start.x + direction.y * -1.0 * start.y
    let e = direction.clone().multiply(Victor(t,t)).add(start)
    let distanceToLine = e.magnitude()

    if (distanceToLine >= size) {
      return {
        intersection: false,
        points: [],
      }
    }

    let dt = Math.sqrt(size*size - distanceToLine*distanceToLine)
    let point1 = direction.clone().multiply(Victor(t - dt,t - dt)).add(start)
    let point2 = direction.clone().multiply(Victor(t + dt,t + dt)).add(start)

    return {
      intersection: true,
      points: [
        {
          point: point1,
          on: onSegment(start, end, point1),
        },
        {
          point: point2,
          on: onSegment(start, end, point2),
        }
      ]}
  }

  onPerimeter(v1, v2, delta=.001) {
    let rm = Math.pow(this.settings.maxRadius, 2)
    let r1 = Math.pow(v1.x, 2) + Math.pow(v1.y, 2)
    let r2 = Math.pow(v2.x, 2) + Math.pow(v2.y, 2)

    return r1 >= rm - delta && r2 >= rm - delta
  }
}
<commit_msg>fix: don't modify original points when creating end points<commit_after>import { angle, onSegment } from '../../common/Geometry'
import Machine from './machine'
import Victor from 'victor'

export const traceCircle = (start, end, size) => {
  const startAngle = start.angle()
  const endAngle = end.angle()
  let resolution = (Math.PI*2.0) / 128.0 // 128 segments per circle. Enough?
  let deltaAngle = ((endAngle - startAngle) + 2.0 * Math.PI) % (2.0 * Math.PI)

  if (deltaAngle > Math.PI) {
    deltaAngle -= 2.0 * Math.PI
  }
  if (deltaAngle < 0.0) {
    resolution *= -1.0
  }

  let tracePoints = []
  for (let step = 0; step < (deltaAngle/resolution) ; step++) {
    tracePoints.push(Victor(size * Math.cos(resolution * step + startAngle),
                            size * Math.sin(resolution * step + startAngle)))
  }
  return tracePoints
}

export default class PolarMachine extends Machine {
  // vertices should be a Victor array
  constructor(vertices, settings) {
    super()
    this.vertices = vertices
    this.settings = settings
  }

  addEndpoints() {
    const maxRadius = this.settings.maxRadius

    if (this.settings.polarStartPoint !== 'none') {
      if (this.settings.polarStartPoint === 'center') {
        this.vertices.unshift(new Victor(0.0, 0.0))
      } else {
        const first = this.vertices[0]
        const scale = maxRadius / first.magnitude()
        const startPoint = Victor.fromObject(first).multiply(new Victor(scale, scale))
        this.vertices.unshift(new Victor(startPoint.x, startPoint.y))
      }
    }

    if (this.settings.polarEndPoint !== 'none') {
      if (this.settings.polarEndPoint === 'center') {
        this.vertices.push(new Victor(0.0, 0.0))
      } else {
        const last = this.vertices[this.vertices.length-1]
        const scale = maxRadius / last.magnitude()
        const endPoint =  Victor.fromObject(last).multiply(new Victor(scale, scale))
        this.vertices.push(new Victor(endPoint.x, endPoint.y))
      }
    }

    return this
  }

  // walk the given vertices, clipping as needed along the circle perimeter
  enforceLimits() {
    let cleanVertices = []
    let previous = null

    for (let next=0; next<this.vertices.length; next++) {
      const vertex = this.vertices[next]

      if (previous) {
        const line = this.clipLine(previous, vertex)

        for (let pt=0; pt<line.length; pt++) {
          if (line[pt] !== previous) {
            cleanVertices.push(line[pt])
          }
        }
      } else {
        cleanVertices.push(this.nearestVertex(vertex))
      }

      previous = vertex
    }

    this.vertices = cleanVertices
    return this
  }

  // Finds the nearest vertex that is in the bounds of the circle. This will change the shape. i.e. this doesn't
  // care about the line segment, only about the point.
  nearestVertex(vertex) {
    const size = this.settings.maxRadius

    if ( vertex.length() > size) {
      const scale = size / vertex.length()
      return vertex.multiply(new Victor(scale, scale))
    } else {
      return vertex
    }
  }

  // returns the distance along the perimeter between two points
  perimeterDistance(v1, v2) {
    const startAngle = angle(v1)
    const endAngle = angle(v2)
    let deltaAngle = Math.abs(endAngle - startAngle)

    if (deltaAngle > Math.PI) {
      deltaAngle -= 2.0 * Math.PI
    }

    return Math.abs(deltaAngle)
  }

  // This method is the guts of logic for this limits enforcer. It will take a single line (defined by
  // start and end) and if the line goes out of bounds, returns the vertices around the outside edge
  // to follow around without messing up the shape of the vertices.
  //
  clipLine(start, end) {
    // Cases:
    // 1 - Entire line is inside
    //     return start, end
    // 2 - Entire line is outside
    //     trace from start to end
    // 3 - only start is inside
    //     find the intersection
    //     include start
    //     include intersection
    //     trace from intersection to closest to end point
    // 4 - only end is inside
    //     do reverse of 3
    // 4 - Neither end is inside, but there is some line segment inside
    //     find both intersections
    //     trace from start to first intersction
    //     trace from second intersection to end
    const size = this.settings.maxRadius
    const radStart = start.magnitude()
    const radEnd = end.magnitude()

    if (radStart <= size && radEnd <= size) {
      // The whole segment is inside
      return [start, end]
    }

    // Check for the odd case of coincident points
    if (start.distance(end) < 0.00001) {
       return [this.nearestVertex(start)]
    }

    let intersections = this.getIntersections(start, end)
    if (!intersections.intersection) {
      // The whole line is outside, just trace.
      return this.tracePerimeter(start, end)
    }

    // if neither point is on the segment, then it should just be a trace
    if (!intersections.points[0].on && ! intersections.points[1].on) {
      return this.tracePerimeter(start, end)
    }

    // If both points are outside, but there's an intersection
    if (radStart > size + 1.0e-9 && radEnd > size + 1.0e-9) {
      let point = intersections.points[0].point
      let otherPoint = intersections.points[1].point

      return [
        ...this.tracePerimeter(start, point),
        point,
        ...this.tracePerimeter(otherPoint, end)
      ]
    }

    // If we're here, then one point is still in the circle.
    if (radStart <= size) {
      let point1 = (intersections.points[0].on && Math.abs(intersections.points[0].point - start) > 0.0001) ? intersections.points[0].point : intersections.points[1].point

      return [
        start,
        ...this.tracePerimeter(point1, end),
        end
      ]
    } else {
      const point1 = intersections.points[0].on ? intersections.points[0].point : intersections.points[1].point

      return [
        ...this.tracePerimeter(start, point1),
        point1,
        end
      ]
    }
  }

  // Returns points along the circle from the start to the end, tracing a circle of radius size.
  tracePerimeter(start, end) {
    return traceCircle(start, end, this.settings.maxRadius)
  }

  getIntersections(start, end) {
    const size = this.settings.maxRadius
    let direction = end.clone().subtract(start).clone().normalize()
    let t = direction.x * -1.0 * start.x + direction.y * -1.0 * start.y
    let e = direction.clone().multiply(Victor(t,t)).add(start)
    let distanceToLine = e.magnitude()

    if (distanceToLine >= size) {
      return {
        intersection: false,
        points: [],
      }
    }

    let dt = Math.sqrt(size*size - distanceToLine*distanceToLine)
    let point1 = direction.clone().multiply(Victor(t - dt,t - dt)).add(start)
    let point2 = direction.clone().multiply(Victor(t + dt,t + dt)).add(start)

    return {
      intersection: true,
      points: [
        {
          point: point1,
          on: onSegment(start, end, point1),
        },
        {
          point: point2,
          on: onSegment(start, end, point2),
        }
      ]}
  }

  onPerimeter(v1, v2, delta=.001) {
    let rm = Math.pow(this.settings.maxRadius, 2)
    let r1 = Math.pow(v1.x, 2) + Math.pow(v1.y, 2)
    let r2 = Math.pow(v2.x, 2) + Math.pow(v2.y, 2)

    return r1 >= rm - delta && r2 >= rm - delta
  }
}
<|endoftext|>


<commit_before>
angular.module('flashpoint')
.directive('firebase', function() {

  /**
   * @ngdoc directive
   * @name firebase
   * @description Wires Firebase into an Angular application.
   *
   * The `firebase` directive is an easy way to make the Firebase controller available
   * to enclosing scope, where it is exposed as `fp`.
   *
   * @example
   * `fp.val` and all succeeding methods take a variable number of path components followed by their
   * necessary arguments (for `fp.set` and `fp.update`, a value; for `fp.setPriority`, a priority; and for
   * `fp.setWithPriority`, a value and a priority). So you could do the following:
   * `Your assigned seat is {{ fp.val('seatAssignments', fp.auth.uid) }}`.
   *
   * `fp.set` and related methods all return a closure {@type function} so that you can
   * easily pass them into a promise chain like so:
   *
   * <example firebase="https://my-firebase.firebaseio.com">
   *   <button ng-disabled='fp.auth === null' ng-click="fp.set('signups', $auth.uid, true)">Sign up!</button>
   * </example>
   *
   *
   * @restrict A
   * @element ANY
   * @scope
   * @param {expression} firebase Full URL to the Firebase, like
   * `https://my-firebase.firebaseio.com`. Interpolatable.
   * @param {expression} challenge Expression to evaluate when fp.challenge() is called
   * somewhere. This expression should evaluate to a promise that resolves on
   */

  var attached, attachedUrl;

  function firebasePreLink(scope, el, attrs, fp) {

    var attachToController = function(url) {

      if (attached && url === attachedUrl) {
        // already attached to this path, no action necessary
        return;
      }

      fp.attachFirebase(url);
      attached = true;
      attachedUrl = url;

    };

    if (attrs.firebase) {
      attachToController(attrs.firebase);
    }
    scope.fp = fp;

    attrs.$observe('firebase', attachToController);

  }

  return {

    restrict: 'A',
    scope: true,
    controller: 'FirebaseCtl',
    priority: 1000,
    link: {
      pre: firebasePreLink
    }

  };

});

<commit_msg>Firebase directive now sets classes for connection and auth status<commit_after>
angular.module('flashpoint')
.directive('firebase', function($animate) {

  /**
   * @ngdoc directive
   * @name firebase
   * @description Wires Firebase into an Angular application.
   *
   * The `firebase` directive is an easy way to make the Firebase controller available
   * to enclosing scope, where it is exposed as `fp`.
   *
   * @example
   * `fp.val` and all succeeding methods take a variable number of path components followed by their
   * necessary arguments (for `fp.set` and `fp.update`, a value; for `fp.setPriority`, a priority; and for
   * `fp.setWithPriority`, a value and a priority). So you could do the following:
   * `Your assigned seat is {{ fp.val('seatAssignments', fp.auth.uid) }}`.
   *
   * `fp.set` and related methods all return a closure {@type function} so that you can
   * easily pass them into a promise chain like so:
   *
   * <example firebase="https://my-firebase.firebaseio.com" firebase-auth="auth">
   *   <button ng-disabled='auth === null' ng-click="fp.set('signups', auth.uid, true)">Sign up!</button>
   * </example>
   *
   *
   * @restrict A
   * @element ANY
   * @scope
   * @param {expression} firebase Full URL to the Firebase, like
   * `https://my-firebase.firebaseio.com`. Interpolatable.
   */

  var attached, attachedUrl;

  function firebasePreLink(scope, el, attrs, fp) {

    var attachToController = function(url) {

      if (attached && url === attachedUrl) {
        // already attached to this path, no action necessary
        return;
      }

      fp.attachFirebase(url);
      attached = true;
      attachedUrl = url;

    };

    if (attrs.firebase) {
      attachToController(attrs.firebase);
    }

    scope.fp = fp;

    attrs.$observe('firebase', attachToController);

    scope.$watch('fp.connected', function(connected) {

      if (connected === true) {
        $animate.setClass(el, 'fp-connected', 'fp-disconnected');
      } else if (connected === false) {
        $animate.setClass(el, 'fp-disconnected', 'fp-connected');
      } else {
        $animate.setClass(el, [], ['fp-connected', 'fp-disconnected']);
      }

    });

    scope.$watch('fp.auth', function(auth) {

      if (auth === undefined) {
        $animate.setClass(el, [], ['fp-unauthenticated', 'fp-authenticated']);
      } else if (auth === null) {
        $animate.setClass(el, 'fp-unauthenticated', 'fp-authenticated');
      } else {
        $animate.setClass(el, 'fp-authenticated', 'fp-unauthenticated');
      }

    });

  }

  return {

    restrict: 'A',
    scope: true,
    controller: 'FirebaseCtl',
    priority: 1000,
    link: {
      pre: firebasePreLink
    }

  };

});

<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.comics.v2',
  name: 'DAOBrowserView',
  extends: 'foam.u2.View',
  requires: [
    'foam.comics.SearchMode',
    'foam.comics.v2.DAOControllerConfig',
    'foam.u2.ActionView',
    'foam.u2.dialog.Popup',
    'foam.u2.layout.Cols',
    'foam.u2.layout.Rows',
    'foam.u2.filter.FilterSearch',
    'foam.u2.view.ScrollTableView',
    'foam.u2.view.SimpleSearch',
    'foam.u2.view.TabChoiceView',
  ],

  implements: [
    'foam.mlang.Expressions'
  ],

  documentation: `
    A scrolling table view customized for the inline DAOController
    with canned queries and a searchbar
  `,

  css: `
    ^export {
      margin-left: 16px;
    }

    ^export img {
      margin-right: 0;
    }

    .foam-u2-ActionView-refreshTable > img {
      margin-right: 0;
    }

    ^top-bar {
      border-bottom: solid 1px #e7eaec;
      align-items: center;
    }

    ^query-bar {
      padding: 24px 16px;
      align-items: center;
      justify-content: flex-end;
    }

    ^toolbar {
      flex-grow: 1;
    }

    ^browse-view-container {
      margin: auto;
      border-bottom: solid 1px #e7eaec;
      margin: 0px 0px 72px 0px;
      box-sizing: border-box;
      padding: 0 16px;
    }

    ^canned-queries {
      padding: 0 16px;
    }

    ^ .foam-u2-view-TableView th {
      background: #ffffff
    }

    ^ .foam-u2-view-TableView td {
      padding-left: 16px;
    }

    ^ .foam-u2-view-SimpleSearch {
      flex-grow: 1;
    }

    ^ .foam-u2-view-SimpleSearch .foam-u2-search-TextSearchView .foam-u2-tag-Input {
      width: 100%;
    }
  `,

  messages: [
    { name: 'REFRESH_MSG', message: 'Refresh Requested ... ' }
  ],

  imports: [
    'stack?'
  ],

  exports: [
    'dblclick',
    'filteredTableColumns'
  ],

  properties: [
    {
      class: 'StringArray',
      name: 'filteredTableColumns'
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'data'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.comics.v2.DAOControllerConfig',
      name: 'config',
      factory: function() {
        return this.DAOControllerConfig.create({ dao: this.data });
      }
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'summaryView',
      factory: function() {
        return {
          class: 'foam.u2.view.ScrollTableView',
          enableDynamicTableHeight: false,
          css: {
            width: '100%',
            height: '424px'
          }
        };
      }
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'cannedQueriesView',
      factory: function() {
        return {
          class: 'foam.u2.view.TabChoiceView'
        };
      }
    },
    {
      class: 'foam.mlang.predicate.PredicateProperty',
      name: 'cannedPredicate',
      expression: function(config$cannedQueries) {
        return config$cannedQueries && config$cannedQueries.length
          ? config$cannedQueries[0].predicate
          : foam.mlang.predicate.True.create();
      }
    },
    {
      class: 'foam.mlang.predicate.PredicateProperty',
      name: 'searchPredicate',
      expression: function() {
        return foam.mlang.predicate.True.create();
      }
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'predicatedDAO',
      expression: function(config, cannedPredicate, searchPredicate) {
        return config.dao$proxy.where(this.AND(cannedPredicate, searchPredicate));
      }
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'searchFilterDAO',
      expression: function(config, cannedPredicate) {
        return config.dao$proxy.where(cannedPredicate);
      }
    }
  ],
  actions: [
    {
      name: 'export',
      label: '',
      toolTip: 'Export Table Data',
      icon: 'images/export-arrow-icon.svg',
      code: function() {
        this.add(this.Popup.create().tag({
          class: 'foam.u2.ExportModal',
          exportData: this.predicatedDAO$proxy,
          predicate: this.config.filterExportPredicate
        }));
      }
    },
    {
      name: 'refreshTable',
      label: '',
      toolTip: 'Refresh Table',
      icon: 'images/refresh-icon-black.svg',
      code: function(X) {
        this.config.dao.cmd_(X, foam.dao.CachingDAO.PURGE);
        this.add(foam.u2.dialog.NotificationMessage.create({
          message: this.REFRESH_MSG
        }));
      }
    }
  ],
  methods: [
    function init() {
      // Reset the search filters when a different canned query is selected
      this.onDetach(this.cannedPredicate$.sub(() => {
        this.searchPredicate = foam.mlang.predicate.True.create();
      }));
    },
    function dblclick(obj) {
      if ( ! this.stack ) return;
      this.stack.push({
        class: 'foam.comics.v2.DAOSummaryView',
        data: obj,
        config: this.config,
        of: this.config.of
      });
    },
    function initE() {
      var self = this;
      this.addClass(this.myClass());
      this.SUPER();
      this
        .add(this.slot(function(data, config$cannedQueries, config$defaultColumns, searchFilterDAO) {
          return self.E()
            .start(self.Rows)
              .callIf(config$cannedQueries.length >= 1, function() {
                this
                  .start(self.Cols)
                    .addClass(self.myClass('top-bar'))
                    .start(self.Cols)
                      .callIf(config$cannedQueries.length > 1, function() {
                        this
                          .start(self.cannedQueriesView, {
                            choices: config$cannedQueries.map((o) => [o.predicate, o.label]),
                            data$: self.cannedPredicate$
                          })
                            .addClass(self.myClass('canned-queries'))
                          .end();
                      })
                    .end()
                  .end();
              })
              .start(self.Cols).addClass(self.myClass('query-bar'))
                .startContext({
                  dao: searchFilterDAO,
                  controllerMode: foam.u2.ControllerMode.EDIT
                })
                  .callIf(self.config.searchMode === self.SearchMode.SIMPLE, function() {
                    this.tag(self.SimpleSearch, {
                      showCount: false,
                      data$: self.searchPredicate$
                    });
                  })
                  .callIf(self.config.searchMode === self.SearchMode.FULL, function() {
                    this.tag(self.FilterSearch, {
                      data$: self.searchPredicate$
                    });
                  })
                .endContext()
                .startContext({ data: self })
                  .start(self.EXPORT, { buttonStyle: 'SECONDARY' })
                    .addClass(self.myClass('export'))
                  .end()
                  .tag(this.REFRESH_TABLE, { buttonStyle: 'SECONDARY' })
                .endContext()
              .end()
              .start(self.summaryView, {
                data: self.predicatedDAO$proxy,
                enableDynamicTableHeight: false,
                columns: config$defaultColumns
              })
                .addClass(self.myClass('browse-view-container'))
              .end()
            .end();
        }));
    }
  ]
});
<commit_msg>Fix refresh button for some tables<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.comics.v2',
  name: 'DAOBrowserView',
  extends: 'foam.u2.View',
  requires: [
    'foam.comics.SearchMode',
    'foam.comics.v2.DAOControllerConfig',
    'foam.u2.ActionView',
    'foam.u2.dialog.Popup',
    'foam.u2.layout.Cols',
    'foam.u2.layout.Rows',
    'foam.u2.filter.FilterSearch',
    'foam.u2.view.ScrollTableView',
    'foam.u2.view.SimpleSearch',
    'foam.u2.view.TabChoiceView',
  ],

  implements: [
    'foam.mlang.Expressions'
  ],

  documentation: `
    A scrolling table view customized for the inline DAOController
    with canned queries and a searchbar
  `,

  css: `
    ^export {
      margin-left: 16px;
    }

    ^export img {
      margin-right: 0;
    }

    .foam-u2-ActionView-refreshTable > img {
      margin-right: 0;
    }

    ^top-bar {
      border-bottom: solid 1px #e7eaec;
      align-items: center;
    }

    ^query-bar {
      padding: 24px 16px;
      align-items: center;
      justify-content: flex-end;
    }

    ^toolbar {
      flex-grow: 1;
    }

    ^browse-view-container {
      margin: auto;
      border-bottom: solid 1px #e7eaec;
      margin: 0px 0px 72px 0px;
      box-sizing: border-box;
      padding: 0 16px;
    }

    ^canned-queries {
      padding: 0 16px;
    }

    ^ .foam-u2-view-TableView th {
      background: #ffffff
    }

    ^ .foam-u2-view-TableView td {
      padding-left: 16px;
    }

    ^ .foam-u2-view-SimpleSearch {
      flex-grow: 1;
    }

    ^ .foam-u2-view-SimpleSearch .foam-u2-search-TextSearchView .foam-u2-tag-Input {
      width: 100%;
    }
  `,

  messages: [
    { name: 'REFRESH_MSG', message: 'Refresh Requested ... ' }
  ],

  imports: [
    'stack?'
  ],

  exports: [
    'dblclick',
    'filteredTableColumns'
  ],

  properties: [
    {
      class: 'StringArray',
      name: 'filteredTableColumns'
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'data'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.comics.v2.DAOControllerConfig',
      name: 'config',
      factory: function() {
        return this.DAOControllerConfig.create({ dao: this.data });
      }
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'summaryView',
      factory: function() {
        return {
          class: 'foam.u2.view.ScrollTableView',
          enableDynamicTableHeight: false,
          css: {
            width: '100%',
            height: '424px'
          }
        };
      }
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'cannedQueriesView',
      factory: function() {
        return {
          class: 'foam.u2.view.TabChoiceView'
        };
      }
    },
    {
      class: 'foam.mlang.predicate.PredicateProperty',
      name: 'cannedPredicate',
      expression: function(config$cannedQueries) {
        return config$cannedQueries && config$cannedQueries.length
          ? config$cannedQueries[0].predicate
          : foam.mlang.predicate.True.create();
      }
    },
    {
      class: 'foam.mlang.predicate.PredicateProperty',
      name: 'searchPredicate',
      expression: function() {
        return foam.mlang.predicate.True.create();
      }
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'predicatedDAO',
      expression: function(config, cannedPredicate, searchPredicate) {
        return config.dao$proxy.where(this.AND(cannedPredicate, searchPredicate));
      }
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'searchFilterDAO',
      expression: function(config, cannedPredicate) {
        return config.dao$proxy.where(cannedPredicate);
      }
    }
  ],
  actions: [
    {
      name: 'export',
      label: '',
      toolTip: 'Export Table Data',
      icon: 'images/export-arrow-icon.svg',
      code: function() {
        this.add(this.Popup.create().tag({
          class: 'foam.u2.ExportModal',
          exportData: this.predicatedDAO$proxy,
          predicate: this.config.filterExportPredicate
        }));
      }
    },
    {
      name: 'refreshTable',
      label: '',
      toolTip: 'Refresh Table',
      icon: 'images/refresh-icon-black.svg',
      code: function(X) {
        this.config.dao.cmd_(X, foam.dao.CachingDAO.PURGE);
        this.config.dao.cmd_(X, foam.dao.AbstractDAO.RESET_CMD);
        this.add(foam.u2.dialog.NotificationMessage.create({
          message: this.REFRESH_MSG
        }));
      }
    }
  ],
  methods: [
    function init() {
      // Reset the search filters when a different canned query is selected
      this.onDetach(this.cannedPredicate$.sub(() => {
        this.searchPredicate = foam.mlang.predicate.True.create();
      }));
    },
    function dblclick(obj) {
      if ( ! this.stack ) return;
      this.stack.push({
        class: 'foam.comics.v2.DAOSummaryView',
        data: obj,
        config: this.config,
        of: this.config.of
      });
    },
    function initE() {
      var self = this;
      this.addClass(this.myClass());
      this.SUPER();
      this
        .add(this.slot(function(data, config$cannedQueries, config$defaultColumns, searchFilterDAO) {
          return self.E()
            .start(self.Rows)
              .callIf(config$cannedQueries.length >= 1, function() {
                this
                  .start(self.Cols)
                    .addClass(self.myClass('top-bar'))
                    .start(self.Cols)
                      .callIf(config$cannedQueries.length > 1, function() {
                        this
                          .start(self.cannedQueriesView, {
                            choices: config$cannedQueries.map((o) => [o.predicate, o.label]),
                            data$: self.cannedPredicate$
                          })
                            .addClass(self.myClass('canned-queries'))
                          .end();
                      })
                    .end()
                  .end();
              })
              .start(self.Cols).addClass(self.myClass('query-bar'))
                .startContext({
                  dao: searchFilterDAO,
                  controllerMode: foam.u2.ControllerMode.EDIT
                })
                  .callIf(self.config.searchMode === self.SearchMode.SIMPLE, function() {
                    this.tag(self.SimpleSearch, {
                      showCount: false,
                      data$: self.searchPredicate$
                    });
                  })
                  .callIf(self.config.searchMode === self.SearchMode.FULL, function() {
                    this.tag(self.FilterSearch, {
                      data$: self.searchPredicate$
                    });
                  })
                .endContext()
                .startContext({ data: self })
                  .start(self.EXPORT, { buttonStyle: 'SECONDARY' })
                    .addClass(self.myClass('export'))
                  .end()
                  .tag(this.REFRESH_TABLE, { buttonStyle: 'SECONDARY' })
                .endContext()
              .end()
              .start(self.summaryView, {
                data: self.predicatedDAO$proxy,
                enableDynamicTableHeight: false,
                columns: config$defaultColumns
              })
                .addClass(self.myClass('browse-view-container'))
              .end()
            .end();
        }));
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.comics.v2',
  name: 'DAOSummaryView',
  extends: 'foam.u2.View',

  documentation: 'A configurable summary view for a specific instance',

  topics: [
    'finished',
    'throwError'
  ],

  axioms: [
    foam.pattern.Faceted.create()
  ],

  css: `
    ^ {
      padding: 32px
    }

    ^ .foam-u2-ActionView-back {
      display: flex;
      align-items: center;
      width: 30%;
    }

    ^account-name {
      font-size: 36px;
      font-weight: 600;
    }

    ^actions-header .foam-u2-ActionView {
      margin-right: 24px;
      line-height: 1.5
    }

    ^view-container {
      margin: auto;
    }
  `,

  requires: [
    'foam.u2.layout.Cols',
    'foam.u2.layout.Rows',
    'foam.u2.ControllerMode',
    'foam.u2.dialog.NotificationMessage',
    'foam.u2.dialog.Popup',
  ],

  imports: [
    'auth',
    'stack'
  ],

  exports: [
    'controllerMode'
  ],

  properties: [
    {
      class: 'FObjectProperty',
      name: 'data'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.comics.v2.DAOControllerConfig',
      name: 'config'
    },
    {
      name: 'controllerMode',
      factory: function() {
        return this.ControllerMode.VIEW;
      }
    },
    {
      name: 'primary',
      expression: function(config$of) {
        var allActions = config$of.getAxiomsByClass(foam.core.Action);
        var defaultAction = allActions.filter((a) => a.isDefault);
        return defaultAction.length >= 1
          ? defaultAction[0]
          : allActions.length >= 1
            ? allActions[0]
            : null;
      }
    },
    {
      class: 'foam.u2.ViewSpecWithJava',
      name: 'viewView',
      expression: function() {
        return foam.u2.detail.SectionedDetailView;
      }
    },
    {
      class: 'String',
      name: 'backLabel',
      expression: function(config$browseTitle) {
        return 'All ' + config$browseTitle;
      }
    }
  ],

  actions: [
    {
      name: 'edit',
      isEnabled: function(config, data) {
        if ( config.CRUDEnabledActionsAuth && config.CRUDEnabledActionsAuth.isEnabled ) {
          try {
            let permissionString = config.CRUDEnabledActionsAuth.enabledActionsAuth.permissionFactory(foam.nanos.ruler.Operations.UPDATE, data);

            return this.auth.check(null, permissionString);
          } catch(e) {
            return false;
          }
        }
        return true;
      },
      isAvailable: function(config) {
        try {
          return config.editPredicate.f();
        } catch(e) {
          return false;
        }
      },
      code: function() {
        if ( ! this.stack ) return;
        this.stack.push({
          class: 'foam.comics.v2.DAOUpdateView',
          data: this.data,
          config: this.config,
          of: this.config.of
        }, this.__subContext__);
      }
    },
    {
      name: 'delete',
      isEnabled: function(config, data) {
        if ( config.CRUDEnabledActionsAuth && config.CRUDEnabledActionsAuth.isEnabled ) {
          try {
            let permissionString = config.CRUDEnabledActionsAuth.enabledActionsAuth.permissionFactory(foam.nanos.ruler.Operations.REMOVE, data);

            return this.auth.check(null, permissionString);
          } catch(e) {
            return false;
          }
        }
        return true;
      },
      isAvailable: function(config) {
        try {
          return config.deletePredicate.f();
        } catch(e) {
          return false;
        }
      },
      code: function() {
        this.add(this.Popup.create({ backgroundColor: 'transparent' }).tag({
          class: 'foam.u2.DeleteModal',
          dao: this.config.dao,
          onDelete: () => {
            this.finished.pub();
            this.stack.back();
          },
          data: this.data
        }));
      }
    }
  ],

  methods: [
    function initE() {
      var self = this;
      this.SUPER();

      // Get a fresh copy of the data, especially when we've been returned
      // to this view from the edit view on the stack.
      this.config.dao.inX(this.__subContext__).find(this.data).then(d => {
        if ( d ) self.data = d;

        this
          .addClass(this.myClass())
          .add(self.slot(function(data, config$viewBorder, viewView) {
            return self.E()
              .start(self.Rows)
                .start(self.Rows)
                  // we will handle this in the StackView instead
                  .startContext({ data: self.stack })
                    .tag(self.stack.BACK, {
                      buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                      icon: 'images/back-icon.svg',
                      label: self.backLabel
                    })
                  .endContext()
                  .start(self.Cols).style({ 'align-items': 'center' })
                    .start()
                      .add(data.toSummary())
                      .addClass(this.myClass('account-name'))
                      .addClass('truncate-ellipsis')
                    .end()
                    .startContext({ data }).add(self.primary).endContext()
                  .end()
                .end()

                .start(self.Cols)
                  .start(self.Cols).addClass(this.myClass('actions-header'))
                    .startContext({ data: self })
                      .tag(self.EDIT, {
                        buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                        icon: 'images/edit-icon.svg'
                      })
                      .tag(self.DELETE, {
                        buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                        icon: 'images/delete-icon.svg'
                      })
                    .endContext()
                  .end()
                .end()

                .start(config$viewBorder)
                  .start(viewView, { data }).addClass(this.myClass('view-container')).end()
                .end()
              .end();
          }));
      });
    }
  ]
});
<commit_msg>Working on creating a copy action<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.comics.v2',
  name: 'DAOSummaryView',
  extends: 'foam.u2.View',

  documentation: 'A configurable summary view for a specific instance',

  topics: [
    'finished',
    'throwError'
  ],

  axioms: [
    foam.pattern.Faceted.create()
  ],

  css: `
    ^ {
      padding: 32px
    }

    ^ .foam-u2-ActionView-back {
      display: flex;
      align-items: center;
      width: 30%;
    }

    ^account-name {
      font-size: 36px;
      font-weight: 600;
    }

    ^actions-header .foam-u2-ActionView {
      margin-right: 24px;
      line-height: 1.5
    }

    ^view-container {
      margin: auto;
    }
  `,

  requires: [
    'foam.u2.layout.Cols',
    'foam.u2.layout.Rows',
    'foam.u2.ControllerMode',
    'foam.u2.dialog.NotificationMessage',
    'foam.u2.dialog.Popup',
  ],

  imports: [
    'auth',
    'stack'
  ],

  exports: [
    'controllerMode'
  ],

  properties: [
    {
      class: 'FObjectProperty',
      name: 'data'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.comics.v2.DAOControllerConfig',
      name: 'config'
    },
    {
      name: 'controllerMode',
      factory: function() {
        return this.ControllerMode.VIEW;
      }
    },
    {
      name: 'primary',
      expression: function(config$of) {
        var allActions = config$of.getAxiomsByClass(foam.core.Action);
        var defaultAction = allActions.filter((a) => a.isDefault);
        return defaultAction.length >= 1
          ? defaultAction[0]
          : allActions.length >= 1
            ? allActions[0]
            : null;
      }
    },
    {
      class: 'foam.u2.ViewSpecWithJava',
      name: 'viewView',
      expression: function() {
        return foam.u2.detail.SectionedDetailView;
      }
    },
    {
      class: 'String',
      name: 'backLabel',
      expression: function(config$browseTitle) {
        return 'All ' + config$browseTitle;
      }
    }
  ],

  actions: [
    {
      name: 'edit',
      isEnabled: function(config, data) {
        if ( config.CRUDEnabledActionsAuth && config.CRUDEnabledActionsAuth.isEnabled ) {
          try {
            let permissionString = config.CRUDEnabledActionsAuth.enabledActionsAuth.permissionFactory(foam.nanos.ruler.Operations.UPDATE, data);

            return this.auth.check(null, permissionString);
          } catch(e) {
            return false;
          }
        }
        return true;
      },
      isAvailable: function(config) {
        try {
          return config.editPredicate.f();
        } catch(e) {
          return false;
        }
      },
      code: function() {
        if ( ! this.stack ) return;
        this.stack.push({
          class: 'foam.comics.v2.DAOUpdateView',
          data: this.data,
          config: this.config,
          of: this.config.of
        }, this.__subContext__);
      }
    },
    {
      name: 'create',
      isEnabled: function(config, data) {
        if ( config.CRUDEnabledActionsAuth && config.CRUDEnabledActionsAuth.isEnabled ) {
          try {
            let permissionString = config.CRUDEnabledActionsAuth.enabledActionsAuth.permissionFactory(foam.nanos.ruler.Operations.CREATE, data);

            return this.auth.check(null, permissionString);
          } catch(e) {
            return false;
          }
        }
        return true;
      },
      isAvailable: function(config) {
        try {
          return config.createPredicate.f();
        } catch(e) {
          return false;
        }
      },
      code: function() {
        if ( ! this.stack ) return;
        this.stack.push({
          class: 'foam.comics.v2.DAOUpdateView',
          data: this.data,
          config: this.config,
          of: this.config.of
        }, this.__subContext__);
      }
    },
    {
      name: 'delete',
      isEnabled: function(config, data) {
        if ( config.CRUDEnabledActionsAuth && config.CRUDEnabledActionsAuth.isEnabled ) {
          try {
            let permissionString = config.CRUDEnabledActionsAuth.enabledActionsAuth.permissionFactory(foam.nanos.ruler.Operations.REMOVE, data);

            return this.auth.check(null, permissionString);
          } catch(e) {
            return false;
          }
        }
        return true;
      },
      isAvailable: function(config) {
        try {
          return config.deletePredicate.f();
        } catch(e) {
          return false;
        }
      },
      code: function() {
        this.add(this.Popup.create({ backgroundColor: 'transparent' }).tag({
          class: 'foam.u2.DeleteModal',
          dao: this.config.dao,
          onDelete: () => {
            this.finished.pub();
            this.stack.back();
          },
          data: this.data
        }));
      }
    }
  ],

  methods: [
    function initE() {
      var self = this;
      this.SUPER();

      // Get a fresh copy of the data, especially when we've been returned
      // to this view from the edit view on the stack.
      this.config.dao.inX(this.__subContext__).find(this.data).then(d => {
        if ( d ) self.data = d;

        this
          .addClass(this.myClass())
          .add(self.slot(function(data, config$viewBorder, viewView) {
            return self.E()
              .start(self.Rows)
                .start(self.Rows)
                  // we will handle this in the StackView instead
                  .startContext({ data: self.stack })
                    .tag(self.stack.BACK, {
                      buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                      icon: 'images/back-icon.svg',
                      label: self.backLabel
                    })
                  .endContext()
                  .start(self.Cols).style({ 'align-items': 'center' })
                    .start()
                      .add(data.toSummary())
                      .addClass(this.myClass('account-name'))
                      .addClass('truncate-ellipsis')
                    .end()
                    .startContext({ data }).add(self.primary).endContext()
                  .end()
                .end()

                .start(self.Cols)
                  .start(self.Cols).addClass(this.myClass('actions-header'))
                    .startContext({ data: self })
                      .tag(self.EDIT, {
                        buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                        icon: 'images/edit-icon.svg'
                      })
                      .tag(self.CREATE, {
                        buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                        icon: 'images/edit-icon.svg'
                      })
                      .tag(self.DELETE, {
                        buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                        icon: 'images/delete-icon.svg'
                      })
                    .endContext()
                  .end()
                .end()

                .start(config$viewBorder)
                  .start(viewView, { data }).addClass(this.myClass('view-container')).end()
                .end()
              .end();
          }));
      });
    }
  ]
});
<|endoftext|>


<commit_before>foam.CLASS({
  package: 'foam.dashboard.view',
  name: 'Dashboard',
  extends: 'foam.u2.Element',
  imports: [
    'setInterval'
  ],
  exports: [
    'as dashboardController'
  ],
  requires: [
    'foam.dashboard.model.Count',
    'foam.dashboard.model.GroupBy',
    'foam.dashboard.model.Table',
    'foam.dashboard.model.VisualizationSize'
  ],
  properties: [
    [ 'nodeName', 'div' ],
  ],
  css: `
^ {
  display: flex;
  flex-wrap: wrap;
  align-items: start;
  justify-content: center;
}
`,
  methods: [
    function initE() {
      var timeout = -1;
      //      var timeout = this.setInterval(this.onUpdate, 5000);
      var view = this;

      this.onDetach(function() {
        this.clearInterval(timeout);
      }.bind(this));

      this.
        addClass(this.myClass());
    }
  ],
  listeners: [
    function onUpdate() {
      this.pub('dashboard', 'update');
    }
  ]
});
<commit_msg>Re-enable dashboard update events.<commit_after>foam.CLASS({
  package: 'foam.dashboard.view',
  name: 'Dashboard',
  extends: 'foam.u2.Element',
  imports: [
    'setInterval'
  ],
  exports: [
    'as dashboardController'
  ],
  requires: [
    'foam.dashboard.model.Count',
    'foam.dashboard.model.GroupBy',
    'foam.dashboard.model.Table',
    'foam.dashboard.model.VisualizationSize'
  ],
  properties: [
    [ 'nodeName', 'div' ],
  ],
  css: `
^ {
  display: flex;
  flex-wrap: wrap;
  align-items: start;
  justify-content: center;
}
`,
  methods: [
    function initE() {
      var timeout = this.setInterval(this.onUpdate, 5000);
      var view = this;

      this.onDetach(function() {
        this.clearInterval(timeout);
      }.bind(this));

      this.
        addClass(this.myClass());
    }
  ],
  listeners: [
    function onUpdate() {
      this.pub('dashboard', 'update');
    }
  ]
});
<|endoftext|>


<commit_before>
var convertValueType = require('./convertValueType');

function callModelAsFunction(view, model, modelArgs)
{
	var args = [];
	if(modelArgs instanceof Array)
	{
		for(i = 0, l = modelArgs.length; i < l; i++)
		{
			if(modelArgs[i] instanceof Array) args[i] = view.getCursor(modelArgs[i]);
			else args[i] = convertValueType(modelArgs[i]);
		}
	}
	return model.apply(null, args);
}

module.exports = callModelAsFunction;
<commit_msg>fix(callModelAsFunction): missing var keyword<commit_after>
var convertValueType = require('./convertValueType');

function callModelAsFunction(view, model, modelArgs)
{
	var args = [];
	if(modelArgs instanceof Array)
	{
		for(var i = 0, l = modelArgs.length; i < l; i++)
		{
			if(modelArgs[i] instanceof Array) args[i] = view.getCursor(modelArgs[i]);
			else args[i] = convertValueType(modelArgs[i]);
		}
	}
	return model.apply(null, args);
}

module.exports = callModelAsFunction;
<|endoftext|>