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

var Ticket = function (ticket, expireTime) {
  if (!(this instanceof Ticket)) {
    return new Ticket(ticket, expireTime);
  }
  this.ticket = ticket;
  this.expireTime = expireTime;
};

Ticket.prototype.isValid = function () {
  return !!this.ticket && (new Date().getTime()) < this.expireTime;
};

/**
 * 多台服务器负载均衡时，ticketToken需要外部存储共享。
 * 需要调用此registerTicketHandle来设置获取和保存的自定义方法。 * Examples:
 * ```
 * api.registerTicketHandle(getTicketToken, saveTicketToken);
 * // getTicketToken
 * function getTicketToken(type) {
 *  settingModel.getItem(type, {key: 'weixin_ticketToken'}, function (err, setting) {
 *    if (err) return callback(err);
 *    callback(null, setting.value);
 *  });
 * }
 * // saveTicketToken
 * function saveTicketToken(type, _ticketToken) {
 *  settingModel.setItem(type, {key:'weixin_ticketToken', value: ticketToken}, function (err) {
 *    if (err) return callback(err);
 *    callback(null);
 *  });
 * }
 * ```
 * @param {Function} getTicketToken 获取外部ticketToken的函数
 * @param {Function} saveTicketToken 存储外部ticketToken的函数
 */
exports.registerTicketHandle = function (getTicketToken, saveTicketToken) {
  if (!getTicketToken && !saveTicketToken) {
    this.ticketStore = {};
  }
  this.getTicketToken = getTicketToken || function* (type) {
    type = type || 'jsapi';
    return this.ticketStore[type];
  };

  this.saveTicketToken = saveTicketToken || function* (type, ticketToken) {
    // 向下兼容
    if (arguments.length === 1) {
      ticketToken = type;
      type = 'jsapi';
    }

    this.ticketStore[type] = ticketToken;
    if (process.env.NODE_ENV === 'production') {
      console.warn("Dont save ticket in memory, when cluster or multi-computer!");
    }
  };
};

/**
 * 获取js sdk所需的有效js ticket
 * - `err`, 异常对象
 * - `result`, 正常获取时的数据 * Result:
 * - `errcode`, 0为成功
 * - `errmsg`, 成功为'ok'，错误则为详细错误信息
 * - `ticket`, js sdk有效票据，如：bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA
 * - `expires_in`, 有效期7200秒，开发者必须在自己的服务全局缓存jsapi_ticket */
exports.getTicket = function* (type) {
  var token = yield* this.ensureAccessToken();
  type = type || 'jsapi';

  var url = this.prefix + 'ticket/getticket?access_token=' + token.accessToken + '&type=' + type;
  var data = yield* this.request(url, {dataType: 'json'});

  // 过期时间，因网络延迟等，将实际过期时间提前10秒，以防止临界点
  var expireTime = (new Date().getTime()) + (data.expires_in - 10) * 1000;
  var ticket = new Ticket(data.ticket, expireTime);
  yield* this.saveTicketToken(type, ticket);
  return ticket;
};

/*!
 * 生成随机字符串 */
var createNonceStr = function () {
  return Math.random().toString(36).substr(2, 15);
};

/*!
 * 生成时间戳 */
var createTimestamp = function () {
  return parseInt(new Date().getTime() / 1000, 0) + '';
};

/*!
 * 排序查询字符串 */
var raw = function (args) {
  var keys = Object.keys(args);
  keys = keys.sort();
  var newArgs = {};
  keys.forEach(function (key) {
    newArgs[key.toLowerCase()] = args[key];
  });

  var string = '';
  for (var k in newArgs) {
    string += '&' + k + '=' + newArgs[k];
  }
  return string.substr(1);
};

/*!
 * 签名算法 * @param {String} nonceStr 生成签名的随机串
 * @param {String} jsapi_ticket 用于签名的jsapi_ticket
 * @param {String} timestamp 时间戳
 * @param {String} url 用于签名的url，注意必须与调用JSAPI时的页面URL完全一致 */
var sign = function (nonceStr, jsapi_ticket, timestamp, url) {
  var ret = {
    jsapi_ticket: jsapi_ticket,
    nonceStr: nonceStr,
    timestamp: timestamp,
    url: url
  };
  var string = raw(ret);
  var shasum = crypto.createHash('sha1');
  shasum.update(string);
  return shasum.digest('hex');
};

/*!
 * 卡券card_ext里的签名算法 * @name signCardExt
 * @param {String} api_ticket 用于签名的临时票据，获取方式见2.获取api_ticket。
 * @param {String} card_id 生成卡券时获得的card_id
 * @param {String} timestamp 时间戳，商户生成从1970 年1 月1 日是微信卡券接口文档00:00:00 至今的秒数,即当前的时间,且最终需要转换为字符串形式;由商户生成后传入。
 * @param {String} code 指定的卡券code 码，只能被领一次。use_custom_code 字段为true 的卡券必须填写，非自定义code 不必填写。
 * @param {String} openid 指定领取者的openid，只有该用户能领取。bind_openid 字段为true 的卡券必须填写，非自定义code 不必填写。
 * @param {String} balance 红包余额，以分为单位。红包类型（LUCKY_MONEY）必填、其他卡券类型不必填。 */
var signCardExt = function(api_ticket, card_id, timestamp, code, openid, balance) {
  var values = [api_ticket, card_id, timestamp, code || '',  openid || '', balance || ''];
  values.sort();

  var string = values.join('');
  var shasum = crypto.createHash('sha1');
  shasum.update(string);
  return shasum.digest('hex');
};

exports.ensureTicket = function* (type) {
  var cache = yield* this.getTicketToken(type);

  var ticket;
  // 有ticket并且ticket有效直接调用
  if (cache) {
    ticket = new Ticket(cache.ticket, cache.expireTime);
  }

  // 没有ticket或者无效
  if (!ticket || !ticket.isValid()) {
    // 从微信端获取ticket
    ticket = yield* this.getTicket(type);
  }
  return ticket;
};

/**
 * 获取微信JS SDK Config的所需参数 * Examples:
 * ```
 * var param = {
 *  debug: false,
 *  jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage'],
 *  url: 'http://www.xxx.com'
 * };
 * api.getJsConfig(param);
 * ```
 * - `result`, 调用正常时得到的js sdk config所需参数
 * @param {Object} param 参数
 */
exports.getJsConfig = function* (param) {
  var ticket = yield* this.ensureTicket('jsapi');
  var nonceStr = createNonceStr();
  var jsAPITicket = ticket.ticket;
  var timestamp = createTimestamp();
  var signature = sign(nonceStr, jsAPITicket, timestamp, param.url);

  return {
    debug: param.debug,
    appId: this.appid,
    timestamp: timestamp,
    nonceStr: nonceStr,
    signature: signature,
    jsApiList: param.jsApiList
  };
};

/**
 * 获取微信JS SDK Config的所需参数
 * Examples:
 * ```
 * var param = {
 *  card_id: 'p-hXXXXXXX',
 *  code: '1234',
 *  openid: '111111',
 *  balance: 100
 * };
 * api.getCardExt(param);
 * ```
 * - `result`, 调用正常时得到的card_ext对象，包含所需参数
 * @name getCardExt
 * @param {Object} param 参数
 */
exports.getCardExt = function* (param) {
  var apiTicket = yield* this.ensureTicket('wx_card');
  var timestamp = createTimestamp();
  var signature = signCardExt(apiTicket, param.card_id, timestamp, param.code, param.openid, param.balance);
  var result = {
    timestamp: timestamp,
    signature: signature
  };

  result.code = param.code || '';
  result.openid = param.openid || '';

  if (param.balance) {
    result.balance = param.balance;
  }
  return result;
};

/**
 * 获取最新的js api ticket
 * Examples:
 * ```
 * api.getLatestTicket();
 * ```
 * - `err`, 获取js api ticket出现异常时的异常对象
 * - `ticket`, 获取的ticket
 */
exports.getLatestTicket = function* () {
  return yield* this.ensureTicket('jsapi');
};
<commit_msg>修复 CardEx 签名错误<commit_after>var crypto = require('crypto');

var Ticket = function (ticket, expireTime) {
  if (!(this instanceof Ticket)) {
    return new Ticket(ticket, expireTime);
  }
  this.ticket = ticket;
  this.expireTime = expireTime;
};

Ticket.prototype.isValid = function () {
  return !!this.ticket && (new Date().getTime()) < this.expireTime;
};

/**
 * 多台服务器负载均衡时，ticketToken需要外部存储共享。
 * 需要调用此registerTicketHandle来设置获取和保存的自定义方法。 * Examples:
 * ```
 * api.registerTicketHandle(getTicketToken, saveTicketToken);
 * // getTicketToken
 * function getTicketToken(type) {
 *  settingModel.getItem(type, {key: 'weixin_ticketToken'}, function (err, setting) {
 *    if (err) return callback(err);
 *    callback(null, setting.value);
 *  });
 * }
 * // saveTicketToken
 * function saveTicketToken(type, _ticketToken) {
 *  settingModel.setItem(type, {key:'weixin_ticketToken', value: ticketToken}, function (err) {
 *    if (err) return callback(err);
 *    callback(null);
 *  });
 * }
 * ```
 * @param {Function} getTicketToken 获取外部ticketToken的函数
 * @param {Function} saveTicketToken 存储外部ticketToken的函数
 */
exports.registerTicketHandle = function (getTicketToken, saveTicketToken) {
  if (!getTicketToken && !saveTicketToken) {
    this.ticketStore = {};
  }
  this.getTicketToken = getTicketToken || function* (type) {
    type = type || 'jsapi';
    return this.ticketStore[type];
  };

  this.saveTicketToken = saveTicketToken || function* (type, ticketToken) {
    // 向下兼容
    if (arguments.length === 1) {
      ticketToken = type;
      type = 'jsapi';
    }

    this.ticketStore[type] = ticketToken;
    if (process.env.NODE_ENV === 'production') {
      console.warn("Dont save ticket in memory, when cluster or multi-computer!");
    }
  };
};

/**
 * 获取js sdk所需的有效js ticket
 * - `err`, 异常对象
 * - `result`, 正常获取时的数据 * Result:
 * - `errcode`, 0为成功
 * - `errmsg`, 成功为'ok'，错误则为详细错误信息
 * - `ticket`, js sdk有效票据，如：bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA
 * - `expires_in`, 有效期7200秒，开发者必须在自己的服务全局缓存jsapi_ticket */
exports.getTicket = function* (type) {
  var token = yield* this.ensureAccessToken();
  type = type || 'jsapi';

  var url = this.prefix + 'ticket/getticket?access_token=' + token.accessToken + '&type=' + type;
  var data = yield* this.request(url, {dataType: 'json'});

  // 过期时间，因网络延迟等，将实际过期时间提前10秒，以防止临界点
  var expireTime = (new Date().getTime()) + (data.expires_in - 10) * 1000;
  var ticket = new Ticket(data.ticket, expireTime);
  yield* this.saveTicketToken(type, ticket);
  return ticket;
};

/*!
 * 生成随机字符串 */
var createNonceStr = function () {
  return Math.random().toString(36).substr(2, 15);
};

/*!
 * 生成时间戳 */
var createTimestamp = function () {
  return parseInt(new Date().getTime() / 1000, 0) + '';
};

/*!
 * 排序查询字符串 */
var raw = function (args) {
  var keys = Object.keys(args);
  keys = keys.sort();
  var newArgs = {};
  keys.forEach(function (key) {
    newArgs[key.toLowerCase()] = args[key];
  });

  var string = '';
  for (var k in newArgs) {
    string += '&' + k + '=' + newArgs[k];
  }
  return string.substr(1);
};

/*!
 * 签名算法 * @param {String} nonceStr 生成签名的随机串
 * @param {String} jsapi_ticket 用于签名的jsapi_ticket
 * @param {String} timestamp 时间戳
 * @param {String} url 用于签名的url，注意必须与调用JSAPI时的页面URL完全一致 */
var sign = function (nonceStr, jsapi_ticket, timestamp, url) {
  var ret = {
    jsapi_ticket: jsapi_ticket,
    nonceStr: nonceStr,
    timestamp: timestamp,
    url: url
  };
  var string = raw(ret);
  var shasum = crypto.createHash('sha1');
  shasum.update(string);
  return shasum.digest('hex');
};

/*!
 * 卡券card_ext里的签名算法 * @name signCardExt
 * @param {String} api_ticket 用于签名的临时票据，获取方式见2.获取api_ticket。
 * @param {String} card_id 生成卡券时获得的card_id
 * @param {String} timestamp 时间戳，商户生成从1970 年1 月1 日是微信卡券接口文档00:00:00 至今的秒数,即当前的时间,且最终需要转换为字符串形式;由商户生成后传入。
 * @param {String} code 指定的卡券code 码，只能被领一次。use_custom_code 字段为true 的卡券必须填写，非自定义code 不必填写。
 * @param {String} openid 指定领取者的openid，只有该用户能领取。bind_openid 字段为true 的卡券必须填写，非自定义code 不必填写。
 * @param {String} balance 红包余额，以分为单位。红包类型（LUCKY_MONEY）必填、其他卡券类型不必填。 */
var signCardExt = function(api_ticket, card_id, timestamp, code, openid, balance) {
  var values = [api_ticket, card_id, timestamp, code || '',  openid || '', balance || ''];
  values.sort();

  var string = values.join('');
  var shasum = crypto.createHash('sha1');
  shasum.update(string);
  return shasum.digest('hex');
};

exports.ensureTicket = function* (type) {
  var cache = yield* this.getTicketToken(type);

  var ticket;
  // 有ticket并且ticket有效直接调用
  if (cache) {
    ticket = new Ticket(cache.ticket, cache.expireTime);
  }

  // 没有ticket或者无效
  if (!ticket || !ticket.isValid()) {
    // 从微信端获取ticket
    ticket = yield* this.getTicket(type);
  }
  return ticket;
};

/**
 * 获取微信JS SDK Config的所需参数 * Examples:
 * ```
 * var param = {
 *  debug: false,
 *  jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage'],
 *  url: 'http://www.xxx.com'
 * };
 * api.getJsConfig(param);
 * ```
 * - `result`, 调用正常时得到的js sdk config所需参数
 * @param {Object} param 参数
 */
exports.getJsConfig = function* (param) {
  var ticket = yield* this.ensureTicket('jsapi');
  var nonceStr = createNonceStr();
  var jsAPITicket = ticket.ticket;
  var timestamp = createTimestamp();
  var signature = sign(nonceStr, jsAPITicket, timestamp, param.url);

  return {
    debug: param.debug,
    appId: this.appid,
    timestamp: timestamp,
    nonceStr: nonceStr,
    signature: signature,
    jsApiList: param.jsApiList
  };
};

/**
 * 获取微信JS SDK Config的所需参数
 * Examples:
 * ```
 * var param = {
 *  card_id: 'p-hXXXXXXX',
 *  code: '1234',
 *  openid: '111111',
 *  balance: 100
 * };
 * api.getCardExt(param);
 * ```
 * - `result`, 调用正常时得到的card_ext对象，包含所需参数
 * @name getCardExt
 * @param {Object} param 参数
 */
exports.getCardExt = function* (param) {
  var apiTicket = yield* this.ensureTicket('wx_card');
  var timestamp = createTimestamp();
  var signature = signCardExt(apiTicket.ticket, param.card_id, timestamp, param.code, param.openid, param.balance);
  var result = {
    timestamp: timestamp,
    signature: signature
  };

  result.code = param.code || '';
  result.openid = param.openid || '';

  if (param.balance) {
    result.balance = param.balance;
  }
  return result;
};

/**
 * 获取最新的js api ticket
 * Examples:
 * ```
 * api.getLatestTicket();
 * ```
 * - `err`, 获取js api ticket出现异常时的异常对象
 * - `ticket`, 获取的ticket
 */
exports.getLatestTicket = function* () {
  return yield* this.ensureTicket('jsapi');
};
<|endoftext|>


<commit_before>var HTTP = {
	_parseQueryString: function(qs) {
		var result = {};
		var parts = qs.split("&");
		for (var i=0;i<parts.length;i++) {
			var item = parts[i];
			if (!item) { continue; }
			var eq = item.indexOf("=");
			var name = "";
			var value = "";

			if (eq == -1) {
				name = item;
			} else {
				name = item.substring(0,eq);
				value = item.substring(eq+1);
			}
			if (name.substring(name.length-2) == "[]") { name = name.substring(0,name.length-2); }

			name = this._decode(name);
			value = this._decode(value);

			this._mixIn(result, name, value);
		}
		return result;
	},
	_decode: function(str) {
		var s = str.replace(/\+/g, " ");
		try { s = decodeURIComponent(s); } catch(e) {} finally { return s; }
	},
	_mixIn: function(result, name, value) {
		if (!(name in result)) {
			result[name] = value;
		} else if (result[name] instanceof Array) {
			result[name].push(value);
		} else {
			result[name] = [result[name], value];
		}
	}
};

HTTP.ServerRequest = function(input, headers) {
	this._input = input;
	this._headers = headers;

    this.get = {};
    this.post = {};
    this.cookie = {};
    this.files = {};

    this.method = null;
    if (this._headers["REQUEST_METHOD"]) { this.method = this._headers["REQUEST_METHOD"].toUpperCase(); }
    if (!this.method) { return; }

    this._parseCookie();
    this.get = HTTP._parseQueryString(this._headers["QUERY_STRING"] || "");
    if (this.method == "POST") { this._parsePOST(); }
}

HTTP.ServerRequest.prototype.headers = function() {
	return this._headers;
}

HTTP.ServerRequest.prototype.header = function(name) {
    var n = "HTTP_" + name.toUpperCase().replace(/-/g,"_");
    return (n in this._headers ? this._headers[n] : null);
}

HTTP.ServerRequest.prototype._parseCookie = function() {
    if (!("HTTP_COOKIE" in this._headers)) { return; }
    var all = this._headers["HTTP_COOKIE"].split("; ");
    for (var i=0;i<all.length;i++) {
		var row = all[i];
		var eq = row.indexOf("=");
		var name = row.substring(0,eq);
		var value = row.substring(eq+1);
		if (!this.cookie[name]) { this.cookie[name] = unescape(value); }
    }
}

HTTP.ServerRequest.prototype._decode = function(str) {
    var s = str;
    try {
		s = decodeURIComponent(str.replace(/\+/g, " "));
	} catch(e) {
    } finally {
		return s;
    }
}

HTTP.ServerRequest.prototype._parsePOST = function() {

    var ct = this._headers["CONTENT_TYPE"] || "";
    var length = parseInt(this._headers["CONTENT_LENGTH"], 10);
    if (!length) { return false; }

    if (ct.indexOf("application/x-www-form-urlencoded") != -1) {
		var retstr = this._readPostToString(length);
		this.post = HTTP._parseQueryString(retstr);
    } else if (ct.match(/boundary/)) {
		var retstr = this._readPostToString(length);
		this._parseMultipart(ct, retstr, false);
    } else {
		return false;
    }
    return true;
}

HTTP.ServerRequest.prototype._readPostToString = function(length) {
    		var datarr = [];
		while(datarr.length < length){
			var tmp = this._input(length, true);
			for (var i=0;i<tmp.length;i++) { datarr.push(tmp[i]); }
		}
		var retstr = "";
		for (var i=0;i<datarr.length;i++) {
			retstr += String.fromCharCode(datarr[i]);
		}
		return retstr;
}

HTTP.ServerRequest.prototype._parseMultipart = function(header, data, name) {
    var boundary = header.match(/boundary=(.*)/); /* find boundary */
    boundary = boundary[1];
    var start = "--"+boundary+"\r\n";
    var end = "--"+boundary+"--";
    var parts = data.split(start); /* split by boundary */
    for (var i=0;i<parts.length;i++) { /* all multipart parts */
		var item = parts[i].substring(0, parts[i].length-2);
		if (item == "" || item == "--") { continue; }

		var lines = item.split("\r\n");
		var done = false;
		var obj = {
			headers:{},
			data:""
		};
		for (var j=0;j<lines.length;j++) {
			if (lines[j] == end) { continue; }
			if (done) { /* data */
				obj.data += (obj.data.length ? "\r\n" : "")+lines[j];
			} else { /* headers */
				if (lines[j] == "") { done = true; continue; } /* headers done */
				var r = lines[j].match(/([^:]+): *(.*)/);
				var name = r[1].replace(/-/g,"_").toUpperCase();
				var value = r[2];
				obj.headers[name] = value;
			}
		}
		this._processMultipart(obj, name); /* content parsed, decide how to handle it */
    }
}

HTTP.ServerRequest.prototype._processMultipart = function(obj, n) {
    var name = n;
    var cd = obj.headers["CONTENT_DISPOSITION"] || "";
    var ct = obj.headers["CONTENT_TYPE"] || "";
    var r = cd.match(/ name="(.*?)"/i); /* form field name */
    if (r) { name = r[1]; }
    if (ct.match(/multipart\/mixed/i)) { /* recursive processing */
		this._parseMultipart(ct, obj.data, name);
    } else { /* no recursion: either file or form field */
        var r = cd.match(/filename="(.*?)"/i);
		if (r) { /* file */
			obj.originalName = r[1];
			HTTP._mixIn(this.files, name, obj);
		} else { /* form field */
			var utf = Util.utf8encode(obj.data);
			HTTP._mixIn(this.post, name, utf);
		}
	}
}

HTTP.ServerResponse = function(output) {
	this._output = output;
    this._outputStarted = false;
    this._ct = false; /* Content-type - the only header which must be present */
}

HTTP.ServerResponse.prototype.write = function(str) {
    if (!this._outputStarted) {
		this._outputStarted = true;
		if (!this._ct) { this.header({"Content-type":"text/html"}); }
		this._output("\r\n");
    }
    this._output(str);
}

HTTP.ServerResponse.prototype.cookie = function(name, value, expires, path, domain, secure, httponly) {
    if (expires && !(expires instanceof Date)) { return false; }
    var arr = [];
    arr.push(encodeURIComponent(name)+"="+encodeURIComponent(value));
    if (expires) { arr.push("expires="+expires.toGMTString()); }
    if (path) { arr.push("path="+path); }
    if (domain) { arr.push("domain="+domain); }
    if (secure) { arr.push("secure"); }
    if (httponly) { arr.push("httponly"); }

    var str = arr.join("; ");
    this.header({"Set-Cookie":str});
    return true;
}

HTTP.ServerResponse.prototype.header = function(h) {
    for (var p in h) {
		if (p.match(/content-type/i)) { this._ct = true; }
		if (p.match(/location/i)) { this.status(303, "See Other"); }
		this._output(p+": "+h[p]+"\r\n");
    }
}

HTTP.ServerResponse.prototype.status = function(number, reason) {
	var text = number.toString();
	if (reason) { text += " "+reason; }
	this.header({"Status":text});
}

HTTP.ClientRequest = function(url) {
	this._headers = {};
	this.method = "GET";
	this.get = {};
	this.post = {};
	this.cookie = {};

	var u = url;
	var index = u.indexOf("?");
	if (index != -1) { /* parse user-supplied get */
		u = url.substring(0, index);
		var qs = url.substring(index+1);
		this.get = HTTP._parseQueryString(qs);
	}

	if (u.indexOf("://") == -1) { u = "http://"+u; }
	if (u.indexOf("/", 8) == -1) { u += "/"; }
	this._url = u;
}

HTTP.ClientRequest.prototype.header = function(obj) {
	for (var p in obj) {
		this._headers[p] = obj[p];
	}
}

HTTP.ClientRequest.prototype.send = function(follow) {
	var Socket = require("socket").Socket;

	var items = this._splitUrl();
	var host = items[0];
	var port = items[1];
	var url = items[2];
	var ip = Socket.getAddrInfo(host);
	this.header({"Host":host});

	/* defaults */
	this.header({
		"Connection":"close",
		"Accept-Charset":"utf-8",
		"Accept-Encoding":"identity"
	});

	/* add get data */
	var get = this._serialize(this.get);
	if (get) { url += "?"+get; }

	/* add cookies */
	var arr = [];
	for (var p in this.cookie) {
		arr.push(escape(p)+"="+escape(this.cookie[p]));
	}
	if (arr.length) { this.header({"Cookie":arr.join("; ")}); }

	/* add post headers */
	var post = this._serialize(this.post);
	if (post.length) {
		this.header({
			"Content-Length": post.length,
			"Content-Type": "application/x-www-form-urlencoded"
		});
	}

	/* build request */
	var data = this.method + " " + url + " HTTP/1.1\r\n";
	for (var p in this._headers) {
		data += p+": "+this._headers[p]+"\r\n";
	}
	data += "\r\n";
	if (post) {
		data += post+"\r\n";
		data += "\r\n";
	}

	var s = new Socket(Socket.PF_INET, Socket.SOCK_STREAM, Socket.IPPROTO_TCP);
	s.connect(ip, port);
	s.send(data);

	var received = "";
	do {
		var part = s.receive(1024, true);
		for (var i=0;i<part.length;i++) {
			received += String.fromCharCode(part[i]);
		}
	} while (part.length > 0);
	s.close();
	received = Util.utf8encode(received);

	return this._handleResponse(received, follow);
}

HTTP.ClientRequest.prototype._serialize = function(obj) {
	var arr = [];
	for (var p in obj) {
		var val = obj[p];
		if (!(val instanceof Array)) { val = [val]; }
		for (var i=0;i<val.length;i++) {
			arr.push(encodeURIComponent(p)+"="+encodeURIComponent(val[i]));
		}
	}
	return arr.join("&");
}

HTTP.ClientRequest.prototype._splitUrl = function() {
	var parts = this._url.match(/^ *(http:\/\/)?([^:\/]+)(:([0-9]+))?(.*)$/);
	var host = parts[2];
	var port = parts[4] || 80;
	var url = parts[5];
	return [host, port, url];
}

HTTP.ClientRequest.prototype._handleResponse = function(data, follow) {
	var codes = [301, 302, 303, 307];
	var r = new HTTP.ClientResponse(data);
	if (!follow) { return r; }

	var code = r.status;
	if (codes.indexOf(code) == -1) { return r; }

	var loc = r.header("Location");
	if (!loc) { return r; }

	if (code == 302 || code == 303) {
		/*
			302 should not be used for switching to GET, but...
			see http://en.wikipedia.org/wiki/HTTP_302 ;)
		*/
		this.method = "GET";
	}
	if (loc.indexOf("://") != -1) {
		/* absolute URI, standards compliant */
		this._url = loc;
	} else {
		/* relative URI */
		if (loc.charAt(0) == "/") {
			var i = this._url.indexOf("/", 8);
		} else {
			var i = this._url.lastIndexOf("/")+1;
		}
		this._url = this._url.substring(0, i) + loc;
	}
	return this.send(follow);
}

HTTP.ClientResponse = function(data) {
	this.data = "";
	this.status = 0;
	this.statusReason = "";
	this._headers = {};

	var index = data.indexOf("\r\n\r\n");
	var body = data.substring(index+4);
	var h = data.substring(0, index);
	var arr = h.split("\r\n");

	var parts = arr.shift().match(/^ *http\/[^ ]+ *([0-9]+) *(.*)$/i);
	this.status = parseInt(parts[1], 10);
	this.statusReason = parts[2] || "";

	for (var i=0;i<arr.length;i++) {
		var parts = arr[i].match(/^ *([^: ]+) *: *(.*)$/);
		if (parts) {
			this._headers[parts[1].toUpperCase()] = parts[2];
		}
	}

	if (this.header("Transfer-encoding") == "chunked") {
		this.data = this._parseChunked(body);
	} else {
		this.data = body;
	}
}

HTTP.ClientResponse.prototype.header = function(name) {
	return this._headers[name.toUpperCase()];
}

HTTP.ClientResponse.prototype.headers = function() {
	return this._headers;
}

HTTP.ClientResponse.prototype._parseChunked = function(origBody) {
	var index = 0
	var ch;
	var result = "";
	var body = Util.utf8decode(origBody);

	while (index < body.length) {
		hex = "";

		/* fetch hex number at the beginning of each chunk. this is terminated by \r\n */
		while (index < body.length) {
			ch = body.charAt(index);
			if (ch == "\r") { break; }
			hex += ch;
			index++;
		}

		/* skip CRLF */
		index += 2;

		var chunkLength = parseInt(hex, 16);
		if (!chunkLength) { break; }

		/* read the chunk */
		result += body.substring(index, index + chunkLength);
		index += chunkLength;

		/* skip CRLF after chunk */
		index += 2;
	}

	return Util.utf8encode(result);
}

exports.HTTP = HTTP;
<commit_msg>#19, support for arbitrary post<commit_after>var HTTP = {
	_parseQueryString: function(qs) {
		var result = {};
		var parts = qs.split("&");
		for (var i=0;i<parts.length;i++) {
			var item = parts[i];
			if (!item) { continue; }
			var eq = item.indexOf("=");
			var name = "";
			var value = "";

			if (eq == -1) {
				name = item;
			} else {
				name = item.substring(0,eq);
				value = item.substring(eq+1);
			}
			if (name.substring(name.length-2) == "[]") { name = name.substring(0,name.length-2); }

			name = this._decode(name);
			value = this._decode(value);

			this._mixIn(result, name, value);
		}
		return result;
	},
	_decode: function(str) {
		var s = str.replace(/\+/g, " ");
		try { s = decodeURIComponent(s); } catch(e) {} finally { return s; }
	},
	_mixIn: function(result, name, value) {
		if (!(name in result)) {
			result[name] = value;
		} else if (result[name] instanceof Array) {
			result[name].push(value);
		} else {
			result[name] = [result[name], value];
		}
	}
};

HTTP.ServerRequest = function(input, headers) {
	this._input = input;
	this._headers = headers;

    this.get = {};
    this.post = {};
    this.cookie = {};
    this.files = {};

    this.method = null;
    if (this._headers["REQUEST_METHOD"]) { this.method = this._headers["REQUEST_METHOD"].toUpperCase(); }
    if (!this.method) { return; }

    this._parseCookie();
    this.get = HTTP._parseQueryString(this._headers["QUERY_STRING"] || "");
    if (this.method == "POST") { this._parsePOST(); }
}

HTTP.ServerRequest.prototype.headers = function() {
	return this._headers;
}

HTTP.ServerRequest.prototype.header = function(name) {
    var n = "HTTP_" + name.toUpperCase().replace(/-/g,"_");
    return (n in this._headers ? this._headers[n] : null);
}

HTTP.ServerRequest.prototype._parseCookie = function() {
    if (!("HTTP_COOKIE" in this._headers)) { return; }
    var all = this._headers["HTTP_COOKIE"].split("; ");
    for (var i=0;i<all.length;i++) {
		var row = all[i];
		var eq = row.indexOf("=");
		var name = row.substring(0,eq);
		var value = row.substring(eq+1);
		if (!this.cookie[name]) { this.cookie[name] = unescape(value); }
    }
}

HTTP.ServerRequest.prototype._decode = function(str) {
    var s = str;
    try {
		s = decodeURIComponent(str.replace(/\+/g, " "));
	} catch(e) {
    } finally {
		return s;
    }
}

HTTP.ServerRequest.prototype._parsePOST = function() {

    var ct = this._headers["CONTENT_TYPE"] || "";
    var length = parseInt(this._headers["CONTENT_LENGTH"], 10);
    if (!length) { return false; }

    if (ct.indexOf("application/x-www-form-urlencoded") != -1) {
		var retstr = this._readPostToString(length);
		this.post = HTTP._parseQueryString(retstr);
    } else if (ct.match(/boundary/)) {
		var retstr = this._readPostToString(length);
		this._parseMultipart(ct, retstr, false);
    } else {
		return false;
    }
    return true;
}

HTTP.ServerRequest.prototype._readPostToString = function(length) {
    		var datarr = [];
		while(datarr.length < length){
			var tmp = this._input(length, true);
			for (var i=0;i<tmp.length;i++) { datarr.push(tmp[i]); }
		}
		var retstr = "";
		for (var i=0;i<datarr.length;i++) {
			retstr += String.fromCharCode(datarr[i]);
		}
		return retstr;
}

HTTP.ServerRequest.prototype._parseMultipart = function(header, data, name) {
    var boundary = header.match(/boundary=(.*)/); /* find boundary */
    boundary = boundary[1];
    var start = "--"+boundary+"\r\n";
    var end = "--"+boundary+"--";
    var parts = data.split(start); /* split by boundary */
    for (var i=0;i<parts.length;i++) { /* all multipart parts */
		var item = parts[i].substring(0, parts[i].length-2);
		if (item == "" || item == "--") { continue; }

		var lines = item.split("\r\n");
		var done = false;
		var obj = {
			headers:{},
			data:""
		};
		for (var j=0;j<lines.length;j++) {
			if (lines[j] == end) { continue; }
			if (done) { /* data */
				obj.data += (obj.data.length ? "\r\n" : "")+lines[j];
			} else { /* headers */
				if (lines[j] == "") { done = true; continue; } /* headers done */
				var r = lines[j].match(/([^:]+): *(.*)/);
				var name = r[1].replace(/-/g,"_").toUpperCase();
				var value = r[2];
				obj.headers[name] = value;
			}
		}
		this._processMultipart(obj, name); /* content parsed, decide how to handle it */
    }
}

HTTP.ServerRequest.prototype._processMultipart = function(obj, n) {
    var name = n;
    var cd = obj.headers["CONTENT_DISPOSITION"] || "";
    var ct = obj.headers["CONTENT_TYPE"] || "";
    var r = cd.match(/ name="(.*?)"/i); /* form field name */
    if (r) { name = r[1]; }
    if (ct.match(/multipart\/mixed/i)) { /* recursive processing */
		this._parseMultipart(ct, obj.data, name);
    } else { /* no recursion: either file or form field */
        var r = cd.match(/filename="(.*?)"/i);
		if (r) { /* file */
			obj.originalName = r[1];
			HTTP._mixIn(this.files, name, obj);
		} else { /* form field */
			var utf = Util.utf8encode(obj.data);
			HTTP._mixIn(this.post, name, utf);
		}
	}
}

HTTP.ServerResponse = function(output) {
	this._output = output;
    this._outputStarted = false;
    this._ct = false; /* Content-type - the only header which must be present */
}

HTTP.ServerResponse.prototype.write = function(str) {
    if (!this._outputStarted) {
		this._outputStarted = true;
		if (!this._ct) { this.header({"Content-type":"text/html"}); }
		this._output("\r\n");
    }
    this._output(str);
}

HTTP.ServerResponse.prototype.cookie = function(name, value, expires, path, domain, secure, httponly) {
    if (expires && !(expires instanceof Date)) { return false; }
    var arr = [];
    arr.push(encodeURIComponent(name)+"="+encodeURIComponent(value));
    if (expires) { arr.push("expires="+expires.toGMTString()); }
    if (path) { arr.push("path="+path); }
    if (domain) { arr.push("domain="+domain); }
    if (secure) { arr.push("secure"); }
    if (httponly) { arr.push("httponly"); }

    var str = arr.join("; ");
    this.header({"Set-Cookie":str});
    return true;
}

HTTP.ServerResponse.prototype.header = function(h) {
    for (var p in h) {
		if (p.match(/content-type/i)) { this._ct = true; }
		if (p.match(/location/i)) { this.status(303, "See Other"); }
		this._output(p+": "+h[p]+"\r\n");
    }
}

HTTP.ServerResponse.prototype.status = function(number, reason) {
	var text = number.toString();
	if (reason) { text += " "+reason; }
	this.header({"Status":text});
}

HTTP.ClientRequest = function(url) {
	this._headers = {};
	this.method = "GET";
	this.get = {};
	this.post = {};
	this.cookie = {};

	var u = url;
	var index = u.indexOf("?");
	if (index != -1) { /* parse user-supplied get */
		u = url.substring(0, index);
		var qs = url.substring(index+1);
		this.get = HTTP._parseQueryString(qs);
	}

	if (u.indexOf("://") == -1) { u = "http://"+u; }
	if (u.indexOf("/", 8) == -1) { u += "/"; }
	this._url = u;
}

HTTP.ClientRequest.prototype.header = function(obj) {
	for (var p in obj) {
		this._headers[p] = obj[p];
	}
}

HTTP.ClientRequest.prototype.send = function(follow) {
	var Socket = require("socket").Socket;

	var items = this._splitUrl();
	var host = items[0];
	var port = items[1];
	var url = items[2];
	var ip = Socket.getAddrInfo(host);
	this.header({"Host":host});

	/* defaults */
	this.header({
		"Connection":"close",
		"Accept-Charset":"utf-8",
		"Accept-Encoding":"identity"
	});

	/* add get data */
	var get = this._serialize(this.get);
	if (get) { url += "?"+get; }

	/* add cookies */
	var arr = [];
	for (var p in this.cookie) {
		arr.push(escape(p)+"="+escape(this.cookie[p]));
	}
	if (arr.length) { this.header({"Cookie":arr.join("; ")}); }

	/* add post headers */
	var post = null;
	if (typeof(this.post) == "object") {
		var post = this._serialize(this.post);
		if (post.length) {
			this.header({
				"Content-Length": post.length,
				"Content-Type": "application/x-www-form-urlencoded"
			});
		}
	} else {
		post = this.post;
		this.header({"Content-Length": post.length});
	}

	/* build request */
	var data = this.method + " " + url + " HTTP/1.1\r\n";
	for (var p in this._headers) {
		data += p+": "+this._headers[p]+"\r\n";
	}
	data += "\r\n";
	if (post) {
		data += post+"\r\n";
		data += "\r\n";
	}

	var s = new Socket(Socket.PF_INET, Socket.SOCK_STREAM, Socket.IPPROTO_TCP);
	s.connect(ip, port);
	s.send(data);

	var received = "";
	do {
		var part = s.receive(1024, true);
		for (var i=0;i<part.length;i++) {
			received += String.fromCharCode(part[i]);
		}
	} while (part.length > 0);
	s.close();
	received = Util.utf8encode(received);

	return this._handleResponse(received, follow);
}

HTTP.ClientRequest.prototype._serialize = function(obj) {
	var arr = [];
	for (var p in obj) {
		var val = obj[p];
		if (!(val instanceof Array)) { val = [val]; }
		for (var i=0;i<val.length;i++) {
			arr.push(encodeURIComponent(p)+"="+encodeURIComponent(val[i]));
		}
	}
	return arr.join("&");
}

HTTP.ClientRequest.prototype._splitUrl = function() {
	var parts = this._url.match(/^ *(http:\/\/)?([^:\/]+)(:([0-9]+))?(.*)$/);
	var host = parts[2];
	var port = parts[4] || 80;
	var url = parts[5];
	return [host, port, url];
}

HTTP.ClientRequest.prototype._handleResponse = function(data, follow) {
	var codes = [301, 302, 303, 307];
	var r = new HTTP.ClientResponse(data);
	if (!follow) { return r; }

	var code = r.status;
	if (codes.indexOf(code) == -1) { return r; }

	var loc = r.header("Location");
	if (!loc) { return r; }

	if (code == 302 || code == 303) {
		/*
			302 should not be used for switching to GET, but...
			see http://en.wikipedia.org/wiki/HTTP_302 ;)
		*/
		this.method = "GET";
	}
	if (loc.indexOf("://") != -1) {
		/* absolute URI, standards compliant */
		this._url = loc;
	} else {
		/* relative URI */
		if (loc.charAt(0) == "/") {
			var i = this._url.indexOf("/", 8);
		} else {
			var i = this._url.lastIndexOf("/")+1;
		}
		this._url = this._url.substring(0, i) + loc;
	}
	return this.send(follow);
}

HTTP.ClientResponse = function(data) {
	this.data = "";
	this.status = 0;
	this.statusReason = "";
	this._headers = {};

	var index = data.indexOf("\r\n\r\n");
	var body = data.substring(index+4);
	var h = data.substring(0, index);
	var arr = h.split("\r\n");

	var parts = arr.shift().match(/^ *http\/[^ ]+ *([0-9]+) *(.*)$/i);
	this.status = parseInt(parts[1], 10);
	this.statusReason = parts[2] || "";

	for (var i=0;i<arr.length;i++) {
		var parts = arr[i].match(/^ *([^: ]+) *: *(.*)$/);
		if (parts) {
			this._headers[parts[1].toUpperCase()] = parts[2];
		}
	}

	if (this.header("Transfer-encoding") == "chunked") {
		this.data = this._parseChunked(body);
	} else {
		this.data = body;
	}
}

HTTP.ClientResponse.prototype.header = function(name) {
	return this._headers[name.toUpperCase()];
}

HTTP.ClientResponse.prototype.headers = function() {
	return this._headers;
}

HTTP.ClientResponse.prototype._parseChunked = function(origBody) {
	var index = 0
	var ch;
	var result = "";
	var body = Util.utf8decode(origBody);

	while (index < body.length) {
		hex = "";

		/* fetch hex number at the beginning of each chunk. this is terminated by \r\n */
		while (index < body.length) {
			ch = body.charAt(index);
			if (ch == "\r") { break; }
			hex += ch;
			index++;
		}

		/* skip CRLF */
		index += 2;

		var chunkLength = parseInt(hex, 16);
		if (!chunkLength) { break; }

		/* read the chunk */
		result += body.substring(index, index + chunkLength);
		index += chunkLength;

		/* skip CRLF after chunk */
		index += 2;
	}

	return Util.utf8encode(result);
}

exports.HTTP = HTTP;
<|endoftext|>


<commit_before>var protocol = require('chip.avr.avr109');
var colors = require('colors');
var fs = require('fs');
var Serialport = require('serialport');
var async = require('async');

var Avr109 = function(board, connection, debug) {
  this.board = board;
  this.connection = connection;

  var Protocol = function() { return protocol; };

  // do I want to use 'chip' here? maybe just protocol
  this.chip = new Protocol();
  this.debug = debug ? console.log.bind(console) : function() {};
};

/**
 * Upload method for the AVR109 protocol
 *
 * @param {string} hex - path of hex file for uploading
 * @param {function} callback - function to run upon completion/error
 */
Avr109.prototype._upload = function(file, callback) {
  var _this = this;

  // do try catch here
  var data = fs.readFileSync(file, {
    encoding: 'utf8'
  });

  _this._reset(function(error) {
    if (error) { return callback(error); }

    _this.debug('reset complete.');

    _this.serialPort = _this.connection.serialPort;

    _this.serialPort.open(function(error) {
      if (error) { return callback(error); }

      _this.debug('connected');

      _this._write(data, function(error) {
        return callback(error);
      });
    });
  });
};

Avr109.prototype._write = function(data, callback) {
  var _this = this;

  var options = {
    signature: _this.board.signature.toString(),
    debug: false
  };

  _this.chip.init(_this.serialPort, options, function(error, flasher) {
    if (error) { return callback(error); }

    _this.debug('flashing, please wait...');

    async.series([
        flasher.erase.bind(flasher),
        flasher.program.bind(flasher, data.toString()),
        flasher.verify.bind(flasher),
        flasher.fuseCheck.bind(flasher)
      ],
      function(error) {
        var color = (error ? colors.red : colors.green);
        _this.debug(color('flash complete.'));

        return callback(error);
      });
  });
};

/**
 * Software resets an Arduino AVR109 bootloaded chip into bootloader mode
 *
 * @param {function} callback - function to run upon completion/error
 */
Avr109.prototype._reset = function(callback) {
  var _this = this;

  // creating a temporary connection for resetting only
  var tempSerialPort = new Serialport.SerialPort(_this.connection.options.port, {
    baudRate: 1200,
  });

  _this.connection.serialPort = tempSerialPort;

  _this.debug('resetting board...');

  tempSerialPort.open(function() {
    _this.connection._cycleDTR(function(error) {
      if (error) { return callback(error); }

      _this.connection._setup(function(error) {
        if (error) { return callback(error); }

        _this.connection._pollForPort(function(error) {
          return callback(error);
        });
      });
    });
  });
};

module.exports = Avr109;
<commit_msg>clean up avr19 reset with async<commit_after>var protocol = require('chip.avr.avr109');
var colors = require('colors');
var fs = require('fs');
var Serialport = require('serialport');
var async = require('async');

var Avr109 = function(board, connection, debug) {
  this.board = board;
  this.connection = connection;

  var Protocol = function() { return protocol; };

  // do I want to use 'chip' here? maybe just protocol
  this.chip = new Protocol();
  this.debug = debug ? console.log.bind(console) : function() {};
};

/**
 * Upload method for the AVR109 protocol
 *
 * @param {string} hex - path of hex file for uploading
 * @param {function} callback - function to run upon completion/error
 */
Avr109.prototype._upload = function(file, callback) {
  var _this = this;

  // do try catch here
  var data = fs.readFileSync(file, {
    encoding: 'utf8'
  });

  _this._reset(function(error) {
    if (error) { return callback(error); }

    _this.debug('reset complete.');

    _this.serialPort = _this.connection.serialPort;

    _this.serialPort.open(function(error) {
      if (error) { return callback(error); }

      _this.debug('connected');

      _this._write(data, function(error) {
        return callback(error);
      });
    });
  });
};

Avr109.prototype._write = function(data, callback) {
  var _this = this;

  var options = {
    signature: _this.board.signature.toString(),
    debug: false
  };

  _this.chip.init(_this.serialPort, options, function(error, flasher) {
    if (error) { return callback(error); }

    _this.debug('flashing, please wait...');

    async.series([
      flasher.erase.bind(flasher),
      flasher.program.bind(flasher, data.toString()),
      flasher.verify.bind(flasher),
      flasher.fuseCheck.bind(flasher)
    ],
    function(error) {
      var color = (error ? colors.red : colors.green);
      _this.debug(color('flash complete.'));

      return callback(error);
    });
  });
};

/**
 * Software resets an Arduino AVR109 bootloaded chip into bootloader mode
 *
 * @param {function} callback - function to run upon completion/error
 */
Avr109.prototype._reset = function(callback) {
  var _this = this;
  var conn;

  // creating a temporary connection for resetting only
  var tempSerialPort = new Serialport.SerialPort(_this.connection.options.port, {
    baudRate: 1200,
  }, false);

  _this.connection.serialPort = tempSerialPort;
  conn = _this.connection;

  _this.debug('resetting board...');

  async.series([
    tempSerialPort.open.bind(tempSerialPort),
    conn._cycleDTR.bind(conn),
    conn._setup.bind(conn),
    conn._pollForPort.bind(conn)
  ],
  function(error) {
    return callback(error);
  });
};

module.exports = Avr109;
<|endoftext|>


<commit_before>/**
 * Sanitize to-publish data by running it through a RESTful Bleach endpoint
 */
exports.bleachData = function(endpoint) {
  var sanitize = require("htmlsanitizer");
  // Whitelist for HTML5 elements
  var ALLOWED_TAGS = [
      "!doctype",
      "html",
      "body",
      "a",
      "abbr",
      "address",
      "area",
      "article",
      "aside",
      "audio",
      "b",
      "base",
      "bdi",
      "bdo",
      "blockquote",
      "body",
      "br",
      "button",
      "canvas",
      "caption",
      "cite",
      "code",
      "col",
      "colgroup",
      //"command",
      //"datalist",
      "dd",
      "del",
      "details",
      "dfn",
      "div",
      "dl",
      "dt",
      "em",
      //"embed",
      "fieldset",
      "figcaption",
      "figure",
      "footer",
      "form",
      "h1","h2","h3","h4","h5","h6",
      "head",
      "header",
      "hgroup",
      "hr",
      "html",
      "i",
      "iframe",
      "img",
      "input",
      "ins",
      "keygen",
      "kbd",
      "label",
      "legend",
      "li",
      "link",
      "map",
      "mark",
      "menu",
      "meta",
      "meter",
      "nav",
      //"noscript",
      //"object",
      "ol",
      "optgroup",
      "option",
      "output",
      "p",
      "param",
      "pre",
      "progress",
      "q",
      "rp",
      "rt",
      "s",
      "samp",
      //"script",
      "section",
      "select",
      "small",
      "source",
      "span",
      "strong",
      "style",
      "sub",
      "summary",
      "sup",
      "table",
      "tbody",
      "td",
      "textarea",
      "tfoot",
      "th",
      "thead",
      "time",
      "title",
      "tr",
      //"track",
      "u",
      "ul",
      "var",
      "video",
      "wbr"
  ];

  // Whitelist for HTML5 element attributes.
  var ALLOWED_ATTRS = {
    "*": ["class", "id", "lang", "style", "tabindex", "title", "disabled"],
    "a": ["href"],
    "audio": ["controls", "autoplay", "preload", "loop", "src"],
    "button": ["name", "type", "value"],
    "base": ["href"],
    "canvas": ["width", "height"],
    //"embed": ["height","src","type","width"],
    "iframe": ["src", "width", "height", "allowfullscreen"],
    "img": ["src", "width", "height", "alt"],
    "input": ["type","checked","min","max","maxlength","name","placeholder","pattern",
              "readonly","required","step","value"],
    "link": ["href", "rel", "type", "crossorigin", "media"],
    "menu": ["type", "label"],
    "meta": ["charset", "name", "content"],
    "meter": ["value","min","max","low","high","optimum"],
    //"object": ["data","height","width","name","type"],
    "optgroup": ["label"],
    "option": ["label", "selected", "value"],
    "output": ["for","name"],
    "param": ["name", "value"],
    "progress": ["max","value"],
    "q": ["cite"],
    "source": ["src","type","media"],
    //"script": ["type", "src"],
    "style": ["type","media","scoped"],
    "textarea": ["cols","name","maxlength","readonly","placeholder","required","rows",
                 "selectionDirection","selectionStart","selectionEnd","wrap"],
    "th": ["colspan","rowspan"],
    "td": ["rowspan"],
    "time": ["datetime","pubdate"],
    //"track": ["default","kind","label","src","srclang"],
    "video": ["controls", "autoplay", "preload", "loop", "mediaGroup", "src",
              "poster", "muted", "width", "height"]
  };

  // Whitelist for CSS properties
  var ALLOWED_STYLES = [
    "accelerator",
    "azimuth",
    "background",
    "background-attachment",
    "background-color",
    "background-image",
    "background-position",
    "background-position-x",
    "background-position-y",
    "background-repeat",
    "behavior",
    "border",
    "border-bottom",
    "border-bottom-color",
    "border-bottom-style",
    "border-bottom-width",
    "border-collapse",
    "border-color",
    "border-left",
    "border-left-color",
    "border-left-style",
    "border-left-width",
    "border-right",
    "border-right-color",
    "border-right-style",
    "border-right-width",
    "border-spacing",
    "border-style",
    "border-top",
    "border-top-color",
    "border-top-style",
    "border-top-width",
    "border-width",
    "bottom",
    "caption-side",
    "clear",
    "clip",
    "color",
    "content",
    "counter-increment",
    "counter-reset",
    "cue",
    "cue-after",
    "cue-before",
    "cursor",
    "direction",
    "display",
    "elevation",
    "empty-cells",
    "filter",
    "float",
    "font",
    "font-family",
    "font-size",
    "font-size-adjust",
    "font-stretch",
    "font-style",
    "font-variant",
    "font-weight",
    "height",
    "ime-mode",
    "include-source",
    "layer-background-color",
    "layer-background-image",
    "layout-flow",
    "layout-grid",
    "layout-grid-char",
    "layout-grid-char-spacing",
    "layout-grid-line",
    "layout-grid-mode",
    "layout-grid-type",
    "left",
    "letter-spacing",
    "line-break",
    "line-height",
    "list-style",
    "list-style-image",
    "list-style-position",
    "list-style-type",
    "margin",
    "margin-bottom",
    "margin-left",
    "margin-right",
    "margin-top",
    "marker-offset",
    "marks",
    "max-height",
    "max-width",
    "min-height",
    "min-width",
    "orphans",
    "outline",
    "outline-color",
    "outline-style",
    "outline-width",
    "overflow",
    "overflow-X",
    "overflow-Y",
    "padding",
    "padding-bottom",
    "padding-left",
    "padding-right",
    "padding-top",
    "page",
    "page-break-after",
    "page-break-before",
    "page-break-inside",
    "pause",
    "pause-after",
    "pause-before",
    "pitch",
    "pitch-range",
    "play-during",
    "position",
    "quotes-replace",
    "richness",
    "right",
    "ruby-align",
    "ruby-overhang",
    "ruby-position",
    "size",
    "speak",
    "speak-header",
    "speak-numeral",
    "speak-punctuation",
    "speech-rate",
    "stress",
    "scrollbar-arrow-color",
    "scrollbar-base-color",
    "scrollbar-dark-shadow-color",
    "scrollbar-face-color",
    "scrollbar-highlight-color",
    "scrollbar-shadow-color",
    "scrollbar-3d-light-color",
    "scrollbar-track-color",
    "table-layout",
    "text-align",
    "text-align-last",
    "text-decoration",
    "text-indent",
    "text-justify",
    "text-overflow",
    "text-shadow",
    "text-transform",
    "text-autospace",
    "text-kashida-space",
    "text-underline-position",
    "top",
    "unicode-bidi",
    "vertical-align",
    "visibility",
    "voice-family",
    "volume",
    "white-space",
    "widows",
    "width",
    "word-break",
    "word-spacing",
    "word-wrap",
    "writing-mode",
    "z-index",
    "zoom"
  ];

  // Sanitize some HTML code by running it through Bleach
  return function(req, res, next) {
    sanitize({
      endpoint: endpoint,
      text: req.body.html,
      tags: ALLOWED_TAGS,
      attributes: ALLOWED_ATTRS,
      styles: ALLOWED_STYLES,
      strip: false,
      strip_comments: false,
      parse_as_fragment: false
    }, function(err, sanitizedData) {
      req.body.sanitizedHTML = sanitizedData;
      next(err);
    });
  };
};
<commit_msg>updated whitelist<commit_after>/**
 * Sanitize to-publish data by running it through a RESTful Bleach endpoint
 */
exports.bleachData = function(endpoint) {
  var sanitize = require("htmlsanitizer");
  // Whitelist for HTML5 elements
  var ALLOWED_TAGS = [
      "!doctype",
      "html",
      "body",
      "a",
      "abbr",
      "address",
      "area",
      "article",
      "aside",
      "audio",
      "b",
      "base",
      "bdi",
      "bdo",
      "blockquote",
      "body",
      "br",
      "button",
      "canvas",
      "caption",
      "cite",
      "code",
      "col",
      "colgroup",
      //"command",
      //"datalist",
      "dd",
      "del",
      "details",
      "dfn",
      "div",
      "dl",
      "dt",
      "em",
      //"embed",
      "fieldset",
      "figcaption",
      "figure",
      "footer",
      "form",
      "h1","h2","h3","h4","h5","h6",
      "head",
      "header",
      "hgroup",
      "hr",
      "html",
      "i",
      "iframe",
      "img",
      "input",
      "ins",
      "keygen",
      "kbd",
      "label",
      "legend",
      "li",
      "link",
      "map",
      "mark",
      "menu",
      "meta",
      "meter",
      "nav",
      //"noscript",
      //"object",
      "ol",
      "optgroup",
      "option",
      "output",
      "p",
      "param",
      "pre",
      "progress",
      "q",
      "rp",
      "rt",
      "s",
      "samp",
      //"script",
      "section",
      "select",
      "small",
      "source",
      "span",
      "strong",
      "style",
      "sub",
      "summary",
      "sup",
      "table",
      "tbody",
      "td",
      "textarea",
      "tfoot",
      "th",
      "thead",
      "time",
      "title",
      "tr",
      //"track",
      "u",
      "ul",
      "var",
      "video",
      "wbr"
  ];

  // Whitelist for HTML5 element attributes.
  var ALLOWED_ATTRS = {
    "*": ["class", "id", "lang", "style", "tabindex", "title", "disabled"],
    "a": ["href"],
    "audio": ["controls", "autoplay", "preload", "loop", "src"],
    "button": ["name", "type", "value"],
    "base": ["href"],
    "canvas": ["width", "height"],
    //"embed": ["height","src","type","width"],
    "iframe": ["src", "width", "height", "allowfullscreen"],
    "img": ["src", "width", "height", "alt"],
    "input": ["type","checked","min","max","maxlength","name","placeholder","pattern",
              "readonly","required","step","value"],
    "link": ["href", "rel", "type", "crossorigin", "media"],
    "menu": ["type", "label"],
    "meta": ["charset", "name", "content"],
    "meter": ["value","min","max","low","high","optimum"],
    //"object": ["data","height","width","name","type"],
    "optgroup": ["label"],
    "option": ["label", "selected", "value"],
    "output": ["for","name"],
    "param": ["name", "value"],
    "progress": ["max","value"],
    "q": ["cite"],
    "source": ["src","type","media"],
    //"script": ["type", "src"],
    "style": ["type","media","scoped"],
    "textarea": ["cols","name","maxlength","readonly","placeholder","required","rows",
                 "selectionDirection","selectionStart","selectionEnd","wrap"],
    "th": ["colspan","rowspan"],
    "td": ["rowspan"],
    "time": ["datetime","pubdate"],
    //"track": ["default","kind","label","src","srclang"],
    "video": ["controls", "autoplay", "preload", "loop", "mediaGroup", "src",
              "poster", "muted", "width", "height"]
  };

  // Whitelist for CSS properties
  var ALLOWED_STYLES = [
    "align-content",
    "align-items",
    "align-self",
    "animation",
    "animation-delay",
    "animation-direction",
    "animation-duration",
    "animation-fill-mode",
    "animation-iteration-count",
    "animation-name",
    "animation-play-state",
    "animation-timing-function",
    "auto",
    "backface-visibility",
    "background",
    "background-attachment",
    "background-clip",
    "background-color",
    "background-image",
    "background-origin",
    "background-position",
    "background-repeat",
    "background-size",
    "border",
    "border-bottom",
    "border-bottom-color",
    "border-bottom-left-radius",
    "border-bottom-right-radius",
    "border-bottom-style",
    "border-bottom-width",
    "border-collapse",
    "border-color",
    "border-image",
    "border-image-outset",
    "border-image-repeat",
    "border-image-slice",
    "border-image-source",
    "border-image-width",
    "border-left",
    "border-left-color",
    "border-left-style",
    "border-left-width",
    "border-radius",
    "border-right",
    "border-right-color",
    "border-right-style",
    "border-right-width",
    "border-spacing",
    "border-style",
    "border-top",
    "border-top-color",
    "border-top-left-radius",
    "border-top-right-radius",
    "border-top-style",
    "border-top-width",
    "border-width",
    "bottom",
    "box-decoration-break",
    "box-shadow",
    "box-sizing",
    "break-after",
    "break-before",
    "break-inside",
    "caption-side",
    "clear",
    "clip",
    "clip-path",
    "color",
    "columns",
    "column-count",
    "column-fill",
    "column-gap",
    "column-rule",
    "column-rule-color",
    "column-rule-style",
    "column-rule-width",
    "column-span",
    "column-width",
    "content",
    "counter-increment",
    "counter-reset",
    "cursor",
    "direction",
    "display",
    "empty-cells",
    "filter",
    "flex",
    "flex-basis",
    "flex-direction",
    "flex-flow",
    "flex-grow",
    "flex-shrink",
    "flex-wrap",
    "float",
    "font",
    "font-family",
    "font-feature-settings",
    "font-kerning",
    "font-language-override",
    "font-size",
    "font-size-adjust",
    "font-stretch",
    "font-style",
    "font-variant",
    "font-variant-ligatures",
    "font-weight",
    "height",
    "hyphens",
    "icon",
    "image-rendering",
    "image-resolution",
    "image-orientation",
    "ime-mode",
    "inherit",
    "initial",
    "justify-content",
    "left",
    "letter-spacing",
    "line-height",
    "list-style",
    "list-style-image",
    "list-style-position",
    "list-style-type",
    "margin",
    "margin-bottom",
    "margin-left",
    "margin-right",
    "margin-top",
    "marks",
    "mask",
    "max-height",
    "max-width",
    "min-height",
    "min-width",
    "nav-down",
    "nav-index",
    "nav-left",
    "nav-right",
    "nav-up",
    "none",
    "normal",
    "object-fit",
    "object-position",
    "opacity",
    "order",
    "orphans",
    "outline",
    "outline-color",
    "outline-offset",
    "outline-style",
    "outline-width",
    "overflow",
    "overflow-wrap",
    "overflow-x",
    "overflow-y",
    "padding",
    "padding-bottom",
    "padding-left",
    "padding-right",
    "padding-top",
    "page-break-after",
    "page-break-before",
    "page-break-inside",
    "perspective",
    "perspective-origin",
    "pointer-events",
    "position",
    "quotes",
    "resize",
    "right",
    "table-layout",
    "tab-size",
    "text-align",
    "text-align-last",
    "text-combine-horizontal",
    "text-decoration",
    "text-decoration-color",
    "text-decoration-line",
    "text-decoration-style",
    "text-indent",
    "text-orientation",
    "text-overflow",
    "text-rendering",
    "text-shadow",
    "text-transform",
    "text-underline-position",
    "top",
    "touch-action",
    "transform",
    "transform-origin",
    "transform-style",
    "transition",
    "transition-delay",
    "transition-duration",
    "transition-property",
    "transition-timing-function",
    "unicode-bidi",
    "vertical-align",
    "visibility",
    "white-space",
    "widows",
    "width",
    "word-break",
    "word-spacing",
    "word-wrap",
    "writing-mode",
    "z-index",
    "zoom"
  ];

  // Sanitize some HTML code by running it through Bleach
  return function(req, res, next) {
    sanitize({
      endpoint: endpoint,
      text: req.body.html,
      tags: ALLOWED_TAGS,
      attributes: ALLOWED_ATTRS,
      styles: ALLOWED_STYLES,
      strip: false,
      strip_comments: false,
      parse_as_fragment: false
    }, function(err, sanitizedData) {
      req.body.sanitizedHTML = sanitizedData;
      next(err);
    });
  };
};
<|endoftext|>


<commit_before>import React from 'react';
import Modal from 'react-modal';
import addons from 'react/addons';
import ReactMixin from 'react-mixin';
import { IntlMixin, FormattedMessage } from 'react-intl';
import { Styles, FlatButton } from 'material-ui';
import ReactZeroClipboard from 'react-zeroclipboard';

import { KeyCodes } from 'constants/ActorAppConstants';
import ActorTheme from 'constants/ActorTheme';

import InviteUserByLinkActions from 'actions/InviteUserByLinkActions';
import InviteUserActions from 'actions/InviteUserActions';

import InviteUserStore from 'stores/InviteUserStore';

const ThemeManager = new Styles.ThemeManager();

const appElement = document.getElementById('actor-web-app');
Modal.setAppElement(appElement);

const {addons: { PureRenderMixin }} = addons;

const getStateFromStores = () => {
  return {
    isShown: InviteUserStore.isInviteWithLinkModalOpen(),
    group: InviteUserStore.getGroup(),
    inviteUrl: InviteUserStore.getInviteUrl()
  };
};

@ReactMixin.decorate(IntlMixin)
@ReactMixin.decorate(PureRenderMixin)
class InviteByLink extends React.Component {
  static childContextTypes = {
    muiTheme: React.PropTypes.object
  };

  getChildContext() {
    return {
      muiTheme: ThemeManager.getCurrentTheme()
    };
  }

  constructor(props) {
    super(props);

    this.state = getStateFromStores();

    ThemeManager.setTheme(ActorTheme);
    ThemeManager.setComponentThemes({
      button: {
        minWidth: 60
      }
    });

    InviteUserStore.addChangeListener(this.onChange);
    document.addEventListener('keydown', this.onKeyDown, false);
  }

  componentWillUnmount() {
    InviteUserStore.removeChangeListener(this.onChange);
    document.removeEventListener('keydown', this.onKeyDown, false);
  }

  render() {
    const group = this.state.group;
    const inviteUrl = this.state.inviteUrl;
    const isShown = this.state.isShown;

    let groupName;
    if (group !== null) {
      groupName = {group.name};
    }

    return (
      <Modal className="modal-new modal-new--invite-by-link"
             closeTimeoutMS={150}
             isOpen={isShown}
             style={{width: 400}}>

          <svg className="modal-new__header__icon icon icon--blue"
               dangerouslySetInnerHTML={{__html: '<use xlink:href="assets/sprite/icons.svg#back"/>'}}
               onClick={this.onBackClick}/>

            <FormattedMessage message={this.getIntlMessage('inviteByLinkModalTitle')}/>

            <FlatButton hoverColor="rgba(81,145,219,.17)"
                        label="Done"
                        labelStyle={{padding: '0 8px'}}
                        onClick={this.onClose}
                        secondary={true}
                        style={{marginTop: -6}}/>

          <FormattedMessage groupName={groupName} message={this.getIntlMessage('inviteByLinkModalDescription')}/>
          <textarea className="invite-url" onClick={this.onInviteLinkClick} readOnly row="3" value={inviteUrl}/>

          <button className="button button--light-blue pull-left hide">
            <FormattedMessage message={this.getIntlMessage('inviteByLinkModalRevokeButton')}/>
          </button>
          <ReactZeroClipboard text={inviteUrl}>
            <button className="button button--blue pull-right">
              <FormattedMessage message={this.getIntlMessage('inviteByLinkModalCopyButton')}/>
            </button>
          </ReactZeroClipboard>

      </Modal>
    );
  }

  onClose = () => {
    InviteUserByLinkActions.hide();
  };

  onBackClick = () => {
    this.onClose();
    InviteUserActions.show(this.state.group);
  };

  onInviteLinkClick = event => {
    event.target.select();
  };

  onChange = () => {
    this.setState(getStateFromStores());
  };

  onKeyDown = (event) => {
    if (event.keyCode === KeyCodes.ESC) {
      event.preventDefault();
      this.onClose();
    }
  }
}

export default InviteByLink;
<commit_msg>feat(web): add notification on invitation link copied<commit_after>import React from 'react';
import Modal from 'react-modal';
import addons from 'react/addons';
import ReactMixin from 'react-mixin';
import { IntlMixin, FormattedMessage } from 'react-intl';
import { Styles, FlatButton, Snackbar } from 'material-ui';
import ReactZeroClipboard from 'react-zeroclipboard';

import { KeyCodes } from 'constants/ActorAppConstants';
import ActorTheme from 'constants/ActorTheme';

import InviteUserByLinkActions from 'actions/InviteUserByLinkActions';
import InviteUserActions from 'actions/InviteUserActions';

import InviteUserStore from 'stores/InviteUserStore';

const ThemeManager = new Styles.ThemeManager();

const appElement = document.getElementById('actor-web-app');
Modal.setAppElement(appElement);

const {addons: { PureRenderMixin }} = addons;

const getStateFromStores = () => {
  return {
    isShown: InviteUserStore.isInviteWithLinkModalOpen(),
    group: InviteUserStore.getGroup(),
    inviteUrl: InviteUserStore.getInviteUrl()
  };
};

@ReactMixin.decorate(IntlMixin)
@ReactMixin.decorate(PureRenderMixin)
class InviteByLink extends React.Component {
  static childContextTypes = {
    muiTheme: React.PropTypes.object
  };

  getChildContext() {
    return {
      muiTheme: ThemeManager.getCurrentTheme()
    };
  }

  constructor(props) {
    super(props);

    this.state = getStateFromStores();

    ThemeManager.setTheme(ActorTheme);
    ThemeManager.setComponentThemes({
      button: {
        minWidth: 60
      }
    });

    InviteUserStore.addChangeListener(this.onChange);
    document.addEventListener('keydown', this.onKeyDown, false);
  }

  componentWillUnmount() {
    InviteUserStore.removeChangeListener(this.onChange);
    document.removeEventListener('keydown', this.onKeyDown, false);
  }

  render() {
    const group = this.state.group;
    const inviteUrl = this.state.inviteUrl;
    const isShown = this.state.isShown;
    const snackbarStyles = ActorTheme.getSnackbarStyles();

    let groupName;
    if (group !== null) {
      groupName = {group.name};
    }

    return (
      <Modal className="modal-new modal-new--invite-by-link"
             closeTimeoutMS={150}
             isOpen={isShown}
             style={{width: 400}}>

          <svg className="modal-new__header__icon icon icon--blue"
               dangerouslySetInnerHTML={{__html: '<use xlink:href="assets/sprite/icons.svg#back"/>'}}
               onClick={this.onBackClick}/>

            <FormattedMessage message={this.getIntlMessage('inviteByLinkModalTitle')}/>

            <FlatButton hoverColor="rgba(81,145,219,.17)"
                        label="Done"
                        labelStyle={{padding: '0 8px'}}
                        onClick={this.onClose}
                        secondary={true}
                        style={{marginTop: -6}}/>

          <FormattedMessage groupName={groupName} message={this.getIntlMessage('inviteByLinkModalDescription')}/>
          <textarea className="invite-url" onClick={this.onInviteLinkClick} readOnly row="3" value={inviteUrl}/>

          <button className="button button--light-blue pull-left hide">
            <FormattedMessage message={this.getIntlMessage('inviteByLinkModalRevokeButton')}/>
          </button>
          <ReactZeroClipboard onCopy={this.onInviteLinkCopied} text={inviteUrl}>
            <button className="button button--blue pull-right">
              <FormattedMessage message={this.getIntlMessage('inviteByLinkModalCopyButton')}/>
            </button>
          </ReactZeroClipboard>

        <Snackbar autoHideDuration={3000}
                  message={this.getIntlMessage('integrationTokenCopied')}
                  ref="inviteLinkCopied"
                  style={snackbarStyles}/>
      </Modal>
    );
  }

  onClose = () => {
    InviteUserByLinkActions.hide();
  };

  onBackClick = () => {
    this.onClose();
    InviteUserActions.show(this.state.group);
  };

  onInviteLinkClick = event => {
    event.target.select();
  };

  onChange = () => {
    this.setState(getStateFromStores());
  };

  onKeyDown = (event) => {
    if (event.keyCode === KeyCodes.ESC) {
      event.preventDefault();
      this.onClose();
    }
  };

  onInviteLinkCopied = () => {
    this.refs.inviteLinkCopied.show();
  };

}

export default InviteByLink;
<|endoftext|>


<commit_before>var process = require("child_process");
var gUtil = require('gulp-util');

module.exports = function (options) {

	function gitBranch(file, enc, next) {
        var self = this;

        // 如果文件为空，不做任何操作，转入下一个操作，即下一个 .pipe()
        if (file.isNull()) {
            this.push(file);
            return next();
        }

        // 插件不支持对stream的直接操作，抛出异常
        if (file.isStream()) {
            this.emit('error', new gUtil.PluginError('gulp-git-branch', 'Streaming not supported'));
            return next();
        }

        var cmd = 'git list ' + opt.args;

        exec(cmd, {cwd: cwd}, function(err, stdout, stderr){
      		if (err) {
      			next(err);
      		};
      		if (!opt.quiet) {
      			gutil.log(stdout, stderr);
      		}

      		files.forEach(that.push.bind(that));
      		next();
    	});
    }
    return through2.obj(gitBranch);
}

<commit_msg>bugfix<commit_after>var process = require("child_process");
var gUtil = require('gulp-util');

module.exports = function (options) {

	function gitBranch(file, enc, next) {
        var self = this;

        // 如果文件为空，不做任何操作，转入下一个操作，即下一个 .pipe()
        if (file.isNull()) {
            this.push(file);
            return next();
        }

        // 插件不支持对stream的直接操作，抛出异常
        if (file.isStream()) {
            this.emit('error', new gUtil.PluginError('gulp-git-branch', 'Streaming not supported'));
            return next();
        }

        var cmd = 'git list ' + opt.args;

        process.exec(cmd, {cwd: cwd}, function(err, stdout, stderr){
      		if (err) {
      			next(err);
      		};
      		if (!opt.quiet) {
      			gutil.log(stdout, stderr);
      		}

      		files.forEach(that.push.bind(that));
      		next();
    	});
    }
    return through2.obj(gitBranch);
}

<|endoftext|>


<commit_before>"use strict";

var adapters = ['http-1', 'local-1'];
var qunit = module;

if (typeof module !== undefined && module.exports) {
  this.Pouch = require('../src/pouch.js');
  this.LevelPouch = require('../src/adapters/pouch.leveldb.js');
  this.utils = require('./test.utils.js');

  for (var k in this.utils) {
    global[k] = global[k] || this.utils[k];
  }
  qunit = QUnit.module;
}

adapters.map(function(adapter) {

  qunit("basics: " + adapter, {
    setup: function() {
      this.name = generateAdapterUrl(adapter);
    },
    teardown: function() {
      if (!PERSIST_DATABASES) {
        Pouch.destroy(this.name);
      }
    }
  });

  asyncTest("Create a pouch", function() {
    initTestDB(this.name, function(err, db) {
      ok(!err, 'created a pouch');
      start();
    });
  });

  asyncTest("Create a pouch without DB setup", function() {
    var instantDB;
    Pouch(this.name, function(err, db) {
      ok(!err, 'created a pouch');
      instantDB = Pouch(db.name, {skipSetup: true})
      instantDB.post({test:"abc"}), function(err, info) {
        ok(!err, 'saved document using the skipSetup flag')
        start();
      }
    });
  });

  asyncTest("Remove a pouch", 1, function() {
    var name = this.name;
    initTestDB(name, function(err, db) {
      Pouch.destroy(name, function(err, db) {
        ok(!err);
        start();
      });
    });
  });

  asyncTest("Add a doc", 2, function() {
    initTestDB(this.name, function(err, db) {
      ok(!err, 'opened the pouch');
      db.post({test:"somestuff"}, function (err, info) {
        ok(!err, 'saved a doc with post');
        start();
      });
    });
  });

  asyncTest("Modify a doc", 3, function() {
    console.info('testing: Modify a doc');
    initTestDB(this.name, function(err, db) {
      ok(!err, 'opened the pouch');
      db.post({test: "somestuff"}, function (err, info) {
        ok(!err, 'saved a doc with post');
        db.put({_id: info.id, _rev: info.rev, another: 'test'}, function(err, info2) {
          ok(!err && info2.rev !== info._rev, 'updated a doc with put');
          start();
        });
      });
    });
  });

  asyncTest("Modify a doc with incorrect rev", 3, function() {
    initTestDB(this.name, function(err, db) {
      ok(!err, 'opened the pouch');
      db.post({test: "somestuff"}, function (err, info) {
        ok(!err, 'saved a doc with post');
        var nDoc = {_id: info.id, _rev: info.rev + 'broken', another: 'test'};
        db.put(nDoc, function(err, info2) {
          ok(err, 'put was denied');
          start();
        });
      });
    });
  });

  asyncTest("Get doc", 2, function() {
    initTestDB(this.name, function(err, db) {
      db.post({test:"somestuff"}, function(err, info) {
        db.get(info.id, function(err, doc) {
          ok(doc.test);
          db.get(info.id+'asdf', function(err) {
            ok(err.error);
            start();
          });
        });
      });
    });
  });

  asyncTest("Add a doc with leading underscore in id", function() {
    initTestDB(this.name, function(err, db) {
      db.post({_id: '_testing', value: 42}, function(err, info) {
        ok(err);
        start();
      });
    });
  });

  asyncTest("Get revisions of removed doc", 1, function() {
    initTestDB(this.name, function(err, db) {
      db.post({test:"somestuff"}, function(err, info) {
        var rev = info.rev;
        db.remove({test:"somestuff", _id:info.id, _rev:info.rev}, function(doc) {
          db.get(info.id, {rev: rev}, function(err, doc) {
            ok(!err, 'Recieved deleted doc with rev');
            start();
          });
        });
      });
    });
  });

  asyncTest("Remove doc", 1, function() {
    initTestDB(this.name, function(err, db) {
      db.post({test:"somestuff"}, function(err, info) {
        db.remove({test:"somestuff", _id:info.id, _rev:info.rev}, function(doc) {
          db.get(info.id, function(err) {
            ok(err.error);
            start();
          });
        });
      });
    });
  });

  asyncTest("Remove doc twice with specified id", 4, function() {
    initTestDB(this.name, function(err, db) {
      db.put({_id:"specifiedId", test:"somestuff"}, function(err, info) {
        db.get("specifiedId", function(err, doc) {
          ok(doc.test, "Put and got doc");
          db.remove(doc, function(err, response) {
            ok(!err, "Removed doc");
            db.put({_id:"specifiedId", test:"somestuff2"}, function(err, info) {
              db.get("specifiedId", function(err, doc){
                ok(doc, "Put and got doc again");
                db.remove(doc, function(err, response) {
                  ok(!err, "Removed doc again");
                  start();
                });
              });
            });
          });
        });
      });
    });
  });

  asyncTest("Remove doc, no callback", 2, function() {
    initTestDB(this.name, function(err, db) {
      var changes = db.changes({
        continuous: true,
        include_docs: true,
        onChange: function(change){
          console.log(change);
          if(change.seq == 2){
            ok(change.doc._deleted, 'Doc deleted properly');
            changes.cancel();
            start();
          }
        }
      });
      db.post({_id:"somestuff"}, function (err, res) {
        ok(!err, 'save a doc with post');
        db.remove({_id: res.id, _rev: res.rev});
      });
    });
  });

  asyncTest("Get design doc", 2, function() {
    initTestDB(this.name, function(err, db) {
      db.put({_id: '_design/someid', test:"somestuff"}, function(err, info) {
        db.get(info.id, function(err, doc) {
          ok(doc.test);
          db.get(info.id+'asdf', function(err) {
            ok(err.error);
            start();
          });
        });
      });
    });
  });

  asyncTest("Delete document without id", 1, function () {
    initTestDB(this.name, function(err, db) {
      db.remove({test:'ing'}, function(err) {
        ok(err, 'failed to delete');
        start();
      });
    });
  });

  asyncTest("Bulk docs", 3, function() {
    initTestDB(this.name, function(err, db) {
      ok(!err, 'opened the pouch');
      db.bulkDocs({docs: [{test:"somestuff"}, {test:"another"}]}, function(err, infos) {
        ok(!infos[0].error);
        ok(!infos[1].error);
        start();
      });
    });
  });

  /*
  asyncTest("Sync a doc", 6, function() {
    var couch = generateAdapterUrl('http-2');
    initTestDB(this.name, function(err, db) {
      ok(!err, 'opened the pouch');
      initTestDB(couch, function(err, db2) {
        ok(!err, 'opened the couch');
        db.put({_id:"adoc", test:"somestuff"}, function (err, info) {
          ok(!err, 'saved a doc with post');
          db.replicate.to(couch, function(err, info) {
            ok(!err, 'replicated pouch to couch');
            db.replicate.from(couch, function(err, info) {
              ok(!err, 'replicated couch back to pouch');
              db.get("adoc", {conflicts:true}, function(err, doc) {
                ok(!doc._conflicts, 'doc has no conflicts');
                start();
              });
            });
          });
        });
      })
    });
  });
  */

  asyncTest("Check revisions", 1, function() {
    initTestDB(this.name, function(err, db) {
      db.post({test: "somestuff"}, function (err, info) {
        db.put({_id: info.id, _rev: info.rev, another: 'test'}, function(err, info) {
          db.put({_id: info.id, _rev: info.rev, a: 'change'}, function(err, info2) {
            db.get(info.id, {revs_info:true}, function(err, doc) {
              ok(doc._revs_info.length === 3, 'updated a doc with put');
              start();
            });
          });
        });
      });
    });
  });

  // From here we are copying over tests from CouchDB
  // https://github.com/apache/couchdb/blob/master/share/www/script/test/basics.js
  /*
  asyncTest("Check database with slashes", 1, function() {
    initTestDB('idb://test_suite_db%2Fwith_slashes', function(err, db) {
      ok(!err, 'opened');
      start();
    });
  });
  */

  asyncTest("Basic checks", 8, function() {
    initTestDB(this.name, function(err, db) {
      db.info(function(err, info) {
        var updateSeq = info.update_seq;
        var doc = {_id: '0', a: 1, b:1};
        ok(info.doc_count === 0);
        db.put(doc, function(err, res) {
          ok(res.ok === true);
          ok(res.id);
          ok(res.rev);
          db.info(function(err, info) {
            ok(info.doc_count === 1);
            equal(info.update_seq, updateSeq + 1, 'update seq incremented');
            db.get(doc._id, function(err, doc) {
              ok(doc._id === res.id && doc._rev === res.rev);
              db.get(doc._id, {revs_info: true}, function(err, doc) {
                ok(doc._revs_info[0].status === 'available');
                start();
              });
            });
          });
        });
      });
    });
  });

  asyncTest("Testing Rev format", 2, function() {
    console.info('testing: Rev format');
    var revs = [];
    initTestDB(this.name, function(err, db) {
      db.post({test: "somestuff"}, function (err, info) {
        revs.unshift(info.rev.split('-')[1]);
        db.put({_id: info.id, _rev: info.rev, another: 'test1'}, function(err, info2) {
          revs.unshift(info2.rev.split('-')[1]);
          db.put({_id: info.id, _rev: info2.rev, last: 'test2'}, function(err, info3) {
            revs.unshift(info3.rev.split('-')[1]);
            db.get(info.id, {revs:true}, function(err, doc) {
              ok(doc._revisions.start === 3, 'correct starting position');
              deepEqual(revs, doc._revisions.ids, 'correct revs returned');
              start();
            });
          });
        });
      });
    });
  });

  asyncTest("Testing issue #48", 1, function() {

    var docs = [{"id":"0"}, {"id":"1"}, {"id":"2"}, {"id":"3"}, {"id":"4"}, {"id":"5"}];
    var x = 0;
    var timer;

    initTestDB(this.name, function(err, db) {
      var save = function() {
        db.bulkDocs({docs: docs}, function(err, res) {
          if (++x === 10) {
            ok(true, 'all updated succedded');
            clearInterval(timer);
            start();
          }
        });
      };
      timer = setInterval(save, 50);
    });
  });

  asyncTest("Testing valid id", 1, function() {
    initTestDB(this.name, function(err, db) {
      db.post({'_id': 123, test: "somestuff"}, function (err, info) {
        ok(err, 'id must be a string');
        start();
      });
    });
  });

  asyncTest("Put doc without _id should fail", 1, function() {
    initTestDB(this.name, function(err, db) {
      db.put({test:"somestuff"}, function(err, info) {
        ok(err, '_id is required');
        start();
      });
    });
  });

  asyncTest("Retrieve old revision", function() {
    initTestDB(this.name, function(err, db) {
      ok(!err, 'opened the pouch');
      db.post({version: "first"}, function (err, info) {
        var firstrev = info.rev;
        ok(!err, 'saved a doc with post');
        db.put({_id: info.id, _rev: info.rev, version: 'second'}, function(err, info2) {
          ok(!err && info2.rev !== info._rev, 'updated a doc with put');
          db.get(info.id, {rev: info.rev}, function(err, oldRev) {
            equal(oldRev.version, 'first', 'Fetched old revision');
            start();
          });
        });
      });
    });
  });

  asyncTest('update_seq persists', 2, function() {
    var name = this.name;
    initTestDB(name, function(err, db) {
      db.post({test:"somestuff"}, function (err, info) {
        Pouch(name, function(err, db) {
          db.info(function(err, info) {
            equal(info.update_seq, 1, 'Update seq persisted');
            equal(info.doc_count, 1, 'Doc Count persists');
            start();
          });
        });
      });
    });
  });
});
<commit_msg>Put assert count back in create a pouch test<commit_after>"use strict";

var adapters = ['http-1', 'local-1'];
var qunit = module;

if (typeof module !== undefined && module.exports) {
  this.Pouch = require('../src/pouch.js');
  this.LevelPouch = require('../src/adapters/pouch.leveldb.js');
  this.utils = require('./test.utils.js');

  for (var k in this.utils) {
    global[k] = global[k] || this.utils[k];
  }
  qunit = QUnit.module;
}

adapters.map(function(adapter) {

  qunit("basics: " + adapter, {
    setup: function() {
      this.name = generateAdapterUrl(adapter);
    },
    teardown: function() {
      if (!PERSIST_DATABASES) {
        Pouch.destroy(this.name);
      }
    }
  });

  asyncTest("Create a pouch", 1, function() {
    initTestDB(this.name, function(err, db) {
      ok(!err, 'created a pouch');
      start();
    });
  });

  asyncTest("Create a pouch without DB setup", function() {
    var instantDB;
    Pouch(this.name, function(err, db) {
      ok(!err, 'created a pouch');
      instantDB = Pouch(db.name, {skipSetup: true})
      instantDB.post({test:"abc"}), function(err, info) {
        ok(!err, 'saved document using the skipSetup flag')
        start();
      }
    });
  });

  asyncTest("Remove a pouch", 1, function() {
    var name = this.name;
    initTestDB(name, function(err, db) {
      Pouch.destroy(name, function(err, db) {
        ok(!err);
        start();
      });
    });
  });

  asyncTest("Add a doc", 2, function() {
    initTestDB(this.name, function(err, db) {
      ok(!err, 'opened the pouch');
      db.post({test:"somestuff"}, function (err, info) {
        ok(!err, 'saved a doc with post');
        start();
      });
    });
  });

  asyncTest("Modify a doc", 3, function() {
    console.info('testing: Modify a doc');
    initTestDB(this.name, function(err, db) {
      ok(!err, 'opened the pouch');
      db.post({test: "somestuff"}, function (err, info) {
        ok(!err, 'saved a doc with post');
        db.put({_id: info.id, _rev: info.rev, another: 'test'}, function(err, info2) {
          ok(!err && info2.rev !== info._rev, 'updated a doc with put');
          start();
        });
      });
    });
  });

  asyncTest("Modify a doc with incorrect rev", 3, function() {
    initTestDB(this.name, function(err, db) {
      ok(!err, 'opened the pouch');
      db.post({test: "somestuff"}, function (err, info) {
        ok(!err, 'saved a doc with post');
        var nDoc = {_id: info.id, _rev: info.rev + 'broken', another: 'test'};
        db.put(nDoc, function(err, info2) {
          ok(err, 'put was denied');
          start();
        });
      });
    });
  });

  asyncTest("Get doc", 2, function() {
    initTestDB(this.name, function(err, db) {
      db.post({test:"somestuff"}, function(err, info) {
        db.get(info.id, function(err, doc) {
          ok(doc.test);
          db.get(info.id+'asdf', function(err) {
            ok(err.error);
            start();
          });
        });
      });
    });
  });

  asyncTest("Add a doc with leading underscore in id", function() {
    initTestDB(this.name, function(err, db) {
      db.post({_id: '_testing', value: 42}, function(err, info) {
        ok(err);
        start();
      });
    });
  });

  asyncTest("Get revisions of removed doc", 1, function() {
    initTestDB(this.name, function(err, db) {
      db.post({test:"somestuff"}, function(err, info) {
        var rev = info.rev;
        db.remove({test:"somestuff", _id:info.id, _rev:info.rev}, function(doc) {
          db.get(info.id, {rev: rev}, function(err, doc) {
            ok(!err, 'Recieved deleted doc with rev');
            start();
          });
        });
      });
    });
  });

  asyncTest("Remove doc", 1, function() {
    initTestDB(this.name, function(err, db) {
      db.post({test:"somestuff"}, function(err, info) {
        db.remove({test:"somestuff", _id:info.id, _rev:info.rev}, function(doc) {
          db.get(info.id, function(err) {
            ok(err.error);
            start();
          });
        });
      });
    });
  });

  asyncTest("Remove doc twice with specified id", 4, function() {
    initTestDB(this.name, function(err, db) {
      db.put({_id:"specifiedId", test:"somestuff"}, function(err, info) {
        db.get("specifiedId", function(err, doc) {
          ok(doc.test, "Put and got doc");
          db.remove(doc, function(err, response) {
            ok(!err, "Removed doc");
            db.put({_id:"specifiedId", test:"somestuff2"}, function(err, info) {
              db.get("specifiedId", function(err, doc){
                ok(doc, "Put and got doc again");
                db.remove(doc, function(err, response) {
                  ok(!err, "Removed doc again");
                  start();
                });
              });
            });
          });
        });
      });
    });
  });

  asyncTest("Remove doc, no callback", 2, function() {
    initTestDB(this.name, function(err, db) {
      var changes = db.changes({
        continuous: true,
        include_docs: true,
        onChange: function(change){
          console.log(change);
          if(change.seq == 2){
            ok(change.doc._deleted, 'Doc deleted properly');
            changes.cancel();
            start();
          }
        }
      });
      db.post({_id:"somestuff"}, function (err, res) {
        ok(!err, 'save a doc with post');
        db.remove({_id: res.id, _rev: res.rev});
      });
    });
  });

  asyncTest("Get design doc", 2, function() {
    initTestDB(this.name, function(err, db) {
      db.put({_id: '_design/someid', test:"somestuff"}, function(err, info) {
        db.get(info.id, function(err, doc) {
          ok(doc.test);
          db.get(info.id+'asdf', function(err) {
            ok(err.error);
            start();
          });
        });
      });
    });
  });

  asyncTest("Delete document without id", 1, function () {
    initTestDB(this.name, function(err, db) {
      db.remove({test:'ing'}, function(err) {
        ok(err, 'failed to delete');
        start();
      });
    });
  });

  asyncTest("Bulk docs", 3, function() {
    initTestDB(this.name, function(err, db) {
      ok(!err, 'opened the pouch');
      db.bulkDocs({docs: [{test:"somestuff"}, {test:"another"}]}, function(err, infos) {
        ok(!infos[0].error);
        ok(!infos[1].error);
        start();
      });
    });
  });

  /*
  asyncTest("Sync a doc", 6, function() {
    var couch = generateAdapterUrl('http-2');
    initTestDB(this.name, function(err, db) {
      ok(!err, 'opened the pouch');
      initTestDB(couch, function(err, db2) {
        ok(!err, 'opened the couch');
        db.put({_id:"adoc", test:"somestuff"}, function (err, info) {
          ok(!err, 'saved a doc with post');
          db.replicate.to(couch, function(err, info) {
            ok(!err, 'replicated pouch to couch');
            db.replicate.from(couch, function(err, info) {
              ok(!err, 'replicated couch back to pouch');
              db.get("adoc", {conflicts:true}, function(err, doc) {
                ok(!doc._conflicts, 'doc has no conflicts');
                start();
              });
            });
          });
        });
      })
    });
  });
  */

  asyncTest("Check revisions", 1, function() {
    initTestDB(this.name, function(err, db) {
      db.post({test: "somestuff"}, function (err, info) {
        db.put({_id: info.id, _rev: info.rev, another: 'test'}, function(err, info) {
          db.put({_id: info.id, _rev: info.rev, a: 'change'}, function(err, info2) {
            db.get(info.id, {revs_info:true}, function(err, doc) {
              ok(doc._revs_info.length === 3, 'updated a doc with put');
              start();
            });
          });
        });
      });
    });
  });

  // From here we are copying over tests from CouchDB
  // https://github.com/apache/couchdb/blob/master/share/www/script/test/basics.js
  /*
  asyncTest("Check database with slashes", 1, function() {
    initTestDB('idb://test_suite_db%2Fwith_slashes', function(err, db) {
      ok(!err, 'opened');
      start();
    });
  });
  */

  asyncTest("Basic checks", 8, function() {
    initTestDB(this.name, function(err, db) {
      db.info(function(err, info) {
        var updateSeq = info.update_seq;
        var doc = {_id: '0', a: 1, b:1};
        ok(info.doc_count === 0);
        db.put(doc, function(err, res) {
          ok(res.ok === true);
          ok(res.id);
          ok(res.rev);
          db.info(function(err, info) {
            ok(info.doc_count === 1);
            equal(info.update_seq, updateSeq + 1, 'update seq incremented');
            db.get(doc._id, function(err, doc) {
              ok(doc._id === res.id && doc._rev === res.rev);
              db.get(doc._id, {revs_info: true}, function(err, doc) {
                ok(doc._revs_info[0].status === 'available');
                start();
              });
            });
          });
        });
      });
    });
  });

  asyncTest("Testing Rev format", 2, function() {
    console.info('testing: Rev format');
    var revs = [];
    initTestDB(this.name, function(err, db) {
      db.post({test: "somestuff"}, function (err, info) {
        revs.unshift(info.rev.split('-')[1]);
        db.put({_id: info.id, _rev: info.rev, another: 'test1'}, function(err, info2) {
          revs.unshift(info2.rev.split('-')[1]);
          db.put({_id: info.id, _rev: info2.rev, last: 'test2'}, function(err, info3) {
            revs.unshift(info3.rev.split('-')[1]);
            db.get(info.id, {revs:true}, function(err, doc) {
              ok(doc._revisions.start === 3, 'correct starting position');
              deepEqual(revs, doc._revisions.ids, 'correct revs returned');
              start();
            });
          });
        });
      });
    });
  });

  asyncTest("Testing issue #48", 1, function() {

    var docs = [{"id":"0"}, {"id":"1"}, {"id":"2"}, {"id":"3"}, {"id":"4"}, {"id":"5"}];
    var x = 0;
    var timer;

    initTestDB(this.name, function(err, db) {
      var save = function() {
        db.bulkDocs({docs: docs}, function(err, res) {
          if (++x === 10) {
            ok(true, 'all updated succedded');
            clearInterval(timer);
            start();
          }
        });
      };
      timer = setInterval(save, 50);
    });
  });

  asyncTest("Testing valid id", 1, function() {
    initTestDB(this.name, function(err, db) {
      db.post({'_id': 123, test: "somestuff"}, function (err, info) {
        ok(err, 'id must be a string');
        start();
      });
    });
  });

  asyncTest("Put doc without _id should fail", 1, function() {
    initTestDB(this.name, function(err, db) {
      db.put({test:"somestuff"}, function(err, info) {
        ok(err, '_id is required');
        start();
      });
    });
  });

  asyncTest("Retrieve old revision", function() {
    initTestDB(this.name, function(err, db) {
      ok(!err, 'opened the pouch');
      db.post({version: "first"}, function (err, info) {
        var firstrev = info.rev;
        ok(!err, 'saved a doc with post');
        db.put({_id: info.id, _rev: info.rev, version: 'second'}, function(err, info2) {
          ok(!err && info2.rev !== info._rev, 'updated a doc with put');
          db.get(info.id, {rev: info.rev}, function(err, oldRev) {
            equal(oldRev.version, 'first', 'Fetched old revision');
            start();
          });
        });
      });
    });
  });

  asyncTest('update_seq persists', 2, function() {
    var name = this.name;
    initTestDB(name, function(err, db) {
      db.post({test:"somestuff"}, function (err, info) {
        Pouch(name, function(err, db) {
          db.info(function(err, info) {
            equal(info.update_seq, 1, 'Update seq persisted');
            equal(info.doc_count, 1, 'Doc Count persists');
            start();
          });
        });
      });
    });
  });
});
<|endoftext|>


<commit_before><commit_msg>pass all clients if no defaultAccount used and no aunthentication procedure implemented<commit_after><|endoftext|>