<commit_before>var WAVEFORM = function(options) {

	if(!options.container) {
		throw new Error('waveform needs a container')
	}

	this.container = options.container
	this.height = options.height || 100
	this.width = options.width || 300
	this.waveform = options.waveform
	this.reflection = options.reflection || 0

	this.colors = {}

	this.gutterWidth = options.gutterWidth || 1
	this.waveWidth = options.waveWidth || 3

	// unique ID
	this.id = Math.floor((Math.random() * 10000) + 1);

	// canvas node
	this.canvas = document.createElement('canvas')

	// active = highlighted section of track
	this.active = -1

	// slected = dimmer highlighted slections
	this.selected = -1

	// mouse dragging
	this.isDragging = false

	// is playing
	this.isPlaying = false

	// is in focus
	this.isFocus = false

}

WAVEFORM.prototype.init = function() {

	//console.log(this.id + ' init')

	// set ID
	this.canvas.setAttribute('data-waveform-id', this.id)

	// set canvas height and width
	this.canvas.width = this.width
	this.canvas.height = this.height

	// get canvas context
	this.ctx = this.canvas.getContext('2d')

	// append canvas
	this.container.appendChild(this.canvas)

	//bind event handlers
	this.bindEventHandlers()

	//calc height
	this.updateHeight()

	//parse waveform
	this.cache()

	//render
	this.draw()

}

WAVEFORM.prototype.destroy = function() {
	//todo
}

WAVEFORM.prototype.bindEventHandlers = function() {

	this.canvas.addEventListener('mousedown', this.onMouseDown.bind(this))
	this.canvas.addEventListener('mousemove', this.onMouseOver.bind(this))
	this.canvas.addEventListener('mouseout', this.onMouseOut.bind(this))
	this.canvas.addEventListener('mouseup', this.onMouseUp.bind(this))
}

WAVEFORM.prototype.onMouseOut = function(e) {

	this.selected = -1

	this.draw()
}

WAVEFORM.prototype.onMouseUp = function(e) {

	this.isDragging = false
}

WAVEFORM.prototype.onMouseOver = function(e) {

	var x = e.x - this.canvas.offsetLeft - this.paddingLeft
	var y = e.y - this.canvas.offsetTop

	var waveClicked = Math.round( x / (this.waveWidth + this.gutterWidth) )

	if(this.isDragging === true) {
		this.selected = -1
		this.active = waveClicked
	}

	else {
		this.selected = waveClicked
	}

	this.draw()

}

WAVEFORM.prototype.onMouseDown = function(e) {

	this.isDragging = true

	var x = e.x - this.canvas.offsetLeft - this.paddingLeft
	var y = e.y - this.canvas.offsetTop

	var waveClicked = Math.round( x / (this.waveWidth + this.gutterWidth) )

	this.active = waveClicked

	this.draw()
}

WAVEFORM.prototype.update = function(options) {

	//console.log(this.id + ' update')

	if(options) {

		if(options.gutterWidth) {
			this.gutterWidth = options.gutterWidth
		}

		if(options.waveWidth) {
			this.waveWidth = options.waveWidth
		}

		if(options.width) {
			this.width = options.width
			this.canvas.width = this.width
		}

		if(options.height) {
			this.height = options.height
			this.canvas.height = this.height
		}

		if(options.reflection == 0 || options.reflection) {
			this.reflection = options.reflection
		}

		// if 1 or more of these is actived, recalc and cache
		if(options.gutterWidth || options.waveWidth || options.width || options.height || (options.reflection || options.reflection === 0)) {
			this.cache()
		}

		if(options.height || (options.reflection || options.reflection === 0)) {
			this.updateHeight()
		}
	}

	//render
	this.draw()
}

// 'experimental'
WAVEFORM.prototype.setGradient = function(name, colors) {

	var gradient = this.ctx.createLinearGradient(0,this.waveOffset,0,0)
	for(var i=0; i<colors.length; i+=2) {
		gradient.addColorStop(colors[i+1], colors[i])
	}

	this.colors[name] = gradient
}

WAVEFORM.prototype.setColor = function(name, color) {

	this.colors[name] = color
}

WAVEFORM.prototype.addColors = function() {

	//default colors
	this.setColor('wave-focus', '#333333')
	this.setGradient('wave', ['#666666', 0, '#868686', 1])
	this.setGradient('wave-active', ['#FF3300', 0, '#FF5100', 1])
	this.setGradient('wave-selected', ['#993016', 0, '#973C15', 1])

	this.setGradient('gutter', ['#6B6B6B', 0, '#c9c9c9', 1])
	this.setGradient('gutter-active', ['#FF3704', 0, '#FF8F63', 1])
	this.setGradient('gutter-selected', ['#9A371E', 0, '#CE9E8A', 1])

	this.setColor('reflection', '#999999')
	this.setColor('reflection-active', '#FFC0A0')
}

WAVEFORM.prototype.draw = function() {

	//console.log(this.id + ' draw')

	var gutter, xPos, yPos

	xPos = this.paddingLeft
	yPos = this.waveOffset

	// clear canvas for redraw
	this.ctx.clearRect ( 0 , 0 , this.width , this.height );

	// itterate waves
	for(var i=0; i<this.waves.length; i+=1) {

		// wave

		// if is hovered
		if(this.selected > 0 && (this.selected <= i && i < this.active) || (this.selected > i && i >= this.active)) {
			this.ctx.fillStyle = this.colors['wave-selected']
		}
		// if is active
		else if(this.active > i) {
			this.ctx.fillStyle = this.colors['wave-active']
		}
		// default
		else {
			this.ctx.fillStyle = this.colors['wave-focus']
		}

		// draw wave
		this.ctx.fillRect(xPos, yPos, this.waveWidth, this.waves[i])

		// gutter

		// if is hovered
		if(this.selected > 0 && (this.selected <= i && i < this.active) || (this.selected > i && i >= this.active)) {
			this.ctx.fillStyle = this.colors['gutter-selected']
		}
		// if is active
		else if(this.active > i) {
			this.ctx.fillStyle = this.colors['gutter-active']
		}
		// default
		else {
			this.ctx.fillStyle = this.colors['gutter']
		}

		// smallest wave between butter is gutters height
		// note: Math.max because wave values are negative
		gutterX = Math.max(this.waves[i],this.waves[i+1])

		// draw gutter
		this.ctx.fillRect(xPos + this.waveWidth, yPos, this.gutterWidth, gutterX)

		// reflection wave
		if(this.reflection > 0) {

			var reflectionHeight =  (Math.abs(this.waves[i]) / (1 - this.reflection) ) * this.reflection

			if(this.active > i) this.ctx.fillStyle = this.colors['reflection-active']
			else this.ctx.fillStyle = this.colors['reflection']

			// draw reflection
			this.ctx.fillRect(xPos, yPos, this.waveWidth, reflectionHeight)
		}

		xPos += this.waveWidth + this.gutterWidth
	}
}

WAVEFORM.prototype.updateHeight = function(first_argument) {

	this.waveOffset = Math.floor( this.height  - (this.height * this.reflection) )

	this.reflectionHeight = Math.floor( this.height - this.waveOffset )

	this.waveHeight = Math.floor( this.height - this.reflectionHeight )

	this.addColors()
}

//TODO refactor
// parse and cache array of points
WAVEFORM.prototype.cache = function() {

	var wavesPerWave, waves, wave, i, waveCount

	// console.log('waveOffset: ' + this.waveOffset )
	// console.log(' waveHeight: ' + this.waveHeight + ' reflectionHeight: ' + this.reflectionHeight + '  = ' + (this.waveHeight + this.reflectionHeight) )

	waveCount = Math.ceil( this.width / (this.gutterWidth + this.waveWidth) )

	wavesPerWave = Math.ceil(this.waveform.length / waveCount)

	waves = []
	wave = 0

	for(i=0; i<this.waveform.length; i+=1) {

		wave += this.waveform[i]

		if(i%wavesPerWave === 0 && i !== 0) {

			wave = (wave/wavesPerWave)
			wave = Math.floor(-Math.abs(wave * this.waveHeight))

			waves.push(wave)

			wave = 0

		}
	}

	this.paddingLeft = Math.floor((this.width - ((this.gutterWidth + this.waveWidth)*waves.length))/2)
	this.paddingRight = Math.ceil((this.width - ((this.gutterWidth + this.waveWidth)*waves.length))/2)

	return this.waves = waves
}

var example = require('./example')(WAVEFORM)
<commit_msg>play/pause<commit_after>var WAVEFORM = function(options) {

	if(!options.container) {
		throw new Error('waveform needs a container')
	}

	this.container = options.container
	this.height = options.height || 100
	this.width = options.width || 300
	this.waveform = options.waveform
	this.reflection = options.reflection || 0

	this.colors = {}

	this.gutterWidth = options.gutterWidth || 1
	this.waveWidth = options.waveWidth || 3

	// unique ID
	this.id = Math.floor((Math.random() * 10000) + 1);

	// canvas node
	this.canvas = document.createElement('canvas')

	// active = highlighted section of track
	this.active = -1

	// slected = dimmer highlighted slections
	this.selected = -1

	// mouse dragging
	this.isDragging = false

	// is playing
	this.isPlaying = false

	// is in focus
	this.isFocus = false

}

WAVEFORM.prototype.init = function() {

	//console.log(this.id + ' init')

	// set ID
	this.canvas.setAttribute('data-waveform-id', this.id)

	// set canvas height and width
	this.canvas.width = this.width
	this.canvas.height = this.height

	// get canvas context
	this.ctx = this.canvas.getContext('2d')

	// append canvas
	this.container.appendChild(this.canvas)

	//bind event handlers
	this.bindEventHandlers()

	//calc height
	this.updateHeight()

	//parse waveform
	this.cache()

	//render
	this.draw()

}

WAVEFORM.prototype.play = function(mediaLength) {

	this.isPlaying = true

	this.mediaLength = mediaLength

	this.secondsPlayed =  0

	this.AnimTime = ( this.mediaLength / this.waves.length)

	function foo(){

		this.secondsPlayed += this.AnimTime

		if(this.secondsPlayed >= this.mediaLength) this.pause()

		console.log(this.secondsPlayed + '  ' + this.AnimTime + '  ' + this.mediaLength)

		this.active += 1
		this.draw()
	}

	this.playInterval = setInterval(foo.bind(this), this.AnimTime)
}

WAVEFORM.prototype.pause = function() {

	this.isPlaying = false

	clearInterval(this.playInterval)
}

WAVEFORM.prototype.destroy = function() {
	//todo
}

WAVEFORM.prototype.bindEventHandlers = function() {

	this.canvas.addEventListener('mousedown', this.onMouseDown.bind(this))
	this.canvas.addEventListener('mousemove', this.onMouseOver.bind(this))
	this.canvas.addEventListener('mouseout', this.onMouseOut.bind(this))
	this.canvas.addEventListener('mouseup', this.onMouseUp.bind(this))
}

WAVEFORM.prototype.onMouseOut = function(e) {

	this.selected = -1

	this.draw()
}

WAVEFORM.prototype.onMouseUp = function(e) {

	this.isDragging = false
}

WAVEFORM.prototype.onMouseOver = function(e) {

	var x = e.x - this.canvas.offsetLeft - this.paddingLeft
	var y = e.y - this.canvas.offsetTop

	var waveClicked = Math.round( x / (this.waveWidth + this.gutterWidth) )

	if(this.isDragging === true) {
		this.selected = -1
		this.active = waveClicked
	}

	else {
		this.selected = waveClicked
	}

	this.draw()

}

WAVEFORM.prototype.onMouseDown = function(e) {

	this.isDragging = true

	var x = e.x - this.canvas.offsetLeft - this.paddingLeft
	var y = e.y - this.canvas.offsetTop

	var waveClicked = Math.round( x / (this.waveWidth + this.gutterWidth) )

	this.active = waveClicked

	this.draw()
}

WAVEFORM.prototype.update = function(options) {

	//console.log(this.id + ' update')

	if(options) {

		if(options.gutterWidth) {
			this.gutterWidth = options.gutterWidth
		}

		if(options.waveWidth) {
			this.waveWidth = options.waveWidth
		}

		if(options.width) {
			this.width = options.width
			this.canvas.width = this.width
		}

		if(options.height) {
			this.height = options.height
			this.canvas.height = this.height
		}

		if(options.reflection == 0 || options.reflection) {
			this.reflection = options.reflection
		}

		// if 1 or more of these is actived, recalc and cache
		if(options.gutterWidth || options.waveWidth || options.width || options.height || (options.reflection || options.reflection === 0)) {
			this.cache()
		}

		if(options.height || (options.reflection || options.reflection === 0)) {
			this.updateHeight()
		}
	}

	//render
	this.draw()
}

// 'experimental'
WAVEFORM.prototype.setGradient = function(name, colors) {

	var gradient = this.ctx.createLinearGradient(0,this.waveOffset,0,0)
	for(var i=0; i<colors.length; i+=2) {
		gradient.addColorStop(colors[i+1], colors[i])
	}

	this.colors[name] = gradient
}

WAVEFORM.prototype.setColor = function(name, color) {

	this.colors[name] = color
}

WAVEFORM.prototype.addColors = function() {

	//default colors
	this.setColor('wave-focus', '#333333')
	this.setGradient('wave', ['#666666', 0, '#868686', 1])
	this.setGradient('wave-active', ['#FF3300', 0, '#FF5100', 1])
	this.setGradient('wave-selected', ['#993016', 0, '#973C15', 1])

	this.setGradient('gutter', ['#6B6B6B', 0, '#c9c9c9', 1])
	this.setGradient('gutter-active', ['#FF3704', 0, '#FF8F63', 1])
	this.setGradient('gutter-selected', ['#9A371E', 0, '#CE9E8A', 1])

	this.setColor('reflection', '#999999')
	this.setColor('reflection-active', '#FFC0A0')
}

WAVEFORM.prototype.draw = function() {

	//console.log(this.id + ' draw')

	var gutter, xPos, yPos

	xPos = this.paddingLeft
	yPos = this.waveOffset

	// clear canvas for redraw
	this.ctx.clearRect ( 0 , 0 , this.width , this.height );

	// itterate waves
	for(var i=0; i<this.waves.length; i+=1) {

		// wave

		// if is hovered
		if(this.selected > 0 && (this.selected <= i && i < this.active) || (this.selected > i && i >= this.active)) {
			this.ctx.fillStyle = this.colors['wave-selected']
		}
		// if is active
		else if(this.active > i) {
			this.ctx.fillStyle = this.colors['wave-active']
		}
		// default
		else {
			this.ctx.fillStyle = this.colors['wave-focus']
		}

		// draw wave
		this.ctx.fillRect(xPos, yPos, this.waveWidth, this.waves[i])

		// gutter

		// if is hovered
		if(this.selected > 0 && (this.selected <= i && i < this.active) || (this.selected > i && i >= this.active)) {
			this.ctx.fillStyle = this.colors['gutter-selected']
		}
		// if is active
		else if(this.active > i) {
			this.ctx.fillStyle = this.colors['gutter-active']
		}
		// default
		else {
			this.ctx.fillStyle = this.colors['gutter']
		}

		// smallest wave between butter is gutters height
		// note: Math.max because wave values are negative
		gutterX = Math.max(this.waves[i],this.waves[i+1])

		// draw gutter
		this.ctx.fillRect(xPos + this.waveWidth, yPos, this.gutterWidth, gutterX)

		// reflection wave
		if(this.reflection > 0) {

			var reflectionHeight =  (Math.abs(this.waves[i]) / (1 - this.reflection) ) * this.reflection

			if(this.active > i) this.ctx.fillStyle = this.colors['reflection-active']
			else this.ctx.fillStyle = this.colors['reflection']

			// draw reflection
			this.ctx.fillRect(xPos, yPos, this.waveWidth, reflectionHeight)
		}

		xPos += this.waveWidth + this.gutterWidth
	}
}

WAVEFORM.prototype.updateHeight = function(first_argument) {

	this.waveOffset = Math.floor( this.height  - (this.height * this.reflection) )

	this.reflectionHeight = Math.floor( this.height - this.waveOffset )

	this.waveHeight = Math.floor( this.height - this.reflectionHeight )

	this.addColors()
}

//TODO refactor
// parse and cache array of points
WAVEFORM.prototype.cache = function() {

	var wavesPerWave, waves, wave, i, waveCount

	// console.log('waveOffset: ' + this.waveOffset )
	// console.log(' waveHeight: ' + this.waveHeight + ' reflectionHeight: ' + this.reflectionHeight + '  = ' + (this.waveHeight + this.reflectionHeight) )

	waveCount = Math.ceil( this.width / (this.gutterWidth + this.waveWidth) )

	wavesPerWave = Math.ceil(this.waveform.length / waveCount)

	waves = []
	wave = 0

	for(i=0; i<this.waveform.length; i+=1) {

		wave += this.waveform[i]

		if(i%wavesPerWave === 0 && i !== 0) {

			wave = (wave/wavesPerWave)
			wave = Math.floor(-Math.abs(wave * this.waveHeight))

			waves.push(wave)

			wave = 0

		}
	}

	this.paddingLeft = Math.floor((this.width - ((this.gutterWidth + this.waveWidth)*waves.length))/2)
	this.paddingRight = Math.ceil((this.width - ((this.gutterWidth + this.waveWidth)*waves.length))/2)

	return this.waves = waves
}

var example = require('./example')(WAVEFORM)
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Jeffrey Hunter
 *
 * Distributed under the MIT license. See the LICENSE file distributed
 * with this work for details and restrictions.
 */

var util = require('util')
  , events = require('events')
;

var cache = {};
var cas = 1;

/**
 * Simple object merge function (shallow merge)
 */
function extend(dest, src) {
  if (src) Object.keys(src).forEach(function(k) { dest[k] = src[k]; });

  if (arguments.length <= 2) return dest;

  return extend.apply(this, [dest].concat(Array.prototype.slice.call(arguments, 2)));
}

/**
 * Return the expiration time from a TTL in seconds
 */
function expires(memcached, ttl) {
  if (ttl === 0) return 0;
  if (ttl > memcached.maxExpiration) return Number(ttl);
  return Date.now() + (Number(ttl) * 1000);
}

/**
 * Handle expiration of an entry with a given key. Returns the entry if
 * it has not expired.
 */
function expire(memcached, key) {
  if (cache[key] && cache[key].expires !== 0 && cache[key].expires <= Date.now())
    delete cache[key];

  return cache[key];
}

/**
 * Return the equivalent ttl for a key
 */
function keyttl(memcached, key) {
  return cache[key] && cache[key].expires ?
    ((cache[key].expires - Date.now()) / 1000) :
    0;
}

/**
 * Return the normalized value for a cache entry
 */
function value(entry) {
  return entry ? entry.value : entry;
}

/**
 * Return the normalized value for a cache entry along with its CAS id
 */
function casvalue(key, entry) {
  if (!entry) return;

  var result = {cas: String(entry.cas)};
  result[key] = entry.value;

  return result;
}

/**
 * Sets the value of a key
 */
function setkey(memcached, key, value, ttl) {
  cache[key] = {value: value, expires: expires(memcached, ttl), cas: (++cas)};
}

/**
 * Returns a not stored error
 */
function notStored() {
  var e = new Error("Item is not stored");
  e.notStored = true;
  return e;
}

/**
 * Invoke a callback with the correct context
 */
function invoke(callback, info) {
  var args = Array.prototype.slice.call(arguments, 2);
  var ctx = {
    start: Date.now(),
    type: info.type,
    command: null,
    validate: []
  };

  info.names.forEach(function(name, idx) {
    ctx[name] = info.args[idx];
    ctx.validate.push([name,null]);
  });

  process.nextTick(function() {
    ctx.execution = Date.now() - ctx.start;
    callback.apply(ctx, args);
  });
}

/**
 * Mock Memcached class
 */
function Memcached(locations, options) {
  extend(this, Memcached.config, options);

  events.EventEmitter.call(this);

  if (locations)
    this.servers = [].concat(locations);
  else
    this.servers = [];
}

util.inherits(Memcached, events.EventEmitter);

extend(Memcached.prototype, {

  /**
   * Touch a given key
   */
  touch: function(key, ttl, callback) {
    expire(this, key);

    if (cache[key]) cache[key].expires = expires(this, ttl);

    invoke(callback, {self: this,
                      type: 'touch',
                      args: arguments,
                      names: ['key', 'lifetime', 'callback']},
      undefined, (cache[key] ? true : false));
  },

  /**
   * Get the value of a given key
   */
  get: function(key, callback) {
    if (Array.isArray(key)) return this.getMulti(key, callback);

    invoke(callback, {self: this,
                      type: 'get',
                      args: arguments,
                      names: ['key', 'callback']},
      undefined, value(expire(this, key)));
  },

  /**
   * Set the value of a given key
   */
  set: function(key, value, ttl, callback) {
    setkey(this, key, value, ttl);

    invoke(callback, {self: this,
                      type: 'set',
                      args: arguments,
                      names: ['key', 'value', 'lifetime', 'callback']},
      undefined, true);
  },

  /**
   * Get the value of a given key and its CAS id
   */
  gets: function(key, callback) {
    invoke(callback, {self: this,
                      type: 'gets',
                      args: arguments,
                      names: ['key', 'callback']},
      undefined, casvalue(key, expire(this, key)));
  },

  /**
   * Get multiple values in a single call
   */
  getMulti: function(keys, callback) {
    var results = {};

    keys.forEach(function(key) {
      if (expire(this, key))
        results[key] = value(cache[key]);
    });

    invoke(callback, {self: this,
                      type: 'getMulti',
                      args: arguments,
                      names: ['keys', 'callback']},
      undefined, results);
  },

  /**
   * Replace a key, but only if it already exists
   */
  replace: function(key, value, ttl, callback) {
    expire(this, key);

    if (cache[key])
      setkey(this, key, value, ttl);

    invoke(callback, {self: this,
                      type: 'replace',
                      args: arguments,
                      names: ['key', 'value', 'lifetime', 'callback']},
      (cache[key] ? undefined : notStored()),
      (cache[key] ? true : false));
  },

  /**
   * Add a key, but only if it does not already exist
   */
  add: function(key, value, ttl, callback) {
    var exists = expire(this, key);

    if (!exists)
      setkey(this, key, value, ttl);

    invoke(callback, {self: this,
                      type: 'add',
                      args: arguments,
                      names: ['key', 'value', 'lifetime', 'callback']},
      (!exists ? undefined : notStored()),
      (!exists ? true : false));
  },

  /**
   * Update a key, but only if its CAS id matches the provided value
   */
  cas: function(key, value, cas, ttl, callback) {
    var entry = expire(this, key);
    var success = false;

    if (entry && String(entry.cas) === cas) {
      setkey(this, key, value, ttl);
      success = true;
    }

    invoke(callback, {self: this,
                      type: 'cas',
                      args: arguments,
                      names: ['key', 'value', 'cas', 'lifetime', 'callback']},
      undefined, success);
  },

  /**
   * Append a string value to a key, but only if the key already exists
   */
  append: function(key, appendValue, callback) {
    expire(this, key);

    if (cache[key])
      setkey(this, key, String(value(cache[key])) + appendValue, keyttl(this, key));

    invoke(callback, {self: this,
                      type: 'append',
                      args: arguments,
                      names: ['key', 'value', 'callback']},
      (cache[key] ? undefined : notStored()),
      (cache[key] ? true : false));
  },

  /**
   * Prepend a string value to a key, but only if the key already exists
   */
  prepend: function(key, prependValue, callback) {
    expire(this, key);

    if (cache[key])
      setkey(this, key, String(prependValue) + value(cache[key]), keyttl(this, key));

    invoke(callback, {self: this,
                      type: 'prepend',
                      args: arguments,
                      names: ['key', 'value', 'callback']},
      (cache[key] ? undefined : notStored()),
      (cache[key] ? true : false));
  },

  /**
   * Increment a value by an amount, but only if the key already exists
   */
  incr: function(key, amount, callback) {
    expire(this, key);

    if (cache[key])
      setkey(this, key, Number(value(cache[key])) + amount, keyttl(this, key));

    invoke(callback, {self: this,
                      type: 'incr',
                      args: arguments,
                      names: ['key', 'value', 'callback']},
      undefined,
      (cache[key] ? value(cache[key]) : false));
  },

  /**
   * Decrement a value by an amount, but only if the key already exists
   */
  decr: function(key, amount, callback) {
    expire(this, key);

    if (cache[key])
      setkey(this, key, Number(value(cache[key])) - amount, keyttl(this, key));

    invoke(callback, {self: this,
                      type: 'decr',
                      args: arguments,
                      names: ['key', 'value', 'callback']},
      undefined,
      (cache[key] ? value(cache[key]) : false));
  },

  /**
   * Delete a key from the cache
   */
  del: function(key, callback) {
    var exists = expire(this, key);

    if (exists)
      delete cache[key];

    invoke(callback, {self: this,
                      type: 'delete',
                      args: arguments,
                      names: ['key', 'callback']},
      undefined, (exists ? true : false));
  },

  /**
   * Provide callback an array of server version information
   */
  version: function(callback) {
    invoke(callback, {self: this,
                      type: 'version',
                      args: arguments,
                      names: ['callback']},
      undefined,
      this.servers.map(function(s) {
        return {server: s, version: "1.4.20", major: "1", minor: "4", bugfix: "20"};
      }));
  },

  /**
   * Flush the contents of the cache
   */
  flush: function(callback) {
    cache = {};

    invoke(callback, {self: this,
                      type: 'flush',
                      args: arguments,
                      names: ['callback']},
      undefined, [true]);
  },

  /**
   * Provide callback an array of server stats
   */
  stats: function(callback) {
    var info = {
      "pid":1,"uptime":1,"time":Math.round(Date.now()/1000),"version":"1.4.20","libevent":"2.0.22-stable","pointer_size":64,"rusage_user":"0.0","rusage_system":"0.0","curr_connections":1,"total_connections":1,"connection_structures":1,"reserved_fds":0,"cmd_get":0,"cmd_set":0,"cmd_flush":0,"cmd_touch":0,"get_hits":0,"get_misses":0,"delete_misses":0,"delete_hits":0,"incr_misses":0,"incr_hits":0,"decr_misses":0,"decr_hits":0,"cas_misses":0,"cas_hits":0,"cas_badval":0,"touch_hits":0,"touch_misses":0,"auth_cmds":0,"auth_errors":0,"bytes_read":1,"bytes_written":1,"limit_maxbytes":67108864,"accepting_conns":1,"listen_disabled_num":0,"threads":4,"conn_yields":0,"hash_power_level":16,"hash_bytes":524288,"hash_is_expanding":0,"malloc_fails":0,"bytes":0,"curr_items":0,"total_items":0,"expired_unfetched":0,"evicted_unfetched":0,"evictions":0,"reclaimed":0,"crawler_reclaimed":0
    };

    invoke(callback, {self: this,
                      type: 'stats',
                      args: arguments,
                      names: ['callback']},
      undefined,
      this.servers.map(function(s) {
        return extend({server: s}, info);
      }));
  },

  /**
   * Provide callback an array of server settings
   */
  settings: function(callback) {
    var info = {
      "maxbytes":67108864,"maxconns":1024,"tcpport":11211,"udpport":11211,"inter":"NULL","verbosity":0,"oldest":13516,"evictions":"on","domain_socket":"NULL","umask":700,"growth_factor":"1.25","chunk_size":48,"num_threads":4,"num_threads_per_udp":4,"stat_key_prefix":":","detail_enabled":"no","reqs_per_event":20,"cas_enabled":"yes","tcp_backlog":1024,"binding_protocol":"auto-negotiate","auth_enabled_sasl":"no","item_size_max":1048576,"maxconns_fast":"no","hashpower_init":0,"slab_reassign":"no","slab_automove":0,"lru_crawler":"no","lru_crawler_sleep":100,"lru_crawler_tocrawl":0,"tail_repair_time":0,"flush_enabled":"yes","hash_algorithm":"jenkins"
    };

    invoke(callback, {self: this,
                      type: 'settings',
                      args: arguments,
                      names: ['callback']},
      undefined,
      this.servers.map(function(s) {
        return extend({server: s}, info);
      }));
  },

  /**
   * Provide callback an array of server slab information
   */
  slabs: function(callback) {
    var info = {
      "1":{"chunk_size":96,"chunks_per_page":10922,"total_pages":1,"total_chunks":10922,"used_chunks":0,"free_chunks":10922,"free_chunks_end":0,"mem_requested":0,"get_hits":0,"cmd_set":0,"delete_hits":0,"incr_hits":0,"decr_hits":0,"cas_hits":0,"cas_badval":0,"touch_hits":0},"active_slabs":{"undefined":1},"total_malloced":{"undefined":1048512}
    };

    invoke(callback, {self: this,
                      type: 'slabs',
                      args: arguments,
                      names: ['callback']},
      undefined,
      this.servers.map(function(s) {
        return extend({server: s}, info);
      }));
  },

  /**
   * Provide callback an array of server items information
   */
  items: function(callback) {
    var len = Object.keys(cache).length;

    var info = len === 0 ? {} :
      {"1":{"number":len,"age":1,"evicted":0,"evicted_nonzero":0,"evicted_time":0,"outofmemory":0,"tailrepairs":0,"reclaimed":0,"expired_unfetched":0,"evicted_unfetched":0,"crawler_reclaimed":0}}
    ;

    invoke(callback, {self: this,
                      type: 'info',
                      args: arguments,
                      names: ['callback']},
      undefined,
      this.servers.map(function(s) {
        return extend(len > 0 ? {server: s} : {}, info);
      }));
  },

  /**
   * Provide callback with a set of cachedump information
   */
  cachedump: function(server, slabid, limit, callback) {
    var items = Object.keys(cache).map(function(key) {
      return {key: key,
              b: String(cache[key].value).length,
              s: Math.round(cache[key].expires/1000)};
    });

    function result() {
      switch(items.length) {
        case 0: return;
        case 1: return items[0];
        default: return items;
      };
    }

    invoke(callback, {self: this,
                      type: 'cachedump',
                      args: arguments,
                      names: ['server', 'slabid', 'number', 'callback']},
      undefined, result());
  },

  /**
   * End the memcached connection
   */
  end: function() {
  },

  /**
   * Directly access the mock cache
   */
  cache: function() {
    return cache;
  }

});

/**
 * Config options
 */
Memcached.config = {
  maxKeySize: 250,
  maxExpiration: 2592000,
  maxValue: 1048576,
  poolSize: 10,
  algorithm: 'md5',
  reconnect: 18000000,
  timeout: 5000,
  retries: 5,
  failures: 5,
  retry: 30000,
  remove: false,
  keyCompression: true,
  idle: 5000
};

module.exports = Memcached;
<commit_msg>Refactor cache access for eventual support of multiple different cache stores<commit_after>/*
 * Copyright (c) 2015 Jeffrey Hunter
 *
 * Distributed under the MIT license. See the LICENSE file distributed
 * with this work for details and restrictions.
 */

var util = require('util')
  , events = require('events')
;

var cache = {};
var cas = 1;

/**
 * Simple object merge function (shallow merge)
 */
function extend(dest, src) {
  if (src) Object.keys(src).forEach(function(k) { dest[k] = src[k]; });

  if (arguments.length <= 2) return dest;

  return extend.apply(this, [dest].concat(Array.prototype.slice.call(arguments, 2)));
}

/**
 * Return the expiration time from a TTL in seconds
 */
function expires(memcached, ttl) {
  if (ttl === 0) return 0;
  if (ttl > memcached.maxExpiration) return Number(ttl);
  return Date.now() + (Number(ttl) * 1000);
}

/**
 * Handle expiration of an entry with a given key. Returns the entry if
 * it has not expired.
 */
function expire(memcached, key) {
  var cache = memcached.cache();

  if (cache[key] && cache[key].expires !== 0 && cache[key].expires <= Date.now())
    delete cache[key];

  return cache[key];
}

/**
 * Return the equivalent ttl for a key
 */
function keyttl(memcached, key) {
  var cache = memcached.cache();

  return cache[key] && cache[key].expires ?
    ((cache[key].expires - Date.now()) / 1000) :
    0;
}

/**
 * Return the normalized value for a cache entry
 */
function value(entry) {
  return entry ? entry.value : entry;
}

/**
 * Return the normalized value for a cache entry along with its CAS id
 */
function casvalue(key, entry) {
  if (!entry) return;

  var result = {cas: String(entry.cas)};
  result[key] = entry.value;

  return result;
}

/**
 * Sets the value of a key
 */
function setkey(memcached, key, value, ttl) {
  var cache = memcached.cache();

  cache[key] = {value: value, expires: expires(memcached, ttl), cas: (++cas)};
}

/**
 * Returns a not stored error
 */
function notStored() {
  var e = new Error("Item is not stored");
  e.notStored = true;
  return e;
}

/**
 * Invoke a callback with the correct context
 */
function invoke(callback, info) {
  var args = Array.prototype.slice.call(arguments, 2);
  var ctx = {
    start: Date.now(),
    type: info.type,
    command: null,
    validate: []
  };

  info.names.forEach(function(name, idx) {
    ctx[name] = info.args[idx];
    ctx.validate.push([name,null]);
  });

  process.nextTick(function() {
    ctx.execution = Date.now() - ctx.start;
    callback.apply(ctx, args);
  });
}

/**
 * Mock Memcached class
 */
function Memcached(locations, options) {
  extend(this, Memcached.config, options);

  events.EventEmitter.call(this);

  if (locations)
    this.servers = [].concat(locations);
  else
    this.servers = [];
}

util.inherits(Memcached, events.EventEmitter);

extend(Memcached.prototype, {

  /**
   * Touch a given key
   */
  touch: function(key, ttl, callback) {
    expire(this, key);

    var cache = this.cache();

    if (cache[key]) cache[key].expires = expires(this, ttl);

    invoke(callback, {self: this,
                      type: 'touch',
                      args: arguments,
                      names: ['key', 'lifetime', 'callback']},
      undefined, (cache[key] ? true : false));
  },

  /**
   * Get the value of a given key
   */
  get: function(key, callback) {
    if (Array.isArray(key)) return this.getMulti(key, callback);

    invoke(callback, {self: this,
                      type: 'get',
                      args: arguments,
                      names: ['key', 'callback']},
      undefined, value(expire(this, key)));
  },

  /**
   * Set the value of a given key
   */
  set: function(key, value, ttl, callback) {
    setkey(this, key, value, ttl);

    invoke(callback, {self: this,
                      type: 'set',
                      args: arguments,
                      names: ['key', 'value', 'lifetime', 'callback']},
      undefined, true);
  },

  /**
   * Get the value of a given key and its CAS id
   */
  gets: function(key, callback) {
    invoke(callback, {self: this,
                      type: 'gets',
                      args: arguments,
                      names: ['key', 'callback']},
      undefined, casvalue(key, expire(this, key)));
  },

  /**
   * Get multiple values in a single call
   */
  getMulti: function(keys, callback) {
    var self = this;
    var cache = this.cache();
    var results = {};

    keys.forEach(function(key) {
      if (expire(self, key))
        results[key] = value(cache[key]);
    });

    invoke(callback, {self: this,
                      type: 'getMulti',
                      args: arguments,
                      names: ['keys', 'callback']},
      undefined, results);
  },

  /**
   * Replace a key, but only if it already exists
   */
  replace: function(key, value, ttl, callback) {
    expire(this, key);

    var cache = this.cache();

    if (cache[key])
      setkey(this, key, value, ttl);

    invoke(callback, {self: this,
                      type: 'replace',
                      args: arguments,
                      names: ['key', 'value', 'lifetime', 'callback']},
      (cache[key] ? undefined : notStored()),
      (cache[key] ? true : false));
  },

  /**
   * Add a key, but only if it does not already exist
   */
  add: function(key, value, ttl, callback) {
    var exists = expire(this, key);

    if (!exists)
      setkey(this, key, value, ttl);

    invoke(callback, {self: this,
                      type: 'add',
                      args: arguments,
                      names: ['key', 'value', 'lifetime', 'callback']},
      (!exists ? undefined : notStored()),
      (!exists ? true : false));
  },

  /**
   * Update a key, but only if its CAS id matches the provided value
   */
  cas: function(key, value, cas, ttl, callback) {
    var entry = expire(this, key);
    var success = false;

    if (entry && String(entry.cas) === cas) {
      setkey(this, key, value, ttl);
      success = true;
    }

    invoke(callback, {self: this,
                      type: 'cas',
                      args: arguments,
                      names: ['key', 'value', 'cas', 'lifetime', 'callback']},
      undefined, success);
  },

  /**
   * Append a string value to a key, but only if the key already exists
   */
  append: function(key, appendValue, callback) {
    expire(this, key);

    var cache = this.cache();

    if (cache[key])
      setkey(this, key, String(value(cache[key])) + appendValue, keyttl(this, key));

    invoke(callback, {self: this,
                      type: 'append',
                      args: arguments,
                      names: ['key', 'value', 'callback']},
      (cache[key] ? undefined : notStored()),
      (cache[key] ? true : false));
  },

  /**
   * Prepend a string value to a key, but only if the key already exists
   */
  prepend: function(key, prependValue, callback) {
    expire(this, key);

    var cache = this.cache();

    if (cache[key])
      setkey(this, key, String(prependValue) + value(cache[key]), keyttl(this, key));

    invoke(callback, {self: this,
                      type: 'prepend',
                      args: arguments,
                      names: ['key', 'value', 'callback']},
      (cache[key] ? undefined : notStored()),
      (cache[key] ? true : false));
  },

  /**
   * Increment a value by an amount, but only if the key already exists
   */
  incr: function(key, amount, callback) {
    expire(this, key);

    var cache = this.cache();

    if (cache[key])
      setkey(this, key, Number(value(cache[key])) + amount, keyttl(this, key));

    invoke(callback, {self: this,
                      type: 'incr',
                      args: arguments,
                      names: ['key', 'value', 'callback']},
      undefined,
      (cache[key] ? value(cache[key]) : false));
  },

  /**
   * Decrement a value by an amount, but only if the key already exists
   */
  decr: function(key, amount, callback) {
    expire(this, key);

    var cache = this.cache();

    if (cache[key])
      setkey(this, key, Number(value(cache[key])) - amount, keyttl(this, key));

    invoke(callback, {self: this,
                      type: 'decr',
                      args: arguments,
                      names: ['key', 'value', 'callback']},
      undefined,
      (cache[key] ? value(cache[key]) : false));
  },

  /**
   * Delete a key from the cache
   */
  del: function(key, callback) {
    var exists = expire(this, key);

    var cache = this.cache();

    if (exists)
      delete cache[key];

    invoke(callback, {self: this,
                      type: 'delete',
                      args: arguments,
                      names: ['key', 'callback']},
      undefined, (exists ? true : false));
  },

  /**
   * Provide callback an array of server version information
   */
  version: function(callback) {
    invoke(callback, {self: this,
                      type: 'version',
                      args: arguments,
                      names: ['callback']},
      undefined,
      this.servers.map(function(s) {
        return {server: s, version: "1.4.20", major: "1", minor: "4", bugfix: "20"};
      }));
  },

  /**
   * Flush the contents of the cache
   */
  flush: function(callback) {
    cache = {};

    invoke(callback, {self: this,
                      type: 'flush',
                      args: arguments,
                      names: ['callback']},
      undefined, [true]);
  },

  /**
   * Provide callback an array of server stats
   */
  stats: function(callback) {
    var info = {
      "pid":1,"uptime":1,"time":Math.round(Date.now()/1000),"version":"1.4.20","libevent":"2.0.22-stable","pointer_size":64,"rusage_user":"0.0","rusage_system":"0.0","curr_connections":1,"total_connections":1,"connection_structures":1,"reserved_fds":0,"cmd_get":0,"cmd_set":0,"cmd_flush":0,"cmd_touch":0,"get_hits":0,"get_misses":0,"delete_misses":0,"delete_hits":0,"incr_misses":0,"incr_hits":0,"decr_misses":0,"decr_hits":0,"cas_misses":0,"cas_hits":0,"cas_badval":0,"touch_hits":0,"touch_misses":0,"auth_cmds":0,"auth_errors":0,"bytes_read":1,"bytes_written":1,"limit_maxbytes":67108864,"accepting_conns":1,"listen_disabled_num":0,"threads":4,"conn_yields":0,"hash_power_level":16,"hash_bytes":524288,"hash_is_expanding":0,"malloc_fails":0,"bytes":0,"curr_items":0,"total_items":0,"expired_unfetched":0,"evicted_unfetched":0,"evictions":0,"reclaimed":0,"crawler_reclaimed":0
    };

    invoke(callback, {self: this,
                      type: 'stats',
                      args: arguments,
                      names: ['callback']},
      undefined,
      this.servers.map(function(s) {
        return extend({server: s}, info);
      }));
  },

  /**
   * Provide callback an array of server settings
   */
  settings: function(callback) {
    var info = {
      "maxbytes":67108864,"maxconns":1024,"tcpport":11211,"udpport":11211,"inter":"NULL","verbosity":0,"oldest":13516,"evictions":"on","domain_socket":"NULL","umask":700,"growth_factor":"1.25","chunk_size":48,"num_threads":4,"num_threads_per_udp":4,"stat_key_prefix":":","detail_enabled":"no","reqs_per_event":20,"cas_enabled":"yes","tcp_backlog":1024,"binding_protocol":"auto-negotiate","auth_enabled_sasl":"no","item_size_max":1048576,"maxconns_fast":"no","hashpower_init":0,"slab_reassign":"no","slab_automove":0,"lru_crawler":"no","lru_crawler_sleep":100,"lru_crawler_tocrawl":0,"tail_repair_time":0,"flush_enabled":"yes","hash_algorithm":"jenkins"
    };

    invoke(callback, {self: this,
                      type: 'settings',
                      args: arguments,
                      names: ['callback']},
      undefined,
      this.servers.map(function(s) {
        return extend({server: s}, info);
      }));
  },

  /**
   * Provide callback an array of server slab information
   */
  slabs: function(callback) {
    var info = {
      "1":{"chunk_size":96,"chunks_per_page":10922,"total_pages":1,"total_chunks":10922,"used_chunks":0,"free_chunks":10922,"free_chunks_end":0,"mem_requested":0,"get_hits":0,"cmd_set":0,"delete_hits":0,"incr_hits":0,"decr_hits":0,"cas_hits":0,"cas_badval":0,"touch_hits":0},"active_slabs":{"undefined":1},"total_malloced":{"undefined":1048512}
    };

    invoke(callback, {self: this,
                      type: 'slabs',
                      args: arguments,
                      names: ['callback']},
      undefined,
      this.servers.map(function(s) {
        return extend({server: s}, info);
      }));
  },

  /**
   * Provide callback an array of server items information
   */
  items: function(callback) {
    var cache = this.cache();
    var len = Object.keys(cache).length;

    var info = len === 0 ? {} :
      {"1":{"number":len,"age":1,"evicted":0,"evicted_nonzero":0,"evicted_time":0,"outofmemory":0,"tailrepairs":0,"reclaimed":0,"expired_unfetched":0,"evicted_unfetched":0,"crawler_reclaimed":0}}
    ;

    invoke(callback, {self: this,
                      type: 'info',
                      args: arguments,
                      names: ['callback']},
      undefined,
      this.servers.map(function(s) {
        return extend(len > 0 ? {server: s} : {}, info);
      }));
  },

  /**
   * Provide callback with a set of cachedump information
   */
  cachedump: function(server, slabid, limit, callback) {
    var cache = this.cache();
    var items = Object.keys(cache).map(function(key) {
      return {key: key,
              b: String(cache[key].value).length,
              s: Math.round(cache[key].expires/1000)};
    });

    function result() {
      switch(items.length) {
        case 0: return;
        case 1: return items[0];
        default: return items;
      };
    }

    invoke(callback, {self: this,
                      type: 'cachedump',
                      args: arguments,
                      names: ['server', 'slabid', 'number', 'callback']},
      undefined, result());
  },

  /**
   * End the memcached connection
   */
  end: function() {
  },

  /**
   * Directly access the mock cache
   */
  cache: function() {
    return cache;
  }

});

/**
 * Config options
 */
Memcached.config = {
  maxKeySize: 250,
  maxExpiration: 2592000,
  maxValue: 1048576,
  poolSize: 10,
  algorithm: 'md5',
  reconnect: 18000000,
  timeout: 5000,
  retries: 5,
  failures: 5,
  retry: 30000,
  remove: false,
  keyCompression: true,
  idle: 5000
};

module.exports = Memcached;
<|endoftext|>


<commit_before>/**
 * alchemy-api - A node module for calling the Alchemy API
 * See http://www.alchemyapi.com/api/ for details about the API requests and responses
 * Copyright (c) 2012 Jason Morgan
 * MIT Licence
 */

var url = require('url');
var http = require('http');
var querystring = require('querystring');
var extend = require('./extend');

//To install via NPM type the following: `npm install alchemy-api`
var AlchemyAPI = function(api_key, opts) {
	var settings = {
		 format: "json"
		,api_url: "access.alchemyapi.com"
		,protocol: "http"
	};

	settings = extend(settings, opts);

	this.config = {
	    	 api_url: settings.api_url
			,protocol: settings.protocol
	};

	this.options = {
	    apikey: api_key
	   ,outputMode: settings.format
	};

	return this;
};

/**
 * Returns API path depending on method being called
 * @param {String} method The Alchemy API method to call with the request
 * @return {String}
 */
AlchemyAPI.prototype._getMethodType = function(method){
	var regex = new RegExp("^(text|html|url)", "i");
	var results = regex.exec(method);
	if(results != null){
		return results[0].toLowerCase();
	}

	return "";
};

AlchemyAPI.prototype._getPathFromMethod = function(method){
	var results = this._getMethodType(method);
	if(results != ""){
		return "/" + results;
	}
	return "";
};

/**
 * Generates the URL object to be passed to the HTTP request for a specific
 * API method call
 * @param  {Object} query  The query object
 * @param  {String} method The Alchemy API method to call with the request
 * @return {Object} The URL object for this request
 */
AlchemyAPI.prototype._generateNiceUrl = function(query, options, method) {
  var result = url.parse(url.format({
    protocol: this.config.protocol,
    hostname: this.config.api_url,
    pathname: '/calls' + this._getPathFromMethod(method) + '/' + method,
	method: "POST",
    query: options
  }));
  // HACK: Fixes the redirection issue in node 0.4.x
  if (!result.path) { result.path = result.pathname + result.search; }
  //console.log(result);
  //if (this._urlCheck())
  return result;
};

/**
 * Function to do a HTTP request with the current query
 * @param  {Object} request_query The current query object
 * @param  {Function} cb The callback function for the returned data
 * @return {void}
 */
AlchemyAPI.prototype._doRequest = function(request_query, cb) {
  // Pass the requested URL as an object to the get request
  //console.log(request_query.nice);

  //var server = http.createClient(80, this.config.api_url);
  //console.log(request_query.nice.path);
  var req = http.request(request_query.nice, function(res) {
     var data = [];
     res
      .on('data', function(chunk) { data.push(chunk); })
      .on('end', function() {
          var urldata = data.join('').trim();
		  //console.log(urldata);
          var result;
          try {
            result = JSON.parse(urldata);
          } catch (exp) {
			//console.log(request_query.nice.href);
			//console.log(querystring.stringify(request_query.post));
			//console.log(urldata);
			//console.log(urldata);
            result = {'status_code': 500, 'status_text': 'JSON Parse Failed'};
          }
		  //console.log(result);
          cb(null, result);
      })
	 .on("error", function (err) {
		//console.log('response error : ' + err);
		cb(new Error("response.error: " + err), null);
	  });

  });

  req.on("error", function (err) {
		cb(new Error("request.error: " + err), null);
  });

  if(req.method == "POST") {
		req.end(querystring.stringify(request_query.post));
  } else {
		req.end();
  }

};

/**
 * Function to check if a passed string is a valid URL
 * @param  {String} str The URL string to be checked
 * @return {Boolean}
 */
AlchemyAPI.prototype._urlCheck = function(str) {
    var v = new RegExp();
    v.compile("^[A-Za-z]+://[A-Za-z0-9-_]+\\.[A-Za-z0-9-_%&\\?\/.=]+$");
    if (!v.test(str)) return false;
    return true;
};

/**
 * Function to check if a passed string contains html (really just checking for tags <...>)
 * @param  {String} str The text string to be checked
 * @return {Boolean}
 */
AlchemyAPI.prototype._htmlCheck = function(str) {
    var v = new RegExp();
    v.compile("<[A-Za-z][A-Za-z0-9][^>]*>");
	//var v = new RegExp("</\?([a-z][a-z0-9]*)\b[^>]*>", "i");
	//var v = //
	//console.log(v.test(str));
	//console.log(str);
	if (!v.test(str)) return false;
    return true;
};

/**
 * Function to return request parameters based in the AlchemyAPI rest interface
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @param  {String} method The Alchemy rest service method to call
 * @return {Object}
 */
AlchemyAPI.prototype._getQuery = function(data, opts, method) {
	var query = {};
	//console.log(this.options);
	var options = extend(this.options, opts);
	query.data = data;
	query.post = {};
	query.apimethod = "HTML" + method;

	var httpMethod = "POST";

	if(this._urlCheck(data)){
		query.apimethod = "URL" + method;
		httpMethod = "GET";
		options.url = data;
		query.headers = {
			'content-length': '0'
		}
		//console.log("======================1==================");
	}
	else if(!this._htmlCheck(data)){
	    query.apimethod = "Text" + method;
		query.post = {text: data};
		query.headers = {
			 'content-length': '' + querystring.stringify(query.post).length + ''
			,'content-type': 'application/x-www-form-urlencoded'
		};
		//console.log(querystring.stringify(query.post).length);
		//console.log("======================2==================");
	}
	else {
		query.post = {html: data};
		query.headers = {
			 'content-length': '' + querystring.stringify(query.post).length + ''
			,'content-type': 'application/x-www-form-urlencoded'
		};
		//console.log("======================3==================");
	}

	query.nice = this._generateNiceUrl(query.url, options, query.apimethod);
	query.nice.method = httpMethod;
	query.nice.headers = query.headers;

	return query;

};

/**
 * Function to return sentiment of the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @param  {Object} options Options to be passed to the AlchemyAPI (no options are currently supported)
 * @return {Object}
 */
AlchemyAPI.prototype.sentiment = function(data, options, cb) {
    this._doRequest(this._getQuery(data, options, "GetTextSentiment"), cb);
};

/**
 * Function to return relations in the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @return {Object}
 */
AlchemyAPI.prototype.relations = function(data, options, cb) {
	this._doRequest(this._getQuery(data, options, "GetRelations"), cb);
};

/**
 * Function to return concepts in the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @return {Object}
 */
AlchemyAPI.prototype.concepts = function(data, options, cb) {
	this._doRequest(this._getQuery(data, options, "GetRankedConcepts"), cb);
};

/**
 * Function to return entities in the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @return {Object}
 */
AlchemyAPI.prototype.entities = function(data, options, cb) {
	this._doRequest(this._getQuery(data, options, "GetRankedNamedEntities"), cb);
};

/**
 * Function to return keywords in the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @return {Object}
 */
AlchemyAPI.prototype.keywords = function(data, options, cb) {
	this._doRequest(this._getQuery(data, options, "GetRankedKeywords"), cb);
};

/**
 * Function to return category of the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @return {Object}
 */
AlchemyAPI.prototype.category = function(data, options, cb) {
	this._doRequest(this._getQuery(data, options, "GetCategory"), cb);
};

/**
 * Function to return language of the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @return {Object}
 */
AlchemyAPI.prototype.language = function(data, options, cb) {
	this._doRequest(this._getQuery(data, options, "GetLanguage"), cb);
};

/**
 * Function to return author of the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url or html text
 * @return {Object}
 */
AlchemyAPI.prototype.author = function(data, options, cb) {
	if (!this._urlCheck(data) && !this._htmlCheck(data)) {
		cb(new Error('The author method can only be used a URL or HTML encoded text.  Plain text is not supported.'), null);
		return;
	}
	this._doRequest(this._getQuery(data, options, "GetAuthor"), cb);
};

/**
 * Function to return plain text of the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url or html text
 * @return {Object}
 */
AlchemyAPI.prototype.text = function(data, options, cb) {
	if (!this._urlCheck(data) && !this._htmlCheck(data)) {
		cb(new Error('The text method can only be used a URL or HTML encoded text.  Plain text is not supported.'), null);
		return;
	}
	this._doRequest(this._getQuery(data, options, "GetText"), cb);
};

/**
 * Function to return structured plain text of the data passed in retaining semantic meanings
 * @param  {String} data The text to be passed to Alchemy can either a url or html text
 * @return {Object}
 */
AlchemyAPI.prototype.scrape = function(data, options, cb) {
	if (!this._urlCheck(data) && !this._htmlCheck(data)) {
		cb(new Error('The scrape method can only use a URL or HTML encoded text.  Plain text is not supported.'), null);
		return;
	}
	this._doRequest(this._getQuery(data, options, "GetConstraintQuery"), cb);
};

/**
 * Function to return the microformats used in a URL or html text passed in
 * @param  {String} data The text to be passed to Alchemy can either a url or html text
 * @return {Object}
 */
AlchemyAPI.prototype.microformats = function(data, options, cb) {
	if (!this._urlCheck(data) && !this._htmlCheck(data)) {
		cb(new Error('The microformats method can only be used a URL or HTML encoded text.  Plain text is not supported.'), null);
		return;
	}
	this._doRequest(this._getQuery(data, options, "GetMicroformatData"), cb);
};

/**
 * Function to return the RSS/ATOM feeds found in a URL or html text passed in
 * @param  {String} data The text to be passed to Alchemy can either a url or html text
 * @return {Object}
 */
AlchemyAPI.prototype.feeds = function(data, options, cb) {
	if (!this._urlCheck(data) && !this._htmlCheck(data)) {
		cb(new Error('The feeds method can only be used a URL or HTML encoded text.  Plain text is not supported.'), null);
		return;
	}
	this._doRequest(this._getQuery(data, options, "GetFeedLinks"), cb);
};

// Export as main entry point in this module
module.exports = AlchemyAPI;<commit_msg>URL check was not reliable for urls that included special chars like '~'. using node's url module to help identify if it's a url.<commit_after>/**
 * alchemy-api - A node module for calling the Alchemy API
 * See http://www.alchemyapi.com/api/ for details about the API requests and responses
 * Copyright (c) 2012 Jason Morgan
 * MIT Licence
 */

var url = require('url');
var http = require('http');
var querystring = require('querystring');
var extend = require('./extend');

//To install via NPM type the following: `npm install alchemy-api`
var AlchemyAPI = function(api_key, opts) {
	var settings = {
		 format: "json"
		,api_url: "access.alchemyapi.com"
		,protocol: "http"
	};

	settings = extend(settings, opts);

	this.config = {
	    	 api_url: settings.api_url
			,protocol: settings.protocol
	};

	this.options = {
	    apikey: api_key
	   ,outputMode: settings.format
	};

	return this;
};

/**
 * Returns API path depending on method being called
 * @param {String} method The Alchemy API method to call with the request
 * @return {String}
 */
AlchemyAPI.prototype._getMethodType = function(method){
	var regex = new RegExp("^(text|html|url)", "i");
	var results = regex.exec(method);
	if(results != null){
		return results[0].toLowerCase();
	}

	return "";
};

AlchemyAPI.prototype._getPathFromMethod = function(method){
	var results = this._getMethodType(method);
	if(results != ""){
		return "/" + results;
	}
	return "";
};

/**
 * Generates the URL object to be passed to the HTTP request for a specific
 * API method call
 * @param  {Object} query  The query object
 * @param  {String} method The Alchemy API method to call with the request
 * @return {Object} The URL object for this request
 */
AlchemyAPI.prototype._generateNiceUrl = function(query, options, method) {
  var result = url.parse(url.format({
    protocol: this.config.protocol,
    hostname: this.config.api_url,
    pathname: '/calls' + this._getPathFromMethod(method) + '/' + method,
	method: "POST",
    query: options
  }));
  // HACK: Fixes the redirection issue in node 0.4.x
  if (!result.path) { result.path = result.pathname + result.search; }
  //console.log(result);
  //if (this._urlCheck())
  return result;
};

/**
 * Function to do a HTTP request with the current query
 * @param  {Object} request_query The current query object
 * @param  {Function} cb The callback function for the returned data
 * @return {void}
 */
AlchemyAPI.prototype._doRequest = function(request_query, cb) {
  // Pass the requested URL as an object to the get request
  //console.log(request_query.nice);

  //var server = http.createClient(80, this.config.api_url);
  //console.log(request_query.nice.path);
  var req = http.request(request_query.nice, function(res) {
     var data = [];
     res
      .on('data', function(chunk) { data.push(chunk); })
      .on('end', function() {
          var urldata = data.join('').trim();
		  //console.log(urldata);
          var result;
          try {
            result = JSON.parse(urldata);
          } catch (exp) {
			//console.log(request_query.nice.href);
			//console.log(querystring.stringify(request_query.post));
			//console.log(urldata);
			//console.log(urldata);
            result = {'status_code': 500, 'status_text': 'JSON Parse Failed'};
          }
		  //console.log(result);
          cb(null, result);
      })
	 .on("error", function (err) {
		//console.log('response error : ' + err);
		cb(new Error("response.error: " + err), null);
	  });

  });

  req.on("error", function (err) {
		cb(new Error("request.error: " + err), null);
  });

  if(req.method == "POST") {
		req.end(querystring.stringify(request_query.post));
  } else {
		req.end();
  }

};

/**
 * Function to check if a passed string is a valid URL
 * @param  {String} str The URL string to be checked
 * @return {Boolean}
 */
AlchemyAPI.prototype._urlCheck = function(str) {
    var parsed = url.parse(str)
    return (!!parsed.hostname && !!parsed.protocol && str.indexOf(' ') < 0);
};

/**
 * Function to check if a passed string contains html (really just checking for tags <...>)
 * @param  {String} str The text string to be checked
 * @return {Boolean}
 */
AlchemyAPI.prototype._htmlCheck = function(str) {
    var v = new RegExp();
    v.compile("<[A-Za-z][A-Za-z0-9][^>]*>");
	//var v = new RegExp("</\?([a-z][a-z0-9]*)\b[^>]*>", "i");
	//var v = //
	//console.log(v.test(str));
	//console.log(str);
	if (!v.test(str)) return false;
    return true;
};

/**
 * Function to return request parameters based in the AlchemyAPI rest interface
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @param  {String} method The Alchemy rest service method to call
 * @return {Object}
 */
AlchemyAPI.prototype._getQuery = function(data, opts, method) {
	var query = {};
	//console.log(this.options);
	var options = extend(this.options, opts);
	query.data = data;
	query.post = {};
	query.apimethod = "HTML" + method;

	var httpMethod = "POST";

	if(this._urlCheck(data)){
		query.apimethod = "URL" + method;
		httpMethod = "GET";
		options.url = data;
		query.headers = {
			'content-length': '0'
		}
		//console.log("======================1==================");
	}
	else if(!this._htmlCheck(data)){
	    query.apimethod = "Text" + method;
		query.post = {text: data};
		query.headers = {
			 'content-length': '' + querystring.stringify(query.post).length + ''
			,'content-type': 'application/x-www-form-urlencoded'
		};
		//console.log(querystring.stringify(query.post).length);
		//console.log("======================2==================");
	}
	else {
		query.post = {html: data};
		query.headers = {
			 'content-length': '' + querystring.stringify(query.post).length + ''
			,'content-type': 'application/x-www-form-urlencoded'
		};
		//console.log("======================3==================");
	}

	query.nice = this._generateNiceUrl(query.url, options, query.apimethod);
	query.nice.method = httpMethod;
	query.nice.headers = query.headers;

	return query;

};

/**
 * Function to return sentiment of the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @param  {Object} options Options to be passed to the AlchemyAPI (no options are currently supported)
 * @return {Object}
 */
AlchemyAPI.prototype.sentiment = function(data, options, cb) {
    this._doRequest(this._getQuery(data, options, "GetTextSentiment"), cb);
};

/**
 * Function to return relations in the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @return {Object}
 */
AlchemyAPI.prototype.relations = function(data, options, cb) {
	this._doRequest(this._getQuery(data, options, "GetRelations"), cb);
};

/**
 * Function to return concepts in the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @return {Object}
 */
AlchemyAPI.prototype.concepts = function(data, options, cb) {
	this._doRequest(this._getQuery(data, options, "GetRankedConcepts"), cb);
};

/**
 * Function to return entities in the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @return {Object}
 */
AlchemyAPI.prototype.entities = function(data, options, cb) {
	this._doRequest(this._getQuery(data, options, "GetRankedNamedEntities"), cb);
};

/**
 * Function to return keywords in the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @return {Object}
 */
AlchemyAPI.prototype.keywords = function(data, options, cb) {
	this._doRequest(this._getQuery(data, options, "GetRankedKeywords"), cb);
};

/**
 * Function to return category of the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @return {Object}
 */
AlchemyAPI.prototype.category = function(data, options, cb) {
	this._doRequest(this._getQuery(data, options, "GetCategory"), cb);
};

/**
 * Function to return language of the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url, html text or plain text
 * @return {Object}
 */
AlchemyAPI.prototype.language = function(data, options, cb) {
	this._doRequest(this._getQuery(data, options, "GetLanguage"), cb);
};

/**
 * Function to return author of the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url or html text
 * @return {Object}
 */
AlchemyAPI.prototype.author = function(data, options, cb) {
	if (!this._urlCheck(data) && !this._htmlCheck(data)) {
		cb(new Error('The author method can only be used a URL or HTML encoded text.  Plain text is not supported.'), null);
		return;
	}
	this._doRequest(this._getQuery(data, options, "GetAuthor"), cb);
};

/**
 * Function to return plain text of the data passed in
 * @param  {String} data The text to be passed to Alchemy can either a url or html text
 * @return {Object}
 */
AlchemyAPI.prototype.text = function(data, options, cb) {
	if (!this._urlCheck(data) && !this._htmlCheck(data)) {
		cb(new Error('The text method can only be used a URL or HTML encoded text.  Plain text is not supported.'), null);
		return;
	}
	this._doRequest(this._getQuery(data, options, "GetText"), cb);
};

/**
 * Function to return structured plain text of the data passed in retaining semantic meanings
 * @param  {String} data The text to be passed to Alchemy can either a url or html text
 * @return {Object}
 */
AlchemyAPI.prototype.scrape = function(data, options, cb) {
	if (!this._urlCheck(data) && !this._htmlCheck(data)) {
		cb(new Error('The scrape method can only use a URL or HTML encoded text.  Plain text is not supported.'), null);
		return;
	}
	this._doRequest(this._getQuery(data, options, "GetConstraintQuery"), cb);
};

/**
 * Function to return the microformats used in a URL or html text passed in
 * @param  {String} data The text to be passed to Alchemy can either a url or html text
 * @return {Object}
 */
AlchemyAPI.prototype.microformats = function(data, options, cb) {
	if (!this._urlCheck(data) && !this._htmlCheck(data)) {
		cb(new Error('The microformats method can only be used a URL or HTML encoded text.  Plain text is not supported.'), null);
		return;
	}
	this._doRequest(this._getQuery(data, options, "GetMicroformatData"), cb);
};

/**
 * Function to return the RSS/ATOM feeds found in a URL or html text passed in
 * @param  {String} data The text to be passed to Alchemy can either a url or html text
 * @return {Object}
 */
AlchemyAPI.prototype.feeds = function(data, options, cb) {
	if (!this._urlCheck(data) && !this._htmlCheck(data)) {
		cb(new Error('The feeds method can only be used a URL or HTML encoded text.  Plain text is not supported.'), null);
		return;
	}
	this._doRequest(this._getQuery(data, options, "GetFeedLinks"), cb);
};

// Export as main entry point in this module
module.exports = AlchemyAPI;<|endoftext|>


<commit_before>var HTTP = require('http');
var QS = require('querystring');

var Client = module.exports = function(params) {
	params = params || {};

	this.protocol = params.protocol || HTTP;

	this.host = params.host;
	this.port = params.port;
};

Client.prototype.request = function(options, body, callback) {
	var data = false;

	// Defaults
	options.hostname = options.hostname || options.host || this.host;
	options.port = options.port || this.port;
	options.headers = options.headers || {};
	if(options.expect) {
		headers['Expect'] = "100-Continue";
	}
	options.path = options.path || '/';
	if (options.query) {
		options.path += '?' + QS.stringify(options.query);
	}

	if (body) {
		// Try to JSON.stringify and wrap in Buffer
		if (typeof body === 'object') {
			try {
				data = JSON.stringify(body, null, 2);
				data = Buffer(data);

				options.headers['Content-Type'] = "application/json";
				if (!options.headers['Accept'])
					options.headers['Accept'] = "application/json";

			} catch (e) {
				callback(e);
				return;
			}

		} else { // Force to string and wrap in Buffer
			data = Buffer(body + "");
			if (options.headers['Content-Type'])
				options.headers['Content-Type'] = "text/palin";
		}

		options.headers['Content-Length'] = data.length;
	}

	var req = this.protocol.request(options);
	req.on('error', function(err) {
		callback(err);
	});

	req.on('response', function(res) {

		// Aggregate response data
		res.data = "";
		res.on('data', function(d) {
			res.data += d.toString('utf8');
		});

		res.on('end', function() {
			if (res.headers && res.headers['content-type']
					&& res.headers['content-type'].split(/;/g)[0] === "application/json") {
				try {
					res.body = JSON.parse(res.data);
				} catch (e) {
					callback(e);
					return;
				}
			}

			callback(null, res);
		});
	});

	if (data) {
		if (options.expect) {
			req.on('continue', function() {
				req.write(data);
				req.end();

			});

		} else {
			req.write(data);
			req.end();
		}
	} else {
		req.end();
	}
};

Client.prototype.get = function(path, options, callback) {
	if (typeof options === 'function') {
		callback = options;
		options = {};
	}

	options.path = path;
	options.method = "GET";
	this.request(options, null, callback);
};

Client.prototype.post = function(path, body, options, callback) {
	if (typeof options === 'function') {
		callback = options;
		options = {};
	}

	options.path = path;
	options.method = "POST";
	this.request(options, body, callback);
};

Client.prototype.put = function(path, body, options, callback) {
	if (typeof options === 'function') {
		callback = options;
		options = {};
	}

	options.path = path;
	options.method = "PUT";
	this.request(options, body, callback);
};

Client.prototype['delete'] = function(path, options, callback) {
	if (typeof options === 'function') {
		callback = options;
		options = {};
	}

	options.path = path;
	options.method = "DELETE";
	this.request(options, null, callback);
};
<commit_msg>HTTP.request() actually takes host, as hostname is supposed to be parsed via url.parse()<commit_after>var HTTP = require('http');
var QS = require('querystring');

var Client = module.exports = function(params) {
	params = params || {};

	this.protocol = params.protocol || HTTP;

	this.host = params.host;
	this.port = params.port;
};

Client.prototype.request = function(options, body, callback) {
	var data = false;

	// Defaults
	options.host = options.hostname || options.host || this.host;
	options.port = options.port || this.port;
	options.headers = options.headers || {};
	if(options.expect) {
		headers['Expect'] = "100-Continue";
	}
	options.path = options.path || '/';
	if (options.query) {
		options.path += '?' + QS.stringify(options.query);
	}

	if (body) {
		// Try to JSON.stringify and wrap in Buffer
		if (typeof body === 'object') {
			try {
				data = JSON.stringify(body, null, 2);
				data = Buffer(data);

				options.headers['Content-Type'] = "application/json";
				if (!options.headers['Accept'])
					options.headers['Accept'] = "application/json";

			} catch (e) {
				callback(e);
				return;
			}

		} else { // Force to string and wrap in Buffer
			data = Buffer(body + "");
			if (options.headers['Content-Type'])
				options.headers['Content-Type'] = "text/palin";
		}

		options.headers['Content-Length'] = data.length;
	}

	var req = this.protocol.request(options);
	req.on('error', function(err) {
		callback(err);
	});

	req.on('response', function(res) {

		// Aggregate response data
		res.data = "";
		res.on('data', function(d) {
			res.data += d.toString('utf8');
		});

		res.on('end', function() {
			if (res.headers && res.headers['content-type']
					&& res.headers['content-type'].split(/;/g)[0] === "application/json") {
				try {
					res.body = JSON.parse(res.data);
				} catch (e) {
					callback(e);
					return;
				}
			}

			callback(null, res);
		});
	});

	if (data) {
		if (options.expect) {
			req.on('continue', function() {
				req.write(data);
				req.end();

			});

		} else {
			req.write(data);
			req.end();
		}
	} else {
		req.end();
	}
};

Client.prototype.get = function(path, options, callback) {
	if (typeof options === 'function') {
		callback = options;
		options = {};
	}

	options.path = path;
	options.method = "GET";
	this.request(options, null, callback);
};

Client.prototype.post = function(path, body, options, callback) {
	if (typeof options === 'function') {
		callback = options;
		options = {};
	}

	options.path = path;
	options.method = "POST";
	this.request(options, body, callback);
};

Client.prototype.put = function(path, body, options, callback) {
	if (typeof options === 'function') {
		callback = options;
		options = {};
	}

	options.path = path;
	options.method = "PUT";
	this.request(options, body, callback);
};

Client.prototype['delete'] = function(path, options, callback) {
	if (typeof options === 'function') {
		callback = options;
		options = {};
	}

	options.path = path;
	options.method = "DELETE";
	this.request(options, null, callback);
};
<|endoftext|>


<commit_before>// CLI tools
var bluebird  = require('bluebird');
var debug     = require('debug')('index');
var size      = 20;

// requires parameters
var page      = process.env.batch || 1;
var startDate = process.env.start;
var dataDir   = process.env.dataDir;
debug(page, startDate, dataDir);
var gigatool  = require('./lib/gigatool')(size, page, dataDir);

bluebird.all([gigatool.clean(startDate), gigatool.continuous()])
  .finally(function(){
    process.exit(0);
  });<commit_msg>change env vars to uppercase and expose runners<commit_after>#!/usr/bin/env node

var bluebird  = require('bluebird');
var gigatool  = require('./lib/gigatool');
var debug     = require('debug')('index');
var size      = 20;

var page      = process.env.BATCH;
var startDate = process.env.START;
var dataDir   = process.env.DATADIR;
debug(page, startDate, dataDir);

// requires parameters
if (!process.env.BATCH) {
  throw new Error('BATCH environment variable is needed');
}

tool = gigatool(size, page, dataDir);

module.exports = bluebird.all([tool.clean(startDate), tool.continuous()])
  .finally(function(){
    if (!process.env.TEST) {
      process.exit(0);
    }
  });<|endoftext|>


<commit_before>var postcss = require('postcss');

module.exports = postcss.plugin('postcss-interpolate', (options = {}) => {
    return css => {

        // Html font-size value
        var rootString;

        // Get 'rootString' value
        css.walkDecls(decl => {
          if ((decl.parent.selector.indexOf('html') > -1) &&
            (decl.prop.indexOf('font-size') > -1)) {
            rootString = decl.value;
            return
          }
        });

        css.walkDecls(decl => {

          // Get property line
          if (decl.value.indexOf('interpolate(') !== -1) {

            // Get array of interolate() values
            const interpolateArray = decl.value.match(/\(([^)]+)\)/)[1].split(',');

            if (interpolateArray.length > 3) {

              function isString(array){
                return isNaN(parseFloat(array))
              }

              function isOdd(array){
                return (array.length & 1)
              }

              var startFrom, directionViewport;

              function checkDirection(arrayForCheck){

                if (isOdd(arrayForCheck) == false && isString(arrayForCheck[0]) == false) {
                  directionViewport = 'vw';
                  startFrom = 0
                }

                // With direction
                else if (isOdd(arrayForCheck) == true && isString(arrayForCheck[0]) == true) {

                  var direction = arrayForCheck[0];

                  if (direction == 'horizontally') {
                    directionViewport = 'vw';
                  } else if (direction == 'vertically') {
                    directionViewport = 'vh';
                  } else {
                    // WARN
                  }
                  startFrom = 1;
                }

                // Syntax error
                else {
                  // WARN
                }

                return
              }

              var mediaArray = [];
              var valueArray = [];
              const rule = decl.parent;
              const root = rule.parent;

              // Generate media and values arrays
              function generateArrays() {
                checkDirection(interpolateArray)

                for (var i = startFrom; i < interpolateArray.length; i += 2) {
                  mediaArray.push(interpolateArray[i]);
                  valueArray.push(interpolateArray[i + 1]);
                }
                return
              }

              // Get html font-size value
              function getHtmlValue(actualViewport) {
                actualViewport = parseFloat(actualViewport)

                function isPx(i) {
                  return i.indexOf('px') > -1
                }

                function isRem(i) {
                  return i.indexOf('rem') > -1
                }

                if (mediaArray.every(isPx) && valueArray.every(isPx)) {
                  return 1;
                }

                else if (mediaArray.every(isPx) && valueArray.every(isRem)) {
                  var rootMediaArray = [];
                  var rootValueArray = [];
                  var bigger = [];
                  var rootReadyArray;

                  if (rootString.indexOf('interpolate(') > -1) {
                    rootReadyArray = rootString.match(/\(([^)]+)\)/)[1].split(',');

                    checkDirection(rootReadyArray)

                    // Get array of mediaqueries and array of values
                    for (var i = startFrom; i < rootReadyArray.length; i += 2) {
                      rootMediaArray.push(rootReadyArray[i]);
                      rootValueArray.push(rootReadyArray[i + 1]);
                    }

                    var minrootMediaArray, maxrootMediaArray, minrootValueArray, maxrootValueArray;

                    for (var i = 0; i < rootMediaArray.length; i++) {
                      if ((actualViewport <= parseInt(rootMediaArray[i], 10)) && (actualViewport >= parseInt(rootMediaArray[i - 1], 10))) {
                        maxrootMediaArray = parseFloat(rootMediaArray[i]);
                        minrootMediaArray = parseFloat(rootMediaArray[i - 1]);
                        maxrootValueArray = parseFloat(rootValueArray[i]);
                        minrootValueArray = parseFloat(rootValueArray[i - 1]);
                      }
                    }

                    var formula;

                    if (minrootMediaArray == maxrootMediaArray) {
                      formula = parseFloat(minrootValueArray)
                    }

                    else {
                      formula = minrootValueArray + (maxrootValueArray - minrootValueArray) * (actualViewport - minrootMediaArray) / (maxrootMediaArray - minrootMediaArray);
                    }

                    return formula
                  }
                }

                else {
                  console.log('LOL')
                  // WARN
                }
              }

              // First @media rule
              function firstRule(){
                decl.replaceWith({
                  prop: decl.prop,
                  value: interpolateArray[startFrom + 1]
                });
              }

              // Middle @media rule
              function middleRule(){
                for (var i = 0; i < mediaArray.length - 1; i++) {
                  for (var i = 0; i < valueArray.length - 1; i++) {

                    // Generate @media atrule
                    var mediaAtrule = postcss.atRule({
                      name: 'media',
                      params: 'screen and (min-width: ' + mediaArray[i] + ')'
                    })

                    var maxMedia = mediaArray[i + 1];
                    var minMedia = mediaArray[i];
                    var maxValue = valueArray[i + 1];
                    var minValue = valueArray[i];

                    // Append current selector with properties and values
                    mediaAtrule.append({
                      selector: rule.selector
                    }).walkRules(function(selector) {
                      selector.append({
                        prop: decl.prop,
                        value: 'calc(' + minValue + ' + ' + (parseFloat(maxValue) - parseFloat(minValue)) + ' * (100' + directionViewport + ' - ' + minMedia + ') / ' + ((parseFloat(maxMedia) - parseFloat(minMedia)) / getHtmlValue(maxMedia)) + ')'
                      });
                    });

                    // Insert middle mediaquery
                    root.insertAfter(root.last, mediaAtrule)
                  }
                }
              }

              // Last @media rule
              function lastRule() {

                // Generate @media atrule
                var lastMedia = postcss.atRule({
                  name: 'media',
                  params: 'screen and (min-width: ' + mediaArray[mediaArray.length - 1] + ')'
                })

                // Append current selector with properties and values
                lastMedia.append({
                  selector: rule.selector
                }).walkRules(function(selector) {
                  selector.append({
                    prop: decl.prop,
                    value: valueArray[valueArray.length - 1]
                  });
                });

                // Insert last media rule
                root.insertAfter(root.last, lastMedia);
              }

              // Get array of mediaqueries and array of values
              generateArrays()

              // Generate and insert firsl value rule
              firstRule()

              // Generate and insert middle @media rule
              middleRule()

              // Generate and insert last @media rule
              lastRule()
            }
          }
      });
    };
});
<commit_msg>fix space and direction problem<commit_after>var postcss = require('postcss');

module.exports = postcss.plugin('postcss-interpolate', (options = {}) => {
    return css => {

        // Html font-size value
        var rootString;

        // Get 'rootString' value
        css.walkDecls(decl => {
          if ((decl.parent.selector.indexOf('html') > -1) &&
            (decl.prop.indexOf('font-size') > -1)) {
            rootString = decl.value;
            return
          }
        });

        css.walkDecls(decl => {

          // Get property line
          if (decl.value.indexOf('interpolate(') !== -1) {

            // Get array of interolate() values
            const interpolateArray = decl.value.match(/\(([^)]+)\)/)[1].split(',');

            if (interpolateArray.length > 3) {

              function isString(array){
                return isNaN(parseFloat(array))
              }

              function isOdd(array){
                return (array.length & 1)
              }

              var startFrom, directionViewport;

              function checkDirection(arrayForCheck){

                if (isOdd(arrayForCheck) == false && isString(arrayForCheck[0]) == false) {
                  directionViewport = 'vw';
                  startFrom = 0
                }

                // With direction
                else if (isOdd(arrayForCheck) == true && isString(arrayForCheck[0]) == true) {

                  var direction = arrayForCheck[0];

                  if (direction.indexOf('horizontally') > -1) {
                    directionViewport = 'vw';
                  } else if (direction.indexOf('vertically') > -1) {
                    directionViewport = 'vh';
                  } else {
                    // WARN
                  }
                  startFrom = 1;
                }

                // Syntax error
                else {
                  // WARN
                }

                return
              }

              var mediaArray = [];
              var valueArray = [];
              const rule = decl.parent;
              const root = rule.parent;

              // Generate media and values arrays
              function generateArrays() {
                checkDirection(interpolateArray)

                for (var i = startFrom; i < interpolateArray.length; i += 2) {
                  mediaArray.push(interpolateArray[i]);
                  valueArray.push(interpolateArray[i + 1]);
                }
                return
              }

              // Get html font-size value
              function getHtmlValue(actualViewport) {
                actualViewport = parseFloat(actualViewport)

                function isPx(i) {
                  return i.indexOf('px') > -1
                }

                function isRem(i) {
                  return i.indexOf('rem') > -1
                }

                if (mediaArray.every(isPx) && valueArray.every(isPx)) {
                  return 1;
                }

                else if (mediaArray.every(isPx) && valueArray.every(isRem)) {
                  var rootMediaArray = [];
                  var rootValueArray = [];
                  var bigger = [];
                  var rootReadyArray;

                  if (rootString.indexOf('interpolate(') > -1) {
                    rootReadyArray = rootString.match(/\(([^)]+)\)/)[1].split(',');

                    checkDirection(rootReadyArray)

                    // Get array of mediaqueries and array of values
                    for (var i = startFrom; i < rootReadyArray.length; i += 2) {
                      rootMediaArray.push(rootReadyArray[i]);
                      rootValueArray.push(rootReadyArray[i + 1]);
                    }

                    var minrootMediaArray, maxrootMediaArray, minrootValueArray, maxrootValueArray;

                    for (var i = 0; i < rootMediaArray.length; i++) {
                      if ((actualViewport <= parseInt(rootMediaArray[i], 10)) && (actualViewport >= parseInt(rootMediaArray[i - 1], 10))) {
                        maxrootMediaArray = parseFloat(rootMediaArray[i]);
                        minrootMediaArray = parseFloat(rootMediaArray[i - 1]);
                        maxrootValueArray = parseFloat(rootValueArray[i]);
                        minrootValueArray = parseFloat(rootValueArray[i - 1]);
                      }
                    }

                    var formula;

                    if (minrootMediaArray == maxrootMediaArray) {
                      formula = parseFloat(minrootValueArray)
                    }

                    else {
                      formula = minrootValueArray + (maxrootValueArray - minrootValueArray) * (actualViewport - minrootMediaArray) / (maxrootMediaArray - minrootMediaArray);
                    }

                    return formula
                  }
                }

                else {
                  console.log('LOL')
                  // WARN
                }
              }

              // First @media rule
              function firstRule(){
                decl.replaceWith({
                  prop: decl.prop,
                  value: interpolateArray[startFrom + 1]
                });
              }

              // Middle @media rule
              function middleRule(){
                for (var i = 0; i < mediaArray.length - 1; i++) {
                  for (var i = 0; i < valueArray.length - 1; i++) {

                    // Generate @media atrule
                    var mediaAtrule = postcss.atRule({
                      name: 'media',
                      params: 'screen and (min-width: ' + mediaArray[i] + ')'
                    })

                    var maxMedia = mediaArray[i + 1];
                    var minMedia = mediaArray[i];
                    var maxValue = valueArray[i + 1];
                    var minValue = valueArray[i];

                    // Append current selector with properties and values
                    mediaAtrule.append({
                      selector: rule.selector
                    }).walkRules(function(selector) {
                      selector.append({
                        prop: decl.prop,
                        value: 'calc(' + minValue + ' + ' + (parseFloat(maxValue) - parseFloat(minValue)) + ' * (100' + directionViewport + ' - ' + minMedia + ') / ' + ((parseFloat(maxMedia) - parseFloat(minMedia)) / getHtmlValue(maxMedia)) + ')'
                      });
                    });

                    // Insert middle mediaquery
                    root.insertAfter(root.last, mediaAtrule)
                  }
                }
              }

              // Last @media rule
              function lastRule() {

                // Generate @media atrule
                var lastMedia = postcss.atRule({
                  name: 'media',
                  params: 'screen and (min-width: ' + mediaArray[mediaArray.length - 1] + ')'
                })

                // Append current selector with properties and values
                lastMedia.append({
                  selector: rule.selector
                }).walkRules(function(selector) {
                  selector.append({
                    prop: decl.prop,
                    value: valueArray[valueArray.length - 1]
                  });
                });

                // Insert last media rule
                root.insertAfter(root.last, lastMedia);
              }

              // Get array of mediaqueries and array of values
              generateArrays()

              // Generate and insert firsl value rule
              firstRule()

              // Generate and insert middle @media rule
              middleRule()

              // Generate and insert last @media rule
              lastRule()
            }
          }
      });
    };
});
<|endoftext|>


<commit_before>var through  = require('through')
  , crypto   = require('crypto')
  , concat   = require('concat-stream')

function cacheify (db, cachee, hash) {

  if (!hash) {
    hash = function (content) {
      return crypto
        .createHash('md5')
        .update(content)
        .digest('hex');
    }
  }

  return function (file) {
    var data = ''

    return through(write, end)

    function write (buf) { data += buf; }
    function end () {
      var hashed = hash(data)
      var self = this

      db.get(hashed, function (err, transformed) {
        if (err) {
          var join = concat(function(d) {
            d = d || ' '
            db.put(hashed, d)
            self.queue(d)
            self.queue(null)
          })

          var tf = cachee(file)
          tf.pipe(join)
          tf.write(data)
          tf.end()
        }
        else {
          self.queue(transformed)
          self.queue(null)
        }
      });
    }
  }
}

module.exports = cacheify
<commit_msg>Changed cacheify to optionally take a function that returns a leveldb api compatible instance instead of the instance itself, also added a filter function. Changed the api significantly.<commit_after>var through  = require('through')
  , crypto   = require('crypto')
  , concat   = require('concat-stream')

function cacheify (cachee, _db) {

  /**
   * Default to taking the md5 of a file
   */
  hash = function (content) {
    return crypto
      .createHash('md5')
      .update(content)
      .digest('hex');
  }

  /**
   * Default to caching every file
   */
  filter = function() {
    return true
  }

  /**
   * Transforms a file, reading it from cache instead if the hash exists in the
   * cache already.
   */
  transform = function (file) {
    // Early exit if we don't match the filter
    if (!filter(file)) {
      return through()
    }

    var data = ''

    return through(write, end)

    // Build up the file data...
    function write (buf) { data += buf; }

    // Do our transform if we need to, or get it from the cache if we can.
    function end () {
      var hashed = hash(data)
      var self = this
      var db = _db

      // Let db be a function that returns a leveldb api compatible object.
      if (typeof db === 'function') db = db()

      db.get(hashed, function (err, transformed) {
        if (err) {
          var join = concat(function(d) {
            d = d || ' '
            db.put(hashed, d)
            self.queue(d)
            self.queue(null)
          })

          var tf = cachee(file)
          tf.pipe(join)
          tf.write(data)
          tf.end()
        }
        else {
          self.queue(transformed)
          self.queue(null)
        }
      });
    }
  }

  /**
   * Lets you set a custom filter function. A filter function receives one
   * argument, the name of the file. Return true to check cacheify for a file,
   * false to use a default through stream.
   */
  transform.filter = function(fn) {
    filter = fn

    return transform
  }

  /**
   * Lets you set a custom hash function. The hash function receives two
   * arguments, the contents of the file and the name of the file. It's up to
   * you if and how you choose to use them.
   *
   * If you pass a RegExp object instead of a function, we'll wrap it in a
   * function that checks the filename on the regexp, returning true for a
   * match.
   */
  transform.hash = function(_fn) {
    var fn = _fn
    if (fn instanceof RegExp) {
      fn = function(name) {
        return _fn.test(name)
      }
    }
    hash = fn

    return transform
  }

  return transform
}

module.exports = cacheify
<|endoftext|>


<commit_before>/* eslint-env node */
const FastbootTransform = require('fastboot-transform');

module.exports = {
  name: 'ember-offline',
  options: {
    nodeAssets: {
      'offline-js': {
        vendor: {
          include: ['offline.js', 'themes/*'],
          processTree(input) {
            return FastbootTransform(input);
          }
        }
      }
    }
  },
  included(app) {
    this._super.included.apply(this, arguments);
    this._ensureThisImport();

    let config = this.project.config(app.env);

    let themes = config.emberOffline.themes;
    let themesDir =  'vendor/offline-js/themes';

    if(!themes) {
      themes = {
        theme: 'chrome',
        indicator: false,
        language: 'english',
      }
    }

    if (themes.theme) {
      this.import(`${themesDir}/offline-theme-${themes.theme}.css`);
      this.import(`${themesDir}/offline-language-${themes.language}.css`);
    }

    if (themes.indicator) {
      this.import(`${themesDir}/offline-theme-${themes.indicator}-indicator.css`);
      this.import(`${themesDir}/offline-language-${themes.language}-indicator.css`);
    }

    this.import('vendor/offline-js/offline.js');
  },
  _ensureThisImport() {
    if (!this.import) {
      this._findHost = function findHostShim() {
        let current = this;
        let app;

        do {
          app = current.app || app;
        } while (current.parent.parent && (current = current.parent));
        return app;
      };
      this.import = function importShim(asset, options) {
        let app = this._findHost();
        app.import(asset, options);
      };
    }
  }
};
<commit_msg>fixing travis build<commit_after>/* eslint-env node */
'use strict';

const FastbootTransform = require('fastboot-transform');

module.exports = {
  name: 'ember-offline',
  options: {
    nodeAssets: {
      'offline-js': {
        vendor: {
          include: ['offline.js', 'themes/*'],
          processTree(input) {
            return FastbootTransform(input);
          }
        }
      }
    }
  },
  included(app) {
    this._super.included.apply(this, arguments);
    this._ensureThisImport();

    let config = this.project.config(app.env);

    let themes = config.emberOffline.themes;
    let themesDir =  'vendor/offline-js/themes';

    if(!themes) {
      themes = {
        theme: 'chrome',
        indicator: false,
        language: 'english',
      }
    }

    if (themes.theme) {
      this.import(`${themesDir}/offline-theme-${themes.theme}.css`);
      this.import(`${themesDir}/offline-language-${themes.language}.css`);
    }

    if (themes.indicator) {
      this.import(`${themesDir}/offline-theme-${themes.indicator}-indicator.css`);
      this.import(`${themesDir}/offline-language-${themes.language}-indicator.css`);
    }

    this.import('vendor/offline-js/offline.js');
  },
  _ensureThisImport() {
    if (!this.import) {
      this._findHost = function findHostShim() {
        let current = this;
        let app;

        do {
          app = current.app || app;
        } while (current.parent.parent && (current = current.parent));
        return app;
      };
      this.import = function importShim(asset, options) {
        let app = this._findHost();
        app.import(asset, options);
      };
    }
  }
};
<|endoftext|>


<commit_before>var express = require('express')
var path = require('path')
var app = express()
var favicon = require('express-favicon')

app.use('/static', express.static(path.join(__dirname, './dist/static/')))
app.use('/data', express.static(path.join(__dirname, './src/data')))
app.use('/img', express.static(path.join(__dirname, './src/assets/img')))

app.use(favicon(path.join(__dirname, './favicon.ico')))

app.get('/', function (req, res) {
  res.sendFile('index.html', { root: path.join(__dirname, './dist') })
})

var server = app.listen(3030, '0.0.0.0', function () {

  var host = server.address().address
  var port = server.address().port

  console.log('sml listening at http://%s:%s', host, port)

})
<commit_msg>2016年07月22日16:06:46<commit_after>var express = require('express')
var path = require('path')
var app = express()
var favicon = require('express-favicon')

app.use('/static', express.static(path.join(__dirname, './dist/static/')))
app.use('/data', express.static(path.join(__dirname, './src/data')))
app.use('/img', express.static(path.join(__dirname, './src/assets/img')))

app.use(favicon(path.join(__dirname, './favicon.ico')))

app.get(['/', '/checkout/:token/:pwd'], function (req, res) {
  res.sendFile('index.html', { root: path.join(__dirname, './dist') })
})

var server = app.listen(3030, '0.0.0.0', function () {

  var host = server.address().address
  var port = server.address().port

  console.log('sml listening at http://%s:%s', host, port)

})
<|endoftext|>


<commit_before>'use strict'

const jade = require('jade')

exports.name = 'jade'
exports.outputFormat = 'html'

exports.compile = function (source, options) {
  const fn = jade.compile(source, options)
  return {
    fn,
    dependencies: fn.dependencies
  }
}
exports.compileClient = function (source, options) {
  return jade.compileClientWithDependenciesTracked(source, options)
}
exports.compileFile = function (path, options) {
  const fn = jade.compileFile(path, options)
  return {
    fn,
    dependencies: fn.dependencies
  }
}
exports.compileFileClient = function (path, options) {
  const fs = require('fs')

  // There is no compileFileClientWithDependenciesTracked so gotta do it
  // manually.
  options = options || {}
  options.filename = options.filename || path
  return exports.compileClient(fs.readFileSync(path, 'utf8'), options)
}
<commit_msg>Update eslint<commit_after>'use strict'

const jade = require('jade')

exports.name = 'jade'
exports.outputFormat = 'html'

exports.compile = function (source, options) {
  const fn = jade.compile(source, options)
  return {
    fn,
    dependencies: fn.dependencies
  }
}

exports.compileClient = function (source, options) {
  return jade.compileClientWithDependenciesTracked(source, options)
}

exports.compileFile = function (path, options) {
  const fn = jade.compileFile(path, options)
  return {
    fn,
    dependencies: fn.dependencies
  }
}

exports.compileFileClient = function (path, options) {
  const fs = require('fs')

  // There is no compileFileClientWithDependenciesTracked so gotta do it
  // manually.
  options = options || {}
  options.filename = options.filename || path
  return exports.compileClient(fs.readFileSync(path, 'utf8'), options)
}
<|endoftext|>