<commit_before>////////////////////////////////////////////////////////////////////////////////
// Tag Entity
////////////////////////////////////////////////////////////////////////////////
/*global require, exports, console */
var	$media = require('../db/media'),
		db = require('../db/db').db,
		entity = require('../db/entity').entity,
		clause = require('../db/entity').clause,

tag = function () {
	var base = Object.create(entity, {kind: {value: 'tags'}}),
			self = Object.create(base);

	// retrieves all tags
	self.getTags = function (handler) {
		var statement = [
			"SELECT",
			"MIN(name) AS name,",
			"CASE WHEN kind IS NOT NULL THEN kind ELSE '' END AS kind,",
			"COUNT(*) AS count",
			"FROM tags",
			"GROUP BY lower(name)",
			"ORDER BY count DESC"
		].join(' ');

		console.log(statement);
		db.query(statement, handler);
	};

	// adds one tag to the file
	self.add = function (after, filter, mediaids, where, handler) {
		var tmp = after.tag.split(':'),
				name = "'" + tmp[0] + "'",
				kind = tmp[1] ? "'" + tmp[1] + "'" : "NULL",

		statement = (typeof after.mediaid !== 'undefined' && after.mediaid !== null ? [
			"INSERT OR IGNORE INTO",
			self.kind,
			"(mediaid, name, kind) VALUES",
			"(" + [after.mediaid, name, kind].join(',') + ")"
		] : [
			"INSERT OR IGNORE INTO",
			self.kind,
			"(mediaid, name, kind)",
			["SELECT mediaid", name, kind].join(","),
			"FROM", self.kind,
			"WHERE 1",
			mediaids ?
				$media.selection(mediaids) :
				filter ?
					$media.filter(filter) :
					"",
			where ?
				"AND " + where :
				""
		]).join(" ");

		if (handler) {
			console.log(statement);
			db.nonQuery(statement, handler);
			return self;
		} else {
			return statement;
		}
	};

	// updates a batch of tags
	self.set = function (before, after, handler) {
		// separating tag name and kind
		before.name = before.tag.split(':')[0];
		delete before.tag;

		var	where =  clause(before, true).join(" AND "),
				tmp = after.tag.split(':'),
				name = "'" + tmp[0] + "'",
				kind = tmp[1] ? "'" + tmp[1] + "'" : "NULL",

		statement = (before.mediaid ? [
			// updaing a single tag
			"BEGIN TRANSACTION",
			// deleting old tag name
			["DELETE FROM", self.kind, "WHERE", where].join(" "),
			// inserting new name
			["INSERT OR IGNORE INTO",
				self.kind, "(mediaid, name, kind)",
				"VALUES",
				"(" + [before.mediaid, name, kind].join(",") + ")"
			].join(" "),
			"COMMIT"
		] : [
			// updaing multiple tags
			"BEGIN TRANSACTION",
			// marking mediaids that contain the old tag name
			"CREATE TEMPORARY TABLE focus (mediaid INTEGER)",
			["INSERT INTO focus (mediaid) SELECT mediaid FROM", self.kind, "WHERE", where].join(" "),
			// deleting entries that already contain both the old and new names
			["DELETE FROM", self.kind, "WHERE mediaid IN focus AND", where].join(" "),
			// inserting new tag names
			["INSERT OR IGNORE INTO",
				self.kind, "(mediaid, name, kind)",
				"SELECT mediaid,",
				name + " AS name,",
				kind + " AS kind",
				"FROM focus"
			].join(" "),
			"DROP TABLE focus",
			"COMMIT"
		]).join(";\n");

		console.log(statement);
		db.nonQuery(statement, handler);
	};

	// removes tag from file(s)
	self.remove = function (before, filter, mediaids, where, handler) {
		var mediaid = before.mediaid,
				tmp = before.tag.split(':'),
				name = tmp[0],
				clause = mediaids ?
					$media.selection(mediaids) :
					filter ?
						$media.filter(filter) :
						"",

		statement = [
			"DELETE FROM", self.kind,
			"WHERE 1",
			clause,
			"AND name = '" + name + "'",
			mediaid ?
				"AND mediaid = " + mediaid :
				"",
			where ?
				"AND " + where :
				""
		].join(" ");

		if (handler) {
			console.log(statement);
			db.nonQuery(statement, handler);
			return self;
		} else {
			return statement;
		}
	};

	// explodes tag
	self.explode = function (before, filter, mediaids, handler) {
		var tmp = before.tag.split(':'),
				where = "name = '" + tmp[0] + "'",
				names = tmp[0].split(' '),
				kind = tmp[1] || '',

		statement = [
			"BEGIN TRANSACTION",
			// adds exploded tags
			(function () {
				var i, name, statement = [];
				for (i = 0; i < names.length; i++) {
					name = names[i];
					if (!name.length) {
						continue;
					}
					statement.push(self.add({
						mediaid: before.mediaid,
						tag: name + ':' + kind
					}, filter, mediaids, where));
				}
				return statement.join(";\n");
			}()),
			// removes old tag
			self.remove(before, filter, mediaids),
			"END TRANSACTION"
		].join(";\n");

		console.log(statement);
		db.nonQuery(statement, handler);
	};

	return self;
}();

exports.tag = tag;

<commit_msg>tag list not sorted by sqlite<commit_after>////////////////////////////////////////////////////////////////////////////////
// Tag Entity
////////////////////////////////////////////////////////////////////////////////
/*global require, exports, console */
var	$media = require('../db/media'),
		db = require('../db/db').db,
		entity = require('../db/entity').entity,
		clause = require('../db/entity').clause,

tag = function () {
	var base = Object.create(entity, {kind: {value: 'tags'}}),
			self = Object.create(base);

	// retrieves all tags
	self.getTags = function (handler) {
		var statement = [
			"SELECT",
			"MIN(name) AS name,",
			"CASE WHEN kind IS NOT NULL THEN kind ELSE '' END AS kind,",
			"COUNT(*) AS count",
			"FROM tags",
			"GROUP BY lower(name)"
		].join(' ');

		console.log(statement);
		db.query(statement, handler);
	};

	// adds one tag to the file
	self.add = function (after, filter, mediaids, where, handler) {
		var tmp = after.tag.split(':'),
				name = "'" + tmp[0] + "'",
				kind = tmp[1] ? "'" + tmp[1] + "'" : "NULL",

		statement = (typeof after.mediaid !== 'undefined' && after.mediaid !== null ? [
			"INSERT OR IGNORE INTO",
			self.kind,
			"(mediaid, name, kind) VALUES",
			"(" + [after.mediaid, name, kind].join(',') + ")"
		] : [
			"INSERT OR IGNORE INTO",
			self.kind,
			"(mediaid, name, kind)",
			["SELECT mediaid", name, kind].join(","),
			"FROM", self.kind,
			"WHERE 1",
			mediaids ?
				$media.selection(mediaids) :
				filter ?
					$media.filter(filter) :
					"",
			where ?
				"AND " + where :
				""
		]).join(" ");

		if (handler) {
			console.log(statement);
			db.nonQuery(statement, handler);
			return self;
		} else {
			return statement;
		}
	};

	// updates a batch of tags
	self.set = function (before, after, handler) {
		// separating tag name and kind
		before.name = before.tag.split(':')[0];
		delete before.tag;

		var	where =  clause(before, true).join(" AND "),
				tmp = after.tag.split(':'),
				name = "'" + tmp[0] + "'",
				kind = tmp[1] ? "'" + tmp[1] + "'" : "NULL",

		statement = (before.mediaid ? [
			// updaing a single tag
			"BEGIN TRANSACTION",
			// deleting old tag name
			["DELETE FROM", self.kind, "WHERE", where].join(" "),
			// inserting new name
			["INSERT OR IGNORE INTO",
				self.kind, "(mediaid, name, kind)",
				"VALUES",
				"(" + [before.mediaid, name, kind].join(",") + ")"
			].join(" "),
			"COMMIT"
		] : [
			// updaing multiple tags
			"BEGIN TRANSACTION",
			// marking mediaids that contain the old tag name
			"CREATE TEMPORARY TABLE focus (mediaid INTEGER)",
			["INSERT INTO focus (mediaid) SELECT mediaid FROM", self.kind, "WHERE", where].join(" "),
			// deleting entries that already contain both the old and new names
			["DELETE FROM", self.kind, "WHERE mediaid IN focus AND", where].join(" "),
			// inserting new tag names
			["INSERT OR IGNORE INTO",
				self.kind, "(mediaid, name, kind)",
				"SELECT mediaid,",
				name + " AS name,",
				kind + " AS kind",
				"FROM focus"
			].join(" "),
			"DROP TABLE focus",
			"COMMIT"
		]).join(";\n");

		console.log(statement);
		db.nonQuery(statement, handler);
	};

	// removes tag from file(s)
	self.remove = function (before, filter, mediaids, where, handler) {
		var mediaid = before.mediaid,
				tmp = before.tag.split(':'),
				name = tmp[0],
				clause = mediaids ?
					$media.selection(mediaids) :
					filter ?
						$media.filter(filter) :
						"",

		statement = [
			"DELETE FROM", self.kind,
			"WHERE 1",
			clause,
			"AND name = '" + name + "'",
			mediaid ?
				"AND mediaid = " + mediaid :
				"",
			where ?
				"AND " + where :
				""
		].join(" ");

		if (handler) {
			console.log(statement);
			db.nonQuery(statement, handler);
			return self;
		} else {
			return statement;
		}
	};

	// explodes tag
	self.explode = function (before, filter, mediaids, handler) {
		var tmp = before.tag.split(':'),
				where = "name = '" + tmp[0] + "'",
				names = tmp[0].split(' '),
				kind = tmp[1] || '',

		statement = [
			"BEGIN TRANSACTION",
			// adds exploded tags
			(function () {
				var i, name, statement = [];
				for (i = 0; i < names.length; i++) {
					name = names[i];
					if (!name.length) {
						continue;
					}
					statement.push(self.add({
						mediaid: before.mediaid,
						tag: name + ':' + kind
					}, filter, mediaids, where));
				}
				return statement.join(";\n");
			}()),
			// removes old tag
			self.remove(before, filter, mediaids),
			"END TRANSACTION"
		].join(";\n");

		console.log(statement);
		db.nonQuery(statement, handler);
	};

	return self;
}();

exports.tag = tag;

<|endoftext|>


<commit_before>'use strict';
/**
 * @file Maps gd$ values to clean keys.
 */

/**
 * Extract the value after the "#".
 * For instance, "http://schemas.google.com/g/2005#mobile" becomes "mobile".
 *
 * @param {String} val Value to extract. It is assumed it contains a hashbang.
 * @return {String} Value after the hashbang.
 */
var postHashbang = function(val) {
  return val.substr(val.indexOf('#') + 1);
};

/**
 * Maps data from google awful soup to "clean" JS.
 *
 * Special value : .url for the contact url, .deleted if the contact has been deleted.
 *
 * @param {Object} c Google object
 * @return {Object} Clean object
 */
module.exports = function (c) {
  var r = {};

  // Extract meaningful data
  r.id = c.id['$t'] || c.id.toString();

  r.modification_date = c.updated['$t'];

  if(c['gd$name']){
    if(c['gd$name']['gd$fullName']) {
      r.name = c['gd$name']['gd$fullName']['$t'];
    }
    if(c['gd$name']['gd$givenName']) {
      r.given_name = c['gd$name']['gd$givenName']['$t'];
    }
    if(c['gd$name']['gd$familyName']) {
      r.family_name = c['gd$name']['gd$familyName']['$t'];
    }
  }

  if (c['gContact$nickname']) {
    r.nickname = c['gContact$nickname']['$t'];
  }

  if (c['gd$email']) {
    r.email = c['gd$email'].map(function(m) {
      return {
        'type': m.rel ? postHashbang(m.rel) : m.label,
        'email': m.address
      };
    });
  }

  if(c['gd$phoneNumber']) {
    r.phone = c['gd$phoneNumber'].map(function(p) {
      return {
        'type': p.rel ? postHashbang(p.rel) : p.label,
        'phone': p['$t'],
      };
    });
  }

  if(c['gd$im']) {
    r.im = c['gd$im'].map(function(p) {
      return {
        'type': p.protocol ? postHashbang(p.protocol) : p.label,
        'address': p.address,
      };
    });
  }

  if(c['gContact$birthday']) {
    r.birthday = new Date(c['gContact$birthday'].when);
  }

  if(c['gContact$website']) {
    r.website = c['gContact$website'].map(function(w) {
      return w.href;
    });
  }

  if(c['gd$organization'] && c['gd$organization'][0]) {
    if(c['gd$organization'][0]['gd$orgTitle']) {
      r.jobTitle = c['gd$organization'][0]['gd$orgTitle']['$t'];
      }

    if(c['gd$organization'][0]['gd$orgName']) {
      r.worksFor = c['gd$organization'][0]['gd$orgName']['$t'];
    }
  }

  if(c['gd$structuredPostalAddress']) {
    r.address = c['gd$structuredPostalAddress'].map(function(a) {
      return {
        'type': a.rel ? postHashbang(a.rel) : a.label,
        'address': a['gd$formattedAddress']['$t']
      };
    });
  }

  if(c['gContact$groupMembershipInfo']) {
    r.groups = c['gContact$groupMembershipInfo'].map(function(g) {
      return g['href'];
    });
  }

  if(c['gd$deleted']) {
    r.deleted = true;
  }

  // Compute additional data
  var re = new RegExp('/([^/]+\\%40[^/]+)/.+/([0-9a-f]+)$');
  var results = re.exec(r.id);
  r.url = 'https://mail.google.com/mail/b/' + results[1].replace('%40', '@') + '/#contact/' + results[2];

  return r;
};
<commit_msg>Fix a bug with undefined value<commit_after>'use strict';
/**
 * @file Maps gd$ values to clean keys.
 */

/**
 * Extract the value after the "#".
 * For instance, "http://schemas.google.com/g/2005#mobile" becomes "mobile".
 *
 * @param {String} val Value to extract. It is assumed it contains a hashbang.
 * @return {String} Value after the hashbang.
 */
var postHashbang = function(val) {
  return val.substr(val.indexOf('#') + 1);
};

/**
 * Maps data from google awful soup to "clean" JS.
 *
 * Special value : .url for the contact url, .deleted if the contact has been deleted.
 *
 * @param {Object} c Google object
 * @return {Object} Clean object
 */
module.exports = function (c) {
  var r = {};

  // Extract meaningful data
  r.id = c.id['$t'] || c.id.toString();

  r.modification_date = c.updated['$t'];

  if(c['gd$name']){
    if(c['gd$name']['gd$fullName']) {
      r.name = c['gd$name']['gd$fullName']['$t'];
    }
    if(c['gd$name']['gd$givenName']) {
      r.given_name = c['gd$name']['gd$givenName']['$t'];
    }
    if(c['gd$name']['gd$familyName']) {
      r.family_name = c['gd$name']['gd$familyName']['$t'];
    }
  }

  if (c['gContact$nickname']) {
    r.nickname = c['gContact$nickname']['$t'];
  }

  if (c['gd$email']) {
    r.email = c['gd$email'].map(function(m) {
      return {
        'type': m.rel ? postHashbang(m.rel) : m.label,
        'email': m.address
      };
    });
  }

  if(c['gd$phoneNumber']) {
    r.phone = c['gd$phoneNumber'].map(function(p) {
      return {
        'type': p.rel ? postHashbang(p.rel) : p.label,
        'phone': p['$t'],
      };
    });
  }

  if(c['gd$im']) {
    r.im = c['gd$im'].map(function(p) {
      return {
        'type': p.protocol ? postHashbang(p.protocol) : p.label,
        'address': p.address,
      };
    });
  }

  if(c['gContact$birthday']) {
    r.birthday = new Date(c['gContact$birthday'].when);
  }

  if(c['gContact$website']) {
    r.website = c['gContact$website'].map(function(w) {
      return w.href;
    });
  }

  if(c['gd$organization'] && c['gd$organization'][0]) {
    if(c['gd$organization'][0]['gd$orgTitle']) {
      r.jobTitle = c['gd$organization'][0]['gd$orgTitle']['$t'];
      }

    if(c['gd$organization'][0]['gd$orgName']) {
      r.worksFor = c['gd$organization'][0]['gd$orgName']['$t'];
    }
  }

  if(c['gd$structuredPostalAddress']) {
    r.address = c['gd$structuredPostalAddress'].map(function(a) {
      return {
        'type': a.rel ? postHashbang(a.rel) : a.label,
        'address': a['gd$formattedAddress'] ? a['gd$formattedAddress']['$t'] : undefined
      };
    });
  }

  if(c['gContact$groupMembershipInfo']) {
    r.groups = c['gContact$groupMembershipInfo'].map(function(g) {
      return g['href'];
    });
  }

  if(c['gd$deleted']) {
    r.deleted = true;
  }

  // Compute additional data
  var re = new RegExp('/([^/]+\\%40[^/]+)/.+/([0-9a-f]+)$');
  var results = re.exec(r.id);
  r.url = 'https://mail.google.com/mail/b/' + results[1].replace('%40', '@') + '/#contact/' + results[2];

  return r;
};
<|endoftext|>


<commit_before>var hoek = require("hoek");

var _extractPrimaryLanguage = function(r){
    var language = {};
    r.i18n.forEach(function(l){
        if(l.Language.IETF.toUpperCase() === r.PrimaryLanguage.toUpperCase()){
            language = l;
        }
    });

    return language;
},

_extractPreferredLanguage = function(r, languages){
    var language = null;

    languages.forEach(function(l){
        r.i18n.forEach(function(rl){
            if(l.code === "*"){
                language = language || l.code;
            }

            if(l.code.toUpperCase() === rl.Language.Code.toUpperCase() &&
                ((l.region === undefined || rl.Language.Region === undefined ) ||
                    l.region.toUpperCase() === rl.Language.Region.toUpperCase())) {
                language = language || rl;
            }
        });
    });

    if(language === "*"){
        return _extractPrimaryLanguage(r);
    }

    return language;
},

_mergeSelectedLanguage = function(destination, language){
  if(!language){
    return destination;
  }
  return hoek.merge(destination, language);
},

_getSelectedLanguage = function(source, languages){

    if(!source.i18n){
        return;
    }

    // expects the language object from the request to be passed in:
    // [
    //   { code: 'en', region: 'US', quality: 1.0 },
    //   { code: 'en',               quality: 0.8 },
    //    ...
    // ]
    if(!languages || languages.length === 0){
        return  _extractPrimaryLanguage(source);
    }
    else {
        languages.sort(function(a, b){
          return b.quality - a.quality;
        });

        return _extractPreferredLanguage(source, languages);
    }
};

module.exports.transform = function(source, languages){
    var selectedLanguageProperties = _getSelectedLanguage(source, languages);
    var result = _mergeSelectedLanguage(source, selectedLanguageProperties);
    delete result.i18n;
    return result;
};

module.exports.transformDestination = function(source, destinationToAddLanguageTo, languages){
    var selectedLanguage = _getSelectedLanguage(source, languages);
    return _mergeSelectedLanguage(destinationToAddLanguageTo, selectedLanguage);
};<commit_msg>transformDestination does not have a return value, it mutates the destination object<commit_after>var hoek = require("hoek");

var _extractPrimaryLanguage = function(r){
    var language = {};
    r.i18n.forEach(function(l){
        if(l.Language.IETF.toUpperCase() === r.PrimaryLanguage.toUpperCase()){
            language = l;
        }
    });

    return language;
},

_extractPreferredLanguage = function(r, languages){
    var language = null;

    languages.forEach(function(l){
        r.i18n.forEach(function(rl){
            if(l.code === "*"){
                language = language || l.code;
            }

            if(l.code.toUpperCase() === rl.Language.Code.toUpperCase() &&
                ((l.region === undefined || rl.Language.Region === undefined ) ||
                    l.region.toUpperCase() === rl.Language.Region.toUpperCase())) {
                language = language || rl;
            }
        });
    });

    if(language === "*"){
        return _extractPrimaryLanguage(r);
    }

    return language;
},

_mergeSelectedLanguage = function(destination, language){
  if(!language){
    return destination;
  }
  return hoek.merge(destination, language);
},

_getSelectedLanguage = function(source, languages){

    if(!source.i18n){
        return;
    }

    // expects the language object from the request to be passed in:
    // [
    //   { code: 'en', region: 'US', quality: 1.0 },
    //   { code: 'en',               quality: 0.8 },
    //    ...
    // ]
    if(!languages || languages.length === 0){
        return  _extractPrimaryLanguage(source);
    }
    else {
        languages.sort(function(a, b){
          return b.quality - a.quality;
        });

        return _extractPreferredLanguage(source, languages);
    }
};

module.exports.transform = function(source, languages){
    var selectedLanguageProperties = _getSelectedLanguage(source, languages);
    var result = _mergeSelectedLanguage(source, selectedLanguageProperties);
    delete result.i18n;
    return result;
};

module.exports.transformDestination = function(source, destinationToAddLanguageTo, languages){
    var selectedLanguage = _getSelectedLanguage(source, languages);
    _mergeSelectedLanguage(destinationToAddLanguageTo, selectedLanguage);
};<|endoftext|>


<commit_before>(function() {

    // when updating dep/i18next.xx.js update version here
    var i18nVersion = '1.7.3';

    var i18n = require('./dep/i18next')//-' + i18nVersion)
      , url = require('url')
      , Cookies = require('cookies')
      , wrapper = {};

    wrapper.version = require('../package.json').version;
    wrapper.clientVersion = i18nVersion;

    if (typeof module !== 'undefined' && module.exports) {
        module.exports = wrapper;
    }

    // detection subfunctions
    function detectLanguageFromUserland(req, res, options) {
        var locale, locales = [];
        locale = options.detectLanguageFn(req, res);
        if (locale) locales.push({lng: cleanLngString(locale, options), q: 1});
        return locales;
    }

    function detectLanguageFromPath(req, res, options) {
        var locale, locales = [];
        var parts = req.originalUrl.split('/');
        if (parts.length > options.detectLngFromPath + 1) {
            var part = parts[options.detectLngFromPath + 1];
            var lookUp = wrapper.pluralExtensions.rules[part.split('-')[0]];
            if (lookUp) locale = part;
        }
        if (locale) locales.push({lng: cleanLngString(locale, options), q: 1});
        return locales;
    }

    function detectLanguageFromQuerystring(req, res, options) {
        var locale, locales = [];
        if (req.query) {
            locale = req.query[options.detectLngQS];
        } else {
            var querystring = url.parse(req.url, true);
            locale = querystring.query[options.detectLngQS];
        }
        if (locale) locales.push({lng: cleanLngString(locale, options), q: 1});
        return locales;
    }

    function detectLanguageFromCookie(req, res, options) {
        var locale, locales = [];
        if (req.cookies) {
            locale = req.cookies[options.cookieName];
        } else {
            var cookies = new Cookies(req, res);
            locale = cookies.get(options.cookieName);
        }
        if (locale) locales.push({lng: cleanLngString(locale, options), q: 1});
        return locales;
    }

    var detectLanguageFromHeader = function(req, res, options) {
        var headers = req.headers;
        var locales = [];

        if (!headers) {
            return locales;
        }

        var acceptLanguage = headers['accept-language'];

        if (acceptLanguage) {
            var lngs = [], i;

            // associate language tags by their 'q' value (between 1 and 0)
            acceptLanguage.split(',').forEach(function(l) {
                var parts = l.split(';'); // 'en-GB;q=0.8' -> ['en-GB', 'q=0.8']

                // get the language tag qvalue: 'q=0.8' -> 0.8
                var qvalue = 1; // default qvalue

                for (i = 0; i < parts.length; i++) {
                    var part = parts[i].split('=');
                    if (part[0] === 'q' && !isNaN(part[1])) {
                        qvalue = Number(part[1]);
                        break;
                    }
                }

                // add the tag and primary subtag to the qvalue associations
                lngs.push({lng: cleanLngString(parts[0], options), q: qvalue});
            });

            lngs.sort(function(a,b) {
                return b.q - a.q;
            });

            for (i = 0; i < lngs.length; i++) {
                locales.push(lngs[i]);
            }
        }

        return locales;
    };

    function checkAgainstSupportedLng(locales, supportedLngs) {
        if (!supportedLngs.length && locales.length) {
            return [locales[0]];
        } else {
            for (var i = 0, len = locales.length; i < len; i++) {
                var locale = locales[i];
                if (supportedLngs.indexOf(locale.lng) > -1) {
                    return [locale];
                }
                if (locale.lng.indexOf('-') > -1) {
                    var unspecific = locale.lng.split('-')[0];
                    if (supportedLngs.indexOf(unspecific) > -1) {
                        locale.lng = unspecific;
                        return [locale];
                    }
                }
            }
        }
        return [];
    }

    function cleanLngString(lng, options) {
        if (typeof lng === 'string' && lng.indexOf('-') > -1) {
            var parts = lng.split('-');

            lng = options.lowerCaseLng ?
                parts[0].toLowerCase() +  '-' + parts[1].toLowerCase() :
                parts[0].toLowerCase() +  '-' + parts[1].toUpperCase();
        }
        return lng;
    }

    // overriding detect language function
    var detectLanguage = function(req, res) {
        var locales = []
          , opts = wrapper.options;

        if (typeof req === 'object') {

            // user-land fn
            if (!locales.length && opts.detectLanguageFn) {
                locales = detectLanguageFromUserland(req, res, opts);
                locales = checkAgainstSupportedLng(locales, opts.supportedLngs);
            }

            // from path
            if (!locales.length && opts.detectLngFromPath !== false) {
                locales = detectLanguageFromPath(req, res, opts);
                locales = checkAgainstSupportedLng(locales, opts.supportedLngs);

                if (!locales.length && opts.forceDetectLngFromPath) {
                    locales = [{lng: opts.fallbackLng[0], q: 1}];
                }
            }

            // from querystring
            if (!locales.length) {
                locales = detectLanguageFromQuerystring(req, res, opts);
                locales = checkAgainstSupportedLng(locales, opts.supportedLngs);
            }

            // from cookie
            if (!locales.length && opts.useCookie) {
                locales = detectLanguageFromCookie(req, res, opts);
                locales = checkAgainstSupportedLng(locales, opts.supportedLngs);
            }

            // from headers
            if (!locales.length && opts.detectLngFromHeaders) {
                locales = detectLanguageFromHeader(req, res, opts);
                locales = checkAgainstSupportedLng(locales, opts.supportedLngs);
            }
        }

        if (!locales.length) locales.push({lng: opts.fallbackLng[0] || 'en', q: 1});

        return locales[0].lng;
    };

    // overriding for the functions in i18next.js
    var f = {

        extend: function(target, source) {
            if (!source || typeof source === 'function') {
                return target;
            }

            for (var attr in source) { target[attr] = source[attr]; }
            return target;
        },

        each: function( object, callback, args ) {
            var name, i = 0,
                length = object.length,
                isObj = length === undefined || typeof object === "function";

            if (args) {
                if (isObj) {
                    for (name in object) {
                        if (callback.apply(object[name], args) === false) {
                            break;
                        }
                    }
                } else {
                    for ( ; i < length; ) {
                        if (callback.apply(object[i++], args) === false) {
                            break;
                        }
                    }
                }

            // A special, fast, case for the most common use of each
            } else {
                if (isObj) {
                    for (name in object) {
                        if (callback.call(object[name], name, object[name]) === false) {
                            break;
                        }
                    }
                } else {
                    for ( ; i < length; ) {
                        if (callback.call(object[i], i, object[i++]) === false) {
                            break;
                        }
                    }
                }
            }

            return object;
        },

        ajax: function() { return null; },

        detectLanguage: detectLanguage,
        detectLanguageFromPath: detectLanguageFromPath,
        detectLanguageFromQuerystring: detectLanguageFromQuerystring,
        detectLanguageFromCookie: detectLanguageFromCookie,
        detectLanguageFromHeader: detectLanguageFromHeader,
        checkAgainstSupportedLng: checkAgainstSupportedLng,

        cookie: {
            create: function() {},
            read: function() {},
            remove: function() {}
        }
    };

    f.extend(i18n.functions, f);

    // override unused sync stuff
    i18n.functions.extend(i18n.sync, {

        configure: function(rstore, options, functions) {
            this.resStore = rstore || {};
            this.functions = functions;
            this.options = options;

            this._fetch = this.fetch;
            this._fetchOne = this.fetchOne;
        },

        load: function(lngs, options, cb) {
            var self = this
              , missingLngs = [];

            for (var i = 0, len = lngs.length; i < len; i++) {
                if (!this.resStore[lngs[i]]) missingLngs.push(lngs[i]);
            }

            if (missingLngs.length > 0) {
                this.fetch(missingLngs, options, function(err, fetched) {
                    if (err) i18n.functions.log(err);

                    self.functions.extend(self.resStore, fetched);
                    cb(err, self.resStore);
                });
            } else {
                cb(null, self.resStore);
            }
        },

        fetch: function(lngs, options, cb) {
            var self = this
              , ns = options.ns
              , store = {};

            var todo = ns.namespaces.length * lngs.length
              , errors;

            if(ns.namespaces.length === 0)
                cb('namespaces not found', null);
            else {
                // load each file individual
                f.each(ns.namespaces, function(nsIndex, nsValue) {
                    f.each(lngs, function(lngIndex, lngValue) {
                        self.fetchOne(lngValue, nsValue, function(err, data) {
                            if(err) {
                                errors = errors || [];
                                errors.push(err);
                            }

                            store[lngValue] = store[lngValue] || {};

                            if(err) {
                                store[lngValue][nsValue] = {};
                            }
                            else {
                                store[lngValue][nsValue] = data;
                            }

                            todo--; // wait for all done befor callback
                            if(todo === 0) {
                                cb(errors, store);
                            }
                        });
                    });
                });
            }
        },

        postMissing: function(lng, ns, key, defaultValue) {
            var self = this
              , lngs = [];

            if (this.options.sendMissingTo === 'fallback' && this.options.fallbackLng !== false) {
                lngs.push(this.options.fallbackLng[0]);
            } else if (this.options.sendMissingTo === 'current' || (this.options.sendMissingTo === 'fallback' && this.options.fallbackLng[0] !== false)) {
                lngs.push(lng);
            } else if (this.options.sendMissingTo === 'all') {
                lngs = i18n.functions.toLanguages(lng);
            }

            f.each(lngs, function(i, l) {
                if (!self.resStore[l] || !self.resStore[l][ns]) {
                    self.functions.log('error saving missingKey `' + key + '` cause namespace `' + ns + '` was not loaded for language `' + l + '`.' );
                } else {
                    self.saveMissing(l, ns, key, defaultValue);
                }
            });

        },

        _loadLocal: function(lngs, cb) {
            cb('not supported');
        },

        _storeLocal: function(store) {
            return;
        }
    });

    f.extend(wrapper, i18n);
    wrapper.detectLanguage = function () {
        // use the real f.detectLanguage() in case .init() overwrote it
        return wrapper.functions.detectLanguage.apply(wrapper, arguments);
    };

})();
<commit_msg>experimental i18n.reload<commit_after>(function() {

    // when updating dep/i18next.xx.js update version here
    var i18nVersion = '1.7.3';

    var i18n = require('./dep/i18next')//-' + i18nVersion)
      , url = require('url')
      , Cookies = require('cookies')
      , wrapper = {};

    wrapper.version = require('../package.json').version;
    wrapper.clientVersion = i18nVersion;

    if (typeof module !== 'undefined' && module.exports) {
        module.exports = wrapper;
    }

    // detection subfunctions
    function detectLanguageFromUserland(req, res, options) {
        var locale, locales = [];
        locale = options.detectLanguageFn(req, res);
        if (locale) locales.push({lng: cleanLngString(locale, options), q: 1});
        return locales;
    }

    function detectLanguageFromPath(req, res, options) {
        var locale, locales = [];
        var parts = req.originalUrl.split('/');
        if (parts.length > options.detectLngFromPath + 1) {
            var part = parts[options.detectLngFromPath + 1];
            var lookUp = wrapper.pluralExtensions.rules[part.split('-')[0]];
            if (lookUp) locale = part;
        }
        if (locale) locales.push({lng: cleanLngString(locale, options), q: 1});
        return locales;
    }

    function detectLanguageFromQuerystring(req, res, options) {
        var locale, locales = [];
        if (req.query) {
            locale = req.query[options.detectLngQS];
        } else {
            var querystring = url.parse(req.url, true);
            locale = querystring.query[options.detectLngQS];
        }
        if (locale) locales.push({lng: cleanLngString(locale, options), q: 1});
        return locales;
    }

    function detectLanguageFromCookie(req, res, options) {
        var locale, locales = [];
        if (req.cookies) {
            locale = req.cookies[options.cookieName];
        } else {
            var cookies = new Cookies(req, res);
            locale = cookies.get(options.cookieName);
        }
        if (locale) locales.push({lng: cleanLngString(locale, options), q: 1});
        return locales;
    }

    var detectLanguageFromHeader = function(req, res, options) {
        var headers = req.headers;
        var locales = [];

        if (!headers) {
            return locales;
        }

        var acceptLanguage = headers['accept-language'];

        if (acceptLanguage) {
            var lngs = [], i;

            // associate language tags by their 'q' value (between 1 and 0)
            acceptLanguage.split(',').forEach(function(l) {
                var parts = l.split(';'); // 'en-GB;q=0.8' -> ['en-GB', 'q=0.8']

                // get the language tag qvalue: 'q=0.8' -> 0.8
                var qvalue = 1; // default qvalue

                for (i = 0; i < parts.length; i++) {
                    var part = parts[i].split('=');
                    if (part[0] === 'q' && !isNaN(part[1])) {
                        qvalue = Number(part[1]);
                        break;
                    }
                }

                // add the tag and primary subtag to the qvalue associations
                lngs.push({lng: cleanLngString(parts[0], options), q: qvalue});
            });

            lngs.sort(function(a,b) {
                return b.q - a.q;
            });

            for (i = 0; i < lngs.length; i++) {
                locales.push(lngs[i]);
            }
        }

        return locales;
    };

    function checkAgainstSupportedLng(locales, supportedLngs) {
        if (!supportedLngs.length && locales.length) {
            return [locales[0]];
        } else {
            for (var i = 0, len = locales.length; i < len; i++) {
                var locale = locales[i];
                if (supportedLngs.indexOf(locale.lng) > -1) {
                    return [locale];
                }
                if (locale.lng.indexOf('-') > -1) {
                    var unspecific = locale.lng.split('-')[0];
                    if (supportedLngs.indexOf(unspecific) > -1) {
                        locale.lng = unspecific;
                        return [locale];
                    }
                }
            }
        }
        return [];
    }

    function cleanLngString(lng, options) {
        if (typeof lng === 'string' && lng.indexOf('-') > -1) {
            var parts = lng.split('-');

            lng = options.lowerCaseLng ?
                parts[0].toLowerCase() +  '-' + parts[1].toLowerCase() :
                parts[0].toLowerCase() +  '-' + parts[1].toUpperCase();
        }
        return lng;
    }

    // overriding detect language function
    var detectLanguage = function(req, res) {
        var locales = []
          , opts = wrapper.options;

        if (typeof req === 'object') {

            // user-land fn
            if (!locales.length && opts.detectLanguageFn) {
                locales = detectLanguageFromUserland(req, res, opts);
                locales = checkAgainstSupportedLng(locales, opts.supportedLngs);
            }

            // from path
            if (!locales.length && opts.detectLngFromPath !== false) {
                locales = detectLanguageFromPath(req, res, opts);
                locales = checkAgainstSupportedLng(locales, opts.supportedLngs);

                if (!locales.length && opts.forceDetectLngFromPath) {
                    locales = [{lng: opts.fallbackLng[0], q: 1}];
                }
            }

            // from querystring
            if (!locales.length) {
                locales = detectLanguageFromQuerystring(req, res, opts);
                locales = checkAgainstSupportedLng(locales, opts.supportedLngs);
            }

            // from cookie
            if (!locales.length && opts.useCookie) {
                locales = detectLanguageFromCookie(req, res, opts);
                locales = checkAgainstSupportedLng(locales, opts.supportedLngs);
            }

            // from headers
            if (!locales.length && opts.detectLngFromHeaders) {
                locales = detectLanguageFromHeader(req, res, opts);
                locales = checkAgainstSupportedLng(locales, opts.supportedLngs);
            }
        }

        if (!locales.length) locales.push({lng: opts.fallbackLng[0] || 'en', q: 1});

        return locales[0].lng;
    };

    // overriding for the functions in i18next.js
    var f = {

        extend: function(target, source) {
            if (!source || typeof source === 'function') {
                return target;
            }

            for (var attr in source) { target[attr] = source[attr]; }
            return target;
        },

        each: function( object, callback, args ) {
            var name, i = 0,
                length = object.length,
                isObj = length === undefined || typeof object === "function";

            if (args) {
                if (isObj) {
                    for (name in object) {
                        if (callback.apply(object[name], args) === false) {
                            break;
                        }
                    }
                } else {
                    for ( ; i < length; ) {
                        if (callback.apply(object[i++], args) === false) {
                            break;
                        }
                    }
                }

            // A special, fast, case for the most common use of each
            } else {
                if (isObj) {
                    for (name in object) {
                        if (callback.call(object[name], name, object[name]) === false) {
                            break;
                        }
                    }
                } else {
                    for ( ; i < length; ) {
                        if (callback.call(object[i], i, object[i++]) === false) {
                            break;
                        }
                    }
                }
            }

            return object;
        },

        ajax: function() { return null; },

        detectLanguage: detectLanguage,
        detectLanguageFromPath: detectLanguageFromPath,
        detectLanguageFromQuerystring: detectLanguageFromQuerystring,
        detectLanguageFromCookie: detectLanguageFromCookie,
        detectLanguageFromHeader: detectLanguageFromHeader,
        checkAgainstSupportedLng: checkAgainstSupportedLng,

        cookie: {
            create: function() {},
            read: function() {},
            remove: function() {}
        },

        reload: function (cb) {
            i18n.configure({}, opts, i18n.functions)
            i18n.setLng(i18n.lng, cb);
        }
    };

    f.extend(i18n.functions, f);

    // override unused sync stuff
    i18n.functions.extend(i18n.sync, {

        configure: function(rstore, options, functions) {
            this.resStore = rstore || {};
            this.functions = functions;
            this.options = options;

            this._fetch = this.fetch;
            this._fetchOne = this.fetchOne;
        },

        load: function(lngs, options, cb) {
            var self = this
              , missingLngs = [];

            for (var i = 0, len = lngs.length; i < len; i++) {
                if (!this.resStore[lngs[i]]) missingLngs.push(lngs[i]);
            }

            if (missingLngs.length > 0) {
                this.fetch(missingLngs, options, function(err, fetched) {
                    if (err) i18n.functions.log(err);

                    self.functions.extend(self.resStore, fetched);
                    cb(err, self.resStore);
                });
            } else {
                cb(null, self.resStore);
            }
        },

        fetch: function(lngs, options, cb) {
            var self = this
              , ns = options.ns
              , store = {};

            var todo = ns.namespaces.length * lngs.length
              , errors;

            if(ns.namespaces.length === 0)
                cb('namespaces not found', null);
            else {
                // load each file individual
                f.each(ns.namespaces, function(nsIndex, nsValue) {
                    f.each(lngs, function(lngIndex, lngValue) {
                        self.fetchOne(lngValue, nsValue, function(err, data) {
                            if(err) {
                                errors = errors || [];
                                errors.push(err);
                            }

                            store[lngValue] = store[lngValue] || {};

                            if(err) {
                                store[lngValue][nsValue] = {};
                            }
                            else {
                                store[lngValue][nsValue] = data;
                            }

                            todo--; // wait for all done befor callback
                            if(todo === 0) {
                                cb(errors, store);
                            }
                        });
                    });
                });
            }
        },

        postMissing: function(lng, ns, key, defaultValue) {
            var self = this
              , lngs = [];

            if (this.options.sendMissingTo === 'fallback' && this.options.fallbackLng !== false) {
                lngs.push(this.options.fallbackLng[0]);
            } else if (this.options.sendMissingTo === 'current' || (this.options.sendMissingTo === 'fallback' && this.options.fallbackLng[0] !== false)) {
                lngs.push(lng);
            } else if (this.options.sendMissingTo === 'all') {
                lngs = i18n.functions.toLanguages(lng);
            }

            f.each(lngs, function(i, l) {
                if (!self.resStore[l] || !self.resStore[l][ns]) {
                    self.functions.log('error saving missingKey `' + key + '` cause namespace `' + ns + '` was not loaded for language `' + l + '`.' );
                } else {
                    self.saveMissing(l, ns, key, defaultValue);
                }
            });

        },

        _loadLocal: function(lngs, cb) {
            cb('not supported');
        },

        _storeLocal: function(store) {
            return;
        }
    });

    f.extend(wrapper, i18n);
    wrapper.detectLanguage = function () {
        // use the real f.detectLanguage() in case .init() overwrote it
        return wrapper.functions.detectLanguage.apply(wrapper, arguments);
    };

})();
<|endoftext|>


<commit_before>var script = document.createElement('script');
script.src = 'js/jquery-3.2.1.min.js';
script.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(script);

var contextMenuItem = {
  "id": "searchWord",
  "title": "Search Word",
  "contexts": ["selection"]
};

chrome.contextMenus.create(contextMenuItem);

function parseDictCCEntry(entry) {
  // given a HTML DOM  object in dict.cc of class 'td7nl',
  // get the innerText of its children attribute tags, and
  // add the words to an array.
  //
  // Return a concatenated string of innerText of
  // within .
  var entryWords = [];
  var entryAttributes = $(entry).find('a').filter('a');

  for (var i=0; i<entryAttributes.length; i++) {
    entryWords.push(entryAttributes[i].innerText);
  }
  return entryWords.join(' ');
}

function parseDictCC(data) {
  // parse the DOM (data) for dictionary entries.
  // Returns an array of [English, German] array entries.

  var text = [];

  // scrape content table
  var content_table =  $($.parseHTML(data)).filter("#maincontent").find('table');

  // scrape definition rows
  var content_rows = $(content_table[2]).find('tbody').find('tr').filter(function() {
    return this.id.match(/tr[0-9]+/)
  });

  console.log("Found " + content_rows.length + " translations.");

  var i, j;
  for (i=0; i<content_rows.length; i++) {
    // first add relevant DOM objects to `text` array
    text.push($(content_rows[i]).find('td').filter(function() {
      //  tags with class 'td7nl' contain dictionary entries
      return this.className.match('td7nl')
    }));

    // now parse the DOM object for innerText,
    // and replace object with string (dictionary entries)
    for (j=0; j<2; j++) {
      // index 0 is the English, entry 1 is the German entry
      text[text.length-1][j] = parseDictCCEntry(text[text.length-1][j]);
      console.log(text[text.length-1]);
    }
  }
  console.log('Finished parsing DictCC.');
  return text;
}

function getData(url, parse) {
  // get the page content using AJAX

  return new Promise((resolve, reject) => {
    $.get(url)
    .done((data) => {
      console.log('Success: got data from ' + url);
      resolve(data); // resolve the promise if data is retrieved
    })
    .fail((xhr, status, err) => {
      console.log('Failed to get data from ' + url);
      reject(status + err.message);
    });
  });
}

function parseLinguee(data) {
  var text = [];
  console.log('Finished parsing Linguee.');
  return text;
}

function displayEntries(url_dictcc, url_linguee, tabId) {

  // have a getData generic function that $.gets the url content,
  // and then if success, apply the callback parseDictCC on the data
  // returned.
  Promise.all([
    getData(url_dictcc),
    getData(url_linguee)
  ])
  .then((datas) => {
    return [parseDictCC(datas[0]), parseLinguee(datas[1])]
  })
  .then((entries) => {
    chrome.tabs.executeScript(
      tabId,
      { file: "js/sidebar.js" },
      chrome.tabs.sendMessage(tabId, entries)
    );
  })
  .catch((error) => {
    console.log('Woops! ' + error.message);
  });

  // $.when(
  //   // wait for AJAX to scrape the two webpages
  //   getDictCcEntries(url_dictcc),
  //   getLingueeEntries(url_linguee)

  // ).then(function(dictcc_entries, linguee_entries) {
  //   // inject sidebar into active tab
  //   // use Chrome message API to pass entry arrays to js/sidebar.js
  //   console.log('Executing script after AJAX');
  //   chrome.tabs.executeScript(tabId, { file: "js/sidebar.js" }, function() {
  //     chrome.tabs.sendMessage(tabId, dictcc_entries);
  //   });
  // });
}

chrome.contextMenus.onClicked.addListener(function(clickData, tab) {
  if (clickData.menuItemId == "searchWord" && clickData.selectionText && tab)
  {
    var query = clickData.selectionText;
    var url_dictcc = "http://www.dict.cc/?s=" + query;
    var url_linguee = "https://www.linguee.com/english-german/search?query=" + query;

    displayEntries(url_dictcc, url_linguee, tab.id);
  }
});

<commit_msg>linguee parsing works<commit_after>var script = document.createElement('script');
script.src = 'js/jquery-3.2.1.min.js';
script.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(script);

var contextMenuItem = {
  "id": "searchWord",
  "title": "Search Word",
  "contexts": ["selection"]
};

chrome.contextMenus.create(contextMenuItem);

function parseDictCCEntry(entry) {
  // given a HTML DOM  object in dict.cc of class 'td7nl',
  // get the innerText of its children attribute tags, and
  // add the words to an array.
  //
  // Return a concatenated string of innerText of
  // within .
  var entryWords = [];
  var entryAttributes = $(entry).find('a').filter('a');

  for (var i=0; i<entryAttributes.length; i++) {
    entryWords.push(entryAttributes[i].innerText);
  }
  return entryWords.join(' ');
}

function parseDictCC(data) {
  // parse the DOM (data) for dictionary entries.
  // Returns an array of [English, German] array entries.

  var text = [];

  // scrape content table
  var content_table =  $($.parseHTML(data)).filter("#maincontent").find('table');

  // scrape definition rows
  var content_rows = $(content_table[2]).find('tbody').find('tr').filter(function() {
    return this.id.match(/tr[0-9]+/)
  });

  console.log("Found " + content_rows.length + " translations.");

  var i, j;
  for (i=0; i<content_rows.length; i++) {
    // first add relevant DOM objects to `text` array
    text.push($(content_rows[i]).find('td').filter(function() {
      //  tags with class 'td7nl' contain dictionary entries
      return this.className.match('td7nl')
    }));

    // now parse the DOM object for innerText,
    // and replace object with string (dictionary entries)
    for (j=0; j<2; j++) {
      // index 0 is the English, entry 1 is the German entry
      text[text.length-1][j] = parseDictCCEntry(text[text.length-1][j]);
      //console.log(text[text.length-1]);
    }
  }
  console.log('Finished parsing DictCC.');
  return text;
}

function getData(url, parse) {
  // get the page content using AJAX

  return new Promise((resolve, reject) => {
    $.get(url)
    .done((data) => {
      console.log('Success: got data from ' + url);
      resolve(data); // resolve the promise if data is retrieved
    })
    .fail((xhr, status, err) => {
      console.log('Failed to get data from ' + url);
      reject(status + err.message);
    });
  });
}

function parseLinguee(data) {
  var text = [];
  // parse html, get translation examples
  var examples = $($.parseHTML(data)).find('div.example.line');
  console.log(examples);

  var german_examples = examples.find('span.tag_s');
  var english_examples = examples.find('span.tag_t');

  var num_examples = german_examples.length;
  for (var i=0; i<num_examples; i++) {
    // add an innerText pair [english, german] to the `text` array
    text.push([english_examples[i].innerText, german_examples[i].innerText]);
  }
  console.log('Finished parsing Linguee.');
  console.log(text);
  return text;
}

function displayEntries(url_dictcc, url_linguee, tabId) {

  // have a getData generic function that $.gets the url content,
  // and then if success, apply the callback parseDictCC on the data
  // returned.
  Promise.all([
    getData(url_dictcc),
    getData(url_linguee)
  ])
  .then((datas) => {
    return [parseDictCC(datas[0]), parseLinguee(datas[1])]
  })
  .then((entries) => {
    chrome.tabs.executeScript(
      tabId,
      { file: "js/sidebar.js" },
      chrome.tabs.sendMessage(tabId, entries)
    );
  })
  .catch((error) => {
    console.log('Woops! ' + error.message);
  });

  // $.when(
  //   // wait for AJAX to scrape the two webpages
  //   getDictCcEntries(url_dictcc),
  //   getLingueeEntries(url_linguee)

  // ).then(function(dictcc_entries, linguee_entries) {
  //   // inject sidebar into active tab
  //   // use Chrome message API to pass entry arrays to js/sidebar.js
  //   console.log('Executing script after AJAX');
  //   chrome.tabs.executeScript(tabId, { file: "js/sidebar.js" }, function() {
  //     chrome.tabs.sendMessage(tabId, dictcc_entries);
  //   });
  // });
}

chrome.contextMenus.onClicked.addListener(function(clickData, tab) {
  if (clickData.menuItemId == "searchWord" && clickData.selectionText && tab)
  {
    var query = clickData.selectionText;
    var url_dictcc = "http://www.dict.cc/?s=" + query;
    var url_linguee = "https://www.linguee.com/english-german/search?query=" + query;

    displayEntries(url_dictcc, url_linguee, tab.id);
  }
});

<|endoftext|>


<commit_before>function createEcho(selectedString, userText, url){
  var echo = new Echo(selectedString, userText, url);
}<commit_msg>updating event page.js<commit_after>chrome.runtime.onMessage.addListener(
  function(echo, sender, sendResponse) {

    var message = JSON.parse(echo);

    var xml = new XMLHttpRequest();
    xml.open("POST", "http://localhost:3000/json", true);
    xml.send(message);

    sendResponse({
      message: "Message: " + echo.message
    });
});
<|endoftext|>


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

exports.MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
                  'Oct', 'Nov', 'Dec'];

exports.setSecure = function(tcpSocket) {
  var pair = tls.createSecurePair(),
      cleartext;

  pair.encrypted.pipe(tcpSocket);
  tcpSocket.pipe(pair.encrypted);
  pair.fd = tcpSocket.fd;

  cleartext = pair.cleartext;
  cleartext.socket = tcpSocket;
  cleartext.encrypted = pair.encrypted;

  function onerror(e) {
    if (cleartext._controlReleased)
      cleartext.emit('error', e);
  }

  function onclose() {
    tcpSocket.removeListener('error', onerror);
    tcpSocket.removeListener('close', onclose);
  }

  tcpSocket.on('error', onerror);
  tcpSocket.on('close', onclose);

  pair.on('secure', function() {
    process.nextTick(function() { cleartext.socket.emit('secure'); });
  });

  cleartext._controlReleased = true;
  return cleartext;
};

/**
 * Adopted from jquery's extend method. Under the terms of MIT License.
 *
 * http://code.jquery.com/jquery-1.4.2.js
 *
 * Modified by Brian White to use Array.isArray instead of the custom isArray
 * method
 */
exports.extend = function() {
  // copy reference to target object
  var target = arguments[0] || {},
      i = 1,
      length = arguments.length,
      deep = false,
      options,
      name,
      src,
      copy;

  // Handle a deep copy situation
  if (typeof target === "boolean") {
    deep = target;
    target = arguments[1] || {};
    // skip the boolean and the target
    i = 2;
  }

  // Handle case when target is a string or something (possible in deep copy)
  if (typeof target !== "object" && !typeof target === 'function')
    target = {};

  var isPlainObject = function(obj) {
    // Must be an Object.
    // Because of IE, we also have to check the presence of the constructor
    // property.
    // Make sure that DOM nodes and window objects don't pass through, as well
    if (!obj || toString.call(obj) !== "[object Object]" || obj.nodeType
        || obj.setInterval)
      return false;

    var has_own_constructor = hasOwnProperty.call(obj, "constructor");
    var has_is_prop_of_method = hasOwnProperty.call(obj.constructor.prototype,
                                                    "isPrototypeOf");
    // Not own constructor property must be Object
    if (obj.constructor && !has_own_constructor && !has_is_prop_of_method)
      return false;

    // Own properties are enumerated firstly, so to speed up,
    // if last one is own, then all properties are own.

    var last_key;
    for (var key in obj)
      last_key = key;

    return last_key === undefined || hasOwnProperty.call(obj, last_key);
  };

  for (; i < length; i++) {
    // Only deal with non-null/undefined values
    if ((options = arguments[i]) !== null) {
      // Extend the base object
      for (name in options) {
        src = target[name];
        copy = options[name];

        // Prevent never-ending loop
        if (target === copy)
            continue;

        // Recurse if we're merging object literal values or arrays
        if (deep && copy && (isPlainObject(copy) || Array.isArray(copy))) {
          var clone = src && (isPlainObject(src) || Array.isArray(src)
                              ? src : (Array.isArray(copy) ? [] : {}));

          // Never move original objects, clone them
          target[name] = exports.extend(deep, clone, copy);

        // Don't bring in undefined values
        } else if (copy !== undefined)
          target[name] = copy;
      }
    }
  }

  // Return the modified object
  return target;
};

exports.bufferAppend = function(buf1, buf2) {
  var newBuf = new Buffer(buf1.length + buf2.length);
  buf1.copy(newBuf, 0, 0);
  if (Buffer.isBuffer(buf2))
    buf2.copy(newBuf, buf1.length, 0);
  else if (Array.isArray(buf2)) {
    for (var i=buf1.length, len=buf2.length; i<len; i++)
      newBuf[i] = buf2[i];
  }

  return newBuf;
};

exports.bufferSplit = function(buf, str) {
  if ((typeof str !== 'string' && !Array.isArray(str))
      || str.length === 0 || str.length > buf.length)
    return [buf];
  var search = !Array.isArray(str)
                ? str.split('').map(function(el) { return el.charCodeAt(0); })
                : str,
      searchLen = search.length,
      ret = [], pos, start = 0;

  while ((pos = exports.bufferIndexOf(buf, search, start)) > -1) {
    ret.push(buf.slice(start, pos));
    start = pos + searchLen;
  }
  if (!ret.length)
    ret = [buf];
  else if (start < buf.length)
    ret.push(buf.slice(start));

  return ret;
};

exports.bufferIndexOf = function(buf, str, start) {
  if (str.length > buf.length)
    return -1;
  var search = !Array.isArray(str)
                ? str.split('').map(function(el) { return el.charCodeAt(0); })
                : str,
      searchLen = search.length,
      ret = -1, i, j, len;
  for (i=start||0,len=buf.length; i<len; ++i) {
    if (buf[i] == search[0] && (len-i) >= searchLen) {
      if (searchLen > 1) {
        for (j=1; j<searchLen; ++j) {
          if (buf[i+j] != search[j])
            break;
          else if (j == searchLen-1) {
            ret = i;
            break;
          }
        }
      } else
        ret = i;
      if (ret > -1)
        break;
    }
  }
  return ret;
};

exports.explode = function(str, delimiter, limit) {
  if (arguments.length < 2 || arguments[0] === undefined
      || arguments[1] === undefined
      || !delimiter || delimiter === '' || typeof delimiter === 'function'
      || typeof delimiter === 'object')
      return false;

  delimiter = (delimiter === true ? '1' : delimiter.toString());

  if (!limit || limit === 0)
    return str.split(delimiter);
  else if (limit < 0)
    return false;
  else if (limit > 0) {
    var splitted = str.split(delimiter);
    var partA = splitted.splice(0, limit - 1);
    var partB = splitted.join(delimiter);
    partA.push(partB);
    return partA;
  }

  return false;
}

exports.isNotEmpty = function(str) {
  return str.trim().length > 0;
}

exports.escape = function(str) {
  return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
}

exports.unescape = function(str) {
  return str.replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}

exports.buildSearchQuery = function(options, extensions, isOrChild) {
  var searchargs = '';
  for (var i=0,len=options.length; i<len; i++) {
    var criteria = (isOrChild ? options : options[i]),
        args = null,
        modifier = (isOrChild ? '' : ' ');
    if (typeof criteria === 'string')
      criteria = criteria.toUpperCase();
    else if (Array.isArray(criteria)) {
      if (criteria.length > 1)
        args = criteria.slice(1);
      if (criteria.length > 0)
        criteria = criteria[0].toUpperCase();
    } else
      throw new Error('Unexpected search option data type. '
                      + 'Expected string or array. Got: ' + typeof criteria);
    if (criteria === 'OR') {
      if (args.length !== 2)
        throw new Error('OR must have exactly two arguments');
      searchargs += ' OR (' + buildSearchQuery(args[0], extensions, true) + ') ('
                    + buildSearchQuery(args[1], extensions, true) + ')'
    } else {
      if (criteria[0] === '!') {
        modifier += 'NOT ';
        criteria = criteria.substr(1);
      }
      switch(criteria) {
        // -- Standard criteria --
        case 'ALL':
        case 'ANSWERED':
        case 'DELETED':
        case 'DRAFT':
        case 'FLAGGED':
        case 'NEW':
        case 'SEEN':
        case 'RECENT':
        case 'OLD':
        case 'UNANSWERED':
        case 'UNDELETED':
        case 'UNDRAFT':
        case 'UNFLAGGED':
        case 'UNSEEN':
          searchargs += modifier + criteria;
        break;
        case 'BCC':
        case 'BODY':
        case 'CC':
        case 'FROM':
        case 'SUBJECT':
        case 'TEXT':
        case 'TO':
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          searchargs += modifier + criteria + ' "' + escape(''+args[0]) + '"';
        break;
        case 'BEFORE':
        case 'ON':
        case 'SENTBEFORE':
        case 'SENTON':
        case 'SENTSINCE':
        case 'SINCE':
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          else if (!(args[0] instanceof Date)) {
            if ((args[0] = new Date(args[0])).toString() === 'Invalid Date')
              throw new Error('Search option argument must be a Date object'
                              + ' or a parseable date string');
          }
          searchargs += modifier + criteria + ' ' + args[0].getDate() + '-'
                        + exports.MONTHS[args[0].getMonth()] + '-'
                        + args[0].getFullYear();
        break;
        case 'KEYWORD':
        case 'UNKEYWORD':
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          searchargs += modifier + criteria + ' ' + args[0];
        break;
        case 'LARGER':
        case 'SMALLER':
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          var num = parseInt(args[0]);
          if (isNaN(num))
            throw new Error('Search option argument must be a number');
          searchargs += modifier + criteria + ' ' + args[0];
        break;
        case 'HEADER':
          if (!args || args.length !== 2)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          searchargs += modifier + criteria + ' "' + escape(''+args[0]) + '" "'
                        + escape(''+args[1]) + '"';
        break;
        case 'UID':
          if (!args)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          exports.validateUIDList(args);
          searchargs += modifier + criteria + ' ' + args.join(',');
        break;
        // -- Extensions criteria --
        case 'X-GM-MSGID': // Gmail unique message ID
        case 'X-GM-THRID': // Gmail thread ID
          if (extensions.indexOf('X-GM-EXT-1') === -1)
            throw new Error('IMAP extension not available: ' + criteria);
          var val;
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          else {
            val = ''+args[0];
            if (!(/^\d+$/.test(args[0])))
              throw new Error('Invalid value');
          }
          searchargs += modifier + criteria + ' ' + val;
        break;
        case 'X-GM-RAW': // Gmail search syntax
          if (extensions.indexOf('X-GM-EXT-1') === -1)
            throw new Error('IMAP extension not available: ' + criteria);
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          searchargs += modifier + criteria + ' "' + escape(''+args[0]) + '"';
        break;
        case 'X-GM-LABELS': // Gmail labels
          if (extensions.indexOf('X-GM-EXT-1') === -1)
            throw new Error('IMAP extension not available: ' + criteria);
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          searchargs += modifier + criteria + ' ' + args[0];
        break;
        default:
          throw new Error('Unexpected search option: ' + criteria);
      }
    }
    if (isOrChild)
      break;
  }
  return searchargs;
}

exports.validateUIDList = function(uids) {
  for (var i=0,len=uids.length,intval; i<len; i++) {
    if (typeof uids[i] === 'string') {
      if (uids[i] === '*' || uids[i] === '*:*') {
        if (len > 1)
          uids = ['*'];
        break;
      } else if (/^(?:[\d]+|\*):(?:[\d]+|\*)$/.test(uids[i]))
        continue;
    }
    intval = parseInt(''+uids[i]);
    if (isNaN(intval)) {
      throw new Error('Message ID/number must be an integer, "*", or a range: '
                      + uids[i]);
    } else if (typeof uids[i] !== 'number')
      uids[i] = intval;
  }
}<commit_msg>Misc performance improvements for utility functions<commit_after>var tls = require('tls');

exports.MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
                  'Oct', 'Nov', 'Dec'];

exports.setSecure = function(tcpSocket) {
  var pair = tls.createSecurePair(),
      cleartext;

  pair.encrypted.pipe(tcpSocket);
  tcpSocket.pipe(pair.encrypted);
  pair.fd = tcpSocket.fd;

  cleartext = pair.cleartext;
  cleartext.socket = tcpSocket;
  cleartext.encrypted = pair.encrypted;

  function onerror(e) {
    if (cleartext._controlReleased)
      cleartext.emit('error', e);
  }

  function onclose() {
    tcpSocket.removeListener('error', onerror);
    tcpSocket.removeListener('close', onclose);
  }

  tcpSocket.on('error', onerror);
  tcpSocket.on('close', onclose);

  pair.on('secure', function() {
    process.nextTick(function() { cleartext.socket.emit('secure'); });
  });

  cleartext._controlReleased = true;
  return cleartext;
};

/**
 * Adopted from jquery's extend method. Under the terms of MIT License.
 *
 * http://code.jquery.com/jquery-1.4.2.js
 *
 * Modified by Brian White to use Array.isArray instead of the custom isArray
 * method
 */
exports.extend = function() {
  // copy reference to target object
  var target = arguments[0] || {},
      i = 1,
      length = arguments.length,
      deep = false,
      options,
      name,
      src,
      copy;

  // Handle a deep copy situation
  if (typeof target === "boolean") {
    deep = target;
    target = arguments[1] || {};
    // skip the boolean and the target
    i = 2;
  }

  // Handle case when target is a string or something (possible in deep copy)
  if (typeof target !== "object" && !typeof target === 'function')
    target = {};

  var isPlainObject = function(obj) {
    // Must be an Object.
    // Because of IE, we also have to check the presence of the constructor
    // property.
    // Make sure that DOM nodes and window objects don't pass through, as well
    if (!obj || toString.call(obj) !== "[object Object]" || obj.nodeType
        || obj.setInterval)
      return false;

    var has_own_constructor = hasOwnProperty.call(obj, "constructor");
    var has_is_prop_of_method = hasOwnProperty.call(obj.constructor.prototype,
                                                    "isPrototypeOf");
    // Not own constructor property must be Object
    if (obj.constructor && !has_own_constructor && !has_is_prop_of_method)
      return false;

    // Own properties are enumerated firstly, so to speed up,
    // if last one is own, then all properties are own.

    var last_key;
    for (var key in obj)
      last_key = key;

    return last_key === undefined || hasOwnProperty.call(obj, last_key);
  };

  for (; i < length; i++) {
    // Only deal with non-null/undefined values
    if ((options = arguments[i]) !== null) {
      // Extend the base object
      for (name in options) {
        src = target[name];
        copy = options[name];

        // Prevent never-ending loop
        if (target === copy)
            continue;

        // Recurse if we're merging object literal values or arrays
        if (deep && copy && (isPlainObject(copy) || Array.isArray(copy))) {
          var clone = src && (isPlainObject(src) || Array.isArray(src)
                              ? src : (Array.isArray(copy) ? [] : {}));

          // Never move original objects, clone them
          target[name] = exports.extend(deep, clone, copy);

        // Don't bring in undefined values
        } else if (copy !== undefined)
          target[name] = copy;
      }
    }
  }

  // Return the modified object
  return target;
};

exports.bufferAppend = function(buf1, buf2) {
  var newBuf = new Buffer(buf1.length + buf2.length);
  buf1.copy(newBuf, 0, 0);
  if (Buffer.isBuffer(buf2))
    buf2.copy(newBuf, buf1.length, 0);
  else if (Array.isArray(buf2)) {
    for (var i=buf1.length, len=buf2.length; i<len; i++)
      newBuf[i] = buf2[i];
  }

  return newBuf;
};

exports.bufferSplit = function(buf, str) {
  if ((typeof str !== 'string' && !Array.isArray(str))
      || str.length === 0 || str.length > buf.length)
    return [buf];
  var search = !Array.isArray(str)
                ? str.split('').map(function(el) { return el.charCodeAt(0); })
                : str,
      searchLen = search.length,
      ret = [], pos, start = 0;

  while ((pos = exports.bufferIndexOf(buf, search, start)) > -1) {
    ret.push(buf.slice(start, pos));
    start = pos + searchLen;
  }
  if (!ret.length)
    ret = [buf];
  else if (start < buf.length)
    ret.push(buf.slice(start));

  return ret;
};

exports.bufferIndexOf = function(buf, str, start) {
  if (str.length > buf.length)
    return -1;
  var search = !Array.isArray(str)
                ? str.split('').map(function(el) { return el.charCodeAt(0); })
                : str,
      searchLen = search.length,
      ret = -1, i, j, len;
  for (i=start||0,len=buf.length; i<len; ++i) {
    if (buf[i] === search[0] && (len-i) >= searchLen) {
      if (searchLen > 1) {
        for (j=1; j<searchLen; ++j) {
          if (buf[i+j] !== search[j])
            break;
          else if (j === searchLen - 1) {
            ret = i;
            break;
          }
        }
      } else
        ret = i;
      if (ret > -1)
        break;
    }
  }
  return ret;
};

exports.explode = function(str, delimiter, limit) {
  if (arguments.length < 2 || arguments[0] === undefined
      || arguments[1] === undefined
      || !delimiter || delimiter === '' || typeof delimiter === 'function'
      || typeof delimiter === 'object')
      return false;

  delimiter = (delimiter === true ? '1' : delimiter.toString());

  if (!limit || limit === 0)
    return str.split(delimiter);
  else if (limit < 0)
    return false;
  else if (limit > 0) {
    var splitted = str.split(delimiter);
    var partA = splitted.splice(0, limit - 1);
    var partB = splitted.join(delimiter);
    partA.push(partB);
    return partA;
  }

  return false;
}

exports.isNotEmpty = function(str) {
  return str.trim().length > 0;
}

exports.escape = function(str) {
  return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
}

exports.unescape = function(str) {
  return str.replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}

exports.buildSearchQuery = function(options, extensions, isOrChild) {
  var searchargs = '';
  for (var i=0,len=options.length; i<len; i++) {
    var criteria = (isOrChild ? options : options[i]),
        args = null,
        modifier = (isOrChild ? '' : ' ');
    if (typeof criteria === 'string')
      criteria = criteria.toUpperCase();
    else if (Array.isArray(criteria)) {
      if (criteria.length > 1)
        args = criteria.slice(1);
      if (criteria.length > 0)
        criteria = criteria[0].toUpperCase();
    } else
      throw new Error('Unexpected search option data type. '
                      + 'Expected string or array. Got: ' + typeof criteria);
    if (criteria === 'OR') {
      if (args.length !== 2)
        throw new Error('OR must have exactly two arguments');
      searchargs += ' OR (';
      searchargs += buildSearchQuery(args[0], extensions, true);
      searchargs += ') (';
      searchargs += buildSearchQuery(args[1], extensions, true);
      searchargs += ')';
    } else {
      if (criteria[0] === '!') {
        modifier += 'NOT ';
        criteria = criteria.substr(1);
      }
      switch(criteria) {
        // -- Standard criteria --
        case 'ALL':
        case 'ANSWERED':
        case 'DELETED':
        case 'DRAFT':
        case 'FLAGGED':
        case 'NEW':
        case 'SEEN':
        case 'RECENT':
        case 'OLD':
        case 'UNANSWERED':
        case 'UNDELETED':
        case 'UNDRAFT':
        case 'UNFLAGGED':
        case 'UNSEEN':
          searchargs += modifier + criteria;
        break;
        case 'BCC':
        case 'BODY':
        case 'CC':
        case 'FROM':
        case 'SUBJECT':
        case 'TEXT':
        case 'TO':
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          searchargs += modifier + criteria + ' "' + escape(''+args[0]) + '"';
        break;
        case 'BEFORE':
        case 'ON':
        case 'SENTBEFORE':
        case 'SENTON':
        case 'SENTSINCE':
        case 'SINCE':
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          else if (!(args[0] instanceof Date)) {
            if ((args[0] = new Date(args[0])).toString() === 'Invalid Date')
              throw new Error('Search option argument must be a Date object'
                              + ' or a parseable date string');
          }
          searchargs += modifier + criteria + ' ' + args[0].getDate() + '-'
                        + exports.MONTHS[args[0].getMonth()] + '-'
                        + args[0].getFullYear();
        break;
        case 'KEYWORD':
        case 'UNKEYWORD':
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          searchargs += modifier + criteria + ' ' + args[0];
        break;
        case 'LARGER':
        case 'SMALLER':
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          var num = parseInt(args[0]);
          if (isNaN(num))
            throw new Error('Search option argument must be a number');
          searchargs += modifier + criteria + ' ' + args[0];
        break;
        case 'HEADER':
          if (!args || args.length !== 2)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          searchargs += modifier + criteria + ' "' + escape(''+args[0]) + '" "'
                        + escape(''+args[1]) + '"';
        break;
        case 'UID':
          if (!args)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          exports.validateUIDList(args);
          searchargs += modifier + criteria + ' ' + args.join(',');
        break;
        // -- Extensions criteria --
        case 'X-GM-MSGID': // Gmail unique message ID
        case 'X-GM-THRID': // Gmail thread ID
          if (extensions.indexOf('X-GM-EXT-1') === -1)
            throw new Error('IMAP extension not available: ' + criteria);
          var val;
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          else {
            val = ''+args[0];
            if (!(/^\d+$/.test(args[0])))
              throw new Error('Invalid value');
          }
          searchargs += modifier + criteria + ' ' + val;
        break;
        case 'X-GM-RAW': // Gmail search syntax
          if (extensions.indexOf('X-GM-EXT-1') === -1)
            throw new Error('IMAP extension not available: ' + criteria);
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          searchargs += modifier + criteria + ' "' + escape(''+args[0]) + '"';
        break;
        case 'X-GM-LABELS': // Gmail labels
          if (extensions.indexOf('X-GM-EXT-1') === -1)
            throw new Error('IMAP extension not available: ' + criteria);
          if (!args || args.length !== 1)
            throw new Error('Incorrect number of arguments for search option: '
                            + criteria);
          searchargs += modifier + criteria + ' ' + args[0];
        break;
        default:
          throw new Error('Unexpected search option: ' + criteria);
      }
    }
    if (isOrChild)
      break;
  }
  return searchargs;
}

exports.validateUIDList = function(uids) {
  for (var i=0,len=uids.length,intval; i<len; i++) {
    if (typeof uids[i] === 'string') {
      if (uids[i] === '*' || uids[i] === '*:*') {
        if (len > 1)
          uids = ['*'];
        break;
      } else if (/^(?:[\d]+|\*):(?:[\d]+|\*)$/.test(uids[i]))
        continue;
    }
    intval = parseInt(''+uids[i]);
    if (isNaN(intval)) {
      throw new Error('Message ID/number must be an integer, "*", or a range: '
                      + uids[i]);
    } else if (typeof uids[i] !== 'number')
      uids[i] = intval;
  }
}<|endoftext|>


<commit_before>var qs = require('querystring');
var iconv = require('iconv-lite');
var vm = require('vm');
var util = require('../util');
var extend = require('util')._extend;
var Transform = require('pipestream').Transform;
var WhistleTransform = util.WhistleTransform;
var ReplacePatternTransform = util.ReplacePatternTransform;
var ReplaceStringTransform = util.ReplaceStringTransform;
var FileWriterTransform = util.FileWriterTransform;
var MAX_REQ_SIZE = 256 * 1024;
var REQ_TYPE = {
		urlencoded: 'application/x-www-form-urlencoded',
		form: 'application/x-www-form-urlencoded',
		json: 'application/json',
		xml: 'text/xml',
		text: 'text/plain',
		upload: 'multipart/form-data',
		multipart: 'multipart/form-data',
		defaultType: 'application/octet-stream'
};

/**
 * 处理请求数据
 *
 * @param req：method、body、headers，top，bottom，speed、delay，charset,timeout
 * @param data
 */
function handleReq(req, data) {
	req.method = data.method || req.method;
	req.timeout = parseInt(data.timeout, 10);
	extend(req.headers, data.headers);
	var method = req.method = typeof req.method != 'string' ? 'GET' : req.method.toUpperCase();
	if (!util.hasRequestBody(method)) {
		delete data.top;
		delete data.bottom;
		delete data.body;
	} else if (data.top || data.bottom || data.body) {
		delete req.headers['content-length'];
	}

	!util.isEmptyObject(data) && req.add(new WhistleTransform(data));
}

function handleCors(data, cors) {
	if (!cors) {
		return;
	}

	if (cors.origin !== undefined) {
		util.setHeader(data, 'origin', cors.origin);
	}

	if (cors.method !== undefined) {
		util.setHeader(data, 'access-control-request-method', cors.method);
	}

	if (cors.headers !== undefined) {
		util.setHeader(data, 'access-control-request-headers', cors.headers);
	}
}

function handleAuth(data, auth) {
	if (!auth) {
		return;
	}

	var basic = [];
	auth.username != null && basic.push(auth.username);
	auth.password != null && basic.push(auth.password);
	if (basic = basic.join(':')) {
		util.setHeader(data, 'authorization', 'Basic ' + new Buffer(basic).toString('base64'));
	}
}

function handleCookies(data, cookies, curCookies) {
	var list = cookies && Object.keys(cookies);
	if (list && list.length) {
		var result = {};
		if (curCookies && typeof curCookies == 'string') {
			curCookies.split(/;\s*/g)
					.forEach(function(cookie) {
						var index = cookie.indexOf('=');
						if (index == -1) {
							result[cookie] = null;
						} else {
							result[cookie.substring(0, index)] = cookie.substring(index + 1);
						}
					});
		}

		list.forEach(function(name) {
			var value = cookies[name];
			value = value && typeof value == 'object' ? value.value : value;
			result[util.encodeURIComponent(name)] = value ? util.encodeURIComponent(value) : value;
		});

		cookies = Object.keys(result).map(function(name) {
			var value = result[name];
			return name + (value == null ? '' : '=' + value);
		}).join('; ');
		util.setHeader(data, 'cookie', cookies);
	}
}

function handleParams(req, params) {
	if (!(params = params && qs.stringify(params))) {
		return;
	}

	if (!req.method || /^GET$/i.test(req.method)) {
		req.options.search = util.replaceUrlQueryString(req.options.search, params);
		req.options.path = util.replaceUrlQueryString(req.options.path, params);
		req.options.href = util.replaceUrlQueryString(req.options.href, params);
	} else if (util.isUrlEncoded(req)) {
		delete req.headers['content-length'];
		var transform = new Transform();
		var buffer, interrupt;
		transform._transform = function(chunk, encoding, callback) {
			if (chunk) {
				if (!interrupt) {
					buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
					chunk = null;
					if (buffer.length > MAX_REQ_SIZE) {
						interrupt = true;
						chunk = buffer;
						buffer = null;
					}
				}
			} else if (buffer) {
				var body = buffer + '';
				var isGBK = body.indexOf('�') == -1;
				if (isGBK) {
					body = iconv.decode(buffer, 'GB18030');
					body = util.replaceQueryString(body, params);
					chunk = iconv.encode(body, 'GB18030');
				} else {
					chunk = new Buffer(body);
				}
				buffer = null;
			} else {
				chunk = new Buffer(params);
			}

			callback(null, chunk);
		};
		req.add(transform);
	} else if (/^multipart\/(?:form-data|related)(?:;|$)/i.test(type)) {
		delete req.headers['content-length'];
	}
}

function removeDisableProps(req) {
	var disable = req.disable;
	var headers = req.headers;

	if (disable.ua) {
		delete headers['user-agent'];
	}

	if (disable.cookie || disable.cookies ||
			disable.reqCookie || disable.reqCookies) {
		delete headers.cookie;
	}

	if (disable.referer) {
		delete headers.referer;
	}

	if (disable.cache) {
		delete headers['if-modified-since'];
		delete headers['if-none-match'];
		delete headers['last-modified'];
		delete headers.etag;

		headers['pragma'] = 'no-cache';
		headers['cache-control'] = 'no-cache';
	}
}

function handleReplace(req, replacement) {
	if (!replacement) {
		return;
	}

	var type = req.headers['content-type'];
	type = util.isUrlEncoded(req) ? 'FORM' : util.getContentType(type);
	if (!type || type == 'IMG') {
		return;
	}

	Object.keys(replacement).forEach(function(pattern) {
		var value = replacement[pattern];
		if (util.isOriginalRegExp(pattern) && (pattern = util.toOriginalRegExp(pattern))) {
			req.addTextTransform(new ReplacePatternTransform(pattern, value));
		} else if (pattern) {
			req.addTextTransform(new ReplaceStringTransform(pattern, value));
		}
	});
}

module.exports = function(req, res, next) {
	var reqRules = req.rules;
	util.parseRuleJson([reqRules.req, reqRules.reqHeaders, reqRules.reqCookies,
	                    reqRules.auth, reqRules.params, reqRules.reqCors, reqRules.reqReplace],
			function(data, headers, cookies, auth, params, cors, replacement) {
		if (reqRules.head && reqRules.head.req) {
			data = extend(reqRules.head.req, data);
		}
		data = data || {};
		if (headers) {
			data.headers =  extend(data.headers || {}, headers);
		}

		if (data.headers) {
			data.headers = util.lowerCaseify(data.headers);
		}

		var method = util.getMatcherValue(reqRules.method);
		if (method) {
			data.method = method;
		}

		var type = util.getMatcherValue(reqRules.reqType);
		if (type) {
			type = type.indexOf('/') != -1 ? type : (REQ_TYPE[type] || REQ_TYPE.defaultType);
			util.setHeader(data, 'content-type', type);
		}

		var referer = util.getMatcherValue(reqRules.referer);
		if (referer) {
			util.setHeader(data, 'referer', referer);
		}

		var ua = util.getMatcherValue(reqRules.ua);
		if (ua) {
			util.setHeader(data, 'user-agent', ua);
		}

		var reqDelay = util.getMatcherValue(reqRules.reqDelay);
		reqDelay = reqDelay && parseInt(reqDelay, 10);
		if (reqDelay > 0) {
			data.delay = reqDelay;
		}

		var reqSpeed = util.getMatcherValue(reqRules.reqSpeed);
		reqSpeed = reqSpeed && parseFloat(reqSpeed);
		if (reqSpeed > 0) {
			data.speed = reqSpeed;
		}

		handleCookies(data, cookies, req.headers.cookie);
		handleAuth(data, auth);
		handleCors(data, cors);

		util.readInjectFiles(data, function(data) {
			util.getRuleValue([reqRules.reqBody, reqRules.reqPrepend, reqRules.reqAppend],
				function(reqBody, reqPrepend, reqAppend) {
					if (reqBody) {
						data.body = reqBody;
					}

					if (reqPrepend) {
						data.top = reqPrepend;
					}

					if (reqAppend) {
						data.append = reqAppend;
					}

					handleReq(req, data);
					handleParams(req, params);
					util.removeUnsupportsHeaders(req.headers);
					removeDisableProps(req);
					handleReplace(req, replacement);
					util.getFileWriter(util.hasRequestBody(req) ? util.getRuleFile(reqRules.reqWrite) : null, function(writer) {
						if (writer) {
							req.add(new FileWriterTransform(writer, req));
						}
						next();
					});
				});
		});
	});
};<commit_msg>upload<commit_after>var qs = require('querystring');
var iconv = require('iconv-lite');
var vm = require('vm');
var util = require('../util');
var extend = require('util')._extend;
var Transform = require('pipestream').Transform;
var WhistleTransform = util.WhistleTransform;
var ReplacePatternTransform = util.ReplacePatternTransform;
var ReplaceStringTransform = util.ReplaceStringTransform;
var FileWriterTransform = util.FileWriterTransform;
var MAX_REQ_SIZE = 256 * 1024;
var REQ_TYPE = {
		urlencoded: 'application/x-www-form-urlencoded',
		form: 'application/x-www-form-urlencoded',
		json: 'application/json',
		xml: 'text/xml',
		text: 'text/plain',
		upload: 'multipart/form-data',
		multipart: 'multipart/form-data',
		defaultType: 'application/octet-stream'
};

/**
 * 处理请求数据
 *
 * @param req：method、body、headers，top，bottom，speed、delay，charset,timeout
 * @param data
 */
function handleReq(req, data) {
	req.method = data.method || req.method;
	req.timeout = parseInt(data.timeout, 10);
	extend(req.headers, data.headers);
	var method = req.method = typeof req.method != 'string' ? 'GET' : req.method.toUpperCase();
	if (!util.hasRequestBody(method)) {
		delete data.top;
		delete data.bottom;
		delete data.body;
	} else if (data.top || data.bottom || data.body) {
		delete req.headers['content-length'];
	}

	!util.isEmptyObject(data) && req.add(new WhistleTransform(data));
}

function handleCors(data, cors) {
	if (!cors) {
		return;
	}

	if (cors.origin !== undefined) {
		util.setHeader(data, 'origin', cors.origin);
	}

	if (cors.method !== undefined) {
		util.setHeader(data, 'access-control-request-method', cors.method);
	}

	if (cors.headers !== undefined) {
		util.setHeader(data, 'access-control-request-headers', cors.headers);
	}
}

function handleAuth(data, auth) {
	if (!auth) {
		return;
	}

	var basic = [];
	auth.username != null && basic.push(auth.username);
	auth.password != null && basic.push(auth.password);
	if (basic = basic.join(':')) {
		util.setHeader(data, 'authorization', 'Basic ' + new Buffer(basic).toString('base64'));
	}
}

function handleCookies(data, cookies, curCookies) {
	var list = cookies && Object.keys(cookies);
	if (list && list.length) {
		var result = {};
		if (curCookies && typeof curCookies == 'string') {
			curCookies.split(/;\s*/g)
					.forEach(function(cookie) {
						var index = cookie.indexOf('=');
						if (index == -1) {
							result[cookie] = null;
						} else {
							result[cookie.substring(0, index)] = cookie.substring(index + 1);
						}
					});
		}

		list.forEach(function(name) {
			var value = cookies[name];
			value = value && typeof value == 'object' ? value.value : value;
			result[util.encodeURIComponent(name)] = value ? util.encodeURIComponent(value) : value;
		});

		cookies = Object.keys(result).map(function(name) {
			var value = result[name];
			return name + (value == null ? '' : '=' + value);
		}).join('; ');
		util.setHeader(data, 'cookie', cookies);
	}
}

function handleParams(req, params) {
	if (!(params = params && qs.stringify(params))) {
		return;
	}

	if (!req.method || /^GET$/i.test(req.method)) {
		req.options.search = util.replaceUrlQueryString(req.options.search, params);
		req.options.path = util.replaceUrlQueryString(req.options.path, params);
		req.options.href = util.replaceUrlQueryString(req.options.href, params);
	} else if (util.isUrlEncoded(req)) {
		delete req.headers['content-length'];
		var transform = new Transform();
		var buffer, interrupt;
		transform._transform = function(chunk, encoding, callback) {
			if (chunk) {
				if (!interrupt) {
					buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
					chunk = null;
					if (buffer.length > MAX_REQ_SIZE) {
						interrupt = true;
						chunk = buffer;
						buffer = null;
					}
				}
			} else if (buffer) {
				var body = buffer + '';
				var isGBK = body.indexOf('�') == -1;
				if (isGBK) {
					body = iconv.decode(buffer, 'GB18030');
					body = util.replaceQueryString(body, params);
					chunk = iconv.encode(body, 'GB18030');
				} else {
					chunk = new Buffer(body);
				}
				buffer = null;
			} else {
				chunk = new Buffer(params);
			}

			callback(null, chunk);
		};
		req.add(transform);
	} else if (util.isUploadForm(req)) {
		delete req.headers['content-length'];
		//TODO:
	}
}

function removeDisableProps(req) {
	var disable = req.disable;
	var headers = req.headers;

	if (disable.ua) {
		delete headers['user-agent'];
	}

	if (disable.cookie || disable.cookies ||
			disable.reqCookie || disable.reqCookies) {
		delete headers.cookie;
	}

	if (disable.referer) {
		delete headers.referer;
	}

	if (disable.cache) {
		delete headers['if-modified-since'];
		delete headers['if-none-match'];
		delete headers['last-modified'];
		delete headers.etag;

		headers['pragma'] = 'no-cache';
		headers['cache-control'] = 'no-cache';
	}
}

function handleReplace(req, replacement) {
	if (!replacement) {
		return;
	}

	var type = req.headers['content-type'];
	type = util.isUrlEncoded(req) ? 'FORM' : util.getContentType(type);
	if (!type || type == 'IMG') {
		return;
	}

	Object.keys(replacement).forEach(function(pattern) {
		var value = replacement[pattern];
		if (util.isOriginalRegExp(pattern) && (pattern = util.toOriginalRegExp(pattern))) {
			req.addTextTransform(new ReplacePatternTransform(pattern, value));
		} else if (pattern) {
			req.addTextTransform(new ReplaceStringTransform(pattern, value));
		}
	});
}

module.exports = function(req, res, next) {
	var reqRules = req.rules;
	util.parseRuleJson([reqRules.req, reqRules.reqHeaders, reqRules.reqCookies,
	                    reqRules.auth, reqRules.params, reqRules.reqCors, reqRules.reqReplace],
			function(data, headers, cookies, auth, params, cors, replacement) {
		if (reqRules.head && reqRules.head.req) {
			data = extend(reqRules.head.req, data);
		}
		data = data || {};
		if (headers) {
			data.headers =  extend(data.headers || {}, headers);
		}

		if (data.headers) {
			data.headers = util.lowerCaseify(data.headers);
		}

		var method = util.getMatcherValue(reqRules.method);
		if (method) {
			data.method = method;
		}

		var type = util.getMatcherValue(reqRules.reqType);
		if (type) {
			type = type.indexOf('/') != -1 ? type : (REQ_TYPE[type] || REQ_TYPE.defaultType);
			util.setHeader(data, 'content-type', type);
		}

		var referer = util.getMatcherValue(reqRules.referer);
		if (referer) {
			util.setHeader(data, 'referer', referer);
		}

		var ua = util.getMatcherValue(reqRules.ua);
		if (ua) {
			util.setHeader(data, 'user-agent', ua);
		}

		var reqDelay = util.getMatcherValue(reqRules.reqDelay);
		reqDelay = reqDelay && parseInt(reqDelay, 10);
		if (reqDelay > 0) {
			data.delay = reqDelay;
		}

		var reqSpeed = util.getMatcherValue(reqRules.reqSpeed);
		reqSpeed = reqSpeed && parseFloat(reqSpeed);
		if (reqSpeed > 0) {
			data.speed = reqSpeed;
		}

		handleCookies(data, cookies, req.headers.cookie);
		handleAuth(data, auth);
		handleCors(data, cors);

		util.readInjectFiles(data, function(data) {
			util.getRuleValue([reqRules.reqBody, reqRules.reqPrepend, reqRules.reqAppend],
				function(reqBody, reqPrepend, reqAppend) {
					if (reqBody) {
						data.body = reqBody;
					}

					if (reqPrepend) {
						data.top = reqPrepend;
					}

					if (reqAppend) {
						data.append = reqAppend;
					}

					handleReq(req, data);
					handleParams(req, params);
					util.removeUnsupportsHeaders(req.headers);
					removeDisableProps(req);
					handleReplace(req, replacement);
					util.getFileWriter(util.hasRequestBody(req) ? util.getRuleFile(reqRules.reqWrite) : null, function(writer) {
						if (writer) {
							req.add(new FileWriterTransform(writer, req));
						}
						next();
					});
				});
		});
	});
};<|endoftext|>