<commit_before>  /*----------------------------- ELEMENT: STYLE -----------------------------*/

  (function() {
    this.classNames = function classNames(element) {
      var results = Fuse.String($(element).className).split(/\s+/);
      return results[0].length ? results : Fuse.List();
    };

    this.hasClassName = function hasClassName(element, className) {
      element = $(element);
      var elementClassName = element.className;
      return (elementClassName.length > 0 && (elementClassName === className ||
        (' ' + elementClassName + ' ').indexOf(' ' + className + ' ') > -1));
    };

    this.addClassName = function addClassName(element, className) {
      element = $(element);
      if (!Element.hasClassName(element, className))
        element.className += (element.className ? ' ' : '') + className;
      return element;
    };

    this.removeClassName = function removeClassName(element, className) {
      element = $(element);
      element.className = Fuse.String(element.className.replace(
        new RegExp('(^|\\s+)' + className + '(\\s+|$)'), ' ')).trim();
      return element;
    };

    this.toggleClassName = function toggleClassName(element, className) {
      return Element[Element.hasClassName(element, className) ?
        'removeClassName' : 'addClassName'](element, className);
    };

    this.getOpacity = (function() {
      var getOpacity = function getOpacity(element) {
        return Fuse.Number(parseFloat($(element).style.opacity));
      };

      if (Feature('ELEMENT_COMPUTED_STYLE')) {
        getOpacity = function getOpacity(element) {
          element = $(element);
          var style = element.ownerDocument.defaultView.getComputedStyle(element, null);
          return Fuse.Number(
            parseFloat(style ? style.opacity : element.style.opacity));
        };
      }
      else if (Feature('ELEMENT_MS_CSS_FILTERS')) {
        getOpacity = function getOpacity(element) {
          element = $(element);
          var s = element.currentStyle || element.style,
            value = s['filter'].match(/alpha\(opacity=(.*)\)/);
          return Fuse.Number(value && value[1] ? parseFloat(value[1]) / 100 : 1.0);
        };
      }
      return getOpacity;
    })();

    this.setOpacity = (function() {
      var setOpacity = function setOpacity(element, value) {
        element = $(element);
        element.style.opacity = (value == 1 || value == '' && Fuse.Object.isString(value)) ? '' :
          (value < 0.00001) ? '0' : value;
        return element;
      };

      if (Fuse.Browser.Agent.WebKit && (userAgent.match(/AppleWebKit\/(\d+)/) || [])[1] < 500) {
        var _setOpacity = setOpacity;
        setOpacity = function setOpacity(element, value) {
          element = _setOpacity(element, value);
          // TODO: Is this really needed or the best approach ?
          if (value == 1) {
            if (getNodeName(element) === 'IMG' && element.width) {
              element.width++; element.width--;
            } else try {
              var n = element.ownerDocument.createTextNode(' ');
              element.removeChild(element.appendChild(n));
            } catch (e) { }
          }
          return element;
        };
      }
      else if (Fuse.Browser.Agent.Gecko && /rv:1\.8\.0/.test(userAgent)) {
        setOpacity = function setOpacity(element, value) {
          element = $(element);
          element.style.opacity = (value == 1) ? 0.999999 :
            (value == '' && Fuse.Object.isString(value)) ? '' :
              (value < 0.00001) ? 0 : value;
          return element;
        };
      }
      else if (Feature('ELEMENT_MS_CSS_FILTERS')) {
        setOpacity = function setOpacity(element, value) {
          element = $(element);
          if (!Element._hasLayout(element))
            element.style.zoom = 1;

          var style = element.style,
           filter = Element.getStyle(element, 'filter');

          // strip alpha
          filter = filter.replace(/alpha\([^)]*\)/gi, '');

          if (value == 1 || value == '' && Fuse.Object.isString(value)) {
            if (filter) style.filter = filter;
            else style.removeAttribute('filter');
            return element;
          }
          else if (value < 0.00001) value = 0;

          style.filter = filter + 'alpha(opacity=' + (value * 100) + ')';
          return element;
        };
      }
      return setOpacity;
    })();

    // prevent JScript bug with named function expressions
    var addClassName = null,
     hasClassName =    null,
     removeClassName = null,
     classNames =      null,
     toggleClassName = null,
     getOpacity =      null,
     setOpacity =      null;
  }).call(Element.Methods);

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

  (function() {
    var DIMENSION_NAMES = { 'height': 1, 'width': 1 };

    var FLOAT_TRANSLATIONS = typeof Fuse._docEl.style.styleFloat !== 'undefined'
     ? { 'float': 'styleFloat', 'cssFloat': 'styleFloat' }
     : { 'float': 'cssFloat' };

    this.setStyle = function setStyle(element, styles) {
      element = $(element);
      var key, name, elementStyle = element.style;
      if (Fuse.Object.isString(styles)) {
        element.style.cssText += ';' + styles;
        return styles.indexOf('opacity') > -1
          ? Element.setOpacity(element, styles.match(/opacity:\s*(\d?\.?\d*)/)[1])
          : element;
      }
      for (key in styles) {
        name = FLOAT_TRANSLATIONS[key] || key;
        if (name === 'opacity') Element.setOpacity(element, styles[key]);
        else elementStyle[name] = styles[key];
      }
      return element;
    };

    this.getStyle =
      Feature('ELEMENT_COMPUTED_STYLE') || !Feature('ELEMENT_CURRENT_STYLE') ?
      (function() {
        function _getComputedStyle(element, name) {
          name = FLOAT_TRANSLATIONS[name] || name;
          var css = element.ownerDocument.defaultView.getComputedStyle(element, null);
          if (css) return _getResult(name, css[name]);
          return _getStyleValue(element, name);
        }

        function _getStyleValue(element, name) {
          name = FLOAT_TRANSLATIONS[name] || name;
          return _getResult(name, element.style[name]);
        }

        function _getResult(name, value) {
          if (name == 'opacity')
            return Fuse.String(value === '1' ? '1.0' : parseFloat(value) || '0');
          return value === 'auto' || value === '' ? null : Fuse.String(value);
        }

        function _isNull(element, name) {
          var length = _isNull.handlers.length;
          while (length--) {
            if (_isNull.handlers[length](element, name))
            return true;
          }
          return false;
        }

        _isNull.handlers = [];

        if (Bug('ELEMENT_COMPUTED_STYLE_DEFAULTS_TO_ZERO')) {
          _isNull.handlers.push((function() {
            var POSITION_NAMES = { 'bottom': 1, 'left': 1, 'right': 1, 'top': 1 };
            return function(element, name) {
              return POSITION_NAMES[name] &&
                _getComputedStyle(element, 'position') === 'static';
            };
          })());
        }
        if (Bug('ELEMENT_COMPUTED_STYLE_HEIGHT_IS_ZERO_WHEN_HIDDEN')) {
          _isNull.handlers.push(function(element, name) {
            return DIMENSION_NAMES[name] && element.style.display === 'none';
          });
        }

        if (!Feature('ELEMENT_COMPUTED_STYLE')) {
          var getStyle = function getStyle(element, name) {
            return _getStyleValue(element, name);
          };
        }
        else if (Bug('ELEMENT_COMPUTED_STYLE_DIMENSIONS_EQUAL_BORDER_BOX')) {
          // Opera 9.2x
          var getStyle = function getStyle(element, name) {
            element = $(element);
            name = Fuse.String(name).camelize();
            if (_isNull(element, name)) return null;
            var value = _getComputedStyle(element, name);

            // returns the border-box dimensions rather than the content-box
            // dimensions, so we subtract padding and borders from the value
            if (DIMENSION_NAMES[name]) {
              var D = name.capitalize(), dim = parseFloat(value) || 0;
              if (dim !== element['offset' + D]) return Fuse.String(dim + 'px');
              return Fuse.String(Element['_getCss' + D](element) + 'px');
            }
            return Fuse.String(value);
          };
        }
        else { // Firefox, Safari, Opera 9.5+
          var getStyle = function getStyle(element, name) {
            element = $(element);
            name = Fuse.String(name).camelize();
              return _isNull(element, name) ? null :
              Fuse.String(_getComputedStyle(element, name));
          };
        }
        return getStyle;
      })() :

      // IE
      (function() {
        var RELATIVE_CSS_UNITS = { 'em': 1, 'ex': 1 };

        // We need to insert into element a span with the M character in it.
        // The element.offsetHeight will give us the font size in px units.
        // Inspired by Google Doctype:
        // http://code.google.com/p/doctype/source/browse/trunk/goog/style/style.js#1146
        var span = Fuse._doc.createElement('span');
        span.style.cssText = 'position:absolute;visibility:hidden;height:1em;lineHeight:0;padding:0;margin:0;border:0;';
        span.innerHTML = 'M';

        function getStyle(element, name) {
          var value;
          if (name == 'opacity') {
            value = String(Element.getOpacity(element));
            if (value.indexOf('.') < 0) value += '.0';
            return Fuse.String(value);
          }

          element = $(element);
          name = Fuse.String(name).camelize();
          name = FLOAT_TRANSLATIONS[name] || name;

          // get cascaded style
          var s = element.currentStyle || element.style;
          value = s[name];
          if (value === 'auto') {
            if (DIMENSION_NAMES[name] && s.display !== 'none')
              return Fuse.String(element['offset' + name.capitalize()] + 'px');
            return null;
          }

          // If the unit is something other than a pixel (em, pt, %),
          // set it on something we can grab a pixel value from.
          // Inspired by Dean Edwards' comment
          // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
          if (/^\d+(\.\d+)?(?!px)[%a-z]+$/i.test(value)) {
            if (name == 'fontSize') {
              var unit = value.match(/\D+$/)[0];
              if (unit === '%') {
                var size = element.appendChild(span).offsetHeight;
                element.removeChild(span);
                return Math.round(size) + 'px';
              }
              else if (RELATIVE_CSS_UNITS[unit])
                element = element.parentNode;
            }

            // backup values
            var pos = Fuse.String(name == 'height' ? 'top' : 'left'),
             stylePos = element.style[pos], runtimePos = element.runtimeStyle[pos];

            // set runtimeStyle so no visible shift is seen
            element.runtimeStyle[pos] = s[pos];
            element.style[pos] = value;
            value = element.style['pixel' + pos.capitalize()] + 'px';

            // revert changes
            element.style[pos] = stylePos;
            element.runtimeStyle[pos] = runtimePos;
          }
          return Fuse.String(value);
        }
        return getStyle;
      })();

    // prevent JScript bug with named function expressions
    var setStyle = null;
  }).call(Element.Methods);
<commit_msg>fusejs: Ensure Element#getStyle returns null and not "null" when appropriate. [jddalton]<commit_after>  /*----------------------------- ELEMENT: STYLE -----------------------------*/

  (function() {
    this.classNames = function classNames(element) {
      var results = Fuse.String($(element).className).split(/\s+/);
      return results[0].length ? results : Fuse.List();
    };

    this.hasClassName = function hasClassName(element, className) {
      element = $(element);
      var elementClassName = element.className;
      return (elementClassName.length > 0 && (elementClassName === className ||
        (' ' + elementClassName + ' ').indexOf(' ' + className + ' ') > -1));
    };

    this.addClassName = function addClassName(element, className) {
      element = $(element);
      if (!Element.hasClassName(element, className))
        element.className += (element.className ? ' ' : '') + className;
      return element;
    };

    this.removeClassName = function removeClassName(element, className) {
      element = $(element);
      element.className = Fuse.String(element.className.replace(
        new RegExp('(^|\\s+)' + className + '(\\s+|$)'), ' ')).trim();
      return element;
    };

    this.toggleClassName = function toggleClassName(element, className) {
      return Element[Element.hasClassName(element, className) ?
        'removeClassName' : 'addClassName'](element, className);
    };

    this.getOpacity = (function() {
      var getOpacity = function getOpacity(element) {
        return Fuse.Number(parseFloat($(element).style.opacity));
      };

      if (Feature('ELEMENT_COMPUTED_STYLE')) {
        getOpacity = function getOpacity(element) {
          element = $(element);
          var style = element.ownerDocument.defaultView.getComputedStyle(element, null);
          return Fuse.Number(
            parseFloat(style ? style.opacity : element.style.opacity));
        };
      }
      else if (Feature('ELEMENT_MS_CSS_FILTERS')) {
        getOpacity = function getOpacity(element) {
          element = $(element);
          var s = element.currentStyle || element.style,
            value = s['filter'].match(/alpha\(opacity=(.*)\)/);
          return Fuse.Number(value && value[1] ? parseFloat(value[1]) / 100 : 1.0);
        };
      }
      return getOpacity;
    })();

    this.setOpacity = (function() {
      var setOpacity = function setOpacity(element, value) {
        element = $(element);
        element.style.opacity = (value == 1 || value == '' && Fuse.Object.isString(value)) ? '' :
          (value < 0.00001) ? '0' : value;
        return element;
      };

      if (Fuse.Browser.Agent.WebKit && (userAgent.match(/AppleWebKit\/(\d+)/) || [])[1] < 500) {
        var _setOpacity = setOpacity;
        setOpacity = function setOpacity(element, value) {
          element = _setOpacity(element, value);
          // TODO: Is this really needed or the best approach ?
          if (value == 1) {
            if (getNodeName(element) === 'IMG' && element.width) {
              element.width++; element.width--;
            } else try {
              var n = element.ownerDocument.createTextNode(' ');
              element.removeChild(element.appendChild(n));
            } catch (e) { }
          }
          return element;
        };
      }
      else if (Fuse.Browser.Agent.Gecko && /rv:1\.8\.0/.test(userAgent)) {
        setOpacity = function setOpacity(element, value) {
          element = $(element);
          element.style.opacity = (value == 1) ? 0.999999 :
            (value == '' && Fuse.Object.isString(value)) ? '' :
              (value < 0.00001) ? 0 : value;
          return element;
        };
      }
      else if (Feature('ELEMENT_MS_CSS_FILTERS')) {
        setOpacity = function setOpacity(element, value) {
          element = $(element);
          if (!Element._hasLayout(element))
            element.style.zoom = 1;

          var style = element.style,
           filter = Element.getStyle(element, 'filter');

          // strip alpha
          filter = filter.replace(/alpha\([^)]*\)/gi, '');

          if (value == 1 || value == '' && Fuse.Object.isString(value)) {
            if (filter) style.filter = filter;
            else style.removeAttribute('filter');
            return element;
          }
          else if (value < 0.00001) value = 0;

          style.filter = filter + 'alpha(opacity=' + (value * 100) + ')';
          return element;
        };
      }
      return setOpacity;
    })();

    // prevent JScript bug with named function expressions
    var addClassName = null,
     hasClassName =    null,
     removeClassName = null,
     classNames =      null,
     toggleClassName = null,
     getOpacity =      null,
     setOpacity =      null;
  }).call(Element.Methods);

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

  (function() {
    var DIMENSION_NAMES = { 'height': 1, 'width': 1 };

    var FLOAT_TRANSLATIONS = typeof Fuse._docEl.style.styleFloat !== 'undefined'
     ? { 'float': 'styleFloat', 'cssFloat': 'styleFloat' }
     : { 'float': 'cssFloat' };

    this.setStyle = function setStyle(element, styles) {
      element = $(element);
      var key, name, elementStyle = element.style;
      if (Fuse.Object.isString(styles)) {
        element.style.cssText += ';' + styles;
        return styles.indexOf('opacity') > -1
          ? Element.setOpacity(element, styles.match(/opacity:\s*(\d?\.?\d*)/)[1])
          : element;
      }
      for (key in styles) {
        name = FLOAT_TRANSLATIONS[key] || key;
        if (name === 'opacity') Element.setOpacity(element, styles[key]);
        else elementStyle[name] = styles[key];
      }
      return element;
    };

    this.getStyle =
      Feature('ELEMENT_COMPUTED_STYLE') || !Feature('ELEMENT_CURRENT_STYLE') ?
      (function() {
        function _getComputedStyle(element, name) {
          name = FLOAT_TRANSLATIONS[name] || name;
          var css = element.ownerDocument.defaultView.getComputedStyle(element, null);
          if (css) return _getResult(name, css[name]);
          return _getStyleValue(element, name);
        }

        function _getStyleValue(element, name) {
          name = FLOAT_TRANSLATIONS[name] || name;
          return _getResult(name, element.style[name]);
        }

        function _getResult(name, value) {
          if (name == 'opacity')
            return Fuse.String(value === '1' ? '1.0' : parseFloat(value) || '0');
          return value === 'auto' || value === '' ? null : Fuse.String(value);
        }

        function _isNull(element, name) {
          var length = _isNull.handlers.length;
          while (length--) {
            if (_isNull.handlers[length](element, name))
            return true;
          }
          return false;
        }

        _isNull.handlers = [];

        if (Bug('ELEMENT_COMPUTED_STYLE_DEFAULTS_TO_ZERO')) {
          _isNull.handlers.push((function() {
            var POSITION_NAMES = { 'bottom': 1, 'left': 1, 'right': 1, 'top': 1 };
            return function(element, name) {
              return POSITION_NAMES[name] &&
                _getComputedStyle(element, 'position') === 'static';
            };
          })());
        }
        if (Bug('ELEMENT_COMPUTED_STYLE_HEIGHT_IS_ZERO_WHEN_HIDDEN')) {
          _isNull.handlers.push(function(element, name) {
            return DIMENSION_NAMES[name] && element.style.display === 'none';
          });
        }

        if (!Feature('ELEMENT_COMPUTED_STYLE')) {
          var getStyle = function getStyle(element, name) {
            return _getStyleValue(element, name);
          };
        }
        else if (Bug('ELEMENT_COMPUTED_STYLE_DIMENSIONS_EQUAL_BORDER_BOX')) {
          // Opera 9.2x
          var getStyle = function getStyle(element, name) {
            element = $(element);
            name = Fuse.String(name).camelize();
            if (_isNull(element, name)) return null;
            var value = _getComputedStyle(element, name);

            // returns the border-box dimensions rather than the content-box
            // dimensions, so we subtract padding and borders from the value
            if (DIMENSION_NAMES[name]) {
              var D = name.capitalize(), dim = parseFloat(value) || 0;
              if (dim !== element['offset' + D]) return Fuse.String(dim + 'px');
              return Fuse.String(Element['_getCss' + D](element) + 'px');
            }
            return Fuse.String(value);
          };
        }
        else { // Firefox, Safari, Opera 9.5+
          var getStyle = function getStyle(element, name) {
            element = $(element);
            name = Fuse.String(name).camelize();
            return _isNull(element, name) ? null : _getComputedStyle(element, name);
          };
        }
        return getStyle;
      })() :

      // IE
      (function() {
        var RELATIVE_CSS_UNITS = { 'em': 1, 'ex': 1 };

        // We need to insert into element a span with the M character in it.
        // The element.offsetHeight will give us the font size in px units.
        // Inspired by Google Doctype:
        // http://code.google.com/p/doctype/source/browse/trunk/goog/style/style.js#1146
        var span = Fuse._doc.createElement('span');
        span.style.cssText = 'position:absolute;visibility:hidden;height:1em;lineHeight:0;padding:0;margin:0;border:0;';
        span.innerHTML = 'M';

        function getStyle(element, name) {
          var value;
          if (name == 'opacity') {
            value = String(Element.getOpacity(element));
            if (value.indexOf('.') < 0) value += '.0';
            return Fuse.String(value);
          }

          element = $(element);
          name = Fuse.String(name).camelize();
          name = FLOAT_TRANSLATIONS[name] || name;

          // get cascaded style
          var s = element.currentStyle || element.style;
          value = s[name];
          if (value === 'auto') {
            if (DIMENSION_NAMES[name] && s.display !== 'none')
              return Fuse.String(element['offset' + name.capitalize()] + 'px');
            return null;
          }

          // If the unit is something other than a pixel (em, pt, %),
          // set it on something we can grab a pixel value from.
          // Inspired by Dean Edwards' comment
          // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
          if (/^\d+(\.\d+)?(?!px)[%a-z]+$/i.test(value)) {
            if (name == 'fontSize') {
              var unit = value.match(/\D+$/)[0];
              if (unit === '%') {
                var size = element.appendChild(span).offsetHeight;
                element.removeChild(span);
                return Math.round(size) + 'px';
              }
              else if (RELATIVE_CSS_UNITS[unit])
                element = element.parentNode;
            }

            // backup values
            var pos = Fuse.String(name == 'height' ? 'top' : 'left'),
             stylePos = element.style[pos], runtimePos = element.runtimeStyle[pos];

            // set runtimeStyle so no visible shift is seen
            element.runtimeStyle[pos] = s[pos];
            element.style[pos] = value;
            value = element.style['pixel' + pos.capitalize()] + 'px';

            // revert changes
            element.style[pos] = stylePos;
            element.runtimeStyle[pos] = runtimePos;
          }
          return Fuse.String(value);
        }
        return getStyle;
      })();

    // prevent JScript bug with named function expressions
    var setStyle = null;
  }).call(Element.Methods);
<|endoftext|>


<commit_before>var Cell = require('./Cell');
var Food = require('./Food');
var Virus = require('./Virus');

function MotherCell() {
    Cell.apply(this, Array.prototype.slice.call(arguments));

    this.cellType = 2;
    this.isSpiked = true;
    this.isMotherCell = true;       // Not to confuse bots
    this.setColor({ r: 0xce, g: 0x63, b: 0x63 });
    this.motherCellMinSize = 149;   // vanilla 149 (mass = 149*149/100 = 222.01)
    this.motherCellSpawnAmount = 2;
    if (!this._size) {
        this.setSize(this.motherCellMinSize);
    }
}

module.exports = MotherCell;
MotherCell.prototype = new Cell();

// Main Functions
MotherCell.prototype.onEaten = Virus.prototype.onEaten; // Copies the virus prototype function

MotherCell.prototype.canEat = function (cell) {
    return cell.cellType == 0 ||  // can eat player cell
           cell.cellType == 2 ||  // can eat virus
           cell.cellType == 3;    // can eat ejected mass
};

MotherCell.prototype.onUpdate = function () {
    if (this._size <= this.motherCellMinSize) {
        return;
    }
    var maxFood = this.gameServer.config.foodMaxAmount;
    if (this.gameServer.currentFood >= maxFood) {
        return;
    }
    var size1 = this._size;
    var size2 = this.gameServer.config.foodMinSize;
    for (var i = 0; i < this.motherCellSpawnAmount; i++) {
        size1 = Math.sqrt(size1 * size1 - size2 * size2);
        size1 = Math.max(size1, this.motherCellMinSize);
        this.setSize(size1);

        // Spawn food with size2
        var angle = Math.random() * 2 * Math.PI;
        var r = this._size;
        var pos = {
            x: this.position.x + r * Math.sin(angle),
            y: this.position.y + r * Math.cos(angle)
        };

        // Spawn food
        var food = new Food(this.gameServer, null, pos, size2);
        food.setColor(this.gameServer.getRandomColor());
        this.gameServer.addNode(food);

        // Eject to random distance
        food.setBoost(32 + 32 * Math.random(), angle);

        if (this.gameServer.currentFood >= maxFood || size1 <= this.motherCellMinSize) {
            break;
        }
    }
    this.gameServer.updateNodeQuad(this);
};

MotherCell.prototype.onAdd = function () {
};

MotherCell.prototype.onRemove = function () {
};
<commit_msg>Update MotherCell.js<commit_after>var Cell = require('./Cell');
var Food = require('./Food');
var Virus = require('./Virus');

function MotherCell() {
    Cell.apply(this, Array.prototype.slice.call(arguments));

    this.cellType = 2;
    this.isSpiked = true;
    this.isMotherCell = true;       // Not to confuse bots
    this.setColor({ r: 0xce, g: 0x63, b: 0x63 });
    this.motherCellMinSize = 149;   // vanilla 149 (mass = 149*149/100 = 222.01)
    this.motherCellSpawnAmount = 2;
    if (!this._size) {
        this.setSize(this.motherCellMinSize);
    }
}

module.exports = MotherCell;
MotherCell.prototype = new Cell();

// Main Functions
MotherCell.prototype.onEaten = Virus.prototype.onEaten; // Copies the virus prototype function

MotherCell.prototype.canEat = function (cell) {
    return cell.cellType == 0 ||  // can eat player cell
           cell.cellType == 2 ||  // can eat virus
           cell.cellType == 3;    // can eat ejected mass
};

MotherCell.prototype.onUpdate = function () {
    if (this._size <= this.motherCellMinSize) {
        return;
    }
    var maxFood = this.gameServer.config.foodMaxAmount;
    if (this.gameServer.nodesFood.length >= maxFood) {
        return;
    }
    var size1 = this._size;
    var size2 = this.gameServer.config.foodMinSize;
    for (var i = 0; i < this.motherCellSpawnAmount; i++) {
        size1 = Math.sqrt(size1 * size1 - size2 * size2);
        size1 = Math.max(size1, this.motherCellMinSize);
        this.setSize(size1);

        // Spawn food with size2
        var angle = Math.random() * 2 * Math.PI;
        var r = this._size;
        var pos = {
            x: this.position.x + r * Math.sin(angle),
            y: this.position.y + r * Math.cos(angle)
        };

        // Spawn food
        var food = new Food(this.gameServer, null, pos, size2);
        food.setColor(this.gameServer.getRandomColor());
        this.gameServer.addNode(food);

        // Eject to random distance
        food.setBoost(32 + 32 * Math.random(), angle);

        if (this.gameServer.nodesFood.length >= maxFood || size1 <= this.motherCellMinSize) {
            break;
        }
    }
    this.gameServer.updateNodeQuad(this);
};

MotherCell.prototype.onAdd = function () {
};

MotherCell.prototype.onRemove = function () {
};
<|endoftext|>


<commit_before>// ----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ----------------------------------------------------------------------------
/**
@module azure-mobile-apps/express/api
@description This module contains functionality for adding custom apis to an Azure
Mobile App. It returns a router that can be attached to an express app with
some additional functions for registering apis.
*/
var express = require('express'),
    loader = require('../../configuration/loader'),
    logger = require('../../logger'),
    assert = require('../../utilities/assert').argument,
    authorize = require('../middleware/authorize');
    supportedVerbs = ['get', 'post', 'put', 'patch', 'delete'];

/**
Create an instance of an express router for routing and handling api requests.
@param {configuration} configuration
@returns An express router with additional members described below.
*/
module.exports = function (configuration) {
    var router = express.Router();

    /**
    Register a single api with the specified definition.
    @function add
    @param {string} name - The name of the api. HTTP operations will be exposed on this route.
    @param {apiDefinition} definition - The definition for the api
    */
    router.add = function (name, definition) {
        assert(name, 'An api name was not specified');
        var apiRouter = express.Router();

        Object.getOwnPropertyNames(definition).forEach(function (property) {
            if (supportedVerbs.some(function (verb) { return verb === property; })) {
                if (definition.authorize || definition[property].authorize) {
                    definition[property].authorize = true;
                    logger.debug("Adding authorization to " + property + " for api " + name);
                }
                logger.debug("Adding method " + property + " to api " + name);
                apiRouter[property]('/', buildMiddlewareArray(definition[property]));
            } else if (property !== 'authorize') {
                logger.warn("Unrecognized property '" + property + "' in api " + name);
            }
        });

        router.use('/' + name, apiRouter);
    };

    // definition is either a function, an array of functions, or an array-like object
    // returns a middleware function or an array of middleware function
    function buildMiddlewareArray(definition) {
        var middlewareArray = definition;

        // if array-like object convert to array
        // {'0': addHeader, '1': return200, authorize: true} should convert to [addHeader,return200]
        var length = 0;
        while(definition.hasOwnProperty(length))
            length++;
        if (length) {
            middlewareArray.length = length;
            middlewareArray = Array.prototype.slice.call(middlewareArray);
        }

        if (definition.authorize)
            middlewareArray = [authorize].concat(middlewareArray);

        return middlewareArray;
    }

    /**
    Import a file or folder of modules containing api definitions
    @function import
    @param {string} path Path to a file or folder containing modules that export a {@link apiDefinition}
    The path is relative to configuration.basePath that defaults to the location of your startup module.
    The api name will be derived from the physical file name.
    */
    router.import = function (path) {
        assert(path, 'A path to api configuration file(s) was not specified');
        var apis = loader.loadPath(path, configuration.basePath);
        Object.keys(apis).forEach(function (name) {
            var definition = apis[name];

            router.add(name, definition);
        });
    };

    return router;
}
<commit_msg>Fixed global leak in api code<commit_after>// ----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ----------------------------------------------------------------------------
/**
@module azure-mobile-apps/express/api
@description This module contains functionality for adding custom apis to an Azure
Mobile App. It returns a router that can be attached to an express app with
some additional functions for registering apis.
*/
var express = require('express'),
    loader = require('../../configuration/loader'),
    logger = require('../../logger'),
    assert = require('../../utilities/assert').argument,
    authorize = require('../middleware/authorize'),
    supportedVerbs = ['get', 'post', 'put', 'patch', 'delete'];

/**
Create an instance of an express router for routing and handling api requests.
@param {configuration} configuration
@returns An express router with additional members described below.
*/
module.exports = function (configuration) {
    var router = express.Router();

    /**
    Register a single api with the specified definition.
    @function add
    @param {string} name - The name of the api. HTTP operations will be exposed on this route.
    @param {apiDefinition} definition - The definition for the api
    */
    router.add = function (name, definition) {
        assert(name, 'An api name was not specified');
        var apiRouter = express.Router();

        Object.getOwnPropertyNames(definition).forEach(function (property) {
            if (supportedVerbs.some(function (verb) { return verb === property; })) {
                if (definition.authorize || definition[property].authorize) {
                    definition[property].authorize = true;
                    logger.debug("Adding authorization to " + property + " for api " + name);
                }
                logger.debug("Adding method " + property + " to api " + name);
                apiRouter[property]('/', buildMiddlewareArray(definition[property]));
            } else if (property !== 'authorize') {
                logger.warn("Unrecognized property '" + property + "' in api " + name);
            }
        });

        router.use('/' + name, apiRouter);
    };

    // definition is either a function, an array of functions, or an array-like object
    // returns a middleware function or an array of middleware function
    function buildMiddlewareArray(definition) {
        var middlewareArray = definition;

        // if array-like object convert to array
        // {'0': addHeader, '1': return200, authorize: true} should convert to [addHeader,return200]
        var length = 0;
        while(definition.hasOwnProperty(length))
            length++;
        if (length) {
            middlewareArray.length = length;
            middlewareArray = Array.prototype.slice.call(middlewareArray);
        }

        if (definition.authorize)
            middlewareArray = [authorize].concat(middlewareArray);

        return middlewareArray;
    }

    /**
    Import a file or folder of modules containing api definitions
    @function import
    @param {string} path Path to a file or folder containing modules that export a {@link apiDefinition}
    The path is relative to configuration.basePath that defaults to the location of your startup module.
    The api name will be derived from the physical file name.
    */
    router.import = function (path) {
        assert(path, 'A path to api configuration file(s) was not specified');
        var apis = loader.loadPath(path, configuration.basePath);
        Object.keys(apis).forEach(function (name) {
            var definition = apis[name];

            router.add(name, definition);
        });
    };

    return router;
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.box',
  name: 'RetryReplyBox',
  extends: 'foam.box.ProxyBox',
  requires: [
    'foam.core.Exception'
  ],
  properties: [
    {
      name: 'attempt',
      value: 0
    },
    {
      name: 'maxAttempts'
    },
    {
      name: 'message'
    },
    {
      name: 'destination'
    }
  ],
  methods: [
    {
      name: 'send',
      code: function send(msg) {
        if ( this.Exception.isInstance(msg.object) && this.attempt < this.maxAttempts) {
          this.attempt++;
          this.destination.send(this.message);
          return;
        }

        this.delegate && this.delegate.send(msg);
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.box',
  name: 'RetryBox',
  extends: 'foam.box.ProxyBox',
  requires: [
    'foam.box.RetryReplyBox'
  ],

  properties: [
    'attempts',
    {
      name: 'maxAttempts',
      value: 3
    }
  ],

  methods: [
    function send(msg) {
      var replyBox = msg.attributes.replyBox;

      if ( replyBox ) {
        var clone = msg.cls_.create(msg);
        clone.attributes = {};
        for ( var key in msg.attributes ) {
          clone.attributes[key] = msg.attributes[key];
        }

        msg.attributes.replyBox = this.RetryReplyBox.create({
          delegate: replyBox,
          maxAttempts: this.maxAttempts,
          message: clone,
          destination: this.delegate
        });
      }

      this.delegate.send(msg);
    }
  ]
});
<commit_msg>Futher RetryBox fixes.  Also enable infinite retry on maxAttemps == -1.<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.box',
  name: 'RetryReplyBox',
  extends: 'foam.box.ProxyBox',
  requires: [
    'foam.core.Exception'
  ],
  properties: [
    {
      name: 'attempt',
      value: 0
    },
    {
      name: 'maxAttempts'
    },
    {
      name: 'message'
    },
    {
      name: 'destination'
    }
  ],
  methods: [
    {
      name: 'send',
      code: function send(msg) {
        if ( this.Exception.isInstance(msg.object) &&
             ( this.maxAttempts == -1 || this.attempt < this.maxAttempts ) ) {
          this.attempt++;
          this.destination.send(this.message);
          return;
        }

        this.delegate && this.delegate.send(msg);
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.box',
  name: 'RetryBox',
  extends: 'foam.box.ProxyBox',
  requires: [
    'foam.box.RetryReplyBox'
  ],

  properties: [
    'attempts',
    {
      name: 'maxAttempts',
      documentation: 'Set to -1 to infinitely retry.',
      value: 3
    }
  ],

  methods: [
    function send(msg) {
      var replyBox = msg.attributes.replyBox;

      if ( replyBox ) {
        var clone = msg.cls_.create(msg);

        msg.attributes.replyBox = this.RetryReplyBox.create({
          delegate: replyBox,
          maxAttempts: this.maxAttempts,
          message: clone,
          destination: this.delegate
        });

        clone.attributes = {};
        for ( var key in msg.attributes ) {
          clone.attributes[key] = msg.attributes[key];
        }
      }

      this.delegate.send(msg);
    }
  ]
});
<|endoftext|>


<commit_before>/**
 *  FullScreen widget:
 *
 *  var widget = new cdb.ui.common.FullScreen({
 *    doc: ".container", // optional; if not specified, we do the fullscreen of the whole window
 *    template: this.getTemplate("table/views/fullscreen")
 *  });
 *
 */

cdb.ui.common.FullScreen = cdb.core.View.extend({

  tagName: 'div',
  className: 'cartodb-fullscreen',

  events: {

    "click a": "_toggleFullScreen"

  },

  initialize: function() {

    _.bindAll(this, 'render');
    _.defaults(this.options, this.default_options);

  },

  _toggleFullScreen: function(ev) {

    ev.stopPropagation();

    var doc   = window.document;
    var docEl = doc.documentElement;

    if (this.options.doc) { // we use a custom element
      docEl = $(this.options.doc)[0];
    }

    var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen;
    var cancelFullScreen  = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen;

    if(!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement) {

      requestFullScreen.call(docEl);

    } else {

      cancelFullScreen.call(doc);

    }
  },

  render: function() {

    var $el = this.$el;

    var options = _.extend(this.options);

    $el.html(this.options.template(options));

    return this;
  }

});
<commit_msg>Prevents link click<commit_after>/**
 *  FullScreen widget:
 *
 *  var widget = new cdb.ui.common.FullScreen({
 *    doc: ".container", // optional; if not specified, we do the fullscreen of the whole window
 *    template: this.getTemplate("table/views/fullscreen")
 *  });
 *
 */

cdb.ui.common.FullScreen = cdb.core.View.extend({

  tagName: 'div',
  className: 'cartodb-fullscreen',

  events: {

    "click a": "_toggleFullScreen"

  },

  initialize: function() {

    _.bindAll(this, 'render');
    _.defaults(this.options, this.default_options);

  },

  _toggleFullScreen: function(ev) {

    ev.stopPropagation();
    ev.preventDefault();

    var doc   = window.document;
    var docEl = doc.documentElement;

    if (this.options.doc) { // we use a custom element
      docEl = $(this.options.doc)[0];
    }

    var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen;
    var cancelFullScreen  = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen;

    if(!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement) {

      requestFullScreen.call(docEl);

    } else {

      cancelFullScreen.call(doc);

    }
  },

  render: function() {

    var $el = this.$el;

    var options = _.extend(this.options);

    $el.html(this.options.template(options));

    return this;
  }

});
<|endoftext|>


<commit_before>"use strict";

function gsuiKeys() {
	var root = document.createElement( "div" );

	this._init();
	this.rootElement = root;
	this.rowElements = [];
	this._nlKeys = root.childNodes;
	this._nbOct = 0;
	root.className = "gsuiKeys";
	root.onmousedown = this._evmdRoot.bind( this );
}

gsuiKeys.prototype = {
	octaves( start, nbOct ) {
		var root = this.rootElement,
			nbDiff = nbOct - this._nbOct;

		if ( nbDiff || this._octStart !== start ) {
			this._octStart = start;
			root.style.counterReset = "octave " + ( start + nbOct );
		}
		if ( nbDiff ) {
			this._nbOct = nbOct;
			if ( nbDiff > 0 ) {
				while ( nbDiff-- > 0 ) {
					root.append( document.importNode( gsuiKeys.octaveTemplate.content, true ) );
				}
				this.newRowElements = root.querySelectorAll( ".gsui-row" );
				this.newRowElements.forEach( function( el ) {
					el.remove();
				} );
				Array.prototype.push.apply( this.rowElements, this.newRowElements );
			} else {
				for ( nbDiff *= 12; nbDiff < 0; ++nbDiff ) {
					root.lastChild.remove();
					this.rowElements[ this.rowElements.length - 1 ].remove();
					this.rowElements.pop();
				}
			}
		}
	},

	// private:
	_init() {
		if ( !gsuiKeys.octaveTemplate ) {
			gsuiKeys.octaveTemplate = document.getElementById( "gsuiKeys-octave" );
			document.body.addEventListener( "mousemove", function( e ) {
				gsuiKeys._focused && gsuiKeys._focused._evmmRoot( e );
			} );
			document.body.addEventListener( "mouseup", function( e ) {
				gsuiKeys._focused && gsuiKeys._focused._evmuRoot( e );
			} );
		}
	},
	_isBlack( keyInd ) {
		return keyInd === 1 || keyInd === 3 || keyInd === 5 || keyInd === 8 || keyInd === 10;
	},
	_keydown( keyInd ) {
		var elKey = this._nlKeys[ keyInd ];

		if ( elKey ) {
			this._keyup();
			this._keyInd = keyInd;
			this._elKey = elKey;
			this._octNum = this._octStart + this._nbOct - 1 - ~~( keyInd / 12 );
			elKey.classList.add( "gsui-active" );
			this.onkeydown && this.onkeydown( elKey.dataset.key, this._octNum, this._gain );
		}
	},
	_keyup() {
		if ( this._elKey ) {
			this._elKey.classList.remove( "gsui-active" );
			this.onkeyup && this.onkeyup( this._elKey.dataset.key, this._octNum, this._gain );
		}
	},

	// events:
	_evmdRoot( e ) {
		if ( this._nbOct ) {
			var blackKeyBCR = this.rootElement.childNodes[ 1 ].getBoundingClientRect();

			this._rootTop = this.rootElement.getBoundingClientRect().top;
			this._blackKeyR = blackKeyBCR.right;
			this._blackKeyH = blackKeyBCR.height;
			this._gain = Math.min( e.layerX / ( e.target.clientWidth - 1 ), 1 );
			gsuiKeys._focused = this;
			this._evmmRoot( e );
		}
	},
	_evmuRoot( e ) {
		this._keyup();
		delete this._elKey;
		delete this._keyInd;
		delete gsuiKeys._focused;
	},
	_evmmRoot( e ) {
		var fKeyInd = ( e.clientY - this._rootTop ) / this._blackKeyH,
			iKeyInd = ~~fKeyInd;

		if ( e.clientX > this._blackKeyR && this._isBlack( ~~( iKeyInd % 12 ) ) ) {
			iKeyInd += fKeyInd - iKeyInd < .5 ? -1 : 1;
		}
		if ( this._keyInd !== iKeyInd ) {
			this._keydown( iKeyInd );
		}
	}
};
<commit_msg>gsuiKeys: .remove()<commit_after>"use strict";

function gsuiKeys() {
	var root = document.createElement( "div" );

	this._init();
	this.rootElement = root;
	this.rowElements = [];
	this._nlKeys = root.childNodes;
	this._nbOct = 0;
	root.className = "gsuiKeys";
	root.onmousedown = this._evmdRoot.bind( this );
}

gsuiKeys.prototype = {
	remove() {
		this.rootElement.remove();
		this.rowElements.length = 0;
	},
	octaves( start, nbOct ) {
		var root = this.rootElement,
			nbDiff = nbOct - this._nbOct;

		if ( nbDiff || this._octStart !== start ) {
			this._octStart = start;
			root.style.counterReset = "octave " + ( start + nbOct );
		}
		if ( nbDiff ) {
			this._nbOct = nbOct;
			if ( nbDiff > 0 ) {
				while ( nbDiff-- > 0 ) {
					root.append( document.importNode( gsuiKeys.octaveTemplate.content, true ) );
				}
				this.newRowElements = root.querySelectorAll( ".gsui-row" );
				this.newRowElements.forEach( function( el ) {
					el.remove();
				} );
				Array.prototype.push.apply( this.rowElements, this.newRowElements );
			} else {
				for ( nbDiff *= 12; nbDiff < 0; ++nbDiff ) {
					root.lastChild.remove();
					this.rowElements[ this.rowElements.length - 1 ].remove();
					this.rowElements.pop();
				}
			}
		}
	},

	// private:
	_init() {
		if ( !gsuiKeys.octaveTemplate ) {
			gsuiKeys.octaveTemplate = document.getElementById( "gsuiKeys-octave" );
			document.body.addEventListener( "mousemove", function( e ) {
				gsuiKeys._focused && gsuiKeys._focused._evmmRoot( e );
			} );
			document.body.addEventListener( "mouseup", function( e ) {
				gsuiKeys._focused && gsuiKeys._focused._evmuRoot( e );
			} );
		}
	},
	_isBlack( keyInd ) {
		return keyInd === 1 || keyInd === 3 || keyInd === 5 || keyInd === 8 || keyInd === 10;
	},
	_keydown( keyInd ) {
		var elKey = this._nlKeys[ keyInd ];

		if ( elKey ) {
			this._keyup();
			this._keyInd = keyInd;
			this._elKey = elKey;
			this._octNum = this._octStart + this._nbOct - 1 - ~~( keyInd / 12 );
			elKey.classList.add( "gsui-active" );
			this.onkeydown && this.onkeydown( elKey.dataset.key, this._octNum, this._gain );
		}
	},
	_keyup() {
		if ( this._elKey ) {
			this._elKey.classList.remove( "gsui-active" );
			this.onkeyup && this.onkeyup( this._elKey.dataset.key, this._octNum, this._gain );
		}
	},

	// events:
	_evmdRoot( e ) {
		if ( this._nbOct ) {
			var blackKeyBCR = this.rootElement.childNodes[ 1 ].getBoundingClientRect();

			this._rootTop = this.rootElement.getBoundingClientRect().top;
			this._blackKeyR = blackKeyBCR.right;
			this._blackKeyH = blackKeyBCR.height;
			this._gain = Math.min( e.layerX / ( e.target.clientWidth - 1 ), 1 );
			gsuiKeys._focused = this;
			this._evmmRoot( e );
		}
	},
	_evmuRoot( e ) {
		this._keyup();
		delete this._elKey;
		delete this._keyInd;
		delete gsuiKeys._focused;
	},
	_evmmRoot( e ) {
		var fKeyInd = ( e.clientY - this._rootTop ) / this._blackKeyH,
			iKeyInd = ~~fKeyInd;

		if ( e.clientX > this._blackKeyR && this._isBlack( ~~( iKeyInd % 12 ) ) ) {
			iKeyInd += fKeyInd - iKeyInd < .5 ? -1 : 1;
		}
		if ( this._keyInd !== iKeyInd ) {
			this._keydown( iKeyInd );
		}
	}
};
<|endoftext|>


<commit_before>
import { Basic, IsNull, IsIn, Between, Exists, Like, Raw } from '../criterion'
import Expression, { Literal } from '../expression'
import { isArray, isString, uniq } from 'lodash'
import { SQ } from './expression'

/**
 *
 * @param {String|Expression} expr
 * @param {Any} value
 * @return {Criterion}
 */
export function EQ(expr, value) {
  return createBasicCriterion(expr, '=', value)
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} value
 * @return {Criterion}
 */
export function NE(expr, value) {
  return createBasicCriterion(expr, '!=', value)
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} value
 * @return {Criterion}
 */
export function GT(expr, value) {
  return createBasicCriterion(expr, '>', value)
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} value
 * @return {Criterion}
 */
export function LT(expr, value) {
  return createBasicCriterion(expr, '<', value)
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} value
 * @return {Criterion}
 */
export function GTE(expr, value) {
  return createBasicCriterion(expr, '>=', value)
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} value
 * @return {Criterion}
 */
export function LTE(expr, value) {
  return createBasicCriterion(expr, '<=', value)
}

/**
 *
 * @param {String|Expression} expr
 * @returns {Criterion}
 */
export function ISNULL(expr) {
  return new IsNull(ensureExpression(expr))
}

/**
 *
 * @returns {Criterion}
 */
export function ISTRUE() {
  return new Raw(Literal.from('1 = 1'))
}

/**
 *
 * @returns {Criterion}
 */
export function ISFALSE() {
  return new Raw(Literal.from('1 = 0'))
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} values
 * @returns {Criterion}
 */
export function IN(expr, values) {
  return new IsIn(ensureExpression(expr), isArray(values) ? uniq(values) : SQ(values))
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} lower
 * @param {Any} higher
 * @returns {Criterion}
 */
export function BETWEEN(expr, lower, higher) {
  return new Between(ensureExpression(expr), [lower, higher])
}

/**
 *
 * @param {Any} query
 * @returns {Criterion}
 */
export function EXISTS(query) {
  return new Exists(SQ(query))
}

/**
 *
 * @param {String|Expression} expr
 * @param {String} patern
 * @returns {Criterion}
 */
export function LIKE(expr, patern) {
  return new Like(ensureExpression(expr), patern)
}

/**
 *
 * @param {String|Expression} expr
 * @param {String} value
 * @returns {Criterion}
 */
export function STARTSWITH(expr, value) {
  return LIKE(expr, value.replace(/(_|%)/g, '\\$1') + '%')
}

/**
 *
 * @param {String|Expression} expr
 * @param {String} value
 * @returns {Criterion}
 */
export function ENDSWITH(expr, value) {
  return LIKE(expr, '%' + value.replace(/(_|%)/g, '\\$1'))
}

/**
 *
 * @param {String|Expression} expr
 * @param {String} operator
 * @param {Any} value
 * @returns {Basic}
 */
function createBasicCriterion(expr, operator, value) {
  return new Basic(ensureExpression(expr), operator, value)
}

/**
 *
 * @param {Any} value
 * @returns {Expression}
 * @throws {TypeError}
 */
function ensureExpression(value) {
  if ( isString(value) )
    value = new Literal(value)

  if ( value instanceof Expression )
    return value

  throw new TypeError("Invalid condition operand")
}<commit_msg>Fix LIKE helper<commit_after>
import { Basic, IsNull, IsIn, Between, Exists, Raw } from '../criterion'
import Expression, { Literal } from '../expression'
import { isArray, isString, uniq } from 'lodash'
import { SQ } from './expression'

/**
 *
 * @param {String|Expression} expr
 * @param {Any} value
 * @return {Criterion}
 */
export function EQ(expr, value) {
  return createBasicCriterion(expr, '=', value)
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} value
 * @return {Criterion}
 */
export function NE(expr, value) {
  return createBasicCriterion(expr, '!=', value)
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} value
 * @return {Criterion}
 */
export function GT(expr, value) {
  return createBasicCriterion(expr, '>', value)
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} value
 * @return {Criterion}
 */
export function LT(expr, value) {
  return createBasicCriterion(expr, '<', value)
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} value
 * @return {Criterion}
 */
export function GTE(expr, value) {
  return createBasicCriterion(expr, '>=', value)
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} value
 * @return {Criterion}
 */
export function LTE(expr, value) {
  return createBasicCriterion(expr, '<=', value)
}

/**
 *
 * @param {String|Expression} expr
 * @returns {Criterion}
 */
export function ISNULL(expr) {
  return new IsNull(ensureExpression(expr))
}

/**
 *
 * @returns {Criterion}
 */
export function ISTRUE() {
  return new Raw(Literal.from('1 = 1'))
}

/**
 *
 * @returns {Criterion}
 */
export function ISFALSE() {
  return new Raw(Literal.from('1 = 0'))
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} values
 * @returns {Criterion}
 */
export function IN(expr, values) {
  return new IsIn(ensureExpression(expr), isArray(values) ? uniq(values) : SQ(values))
}

/**
 *
 * @param {String|Expression} expr
 * @param {Any} lower
 * @param {Any} higher
 * @returns {Criterion}
 */
export function BETWEEN(expr, lower, higher) {
  return new Between(ensureExpression(expr), [lower, higher])
}

/**
 *
 * @param {Any} query
 * @returns {Criterion}
 */
export function EXISTS(query) {
  return new Exists(SQ(query))
}

/**
 *
 * @param {String|Expression} expr
 * @param {String} pattern
 * @returns {Criterion}
 */
export function LIKE(expr, pattern) {
  return createBasicCriterion(expr, 'like', pattern)
}

/**
 *
 * @param {String|Expression} expr
 * @param {String} value
 * @returns {Criterion}
 */
export function STARTSWITH(expr, value) {
  return LIKE(expr, value.replace(/(_|%)/g, '\\$1') + '%')
}

/**
 *
 * @param {String|Expression} expr
 * @param {String} value
 * @returns {Criterion}
 */
export function ENDSWITH(expr, value) {
  return LIKE(expr, '%' + value.replace(/(_|%)/g, '\\$1'))
}

/**
 *
 * @param {String|Expression} expr
 * @param {String} operator
 * @param {Any} value
 * @returns {Basic}
 */
function createBasicCriterion(expr, operator, value) {
  return new Basic(ensureExpression(expr), operator, value)
}

/**
 *
 * @param {Any} value
 * @returns {Expression}
 * @throws {TypeError}
 */
function ensureExpression(value) {
  if ( isString(value) )
    value = new Literal(value)

  if ( value instanceof Expression )
    return value

  throw new TypeError("Invalid condition operand")
}
<|endoftext|>


<commit_before>import _ from 'lodash'
import moment from 'moment'
import { objectifyIDs, toObjectIDs } from '../db'
import { discard, disallow, getItems, replaceItems } from 'feathers-hooks-common'
// import makeDebug from 'debug'

// const debug = makeDebug('kalisio:kCore')

export function processPerspectives (hook) {
  let params = hook.params
  let query = params.query
  let service = hook.service

  // Test if some perspectives are defined on the model
  if (!service.options || !service.options.perspectives) return

  // Iterate through known perspectives of the model
  service.options.perspectives.forEach(perspective => {
    // Only discard if not explicitely asked by $select
    let filterPerspective = true
    if (!_.isNil(query) && !_.isNil(query.$select)) {
      // Transform to array to unify processing
      let selectedFields = (typeof query.$select === 'string' ? [query.$select] : query.$select)
      if (Array.isArray(selectedFields)) {
        selectedFields.forEach(field => {
          // Take care that we might only ask for a subset of perspective fields like ['perspective.fieldName']
          if ((field === perspective) || field.startsWith(perspective + '.')) {
            filterPerspective = false
          }
        })
      }
    }
    if (filterPerspective) {
      discard(perspective)(hook)
    }
  })
}

// When perspectives are present we disallow update in order to avoid erase them.
// Indeed when requesting an object they are not retrieved by default
export function preventUpdatePerspectives (hook) {
  let service = hook.service

  // Test if some perspectives are defined on the model
  if (!service.options || !service.options.perspectives) return

  disallow()(hook)
}

// The hook serialize allows to copy/move some properties within the objects holded by the hook
// It applies an array of rules defined by:
// - source: the path to the property to be copied
// - target: the path where to copy the property
// - delete: a flag to define whether the hook has to delete the source property
export function serialize (rules, options = {}) {
  return function (hook) {
    // Retrieve the items from the hook
    let items = getItems(hook)
    const isArray = Array.isArray(items)
    items = (isArray ? items : [items])
    // Apply the rules for each item
    items.forEach(item => {
      rules.forEach(rule => {
        const source = _.get(item, rule.source)
        if (!_.isNil(source)) {
          _.set(item, rule.target, source)
          if (rule.delete) {
            _.unset(item, rule.source)
          }
        } else if (options.throwOnNotFound || rule.throwOnNotFound) {
          throw new Error('Cannot find valid input value for property ' + rule.target)
        }
      })
    })
    // Replace the items within the hook
    replaceItems(hook, isArray ? items : items[0])
  }
}

// The hook objectify allows to transform the value bound to an '_id' like key as a string
// into a Mongo ObjectId on client queries
export function processObjectIDs (hook) {
  if (hook.params.query) objectifyIDs(hook.params.query)
  if (hook.data) objectifyIDs(hook.data)
}

// The hook convert allows to transform a set of input properties as a string
// into a Mongo ObjectId on client queries
export function convertObjectIDs (properties) {
  return function (hook) {
    if (hook.params.query) toObjectIDs(hook.params.query, properties)
    if (hook.data) toObjectIDs(hook.data, properties)
  }
}

// Utility function used to convert from string to Dates a fixed set of properties on a given object
export function toDates (object, properties) {
  properties.forEach(property => {
    const date = moment.utc(_.get(object, property))
    if (date.isValid()) {
      _.set(object, property, date)
    }
  })
}

// The hook convert allows to transform a set of input properties as a string
// into a Date object on client queries
export function convertDates (properties) {
  return function (hook) {
    if (hook.params.query) toDates(hook.params.query, properties)
    if (hook.data) toDates(hook.data, properties)
  }
}

export async function populatePreviousObject (hook) {
  if (hook.type !== 'before') {
    throw new Error(`The 'populatePreviousObject' hook should only be used as a 'before' hook.`)
  }
  let item = getItems(hook)
  // Retrieve previous version of the item and make it available to next hooks
  if (item._id) {
    hook.params.previousItem = await hook.service.get(item._id.toString())
  }
  return hook
}
<commit_msg>Fixed bug in date converter hook<commit_after>import _ from 'lodash'
import moment from 'moment'
import { objectifyIDs, toObjectIDs } from '../db'
import { discard, disallow, getItems, replaceItems } from 'feathers-hooks-common'
// import makeDebug from 'debug'

// const debug = makeDebug('kalisio:kCore')

export function processPerspectives (hook) {
  let params = hook.params
  let query = params.query
  let service = hook.service

  // Test if some perspectives are defined on the model
  if (!service.options || !service.options.perspectives) return

  // Iterate through known perspectives of the model
  service.options.perspectives.forEach(perspective => {
    // Only discard if not explicitely asked by $select
    let filterPerspective = true
    if (!_.isNil(query) && !_.isNil(query.$select)) {
      // Transform to array to unify processing
      let selectedFields = (typeof query.$select === 'string' ? [query.$select] : query.$select)
      if (Array.isArray(selectedFields)) {
        selectedFields.forEach(field => {
          // Take care that we might only ask for a subset of perspective fields like ['perspective.fieldName']
          if ((field === perspective) || field.startsWith(perspective + '.')) {
            filterPerspective = false
          }
        })
      }
    }
    if (filterPerspective) {
      discard(perspective)(hook)
    }
  })
}

// When perspectives are present we disallow update in order to avoid erase them.
// Indeed when requesting an object they are not retrieved by default
export function preventUpdatePerspectives (hook) {
  let service = hook.service

  // Test if some perspectives are defined on the model
  if (!service.options || !service.options.perspectives) return

  disallow()(hook)
}

// The hook serialize allows to copy/move some properties within the objects holded by the hook
// It applies an array of rules defined by:
// - source: the path to the property to be copied
// - target: the path where to copy the property
// - delete: a flag to define whether the hook has to delete the source property
export function serialize (rules, options = {}) {
  return function (hook) {
    // Retrieve the items from the hook
    let items = getItems(hook)
    const isArray = Array.isArray(items)
    items = (isArray ? items : [items])
    // Apply the rules for each item
    items.forEach(item => {
      rules.forEach(rule => {
        const source = _.get(item, rule.source)
        if (!_.isNil(source)) {
          _.set(item, rule.target, source)
          if (rule.delete) {
            _.unset(item, rule.source)
          }
        } else if (options.throwOnNotFound || rule.throwOnNotFound) {
          throw new Error('Cannot find valid input value for property ' + rule.target)
        }
      })
    })
    // Replace the items within the hook
    replaceItems(hook, isArray ? items : items[0])
  }
}

// The hook objectify allows to transform the value bound to an '_id' like key as a string
// into a Mongo ObjectId on client queries
export function processObjectIDs (hook) {
  if (hook.params.query) objectifyIDs(hook.params.query)
  if (hook.data) objectifyIDs(hook.data)
  return hook
}

// The hook convert allows to transform a set of input properties as a string
// into a Mongo ObjectId on client queries
export function convertObjectIDs (properties) {
  return function (hook) {
    if (hook.params.query) toObjectIDs(hook.params.query, properties)
    if (hook.data) toObjectIDs(hook.data, properties)
    return hook
  }
}

// Utility function used to convert from string to Dates a fixed set of properties on a given object
export function toDates (object, properties, asMoment) {
  properties.forEach(property => {
    let date = _.get(object, property)
    if (date) {
      // We use moment to validate the date
      date = moment.utc(date)
      if (date.isValid()) {
        if (!asMoment) {
          date = date.toDate()
        }
        _.set(object, property, date)
      }
    }
  })
}

// The hook allows to transform a set of input properties as a string
// into a Date/Moment object on client queries
export function convertDates (properties, asMoment) {
  return function (hook) {
    if (hook.params.query) toDates(hook.params.query, properties, asMoment)
    if (hook.data) toDates(hook.data, properties, asMoment)
    return hook
  }
}

export async function populatePreviousObject (hook) {
  if (hook.type !== 'before') {
    throw new Error(`The 'populatePreviousObject' hook should only be used as a 'before' hook.`)
  }
  let item = getItems(hook)
  // Retrieve previous version of the item and make it available to next hooks
  if (item._id) {
    hook.params.previousItem = await hook.service.get(item._id.toString())
  }
  return hook
}
<|endoftext|>


<commit_before>import _ from 'underscore';
import ipc from './lib/ipc';
import Buffers from './lib/buffers';
import BufferView from './buffer-view';
import configuration from './lib/configuration';
import InputBox from './input-box';
import Names from './lib/names';
import NameView from './name-view';
import shortcutManager from './lib/shortcut-manager';
import TabNav from './tab-nav';
import React from 'react';

const rootBufferName = configuration.get('root-buffer-name');
const commandSymbol = configuration.get('command-symbol');

export default class IrcWindow extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      nick: '',
      buffers: new Buffers(rootBufferName),
      names: new Names(),
    };
  }

  setNick(nick) {
    this.setState({nick});
  }

  componentDidMount() {
    // irc events
    ipc.on('registered', data => this.setNick(data.nick));
    ipc.on('message', this.onMessage.bind(this));
    ipc.on('join', this.onJoin.bind(this));
    ipc.on('part', this.onPart.bind(this));
    ipc.on('nick', this.onChangeNick.bind(this));
    ipc.on('names', this.onNames.bind(this));
    ipc.on('quit', this.onQuit.bind(this));

    // shortcuts
    shortcutManager.on('next-tab', function () {
      this.state.buffers.setCurrent(this.state.buffers.next().name);
      this.forceUpdate();
    }.bind(this));
    shortcutManager.on('previous-tab', function () {
      this.state.buffers.setCurrent(this.state.buffers.previous().name);
      this.forceUpdate();
    }.bind(this));

    this.props.errorHandler.on('irc', this.onError.bind(this));
  }

  setWindowTitle(title) {
    let titleTag = document.getElementsByTagName('title')[0];
    titleTag.innerText = `koko - ${title}`;
  }

  render() {
    this.setWindowTitle(this.props.server);

    let currentBufferName = this.state.buffers.current().name;
    let currentNames = this.state.names.get(currentBufferName);

    return (

        <TabNav buffers={this.state.buffers} />
        <NameView names={currentNames} />
        <BufferView buffers={this.state.buffers} />
        <InputBox channel={this.state.buffers.current().name}
                  names={currentNames}
                  submit={this.submitInput.bind(this)} />

    );
  }

  submitInput(raw) {
    let target = this.state.buffers.current().name;
    if (raw.startsWith(commandSymbol)) {
      raw = raw.substring(1);
      let methodName = this.tryGetLocalHandler(raw);
      if (methodName) {
        this[methodName](raw);
      } else {
        ipc.send('command', {raw, context: {target}});
      }
    } else {
      if (target !== rootBufferName) {
        ipc.send('message', {raw, context: {target}});
        this.state.buffers.send(target, this.state.nick, raw);
        this.forceUpdate();
      }
    }
  }

  tryGetLocalHandler(raw) {
    let tokens = raw.split(' ');
    if (tokens.length === 1 && tokens[0] === 'part' &&
        this.state.buffers.current().name[0] !== '#') {
      return 'partPersonalChat';
    } else if (tokens[0] === 'pm') {
      return 'startPersonalChat';
    }
  }

  onMessage(data) {
    let to = data.to[0] === '#' || data.to === rootBufferName ? data.to : data.nick;
    this.state.buffers.send(to, data.nick, data.text);
    this.forceUpdate();
  }

  onJoin(data) {
    let isMe = data.nick === this.state.nick;
    if (isMe) {
      this.state.buffers.add(data.channel);
      this.state.buffers.setCurrent(data.channel);
    } else {
      this.state.names.add(data.channel, data.nick);
    }
    this.state.buffers.joinMessage(data.channel, data.nick, data.message);
    this.forceUpdate();
  }

  onPart(data) {
    let isMe = data.nick === this.state.nick;
    if (isMe && data.channel !== rootBufferName) {
      this.state.buffers.remove(data.channel);
      this.state.names.delete(data.channel);
    } else {
      this.state.buffers.partMessage(data.channel, data.nick, data.reason, data.message);
      this.state.names.remove(data.channel, data.nick);
    }
    this.forceUpdate();
  }

  startPersonalChat(raw) {
    let tokens = raw.split(' ');
    if (tokens.length < 3) {
      this.props.errorHandler.handle({
        type: 'normal',
        error: new Error('Invalid command arguments: [nick,message]'),
      });
    } else {
      let target = tokens[1];
      let raw = tokens.splice(2).join(' ');
      ipc.send('message', {raw, context: {target}});
      this.state.buffers.send(target, this.state.nick, raw);
      this.state.buffers.setCurrent(target);
      this.forceUpdate();
    }
  }

  partPersonalChat() {
    let target = this.state.buffers.current().name;
    this.state.buffers.remove(target);
    this.forceUpdate();
  }

  onChangeNick(data) {
    if (data.oldnick === this.state.nick) {
      this.setState({nick: data.newnick});
      data.channels.push(rootBufferName);
    }
    data.channels.forEach(function (channel) {
      this.state.buffers.changeNick(channel, data.oldnick, data.newnick);
      this.state.names.update(channel, data.oldnick, data.newnick);
    }.bind(this));
    this.forceUpdate();
  }

  onNames(data) {
    let names = Object.keys(data.names).map(function (name) {
      return {name, mode: data.names[name], isMe: name === this.state.nick}
    }.bind(this));
    this.state.names.set(data.channel, names);
    this.forceUpdate();
  }

  onQuit(data) {
    data.channels.forEach(function (channel) {
      let dataForChannel = _.extend(_.omit(data, 'channels'), {channel});
      this.onPart(dataForChannel);
    }.bind(this));
  }

  onError(error) {
    switch (error.command) {
    case "err_nosuchnick":
      this.state.buffers.send(error.args[1], error.args[1], error.args[2]);
      this.forceUpdate();
      break;
    }
  }
}
<commit_msg>Remove 'koko - ' from window title.<commit_after>import _ from 'underscore';
import ipc from './lib/ipc';
import Buffers from './lib/buffers';
import BufferView from './buffer-view';
import configuration from './lib/configuration';
import InputBox from './input-box';
import Names from './lib/names';
import NameView from './name-view';
import shortcutManager from './lib/shortcut-manager';
import TabNav from './tab-nav';
import React from 'react';

const rootBufferName = configuration.get('root-buffer-name');
const commandSymbol = configuration.get('command-symbol');

export default class IrcWindow extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      nick: '',
      buffers: new Buffers(rootBufferName),
      names: new Names(),
    };
  }

  setNick(nick) {
    this.setState({nick});
  }

  componentDidMount() {
    // irc events
    ipc.on('registered', data => this.setNick(data.nick));
    ipc.on('message', this.onMessage.bind(this));
    ipc.on('join', this.onJoin.bind(this));
    ipc.on('part', this.onPart.bind(this));
    ipc.on('nick', this.onChangeNick.bind(this));
    ipc.on('names', this.onNames.bind(this));
    ipc.on('quit', this.onQuit.bind(this));

    // shortcuts
    shortcutManager.on('next-tab', function () {
      this.state.buffers.setCurrent(this.state.buffers.next().name);
      this.forceUpdate();
    }.bind(this));
    shortcutManager.on('previous-tab', function () {
      this.state.buffers.setCurrent(this.state.buffers.previous().name);
      this.forceUpdate();
    }.bind(this));

    this.props.errorHandler.on('irc', this.onError.bind(this));
  }

  setWindowTitle(title) {
    let titleTag = document.getElementsByTagName('title')[0];
    titleTag.innerText = title;
  }

  render() {
    this.setWindowTitle(this.props.server);

    let currentBufferName = this.state.buffers.current().name;
    let currentNames = this.state.names.get(currentBufferName);

    return (

        <TabNav buffers={this.state.buffers} />
        <NameView names={currentNames} />
        <BufferView buffers={this.state.buffers} />
        <InputBox channel={this.state.buffers.current().name}
                  names={currentNames}
                  submit={this.submitInput.bind(this)} />

    );
  }

  submitInput(raw) {
    let target = this.state.buffers.current().name;
    if (raw.startsWith(commandSymbol)) {
      raw = raw.substring(1);
      let methodName = this.tryGetLocalHandler(raw);
      if (methodName) {
        this[methodName](raw);
      } else {
        ipc.send('command', {raw, context: {target}});
      }
    } else {
      if (target !== rootBufferName) {
        ipc.send('message', {raw, context: {target}});
        this.state.buffers.send(target, this.state.nick, raw);
        this.forceUpdate();
      }
    }
  }

  tryGetLocalHandler(raw) {
    let tokens = raw.split(' ');
    if (tokens.length === 1 && tokens[0] === 'part' &&
        this.state.buffers.current().name[0] !== '#') {
      return 'partPersonalChat';
    } else if (tokens[0] === 'pm') {
      return 'startPersonalChat';
    }
  }

  onMessage(data) {
    let to = data.to[0] === '#' || data.to === rootBufferName ? data.to : data.nick;
    this.state.buffers.send(to, data.nick, data.text);
    this.forceUpdate();
  }

  onJoin(data) {
    let isMe = data.nick === this.state.nick;
    if (isMe) {
      this.state.buffers.add(data.channel);
      this.state.buffers.setCurrent(data.channel);
    } else {
      this.state.names.add(data.channel, data.nick);
    }
    this.state.buffers.joinMessage(data.channel, data.nick, data.message);
    this.forceUpdate();
  }

  onPart(data) {
    let isMe = data.nick === this.state.nick;
    if (isMe && data.channel !== rootBufferName) {
      this.state.buffers.remove(data.channel);
      this.state.names.delete(data.channel);
    } else {
      this.state.buffers.partMessage(data.channel, data.nick, data.reason, data.message);
      this.state.names.remove(data.channel, data.nick);
    }
    this.forceUpdate();
  }

  startPersonalChat(raw) {
    let tokens = raw.split(' ');
    if (tokens.length < 3) {
      this.props.errorHandler.handle({
        type: 'normal',
        error: new Error('Invalid command arguments: [nick,message]'),
      });
    } else {
      let target = tokens[1];
      let raw = tokens.splice(2).join(' ');
      ipc.send('message', {raw, context: {target}});
      this.state.buffers.send(target, this.state.nick, raw);
      this.state.buffers.setCurrent(target);
      this.forceUpdate();
    }
  }

  partPersonalChat() {
    let target = this.state.buffers.current().name;
    this.state.buffers.remove(target);
    this.forceUpdate();
  }

  onChangeNick(data) {
    if (data.oldnick === this.state.nick) {
      this.setState({nick: data.newnick});
      data.channels.push(rootBufferName);
    }
    data.channels.forEach(function (channel) {
      this.state.buffers.changeNick(channel, data.oldnick, data.newnick);
      this.state.names.update(channel, data.oldnick, data.newnick);
    }.bind(this));
    this.forceUpdate();
  }

  onNames(data) {
    let names = Object.keys(data.names).map(function (name) {
      return {name, mode: data.names[name], isMe: name === this.state.nick}
    }.bind(this));
    this.state.names.set(data.channel, names);
    this.forceUpdate();
  }

  onQuit(data) {
    data.channels.forEach(function (channel) {
      let dataForChannel = _.extend(_.omit(data, 'channels'), {channel});
      this.onPart(dataForChannel);
    }.bind(this));
  }

  onError(error) {
    switch (error.command) {
    case "err_nosuchnick":
      this.state.buffers.send(error.args[1], error.args[1], error.args[2]);
      this.forceUpdate();
      break;
    }
  }
}
<|endoftext|>


<commit_before>// To keep the UI snappy, we run WebTorrent in its own hidden window, a separate
// process from the main window.
console.time('init')

var WebTorrent = require('webtorrent')
var defaultAnnounceList = require('create-torrent').announceList
var deepEqual = require('deep-equal')
var electron = require('electron')
var fs = require('fs-extra')
var musicmetadata = require('musicmetadata')
var networkAddress = require('network-address')
var path = require('path')

var crashReporter = require('../crash-reporter')
var config = require('../config')
var torrentPoster = require('./lib/torrent-poster')

// Report when the process crashes
crashReporter.init()

// Send & receive messages from the main window
var ipc = electron.ipcRenderer

// Force use of webtorrent trackers on all torrents
global.WEBTORRENT_ANNOUNCE = defaultAnnounceList
  .map((arr) => arr[0])
  .filter((url) => url.indexOf('wss://') === 0 || url.indexOf('ws://') === 0)

// Connect to the WebTorrent and BitTorrent networks. WebTorrent Desktop is a hybrid
// client, as explained here: https://webtorrent.io/faq
var client = window.client = new WebTorrent()

// WebTorrent-to-HTTP streaming sever
var server = window.server = null

// Used for diffing, so we only send progress updates when necessary
var prevProgress = window.prevProgress = null

init()

function init () {
  client.on('warning', (err) => ipc.send('wt-warning', null, err.message))
  client.on('error', (err) => ipc.send('wt-error', null, err.message))

  ipc.on('wt-start-torrenting', (e, torrentKey, torrentID, path, fileModtimes) =>
    startTorrenting(torrentKey, torrentID, path, fileModtimes))
  ipc.on('wt-stop-torrenting', (e, infoHash) =>
    stopTorrenting(infoHash))
  ipc.on('wt-create-torrent', (e, torrentKey, options) =>
    createTorrent(torrentKey, options))
  ipc.on('wt-save-torrent-file', (e, torrentKey) =>
    saveTorrentFile(torrentKey))
  ipc.on('wt-generate-torrent-poster', (e, torrentKey) =>
    generateTorrentPoster(torrentKey))
  ipc.on('wt-get-audio-metadata', (e, infoHash, index) =>
    getAudioMetadata(infoHash, index))
  ipc.on('wt-start-server', (e, infoHash, index) =>
    startServer(infoHash, index))
  ipc.on('wt-stop-server', (e) =>
    stopServer())

  ipc.send('ipcReadyWebTorrent')

  setInterval(updateTorrentProgress, 1000)
}

// Starts a given TorrentID, which can be an infohash, magnet URI, etc. Returns WebTorrent object
// See https://github.com/feross/webtorrent/blob/master/docs/api.md#clientaddtorrentid-opts-function-ontorrent-torrent-
function startTorrenting (torrentKey, torrentID, path, fileModtimes) {
  console.log('starting torrent %s: %s', torrentKey, torrentID)
  var torrent = client.add(torrentID, {
    path: path,
    fileModtimes: fileModtimes
  })
  torrent.key = torrentKey
  addTorrentEvents(torrent)
  return torrent
}

function stopTorrenting (infoHash) {
  var torrent = client.get(infoHash)
  torrent.destroy()
}

// Create a new torrent, start seeding
function createTorrent (torrentKey, options) {
  console.log('creating torrent %s', torrentKey, options)
  var torrent = client.seed(options.files, options)
  torrent.key = torrentKey
  addTorrentEvents(torrent)
  ipc.send('wt-new-torrent')
}

function addTorrentEvents (torrent) {
  torrent.on('warning', (err) =>
    ipc.send('wt-warning', torrent.key, err.message))
  torrent.on('error', (err) =>
    ipc.send('wt-error', torrent.key, err.message))
  torrent.on('infoHash', () =>
    ipc.send('wt-infohash', torrent.key, torrent.infoHash))
  torrent.on('metadata', torrentMetadata)
  torrent.on('ready', torrentReady)
  torrent.on('done', torrentDone)

  function torrentMetadata () {
    var info = getTorrentInfo(torrent)
    ipc.send('wt-metadata', torrent.key, info)

    updateTorrentProgress()
  }

  function torrentReady () {
    var info = getTorrentInfo(torrent)
    ipc.send('wt-ready', torrent.key, info)
    ipc.send('wt-ready-' + torrent.infoHash, torrent.key, info) // TODO: hack

    updateTorrentProgress()
  }

  function torrentDone () {
    var info = getTorrentInfo(torrent)
    ipc.send('wt-done', torrent.key, info)

    updateTorrentProgress()

    torrent.getFileModtimes(function (err, fileModtimes) {
      if (err) return onError(err)
      ipc.send('wt-file-modtimes', torrent.key, fileModtimes)
    })
  }
}

// Produces a JSON saveable summary of a torrent
function getTorrentInfo (torrent) {
  return {
    infoHash: torrent.infoHash,
    magnetURI: torrent.magnetURI,
    name: torrent.name,
    path: torrent.path,
    files: torrent.files.map(getTorrentFileInfo),
    bytesReceived: torrent.received
  }
}

// Produces a JSON saveable summary of a file in a torrent
function getTorrentFileInfo (file) {
  return {
    name: file.name,
    length: file.length,
    path: file.path,
    numPiecesPresent: 0,
    numPieces: null
  }
}

// Every time we resolve a magnet URI, save the torrent file so that we never
// have to download it again. Never ask the DHT the same question twice.
function saveTorrentFile (torrentKey) {
  var torrent = getTorrent(torrentKey)
  checkIfTorrentFileExists(torrent.infoHash, function (torrentPath, exists) {
    var fileName = torrent.infoHash + '.torrent'
    if (exists) {
      // We've already saved the file
      return ipc.send('wt-file-saved', torrentKey, fileName)
    }

    // Otherwise, save the .torrent file, under the app config folder
    fs.mkdir(config.CONFIG_TORRENT_PATH, function (_) {
      fs.writeFile(torrentPath, torrent.torrentFile, function (err) {
        if (err) return console.log('error saving torrent file %s: %o', torrentPath, err)
        console.log('saved torrent file %s', torrentPath)
        return ipc.send('wt-file-saved', torrentKey, fileName)
      })
    })
  })
}

// Checks whether we've already resolved a given infohash to a torrent file
// Calls back with (torrentPath, exists). Logs, does not call back on error
function checkIfTorrentFileExists (infoHash, cb) {
  var torrentPath = path.join(config.CONFIG_TORRENT_PATH, infoHash + '.torrent')
  fs.exists(torrentPath, function (exists) {
    cb(torrentPath, exists)
  })
}

// Save a JPG that represents a torrent.
// Auto chooses either a frame from a video file, an image, etc
function generateTorrentPoster (torrentKey) {
  var torrent = getTorrent(torrentKey)
  torrentPoster(torrent, function (err, buf, extension) {
    if (err) return console.log('error generating poster: %o', err)
    // save it for next time
    fs.mkdirp(config.CONFIG_POSTER_PATH, function (err) {
      if (err) return console.log('error creating poster dir: %o', err)
      var posterFileName = torrent.infoHash + extension
      var posterFilePath = path.join(config.CONFIG_POSTER_PATH, posterFileName)
      fs.writeFile(posterFilePath, buf, function (err) {
        if (err) return console.log('error saving poster: %o', err)
        // show the poster
        ipc.send('wt-poster', torrentKey, posterFileName)
      })
    })
  })
}

function updateTorrentProgress () {
  var progress = getTorrentProgress()
  // TODO: diff torrent-by-torrent, not once for the whole update
  if (prevProgress && deepEqual(progress, prevProgress, {strict: true})) {
    return /* don't send heavy object if it hasn't changed */
  }
  ipc.send('wt-progress', progress)
  prevProgress = progress
}

function getTorrentProgress () {
  // First, track overall progress
  var progress = client.progress
  var hasActiveTorrents = client.torrents.some(function (torrent) {
    return torrent.progress !== 1
  })

  // Track progress for every file in each torrent
  // TODO: ideally this would be tracked by WebTorrent, which could do it
  // more efficiently than looping over torrent.bitfield
  var torrentProg = client.torrents.map(function (torrent) {
    var fileProg = torrent.files && torrent.files.map(function (file, index) {
      var numPieces = file._endPiece - file._startPiece + 1
      var numPiecesPresent = 0
      for (var piece = file._startPiece; piece <= file._endPiece; piece++) {
        if (torrent.bitfield.get(piece)) numPiecesPresent++
      }
      return {
        startPiece: file._startPiece,
        endPiece: file._endPiece,
        numPieces,
        numPiecesPresent
      }
    })
    return {
      torrentKey: torrent.key,
      ready: torrent.ready,
      progress: torrent.progress,
      downloaded: torrent.downloaded,
      downloadSpeed: torrent.downloadSpeed,
      uploadSpeed: torrent.uploadSpeed,
      numPeers: torrent.numPeers,
      length: torrent.length,
      bitfield: torrent.bitfield,
      files: fileProg
    }
  })

  return {
    torrents: torrentProg,
    progress,
    hasActiveTorrents
  }
}

function startServer (infoHash, index) {
  var torrent = client.get(infoHash)
  if (torrent.ready) startServerFromReadyTorrent(torrent, index)
  else torrent.on('ready', () => startServerFromReadyTorrent(torrent, index))
}

function startServerFromReadyTorrent (torrent, index, cb) {
  if (server) return

  // start the streaming torrent-to-http server
  server = torrent.createServer()
  server.listen(0, function () {
    var port = server.address().port
    var urlSuffix = ':' + port + '/' + index
    var info = {
      torrentKey: torrent.key,
      localURL: 'http://localhost' + urlSuffix,
      networkURL: 'http://' + networkAddress() + urlSuffix
    }

    ipc.send('wt-server-running', info)
    ipc.send('wt-server-' + torrent.infoHash, info) // TODO: hack
  })
}

function stopServer () {
  if (!server) return
  server.destroy()
  server = null
}

function getAudioMetadata (infoHash, index) {
  var torrent = client.get(infoHash)
  var file = torrent.files[index]
  musicmetadata(file.createReadStream(), function (err, info) {
    if (err) return
    console.log('got audio metadata for %s: %o', file.name, info)
    ipc.send('wt-audio-metadata', infoHash, index, info)
  })
}

// Gets a WebTorrent handle by torrentKey
// Throws an Error if we're not currently torrenting anything w/ that key
function getTorrent (torrentKey) {
  var ret = client.torrents.find((x) => x.key === torrentKey)
  if (!ret) throw new Error('missing torrent key ' + torrentKey)
  return ret
}

function onError (err) {
  console.log(err)
}
<commit_msg>Show an error when adding an invalid magnet link (#428)<commit_after>// To keep the UI snappy, we run WebTorrent in its own hidden window, a separate
// process from the main window.
console.time('init')

var WebTorrent = require('webtorrent')
var defaultAnnounceList = require('create-torrent').announceList
var deepEqual = require('deep-equal')
var electron = require('electron')
var fs = require('fs-extra')
var musicmetadata = require('musicmetadata')
var networkAddress = require('network-address')
var path = require('path')

var crashReporter = require('../crash-reporter')
var config = require('../config')
var torrentPoster = require('./lib/torrent-poster')

// Report when the process crashes
crashReporter.init()

// Send & receive messages from the main window
var ipc = electron.ipcRenderer

// Force use of webtorrent trackers on all torrents
global.WEBTORRENT_ANNOUNCE = defaultAnnounceList
  .map((arr) => arr[0])
  .filter((url) => url.indexOf('wss://') === 0 || url.indexOf('ws://') === 0)

// Connect to the WebTorrent and BitTorrent networks. WebTorrent Desktop is a hybrid
// client, as explained here: https://webtorrent.io/faq
var client = window.client = new WebTorrent()

// WebTorrent-to-HTTP streaming sever
var server = window.server = null

// Used for diffing, so we only send progress updates when necessary
var prevProgress = window.prevProgress = null

init()

function init () {
  client.on('warning', (err) => ipc.send('wt-warning', null, err.message))
  client.on('error', (err) => ipc.send('wt-error', null, err.message))

  ipc.on('wt-start-torrenting', (e, torrentKey, torrentID, path, fileModtimes) =>
    startTorrenting(torrentKey, torrentID, path, fileModtimes))
  ipc.on('wt-stop-torrenting', (e, infoHash) =>
    stopTorrenting(infoHash))
  ipc.on('wt-create-torrent', (e, torrentKey, options) =>
    createTorrent(torrentKey, options))
  ipc.on('wt-save-torrent-file', (e, torrentKey) =>
    saveTorrentFile(torrentKey))
  ipc.on('wt-generate-torrent-poster', (e, torrentKey) =>
    generateTorrentPoster(torrentKey))
  ipc.on('wt-get-audio-metadata', (e, infoHash, index) =>
    getAudioMetadata(infoHash, index))
  ipc.on('wt-start-server', (e, infoHash, index) =>
    startServer(infoHash, index))
  ipc.on('wt-stop-server', (e) =>
    stopServer())

  ipc.send('ipcReadyWebTorrent')

  setInterval(updateTorrentProgress, 1000)
}

// Starts a given TorrentID, which can be an infohash, magnet URI, etc. Returns WebTorrent object
// See https://github.com/feross/webtorrent/blob/master/docs/api.md#clientaddtorrentid-opts-function-ontorrent-torrent-
function startTorrenting (torrentKey, torrentID, path, fileModtimes) {
  console.log('starting torrent %s: %s', torrentKey, torrentID)
  var torrent
  try {
    torrent = client.add(torrentID, {
      path: path,
      fileModtimes: fileModtimes
    })
  } catch (err) {
    return ipc.send('wt-error', torrentKey, err.message)
  }
  torrent.key = torrentKey
  addTorrentEvents(torrent)
  return torrent
}

function stopTorrenting (infoHash) {
  var torrent = client.get(infoHash)
  torrent.destroy()
}

// Create a new torrent, start seeding
function createTorrent (torrentKey, options) {
  console.log('creating torrent %s', torrentKey, options)
  var torrent = client.seed(options.files, options)
  torrent.key = torrentKey
  addTorrentEvents(torrent)
  ipc.send('wt-new-torrent')
}

function addTorrentEvents (torrent) {
  torrent.on('warning', (err) =>
    ipc.send('wt-warning', torrent.key, err.message))
  torrent.on('error', (err) =>
    ipc.send('wt-error', torrent.key, err.message))
  torrent.on('infoHash', () =>
    ipc.send('wt-infohash', torrent.key, torrent.infoHash))
  torrent.on('metadata', torrentMetadata)
  torrent.on('ready', torrentReady)
  torrent.on('done', torrentDone)

  function torrentMetadata () {
    var info = getTorrentInfo(torrent)
    ipc.send('wt-metadata', torrent.key, info)

    updateTorrentProgress()
  }

  function torrentReady () {
    var info = getTorrentInfo(torrent)
    ipc.send('wt-ready', torrent.key, info)
    ipc.send('wt-ready-' + torrent.infoHash, torrent.key, info) // TODO: hack

    updateTorrentProgress()
  }

  function torrentDone () {
    var info = getTorrentInfo(torrent)
    ipc.send('wt-done', torrent.key, info)

    updateTorrentProgress()

    torrent.getFileModtimes(function (err, fileModtimes) {
      if (err) return onError(err)
      ipc.send('wt-file-modtimes', torrent.key, fileModtimes)
    })
  }
}

// Produces a JSON saveable summary of a torrent
function getTorrentInfo (torrent) {
  return {
    infoHash: torrent.infoHash,
    magnetURI: torrent.magnetURI,
    name: torrent.name,
    path: torrent.path,
    files: torrent.files.map(getTorrentFileInfo),
    bytesReceived: torrent.received
  }
}

// Produces a JSON saveable summary of a file in a torrent
function getTorrentFileInfo (file) {
  return {
    name: file.name,
    length: file.length,
    path: file.path,
    numPiecesPresent: 0,
    numPieces: null
  }
}

// Every time we resolve a magnet URI, save the torrent file so that we never
// have to download it again. Never ask the DHT the same question twice.
function saveTorrentFile (torrentKey) {
  var torrent = getTorrent(torrentKey)
  checkIfTorrentFileExists(torrent.infoHash, function (torrentPath, exists) {
    var fileName = torrent.infoHash + '.torrent'
    if (exists) {
      // We've already saved the file
      return ipc.send('wt-file-saved', torrentKey, fileName)
    }

    // Otherwise, save the .torrent file, under the app config folder
    fs.mkdir(config.CONFIG_TORRENT_PATH, function (_) {
      fs.writeFile(torrentPath, torrent.torrentFile, function (err) {
        if (err) return console.log('error saving torrent file %s: %o', torrentPath, err)
        console.log('saved torrent file %s', torrentPath)
        return ipc.send('wt-file-saved', torrentKey, fileName)
      })
    })
  })
}

// Checks whether we've already resolved a given infohash to a torrent file
// Calls back with (torrentPath, exists). Logs, does not call back on error
function checkIfTorrentFileExists (infoHash, cb) {
  var torrentPath = path.join(config.CONFIG_TORRENT_PATH, infoHash + '.torrent')
  fs.exists(torrentPath, function (exists) {
    cb(torrentPath, exists)
  })
}

// Save a JPG that represents a torrent.
// Auto chooses either a frame from a video file, an image, etc
function generateTorrentPoster (torrentKey) {
  var torrent = getTorrent(torrentKey)
  torrentPoster(torrent, function (err, buf, extension) {
    if (err) return console.log('error generating poster: %o', err)
    // save it for next time
    fs.mkdirp(config.CONFIG_POSTER_PATH, function (err) {
      if (err) return console.log('error creating poster dir: %o', err)
      var posterFileName = torrent.infoHash + extension
      var posterFilePath = path.join(config.CONFIG_POSTER_PATH, posterFileName)
      fs.writeFile(posterFilePath, buf, function (err) {
        if (err) return console.log('error saving poster: %o', err)
        // show the poster
        ipc.send('wt-poster', torrentKey, posterFileName)
      })
    })
  })
}

function updateTorrentProgress () {
  var progress = getTorrentProgress()
  // TODO: diff torrent-by-torrent, not once for the whole update
  if (prevProgress && deepEqual(progress, prevProgress, {strict: true})) {
    return /* don't send heavy object if it hasn't changed */
  }
  ipc.send('wt-progress', progress)
  prevProgress = progress
}

function getTorrentProgress () {
  // First, track overall progress
  var progress = client.progress
  var hasActiveTorrents = client.torrents.some(function (torrent) {
    return torrent.progress !== 1
  })

  // Track progress for every file in each torrent
  // TODO: ideally this would be tracked by WebTorrent, which could do it
  // more efficiently than looping over torrent.bitfield
  var torrentProg = client.torrents.map(function (torrent) {
    var fileProg = torrent.files && torrent.files.map(function (file, index) {
      var numPieces = file._endPiece - file._startPiece + 1
      var numPiecesPresent = 0
      for (var piece = file._startPiece; piece <= file._endPiece; piece++) {
        if (torrent.bitfield.get(piece)) numPiecesPresent++
      }
      return {
        startPiece: file._startPiece,
        endPiece: file._endPiece,
        numPieces,
        numPiecesPresent
      }
    })
    return {
      torrentKey: torrent.key,
      ready: torrent.ready,
      progress: torrent.progress,
      downloaded: torrent.downloaded,
      downloadSpeed: torrent.downloadSpeed,
      uploadSpeed: torrent.uploadSpeed,
      numPeers: torrent.numPeers,
      length: torrent.length,
      bitfield: torrent.bitfield,
      files: fileProg
    }
  })

  return {
    torrents: torrentProg,
    progress,
    hasActiveTorrents
  }
}

function startServer (infoHash, index) {
  var torrent = client.get(infoHash)
  if (torrent.ready) startServerFromReadyTorrent(torrent, index)
  else torrent.on('ready', () => startServerFromReadyTorrent(torrent, index))
}

function startServerFromReadyTorrent (torrent, index, cb) {
  if (server) return

  // start the streaming torrent-to-http server
  server = torrent.createServer()
  server.listen(0, function () {
    var port = server.address().port
    var urlSuffix = ':' + port + '/' + index
    var info = {
      torrentKey: torrent.key,
      localURL: 'http://localhost' + urlSuffix,
      networkURL: 'http://' + networkAddress() + urlSuffix
    }

    ipc.send('wt-server-running', info)
    ipc.send('wt-server-' + torrent.infoHash, info) // TODO: hack
  })
}

function stopServer () {
  if (!server) return
  server.destroy()
  server = null
}

function getAudioMetadata (infoHash, index) {
  var torrent = client.get(infoHash)
  var file = torrent.files[index]
  musicmetadata(file.createReadStream(), function (err, info) {
    if (err) return
    console.log('got audio metadata for %s: %o', file.name, info)
    ipc.send('wt-audio-metadata', infoHash, index, info)
  })
}

// Gets a WebTorrent handle by torrentKey
// Throws an Error if we're not currently torrenting anything w/ that key
function getTorrent (torrentKey) {
  var ret = client.torrents.find((x) => x.key === torrentKey)
  if (!ret) throw new Error('missing torrent key ' + torrentKey)
  return ret
}

function onError (err) {
  console.log(err)
}
<|endoftext|>