<commit_before>const SHA3 = require('sha3')
const ec = require('elliptic').ec('secp256k1')
const assert = require('assert')
const rlp = require('rlp')
const BN = require('bn.js')

//the max interger that this VM can handle
exports.MAX_INTEGER = new BN('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)
exports.TWO_POW256 = new BN('115792089237316195423570985008687907853269984665640564039457584007913129639936')

//hex string of SHA3-256 hash of `null`
exports.SHA3_NULL = 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'

//SHA3-256 hash of the rlp of []
exports.SHA3_RLP_ARRAY = '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347'

//SHA3-256 hash of the rlp of `null`
exports.SHA3_RLP = '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'

exports.BN = BN
exports.rlp = rlp

/**
 * Returns a buffer filled with 0s
 * @method zeros
 * @param {Integer} bytes  the number of bytes the buffer should be
 * @return {Buffer}
 */
exports.zeros = function(bytes) {
  var buf = new Buffer(bytes);
  buf.fill(0);
  return buf;
}

/**
 * pads an array of buffer with leading zeros till it has `length` bytes
 * @method pad
 * @param {Buffer|Array} array
 * @param {Integer}  length the number of bytes the output should be
 * @return {Buffer|Array}
 */
exports.pad = function(msg, length) {
  if (msg.length < length) {
    var buf = exports.zeros(length)
    msg.copy(buf, length - msg.length)
    return buf
  }
  return msg.slice(-length)
}

/**
 * Trims leading zeros from a buffer or an array
 * @method unpad
 * @param {Buffer|Array|String}
 * @return {Buffer|Array|String}
 */
exports.unpad = function(a) {
  var first = a[0]
  while (a.length > 0 && first.toString() === '0') {
    a = a.slice(1)
    first = a[0]
  }
  return a
}

/**
 * Converts an integer into a hex string
 * @method intToHex
 * @param {Number}
 * @return {String}
 */
exports.intToHex = function(i) {
  assert(i % 1 === 0, 'number is not a interger')
  assert(i >= 0, 'number must be positive')
  var hex = i.toString(16)
  if (hex.length % 2)
    hex = '0' + hex

  return hex
}

/**
 * Converts an integer to a buffer
 * @method intToBuffer
 * @param {Number}
 * @return {Buffer}
 */
exports.intToBuffer = function(i) {
  var hex = exports.intToHex(i)
  return new Buffer(hex, 'hex')
}

/**
 * Converts a buffer to an Interger
 * @method bufferToInt
 * @par[MaÅam {B[M`Êuffer}
 * @return {Number}
 */
exports.bufferToInt = function(buf) {
  if (buf.length === 0)
    return 0

  return parseInt(buf.toString('hex'), 16)
}

/**
 * interpets a buffer as a signed integer and returns a bignum
 * @method fromSigned
 * @param {Buffer} num
 * @return {BN}
 */
exports.fromSigned = function(num) {
  if (num.length === 32 && num[0] >= 128)
    return new BN(num).sub(exports.TWO_POW256)

  return new BN(num)
}

/**
 * Converts a bignum to an unsigned interger and returns it as a buffer
 * @method toUnsigned
 * @param {Bignum} num
 * @return {Buffer}
 */
exports.toUnsigned = function(num) {
  if (num.cmpn(0) === -1)
    return new Buffer(num.add(exports.TWO_POW256).toArray())

  return new Buffer(num.toArray())
}

exports.sha3 = function(a, bytes) {
  if(!bytes) bytes = 256

  var h = new SHA3.SHA3Hash(bytes)
  if (a)
    h.update(a)

  return new Buffer(h.digest('hex'), 'hex')
}

/**
 * Returns the ethereum address of a given public key
 * @method pubToAddress
 * @param {Buffer}
 * @return {Buffer}
 */
exports.pubToAddress = exports.publicToAddress = function(pubKey) {
  var hash = new SHA3.SHA3Hash(256)
  hash.update(pubKey.slice(-64))
  return new Buffer(hash.digest('hex').slice(-40), 'hex')
}

/**
 * Returns the ethereum public key of a given private key
 * @method privateToPublic
 * @param {Buffer} privateKey
 * @return {Buffer}
 */
var privateToPublic = exports.privateToPublic = function(privateKey){
  privateKey = new BN(privateKey)
  var key = ec.keyFromPrivate(privateKey).getPublic().toJSON()
  return new Buffer(key[0].toArray().concat(key[1].toArray()))
}

/**
 * Returns the ethereum address of a given private key
 * @method privateToAddress
 * @param {Buffer} privateKey
 * @return {Buffer}
 */
exports.privateToAddress = function(privateKey){
  return exports.publicToAddress(privateToPublic(privateKey))
}

/**
 * Generates a new address
 * @method generateAddress
 * @param {Buffer} from the address which is creating this new address
 * @param {Buffer} nonce the nonce of the from account
 */
exports.generateAddress = function(from, nonce) {
  nonce = new Buffer(new BN(nonce).subn(1).toArray())

  if (nonce.toString('hex') === '00')
    nonce = 0;

  var hash = exports.sha3(rlp.encode([new Buffer(from, 'hex'), nonce]))
  return hash.slice(12)
}

/**
 * defines properties on a `Object`
 * @method defineProperties
 * @para[M`Êm {Object} self the `Object` to define properties on
 * @param {Array} fields an array fields to define
 */
exports.defineProperties = function(self, fields, data) {

  self.raw = []
  self._fields = []

  self.toJSON = function(label){
    if (label) {
      var obj = {}

      for (var prop in this) {
        if (typeof this[prop] !== 'function' && prop !== 'raw' && prop !== '_fields')
          obj[prop] = this[prop].toString('hex')
      }
      return obj
    }

    return exports.baToJSON(this.raw)
  }

  fields.forEach(function(field, i) {
    self._fields.push(field.name)
    Object.defineProperty(self, field.name, {
      enumerable: true,
      configurable: true,
      get: function() {
        return this.raw[i]
      },
      set: function(v) {
        if (!Buffer.isBuffer(v)) {
          if (typeof v === 'string')
            v = new Buffer(padToEven(exports.stripHexPrefix(v)), 'hex')
          else if (typeof v === 'number')
            v = exports.intToBuffer(v)
          else if (v === null || v === undefined)
            v = new Buffer([])
          else if (v.toArray)
            //converts a BN to a Buffer
            v = new Buffer(v.toArray())
          else
            throw new Error('invalid type')
        }

        if(v.toString('hex') === '00' &&  field.noZero)
          v = new Buffer([])

        if(field.word && new BN(v).cmp(exports.MAX_INTEGER) === 1)
          throw(new Error('to large of value'))

        if(!(field.empty && v.length === 0) && field.pad && v.length < field.length)
          v = exports.pad(v, field.length)

        if(field.allowLess && field.length){
          assert(field.length >= v.length)
        } else if (!(field.empty && v.length === 0) && field.length)
          assert(field.length === v.length, 'The field ' + field.name + 'must have byte length of ' + field.length)

        this.raw[i] = v
      }
    })

    if(field.default)
      self[field.name] = field.default
  })

  if (data) {
    if (Buffer.isBuffer(data))
      data = rlp.decode(data)

    if (Array.isArray(data)) {
      if(data.length > self._fields.length)
        throw(new Error('wrong number of fields in data'))

      //make sure all the items are buffers
      data.forEach(function(d, i) {
         self[self._fields[i]] = typeof d === 'string' ? new Buffer(d, 'hex') : d
      })
    } else {
      for (var prop in data) {
        if (self._fields.indexOf(prop) !== -1)
          self[prop] = data[prop]
      }
    }
  }
}

/**
 * Print a Buffer Array
 * @method printBA
 * @param {Buffer|Array}
 */
exports.printBA = function(ba) {
  if (Buffer.isBuffer(ba)) {
    if (ba.length === 0)
      console.log('new Buffer(0)')
    else
      console.log('new Buffer(\'' + ba.toString('hex') + '\', \'hex\')')
  } else if (ba instanceof Array) {
    console.log('[')
    for (var i = 0; i < ba.length; i++) {
      exports.printBA(ba[i])
      console.log(',')
    }
    console.log(']')
  } else
    console.log(ba)
}

/**
 * converts a buffer array to JSON
 * @method BAToJSON
 * @param {Buffer|Array}
 */
exports.baToJSON = function(ba) {
  if (Buffer.isBuffer(ba)) {
    return ba.toString('hex')
  } else if (ba instanceof Array) {
    var array = []
    for (var i = 0; i < ba.length; i++) {
      array.push(exports.baToJSON(ba[i]))
    }
    return array
  }
}

//Returns a Boolean on whether or not the a sting starts with 0x
exports.isHexPrefixed = function(str){
  return str.slice(0, 2) === '0x'
}

//Removes 0x from a given String
exports.stripHexPrefix = function(str){
  if (typeof str !== 'string')
     return str
  return exports.isHexPrefixed(str) ?  str.slice(2) : str
}

//Adds 0x to a given string if it does not already start with 0x
exports.addHexPrefix = function(str){
  if (typeof str !== 'string')
     return str
  return exports.isHexPrefixed(str) ? '0x' + str : str
}

function padToEven(a){
  if (a.length % 2) a = '0' + a;
  return a
}
<commit_msg>added type check<commit_after>const SHA3 = require('sha3')
const ec = require('elliptic').ec('secp256k1')
const assert = require('assert')
const rlp = require('rlp')
const BN = require('bn.js')

//the max interger that this VM can handle
exports.MAX_INTEGER = new BN('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)
exports.TWO_POW256 = new BN('115792089237316195423570985008687907853269984665640564039457584007913129639936')

//hex string of SHA3-256 hash of `null`
exports.SHA3_NULL = 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'

//SHA3-256 hash of the rlp of []
exports.SHA3_RLP_ARRAY = '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347'

//SHA3-256 hash of the rlp of `null`
exports.SHA3_RLP = '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'

exports.BN = BN
exports.rlp = rlp

/**
 * Returns a buffer filled with 0s
 * @method zeros
 * @param {Integer} bytes  the number of bytes the buffer should be
 * @return {Buffer}
 */
exports.zeros = function(bytes) {
  var buf = new Buffer(bytes);
  buf.fill(0);
  return buf;
}

/**
 * pads an array of buffer with leading zeros till it has `length` bytes
 * @method pad
 * @param {Buffer|Array} array
 * @param {Integer}  length the number of bytes the output should be
 * @return {Buffer|Array}
 */
exports.pad = function(msg, length) {
  if (msg.length < length) {
    var buf = exports.zeros(length)
    msg.copy(buf, length - msg.length)
    return buf
  }
  return msg.slice(-length)
}

/**
 * Trims leading zeros from a buffer or an array
 * @method unpad
 * @param {Buffer|Array|String}
 * @return {Buffer|Array|String}
 */
exports.unpad = function(a) {
  var first = a[0]
  while (a.length > 0 && first.toString() === '0') {
    a = a.slice(1)
    first = a[0]
  }
  return a
}

/**
 * Converts an integer into a hex string
 * @method intToHex
 * @param {Number}
 * @return {String}
 */
exports.intToHex = function(i) {
  assert(i % 1 === 0, 'number is not a interger')
  assert(i >= 0, 'number must be positive')
  var hex = i.toString(16)
  if (hex.length % 2)
    hex = '0' + hex

  return hex
}

/**
 * Converts an integer to a buffer
 * @method intToBuffer
 * @param {Number}
 * @return {Buffer}
 */
exports.intToBuffer = function(i) {
  var hex = exports.intToHex(i)
  return new Buffer(hex, 'hex')
}

/**
 * Converts a buffer to an Interger
 * @method bufferToInt
 * @par[MaÅam {B[M`Êuffer}
 * @return {Number}
 */
exports.bufferToInt = function(buf) {
  if (buf.length === 0)
    return 0

  return parseInt(buf.toString('hex'), 16)
}

/**
 * interpets a buffer as a signed integer and returns a bignum
 * @method fromSigned
 * @param {Buffer} num
 * @return {BN}
 */
exports.fromSigned = function(num) {
  if (num.length === 32 && num[0] >= 128)
    return new BN(num).sub(exports.TWO_POW256)

  return new BN(num)
}

/**
 * Converts a bignum to an unsigned interger and returns it as a buffer
 * @method toUnsigned
 * @param {Bignum} num
 * @return {Buffer}
 */
exports.toUnsigned = function(num) {
  if (num.cmpn(0) === -1)
    return new Buffer(num.add(exports.TWO_POW256).toArray())

  return new Buffer(num.toArray())
}

exports.sha3 = function(a, bytes) {
  if(!bytes) bytes = 256

  var h = new SHA3.SHA3Hash(bytes)
  if (a)
    h.update(a)

  return new Buffer(h.digest('hex'), 'hex')
}

/**
 * Returns the ethereum address of a given public key
 * @method pubToAddress
 * @param {Buffer}
 * @return {Buffer}
 */
exports.pubToAddress = exports.publicToAddress = function(pubKey) {
  var hash = new SHA3.SHA3Hash(256)
  hash.update(pubKey.slice(-64))
  return new Buffer(hash.digest('hex').slice(-40), 'hex')
}

/**
 * Returns the ethereum public key of a given private key
 * @method privateToPublic
 * @param {Buffer} privateKey
 * @return {Buffer}
 */
var privateToPublic = exports.privateToPublic = function(privateKey){
  privateKey = new BN(privateKey)
  var key = ec.keyFromPrivate(privateKey).getPublic().toJSON()
  return new Buffer(key[0].toArray().concat(key[1].toArray()))
}

/**
 * Returns the ethereum address of a given private key
 * @method privateToAddress
 * @param {Buffer} privateKey
 * @return {Buffer}
 */
exports.privateToAddress = function(privateKey){
  return exports.publicToAddress(privateToPublic(privateKey))
}

/**
 * Generates a new address
 * @method generateAddress
 * @param {Buffer} from the address which is creating this new address
 * @param {Buffer} nonce the nonce of the from account
 */
exports.generateAddress = function(from, nonce) {
  nonce = new Buffer(new BN(nonce).subn(1).toArray())

  if (nonce.toString('hex') === '00')
    nonce = 0;

  var hash = exports.sha3(rlp.encode([new Buffer(from, 'hex'), nonce]))
  return hash.slice(12)
}

/**
 * defines properties on a `Object`
 * @method defineProperties
 * @para[M`Êm {Object} self the `Object` to define properties on
 * @param {Array} fields an array fields to define
 */
exports.defineProperties = function(self, fields, data) {

  self.raw = []
  self._fields = []

  self.toJSON = function(label){
    if (label) {
      var obj = {}

      for (var prop in this) {
        if (typeof this[prop] !== 'function' && prop !== 'raw' && prop !== '_fields')
          obj[prop] = this[prop].toString('hex')
      }
      return obj
    }

    return exports.baToJSON(this.raw)
  }

  fields.forEach(function(field, i) {
    self._fields.push(field.name)
    Object.defineProperty(self, field.name, {
      enumerable: true,
      configurable: true,
      get: function() {
        return this.raw[i]
      },
      set: function(v) {
        if (!Buffer.isBuffer(v)) {
          if (typeof v === 'string')
            v = new Buffer(padToEven(exports.stripHexPrefix(v)), 'hex')
          else if (typeof v === 'number')
            v = exports.intToBuffer(v)
          else if (v === null || v === undefined)
            v = new Buffer([])
          else if (v.toArray)
            //converts a BN to a Buffer
            v = new Buffer(v.toArray())
          else
            throw new Error('invalid type')
        }

        if(v.toString('hex') === '00' &&  field.noZero)
          v = new Buffer([])

        if(field.word && new BN(v).cmp(exports.MAX_INTEGER) === 1)
          throw(new Error('to large of value'))

        if(!(field.empty && v.length === 0) && field.pad && v.length < field.length)
          v = exports.pad(v, field.length)

        if(field.allowLess && field.length){
          assert(field.length >= v.length)
        } else if (!(field.empty && v.length === 0) && field.length)
          assert(field.length === v.length, 'The field ' + field.name + 'must have byte length of ' + field.length)

        this.raw[i] = v
      }
    })

    if(field.default)
      self[field.name] = field.default
  })

  if (data) {
    if (Buffer.isBuffer(data))
      data = rlp.decode(data)

    if (Array.isArray(data)) {
      if(data.length > self._fields.length)
        throw(new Error('wrong number of fields in data'))

      //make sure all the items are buffers
      data.forEach(function(d, i) {
         self[self._fields[i]] = typeof d === 'string' ? new Buffer(d, 'hex') : d
      })
    } else if(typeof data === 'object') {
      for (var prop in data) {
        if (self._fields.indexOf(prop) !== -1)
          self[prop] = data[prop]
      }
    } else{
      throw new Error('invalid data')
    }
  }
}

/**
 * Print a Buffer Array
 * @method printBA
 * @param {Buffer|Array}
 */
exports.printBA = function(ba) {
  if (Buffer.isBuffer(ba)) {
    if (ba.length === 0)
      console.log('new Buffer(0)')
    else
      console.log('new Buffer(\'' + ba.toString('hex') + '\', \'hex\')')
  } else if (ba instanceof Array) {
    console.log('[')
    for (var i = 0; i < ba.length; i++) {
      exports.printBA(ba[i])
      console.log(',')
    }
    console.log(']')
  } else
    console.log(ba)
}

/**
 * converts a buffer array to JSON
 * @method BAToJSON
 * @param {Buffer|Array}
 */
exports.baToJSON = function(ba) {
  if (Buffer.isBuffer(ba)) {
    return ba.toString('hex')
  } else if (ba instanceof Array) {
    var array = []
    for (var i = 0; i < ba.length; i++) {
      array.push(exports.baToJSON(ba[i]))
    }
    return array
  }
}

//Returns a Boolean on whether or not the a sting starts with 0x
exports.isHexPrefixed = function(str){
  return str.slice(0, 2) === '0x'
}

//Removes 0x from a given String
exports.stripHexPrefix = function(str){
  if (typeof str !== 'string')
     return str
  return exports.isHexPrefixed(str) ?  str.slice(2) : str
}

//Adds 0x to a given string if it does not already start with 0x
exports.addHexPrefix = function(str){
  if (typeof str !== 'string')
     return str
  return exports.isHexPrefixed(str) ? '0x' + str : str
}

function padToEven(a){
  if (a.length % 2) a = '0' + a;
  return a
}
<|endoftext|>


<commit_before>var containercss = {
  position : "relative",
  // "margin" : "20px 0 0 0",
  // "background" : "gray",
  overflow : "hidden"
};

var canvascss = {
  position : "absolute",
  left : 0,
  top : 0,
  "z-index" : 100,
  cursor : "move"
};

// Annotations - as distinct on the canvas
function Annotation(type) {
  this.valid = false;
  this.pts = [{x:0,y:0}, {x:0,y:0}];
  this.type = type;
};

//////////////////////////////////////////////////////
///// Annotator Object Definition ////////////////////

// Creates a new annotator (to be bound to a DOM object)
function Annotator(src, w, h) {
  // Parameters
  this.src = src;
  this.w = w;
  this.h = h;

  // Controls
  this.zoomin = null;
  this.zoomout = null;
  this.pan = null;
  this.annotate = null;
  this.attType = null;

  // Components
  this.container = null
  this.canvas = null;

  // Annotations
  this.att = null;
  this.atts = new Array();
  this.selectedType = "rect";

  // Drawing
  this.g = null;

  // Transform info
  this.curScale = 0.9;
  this.xOffs = 0;
  this.yOffs = 0;

  // Canvas ops
  this.x0 = 0;
  this.x1 = 0;
  this.y0 = 0;
  this.y1 = 0;
  this.curOp = "pan";
  this.active = false;
  this.polyC = 0;
}

Annotator.fn = Annotator.prototype;

//////////////////////////////////////////////////////
// Instantiates an annotator inside a DOM object
Annotator.fn.build = function($parent) {
  // Register and generate annotator components
  $parent.addClass("annotator");
  $parent.data("Annotator", this);

  // Controls
  this.zoomin    = $('<button id="zoomin">+</button>').appendTo($parent);
  this.zoomout   = $('<button id="zoomout">-</button>').appendTo($parent);
  this.pan       = $('<button id="pan">Pan</button>').appendTo($parent);
  this.annotate  = $('<button id="annot">Annotate</button>').appendTo($parent);

  this.attType      = $('<select id="typesel"></select>')
                      .html('<option>Box</option><option>Polygon</option>')
                      .appendTo($parent);

  // Canvas container
  this.container = $('

')
                      .css(containercss)
                      .width(this.w)
                      .height(this.h)
                      .appendTo($parent);

  // Load the image
  this.img = $('')
                      .width(this.w)
                      .height(this.h)
                      .hide()
                      .appendTo($parent);

  // The drawing canvas
  this.canvas = $('<canvas>Unsupported browser.</canvas>')
                      .css(canvascss)
                      .appendTo(this.container);
  // Resize canvas
  this.canvas[0].width = this.w;
  this.canvas[0].height = this.h;

  // Get the drawing context
  this.g = this.canvas[0].getContext("2d");

  var a = this;

  // Zoom control
  this.zoomin.click(function(){a.zoom(1.25);});
  this.zoomout.click(function(){a.zoom(0.8);});

  // Operation selection
  this.attType.change(function() {
    var str = $(this).val();

    if (str == "Box") {
      a.selectedType = "rect";
    }
    else if (str == "Polygon") {
      a.selectedType = "poly";
    }
  });

  this.pan.click(function(){
    a.curOp = "pan";
    a.canvas.css("cursor", "move");
  });

  this.annotate.click(function(){
    a.curOp = "annotate";
    a.canvas.css("cursor", "crosshair");
  });

  // Mouse down - start drawing or panning
  this.canvas.mousedown(function(e){
    a.mbDown(e.pageX, e.pageY);
  });

  // Movement continues draw/pan as long as the mouse button is held
  this.canvas.mousemove(function(e){
    a.mMove(e.pageX, e.pageY);
  });

  // Operation end
  this.canvas.mouseup(function(){
    a.mbUp();
  });

  // We have to wait for the image to load before we can use it
  this.img.load(function(){
    a.doTransform();
  });
}

//////////////////////////////////////////////////////
// Mouse control
Annotator.fn.mbDown = function(x, y) {
  if (!this.active) {
    var offset = this.canvas.offset();
    this.x1 = this.x0 = x - offset.left;
    this.y1 = this.y0 = y - offset.top;
    this.active = true;

    if (this.curOp == "annotate") {
      this.att = new Annotation(this.selectedType);
      this.att.valid = true;
      this.atts.push(this.att);

      if (this.att.type == "poly") {
        this.att.pts[0] = new ptToImg(this.x0, this.y0);
        this.polyC = 1;
      }
    }
  }
}

Annotator.fn.mbUp = function() {
  // End ONLY if dragged
  if (this.curOp == "annotate") {
    if (this.x0 != this.x1 && this.y0 != this.y1) {
      if (this.att.type == "rect") this.active = false;
      else if (this.att.type == "poly") {
        this.x0 = this.x1;
        this.y0 = this.y1;
        this.polyC++;
      }
    }
    else if (this.att.type == "poly" && this.polyC > 1) {
      this.active = false;
    }
  }
  else {
    this.active = false;
  }
}

Annotator.fn.mMove = function(x, y) {
  if (!this.active) return;

  var offset = this.canvas.offset();
  this.x1 = x - offset.left;
  this.y1 = y - offset.top;

  var dx = this.x1 - this.x0;
  var dy = this.y1 - this.y0;

  if (this.curOp == "pan") {
    // Panning the image
    this.doPan(dx, dy);
    this.x0 = this.x1;
    this.y0 = this.y1;
  }
  else if (this.curOp == "annotate") {
    // Annotation - in image space
    var pt1 = new ptToImg(this, this.x0, this.y0);
    var pt2 = new ptToImg(this, this.x1, this.y1);

    if (this.att.type == "rect") {
      this.att.pts[0] = pt1;
      this.att.pts[1] = pt2;
    }
    else if (this.att.type == "poly") {
      // Save next point
      this.att.pts[this.polyC] = pt2;
    }

    // Redraw
    this.doTransform();
  }
}

// Updates an existing annotator with a new image
// (Also resets the pan/zoom and annotations)
Annotator.fn.update = function(src, w, h) {
  this.src = src;
  this.w = w;
  this.h = h;

  // Reloading & resizing
  this.container.width(w).height(h);
  this.img.attr("src", src).width(w).height(h);

  // Reset pan/zoom
  this.curScale = 0.9;
  this.xOffs = 0;
  this.yOffs = 0;

  // Reset annotation
  this.att = null;
  this.atts = new Array();

  // Resize canvas
  this.canvas[0].width = w;
  this.canvas[0].height = h;
}

// Canvas re-draw op
Annotator.fn.repaint = function() {
  var g = this.g;
  var $img = this.img;

  var w = $img.width();
  var h = $img.height();

  // Drop shadow
  g.shadowColor = "#555";
  g.shadowBlur = 40;

  // Draw the image
  g.drawImage($img[0], -w/2, -h/2);

  // Annotation
  for (var i = 0; i < this.atts.length; i++) {
    this.drawAtt(this.atts[i]);
  }
}

// Annotation draw op
Annotator.fn.drawAtt = function(att) {
  var g = this.g;

  if (!att.valid) return;

  g.shadowColor = "#222";
  g.shadowBlur = 5;
  g.strokeStyle = "white";
  g.lineWidth = 1 / this.curScale;
  g.fillStyle = "white";

  // Box drawing (2-point)
  if (att.type == "rect") {
    var x0 = att.pts[0].x;
    var y0 = att.pts[0].y;
    var x1 = att.pts[1].x;
    var y1 = att.pts[1].y;

    var dx = Math.abs(x1-x0);
    var dy = Math.abs(y1-y0);
    var x = Math.min(x0, x1);
    var y = Math.min(y0, y1);

    g.strokeRect(x, y, dx, dy);

    this.drawPt({x:x0, y:y0});
    this.drawPt({x:x0, y:y1});
    this.drawPt({x:x1, y:y0});
    this.drawPt({x:x1, y:y1});
  }
  // Polygon drawing (n-point)
  else if (att.type == "poly") {
    g.beginPath();
    g.moveTo(att.pts[0].x, att.pts[0].y);

    for (var i = 1; i < att.pts.length; i++) {
      g.lineTo(att.pts[i].x, att.pts[i].y);
    }

    g.lineTo(att.pts[0].x, att.pts[0].y);
    g.stroke();

    for (var i = 0; i < att.pts.length; i++) {
      this.drawPt(att.pts[i]);
    }
  }
}

// Point drawing util
Annotator.fn.drawPt = function(pt) {
  var g = this.g;
  g.beginPath();
  g.arc(pt.x, pt.y, 2.5/this.curScale, 0, 2*Math.PI, false);
  g.fill();
}

// General transform op
Annotator.fn.doTransform = function() {
  var g = this.g;

  // Reset xform & clear
  g.setTransform(1,0,0,1,0,0);
  g.clearRect(0, 0, this.w, this.h);

  // To draw in position with scaling,
  // move to position (translate), then
  // scale before drawing at (0, 0)
  g.translate(this.w/2 + this.xOffs, this.h/2 + this.yOffs);
  g.scale(this.curScale, this.curScale);

  this.repaint();
}

// Pan op
Annotator.fn.doPan = function(x, y) {
  // New offset
  var margin = 100;

  this.xOffs += x;
  this.yOffs += y;

  var xLim = (this.w/2)*this.curScale;
  var yLim = (this.h/2)*this.curScale;

  if (this.xOffs >  xLim) this.xOffs =  xLim;
  if (this.xOffs < -xLim) this.xOffs = -xLim;
  if (this.yOffs >  yLim) this.yOffs =  yLim;
  if (this.yOffs < -yLim) this.yOffs = -yLim;

  this.doTransform();
}

// Zoom op
Annotator.fn.zoom = function(scale) {
  // New scaling level
  this.curScale *= scale;
  if (this.curScale < 0.9) this.curScale = 0.9;
  this.doTransform();
}

// Util - canvas to image space
function ptToImg(a, x, y) {
  var x = (x-a.w/2-a.xOffs)/a.curScale;
  var y = (y-a.h/2-a.yOffs)/a.curScale;

  return {x:x,y:y};
}

//////////////////////////////////////////////////////
///// API Definition /////////////////////////////////

(function( $ ) {
  // The annotator function - appplicable to any jQuery object collection
  $.fn.annotator = function(src, width, height) {
    return this.each(function() {
      // Check for annotator class
      $parent = $(this);
      var a;

      // Update if we're passed an existing annotator
      if ($parent.hasClass("annotator")) {
        a = $parent.data("Annotator");
        a.update(src, width, height);
      }
      else {
        a = new Annotator(src, width, height);
        a.build($parent);
      }
    });
  };
}(jQuery));
<commit_msg>fix invalid first polygon point<commit_after>var containercss = {
  position : "relative",
  // "margin" : "20px 0 0 0",
  // "background" : "gray",
  overflow : "hidden"
};

var canvascss = {
  position : "absolute",
  left : 0,
  top : 0,
  "z-index" : 100,
  cursor : "move"
};

// Annotations - as distinct on the canvas
function Annotation(type) {
  this.valid = false;
  this.pts = [{x:0,y:0}, {x:0,y:0}];
  this.type = type;
};

//////////////////////////////////////////////////////
///// Annotator Object Definition ////////////////////

// Creates a new annotator (to be bound to a DOM object)
function Annotator(src, w, h) {
  // Parameters
  this.src = src;
  this.w = w;
  this.h = h;

  // Controls
  this.zoomin = null;
  this.zoomout = null;
  this.pan = null;
  this.annotate = null;
  this.attType = null;

  // Components
  this.container = null
  this.canvas = null;

  // Annotations
  this.att = null;
  this.atts = new Array();
  this.selectedType = "rect";

  // Drawing
  this.g = null;

  // Transform info
  this.curScale = 0.9;
  this.xOffs = 0;
  this.yOffs = 0;

  // Canvas ops
  this.x0 = 0;
  this.x1 = 0;
  this.y0 = 0;
  this.y1 = 0;
  this.curOp = "pan";
  this.active = false;
  this.polyC = 0;
}
Annotator.fn = Annotator.prototype;

// Updates an existing annotator with a new image
// (Also resets the pan/zoom and annotations)
Annotator.fn.update = function(src, w, h) {
  this.src = src;
  this.w = w;
  this.h = h;

  // Reloading & resizing
  this.container.width(w).height(h);
  this.img.attr("src", src).width(w).height(h);

  // Reset pan/zoom
  this.curScale = 0.9;
  this.xOffs = 0;
  this.yOffs = 0;

  // Reset annotation
  this.att = null;
  this.atts = new Array();

  // Resize canvas
  this.canvas[0].width = w;
  this.canvas[0].height = h;
}

//////////////////////////////////////////////////////
// Instantiates an annotator inside a DOM object
Annotator.fn.build = function($parent) {
  // Register and generate annotator components
  $parent.addClass("annotator");
  $parent.data("Annotator", this);

  // Controls
  this.zoomin    = $('<button id="zoomin">+</button>').appendTo($parent);
  this.zoomout   = $('<button id="zoomout">-</button>').appendTo($parent);
  this.pan       = $('<button id="pan">Pan</button>').appendTo($parent);
  this.annotate  = $('<button id="annot">Annotate</button>').appendTo($parent);

  this.attType      = $('<select id="typesel"></select>')
                      .html('<option>Box</option><option>Polygon</option>')
                      .appendTo($parent);

  // Canvas container
  this.container = $('

')
                      .css(containercss)
                      .width(this.w)
                      .height(this.h)
                      .appendTo($parent);

  // Load the image
  this.img = $('')
                      .width(this.w)
                      .height(this.h)
                      .hide()
                      .appendTo($parent);

  // The drawing canvas
  this.canvas = $('<canvas>Unsupported browser.</canvas>')
                      .css(canvascss)
                      .appendTo(this.container);
  // Resize canvas
  this.canvas[0].width = this.w;
  this.canvas[0].height = this.h;

  // Get the drawing context
  this.g = this.canvas[0].getContext("2d");

  var a = this; // loss of context when defining callbacks

  // Zoom control
  this.zoomin.click(function(){a.zoom(1.25);});
  this.zoomout.click(function(){a.zoom(0.8);});

  // Operation selection
  this.attType.change(function() {
    var str = $(this).val();

    if (str == "Box") {
      a.selectedType = "rect";
    }
    else if (str == "Polygon") {
      a.selectedType = "poly";
    }
  });

  this.pan.click(function(){
    a.curOp = "pan";
    a.canvas.css("cursor", "move");
  });

  this.annotate.click(function(){
    a.curOp = "annotate";
    a.canvas.css("cursor", "crosshair");
  });

  // Mouse down - start drawing or panning
  this.canvas.mousedown(function(e){
    a.mbDown(e.pageX, e.pageY);
  });

  // Movement continues draw/pan as long as the mouse button is held
  this.canvas.mousemove(function(e){
    a.mMove(e.pageX, e.pageY);
  });

  // Operation end
  this.canvas.mouseup(function(){
    a.mbUp();
  });

  // We have to wait for the image to load before we can use it
  this.img.load(function(){
    a.doTransform();
  });
}

//////////////////////////////////////////////////////
// Mouse control
Annotator.fn.mbDown = function(x, y) {
  if (!this.active) {
    var offset = this.canvas.offset();
    this.x1 = this.x0 = x - offset.left;
    this.y1 = this.y0 = y - offset.top;
    this.active = true;

    if (this.curOp == "annotate") {
      this.att = new Annotation(this.selectedType);
      this.att.valid = true;
      this.atts.push(this.att);

      if (this.att.type == "poly") {
        this.att.pts[0] = new ptToImg(this, this.x0, this.y0);
        this.polyC = 1;
      }
    }
  }
}

Annotator.fn.mbUp = function() {
  // End ONLY if dragged
  if (this.curOp == "annotate") {
    if (this.x0 != this.x1 && this.y0 != this.y1) {
      if (this.att.type == "rect") this.active = false;
      else if (this.att.type == "poly") {
        this.x0 = this.x1;
        this.y0 = this.y1;
        this.polyC++;
      }
    }
    else if (this.att.type == "poly" && this.polyC > 1) {
      this.active = false;
    }
  }
  else {
    this.active = false;
  }
}

Annotator.fn.mMove = function(x, y) {
  if (!this.active) return;

  var offset = this.canvas.offset();
  this.x1 = x - offset.left;
  this.y1 = y - offset.top;

  var dx = this.x1 - this.x0;
  var dy = this.y1 - this.y0;

  if (this.curOp == "pan") {
    // Panning the image
    this.doPan(dx, dy);
    this.x0 = this.x1;
    this.y0 = this.y1;
  }
  else if (this.curOp == "annotate") {
    // Annotation - in image space
    var pt1 = new ptToImg(this, this.x0, this.y0);
    var pt2 = new ptToImg(this, this.x1, this.y1);

    if (this.att.type == "rect") {
      this.att.pts[0] = pt1;
      this.att.pts[1] = pt2;
    }
    else if (this.att.type == "poly") {
      // Save next point
      this.att.pts[this.polyC] = pt2;
    }

    // Redraw
    this.doTransform();
  }
}

//////////////////////////////////////////////////////
// Canvas Operations

// Canvas re-draw op
Annotator.fn.repaint = function() {
  var g = this.g;
  var $img = this.img;

  var w = $img.width();
  var h = $img.height();

  // Drop shadow
  g.shadowColor = "#555";
  g.shadowBlur = 40;

  // Draw the image
  g.drawImage($img[0], -w/2, -h/2);

  // Annotation
  for (var i = 0; i < this.atts.length; i++) {
    this.drawAtt(this.atts[i]);
  }
}

// Annotation draw op
Annotator.fn.drawAtt = function(att) {
  var g = this.g;

  if (!att.valid) return;

  g.shadowColor = "#222";
  g.shadowBlur = 5;
  g.strokeStyle = "white";
  g.lineWidth = 1 / this.curScale;
  g.fillStyle = "white";

  // Box drawing (2-point)
  if (att.type == "rect") {
    var x0 = att.pts[0].x;
    var y0 = att.pts[0].y;
    var x1 = att.pts[1].x;
    var y1 = att.pts[1].y;

    var dx = Math.abs(x1-x0);
    var dy = Math.abs(y1-y0);
    var x = Math.min(x0, x1);
    var y = Math.min(y0, y1);

    g.strokeRect(x, y, dx, dy);

    this.drawPt({x:x0, y:y0});
    this.drawPt({x:x0, y:y1});
    this.drawPt({x:x1, y:y0});
    this.drawPt({x:x1, y:y1});
  }
  // Polygon drawing (n-point)
  else if (att.type == "poly") {
    g.beginPath();
    g.moveTo(att.pts[0].x, att.pts[0].y);

    for (var i = 1; i < att.pts.length; i++) {
      g.lineTo(att.pts[i].x, att.pts[i].y);
    }

    g.lineTo(att.pts[0].x, att.pts[0].y);
    g.stroke();

    for (var i = 0; i < att.pts.length; i++) {
      this.drawPt(att.pts[i]);
    }
  }
}

// Point drawing util
Annotator.fn.drawPt = function(pt) {
  var g = this.g;
  g.beginPath();
  g.arc(pt.x, pt.y, 2.5/this.curScale, 0, 2*Math.PI, false);
  g.fill();
}

// General transform op
Annotator.fn.doTransform = function() {
  var g = this.g;

  // Reset xform & clear
  g.setTransform(1,0,0,1,0,0);
  g.clearRect(0, 0, this.w, this.h);

  // To draw in position with scaling,
  // move to position (translate), then
  // scale before drawing at (0, 0)
  g.translate(this.w/2 + this.xOffs, this.h/2 + this.yOffs);
  g.scale(this.curScale, this.curScale);

  this.repaint();
}

// Pan op
Annotator.fn.doPan = function(x, y) {
  // New offset
  var margin = 100;

  this.xOffs += x;
  this.yOffs += y;

  var xLim = (this.w/2)*this.curScale;
  var yLim = (this.h/2)*this.curScale;

  if (this.xOffs >  xLim) this.xOffs =  xLim;
  if (this.xOffs < -xLim) this.xOffs = -xLim;
  if (this.yOffs >  yLim) this.yOffs =  yLim;
  if (this.yOffs < -yLim) this.yOffs = -yLim;

  this.doTransform();
}

// Zoom op
Annotator.fn.zoom = function(scale) {
  // New scaling level
  this.curScale *= scale;
  if (this.curScale < 0.9) this.curScale = 0.9;
  this.doTransform();
}

// Util - canvas to image space
function ptToImg(a, x, y) {
  var x = (x-a.w/2-a.xOffs)/a.curScale;
  var y = (y-a.h/2-a.yOffs)/a.curScale;

  return {x:x,y:y};
}

//////////////////////////////////////////////////////
///// API Definition /////////////////////////////////

(function( $ ) {
  // The annotator function - appplicable to any jQuery object collection
  $.fn.annotator = function(src, width, height) {
    return this.each(function() {
      // Check for annotator class
      $parent = $(this);
      var a;

      // Update if we're passed an existing annotator
      if ($parent.hasClass("annotator")) {
        a = $parent.data("Annotator");
        a.update(src, width, height);
      }
      else {
        a = new Annotator(src, width, height);
        a.build($parent);
      }
    });
  };
}(jQuery));
<|endoftext|>


<commit_before>'use strict';

const request = require('request-promise');
const _ = require('lodash');

/**
 * The list of endpoints that are currently supported by the wrapper
 *
 * @type {string[]}
 */
const allowedEndpoints = [
	'episodes',
	'genres',
	'movies',
	'people',
	'search',
	'seasons',
	'shows'
];

/**
 * Trakt main class
 */
class Trakt {
	/**
	 * Initializes the trakt object.
	 *
	 * @param {Object} options The options object
	 * @param {string} options.clientId Trakt.tv client ID
	 * @param {Boolean} [options.staging=false] Whether to use the staging API instead of production
	 */
	constructor(options) {
		let clientId;

		let defaults = {
			staging: false
		};

		this.api = (options.staging ? 'http://api.staging.trakt.tv/' : 'https://api-v2launch.trakt.tv');

		if (_.isObject(options) && options.hasOwnProperty('clientId')) {
			clientId = options.clientId;
		} else if (_.isString(options)) {
			clientId = options;
		}

		if (clientId.length !== 64) {
			throw new Error('Invalid clientId length (must be 64 characters).');
		}

		this.options = {
			clientId: clientId,
			headers: {
				'Content-Type': 'application/json',
				'User-Agent': 'node-trakt',
				'trakt-api-key': clientId,
				'trakt-api-version': '2'
			},
			json: true
		};

		this.options = _.defaultsDeep({}, this.options, defaults);
	}

	/**
	 * Checks if the requested endpoint is supported by the wrapper
	 *
	 * @param {string} endpoint The endpoint to check
	 * @returns {Boolean}
	 */
	isAllowedEndpoint(endpoint) {
		return allowedEndpoints.indexOf(endpoint.split('/')[0]) > -1;
	}

	/**
	 * Places the specified endpoint's URI into the options object, and
	 * returns the options object.
	 *
	 * @param {string} endpoint The endpoint (and any parameters of it) to send to the API.
	 * @returns {Object} options The options object from the trakt object
	 */
	getOptionsWithURI(endpoint) {
		this.options.uri = this.api + endpoint;

		return this.options;
	}

	/**
	 * Send a request to the Trakt API.
	 * Returns a promise.
	 *
	 * @param {string} endpoint The endpoint (and any parameters of it) to send to the API.
	 * @throws {Error} Endpoint not supported
	 * @returns {Promise}
	 */
	send(endpoint) {
		let options;

		if (!this.isAllowedEndpoint(endpoint)) {
			throw new Error(`Endpoint ${endpoint.split('/')[0]} not supported`);
		}

		options = this.getOptionsWithURI(endpoint);

		return request(options);
	}
}

/**
 * Register all allowed endpoints functions into the Trakt class
 */
for (let endpoint of allowedEndpoints) {
	Trakt.prototype[endpoint] = function (params) {
		return this.send(`${endpoint}/${params}`);
	};
}

module.exports = exports = Trakt;
<commit_msg>fix jsdoc annotation for constructor throw<commit_after>'use strict';

const request = require('request-promise');
const _ = require('lodash');

/**
 * The list of endpoints that are currently supported by the wrapper
 *
 * @type {string[]}
 */
const allowedEndpoints = [
	'episodes',
	'genres',
	'movies',
	'people',
	'search',
	'seasons',
	'shows'
];

/**
 * Trakt main class
 */
class Trakt {
	/**
	 * Initializes the trakt object.
	 *
	 * @param {Object} options The options object
	 * @param {string} options.clientId Trakt.tv client ID
	 * @param {Boolean} [options.staging=false] Whether to use the staging API instead of production
	 * @throws {Error} Invalid clientId length (must be 64 characters)
	 */
	constructor(options) {
		let clientId;

		let defaults = {
			staging: false
		};

		this.api = (options.staging ? 'http://api.staging.trakt.tv/' : 'https://api-v2launch.trakt.tv');

		if (_.isObject(options) && options.hasOwnProperty('clientId')) {
			clientId = options.clientId;
		} else if (_.isString(options)) {
			clientId = options;
		}

		if (clientId.length !== 64) {
			throw new Error('Invalid clientId length (must be 64 characters)');
		}

		this.options = {
			clientId: clientId,
			headers: {
				'Content-Type': 'application/json',
				'User-Agent': 'node-trakt',
				'trakt-api-key': clientId,
				'trakt-api-version': '2'
			},
			json: true
		};

		this.options = _.defaultsDeep({}, this.options, defaults);
	}

	/**
	 * Checks if the requested endpoint is supported by the wrapper
	 *
	 * @param {string} endpoint The endpoint to check
	 * @returns {Boolean}
	 */
	isAllowedEndpoint(endpoint) {
		return allowedEndpoints.indexOf(endpoint.split('/')[0]) > -1;
	}

	/**
	 * Places the specified endpoint's URI into the options object, and
	 * returns the options object.
	 *
	 * @param {string} endpoint The endpoint (and any parameters of it) to send to the API.
	 * @returns {Object} options The options object from the trakt object
	 */
	getOptionsWithURI(endpoint) {
		this.options.uri = this.api + endpoint;

		return this.options;
	}

	/**
	 * Send a request to the Trakt API.
	 * Returns a promise.
	 *
	 * @param {string} endpoint The endpoint (and any parameters of it) to send to the API.
	 * @throws {Error} Endpoint not supported
	 * @returns {Promise}
	 */
	send(endpoint) {
		let options;

		if (!this.isAllowedEndpoint(endpoint)) {
			throw new Error(`Endpoint ${endpoint.split('/')[0]} not supported`);
		}

		options = this.getOptionsWithURI(endpoint);

		return request(options);
	}
}

/**
 * Register all allowed endpoints functions into the Trakt class
 */
for (let endpoint of allowedEndpoints) {
	Trakt.prototype[endpoint] = function (params) {
		return this.send(`${endpoint}/${params}`);
	};
}

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


<commit_before>/* jshint node: true */
'use strict';

var inject = false;

var fs = require('fs');

var Handlebars = require('handlebars');

module.exports = {
  name: 'ember-cli-inject-head',

  contentFor: function(type, config){
	if( inject && type === 'head' ) {
	  return inject;
	}
  },

  serverMiddleware: function(config) {
	var options = config.options['inject-head'];

	if( options && options.template ) {
	  try {
		var head = fs.readFileSync(options.template, 'utf8').toString();
	  } catch (err) {
		return inject = false;
	  }
	  return inject = Handlebars.compile(head)(options.context);
	}
  }
};
<commit_msg>change to using prebuild so it works in production<commit_after>/* jshint node: true */
'use strict';

var inject = false;

var fs = require('fs');

var Handlebars = require('handlebars');

module.exports = {
  name: 'ember-cli-inject-head',

  contentFor: function(type, config){
	if( inject && type === 'head' ) {
	  return inject;
	}
  },

  preBuild: function() {
	var config = this.app.options;
	var options = config['inject-head'];

	if( options && options.template ) {
	  try {
		var head = fs.readFileSync(options.template, 'utf8').toString();
		return inject = Handlebars.compile(head)(options.context);
	  } catch (err) {
		console.error('!!!!!!!! EMBER CLI INJECT HEAD - ERROR !!!!!!!\n', err);
		return inject = false;
	  }
	}
  }
};
<|endoftext|>


<commit_before>var timer = require('sdk/timers');
var self = require('sdk/self');
var tabs = require('sdk/tabs');
var url = require('sdk/url');
var worker = require('./lib/worker');

var links;

var button = require("sdk/ui/button/action").ActionButton({
	id: "style-tab",
	label: "Song's Facebook Admin Tool",
	icon: "./icon-16.png",
	onClick: showMenu
});

var entry = require('sdk/panel').Panel({
	contentURL: self.data.url('prompt/prompt.html'),
	position: {
		top: 50,
		bottom: 50,
		left: 100,
		right: 100
	}
});

var menu = require('sdk/panel').Panel({
	contentURL: self.data.url('menu/menu.html'),
	position: button
});

entry.port.on('ok-button', handleLinks);
menu.port.on('menu-delete', handleDelete);
menu.port.on('menu-extract', handleExtract);

function handleDelete()
{
	hideMenu();
	getLinks();
}

function handleExtract()
{
	hideMenu();
	tabs.activeTab.attach({
		contentScriptFile: self.data.url('extractor.js')
	});
}

function getLinks()
{
	entry.show();
	entry.port.emit('prep', 'Enter a list of links, one per line', '');
}

function showMenu()
{
	menu.show();
}

function hideMenu()
{
	menu.hide();
}

function handleLinks(gottenLinks)
{
	entry.hide();
	links = gottenLinks.split('\n');
	links = links.filter(isPermalink);

	if (links.length > 0)
	{
		console.log('Starting deleting');

		worker.init(self.data.url('delete.js'), deletePost);
		timer.setTimeout(tryStart, 1000);
	}
	else
	{
		console.log('No links passed the filter.');
	}
}

function isPermalink(href)
{
	try
	{
		var link = url.URL(href);
		var protocolPattern = /^https:$/;
		var hostPattern = /^www\.facebook\.com$/;
		var pathPattern = /^\/groups\/\d+\/permalink\/\d+\/$/;
		var queryPattern = /^$/;

		if (protocolPattern.test(link.protocol) === false)
		{
			return false;
		}
		if (hostPattern.test(link.host) === false)
		{
			return false;
		}
		if (pathPattern.test(link.pathname) === false)
		{
			return false;
		}
		if (queryPattern.test(link.search) === false)
		{
			return false;
		}
		return true;
	}
	catch (exc)
	{
		return false;
	}
	console.log('impossible fail');
}

function tryStart()
{
	if (worker.isReady())
	{
		deletePost();
	}
	else
	{
		console.log('Waiting for worker to get ready');
		timer.setTimeout(tryStart, 1000);
	}
}

function deletePost()
{
	if (links.length > 0)
	{
		console.log('Deleting post: ' + links.length + ' remaining');
		worker.setUrl(links.pop());
	}
	else
	{
		console.log('Out of links');
	}
}
<commit_msg>Added old extract support to index.js<commit_after>var timer = require('sdk/timers');
var self = require('sdk/self');
var tabs = require('sdk/tabs');
var url = require('sdk/url');
var worker = require('./lib/worker');

var links;

var button = require("sdk/ui/button/action").ActionButton({
	id: "style-tab",
	label: "Song's Facebook Admin Tool",
	icon: "./icon-16.png",
	onClick: showMenu
});

var entry = require('sdk/panel').Panel({
	contentURL: self.data.url('prompt/prompt.html'),
	position: {
		top: 50,
		bottom: 50,
		left: 100,
		right: 100
	}
});

var menu = require('sdk/panel').Panel({
	contentURL: self.data.url('menu/menu.html'),
	position: button
});

entry.port.on('ok-button', handleLinks);
menu.port.on('menu-delete', handleDelete);
menu.port.on('menu-extract', handleExtract);
menu.port.on('menu-extract-old', handleExtractOld);

function handleDelete()
{
	hideMenu();
	getLinks();
}

function handleExtract()
{
	hideMenu();
	tabs.activeTab.attach({
		contentScriptFile: self.data.url('extractor.js')
	});
}

function handleExtractOld(age)
{
	hideMenu();
	tabs.activeTab.attach({
		contentScriptFile: self.data.url('oldExtractor.js')
	});
}

function getLinks()
{
	entry.show();
	entry.port.emit('prep', 'Enter a list of links, one per line', '');
}

function showMenu()
{
	menu.show();
}

function hideMenu()
{
	menu.hide();
}

function handleLinks(gottenLinks)
{
	entry.hide();
	links = gottenLinks.split('\n');
	links = links.filter(isPermalink);

	if (links.length > 0)
	{
		console.log('Starting deleting');

		worker.init(self.data.url('delete.js'), deletePost);
		timer.setTimeout(tryStart, 1000);
	}
	else
	{
		console.log('No links passed the filter.');
	}
}

function isPermalink(href)
{
	try
	{
		var link = url.URL(href);
		var protocolPattern = /^https:$/;
		var hostPattern = /^www\.facebook\.com$/;
		var pathPattern = /^\/groups\/\d+\/permalink\/\d+\/$/;
		var queryPattern = /^$/;

		if (protocolPattern.test(link.protocol) === false)
		{
			return false;
		}
		if (hostPattern.test(link.host) === false)
		{
			return false;
		}
		if (pathPattern.test(link.pathname) === false)
		{
			return false;
		}
		if (queryPattern.test(link.search) === false)
		{
			return false;
		}
		return true;
	}
	catch (exc)
	{
		return false;
	}
	console.log('impossible fail');
}

function tryStart()
{
	if (worker.isReady())
	{
		deletePost();
	}
	else
	{
		console.log('Waiting for worker to get ready');
		timer.setTimeout(tryStart, 1000);
	}
}

function deletePost()
{
	if (links.length > 0)
	{
		console.log('Deleting post: ' + links.length + ' remaining');
		worker.setUrl(links.pop());
	}
	else
	{
		console.log('Out of links');
	}
}
<|endoftext|>


<commit_before>"use strict";
var express = require('express');
var app = express();
var token = process.env.SLACK_BOT_TOKEN || '';
var channelName = 'nyc-office';
var sucker = 'drewpnyc';
var suckerName = 'Drew';

var RtmClient = require('@slack/client').RtmClient;
var CLIENT_EVENTS = require('@slack/client').CLIENT_EVENTS;
var RTM_EVENTS = require('@slack/client').RTM_EVENTS;

var rtm = new RtmClient(token);

var currentlyTrackingDate = null;
var pizzaCounter = 0;

rtm.on(CLIENT_EVENTS.RTM.AUTHENTICATED, function (rtmStartData) {

    var channels = rtmStartData.channels || [];
    var channelId = null;

    channels.forEach(function(channel) {
        if (channel.name === channelName) {
            channelId = channel.id;
        }
    });

    var logPizza = function() {

        var now = new Date();
        var date = `${now.getFullYear()}-${now.getMonth()}-${now.getDate()}`;

        if (date !== currentlyTrackingDate) {
            currentlyTrackingDate = date;
            pizzaCounter = 0;
        }

        pizzaCounter++;

        if (pizzaCounter === 15) {

            rtm.sendMessage(`OK, you guys need to calm down RIGHT NOW.`, channelId);

        } else if (pizzaCounter === 10) {

            rtm.sendMessage(`<@${sucker}|${suckerName}>, I hope you ordered that pizza.`, channelId);

        } else if (pizzaCounter === 5) {

            rtm.sendMessage(`<@${sucker}|${suckerName}>, pizza?`, channelId);
        }
        console.log('pizza logged:', pizzaCounter);
    };

    rtm.on(RTM_EVENTS.MESSAGE, function handleRtmMessage(message) {

        if (
            message.channel === channelId &&
            message.type === 'message' &&
            !message.subtype
        ) {

            var text = message.text || '';

            if (text.indexOf(':pizza:') !== -1) {

                logPizza();
            }
        } else if (
            message.channel === channelId &&
            message.type === 'message' &&
            message.subtype === 'message_deleted' &&
            message.previous_message
        ) {

            var text = message.previous_message.text || '';

            if (text.indexOf(':pizza:') !== -1) {

                pizzaCounter--;
                console.log('removed pizza:', pizzaCounter);
            }
        }
    });

    rtm.on(RTM_EVENTS.REACTION_ADDED, function handleRtmReaction(message) {

        if (message.item.channel === channelId &&
            message.type === 'reaction_added') {

            var reaction = message.reaction || '';

            if (reaction === 'pizza') {

                logPizza();
            }
        }
    });

    rtm.on(RTM_EVENTS.REACTION_REMOVED, function handleRtmReaction(message) {

        if (message.item.channel === channelId &&
            message.type === 'reaction_removed') {

            var reaction = message.reaction || '';

            if (reaction === 'pizza') {

                pizzaCounter--;
                console.log('removed pizza:', pizzaCounter);
            }
        }
    });

});

app.get('/', function (req, res) {
    res.send('Drew. Pizza. Bot.');
});

app.listen(process.env.PORT, function() {
    rtm.start();
});<commit_msg>Should only send messages once per day<commit_after>"use strict";
var express = require('express');
var app = express();
var token = process.env.SLACK_BOT_TOKEN || '';
var channelName = 'nyc-office';
var sucker = 'drewpnyc';
var suckerName = 'Drew';

var RtmClient = require('@slack/client').RtmClient;
var CLIENT_EVENTS = require('@slack/client').CLIENT_EVENTS;
var RTM_EVENTS = require('@slack/client').RTM_EVENTS;

var rtm = new RtmClient(token);

var currentlyTrackingDate = null;
var pizzaCounter = 0;
var sentFirst = false;
var sentSecond = false;
var sentThird = false;

rtm.on(CLIENT_EVENTS.RTM.AUTHENTICATED, function (rtmStartData) {

    var channels = rtmStartData.channels || [];
    var channelId = null;

    channels.forEach(function(channel) {
        if (channel.name === channelName) {
            channelId = channel.id;
        }
    });

    var logPizza = function() {

        var now = new Date();
        var date = `${now.getFullYear()}-${now.getMonth()}-${now.getDate()}`;

        if (date !== currentlyTrackingDate) {
            currentlyTrackingDate = date;
            pizzaCounter = 0;
            sentFirst = false;
            sentSecond = false;
            sentThird = false;
        }

        pizzaCounter++;

        if (pizzaCounter >= 15 && !sentThird) {

            rtm.sendMessage(`OK, you guys need to calm down RIGHT NOW.`, channelId);
            sentThird = true;

        } else if (pizzaCounter >= 10 && !sentSecond) {

            rtm.sendMessage(`<@${sucker}|${suckerName}>, I hope you ordered that pizza.`, channelId);
            sentSecond = true;

        } else if (pizzaCounter >= 5 && !sentFirst) {

            rtm.sendMessage(`<@${sucker}|${suckerName}>, pizza?`, channelId);
            sentFirst = true;
        }
        console.log('pizza logged:', pizzaCounter);
    };

    rtm.on(RTM_EVENTS.MESSAGE, function handleRtmMessage(message) {

        var text;
        if (
            message.channel === channelId &&
            message.type === 'message' &&
            !message.subtype
        ) {

            text = message.text || '';

            if (text.indexOf(':pizza:') !== -1) {

                logPizza();
            }
        } else if (
            message.channel === channelId &&
            message.type === 'message' &&
            message.subtype === 'message_deleted' &&
            message.previous_message
        ) {

            text = message.previous_message.text || '';

            if (text.indexOf(':pizza:') !== -1) {

                pizzaCounter--;
                console.log('removed pizza:', pizzaCounter);
            }
        }
    });

    rtm.on(RTM_EVENTS.REACTION_ADDED, function handleRtmReaction(message) {

        if (message.item.channel === channelId &&
            message.type === 'reaction_added') {

            var reaction = message.reaction || '';

            if (reaction === 'pizza') {

                logPizza();
            }
        }
    });

    rtm.on(RTM_EVENTS.REACTION_REMOVED, function handleRtmReaction(message) {

        if (message.item.channel === channelId &&
            message.type === 'reaction_removed') {

            var reaction = message.reaction || '';

            if (reaction === 'pizza') {

                pizzaCounter--;
                console.log('removed pizza:', pizzaCounter);
            }
        }
    });

});

app.get('/', function (req, res) {
    res.send('Drew. Pizza. Bot.');
});

app.listen(process.env.PORT, function() {
    rtm.start();
});<|endoftext|>


<commit_before>var express = require('express');
var bodyParser = require('body-parser');
var sqlite = require('sqlite3');
var shortid = require('shortid');
var urlapi = require('url');
var validator = require('validator');
var RateLimit = require('express-rate-limit');
var compressor = require('node-minify');

var app = express();
const PORT = 80;
const ROUTE_LIFESPAN = 600;
const DB_NAME = 'routes.db';

//set up express server configuration
app.set('view engine', 'pug');
// app.locals.pretty = true;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

//configure rate limiter
var limiter = new RateLimit({
  windowMs: 15*60*1000,
  max: 100,
  delayMs: 100
});
app.use(limiter);

//minimize index.js script
compressor.minify({
  compressor: 'uglifyjs',
  input: __dirname + '/scripts/index.js',
  output: __dirname + '/scripts/index-min.js'
});

//initialize sqlite database
var db = new sqlite.Database(DB_NAME);
db.run("CREATE TABLE IF NOT EXISTS Routes "
       + "(id TEXT, url TEXT, hits INTEGER, created_on INTEGER)");

//root directory, show index page
app.get('/', function(req, res) {
  console.log('Root requested...');
  res.render('index', { title: 'URL Shortener' });
});

//handles POST requests to "/submit"
//used for creating new entries in routes database
app.post('/submit', function(req, res) {
  console.log('POST received...');
  var url = req.body.url;
  console.log('URL submitted: ' + url);
  urlInfo = urlapi.parse(url);
  if (urlInfo.protocol == null && url.length > 0) {
    url = 'http://' + url;
  }
  if (validator.isURL(url)) {
    var newID = shortid.generate();
    var timestamp = new Date().getTime() / 1000;
    var hits = 0;
    db.serialize(function() {
      var stmt = db.prepare("INSERT INTO Routes VALUES (?, ?, ?, ?)");
      stmt.run(newID, url, hits, timestamp);
      stmt.finalize();
    });
    //log record creation
    console.log('Record created: ' + newID + '/' + url + '/' + timestamp + '/' + hits);

    //create two versions of URL, short and long
    //short version (doesn't have protocol) for display
    var shortformat = urlapi.format({
      hostname: req.hostname,
      pathname: '/' + newID}
    );

    //long version (contains protocol) for href
    var longformat = urlapi.format({
      protocol: req.protocol,
      hostname: req.hostname,
      pathname: newID}
    );

    res.send("Your new URL is " + shortformat + "");
  } else {
    // console.log('Invalid URL entered: ' + url);
    // res.writeHead(302, { 'Location': '/' });
    // res.end();
    res.send('Invalid URL entered.');
  }
});

//handles requests for individual ID keys (shortid)
app.get('/:key', function(req, res) {
  var key = req.params['key'];
  if (key == 'favicon.ico') { return; }
  console.log('Route requested for key: ' + key);
  db.serialize(function() {
    //by default, invalid requests redirect to root dir
    var stmt = db.prepare("SELECT url FROM Routes WHERE id = ?");
    stmt.get(key, function(err, row) {
      //if the provided key was valid, then
      //  set our destination to the stored URL
      var destination = '/';
      if (typeof row != 'undefined') {
        var targetUrl = row.url;
      	if (validator.isURL(targetUrl)) {
          destination = targetUrl;
      	}
      }
      res.writeHead(302, { 'Location': destination });
      res.end();
    });
    stmt.finalize();
  });
});

app.get('/scripts/index.js', function(req, res) {
  res.sendFile(__dirname + '/scripts/index-min.js');
});

app.listen(PORT, function() {
  console.log('listening...');
});
<commit_msg>re-enabling pretty html<commit_after>var express = require('express');
var bodyParser = require('body-parser');
var sqlite = require('sqlite3');
var shortid = require('shortid');
var urlapi = require('url');
var validator = require('validator');
var RateLimit = require('express-rate-limit');
var compressor = require('node-minify');

var app = express();
const PORT = 80;
const ROUTE_LIFESPAN = 600;
const DB_NAME = 'routes.db';

//set up express server configuration
app.set('view engine', 'pug');
app.locals.pretty = true;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

//configure rate limiter
var limiter = new RateLimit({
  windowMs: 15*60*1000,
  max: 100,
  delayMs: 100
});
app.use(limiter);

//minimize index.js script
compressor.minify({
  compressor: 'uglifyjs',
  input: __dirname + '/scripts/index.js',
  output: __dirname + '/scripts/index-min.js'
});

//initialize sqlite database
var db = new sqlite.Database(DB_NAME);
db.run("CREATE TABLE IF NOT EXISTS Routes "
       + "(id TEXT, url TEXT, hits INTEGER, created_on INTEGER)");

//root directory, show index page
app.get('/', function(req, res) {
  console.log('Root requested...');
  res.render('index', { title: 'URL Shortener' });
});

//handles POST requests to "/submit"
//used for creating new entries in routes database
app.post('/submit', function(req, res) {
  console.log('POST received...');
  var url = req.body.url;
  console.log('URL submitted: ' + url);
  urlInfo = urlapi.parse(url);
  if (urlInfo.protocol == null && url.length > 0) {
    url = 'http://' + url;
  }
  if (validator.isURL(url)) {
    var newID = shortid.generate();
    var timestamp = new Date().getTime() / 1000;
    var hits = 0;
    db.serialize(function() {
      var stmt = db.prepare("INSERT INTO Routes VALUES (?, ?, ?, ?)");
      stmt.run(newID, url, hits, timestamp);
      stmt.finalize();
    });
    //log record creation
    console.log('Record created: ' + newID + '/' + url + '/' + timestamp + '/' + hits);

    //create two versions of URL, short and long
    //short version (doesn't have protocol) for display
    var shortformat = urlapi.format({
      hostname: req.hostname,
      pathname: '/' + newID}
    );

    //long version (contains protocol) for href
    var longformat = urlapi.format({
      protocol: req.protocol,
      hostname: req.hostname,
      pathname: newID}
    );

    res.send("Your new URL is " + shortformat + "");
  } else {
    // console.log('Invalid URL entered: ' + url);
    // res.writeHead(302, { 'Location': '/' });
    // res.end();
    res.send('Invalid URL entered.');
  }
});

//handles requests for individual ID keys (shortid)
app.get('/:key', function(req, res) {
  var key = req.params['key'];
  if (key == 'favicon.ico') { return; }
  console.log('Route requested for key: ' + key);
  db.serialize(function() {
    //by default, invalid requests redirect to root dir
    var stmt = db.prepare("SELECT url FROM Routes WHERE id = ?");
    stmt.get(key, function(err, row) {
      //if the provided key was valid, then
      //  set our destination to the stored URL
      var destination = '/';
      if (typeof row != 'undefined') {
        var targetUrl = row.url;
      	if (validator.isURL(targetUrl)) {
          destination = targetUrl;
      	}
      }
      res.writeHead(302, { 'Location': destination });
      res.end();
    });
    stmt.finalize();
  });
});

app.get('/scripts/index.js', function(req, res) {
  res.sendFile(__dirname + '/scripts/index-min.js');
});

app.listen(PORT, function() {
  console.log('listening...');
});
<|endoftext|>


<commit_before>/**
 * mediaserver module for node.js
 *
 * MIT license, Oguz Bastemur 2014-2016
 */

var fs = require('fs'),
  exts = require('./libs/exts'),
  pathModule = require('path');

var pipe_extensions = {};
var pipe_extension_id = 0;

var shared = {};
var fileInfo = function (path) {
  if (path) {
    if (!exports.noCache && shared[path]) {
      return shared[path];
    }
    else {
      if (!fs.existsSync(path)) {
        return null;
      }
      var stat = fs.statSync(path);
      if (!exports.noCache)
        shared[path] = stat.size;

      return stat.size;
    }
  }
  return 0;
};

// set this to true for development mode
exports.noCache = false;
exports.mediaTypes = exts;

var getRange = function (req, total) {
  var range = [0, total, 0];
  var rinfo = req.headers ? req.headers.range : null;

  if (rinfo) {
    var rloc = rinfo.indexOf('bytes=');
    if (rloc >= 0) {
      var ranges = rinfo.substr(rloc + 6).split('-');
      try {
        range[0] = parseInt(ranges[0]);
        if (ranges[1] && ranges[1].length) {
          range[1] = parseInt(ranges[1]);
          range[1] = range[1] < 16 ? 16 : range[1];
        }
      } catch (e) {}
    }

    if (range[1] == total)
     range[1]--;

    range[2] = total;
  }

  return range;
};

var isString = function (str) {
  if (!str) return false;
  return (typeof str == 'string' || str instanceof String);
};

exports.pipe = function (req, res, path, type, opt_cb) {
  if (!isString(path)) {
    throw new TypeError("path must be a string value");
  }

  var total = fileInfo(path);

  if (total == null) {
    res.end(path + " not found");
    return false;
  }

  var range = getRange(req, total);

  var ext = pathModule.extname(path).toLowerCase();
  if (!type && ext && ext.length) {
    type = exts[ext];
  }

  if (type && type.length && type[0] == '.') {
    ext = type;
    type = exts[type];
  }

  if (!type || !type.length) {
    res.write("Media format not found for " + pathModule.basename(path));
  } else {
    var file = fs.createReadStream(path, {start: range[0], end: range[1]});

    if (!ext.length || !pipe_extensions[ext]) {
      var header = {
        'Content-Length': range[1],
        'Content-Type': type,
        'Access-Control-Allow-Origin': req.headers.origin || "*",
        'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
        'Access-Control-Allow-Headers': 'POST, GET, OPTIONS'
      };

      if (range[2]) {
        header['Accept-Ranges'] = 'bytes';
        header['Content-Range'] = 'bytes ' + range[0] + '-' + range[1] + '/' + total;
        header['Content-Length'] = range[2];

        res.writeHead(206, header);
      } else {
        res.writeHead(200, header);
      }

      file.pipe(res);
      file.on('close', function () {
        res.end(0);
        if (opt_cb && typeof opt_cb == 'function') {
          opt_cb(path);
        }
      });
    } else {
      var _exts = pipe_extensions[ext];
      res.writeHead(200,
        {
          'Content-Type': type,
          'Access-Control-Allow-Origin': req.headers.origin || "*",
          'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
          'Access-Control-Allow-Headers': 'POST, GET, OPTIONS'
        });
      for (var o in _exts) {
        _exts[o](file, req, res, function () {
          if (!res.__ended) {
            res.__ended = true;
            res.end(0);
          }
        });
      }
    }

    return true;
  }

  return false;
};

exports.on = function (ext, m) {
  if (!pipe_extensions[ext]) {
    pipe_extensions[ext] = [];
  }

  m.pipe_extension_id = pipe_extension_id++;
  m.pipe_extension = ext;

  pipe_extensions[ext].push(m);
};

exports.removeEvent = function (method) {
  if (!method || !method.pipe_extension || !method.pipe_extension_id) {
    return;
  }

  if (pipe_extensions[method.pipe_extension]) {
    var exts = pipe_extensions[method.pipe_extension];
    for (var i = 0, ln = exts.length; i < ln; i++) {
      if (exts[i].pipe_extension_id == method.pipe_extension_id) {
        pipe_extensions[method.pipe_extension] = exts.splice(i, 1);
      }
    }
  }
};
<commit_msg>clean up the file stream on response end/close/finish (#10)<commit_after>/**
 * mediaserver module for node.js
 *
 * MIT license, Oguz Bastemur 2014-2016
 */

var fs = require('fs'),
  exts = require('./libs/exts'),
  pathModule = require('path');

var pipe_extensions = {};
var pipe_extension_id = 0;

var shared = {};
var fileInfo = function (path) {
  if (path) {
    if (!exports.noCache && shared[path]) {
      return shared[path];
    }
    else {
      if (!fs.existsSync(path)) {
        return null;
      }
      var stat = fs.statSync(path);
      if (!exports.noCache)
        shared[path] = stat.size;

      return stat.size;
    }
  }
  return 0;
};

// set this to true for development mode
exports.noCache = false;
exports.mediaTypes = exts;

var getRange = function (req, total) {
  var range = [0, total, 0];
  var rinfo = req.headers ? req.headers.range : null;

  if (rinfo) {
    var rloc = rinfo.indexOf('bytes=');
    if (rloc >= 0) {
      var ranges = rinfo.substr(rloc + 6).split('-');
      try {
        range[0] = parseInt(ranges[0]);
        if (ranges[1] && ranges[1].length) {
          range[1] = parseInt(ranges[1]);
          range[1] = range[1] < 16 ? 16 : range[1];
        }
      } catch (e) {}
    }

    if (range[1] == total)
     range[1]--;

    range[2] = total;
  }

  return range;
};

var isString = function (str) {
  if (!str) return false;
  return (typeof str == 'string' || str instanceof String);
};

exports.pipe = function (req, res, path, type, opt_cb) {
  if (!isString(path)) {
    throw new TypeError("path must be a string value");
  }

  var total = fileInfo(path);

  if (total == null) {
    res.end(path + " not found");
    return false;
  }

  var range = getRange(req, total);

  var ext = pathModule.extname(path).toLowerCase();
  if (!type && ext && ext.length) {
    type = exts[ext];
  }

  if (type && type.length && type[0] == '.') {
    ext = type;
    type = exts[type];
  }

  if (!type || !type.length) {
    res.write("Media format not found for " + pathModule.basename(path));
  } else {
    var file = fs.createReadStream(path, {start: range[0], end: range[1]});

    var cleanupFileStream = function() {
      file.close();
    }

    // the event emitted seems to change based on version of node.js
    // 'close' is fired as of v6.11.5
    res.on('close', cleanupFileStream); // https://stackoverflow.com/a/9021242
    res.on('end', cleanupFileStream);  // https://stackoverflow.com/a/16897986
    res.on('finish', cleanupFileStream); // https://stackoverflow.com/a/14093091 - https://stackoverflow.com/a/38057516

    if (!ext.length || !pipe_extensions[ext]) {
      var header = {
        'Content-Length': range[1],
        'Content-Type': type,
        'Access-Control-Allow-Origin': req.headers.origin || "*",
        'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
        'Access-Control-Allow-Headers': 'POST, GET, OPTIONS'
      };

      if (range[2]) {
        header['Accept-Ranges'] = 'bytes';
        header['Content-Range'] = 'bytes ' + range[0] + '-' + range[1] + '/' + total;
        header['Content-Length'] = range[2];

        res.writeHead(206, header);
      } else {
        res.writeHead(200, header);
      }

      file.pipe(res);
      file.on('close', function () {
        res.end(0);
        if (opt_cb && typeof opt_cb == 'function') {
          opt_cb(path);
        }
      });
    } else {
      var _exts = pipe_extensions[ext];
      res.writeHead(200,
        {
          'Content-Type': type,
          'Access-Control-Allow-Origin': req.headers.origin || "*",
          'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
          'Access-Control-Allow-Headers': 'POST, GET, OPTIONS'
        });
      for (var o in _exts) {
        _exts[o](file, req, res, function () {
          if (!res.__ended) {
            res.__ended = true;
            res.end(0);
          }
        });
      }
    }

    return true;
  }

  return false;
};

exports.on = function (ext, m) {
  if (!pipe_extensions[ext]) {
    pipe_extensions[ext] = [];
  }

  m.pipe_extension_id = pipe_extension_id++;
  m.pipe_extension = ext;

  pipe_extensions[ext].push(m);
};

exports.removeEvent = function (method) {
  if (!method || !method.pipe_extension || !method.pipe_extension_id) {
    return;
  }

  if (pipe_extensions[method.pipe_extension]) {
    var exts = pipe_extensions[method.pipe_extension];
    for (var i = 0, ln = exts.length; i < ln; i++) {
      if (exts[i].pipe_extension_id == method.pipe_extension_id) {
        pipe_extensions[method.pipe_extension] = exts.splice(i, 1);
      }
    }
  }
};
<|endoftext|>


<commit_before>// npm
const fs =    Plugin.fs;
const path =  Plugin.path;
const Sass =  Npm.require('node-sass');

// Paths and filenames
const assetsPath =        path.join('assets');
const defaultsPath =      path.join(assetsPath, 'defaults');
const scssPath =          path.join(assetsPath, 'bootstrap', 'scss');
const jsPath =            path.join(assetsPath, 'bootstrap', 'js', 'src');

const jsLoadFirst = [ // Specifies which js modules should be loaded first due to other js modules depending on them
  'util.js',
  'tooltip.js'
];

const bootstrapSettings =   'bootstrap-settings.json';
const bootstrapVariables =  '_bootstrap-variables.scss';
const bootstrapMixins =     '_bootstrap-mixins.scss';

const variablesFilesInstruction =
`// These are custom bootstrap variables for you to edit.
// These simply override any default bootstrap variables.
// This means that you may delete anything in this file
// and the default bootstrap values will be used instead.
`;

const mixinFilesInstruction =
`// Editing these mixins will not edit the mixins used by the core bootstrap modules.
// They are exposed here for your use and convenience.
// They can be imported using @import "path/to/${ bootstrapMixins.replace(/^\_(.+)\.scss*/, '$1') }'
`;

const getAsset = _bootstrapGetAsset;

// Register the compiler for the bootstrap-settings json file
Plugin.registerCompiler({
  extensions: [],
  filenames: [bootstrapSettings, bootstrapVariables, bootstrapMixins]
}, () => new BootstrapCompiler());

// BootstrapCompiler class
class BootstrapCompiler {
  // Actual processing of file (bootstrap-settings json)
  processFilesForTarget(filesFound) {
    let settingsFile;

    // Loop through and find the settings file
    _.each(filesFound, file => {
      let fn = path.basename(file.getDisplayPath());
      if (fn === bootstrapSettings) {
        if (settingsFile)
          throw new Error('You cannot have more than one ' + bootstrapSettings + ' in your Meteor project.');

        settingsFile = file;
      }
    });

    if (settingsFile) {
      // (1) Get the bootstrap-settings json

      // Flag the settings file as being present so a warning isn't displayed later
      settingsFile.addJavaScript({
        data: 'this._bootstrapSettingsFileLoaded = true;\n',
        path: path.join('client', 'lib', 'settings-file-checked.generated.js')
      });

      // Get the settings file dir
      let settingsPathDir = path.dirname(path.join('.', settingsFile.getDisplayPath()));

      // This will throw if settings file is blank/empty or invalid
      let settings;
      try {
        settings = JSON.parse(settingsFile.getContentsAsString());
      }
      catch (err) {
        // Create the settings json file because it doesn't exist

        // Load in the template settings file
        let src = getAsset(path.join(defaultsPath, 'bootstrap-settings.default.json'));

        // Get the default trailing whitespace
        let scssWhitespace = src.match(/\n(\s*)\/\*SCSS_MODULES\*\//)[1] || '';
        let jsWhitespace = src.match(/\n(\s*)\/\*JS_MODULES\*\//)[1] || '';

        // Get all scss modules specified in default bootstrap.scss
        let bootstrapDefaultScss = getAsset(path.join(scssPath, 'bootstrap.scss'));
        let scssModules = [];
        let re = /\@import\s+\"(.+)\"\;?/g;
        let found;
        while (found = re.exec(bootstrapDefaultScss)) {
          if (found[1]) scssModules.push(found[1]);
        }

        // Remove default variables module and mixins module
        scssModules.splice(scssModules.indexOf('variables'), 1);
        scssModules.splice(scssModules.indexOf('mixins'), 1);

        // Sort them alphabetically
        scssModules.sort();

        // Create scss modules json
        let scssJson = _.map(scssModules, function (name) {
          return scssWhitespace + '"' + name + '": true';
        }).join(',\n');

        // Get all js modules
        let jsModules = _.clone(_bootstrapGetJsList());

        // Create js modules json
        let jsJson = _.map(jsModules, name => `${ jsWhitespace }"${ name.match(/(.*)\.js/i)[1] }": true`)
                      .join(',\n');

        // Insert the json modules into the template settings file
        src = src.replace(/\n\s*\/\*SCSS_MODULES\*\//, '\n' + scssJson);
        src = src.replace(/\n\s*\/\*JS_MODULES\*\//, '\n' + jsJson);

        fs.writeFileSync(path.join('.', settingsFile.getDisplayPath()), src);

        settings = JSON.parse(src);
      }
      _.defaults(settings, {
        scss: {},
        javascript: {}
      });
      _.defaults(settings.scss, {
        enableFlex: false,
        customVariables: false,
        exposeMixins: false,
        modules: {}
      });
      _.defaults(settings.javascript, {
        namespace: 'Bootstrap',
        modules: {}
      });
      if (!settings.javascript.namespace ||
          settings.javascript.namespace === 'false' ||
          !_.isString(settings.javascript.namespace) ||
          !/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(settings.javascript.namespace)) {

        settings.javascript.namespace = false;
      }
      else if (settings.javascript.namespace.toLowerCase() === 'global') {
        settings.javascript.namespace = 'window';
      }

      // (2) Handle the scss

      // Get all scss modules specified in default bootstrap.scss
      // This will give a nicely ordered list of all bootstrap modules
      let bootstrapDefaultScss = getAsset(path.join(scssPath, 'bootstrap.scss'));
      let scssModules = [];
      let re = /\@import\s+\"(.+)\"/g;
      let found;
      while (found = re.exec(bootstrapDefaultScss)) {
        if (found[1]) scssModules.push(found[1]);
      }

      // Remove default variables module and mixins module
      scssModules.splice(scssModules.indexOf('variables'), 1);
      scssModules.splice(scssModules.indexOf('mixins'), 1);

      // Filter the modules to include only those enabled in the bootstrap-settings json
      scssModules = _.filter(scssModules, moduleName => settings.scss.modules[moduleName]);

      // Reinsert default variables and mixins modules
      scssModules.splice(0, 0, 'variables', 'mixins');

      // Insert custom variables module (after default variables module)
      if (settings.scss.customVariables) {
        if (!fs.existsSync(path.join(settingsPathDir, bootstrapVariables))) {
          // Generate the custom variables file because it doesn't exist
          let src = getAsset(path.join(scssPath, '_variables.scss'));
          src = src.substr(Math.max(src.indexOf('\n\n'), 0));
          src = src.replace(/\s*\!default/g, '');
          src = variablesFilesInstruction + src;

          fs.writeFileSync(path.join(settingsPathDir, bootstrapVariables), src);
        }

        scssModules.splice(scssModules.indexOf('variables') + 1, 0, bootstrapVariables.replace(/_(.+)\.scss/, '$1'));
      }

      // Expose mixins if specified
      if (settings.scss.exposeMixins && !fs.exists(path.join(settingsPathDir, bootstrapMixins))) {
        // Generate the mixins file because it doesn't exist
        let src = getAsset(path.join(scssPath, '_mixins.scss'));
        src = src.substr(Math.max(src.indexOf('\n\n'), 0));
        src = src.replace(/\@import\s+\"mixins\/(.+)\"\;?/g, (match, mixin) => {
          let fullPath = path.join(scssPath, 'mixins', '_' + mixin + '.scss');

          return getAsset(fullPath);
        });
        src = mixinFilesInstruction + src;

        fs.writeFileSync(path.join(settingsPathDir, bootstrapMixins), src);
      }

      // Enable flex if specified
      let scssPrefix = '';
      if (settings.scss.enableFlex) scssPrefix += '$enable-flex: true;\n';

      // Render the scss into css using a custom importer
      let rendered = Sass.renderSync({
        data: scssPrefix + _.map(scssModules, fn => { return '@import "' + fn + '";'; }).join('\n'),
        importer: (url, prev, done) => {
          // I will admit that this regexp could have more possible cases, but this works for the current bootstrap
          url = url.replace(/(.*)(?:\/|^)(?!.+\/)(.+)/, (match, dir, fn) => path.join(dir, '_' + fn + '.scss'));

          // XXX There is a pitfall here which is that a mixin could possibly import something in the future,
          //     in which case it would not know to look in the mixin folder, but would look in the scss folder
          //     however this is not currently the case so we're not going to worry about it yet.

          // So let's try two potential locations
          try {
            // First the bootstrap scss location (asset)
            return { contents: getAsset(path.join(scssPath, url)) };
          }
          catch (err) {
            // Second the directory the bootstrap settings file is in (in the actual meteor project)
            return { file: path.join(settingsPathDir, url) };
          }
        }
      });

      // Add the newly generated css as a stylesheet
      settingsFile.addStylesheet({
        data: rendered.css.toString(),
        path: path.join('client', 'stylesheets', 'bootstrap', 'bootstrap.generated.css')
      });

      // (3) Handle the js

      // Get all js modules
      let jsModules = _.clone(_bootstrapGetJsList());

      // Filter the modules to include only those enabled in the bootstrap-settings json
      jsModules = _.filter(jsModules, moduleName => settings.javascript.modules[moduleName.match(/(.*)\.js/i)[1]]);

      // Push 'load first' modules to top of list
      let jsReversedLoadFirst = _.clone(jsLoadFirst).reverse();
      _.each(jsReversedLoadFirst, fn => {
        let index = jsModules.indexOf(fn);

        if (index > -1)
          jsModules.unshift(jsModules.splice(index, 1)[0]);
      });

      // Get source from each bootstrap js file and compile it into one file
      let src = '';
      _.each(jsModules, moduleFn => {
        src += getAsset(path.join(jsPath, moduleFn)) + '\n';
      });

      // Kill the imports
      src = src.replace(/import\s+(?:\S+)\s+from\s+\'.+\'/g, '');

      // Build the exports
      if (settings.javascript.namespace !== false) {
        src = `if (typeof window. ${ settings.javascript.namespace } === "undefined")
                window. ${ settings.javascript.namespace } = {};
                ${ src }`;

        src = src.replace(
          /export\s+default\s+(\S+)/g,
          `window.${ settings.javascript.namespace }.$1 = $1`
        );
      }

      src = `if (Meteor.isClient) {
              ${ src }
            }`;

      // Compile the ES6
      let babelOptions = Babel.getDefaultOptions();
      let filename = path.join('client', 'lib', 'bootstrap', 'bootstrap.generated.js');
      babelOptions.sourceMap = true;
      babelOptions.filename = filename;
      babelOptions.sourceFileName = path.join('/', filename);
      babelOptions.sourceMapName = path.join('/', filename + '.map');
      let compiled = Babel.compile(src, babelOptions);

      // Add the javascript
      settingsFile.addJavaScript({
        data: compiled.code,
        map: compiled.map,
        path: filename
      });
    }
  }
};
<commit_msg>Minor change to hardcoded js filenames function.<commit_after>// npm
const fs =    Plugin.fs;
const path =  Plugin.path;
const Sass =  Npm.require('node-sass');

// Paths and filenames
const assetsPath =        path.join('assets');
const defaultsPath =      path.join(assetsPath, 'defaults');
const scssPath =          path.join(assetsPath, 'bootstrap', 'scss');
const jsPath =            path.join(assetsPath, 'bootstrap', 'js', 'src');

const jsLoadFirst = [ // Specifies which js modules should be loaded first due to other js modules depending on them
  'util.js',
  'tooltip.js'
];

const bootstrapSettings =   'bootstrap-settings.json';
const bootstrapVariables =  '_bootstrap-variables.scss';
const bootstrapMixins =     '_bootstrap-mixins.scss';

const variablesFilesInstruction =
`// These are custom bootstrap variables for you to edit.
// These simply override any default bootstrap variables.
// This means that you may delete anything in this file
// and the default bootstrap values will be used instead.
`;

const mixinFilesInstruction =
`// Editing these mixins will not edit the mixins used by the core bootstrap modules.
// They are exposed here for your use and convenience.
// They can be imported using @import "path/to/${ bootstrapMixins.replace(/^\_(.+)\.scss*/, '$1') }'
`;

const getAsset = _bootstrapGetAsset;
const getJsFilenames = _bootstrapGetJsList;

// Register the compiler for the bootstrap-settings json file
Plugin.registerCompiler({
  extensions: [],
  filenames: [bootstrapSettings, bootstrapVariables, bootstrapMixins]
}, () => new BootstrapCompiler());

// BootstrapCompiler class
class BootstrapCompiler {
  // Actual processing of file (bootstrap-settings json)
  processFilesForTarget(filesFound) {
    let settingsFile;

    // Loop through and find the settings file
    _.each(filesFound, file => {
      let fn = path.basename(file.getDisplayPath());
      if (fn === bootstrapSettings) {
        if (settingsFile)
          throw new Error('You cannot have more than one ' + bootstrapSettings + ' in your Meteor project.');

        settingsFile = file;
      }
    });

    if (settingsFile) {
      // (1) Get the bootstrap-settings json

      // Flag the settings file as being present so a warning isn't displayed later
      settingsFile.addJavaScript({
        data: 'this._bootstrapSettingsFileLoaded = true;\n',
        path: path.join('client', 'lib', 'settings-file-checked.generated.js')
      });

      // Get the settings file dir
      let settingsPathDir = path.dirname(path.join('.', settingsFile.getDisplayPath()));

      // This will throw if settings file is blank/empty or invalid
      let settings;
      try {
        settings = JSON.parse(settingsFile.getContentsAsString());
      }
      catch (err) {
        // Create the settings json file because it doesn't exist

        // Load in the template settings file
        let src = getAsset(path.join(defaultsPath, 'bootstrap-settings.default.json'));

        // Get the default trailing whitespace
        let scssWhitespace = src.match(/\n(\s*)\/\*SCSS_MODULES\*\//)[1] || '';
        let jsWhitespace = src.match(/\n(\s*)\/\*JS_MODULES\*\//)[1] || '';

        // Get all scss modules specified in default bootstrap.scss
        let bootstrapDefaultScss = getAsset(path.join(scssPath, 'bootstrap.scss'));
        let scssModules = [];
        let re = /\@import\s+\"(.+)\"\;?/g;
        let found;
        while (found = re.exec(bootstrapDefaultScss)) {
          if (found[1]) scssModules.push(found[1]);
        }

        // Remove default variables module and mixins module
        scssModules.splice(scssModules.indexOf('variables'), 1);
        scssModules.splice(scssModules.indexOf('mixins'), 1);

        // Sort them alphabetically
        scssModules.sort();

        // Create scss modules json
        let scssJson = _.map(scssModules, function (name) {
          return scssWhitespace + '"' + name + '": true';
        }).join(',\n');

        // Get all js modules
        let jsModules = _.clone(getJsFilenames());

        // Create js modules json
        let jsJson = _.map(jsModules, name => `${ jsWhitespace }"${ name.match(/(.*)\.js/i)[1] }": true`)
                      .join(',\n');

        // Insert the json modules into the template settings file
        src = src.replace(/\n\s*\/\*SCSS_MODULES\*\//, '\n' + scssJson);
        src = src.replace(/\n\s*\/\*JS_MODULES\*\//, '\n' + jsJson);

        fs.writeFileSync(path.join('.', settingsFile.getDisplayPath()), src);

        settings = JSON.parse(src);
      }
      _.defaults(settings, {
        scss: {},
        javascript: {}
      });
      _.defaults(settings.scss, {
        enableFlex: false,
        customVariables: false,
        exposeMixins: false,
        modules: {}
      });
      _.defaults(settings.javascript, {
        namespace: 'Bootstrap',
        modules: {}
      });
      if (!settings.javascript.namespace ||
          settings.javascript.namespace === 'false' ||
          !_.isString(settings.javascript.namespace) ||
          !/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(settings.javascript.namespace)) {

        settings.javascript.namespace = false;
      }
      else if (settings.javascript.namespace.toLowerCase() === 'global') {
        settings.javascript.namespace = 'window';
      }

      // (2) Handle the scss

      // Get all scss modules specified in default bootstrap.scss
      // This will give a nicely ordered list of all bootstrap modules
      let bootstrapDefaultScss = getAsset(path.join(scssPath, 'bootstrap.scss'));
      let scssModules = [];
      let re = /\@import\s+\"(.+)\"/g;
      let found;
      while (found = re.exec(bootstrapDefaultScss)) {
        if (found[1]) scssModules.push(found[1]);
      }

      // Remove default variables module and mixins module
      scssModules.splice(scssModules.indexOf('variables'), 1);
      scssModules.splice(scssModules.indexOf('mixins'), 1);

      // Filter the modules to include only those enabled in the bootstrap-settings json
      scssModules = _.filter(scssModules, moduleName => settings.scss.modules[moduleName]);

      // Reinsert default variables and mixins modules
      scssModules.splice(0, 0, 'variables', 'mixins');

      // Insert custom variables module (after default variables module)
      if (settings.scss.customVariables) {
        if (!fs.existsSync(path.join(settingsPathDir, bootstrapVariables))) {
          // Generate the custom variables file because it doesn't exist
          let src = getAsset(path.join(scssPath, '_variables.scss'));
          src = src.substr(Math.max(src.indexOf('\n\n'), 0));
          src = src.replace(/\s*\!default/g, '');
          src = variablesFilesInstruction + src;

          fs.writeFileSync(path.join(settingsPathDir, bootstrapVariables), src);
        }

        scssModules.splice(scssModules.indexOf('variables') + 1, 0, bootstrapVariables.replace(/_(.+)\.scss/, '$1'));
      }

      // Expose mixins if specified
      if (settings.scss.exposeMixins && !fs.exists(path.join(settingsPathDir, bootstrapMixins))) {
        // Generate the mixins file because it doesn't exist
        let src = getAsset(path.join(scssPath, '_mixins.scss'));
        src = src.substr(Math.max(src.indexOf('\n\n'), 0));
        src = src.replace(/\@import\s+\"mixins\/(.+)\"\;?/g, (match, mixin) => {
          let fullPath = path.join(scssPath, 'mixins', '_' + mixin + '.scss');

          return getAsset(fullPath);
        });
        src = mixinFilesInstruction + src;

        fs.writeFileSync(path.join(settingsPathDir, bootstrapMixins), src);
      }

      // Enable flex if specified
      let scssPrefix = '';
      if (settings.scss.enableFlex) scssPrefix += '$enable-flex: true;\n';

      // Render the scss into css using a custom importer
      let rendered = Sass.renderSync({
        data: scssPrefix + _.map(scssModules, fn => { return '@import "' + fn + '";'; }).join('\n'),
        importer: (url, prev, done) => {
          // I will admit that this regexp could have more possible cases, but this works for the current bootstrap
          url = url.replace(/(.*)(?:\/|^)(?!.+\/)(.+)/, (match, dir, fn) => path.join(dir, '_' + fn + '.scss'));

          // XXX There is a pitfall here which is that a mixin could possibly import something in the future,
          //     in which case it would not know to look in the mixin folder, but would look in the scss folder
          //     however this is not currently the case so we're not going to worry about it yet.

          // So let's try two potential locations
          try {
            // First the bootstrap scss location (asset)
            return { contents: getAsset(path.join(scssPath, url)) };
          }
          catch (err) {
            // Second the directory the bootstrap settings file is in (in the actual meteor project)
            return { file: path.join(settingsPathDir, url) };
          }
        }
      });

      // Add the newly generated css as a stylesheet
      settingsFile.addStylesheet({
        data: rendered.css.toString(),
        path: path.join('client', 'stylesheets', 'bootstrap', 'bootstrap.generated.css')
      });

      // (3) Handle the js

      // Get all js modules
      let jsModules = _.clone(getJsFilenames());

      // Filter the modules to include only those enabled in the bootstrap-settings json
      jsModules = _.filter(jsModules, moduleName => settings.javascript.modules[moduleName.match(/(.*)\.js/i)[1]]);

      // Push 'load first' modules to top of list
      let jsReversedLoadFirst = _.clone(jsLoadFirst).reverse();
      _.each(jsReversedLoadFirst, fn => {
        let index = jsModules.indexOf(fn);

        if (index > -1)
          jsModules.unshift(jsModules.splice(index, 1)[0]);
      });

      // Get source from each bootstrap js file and compile it into one file
      let src = '';
      _.each(jsModules, moduleFn => {
        src += getAsset(path.join(jsPath, moduleFn)) + '\n';
      });

      // Kill the imports
      src = src.replace(/import\s+(?:\S+)\s+from\s+\'.+\'/g, '');

      // Build the exports
      if (settings.javascript.namespace !== false) {
        src = `if (typeof window. ${ settings.javascript.namespace } === "undefined")
                window. ${ settings.javascript.namespace } = {};
                ${ src }`;

        src = src.replace(
          /export\s+default\s+(\S+)/g,
          `window.${ settings.javascript.namespace }.$1 = $1`
        );
      }

      src = `if (Meteor.isClient) {
              ${ src }
            }`;

      // Compile the ES6
      let babelOptions = Babel.getDefaultOptions();
      let filename = path.join('client', 'lib', 'bootstrap', 'bootstrap.generated.js');
      babelOptions.sourceMap = true;
      babelOptions.filename = filename;
      babelOptions.sourceFileName = path.join('/', filename);
      babelOptions.sourceMapName = path.join('/', filename + '.map');
      let compiled = Babel.compile(src, babelOptions);

      // Add the javascript
      settingsFile.addJavaScript({
        data: compiled.code,
        map: compiled.map,
        path: filename
      });
    }
  }
};
<|endoftext|>