<commit_before>/*!
 * parallax.js v1.1 (http://pixelcog.github.io/parallax.js/)
 * Copyright (c) 2014 PixelCog, Inc.
 * Licensed under MIT (https://github.com/pixelcog/parallax.js/blob/master/LICENSE)
 */
(function($,window,document,undefined){(function(){var lastTime=0;var vendors=["ms","moz","webkit","o"];for(var x=0;x<vendors.length&&!window.requestAnimationFrame;++x){window.requestAnimationFrame=window[vendors[x]+"RequestAnimationFrame"];window.cancelAnimationFrame=window[vendors[x]+"CancelAnimationFrame"]||window[vendors[x]+"CancelRequestAnimationFrame"]}if(!window.requestAnimationFrame)window.requestAnimationFrame=function(callback,element){var currTime=(new Date).getTime();var timeToCall=Math.max(0,16-(currTime-lastTime));var id=window.setTimeout(function(){callback(currTime+timeToCall)},timeToCall);lastTime=currTime+timeToCall;return id};if(!window.cancelAnimationFrame)window.cancelAnimationFrame=function(id){clearTimeout(id)}})();var $body=$("body");var $window=$(window);function Parallax(element,options){var self=this;if(typeof options=="object"){delete options.refresh;delete options.render;$.extend(this,options)}this.$element=$(element);if(!this.imageSrc&&this.$element.is("img")){this.imageSrc=this.$element.attr("src")}var positions=(this.position+"").toLowerCase().match(/\S+/g)||[];if(positions.length<1){positions.push("center")}if(positions.length==1){positions.push(positions[0])}if(positions[0]=="top"||positions[0]=="bottom"||positions[1]=="left"||positions[1]=="right"){self.positionX=positions[1];self.positionY=positions[0]}else{self.positionX=positions[0];self.positionY=positions[1]}if(this.positionX!=undefined)positions[0]=this.positionX.toLowerCase();if(this.positionY!=undefined)positions[1]=this.positionY.toLowerCase();if(this.positionX!="left"&&this.positionX!="right"){if(isNaN(parseInt(this.positionX))){this.positionX="center"}else{this.positionX=parseInt(this.positionX)}}if(this.positionY!="top"&&this.positionY!="bottom"){if(isNaN(parseInt(this.positionY))){this.positionY="center"}else{this.positionY=parseInt(this.positionY)}}this.position=this.positionX+(isNaN(this.positionX)?"":"px")+" "+this.positionY+(isNaN(this.positionY)?"":"px");if(navigator.userAgent.match(/(iPod|iPhone|iPad)/)){if(this.iosFix&&!this.$element.is("img")){this.$element.css({backgroundImage:"url("+encodeURIComponent(this.imageSrc)+")",backgroundSize:"cover",backgroundPosition:this.position})}return this}this.$mirror=$("<div />").prependTo("body");this.$slider=$("<img />").prependTo(this.$mirror);this.$mirror.addClass("parallax-mirror").css({visibility:"hidden",zIndex:this.zIndex,position:"fixed",top:0,left:0,overflow:"hidden"});this.$slider.addClass("parallax-slider").one("load",function(){if(!self.naturalHeight||!self.naturalWidth){self.naturalHeight=this.naturalHeight||this.height||1;self.naturalWidth=this.naturalWidth||this.width||1}self.aspectRatio=self.naturalWidth/self.naturalHeight;Parallax.isSetup||Parallax.setup();Parallax.sliders.push(self);Parallax.isFresh=false;Parallax.requestRender()});this.$slider[0].src=this.imageSrc;if(this.naturalHeight&&this.naturalWidth||this.$slider[0].complete){this.$slider.trigger("load")}}$.extend(Parallax.prototype,{speed:.2,bleed:0,zIndex:-100,iosFix:true,position:"center",refresh:function(){this.boxWidth=this.$element.width();this.boxHeight=this.$element.height()+this.bleed*2;this.boxOffsetTop=this.$element.offset().top-this.bleed;this.boxOffsetLeft=this.$element.offset().left;this.boxOffsetBottom=this.boxOffsetTop+this.boxHeight;var margin=0;var winHeight=Parallax.winHeight;var imageHeightMin=winHeight-(winHeight-this.boxHeight)*this.speed|0;if(imageHeightMin*this.aspectRatio>=this.boxWidth){this.imageWidth=imageHeightMin*this.aspectRatio|0;this.imageHeight=imageHeightMin;this.offsetBaseTop=0;margin=this.imageWidth-this.boxWidth;if(this.positionX=="left"){this.offsetLeft=0}else if(this.positionX=="right"){this.offsetLeft=-margin}else if(!isNaN(this.positionX)){this.offsetLeft=Math.max(this.positionX,-margin)}else{this.offsetLeft=-margin/2|0}}else{this.imageWidth=this.boxWidth;this.imageHeight=this.boxWidth/this.aspectRatio|0;this.offsetLeft=0;margin=this.imageHeight-imageHeightMin;if(this.positionY=="top"){this.offsetBaseTop=0}else if(this.positionY=="bottom"){this.offsetBaseTop=-margin}else if(!isNaN(this.positionY)){this.offsetBaseTop=Math.max(this.positionY,-margin)}else{this.offsetBaseTop=-margin/2|0}}},render:function(){var scrollTop=Parallax.scrollTop;var scrollLeft=Parallax.scrollLeft;var scrollBottom=scrollTop+Parallax.winHeight;if(this.boxOffsetBottom>scrollTop&&this.boxOffsetTop<scrollBottom){this.visibility="visible"}else{this.visibility="hidden"}this.mirrorTop=this.boxOffsetTop-scrollTop;this.mirrorLeft=this.boxOffsetLeft-scrollLeft;this.offsetTop=this.offsetBaseTop-this.mirrorTop*(1-this.speed);this.$mirror.css({transform:"translate3d(0px, 0px, 0px)",visibility:this.visibility,top:this.mirrorTop,left:this.mirrorLeft,height:this.boxHeight,width:this.boxWidth});this.$slider.css({transform:"translate3d(0px, 0px, 0px)",position:"absolute",top:this.offsetTop,left:this.offsetLeft,height:this.imageHeight,width:this.imageWidth})}});$.extend(Parallax,{scrollTop:0,scrollLeft:0,winHeight:0,winWidth:0,docHeight:1<<30,docWidth:1<<30,sliders:[],isReady:false,isFresh:false,isBusy:false,setup:function(){if(this.isReady)return;$window.on("scroll.px.parallax load.px.parallax",function(){var scrollTopMax=Parallax.docHeight-Parallax.winHeight;var scrollLeftMax=Parallax.docWidth-Parallax.winWidth;Parallax.scrollTop=Math.max(0,Math.min(scrollTopMax,$window.scrollTop()));Parallax.scrollLeft=Math.max(0,Math.min(scrollLeftMax,$window.scrollLeft()));Parallax.requestRender()}).on("resize.px.parallax load.px.parallax",function(){Parallax.winHeight=$window.height();Parallax.winWidth=$window.width();Parallax.docHeight=$(document).height();Parallax.docWidth=$(document).width();Parallax.isFresh=false;Parallax.requestRender()});this.isReady=true},configure:function(options){if(typeof options=="object"){delete options.refresh;delete options.render;$.extend(this.prototype,options)}},refresh:function(){$.each(this.sliders,function(){this.refresh()});this.isFresh=true},render:function(){this.isFresh||this.refresh();$.each(this.sliders,function(){this.render()})},requestRender:function(){var self=this;if(!this.isBusy){this.isBusy=true;window.requestAnimationFrame(function(){self.render();self.isBusy=false})}}});function Plugin(option){return this.each(function(){var $this=$(this);var options=typeof option=="object"&&option;if(this==window||this==document||$this.is("body")){Parallax.configure(options)}else if(!$this.data("px.parallax")){options=$.extend({},$this.data(),options);$this.data("px.parallax",new Parallax(this,options))}if(typeof option=="string"){Parallax[option]()}})}var old=$.fn.parallax;$.fn.parallax=Plugin;$.fn.parallax.Constructor=Parallax;$.fn.parallax.noConflict=function(){$.fn.parallax=old;return this};$(document).on("ready.px.parallax.data-api",function(){$('[data-parallax="scroll"]').parallax()})})(jQuery,window,document);
<commit_msg>update parallax.min.js to reflect changes in parallax.js (closes #9)<commit_after>/*!
 * parallax.js v1.1 (http://pixelcog.github.io/parallax.js/)
 * Copyright (c) 2014 PixelCog, Inc.
 * Licensed under MIT (https://github.com/pixelcog/parallax.js/blob/master/LICENSE)
 */
(function($,window,document,undefined){(function(){var lastTime=0;var vendors=["ms","moz","webkit","o"];for(var x=0;x<vendors.length&&!window.requestAnimationFrame;++x){window.requestAnimationFrame=window[vendors[x]+"RequestAnimationFrame"];window.cancelAnimationFrame=window[vendors[x]+"CancelAnimationFrame"]||window[vendors[x]+"CancelRequestAnimationFrame"]}if(!window.requestAnimationFrame)window.requestAnimationFrame=function(callback,element){var currTime=(new Date).getTime();var timeToCall=Math.max(0,16-(currTime-lastTime));var id=window.setTimeout(function(){callback(currTime+timeToCall)},timeToCall);lastTime=currTime+timeToCall;return id};if(!window.cancelAnimationFrame)window.cancelAnimationFrame=function(id){clearTimeout(id)}})();var $body=$("body");var $window=$(window);function Parallax(element,options){var self=this;if(typeof options=="object"){delete options.refresh;delete options.render;$.extend(this,options)}this.$element=$(element);if(!this.imageSrc&&this.$element.is("img")){this.imageSrc=this.$element.attr("src")}var positions=(this.position+"").toLowerCase().match(/\S+/g)||[];if(positions.length<1){positions.push("center")}if(positions.length==1){positions.push(positions[0])}if(positions[0]=="top"||positions[0]=="bottom"||positions[1]=="left"||positions[1]=="right"){self.positionX=positions[1];self.positionY=positions[0]}else{self.positionX=positions[0];self.positionY=positions[1]}if(this.positionX!=undefined)positions[0]=this.positionX.toLowerCase();if(this.positionY!=undefined)positions[1]=this.positionY.toLowerCase();if(this.positionX!="left"&&this.positionX!="right"){if(isNaN(parseInt(this.positionX))){this.positionX="center"}else{this.positionX=parseInt(this.positionX)}}if(this.positionY!="top"&&this.positionY!="bottom"){if(isNaN(parseInt(this.positionY))){this.positionY="center"}else{this.positionY=parseInt(this.positionY)}}this.position=this.positionX+(isNaN(this.positionX)?"":"px")+" "+this.positionY+(isNaN(this.positionY)?"":"px");if(navigator.userAgent.match(/(iPod|iPhone|iPad)/)){if(this.iosFix&&!this.$element.is("img")){this.$element.css({backgroundImage:"url("+this.imageSrc+")",backgroundSize:"cover",backgroundPosition:this.position})}return this}this.$mirror=$("<div />").prependTo("body");this.$slider=$("<img />").prependTo(this.$mirror);this.$mirror.addClass("parallax-mirror").css({visibility:"hidden",zIndex:this.zIndex,position:"fixed",top:0,left:0,overflow:"hidden"});this.$slider.addClass("parallax-slider").one("load",function(){if(!self.naturalHeight||!self.naturalWidth){self.naturalHeight=this.naturalHeight||this.height||1;self.naturalWidth=this.naturalWidth||this.width||1}self.aspectRatio=self.naturalWidth/self.naturalHeight;Parallax.isSetup||Parallax.setup();Parallax.sliders.push(self);Parallax.isFresh=false;Parallax.requestRender()});this.$slider[0].src=this.imageSrc;if(this.naturalHeight&&this.naturalWidth||this.$slider[0].complete){this.$slider.trigger("load")}}$.extend(Parallax.prototype,{speed:.2,bleed:0,zIndex:-100,iosFix:true,position:"center",refresh:function(){this.boxWidth=this.$element.width();this.boxHeight=this.$element.height()+this.bleed*2;this.boxOffsetTop=this.$element.offset().top-this.bleed;this.boxOffsetLeft=this.$element.offset().left;this.boxOffsetBottom=this.boxOffsetTop+this.boxHeight;var margin=0;var winHeight=Parallax.winHeight;var imageHeightMin=winHeight-(winHeight-this.boxHeight)*this.speed|0;if(imageHeightMin*this.aspectRatio>=this.boxWidth){this.imageWidth=imageHeightMin*this.aspectRatio|0;this.imageHeight=imageHeightMin;this.offsetBaseTop=0;margin=this.imageWidth-this.boxWidth;if(this.positionX=="left"){this.offsetLeft=0}else if(this.positionX=="right"){this.offsetLeft=-margin}else if(!isNaN(this.positionX)){this.offsetLeft=Math.max(this.positionX,-margin)}else{this.offsetLeft=-margin/2|0}}else{this.imageWidth=this.boxWidth;this.imageHeight=this.boxWidth/this.aspectRatio|0;this.offsetLeft=0;margin=this.imageHeight-imageHeightMin;if(this.positionY=="top"){this.offsetBaseTop=0}else if(this.positionY=="bottom"){this.offsetBaseTop=-margin}else if(!isNaN(this.positionY)){this.offsetBaseTop=Math.max(this.positionY,-margin)}else{this.offsetBaseTop=-margin/2|0}}},render:function(){var scrollTop=Parallax.scrollTop;var scrollLeft=Parallax.scrollLeft;var scrollBottom=scrollTop+Parallax.winHeight;if(this.boxOffsetBottom>scrollTop&&this.boxOffsetTop<scrollBottom){this.visibility="visible"}else{this.visibility="hidden"}this.mirrorTop=this.boxOffsetTop-scrollTop;this.mirrorLeft=this.boxOffsetLeft-scrollLeft;this.offsetTop=this.offsetBaseTop-this.mirrorTop*(1-this.speed);this.$mirror.css({transform:"translate3d(0px, 0px, 0px)",visibility:this.visibility,top:this.mirrorTop,left:this.mirrorLeft,height:this.boxHeight,width:this.boxWidth});this.$slider.css({transform:"translate3d(0px, 0px, 0px)",position:"absolute",top:this.offsetTop,left:this.offsetLeft,height:this.imageHeight,width:this.imageWidth})}});$.extend(Parallax,{scrollTop:0,scrollLeft:0,winHeight:0,winWidth:0,docHeight:1<<30,docWidth:1<<30,sliders:[],isReady:false,isFresh:false,isBusy:false,setup:function(){if(this.isReady)return;$window.on("scroll.px.parallax load.px.parallax",function(){var scrollTopMax=Parallax.docHeight-Parallax.winHeight;var scrollLeftMax=Parallax.docWidth-Parallax.winWidth;Parallax.scrollTop=Math.max(0,Math.min(scrollTopMax,$window.scrollTop()));Parallax.scrollLeft=Math.max(0,Math.min(scrollLeftMax,$window.scrollLeft()));Parallax.requestRender()}).on("resize.px.parallax load.px.parallax",function(){Parallax.winHeight=$window.height();Parallax.winWidth=$window.width();Parallax.docHeight=$(document).height();Parallax.docWidth=$(document).width();Parallax.isFresh=false;Parallax.requestRender()});this.isReady=true},configure:function(options){if(typeof options=="object"){delete options.refresh;delete options.render;$.extend(this.prototype,options)}},refresh:function(){$.each(this.sliders,function(){this.refresh()});this.isFresh=true},render:function(){this.isFresh||this.refresh();$.each(this.sliders,function(){this.render()})},requestRender:function(){var self=this;if(!this.isBusy){this.isBusy=true;window.requestAnimationFrame(function(){self.render();self.isBusy=false})}}});function Plugin(option){return this.each(function(){var $this=$(this);var options=typeof option=="object"&&option;if(this==window||this==document||$this.is("body")){Parallax.configure(options)}else if(!$this.data("px.parallax")){options=$.extend({},$this.data(),options);$this.data("px.parallax",new Parallax(this,options))}if(typeof option=="string"){Parallax[option]()}})}var old=$.fn.parallax;$.fn.parallax=Plugin;$.fn.parallax.Constructor=Parallax;$.fn.parallax.noConflict=function(){$.fn.parallax=old;return this};$(document).on("ready.px.parallax.data-api",function(){$('[data-parallax="scroll"]').parallax()})})(jQuery,window,document);<|endoftext|>


<commit_before>var template = require('lodash.template');
var reEscape = require('lodash._reescape');
var reEvaluate = require('lodash._reevaluate');
var reInterpolate = require('lodash._reinterpolate');

var forcedSettings = {
  escape: reEscape,
  evaluate: reEvaluate,
  interpolate: reInterpolate
};

module.exports = function(tmpl, data){
  var fn = template(tmpl, forcedSettings);

  var wrapped = function(o) {
    if (typeof o === 'undefined' || typeof o.file === 'undefined') throw new Error('Failed to provide the current file as "file" to the template');
    return fn(o);
  };

  return (data ? wrapped(data) : wrapped);
};
<commit_msg>more readable<commit_after>var template = require('lodash.template');
var reEscape = require('lodash._reescape');
var reEvaluate = require('lodash._reevaluate');
var reInterpolate = require('lodash._reinterpolate');

var forcedSettings = {
  escape: reEscape,
  evaluate: reEvaluate,
  interpolate: reInterpolate
};

module.exports = function(tmpl, data){
  var fn = template(tmpl, forcedSettings);

  var wrapped = function(o) {
    if (typeof o === 'undefined' || typeof o.file === 'undefined'){
      throw new Error('Failed to provide the current file as "file" to the template');
    }
    return fn(o);
  };

  return (data ? wrapped(data) : wrapped);
};
<|endoftext|>


<commit_before>var inherits = require('util').inherits;
var myUtil = require('./util');
var TermState = require('./term_state.js');
var DomOutput = require('./output/dom.js');
var DomInput = require('./input/dom.js');

var outputs = {
	plain: require('./output/plain.js'),
	html: require('./output/html.js'),
	ansi: require('./output/ansi.js')
};

var CSI_PATTERN = /^\x1b\[([?!>]?)([0-9;]*)([@A-Za-z`]?)/;
var DCS_PATTERN = /^\x1bP([0-9;@A-Za-z`]*)\x1b\\/;
var OSC_PATTERN = /^\x1b\]([0-9]+);([^\x07]*)(\x07?)/;

/**
* Terminal is the glue between a TerminalState and the escape sequence
* interpreters.
* @constructor
*/
function Terminal(options) {
	Terminal.super_.call(this, { decodeStrings: false });

	this.options = myUtil.extend({
		columns: 80,
		rows: 24,
		attributes: {}
	}, options || {});
	this.state = new TermState(this.options);
	this.oldChunk = null;
	this.on('pipe', this._pipe);
}
inherits(Terminal, require('stream').Writable);
module.exports = Terminal;

Terminal.prototype.handlers = {
	chr: require('./handler/chr.js'),
	esc: require('./handler/esc.js'),
	csi: require('./handler/csi.js'),
	sgr: require('./handler/sgr.js'),
	dcs: require('./handler/dcs.js'),
	mode: require('./handler/mode.js'),
	osc: require('./handler/osc.js'),
};

/**
* emits resize on the reader of this class
* @param {ReadableStream} a Readable Stream
* @private
*/
Terminal.prototype._pipe = function(src) {
	var onresize = function(size) {
		if(typeof src.resize === 'function') // assume it's a pty.js-object
			src.resize(size.columns, size.rows);
		src.emit('resize', size);
	};
	this.on('resize', onresize)
		.on('unpipe', function(src) {
			src.removeListener(onresize);
		});
};

/**
* Takes a chunk of data, interprets its escape sequences, and fills backend state
* @alias Terminal.prototype.write
* @see http://nodejs.org/docs/latest/api/stream.html#stream_writable_write_chunk_encoding_callback
*/
Terminal.prototype._write = function(chunk, encoding, callback) {
	var len = 1;
	if(typeof chunk !== 'string')
		chunk = chunk.toString();

	if(this.oldChunk !== null) {
		chunk = this.oldChunk + chunk;
		this.oldChunk = null;
	}

	while(chunk.length > 0 && len > 0) {
		len = this.callHandler('chr', chunk[0], chunk);
		if(len === null) {
			for(len = 1; len < chunk.length &&
				!(chunk[len] in this.handlers.chr); len++);

			this.state.write(chunk.substr(0, len));
		}

		if(len > 0)
			chunk = chunk.slice(len);
	}
	if(chunk.length !== 0)
		this.oldChunk = chunk;
	this.emit('ready');
	callback();
};

/**
* calls an handler
* @param type {string} one of the following types:
*

*
chr: interprets special characters (such as \r or \b)

*
esc: interprets simple escape characters starting with \x1b

*
csi: interprets CSI escape sequences

*
sgr: interprets SGR escape sequences

*
dcs: interprets DCS escape sequences

*
mode: interprets mode sequences

*
osc: interpretes OSC escape sequences

*

* @param cmd {string} command to execute
* @param ... {...array} passed to the command function
*/
Terminal.prototype.callHandler = function(type, cmd) {
	if(!(type in this.handlers && cmd in this.handlers[type]))
		return null;

	var args = Array.prototype.slice.call(arguments, 1);
	if(typeof this.handlers[type][cmd] === 'string')
		cmd = this.handlers[type][cmd];

	result = this.handlers[type][cmd].apply(this, args);
	return result === undefined ? 1 : result;
};

/**
* reads a CSI command sequence from a chunk of data
* @params chunk {string} a chunk of data to parse
* @returns {{args: Number|Array, mod: String, cmd: String, length: Number}}
*/
Terminal.prototype.parseCsi = function(chunk) {
	var i;
	var match = CSI_PATTERN.exec(chunk);
	if(match === null)
		return null;
	var args = match[2] === '' ? [] : match[2].split(';');
	for(i = 0; i < args.length; i++)
		args[i] = +args[i];
	return {
		args: args,
		mod: match[1],
		cmd: match[3],
		length: match[0].length
	};
};

/**
* reads a OSC command sequence from a chunk of data
* @params chunk {string} a chunk of data to parse
* @returns {{args: String|Array, mod: String, cmd: String, length: Number,
* terminated: Boolean}}
*/
Terminal.prototype.parseOsc = function(chunk) {
	var match = OSC_PATTERN.exec(chunk);
	if(match === null)
		return null;
	return {
		args: match[2].split(';'),
		cmd: match[1],
		terminated: match[3] === '\x07',
		length: match[0].length
	};
};

/**
* reads a OSC command sequence from a chunk of data
* @params chunk {string} a chunk of data to parse
* @returns {{args: String|Array, mod: String, cmd: String, length: Number}}
*/
Terminal.prototype.parseDcs = function(chunk) {
	var i;
	var match = DCS_PATTERN.exec(chunk);
	if(match === null)
		return null;
	return {
		args: [null,null],
		mod: match[1],
		cmd: match[1],
		length: match[0].length
	};
};

/**
* sets up a DOM element as Terminal in- and output
* @params element a DOM element node
* @params options options field
* @returns a terminal input which can be used to send data to a pty
*/
Terminal.prototype.dom = function(element, opts) {
	var input = new DomInput(element, this.state, opts);
	var output = new DomOutput(this.state, this, element, opts);

	return input;
};

/**
* @see TermState.prototype.toString
*/
Terminal.prototype.toString = function(format) {
	if(typeof format !== 'string')
		return this.state.toString.apply(this.state, arguments);

	var output = new outputs[format](this.state);

	return output.toString();
};
<commit_msg>lib/terminal.js: fix documentation.<commit_after>var inherits = require('util').inherits;
var myUtil = require('./util');
var TermState = require('./term_state.js');
var DomOutput = require('./output/dom.js');
var DomInput = require('./input/dom.js');

var outputs = {
	plain: require('./output/plain.js'),
	html: require('./output/html.js'),
	ansi: require('./output/ansi.js')
};

var CSI_PATTERN = /^\x1b\[([?!>]?)([0-9;]*)([@A-Za-z`]?)/;
var DCS_PATTERN = /^\x1bP([0-9;@A-Za-z`]*)\x1b\\/;
var OSC_PATTERN = /^\x1b\]([0-9]+);([^\x07]*)(\x07?)/;

/**
* Terminal is the glue between a TerminalState and the escape sequence
* interpreters.
* @constructor
*/
function Terminal(options) {
	Terminal.super_.call(this, { decodeStrings: false });

	this.options = myUtil.extend({
		columns: 80,
		rows: 24,
		attributes: {}
	}, options || {});
	this.state = new TermState(this.options);
	this.oldChunk = null;
	this.on('pipe', this._pipe);
}
inherits(Terminal, require('stream').Writable);
module.exports = Terminal;

Terminal.prototype.handlers = {
	chr: require('./handler/chr.js'),
	esc: require('./handler/esc.js'),
	csi: require('./handler/csi.js'),
	sgr: require('./handler/sgr.js'),
	dcs: require('./handler/dcs.js'),
	mode: require('./handler/mode.js'),
	osc: require('./handler/osc.js'),
};

/**
* emits resize on the reader of this class
* @param {ReadableStream} a Readable Stream
* @private
*/
Terminal.prototype._pipe = function(src) {
	var onresize = function(size) {
		if(typeof src.resize === 'function') // assume it's a pty.js-object
			src.resize(size.columns, size.rows);
		src.emit('resize', size);
	};
	this.on('resize', onresize)
		.on('unpipe', function(src) {
			src.removeListener(onresize);
		});
};

/**
* Takes a chunk of data, interprets its escape sequences, and fills backend state
* @alias Terminal.prototype.write
* @see http://nodejs.org/docs/latest/api/stream.html#stream_writable_write_chunk_encoding_callback
*/
Terminal.prototype._write = function(chunk, encoding, callback) {
	var len = 1;
	if(typeof chunk !== 'string')
		chunk = chunk.toString();

	if(this.oldChunk !== null) {
		chunk = this.oldChunk + chunk;
		this.oldChunk = null;
	}

	while(chunk.length > 0 && len > 0) {
		len = this.callHandler('chr', chunk[0], chunk);
		if(len === null) {
			for(len = 1; len < chunk.length &&
				!(chunk[len] in this.handlers.chr); len++);

			this.state.write(chunk.substr(0, len));
		}

		if(len > 0)
			chunk = chunk.slice(len);
	}
	if(chunk.length !== 0)
		this.oldChunk = chunk;
	this.emit('ready');
	callback();
};

/**
* calls an handler
* @param type {string} one of the following types:
*

*
chr: interprets special characters (such as \r or \b)

*
esc: interprets simple escape characters starting with \x1b

*
csi: interprets CSI escape sequences

*
sgr: interprets SGR escape sequences

*
dcs: interprets DCS escape sequences

*
mode: interprets mode sequences

*
osc: interpretes OSC escape sequences

*

* @param cmd {string} command to execute
* @param ... {...array} passed to the command function
*/
Terminal.prototype.callHandler = function(type, cmd) {
	if(!(type in this.handlers && cmd in this.handlers[type]))
		return null;

	var args = Array.prototype.slice.call(arguments, 1);
	if(typeof this.handlers[type][cmd] === 'string')
		cmd = this.handlers[type][cmd];

	result = this.handlers[type][cmd].apply(this, args);
	return result === undefined ? 1 : result;
};

/**
* reads a CSI command sequence from a chunk of data
* @param chunk {string} a chunk of data to parse
* @returns {{args: Number|Array, mod: String, cmd: String, length: Number}}
*/
Terminal.prototype.parseCsi = function(chunk) {
	var i;
	var match = CSI_PATTERN.exec(chunk);
	if(match === null)
		return null;
	var args = match[2] === '' ? [] : match[2].split(';');
	for(i = 0; i < args.length; i++)
		args[i] = +args[i];
	return {
		args: args,
		mod: match[1],
		cmd: match[3],
		length: match[0].length
	};
};

/**
* reads a OSC command sequence from a chunk of data
* @param chunk {string} a chunk of data to parse
* @returns {{args: String|Array, mod: String, cmd: String, length: Number,
* terminated: Boolean}}
*/
Terminal.prototype.parseOsc = function(chunk) {
	var match = OSC_PATTERN.exec(chunk);
	if(match === null)
		return null;
	return {
		args: match[2].split(';'),
		cmd: match[1],
		terminated: match[3] === '\x07',
		length: match[0].length
	};
};

/**
* reads a OSC command sequence from a chunk of data
* @param chunk {string} a chunk of data to parse
* @returns {{args: String|Array, mod: String, cmd: String, length: Number}}
*/
Terminal.prototype.parseDcs = function(chunk) {
	var i;
	var match = DCS_PATTERN.exec(chunk);
	if(match === null)
		return null;
	return {
		args: [null,null],
		mod: match[1],
		cmd: match[1],
		length: match[0].length
	};
};

/**
* sets up a DOM element as Terminal in- and output
* @param element a DOM element node
* @param options options field
* @returns a terminal input which can be used to send data to a pty
*/
Terminal.prototype.dom = function(element, opts) {
	var input = new DomInput(element, this.state, opts);
	var output = new DomOutput(this.state, this, element, opts);

	return input;
};

/**
* will give a string representation of the terminal
* @param format one of 'html', 'ansi', 'plain' if not present,
* {@link TermState#toString} will be called.
* @returns string representation of the terminal
*/
Terminal.prototype.toString = function(format) {
	if(typeof format !== 'string')
		return this.state.toString.apply(this.state, arguments);

	var output = new outputs[format](this.state);

	return output.toString();
};
<|endoftext|>


<commit_before>/*eslint-disable no-multi-str */

'use strict';

var envify = require('loose-envify/custom');
var grunt = require('grunt');
var UglifyJS = require('uglify-js');
var uglifyify = require('uglifyify');
var derequire = require('derequire');
var aliasify = require('aliasify');
var collapser = require('bundle-collapser/plugin');

var envifyDev = envify({NODE_ENV: process.env.NODE_ENV || 'development'});
var envifyProd = envify({NODE_ENV: process.env.NODE_ENV || 'production'});

var shimSharedModules = aliasify.configure({
  'aliases': {
    'react/lib/React': 'react/lib/ReactUMDShim',
    'react/lib/ReactCurrentOwner': 'react/lib/ReactCurrentOwnerUMDShim',
    'react/lib/ReactComponentTreeHook': 'react/lib/ReactComponentTreeHookUMDShim',
  },
});

var shimDOMModules = aliasify.configure({
  'aliases': {
    './ReactAddonsDOMDependencies': {relative: './ReactAddonsDOMDependenciesUMDShim'},
  },
});

var SIMPLE_TEMPLATE =
  grunt.file.read('./grunt/data/header-template-short.txt');

var LICENSE_TEMPLATE =
  grunt.file.read('./grunt/data/header-template-extended.txt');

function minify(src) {
  return UglifyJS.minify(src, {fromString: true}).code;
}

// TODO: move this out to another build step maybe.
function bannerify(src) {
  var version = grunt.config.data.pkg.version;
  var packageName = this.data.packageName || this.data.standalone;
  return (
    grunt.template.process(
      LICENSE_TEMPLATE,
      {data: {package: packageName, version: version}}
    ) +
    src
  );
}

function simpleBannerify(src) {
  var version = grunt.config.data.pkg.version;
  var packageName = this.data.packageName || this.data.standalone;
  return (
    grunt.template.process(
      SIMPLE_TEMPLATE,
      {data: {package: packageName, version: version}}
    ) +
    src
  );
}

// Our basic config which we'll add to to make our other builds
var basic = {
  entries: [
    './build/node_modules/react/lib/ReactUMDEntry.js',
  ],
  outfile: './build/react.js',
  debug: false,
  standalone: 'React',
  // Apply as global transform so that we also envify fbjs and any other deps
  globalTransforms: [envifyDev],
  plugins: [collapser],
  after: [derequire, simpleBannerify],
};

var min = {
  entries: [
    './build/node_modules/react/lib/ReactUMDEntry.js',
  ],
  outfile: './build/react.min.js',
  debug: false,
  standalone: 'React',
  // Envify twice. The first ensures that when we uglifyify, we have the right
  // conditions to exclude requires. The global transform runs on deps.
  transforms: [envifyProd, uglifyify],
  globalTransforms: [envifyProd],
  plugins: [collapser],
  // No need to derequire because the minifier will mangle
  // the "require" calls.

  after: [minify, bannerify],
};

var addons = {
  entries: [
    './build/node_modules/react/lib/ReactWithAddonsUMDEntry.js',
  ],
  outfile: './build/react-with-addons.js',
  debug: false,
  standalone: 'React',
  packageName: 'React (with addons)',
  transforms: [shimDOMModules],
  globalTransforms: [envifyDev],
  plugins: [collapser],
  after: [derequire, simpleBannerify],
};

var addonsMin = {
  entries: [
    './build/node_modules/react/lib/ReactWithAddonsUMDEntry.js',
  ],
  outfile: './build/react-with-addons.min.js',
  debug: false,
  standalone: 'React',
  packageName: 'React (with addons)',
  transforms: [shimDOMModules, envifyProd, uglifyify],
  globalTransforms: [envifyProd],
  plugins: [collapser],
  // No need to derequire because the minifier will mangle
  // the "require" calls.

  after: [minify, bannerify],
};

// The DOM Builds
var dom = {
  entries: [
    './build/node_modules/react-dom/lib/ReactDOMUMDEntry.js',
  ],
  outfile: './build/react-dom.js',
  debug: false,
  standalone: 'ReactDOM',
  // Apply as global transform so that we also envify fbjs and any other deps
  transforms: [shimSharedModules],
  globalTransforms: [envifyDev],
  plugins: [collapser],
  after: [derequire, simpleBannerify],
};

var domMin = {
  entries: [
    './build/node_modules/react-dom/lib/ReactDOMUMDEntry.js',
  ],
  outfile: './build/react-dom.min.js',
  debug: false,
  standalone: 'ReactDOM',
  // Envify twice. The first ensures that when we uglifyify, we have the right
  // conditions to exclude requires. The global transform runs on deps.
  transforms: [shimSharedModules, envifyProd, uglifyify],
  globalTransforms: [envifyProd],
  plugins: [collapser],
  // No need to derequire because the minifier will mangle
  // the "require" calls.

  after: [minify, bannerify],
};

var domServer = {
  entries: [
    './build/node_modules/react-dom/lib/ReactDOMServerUMDEntry.js',
  ],
  outfile: './build/react-dom-server.js',
  debug: false,
  standalone: 'ReactDOMServer',
  // Apply as global transform so that we also envify fbjs and any other deps
  transforms: [shimSharedModules],
  globalTransforms: [envifyDev],
  plugins: [collapser],
  after: [derequire, simpleBannerify],
};

var domServerMin = {
  entries: [
    './build/node_modules/react-dom/lib/ReactDOMServerUMDEntry.js',
  ],
  outfile: './build/react-dom-server.min.js',
  debug: false,
  standalone: 'ReactDOMServer',
  // Envify twice. The first ensures that when we uglifyify, we have the right
  // conditions to exclude requires. The global transform runs on deps.
  transforms: [shimSharedModules, envifyProd, uglifyify],
  globalTransforms: [envifyProd],
  plugins: [collapser],
  // No need to derequire because the minifier will mangle
  // the "require" calls.

  after: [minify, bannerify],
};

var domFiber = {
  entries: [
    './build/node_modules/react-dom/lib/ReactDOMFiber.js',
  ],
  outfile: './build/react-dom-fiber.js',
  debug: false,
  standalone: 'ReactDOMFiber',
  // Apply as global transform so that we also envify fbjs and any other deps
  transforms: [shimSharedModules],
  globalTransforms: [envifyDev],
  plugins: [collapser],
  after: [derequire, simpleBannerify],
};

var domFiberMin = {
  entries: [
    './build/node_modules/react-dom/lib/ReactDOMFiber.js',
  ],
  outfile: './build/react-dom-fiber.min.js',
  debug: false,
  standalone: 'ReactDOMFiber',
  // Envify twice. The first ensures that when we uglifyify, we have the right
  // conditions to exclude requires. The global transform runs on deps.
  transforms: [shimSharedModules, envifyProd, uglifyify],
  globalTransforms: [envifyProd],
  plugins: [collapser],
  // No need to derequire because the minifier will mangle
  // the "require" calls.

  after: [minify, bannerify],
};

module.exports = {
  basic: basic,
  min: min,
  addons: addons,
  addonsMin: addonsMin,
  dom: dom,
  domMin: domMin,
  domServer: domServer,
  domServerMin: domServerMin,
  domFiber: domFiber,
  domFiberMin: domFiberMin,
};
<commit_msg>Fix UMD bundles, making safe to use as required modules (#7840)<commit_after>/*eslint-disable no-multi-str */

'use strict';

var envify = require('loose-envify/custom');
var grunt = require('grunt');
var UglifyJS = require('uglify-js');
var uglifyify = require('uglifyify');
var derequire = require('derequire');
var aliasify = require('aliasify');
var collapser = require('bundle-collapser/plugin');

var envifyDev = envify({NODE_ENV: process.env.NODE_ENV || 'development'});
var envifyProd = envify({NODE_ENV: process.env.NODE_ENV || 'production'});

var shimSharedModules = aliasify.configure({
  'aliases': {
    'react/lib/React': 'react/lib/ReactUMDShim',
    'react/lib/ReactCurrentOwner': 'react/lib/ReactCurrentOwnerUMDShim',
    'react/lib/ReactComponentTreeHook': 'react/lib/ReactComponentTreeHookUMDShim',
  },
});

var shimDOMModules = aliasify.configure({
  'aliases': {
    './ReactAddonsDOMDependencies': {relative: './ReactAddonsDOMDependenciesUMDShim'},
  },
});

var SIMPLE_TEMPLATE =
  grunt.file.read('./grunt/data/header-template-short.txt');

var LICENSE_TEMPLATE =
  grunt.file.read('./grunt/data/header-template-extended.txt');

function minify(src) {
  return UglifyJS.minify(src, {fromString: true}).code;
}

// TODO: move this out to another build step maybe.
function bannerify(src) {
  var version = grunt.config.data.pkg.version;
  var packageName = this.data.packageName || this.data.standalone;
  return (
    grunt.template.process(
      LICENSE_TEMPLATE,
      {data: {package: packageName, version: version}}
    ) +
    src
  );
}

function simpleBannerify(src) {
  var version = grunt.config.data.pkg.version;
  var packageName = this.data.packageName || this.data.standalone;
  return (
    grunt.template.process(
      SIMPLE_TEMPLATE,
      {data: {package: packageName, version: version}}
    ) +
    src
  );
}

// What is happening here???
// I'm glad you asked. It became really to make our bundle splitting work.
// Everything is fine in node and when bundling with those packages, but when
// using our pre-packaged files, the splitting didn't work. Specifically due to
// the UMD wrappers defining their own require and creating their own encapsulated
// "registry" scope, we couldn't require across the boundaries. Webpack tries to
// be smart and looks for top-level requires (even when aliasing to a bundle),
// but since we didn't have those, we couldn't require 'react' from 'react-dom'.
// But we are already shimming in some modules that look for a global React
// variable. So we write a wrapper around the UMD bundle that browserify creates,
// and define a React variable that will require across Webpack-boundaries or fall
// back to the global, just like it would previously.
function wrapperify(src) {
  return `
;(function(f) {
  // CommonJS
  if (typeof exports === "object" && typeof module !== "undefined") {
    f(require('react'));

  // RequireJS
  } else if (typeof define === "function" && define.amd) {
    require(['react'], f);

  // <script>
  } else {
    var g;
    if (typeof window !== "undefined") {
      g = window;
    } else if (typeof global !== "undefined") {
      g = global;
    } else if (typeof self !== "undefined") {
      g = self;
    } else {
      // works providing we're not in "use strict";
      // needed for Java 8 Nashorn
      // see https://github.com/facebook/react/issues/3037
      g = this;
    }
    f(g.React)
  }
})(function(React) {
  ${src}
});
`;
}

// Our basic config which we'll add to to make our other builds
var basic = {
  entries: [
    './build/node_modules/react/lib/ReactUMDEntry.js',
  ],
  outfile: './build/react.js',
  debug: false,
  standalone: 'React',
  // Apply as global transform so that we also envify fbjs and any other deps
  globalTransforms: [envifyDev],
  plugins: [collapser],
  after: [derequire, simpleBannerify],
};

var min = {
  entries: [
    './build/node_modules/react/lib/ReactUMDEntry.js',
  ],
  outfile: './build/react.min.js',
  debug: false,
  standalone: 'React',
  // Envify twice. The first ensures that when we uglifyify, we have the right
  // conditions to exclude requires. The global transform runs on deps.
  transforms: [envifyProd, uglifyify],
  globalTransforms: [envifyProd],
  plugins: [collapser],
  // No need to derequire because the minifier will mangle
  // the "require" calls.

  after: [minify, bannerify],
};

var addons = {
  entries: [
    './build/node_modules/react/lib/ReactWithAddonsUMDEntry.js',
  ],
  outfile: './build/react-with-addons.js',
  debug: false,
  standalone: 'React',
  packageName: 'React (with addons)',
  transforms: [shimDOMModules],
  globalTransforms: [envifyDev],
  plugins: [collapser],
  after: [derequire, simpleBannerify],
};

var addonsMin = {
  entries: [
    './build/node_modules/react/lib/ReactWithAddonsUMDEntry.js',
  ],
  outfile: './build/react-with-addons.min.js',
  debug: false,
  standalone: 'React',
  packageName: 'React (with addons)',
  transforms: [shimDOMModules, envifyProd, uglifyify],
  globalTransforms: [envifyProd],
  plugins: [collapser],
  // No need to derequire because the minifier will mangle
  // the "require" calls.

  after: [minify, bannerify],
};

// The DOM Builds
var dom = {
  entries: [
    './build/node_modules/react-dom/lib/ReactDOMUMDEntry.js',
  ],
  outfile: './build/react-dom.js',
  debug: false,
  standalone: 'ReactDOM',
  // Apply as global transform so that we also envify fbjs and any other deps
  transforms: [shimSharedModules],
  globalTransforms: [envifyDev],
  plugins: [collapser],
  after: [derequire, wrapperify, simpleBannerify],
};

var domMin = {
  entries: [
    './build/node_modules/react-dom/lib/ReactDOMUMDEntry.js',
  ],
  outfile: './build/react-dom.min.js',
  debug: false,
  standalone: 'ReactDOM',
  // Envify twice. The first ensures that when we uglifyify, we have the right
  // conditions to exclude requires. The global transform runs on deps.
  transforms: [shimSharedModules, envifyProd, uglifyify],
  globalTransforms: [envifyProd],
  plugins: [collapser],
  // No need to derequire because the minifier will mangle
  // the "require" calls.

  after: [wrapperify, minify, bannerify],
};

var domServer = {
  entries: [
    './build/node_modules/react-dom/lib/ReactDOMServerUMDEntry.js',
  ],
  outfile: './build/react-dom-server.js',
  debug: false,
  standalone: 'ReactDOMServer',
  // Apply as global transform so that we also envify fbjs and any other deps
  transforms: [shimSharedModules],
  globalTransforms: [envifyDev],
  plugins: [collapser],
  after: [derequire, wrapperify, simpleBannerify],
};

var domServerMin = {
  entries: [
    './build/node_modules/react-dom/lib/ReactDOMServerUMDEntry.js',
  ],
  outfile: './build/react-dom-server.min.js',
  debug: false,
  standalone: 'ReactDOMServer',
  // Envify twice. The first ensures that when we uglifyify, we have the right
  // conditions to exclude requires. The global transform runs on deps.
  transforms: [shimSharedModules, envifyProd, uglifyify],
  globalTransforms: [envifyProd],
  plugins: [collapser],
  // No need to derequire because the minifier will mangle
  // the "require" calls.

  after: [wrapperify, minify, bannerify],
};

var domFiber = {
  entries: [
    './build/node_modules/react-dom/lib/ReactDOMFiber.js',
  ],
  outfile: './build/react-dom-fiber.js',
  debug: false,
  standalone: 'ReactDOMFiber',
  // Apply as global transform so that we also envify fbjs and any other deps
  transforms: [shimSharedModules],
  globalTransforms: [envifyDev],
  plugins: [collapser],
  after: [derequire, wrapperify, simpleBannerify],
};

var domFiberMin = {
  entries: [
    './build/node_modules/react-dom/lib/ReactDOMFiber.js',
  ],
  outfile: './build/react-dom-fiber.min.js',
  debug: false,
  standalone: 'ReactDOMFiber',
  // Envify twice. The first ensures that when we uglifyify, we have the right
  // conditions to exclude requires. The global transform runs on deps.
  transforms: [shimSharedModules, envifyProd, uglifyify],
  globalTransforms: [envifyProd],
  plugins: [collapser],
  // No need to derequire because the minifier will mangle
  // the "require" calls.

  after: [wrapperify, minify, bannerify],
};

module.exports = {
  basic: basic,
  min: min,
  addons: addons,
  addonsMin: addonsMin,
  dom: dom,
  domMin: domMin,
  domServer: domServer,
  domServerMin: domServerMin,
  domFiber: domFiber,
  domFiberMin: domFiberMin,
};
<|endoftext|>


<commit_before>const u = require('../styles/utils');
const c = require('../styles/config');

const dashboardPadding = 20;

const categoryBorderWidth = 1;
const categoryBorder =
  `${categoryBorderWidth}px solid currentColor`;

module.exports = {
  '.dashboard': {
    'padding': u.inRem(dashboardPadding),
    'width': u.inRem(c.navBarWidth),
    'height': '100%',
    'background': [
      'url(/images/background.jpg)',
      'url(/images/background.prerender.png)',
    ].join(','),
    'background-size': 'cover',
  },

  '.dashboard’s-title': {
    'font-size': u.inRem(40),
    'line-height': u.inRem(40),
  },

  '.dashboard’s-subtitle': {
    'font-size': u.inRem(12),
    'text-transform': 'uppercase',
    'letter-spacing': '0.2em',
  },

  '.dashboard’s-categories': {
    'list-style-type': 'none',
    'padding-top': u.inRem(40),
  },

  '.dashboard’s-category': {
    'position': 'relative',
    'line-height': u.inRem(40),
    'padding': [
      u.inRem(10),
      '0',
    ].join(' '),
  },

  '.dashboard’s-category:not(:first-child)::before': {
    'content': '" "',
    'display': 'block',
    'width': '20%',
    'height': '0',
    'border-bottom': categoryBorder,
    'position': 'absolute',
    'top': '0',
  },
};
<commit_msg>Better category division<commit_after>const u = require('../styles/utils');
const c = require('../styles/config');

const dashboardPadding = 20;

const categoryBorderWidth = 1;
const categoryBorder =
  `${categoryBorderWidth}px solid currentColor`;

module.exports = {
  '.dashboard': {
    'padding': u.inRem(dashboardPadding),
    'width': u.inRem(c.navBarWidth),
    'height': '100%',
    'background': [
      'url(/images/background.jpg)',
      'url(/images/background.prerender.png)',
    ].join(','),
    'background-size': 'cover',
  },

  '.dashboard’s-title': {
    'font-size': u.inRem(40),
    'line-height': u.inRem(40),
  },

  '.dashboard’s-subtitle': {
    'font-size': u.inRem(12),
    'text-transform': 'uppercase',
    'letter-spacing': '0.2em',
  },

  '.dashboard’s-categories': {
    'list-style-type': 'none',
    'padding-top': u.inRem(40),
  },

  '.dashboard’s-category': {
    'position': 'relative',
    'line-height': u.inRem(40),
    'padding': [
      u.inRem(10),
      '0',
    ].join(' '),
  },

  '.dashboard’s-category::before': {
    'content': '" "',
    'display': 'block',
    'position': 'absolute',
    'top': u.inRem(5),
    'bottom': u.inRem(5),
    'left': u.inRem(-dashboardPadding),
    'width': u.inRem(5),
    'background': 'rgba(0, 0, 0, 0.5)',
  },
};
<|endoftext|>


<commit_before>var tilelive = exports;
var _ = require('underscore');
var path = require('path');
var url = require('url');
var Step = require('step');

tilelive.protocols = {
    'mbtiles:': require('mbtiles'),
    'tilejson:': require('tilejson')
};

tilelive.defaults = {
    id: null,
    name: '',
    description: '',
    version: "1.0.0",
    legend: null,
    scheme: "xyz",
    minzoom: -1,
    maxzoom: -1,
    bounds: null,
    center: null
};

// List all tile source URIs from all tile sources.
tilelive.list = function(source, callback) {
    if (typeof callback !== 'function') {
        throw new Error('Callback required as second argument');
    }

    Step(function() {
        var group = this.group();
        for (var name in tilelive.protocols) {
            tilelive.protocols[name].list(source, group());
        }
    }, function(err, uris) {
        if (err) return callback(err);

        // Combine lists from all protocol sources, eliminating duplicate IDs.
        var result = {};
        for (var i = 0; i < uris.length; i++) {
            _.defaults(result, uris[i]);
        }
        callback(null, result);
    });
};

// Obtains a tile source URI from an ID, checking all tile source protocols
// until one is found.
tilelive.findID = function(source, id, callback) {
    if (typeof callback !== 'function') {
        throw new Error('Callback required as third argument');
    }
    var protocols = Object.keys(tilelive.protocols);
    check();
    function check(err, uri) {
        if (!protocols.length) {
            return callback(new Error('Tileset does not exist'));
        }
        tilelive.protocols[protocols.shift()].findID(source, id, function(err, uri) {
            if (err) check();
            else callback(null, uri);
        });
    }
};

tilelive.load = function(uri, callback) {
    if (typeof callback !== 'function') {
        throw new Error('Callback required as second argument');
    }

    uri = url.parse(uri);

    if (!tilelive.protocols[uri.protocol]) {
        return callback(new Error('Invalid tilesource protocol'));
    }

    var handler = new tilelive.protocols[uri.protocol](uri, function(err) {
        if (err) return callback(err);
        callback(null, handler);
    });
};

// Load a tilesource and retrieve metadata
tilelive.info = function(uri, callback) {
    if (typeof callback !== 'function') {
        throw new Error('Callback required as second argument');
    }

    tilelive.load(uri, function(err, handler) {
        if (err) return callback(err);
        handler.getInfo(function(err, data) {
            if (err) return callback(err);
            _.defaults(data);
            err = tilelive.verify(data);
            if (err) return callback(err);
            callback(null, data);
        });
    });
};

// Load metadata for all tile source URIs.
// Ignore errors from loading individual models (e.g.
// don't let one bad apple spoil the collection).
tilelive.all = function(source, callback) {
    if (typeof callback !== 'function') {
        throw new Error('Callback required as second argument');
    }

    tilelive.list(source, function(err, uris) {
        if (err) return callback(err);
        if (!uris) return callback(null, []);

        Step(function() {
            var group = this.group();
            for (var id in uris) {
                tilelive.info(uris[id], group());
            }
        }, function(err, models) {
            // TODO: error reporting for individual failures?
            models = _.compact(models || []);
            models = _.sortBy(models, function(m) { return (m.name || m.id).toLowerCase(); });
            callback(null, models);
        });
    });
};

tilelive.verify = function(ts) {
    _(ts).defaults(tilelive.defaults);

    if (!ts.id) {
        return new Error("Tilesource has invalid ID");
    }

    // validates the retrieved settings.
    // Returns an error object when invalid, otherwise undefined.
    if (!(/^\d+\.\d+\.\d+$/).test(ts.version)) {
        return new Error("Tilesource has invalid version");
    }

    if (ts.attributions) {
        if (!Array.isArray(ts.attributions)) {
            return new Error("Tilesource has invalid attributions");
        }
        for (var i = 0; i < ts.attributions.length; i++) {
            if (!ts.attributions[i].text) {
                return new Error("Tilesource has invalid attribution text");
            }
            // TODO: validate URLs
        }
    }

    if (ts.scheme !== 'xyz' && ts.scheme !== 'tms') {
        return new Error("Tilesource has invalid scheme");
    }

    if (ts.minzoom < 0 || ts.minzoom > 22) {
        return new Error("Tilesource has invalid minzoom");
    }
    if (ts.maxzoom < 0 || ts.maxzoom > 22) {
        return new Error("Tilesource has invalid maxzoom");
    }
    if (ts.minzoom > ts.maxzoom) {
        return new Error("Tilesource's minzoom > maxzoom");
    }

    if (!Array.isArray(ts.bounds) || ts.bounds.length !== 4) {
        return new Error("Tilesource has invalid bounds");
    }
    if (ts.bounds[0] < -180 || ts.bounds[0] > 180) {
        return new Error("Tilesource's left bound is invalid");
    }
    if (ts.bounds[1] < -90 || ts.bounds[1] > 90) {
        return new Error("Tilesource's bottom bound is invalid");
    }
    if (ts.bounds[2] < -180 || ts.bounds[2] > 180) {
        return new Error("Tilesource's right bound is invalid");
    }
    if (ts.bounds[3] < -90 || ts.bounds[3] > 90) {
        return new Error("Tilesource's top bound is invalid");
    }

    if (Array.isArray(ts.center)) {
        if (ts.center.length !== 3) {
            return new Error("Tilesource has invalid center");
        }
        if (ts.center[0] < ts.bounds[0] || ts.center[0] > ts.bounds[2]) {
            return new Error("Tilesource has invalid longitude as center");
        }
        if (ts.center[1] < ts.bounds[1] || ts.center[1] > ts.bounds[3]) {
            return new Error("Tilesource has invalid latitude as center");
        }
        if (ts.center[2] < ts.minzoom || ts.center[2] > ts.maxzoom) {
            return new Error("Tilesource has invalid zoom as center");
        }
    } else if (ts.center !== null) {
        return new Error("Tilesource has invalid center");
    }
};
<commit_msg>allow loading from ID with a uri<commit_after>var tilelive = exports;
var _ = require('underscore');
var path = require('path');
var url = require('url');
var Step = require('step');

tilelive.protocols = {
    'mbtiles:': require('mbtiles'),
    'tilejson:': require('tilejson')
};

tilelive.defaults = {
    id: null,
    name: '',
    description: '',
    version: "1.0.0",
    legend: null,
    scheme: "xyz",
    minzoom: -1,
    maxzoom: -1,
    bounds: null,
    center: null
};

// List all tile source URIs from all tile sources.
tilelive.list = function(source, callback) {
    if (typeof callback !== 'function') {
        throw new Error('Callback required as second argument');
    }

    Step(function() {
        var group = this.group();
        for (var name in tilelive.protocols) {
            tilelive.protocols[name].list(source, group());
        }
    }, function(err, uris) {
        if (err) return callback(err);

        // Combine lists from all protocol sources, eliminating duplicate IDs.
        var result = {};
        for (var i = 0; i < uris.length; i++) {
            _.defaults(result, uris[i]);
        }
        callback(null, result);
    });
};

// Obtains a tile source URI from an ID, checking all tile source protocols
// until one is found.
tilelive.findID = function(source, id, callback) {
    if (typeof callback !== 'function') {
        throw new Error('Callback required as third argument');
    }
    var protocols = Object.keys(tilelive.protocols);
    check();
    function check(err, uri) {
        if (!protocols.length) {
            return callback(new Error('Tileset does not exist'));
        }
        tilelive.protocols[protocols.shift()].findID(source, id, function(err, uri) {
            if (err) check();
            else callback(null, uri);
        });
    }
};

tilelive.load = function(uri, callback) {
    if (typeof callback !== 'function') {
        throw new Error('Callback required as second argument');
    }

    uri = url.parse(uri, true);

    // Handle uris in the format /path/to/dir?id=bar
    if (!uri.protocol && uri.pathname && uri.query.id) {
        tilelive.findID(uri.pathname, uri.query.id, function(err, uri) {
            if (err) callback(err);
            else tilelive.load(uri, callback);
        });
        return;
    }

    if (!tilelive.protocols[uri.protocol]) {
        return callback(new Error('Invalid tilesource protocol'));
    }

    var handler = new tilelive.protocols[uri.protocol](uri, function(err) {
        if (err) return callback(err);
        callback(null, handler);
    });
};

// Load a tilesource and retrieve metadata
tilelive.info = function(uri, callback) {
    if (typeof callback !== 'function') {
        throw new Error('Callback required as second argument');
    }

    tilelive.load(uri, function(err, handler) {
        if (err) return callback(err);
        handler.getInfo(function(err, data) {
            if (err) return callback(err);
            err = tilelive.verify(data);
            if (err) return callback(err);
            callback(null, data, handler);
        });
    });
};

// Load metadata for all tile source URIs.
// Ignore errors from loading individual models (e.g.
// don't let one bad apple spoil the collection).
tilelive.all = function(source, callback) {
    if (typeof callback !== 'function') {
        throw new Error('Callback required as second argument');
    }

    tilelive.list(source, function(err, uris) {
        if (err) return callback(err);
        if (!uris) return callback(null, []);

        Step(function() {
            var group = this.group();
            for (var id in uris) {
                tilelive.info(uris[id], group());
            }
        }, function(err, models) {
            // TODO: error reporting for individual failures?
            models = _.compact(models || []);
            models = _.sortBy(models, function(m) { return (m.name || m.id).toLowerCase(); });
            callback(null, models);
        });
    });
};

tilelive.verify = function(ts) {
    _(ts).defaults(tilelive.defaults);

    if (!ts.id) {
        return new Error("Tilesource has invalid ID");
    }

    // validates the retrieved settings.
    // Returns an error object when invalid, otherwise undefined.
    if (!(/^\d+\.\d+\.\d+$/).test(ts.version)) {
        return new Error("Tilesource has invalid version");
    }

    if (ts.attributions) {
        if (!Array.isArray(ts.attributions)) {
            return new Error("Tilesource has invalid attributions");
        }
        for (var i = 0; i < ts.attributions.length; i++) {
            if (!ts.attributions[i].text) {
                return new Error("Tilesource has invalid attribution text");
            }
            // TODO: validate URLs
        }
    }

    if (ts.scheme !== 'xyz' && ts.scheme !== 'tms') {
        return new Error("Tilesource has invalid scheme");
    }

    if (ts.minzoom < 0 || ts.minzoom > 22) {
        return new Error("Tilesource has invalid minzoom");
    }
    if (ts.maxzoom < 0 || ts.maxzoom > 22) {
        return new Error("Tilesource has invalid maxzoom");
    }
    if (ts.minzoom > ts.maxzoom) {
        return new Error("Tilesource's minzoom > maxzoom");
    }

    if (!Array.isArray(ts.bounds) || ts.bounds.length !== 4) {
        return new Error("Tilesource has invalid bounds");
    }
    if (ts.bounds[0] < -180 || ts.bounds[0] > 180) {
        return new Error("Tilesource's left bound is invalid");
    }
    if (ts.bounds[1] < -90 || ts.bounds[1] > 90) {
        return new Error("Tilesource's bottom bound is invalid");
    }
    if (ts.bounds[2] < -180 || ts.bounds[2] > 180) {
        return new Error("Tilesource's right bound is invalid");
    }
    if (ts.bounds[3] < -90 || ts.bounds[3] > 90) {
        return new Error("Tilesource's top bound is invalid");
    }

    if (Array.isArray(ts.center)) {
        if (ts.center.length !== 3) {
            return new Error("Tilesource has invalid center");
        }
        if (ts.center[0] < ts.bounds[0] || ts.center[0] > ts.bounds[2]) {
            return new Error("Tilesource has invalid longitude as center");
        }
        if (ts.center[1] < ts.bounds[1] || ts.center[1] > ts.bounds[3]) {
            return new Error("Tilesource has invalid latitude as center");
        }
        if (ts.center[2] < ts.minzoom || ts.center[2] > ts.maxzoom) {
            return new Error("Tilesource has invalid zoom as center");
        }
    } else if (ts.center !== null) {
        return new Error("Tilesource has invalid center");
    }
};
<|endoftext|>


<commit_before>/**
 * Appcelerator Common Library for Node.js
 * Copyright (c) 2012 by Appcelerator, Inc. All Rights Reserved.
 * Please see the LICENSE file for information about licensing.
 */

var afs = require('./fs'),
	sdkPaths = require('./environ').os.sdkPaths,
	util = require('./util'),
	version = require('./version'),
	zip = require('./zip'),
	async = require('async'),
	fs = require('fs'),
	path = require('path');

exports.detect = function (projectDir, logger, callback) {
	var searchPaths = (projectDir ? [projectDir] : []).concat(sdkPaths.map(function (p) {
			return afs.resolvePath(p);
		})),
		moduleZipRegExp = /^.+\-.+?\-.+?\.zip$/,
		modules = {};

	function searchModuleDir(moduleName, modulePath, platform) {
		afs.visitDirsSync(modulePath, function (version, versionPath) {
			var manifestFile = path.join(versionPath, 'manifest');
			if (afs.exists(manifestFile)) {
				var module = modules[moduleName] || (modules[moduleName] = {}),
					details = module[version] || (module[version] = {
						modulePath: versionPath,
						manifest: {},
						platforms: {}
					});

				fs.readFileSync(manifestFile).toString().split('\n').forEach(function (line) {
					var p = line.indexOf(':'),
						key,
						value;
					if (line.charAt(0) != '#' && p != -1) {
						key = line.substring(0, p);
						value = details.manifest[key] = line.substring(p + 1).trim();
						if (platform) {
							// old style
							details.platforms[platform] || (details.platforms[platform] = { originalManifest: {} });
							details.platforms[platform].originalManifest[key] = value;
						} else {
							// new style
							key == 'platforms' && value.split(',').forEach(function (p) {
								details.platforms[p.trim()] = {}; // empty for now...
							});
						}
					}
				});

				logger && logger.debug(__('Detected module for %s: %s %s @ %s', Object.keys(details.platforms).join(','), details.manifest.moduleid.cyan, details.manifest.version, details.modulePath));
			}
		});
	}

	async.parallel(searchPaths.map(function (titaniumRoot) {
		return function(cb) {
			afs.exists(titaniumRoot, function (exists) {
				if (!exists) return cb();

				var moduleRoot = path.join(titaniumRoot, 'modules'),
					tasks = [];

				// auto-install zipped modules
				fs.readdirSync(titaniumRoot).forEach(function (file) {
					var moduleZip = path.join(titaniumRoot, file);
					if (fs.statSync(moduleZip).isFile() && moduleZipRegExp.test(file)) {
						tasks.push(function (taskDone) {
							logger && logger.info(__('Installing module: %s', file));
							zip.unzip(moduleZip, titaniumRoot, function () {
								try {
									fs.unlinkSync(moduleZip);
								} catch (e) {}
								taskDone();
							});
						});
					}
				});

				async.parallel(tasks, function () {
					// search module directories
					logger && logger.debug(__('Detecting modules in %s', moduleRoot.cyan));
					afs.visitDirs(moduleRoot, function (platform, modulesPath) {
						if (/^osx|win32|linux$/.test(platform)) {
							// skip old Titanium Desktop directories
						} else if (/^android|iphone|mobileweb|commonjs$/.test(platform)) {
							// old style module directory
							afs.visitDirsSync(modulesPath, function (moduleName, modulePath) {
								searchModuleDir(moduleName, modulePath, platform);
							});
						} else {
							// possibly a new style module directory
							searchModuleDir(platform, modulesPath);
						}
					}, cb);
				});
			});
		};
	}), function () {
		callback(modules);
	});
};

exports.find = function (modules, platforms, deployType, projectDir, logger, callback) {
	var result = {
			found: [],
			missing: [],
			incompatible: []
		};

	// if there are modules to find, then just exit now
	if (!modules || !modules.length) return callback(result);

	Array.isArray(platforms) || (platforms = [platforms]);
	platforms.push('commonjs');

	exports.detect(projectDir, logger, function (installed) {
		modules.forEach(function (module) {
			if (module.platform && platforms.indexOf(module.platform) == -1) return;
			if (module.deployType && module.deployType != deployType) return;

			logger && logger.debug(__('Looking for Titanium Module id: %s version: %s platform: %s', module.id.cyan, (module.version || 'latest').cyan, module.platform.cyan));

			if (module.id && installed[module.id]) {
				var mp = module.platform ? [module.platform] : platforms, // if the module doesn't have any platforms, then it's any platform
					i = 0,
					info,
					platform,
					found;

				while (!found && i < mp.length) {
					platform = mp[i++];
					if (platforms.indexOf(platform) != -1) {
						if (module.version) {
							info = installed[module.id][module.version];
							if (info && info.platforms[platform]) {
								util.mix(module, info);
								if (info.minsdk && version.gt(info.minsdk, ti.manifest.version)) {
									incompatible.push(module);
								} else {
									result.found.push(module);
								}
								found = true;
							}
						} else {
							// no version, scan newest versions for the first one to match this platform
							var versions = Object.keys(installed[module.id]).sort().reverse(),
								j = 0;
							while (j < versions.length) {
								info = installed[module.id][versions[j++]];
								if (info.platforms[platform]) {
									util.mix(module, info);
									if (info.minsdk && version.gt(info.minsdk, ti.manifest.version)) {
										incompatible.push(module);
									} else {
										result.found.push(module);
									}
									found = true;
									break;
								}
							}
						}
					}
				}

				found || result.missing.push(module);
			} else {
				logger && logger.warn(__('Could not find Titanium Module id: %s version: %s platform: %s', module.id.cyan, (module.version || 'latest').cyan, module.platform.cyan));
				result.missing.push(module);
			}
		});

		callback(result);
	});
};<commit_msg>[TIMOB-10767] Implemented support for working with modules<commit_after>/**
 * Appcelerator Common Library for Node.js
 * Copyright (c) 2012 by Appcelerator, Inc. All Rights Reserved.
 * Please see the LICENSE file for information about licensing.
 */

var path = require('path'),
	fs = require('fs'),

	async = require('async'),

	afs = require('./fs'),
	mix = require('./util').mix,
	sdkPaths = require('./environ').os.sdkPaths,

	globalModulesManifestPath = afs.resolvePath(path.join('~', '.titanium', 'modules.json')),
	globalModules,
	moduleZipRegExp = /^.+\-.+?\-.+?\.zip$/;

exports.getGlobalModules = function(callback) {
	if (!globalModules) {
		globalModules = {};
		async.parallel(sdkPaths.map(function (p) {
			return function(cb) {
				detectModules(p, globalModules, cb);
			};
		}), function() {
			var globalModulesManifest = loadModulesManifest(globalModulesManifestPath);
			for(var mod in globalModules) {
				for(var version in globalModules[mod]) {
					globalModules[mod][version].activated = !!globalModulesManifest[mod] &&
						globalModulesManifest[mod]['activated-version'] === version;
				}
			}
			callback && callback(globalModules);
		});
	} else {
		callback && callback(globalModules);
	}
};

exports.getProjectModules = function(projectDir, callback) {
	var modules = {};
	if (afs.exists(afs.resolvePath(projectDir))) {
		detectModules(projectDir, modules, function() {
			var projectModulesManifest = loadModulesManifest(path.join(projectDir, '.modules.json'));
			for(var mod in modules) {
				for(var version in modules[mod]) {
					modules[mod][version].activated = !!projectModulesManifest[mod] &&
						projectModulesManifest[mod]['activated-version'] === version;

				}
			}
			callback && callback(modules);
		});
	} else {
		callback && callback(modules);
	}
};

function detectModules(searchPath, modules, callback) {

	function searchModuleDir(moduleName, modulePath, platform, isGlobal) {
		afs.visitDirsSync(modulePath, function (version, versionPath) {
			var manifestFile = path.join(versionPath, 'manifest');
			if (afs.exists(manifestFile)) {
				var module = modules[moduleName] || (modules[moduleName] = {}),
					details = module[version] || (module[version] = {
						modulePath: versionPath,
						manifest: {},
						platforms: {},
						global: isGlobal
					});

				fs.readFileSync(manifestFile).toString().split('\n').forEach(function (line) {
					var p = line.indexOf(':'),
						key,
						value;
					if (line.charAt(0) != '#' && p != -1) {
						key = line.substring(0, p);
						value = details.manifest[key] = line.substring(p + 1).trim();
						if (platform) {
							// old style
							details.platforms[platform] || (details.platforms[platform] = { originalManifest: {} });
							details.platforms[platform].originalManifest[key] = value;
						} else {
							// new style
							key == 'platforms' && value.split(',').forEach(function (p) {
								details.platforms[p.trim()] = {}; // empty for now...
							});
						}
					}
				});
			}
		});
	}

	searchPath = afs.resolvePath(searchPath);
	afs.exists(searchPath, function (exists) {
		if (!exists) return callback();

		var moduleRoot = path.join(searchPath, 'modules'),
			tasks = [];

		// auto-install zipped modules
		fs.readdirSync(searchPath).forEach(function (file) {
			var moduleZip = path.join(searchPath, file);
			if (fs.lstatSync(moduleZip).isFile() && moduleZipRegExp.test(file)) {
				tasks.push(function (taskDone) {
					zip.unzip(moduleZip, searchPath, function () {
						try {
							fs.unlinkSync(moduleZip);
						} catch (e) {}
						taskDone();
					});
				});
			}
		});

		async.parallel(tasks, function () {
			// search module directories
			afs.visitDirs(moduleRoot, function (platform, modulesPath) {
				if (/^osx|win32|linux$/.test(platform)) {
					// skip old Titanium Desktop directories
				} else if (/^android|iphone|mobileweb|commonjs$/.test(platform)) {
					// old style module directory
					afs.visitDirsSync(modulesPath, function (moduleName, modulePath) {
						searchModuleDir(moduleName, modulePath, platform);
					});
				} else {
					// possibly a new style module directory
					searchModuleDir(platform, modulesPath);
				}
			}, callback);
		});
	});
}

exports.getModules = function(projectDir, callback) {
	async.parallel([
		function(next) {
			exports.getGlobalModules(function(modules) {
				next(null, modules);
			});
		},
		function(next) {
			exports.getProjectModules(projectDir, function(modules) {
				next(null, modules);
			});
		}
	], function(err, results) {
		callback && callback(mix(results[0], results[1]));
	});
};

exports.getActivatedModules = function(projectDir, callback) {
	exports.getModules(projectDir, function(modules) {
		for(var mod in modules) {
			for(var version in modules[mod]) {
				if (!modules[mod][version].activated) {
					delete modules[mod][version];
				}
			}
			if (!Object.keys(modules[mod]).length) {
				delete modules[mod];
			}
		}
		callback && callback(modules);
	});
};

exports.activateGlobalModule = function(name, version) {
	var globalModulesManifest = loadModulesManifest(globalModulesManifestPath);
	globalModulesManifest[name] || (globalModulesManifest[name] = {});
	globalModulesManifest[name] = {
		'activated-version': version
	};
	saveModulesManifest(globalModulesManifestPath, globalModulesManifest);
};

exports.deactivateGlobalModule = function(name) {
	var globalModulesManifest = loadModulesManifest(globalModulesManifestPath);
	delete globalModulesManifest[name];
	saveModulesManifest(globalModulesManifestPath, globalModulesManifest);
};

exports.activateProjectModule = function(projectDir, name, version) {
	var projectModulesManifestPath = path.join(projectDir, '.modules.json'),
		projectModulesManifest = loadModulesManifest(projectModulesManifestPath);
	projectModulesManifest[name] || (projectModulesManifest[name] = {});
	projectModulesManifest[name] = {
		'activated-version': version
	};
	saveModulesManifest(projectModulesManifestPath, projectModulesManifest);
};

exports.deactivateProjectModule = function(projectDir, name) {
	var projectModulesManifestPath = path.join(projectDir, '.modules.json'),
		projectModulesManifest = loadModulesManifest(projectModulesManifestPath);
	delete projectModulesManifest[name];
	saveModulesManifest(projectModulesManifestPath, projectModulesManifest);
};

function loadModulesManifest(path) {
	path = afs.resolvePath(path);
	if (afs.exists(path)) {
		try {
			return JSON.parse(fs.readFileSync(path));
		} catch (e) {
			return {};
		}
	} else {
		return {};
	}
}

function saveModulesManifest(path, content) {
	fs.writeFileSync(afs.resolvePath(path), JSON.stringify(content, null, '\t'));
}<|endoftext|>


<commit_before>var request = require('request');
var crypto = require('crypto');
var async = require('async');
var FilesystemAdapter = require('parse-server-fs-adapter');
var S3Adapter = require('parse-server-s3-adapter');
var GCSAdapter = require('parse-server-gcs-adapter');
var AzureStorageAdapter = require('parse-server-azure-storage').AzureStorageAdapter;
var MongoClient = require('mongodb').MongoClient;

// regex that matches old legacy Parse hosted files
var legacyFilesPrefixRegex = new RegExp("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}-");
var migratedFilePrefix = 'mfp_';

var db, config;

module.exports.init = init;
module.exports.run = run;

function init(options) {
  console.log('Initializing transfer configuration...');
  config = options;
  return new Promise(function(resolve, reject) {
    if (config.renameInDatabase) {
      console.log('Connecting to MongoDB');
      MongoClient.connect(config.mongoURL, function(error, database) {
        if (error) {
          return reject(error);
        }
        console.log('Successfully connected to MongoDB');
        db = database;
        _setup().then(resolve, reject);
      });
    } else {
      _setup().then(resolve, reject);
    }
  });
}

function _setup() {
  config.adapterName = config.transferTo || config.filesAdapter.constructor.name;
  console.log('Initializing '+config.adapterName+' adapter');
  if (config.filesAdapter && config.filesAdapter.createFile) {
    return Promise.resolve();
  } else if (config.transferTo == 'print') {
    return Promise.resolve();
  } else if (config.transferTo == 'filesystem') {
    config.filesAdapter = new FilesystemAdapter({
      filesSubDirectory: config.filesystemPath
    });
  } else if (config.transferTo == 's3') {
    config.filesAdapter = new S3Adapter({
      accessKey: config.aws_accessKeyId,
      secretKey: config.aws_secretAccessKey,
      bucket: config.aws_bucket,
      bucketPrefix: config.aws_bucketPrefix,
      directAccess: true
    });
  } else if (config.transferTo == 'gcs') {
    config.filesAdapter = new GCSAdapter({
      projectId: config.gcs_projectId,
      keyFilename: config.gcs_keyFilename,
      bucket: config.gcs_bucket,
      directAccess: true
    });
  } else if (config.transferTo == 'azure') {

    var account = config.azure_account;
    var container = config.azure_container;
    var options = {
        accessKey: config.azure_accessKey,
        directAccess: false // If set to true, files will be served by Azure Blob Storage directly
    }
    config.filesAdapter = new AzureStorageAdapter(account, container, options);

  } else {
    return Promise.reject('Invalid files adapter');
  }
  return Promise.resolve();
}

function run(files) {
  console.log('Processing '+files.length+' files');
  console.log('Saving files with '+config.adapterName);
  return _processFiles(files);
}

/**
 * Handle error from requests
 */
function _requestErrorHandler(error, response) {
  if (error) {
    return error;
  } else if (response.statusCode >= 300) {
    console.log('Failed request ('+response.statusCode+') skipping: '+response.request.href);
    return true;
  }
  return false;
}

/**
 * Converts a file into a non Parse file name
 * @param  {String} fileName
 * @return {String}
 */
function _createNewFileName(fileName) {
  if (!config.renameFiles) {
    return fileName;
  }
  if (_isParseHostedFile(fileName)) {
    fileName = fileName.replace('tfss-', '');
    var newPrefix = crypto.randomBytes(32/2).toString('hex');
    fileName = newPrefix + fileName.replace(legacyFilesPrefixRegex, '');
  }
  return migratedFilePrefix + fileName;
}

function _isParseHostedFile(fileName) {
  if (fileName.indexOf('tfss-') === 0 || legacyFilesPrefixRegex.test(fileName)) {
    return true;
  }
  return false;
}

/**
 * Loops through n files at a time and calls handler
 * @param  {Array}    files     Array of files
 * @param  {Function} handler   handler function for file
 * @return {Promise}
 */
function _processFiles(files, handler) {
  var asyncLimit = config.asyncLimit || 5;
  return new Promise(function(resolve, reject) {
    async.eachOfLimit(files, asyncLimit, function(file, index, callback) {
      process.stdout.write('Processing '+(index+1)+'/'+files.length+'\r');
      file.newFileName = _createNewFileName(file.fileName);
      if (_shouldTransferFile(file)) {
        _transferFile(file).then(callback, callback);
      } else {
        process.nextTick(callback);
      }
    }, function(error) {
      if (error) {
        return reject(error);
      }
      resolve('\nComplete!');
    });
  })
}

/**
 * Changes the file name that is saved in MongoDB
 * @param  {Object}   file     the file info
 */
function _changeDBFileField(file) {
  return new Promise(function(resolve, reject) {
    if (file.fileName == file.newFileName || !config.renameInDatabase) {
      return resolve();
    }
    var update = {$set:{}};
    update.$set[file.fieldName] = file.newFileName;
    db.collection(file.className).update(
      { _id : file.objectId },
      update,
      function(error, result ) {
        if (error) {
          return reject(error);
        }
        resolve();
      }
    );
  });
}

/**
 * Determines if a file should be transferred based on configuration
 * @param  {Object} file the file info
 */
function _shouldTransferFile(file) {
  if (config.filesToTransfer == 'all') {
    return true;
  } else if (
    config.filesToTransfer == 'parseOnly' &&
    _isParseHostedFile(file.fileName)
  ) {
    return true;
  } else if (
    config.filesToTransfer == 'parseServerOnly' &&
    !_isParseHostedFile(file.fileName)
  ) {
    return true;
  }
  return false;
}

/**
 * Request file from URL and upload with filesAdapter
 * @param  {Object}   file     the file info object
 */
function _transferFile(file) {
  return new Promise(function(resolve, reject) {
    if (config.transferTo == 'print') {
      console.log(file.url);
      // Use process.nextTick to avoid max call stack error
      return process.nextTick(resolve);
    }
    request({ url: file.url, encoding: null }, function(error, response, body) {
      if (_requestErrorHandler(error, response)) {
        return reject(error);
      }
      config.filesAdapter.createFile(
        file.newFileName, body, response.headers['content-type']
      ).then(function() {
        return _changeDBFileField(file);
      }).then(resolve, reject);
    });
  });
}
<commit_msg>replace domain name with direct S3 access<commit_after>var request = require('request');
var crypto = require('crypto');
var async = require('async');
var FilesystemAdapter = require('parse-server-fs-adapter');
var S3Adapter = require('parse-server-s3-adapter');
var GCSAdapter = require('parse-server-gcs-adapter');
var AzureStorageAdapter = require('parse-server-azure-storage').AzureStorageAdapter;
var MongoClient = require('mongodb').MongoClient;

// regex that matches old legacy Parse hosted files
var legacyFilesPrefixRegex = new RegExp("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}-");
var migratedFilePrefix = 'mfp_';

var db, config;

module.exports.init = init;
module.exports.run = run;

function init(options) {
  console.log('Initializing transfer configuration...');
  config = options;
  return new Promise(function(resolve, reject) {
    if (config.renameInDatabase) {
      console.log('Connecting to MongoDB');
      MongoClient.connect(config.mongoURL, function(error, database) {
        if (error) {
          return reject(error);
        }
        console.log('Successfully connected to MongoDB');
        db = database;
        _setup().then(resolve, reject);
      });
    } else {
      _setup().then(resolve, reject);
    }
  });
}

function _setup() {
  config.adapterName = config.transferTo || config.filesAdapter.constructor.name;
  console.log('Initializing '+config.adapterName+' adapter');
  if (config.filesAdapter && config.filesAdapter.createFile) {
    return Promise.resolve();
  } else if (config.transferTo == 'print') {
    return Promise.resolve();
  } else if (config.transferTo == 'filesystem') {
    config.filesAdapter = new FilesystemAdapter({
      filesSubDirectory: config.filesystemPath
    });
  } else if (config.transferTo == 's3') {
    config.filesAdapter = new S3Adapter({
      accessKey: config.aws_accessKeyId,
      secretKey: config.aws_secretAccessKey,
      bucket: config.aws_bucket,
      bucketPrefix: config.aws_bucketPrefix,
      directAccess: true
    });
  } else if (config.transferTo == 'gcs') {
    config.filesAdapter = new GCSAdapter({
      projectId: config.gcs_projectId,
      keyFilename: config.gcs_keyFilename,
      bucket: config.gcs_bucket,
      directAccess: true
    });
  } else if (config.transferTo == 'azure') {

    var account = config.azure_account;
    var container = config.azure_container;
    var options = {
        accessKey: config.azure_accessKey,
        directAccess: false // If set to true, files will be served by Azure Blob Storage directly
    }
    config.filesAdapter = new AzureStorageAdapter(account, container, options);

  } else {
    return Promise.reject('Invalid files adapter');
  }
  return Promise.resolve();
}

function run(files) {
  console.log('Processing '+files.length+' files');
  console.log('Saving files with '+config.adapterName);
  return _processFiles(files);
}

/**
 * Handle error from requests
 */
function _requestErrorHandler(error, response) {
  if (error) {
    return error;
  } else if (response.statusCode >= 300) {
    console.log('Failed request ('+response.statusCode+') skipping: '+response.request.href);
    return true;
  }
  return false;
}

/**
 * Converts a file into a non Parse file name
 * @param  {String} fileName
 * @return {String}
 */
function _createNewFileName(fileName) {
  if (!config.renameFiles) {
    return fileName;
  }
  if (_isParseHostedFile(fileName)) {
    fileName = fileName.replace('tfss-', '');
    var newPrefix = crypto.randomBytes(32/2).toString('hex');
    fileName = newPrefix + fileName.replace(legacyFilesPrefixRegex, '');
  }
  return migratedFilePrefix + fileName;
}

function _isParseHostedFile(fileName) {
  if (fileName.indexOf('tfss-') === 0 || legacyFilesPrefixRegex.test(fileName)) {
    return true;
  }
  return false;
}

/**
 * Loops through n files at a time and calls handler
 * @param  {Array}    files     Array of files
 * @param  {Function} handler   handler function for file
 * @return {Promise}
 */
function _processFiles(files, handler) {
  var asyncLimit = config.asyncLimit || 5;
  return new Promise(function(resolve, reject) {
    async.eachOfLimit(files, asyncLimit, function(file, index, callback) {
      process.stdout.write('Processing '+(index+1)+'/'+files.length+'\r');
      file.newFileName = _createNewFileName(file.fileName);
      if (_shouldTransferFile(file)) {
        _transferFile(file).then(callback, callback);
      } else {
        process.nextTick(callback);
      }
    }, function(error) {
      if (error) {
        return reject(error);
      }
      resolve('\nComplete!');
    });
  })
}

/**
 * Changes the file name that is saved in MongoDB
 * @param  {Object}   file     the file info
 */
function _changeDBFileField(file) {
  return new Promise(function(resolve, reject) {
    if (file.fileName == file.newFileName || !config.renameInDatabase) {
      return resolve();
    }
    var update = {$set:{}};
    update.$set[file.fieldName] = file.newFileName;
    db.collection(file.className).update(
      { _id : file.objectId },
      update,
      function(error, result ) {
        if (error) {
          return reject(error);
        }
        resolve();
      }
    );
  });
}

/**
 * Determines if a file should be transferred based on configuration
 * @param  {Object} file the file info
 */
function _shouldTransferFile(file) {
  if (config.filesToTransfer == 'all') {
    return true;
  } else if (
    config.filesToTransfer == 'parseOnly' &&
    _isParseHostedFile(file.fileName)
  ) {
    return true;
  } else if (
    config.filesToTransfer == 'parseServerOnly' &&
    !_isParseHostedFile(file.fileName)
  ) {
    return true;
  }
  return false;
}

function _replaceDomainName(url) {
  url = url.replace(/http(s)?:\/\/filescdn\.parsetfss\.com\//, 'https://s3.amazonaws.com/files.parsetfss.com/');
  url = url.replace(/http(s)?:\/\/files\.parsetfss\.com\//, 'https://s3.amazonaws.com/files.parsetfss.com/');
  url = url.replace(/http(s)?:\/\/files\.parse\.com\//, 'https://s3.amazonaws.com/files.parse.com/');
  return url;
}

/**
 * Request file from URL and upload with filesAdapter
 * @param  {Object}   file     the file info object
 */
function _transferFile(file) {
  return new Promise(function(resolve, reject) {
    if (config.transferTo == 'print') {
      console.log(file.url);
      // Use process.nextTick to avoid max call stack error
      return process.nextTick(resolve);
    }
    request({ url: _replaceDomainName(file.url), encoding: null }, function(error, response, body) {
      if (_requestErrorHandler(error, response)) {
        return reject(error);
      }
      config.filesAdapter.createFile(
        file.newFileName, body, response.headers['content-type']
      ).then(function() {
        return _changeDBFileField(file);
      }).then(resolve, reject);
    });
  });
}
<|endoftext|>


<commit_before>var http = require('http'),
    OAuth = require('oauth').OAuth,
    url = require('url');

/* From /node/lib/utils.js */
function timestamp() {
  function pad(n) {
    return n < 10 ? '0' + n.toString(10) : n.toString(10);
  }
  var d = new Date(),
    months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    time = [pad(d.getHours()), pad(d.getMinutes()), pad(d.getSeconds())].join(':');
  return [d.getDate(), months[d.getMonth()], time].join(' ');
}

function debug(self, str) {
  if (self && str) {
    str = [timestamp(), str.toString()].join(' - ');
    self.log.push(str);
    if (self.debug) console.log(str);
  }
}

// first argument can be string or object
function tumblr(hostname, key, secret, access_key, access_secret) {
  if (!(this instanceof tumblr)) return new tumblr(hostname, key, secret, access_key, access_secret);

  var obj = (typeof hostname !== 'object') ? {
    hostname : hostname,
    secret : secret,
    key : key,
    access_secret : access_secret,
    access_key : access_key
  } : hostname;

  // reference
  if (obj && obj.hostname) this.hostname = !/\./.test(obj.hostname) ? obj.hostname + '.tumblr.com' : obj.hostname;
  this.key = obj.key;
  this.secret = obj.secret;
  this.access_key = obj.access_key;
  this.access_secret = obj.access_secret;

  // output the log to console
  this.debug = false;

  // keeping the logs even if debug is false
  this.log = [];

  // caching requests
  this.caches = false;
  this._cache = {};

  // oauth access
  this._oauth_access = this.access_key && this.access_secret;

  if (this.key && this.secret && this._oauth_access) {
    this._oa = oa = new OAuth('http://www.tumblr.com/oauth/request_token',
                              'http://www.tumblr.com/oauth/access_token',
                              this.key, this.secret, '1.0A',
                              null, 'HMAC-SHA1');

    var self = this;
    oa.getOAuthRequestToken(function(err, req_key, req_secret, res) {
      if (!err) {
        var url = ['http://www.tumblr.com/oauth/authorize/?oauth_token=', req_key].join('');
        self.OAuthAuthorizationURL = url;
        self._oa.session = self._oa.session || {};
        self._oa.session.key = req_key;
        self._oa.session.secret = req_secret;
      }
      else {
        debug(err);
      }
    });
  }
  return this;
}

tumblr.fn = tumblr.prototype;

tumblr.fn.setOAuthVerifier = function(verifier, fn) {
  var self = this;
  this._oa.getOAuthAccessToken(this._oa.session.key, this._oa.session.secret, verifier, function(er, key, secret, res) {
    if (!er) {
      self.access_key = key;
      self.access_secret = secret;
    }
    else {
      debug(er);
    }
  });
  return this;
};

tumblr.fn._request = function(obj, fn) {
  var cache = this._cache, log_key = obj.type + '/' + obj.path;
  if (!cache[log_key]) cache[log_key] = {};
  var cached = cache[log_key];

  if (this.caches && cached._data) {
    if (fn) fn(cached._data);
    return this;
  }

  // consumer key is required
  if (obj.method == 'GET' && /^\/(posts|info)/.test(obj.path)) {
    obj.query = obj.query || {};
    obj.query.api_key = this.key;
  }

  // cleaning the query
  for (var i in obj.query) {
    if (typeof obj.query[i] !== 'number' && !obj.query[i]) delete obj.query[i];
  }

  var settings = {
    host : "api.tumblr.com",
    method : obj.method,
    path : "/v2/",
    port : 80
  },
  query = url.format({ query : obj.query });

  settings.path += (obj.type === "blog") ? "blog/" + this.hostname : "user";
  settings.path += obj.path;
  settings.path += (obj.method === 'GET') ? query : '';

  var self = this,
      body = "";

  if (!/^\/(user\/posts|blog\/(info|avatar))/.test(settings.path)) {
    if (this.key && this.secret && this._oauth_access) {
      settings.pathname = settings.path;
      settings.protocol = 'http';
      var method = obj.method.toLowerCase();
      cached._req = req = this._oa[method](url.format(settings), this.access_key, this.access_secret, method === 'post' ? obj.query : null).on('response', onRes);
    }
    else {
      debug('Missing or invalid information')
    }
  }
  else {
    cached._req = req = http.request(settings, onRes);
  }

  function onRes(res) {
    cached._res = res;
    res.on('data', function (data) {
      body += data;
    }).on('end', function() {
      debug(self, ['HTTP', res.httpVersion, settings.method, res.statusCode, settings.path].join(' '));
      cached._data = JSON.parse(body);
      if (fn) fn(cached._data);
    });
  }

  req.on('error', debug).end();

  return this;
};

tumblr.fn._get = function(obj, fn) {
  obj.method = 'GET';
  return this._request(obj, fn);
};

tumblr.fn._post = function(obj, fn) {
  obj.method = 'POST';
  return this._request(obj, fn);
};

// api
tumblr.fn.avatar = function(size, fn) {
  size = size || 64;
  if (typeof size === 'function') {
    fn = size;
    size = 64;
  }

  return this._get({
    type : 'blog',
    path : '/avatar',
    query : {
      size : size
    }
  }, fn);
};

tumblr.fn.info = function(fn) {
  return this._get({
    type : 'blog',
    path : '/info'
  }, fn);
};

tumblr.fn.user_info = function(fn) {
  return this._post({
    type : 'user',
    path : '/info'
  }, fn);
};

tumblr.fn.posts = function(obj, fn) {
  fn = typeof obj === 'function' ? obj : fn;
  obj = obj || {};

  return this._get({
    type : 'blog',
    path : '/posts',
    query : {
      type : obj.type || null,
      id : obj.id || null,
      tag : obj.tag || null,
      limit : obj.limit || 20,
      offset : obj.offset || 0,
      reblog_info : obj.reblog_info || null,
      notes_info : obj.notes_info || null,
      format : obj.format || null
    }
  }, fn);
};

tumblr.fn.followers = function(obj, fn) {
  fn = typeof obj === 'function' ? obj : fn;
  obj = obj || {};

  return this._get({
    type : 'blog',
    path : '/followers',
    query : {
      limit : obj.limit || 20,
      offset : obj.offset || 0
    }
  }, fn);
};

tumblr.fn.following = function(obj, fn) {
  fn = typeof obj === 'function' ? obj : fn;
  obj = obj || {};

  return this._get({
    type : 'blog',
    path : '/following',
    query : {
      limit : obj.limit || 20,
      offset : obj.offset || 0
    }
  }, fn);
};

tumblr.fn.follow = function(url, fn) {
  if (typeof url !== 'object') url = {url:url};

  return this._post({
    type : 'blog',
    path : '/follow',
    query : url
  }, fn);
};

tumblr.fn.reblog = function(obj, fn) {
  fn = typeof obj === 'function' ? obj : fn;
  obj = obj || {};

  return this._post({
    type : 'blog',
    path : '/post/reblog',
    query : obj
  }, fn);
};

tumblr.fn.like = function(obj, fn) {
  fn = typeof obj === 'function' ? obj : fn;
  obj = obj || {};

  return this._post({
    type : 'user',
    path : '/like',
    query : obj
  }, fn);
};

tumblr.fn.likes = function(obj, fn) {
  fn = typeof obj === 'function' ? obj : fn;
  obj = obj || {};

  return this._get({
    type : 'user',
    path : '/likes',
    query : obj
  }, fn);
};

tumblr.fn['delete'] = function(id, fn) {
  if (typeof id !== 'object') id = {id:id};

  return this._post({
    type : 'blog',
    path : '/post/delete',
    query : id
  }, fn);
};

module.exports = tumblr;
<commit_msg>methods `unlike` and `unfollow`<commit_after>var http = require('http'),
    OAuth = require('oauth').OAuth,
    url = require('url');

/* From /node/lib/utils.js */
function timestamp() {
  function pad(n) {
    return n < 10 ? '0' + n.toString(10) : n.toString(10);
  }
  var d = new Date(),
    months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    time = [pad(d.getHours()), pad(d.getMinutes()), pad(d.getSeconds())].join(':');
  return [d.getDate(), months[d.getMonth()], time].join(' ');
}

function debug(self, str) {
  if (self && str) {
    str = [timestamp(), str.toString()].join(' - ');
    self.log.push(str);
    if (self.debug) console.log(str);
  }
}

// first argument can be string or object
function tumblr(hostname, key, secret, access_key, access_secret) {
  if (!(this instanceof tumblr)) return new tumblr(hostname, key, secret, access_key, access_secret);

  var obj = (typeof hostname !== 'object') ? {
    hostname : hostname,
    secret : secret,
    key : key,
    access_secret : access_secret,
    access_key : access_key
  } : hostname;

  // reference
  if (obj && obj.hostname) this.hostname = !/\./.test(obj.hostname) ? obj.hostname + '.tumblr.com' : obj.hostname;
  this.key = obj.key;
  this.secret = obj.secret;
  this.access_key = obj.access_key;
  this.access_secret = obj.access_secret;

  // output the log to console
  this.debug = false;

  // keeping the logs even if debug is false
  this.log = [];

  // caching requests
  this.caches = false;
  this._cache = {};

  // oauth access
  this._oauth_access = this.access_key && this.access_secret;

  if (this.key && this.secret && this._oauth_access) {
    this._oa = oa = new OAuth('http://www.tumblr.com/oauth/request_token',
                              'http://www.tumblr.com/oauth/access_token',
                              this.key, this.secret, '1.0A',
                              null, 'HMAC-SHA1');

    var self = this;
    oa.getOAuthRequestToken(function(err, req_key, req_secret, res) {
      if (!err) {
        var url = ['http://www.tumblr.com/oauth/authorize/?oauth_token=', req_key].join('');
        self.OAuthAuthorizationURL = url;
        self._oa.session = self._oa.session || {};
        self._oa.session.key = req_key;
        self._oa.session.secret = req_secret;
      }
      else {
        debug(err);
      }
    });
  }
  return this;
}

tumblr.fn = tumblr.prototype;

tumblr.fn.setOAuthVerifier = function(verifier, fn) {
  var self = this;
  this._oa.getOAuthAccessToken(this._oa.session.key, this._oa.session.secret, verifier, function(er, key, secret, res) {
    if (!er) {
      self.access_key = key;
      self.access_secret = secret;
    }
    else {
      debug(er);
    }
  });
  return this;
};

tumblr.fn._request = function(obj, fn) {
  var cache = this._cache, log_key = obj.type + '/' + obj.path;
  if (!cache[log_key]) cache[log_key] = {};
  var cached = cache[log_key];

  if (this.caches && cached._data) {
    if (fn) fn(cached._data);
    return this;
  }

  // consumer key is required
  if (obj.method == 'GET' && /^\/(posts|info)/.test(obj.path)) {
    obj.query = obj.query || {};
    obj.query.api_key = this.key;
  }

  // cleaning the query
  for (var i in obj.query) {
    if (typeof obj.query[i] !== 'number' && !obj.query[i]) delete obj.query[i];
  }

  var settings = {
    host : "api.tumblr.com",
    method : obj.method,
    path : "/v2/",
    port : 80
  },
  query = url.format({ query : obj.query });

  settings.path += (obj.type === "blog") ? "blog/" + this.hostname : "user";
  settings.path += obj.path;
  settings.path += (obj.method === 'GET') ? query : '';

  var self = this,
      body = "";

  if (!/^\/(user\/posts|blog\/(info|avatar))/.test(settings.path)) {
    if (this.key && this.secret && this._oauth_access) {
      settings.pathname = settings.path;
      settings.protocol = 'http';
      var method = obj.method.toLowerCase();
      cached._req = req = this._oa[method](url.format(settings), this.access_key, this.access_secret, method === 'post' ? obj.query : null).on('response', onRes);
    }
    else {
      debug('Missing or invalid information')
    }
  }
  else {
    cached._req = req = http.request(settings, onRes);
  }

  function onRes(res) {
    cached._res = res;
    res.on('data', function (data) {
      body += data;
    }).on('end', function() {
      debug(self, ['HTTP', res.httpVersion, settings.method, res.statusCode, settings.path].join(' '));
      cached._data = JSON.parse(body);
      if (fn) fn(cached._data);
    });
  }

  req.on('error', debug).end();

  return this;
};

tumblr.fn._get = function(obj, fn) {
  obj.method = 'GET';
  return this._request(obj, fn);
};

tumblr.fn._post = function(obj, fn) {
  obj.method = 'POST';
  return this._request(obj, fn);
};

// api
tumblr.fn.avatar = function(size, fn) {
  size = size || 64;
  if (typeof size === 'function') {
    fn = size;
    size = 64;
  }

  return this._get({
    type : 'blog',
    path : '/avatar',
    query : {
      size : size
    }
  }, fn);
};

tumblr.fn.info = function(fn) {
  return this._get({
    type : 'blog',
    path : '/info'
  }, fn);
};

tumblr.fn.user_info = function(fn) {
  return this._post({
    type : 'user',
    path : '/info'
  }, fn);
};

tumblr.fn.posts = function(obj, fn) {
  fn = typeof obj === 'function' ? obj : fn;
  obj = obj || {};

  return this._get({
    type : 'blog',
    path : '/posts',
    query : {
      type : obj.type || null,
      id : obj.id || null,
      tag : obj.tag || null,
      limit : obj.limit || 20,
      offset : obj.offset || 0,
      reblog_info : obj.reblog_info || null,
      notes_info : obj.notes_info || null,
      format : obj.format || null
    }
  }, fn);
};

tumblr.fn.followers = function(obj, fn) {
  fn = typeof obj === 'function' ? obj : fn;
  obj = obj || {};

  return this._get({
    type : 'blog',
    path : '/followers',
    query : {
      limit : obj.limit || 20,
      offset : obj.offset || 0
    }
  }, fn);
};

tumblr.fn.following = function(obj, fn) {
  fn = typeof obj === 'function' ? obj : fn;
  obj = obj || {};

  return this._get({
    type : 'blog',
    path : '/following',
    query : {
      limit : obj.limit || 20,
      offset : obj.offset || 0
    }
  }, fn);
};

['follow', 'unfollow'].forEach(function(f) {
  tumblr.fn[f] = function(url, fn) {
    if (typeof url !== 'object') url = {url:url};

    return this._post({
      type : 'blog',
      path : '/' + f,
      query : url
    }, fn);
  };
});

tumblr.fn.reblog = function(obj, fn) {
  fn = typeof obj === 'function' ? obj : fn;
  obj = obj || {};

  return this._post({
    type : 'blog',
    path : '/post/reblog',
    query : obj
  }, fn);
};

['like', 'unlike'].forEach(function(l) {
  tumblr.fn[l] = function(obj, fn) {
    fn = typeof obj === 'function' ? obj : fn;
    obj = obj || {};

    return this._post({
      type : 'user',
      path : '/' + l,
      query : obj
    }, fn);
  };
});

tumblr.fn.likes = function(obj, fn) {
  fn = typeof obj === 'function' ? obj : fn;
  obj = obj || {};

  return this._get({
    type : 'user',
    path : '/likes',
    query : obj
  }, fn);
};

tumblr.fn['delete'] = function(id, fn) {
  if (typeof id !== 'object') id = {id:id};

  return this._post({
    type : 'blog',
    path : '/post/delete',
    query : id
  }, fn);
};

module.exports = tumblr;
<|endoftext|>