<commit_before>'use strict';

// MODULES //

var isFunction = require( 'validate.io-function' );
var merge = require( 'utils-merge2' )();
var validate = require( './validate.js' );
var defaults = require( './defaults.json' );
var resolve = require( './resolve.js' );

// VARIABLES //

var DEFAULT_HTTP_PORT = 80;
var DEFAULT_HTTPS_PORT = 443;

// FACTORY //

/**
* FUNCTION: factory( options, clbk )
*	Returns a function for fetching resources from a Github API endpoint.
*
* @param {Object} options - function options
* @param {String} [options.protocol='https'] - request protocol
* @param {String} [options.hostname='api.github.com'] - endpoint hostname
* @param {Number} [options.port] - endpoint port
* @param {String} [options.pathname='/'] - resource pathname
* @param {Number} [options.page=1] - resource page
* @param {Number} [options.last_page=1] - last resource page
* @param {Number} [options.per_page=100] - page size
* @param {String} [options.token] - Github access token
* @param {String} [options.accept='application/vnd.github.moondragon+json'] - media type
* @param {String} [options.useragent] - user agent string
* @param {Function} clbk - callback to invoke upon query completion
* @returns {Function} function for getting download counts
*/
function factory( options, clbk ) {
	var opts;
	var err;
	opts = merge( {}, defaults );
	err = validate( opts, options );
	if ( err ) {
		throw err;
	}
	if ( opts.port === null ) {
		if ( opts.protocol === 'https' ) {
			opts.port = DEFAULT_HTTPS_PORT;
		} else {
			opts.port = DEFAULT_HTTP_PORT;
		}
	}
	if ( !isFunction( clbk ) ) {
		throw new TypeError( 'invalid input argument. Callback argument must be a function. Value: `' + clbk + '`.' );
	}
	/**
	* FUNCTION: get()
	*	Resolves endpoint resources.
	*
	* @returns {Void}
	*/
	return function get() {
		resolve( opts, done );
		/**
		* FUNCTION: done( error, data, info )
		*	Callback invoked after resolving resources.
		*
		* @private
		* @param {Error|Null} error - error object
		* @param {Object[]} data - query data
		* @param {Object} info - response info
		* @returns {Void}
		*/
		function done( error, data, info ) {
			error = error || null;
			data = data || null;
			info = info || null;
			clbk( error, data, info );
		} // end FUNCTION done()
	}; // end FUNCTION get()
} // end FUNCTION factory()

// EXPORTS //

module.exports = factory;
<commit_msg>Fix return desc<commit_after>'use strict';

// MODULES //

var isFunction = require( 'validate.io-function' );
var merge = require( 'utils-merge2' )();
var validate = require( './validate.js' );
var defaults = require( './defaults.json' );
var resolve = require( './resolve.js' );

// VARIABLES //

var DEFAULT_HTTP_PORT = 80;
var DEFAULT_HTTPS_PORT = 443;

// FACTORY //

/**
* FUNCTION: factory( options, clbk )
*	Returns a function for fetching resources from a Github API endpoint.
*
* @param {Object} options - function options
* @param {String} [options.protocol='https'] - request protocol
* @param {String} [options.hostname='api.github.com'] - endpoint hostname
* @param {Number} [options.port] - endpoint port
* @param {String} [options.pathname='/'] - resource pathname
* @param {Number} [options.page=1] - resource page
* @param {Number} [options.last_page=1] - last resource page
* @param {Number} [options.per_page=100] - page size
* @param {String} [options.token] - Github access token
* @param {String} [options.accept='application/vnd.github.moondragon+json'] - media type
* @param {String} [options.useragent] - user agent string
* @param {Function} clbk - callback to invoke upon query completion
* @returns {Function} function for getting resources
*/
function factory( options, clbk ) {
	var opts;
	var err;
	opts = merge( {}, defaults );
	err = validate( opts, options );
	if ( err ) {
		throw err;
	}
	if ( opts.port === null ) {
		if ( opts.protocol === 'https' ) {
			opts.port = DEFAULT_HTTPS_PORT;
		} else {
			opts.port = DEFAULT_HTTP_PORT;
		}
	}
	if ( !isFunction( clbk ) ) {
		throw new TypeError( 'invalid input argument. Callback argument must be a function. Value: `' + clbk + '`.' );
	}
	/**
	* FUNCTION: get()
	*	Resolves endpoint resources.
	*
	* @returns {Void}
	*/
	return function get() {
		resolve( opts, done );
		/**
		* FUNCTION: done( error, data, info )
		*	Callback invoked after resolving resources.
		*
		* @private
		* @param {Error|Null} error - error object
		* @param {Object[]} data - query data
		* @param {Object} info - response info
		* @returns {Void}
		*/
		function done( error, data, info ) {
			error = error || null;
			data = data || null;
			info = info || null;
			clbk( error, data, info );
		} // end FUNCTION done()
	}; // end FUNCTION get()
} // end FUNCTION factory()

// EXPORTS //

module.exports = factory;
<|endoftext|>


<commit_before>var _ = require('lodash')

var Folding = function Folding(db, ns) {
  this.db = db
  this.ns = ns + '-'
  this.cache = {}
  this.startWith = {}
  this.indexWith = function(event) { return event['$id'] || 1 }
  this.howToFoldEvents = function(s, e) { return null }
}

Folding.prototype.put = function(key, value) {
  var cache = this.cache

  cache[key] = value
  this.db.put(this.ns + key, value, function(err) {
    if (cache[key] === value) {
      delete cache[key]
    }
  })
}

Folding.prototype.get = function(key, callback) {
  var cache = this.cache

  if (cache[key]) {
    return callback(null, cache[key])
  }
  this.db.get(this.ns + key, function(err, value) {
    if (err) {
      return callback(err, value)
    }
    if (cache[key] && (cache[key] !== value)) {
      return callback(null, cache[key])
    }
    return callback(null, value)
  })
}

Folding.prototype.fold = function(event, lw) {
  var self = this, index = self.indexWith(event)
  if (index) {
    self.get(index, function(err, previousFoldingStatus) {
      if (err) {
        previousFoldingStatus = self.startWith
      }
      var nextFoldingStatus =
        self.howToFoldEvents['$any'](
          _.clone(previousFoldingStatus), event, index, lw
        )
      if (nextFoldingStatus && (JSON.stringify(nextFoldingStatus) !== JSON.stringify(previousFoldingStatus))) {
        self.put(index, nextFoldingStatus)
      }
    })
  }
}

module.exports = function(db) {
  return new Folding(db, '$fs')
}
<commit_msg>Fixed folding cache<commit_after>var _ = require('lodash')

var Folding = function Folding(db, ns) {
  this.db = db
  this.ns = ns + '-'
  this.cache = {}
  this.startWith = {}
  this.indexWith = function(event) { return event['$id'] || 1 }
  this.howToFoldEvents = function(s, e) { return null }
}

Folding.prototype.put = function(key, value) {
  var cache = this.cache

  cache[key] = value
  this.db.put(this.ns + key, value, function(err) {
    if (JSON.stringify(cache[key]) === JSON.stringify(value)) {
      delete cache[key]
    }
  })
}

Folding.prototype.get = function(key, callback) {
  var cache = this.cache

  if (cache[key]) {
    return callback(null, cache[key])
  }
  this.db.get(this.ns + key, function(err, value) {
    if (err) {
      if (cache[key]) {
        return callback(null, cache[key])
      }
      return callback(err, value)
    }
    if (cache[key] && (JSON.stringify(cache[key]) !== JSON.stringify(value))) {
      return callback(null, cache[key])
    }
    return callback(null, value)
  })
}

Folding.prototype.fold = function(event, lw) {
  var self = this, index = self.indexWith(event)
  if (index) {
    self.get(index, function(err, previousFoldingStatus) {
      if (err) {
        previousFoldingStatus = self.startWith
      }
      var nextFoldingStatus =
        self.howToFoldEvents['$any'](
          _.clone(previousFoldingStatus), event, index, lw
        )
      if (nextFoldingStatus && (JSON.stringify(nextFoldingStatus) !== JSON.stringify(previousFoldingStatus))) {
        self.put(index, nextFoldingStatus)
      }
    })
  }
}

module.exports = function(db) {
  return new Folding(db, '$fs')
}
<|endoftext|>


<commit_before>'use strict';

var Client = require('poeditor-client');
var Promise = require('bluebird');
var Immutable = require('immutable');
var fs = Promise.promisifyAll(require('fs'));
var path = require('path');
var stringify = require('json-stable-stringify');

exports.clientFactory = function (apiToken) {
	return new Client(apiToken);
};

exports.getProject = function (apiToken, projectName) {
	return Promise.resolve(exports.clientFactory(apiToken).projects.list())
	.then(function (projects) {
		return Immutable.List(projects)
		.find(function (project) {
			return project.name == projectName;
		});
	});
};

exports.getTranslations = function (project) {
	return Promise.resolve(project.languages.list())
	.map(function (language) {
		return Promise.resolve(language.terms.list());
	})
	.then(function (terms) {
		return Immutable.fromJS(terms).flatten(1).toJS();
	});
};

exports.writeTranslations = function (translations, dest) {
	var writes = Immutable.List(translations)
	.groupBy(function (translation) {
		return translation.__languageCode;
	})
	.map(function (translations) {
		return Immutable.List(translations)
		.reduce(function (result, translation) {
			return result.set(translation.term, translation.translation);
		}, Immutable.Map());
	})
	.map(function (translations, languageCode) {
		var file = path.join(dest, languageCode + '.json');
		var data = stringify(translations, {
			space: '\t'
		});
		return fs.writeFileAsync(file, data);
	})
	.toList()
	.toJS();
	return Promise.all(writes);
};
<commit_msg>fixed write to return written file names<commit_after>'use strict';

var Client = require('poeditor-client');
var Promise = require('bluebird');
var Immutable = require('immutable');
var fs = Promise.promisifyAll(require('fs'));
var path = require('path');
var stringify = require('json-stable-stringify');

exports.clientFactory = function (apiToken) {
	return new Client(apiToken);
};

exports.getProject = function (apiToken, projectName) {
	return Promise.resolve(exports.clientFactory(apiToken).projects.list())
	.then(function (projects) {
		return Immutable.List(projects)
		.find(function (project) {
			return project.name == projectName;
		});
	});
};

exports.getTranslations = function (project) {
	return Promise.resolve(project.languages.list())
	.map(function (language) {
		return Promise.resolve(language.terms.list());
	})
	.then(function (terms) {
		return Immutable.fromJS(terms).flatten(1).toJS();
	});
};

exports.writeTranslations = function (translations, dest) {
	var writes = Immutable.List(translations)
	.groupBy(function (translation) {
		return translation.__languageCode;
	})
	.map(function (translations) {
		return Immutable.List(translations)
		.reduce(function (result, translation) {
			return result.set(translation.term, translation.translation);
		}, Immutable.Map());
	})
	.map(function (translations, languageCode) {
		var file = path.join(dest, languageCode + '.json');
		var data = stringify(translations, {
			space: '\t'
		});
		return fs.writeFileAsync(file, data)
		.then(function () {
			return file;
		});
	})
	.toList()
	.toJS();
	return Promise.all(writes);
};
<|endoftext|>


<commit_before>var querystring = require('querystring');
var config = require('./config');
var _ = require('lodash');

function zeroPad(num) {
  num = String(num);
  if (num.length < 2) {
    num = '0' + num;
  }
  return num;
}

function capitalizeFirstLetter(string) {
  return string.charAt(0).toUpperCase() + string.slice(1);
}

// Replace / add a query parameter to the base url
exports.urlQuery = function(base, key, val) {
  var params = {};
  var queryPos = base.indexOf('?');
  if (queryPos !== -1) {
    // Add one to also cut `?`
    params = querystring.parse(base.substr(queryPos + 1));
    base = base.substr(0, queryPos);
  }

  if (val !== undefined) {
    params[key] = val;
  } else {
    delete params[key];
  }

  return `${base}?${querystring.stringify(params)}`;
};

exports.DMYToYDM = function(d) {
  var parts = [];
  if (d.indexOf('/') !== -1) {
    parts = d.split('\/');
  } else if (d.indexOf('-') !== -1) {
    parts = d.split('\-');
  }

  var day = zeroPad(parts[0]);
  var month = zeroPad(parts[1]);
  var year = zeroPad(parts[2]);

  return `${year}-${month}-${day}`;
};

exports.ensureYMD = function(d) {
  var dmy = exports.ensureDMY(d);
  if (dmy !== null) {
    return exports.DMYToYDM(dmy);
  }
  return null;
};

exports.ensureDMY = function(d) {
  if (!d) {
    return null;
  }

  var parts = [];
  var output = '';
  if (d.indexOf('/') !== -1) {
    parts = d.split('\/');
  } else if (d.indexOf('-') !== -1) {
    parts = d.split('\-');
  }

  console.log(parts);

  if (parts.length === 3) {
    if (parts[2].length === 4) {
      return parts[0] +'-'+ parts[1] +'-'+ parts[2];
    } else {
      return parts[2] +'-'+ parts[1] +'-'+ parts[0];
    }
  }
  return null;
};

exports.getTwitterAccount = (asset) => {
  if(typeof(config.twitterAccount) == 'string') {
    return config.twitterAccount;
  } else if(typeof(config.twitterAccount) == 'object' && asset) {
    return config.twitterAccount[asset.catalog];
  } else {
    return null;
  }
};

exports.getFacebookAppId = (asset) => {
  if(typeof(config.facebookAppId) == 'string') {
    return config.facebookAppId;
  } else if(typeof(config.facebookAppId) == 'object' && asset) {
    return config.facebookAppId[asset.catalog];
  } else {
    return null;
  }
};

exports.licenseMapped = function(license) {
  if(license && license.id) {
    return _.extend(license, config.licenseMapping[license.id]);
  } else {
    return license;
  }
};

const MONTHS = [
  'januar', 'februar', 'marts', 'april', 'maj', 'juni',
  'juli', 'august', 'september', 'oktober', 'november', 'december'
];

exports.formatDate = function(date) {
  var points = [];
  if(date.day && date.month) {
    points.push(date.day + '.');
  }
  if(date.month) {
    var month = MONTHS[date.month-1];
    if(!date.day) {
      // Capitalize if the month is displayed first
      month = capitalizeFirstLetter(month);
    }
    points.push(month);
  }
  if(date.year) {
    points.push(date.year);
  }
  return points.join(' ');
};

exports.magic360Options = function(imageRotationSet) {
  var smallImages = imageRotationSet.small;
  var largeImages = imageRotationSet.large;
  var options = {
    'magnifier-shape': 'circle',
    'magnifier-width': '100%',
    'columns': smallImages.length,
    'images': smallImages.join(' '),
    'large-images': largeImages.join(' ')
  };
  var result = '';
  for (var o in options) {
    result += o + ': ' + options[o] + '; ';
  }
  return result;
};

exports.link = function(url, text) {
  if(!text) {
    text = url;
  }
  return '' + text + '';
};

exports.decimals = function(n, decimals) {
  if (typeof(n) !== 'undefined' && n !== null) {
    return parseFloat(n).toFixed(decimals).replace('.', ',');
  } else {
    return n;
  }
};

exports.filesizeMB = function(filesize) {
  if (filesize && filesize.value) {
    var mb = filesize.value / 1024 / 1024;
    // Formatted
    mb = exports.decimals(mb, 1);
    return mb + ' MB';
  } else {
    return undefined;
  }
};

// TODO should maybe be moved somewhere inside natmus
exports.natmusFilesizeMB = function(filesize) {
  if (filesize) {
    var mb = filesize / 1024 / 1024;
    // Formatted
    mb = exports.decimals(mb, 1);
    return mb + ' MB';
  } else {
    return undefined;
  }
};

exports.licenseLinked = function(license) {
  if (license) {
    var licenseMapped = config.licenseMapping[license.id];
    if (licenseMapped && licenseMapped.url && licenseMapped.short) {
      return exports.link(licenseMapped.url, licenseMapped.short);
    } else {
      return license.displaystring || 'Ukendt';
    }
  } else {
    return '';
  }
};

exports.catalogLinked = function(catalogs, catalogAlias) {
  if (catalogAlias) {
    var url = '/' + catalogAlias;
    var text = catalogs.reduce(function(name, catalog){
      if (name){
        return name;
      } else if (catalog.alias === catalogAlias) {
        return catalog.name;
      }
    }, null);
    return exports.link(url, text);
  } else {
    return '';
  }
};

exports.get = function(metadata, path) {
  return _.get(metadata, path);
};
<commit_msg>Show creator function<commit_after>var querystring = require('querystring');
var config = require('./config');
var _ = require('lodash');

function zeroPad(num) {
  num = String(num);
  if (num.length < 2) {
    num = '0' + num;
  }
  return num;
}

function capitalizeFirstLetter(string) {
  return string.charAt(0).toUpperCase() + string.slice(1);
}

// Replace / add a query parameter to the base url
exports.urlQuery = function(base, key, val) {
  var params = {};
  var queryPos = base.indexOf('?');
  if (queryPos !== -1) {
    // Add one to also cut `?`
    params = querystring.parse(base.substr(queryPos + 1));
    base = base.substr(0, queryPos);
  }

  if (val !== undefined) {
    params[key] = val;
  } else {
    delete params[key];
  }

  return `${base}?${querystring.stringify(params)}`;
};

exports.DMYToYDM = function(d) {
  var parts = [];
  if (d.indexOf('/') !== -1) {
    parts = d.split('\/');
  } else if (d.indexOf('-') !== -1) {
    parts = d.split('\-');
  }

  var day = zeroPad(parts[0]);
  var month = zeroPad(parts[1]);
  var year = zeroPad(parts[2]);

  return `${year}-${month}-${day}`;
};

exports.ensureYMD = function(d) {
  var dmy = exports.ensureDMY(d);
  if (dmy !== null) {
    return exports.DMYToYDM(dmy);
  }
  return null;
};

exports.ensureDMY = function(d) {
  if (!d) {
    return null;
  }

  var parts = [];
  var output = '';
  if (d.indexOf('/') !== -1) {
    parts = d.split('\/');
  } else if (d.indexOf('-') !== -1) {
    parts = d.split('\-');
  }

  console.log(parts);

  if (parts.length === 3) {
    if (parts[2].length === 4) {
      return parts[0] +'-'+ parts[1] +'-'+ parts[2];
    } else {
      return parts[2] +'-'+ parts[1] +'-'+ parts[0];
    }
  }
  return null;
};

exports.getTwitterAccount = (asset) => {
  if(typeof(config.twitterAccount) == 'string') {
    return config.twitterAccount;
  } else if(typeof(config.twitterAccount) == 'object' && asset) {
    return config.twitterAccount[asset.catalog];
  } else {
    return null;
  }
};

exports.getFacebookAppId = (asset) => {
  if(typeof(config.facebookAppId) == 'string') {
    return config.facebookAppId;
  } else if(typeof(config.facebookAppId) == 'object' && asset) {
    return config.facebookAppId[asset.catalog];
  } else {
    return null;
  }
};

exports.licenseMapped = function(license) {
  if(license && license.id) {
    return _.extend(license, config.licenseMapping[license.id]);
  } else {
    return license;
  }
};

const MONTHS = [
  'januar', 'februar', 'marts', 'april', 'maj', 'juni',
  'juli', 'august', 'september', 'oktober', 'november', 'december'
];

exports.formatDate = function(date) {
  var points = [];
  if(date.day && date.month) {
    points.push(date.day + '.');
  }
  if(date.month) {
    var month = MONTHS[date.month-1];
    if(!date.day) {
      // Capitalize if the month is displayed first
      month = capitalizeFirstLetter(month);
    }
    points.push(month);
  }
  if(date.year) {
    points.push(date.year);
  }
  return points.join(' ');
};

exports.magic360Options = function(imageRotationSet) {
  var smallImages = imageRotationSet.small;
  var largeImages = imageRotationSet.large;
  var options = {
    'magnifier-shape': 'circle',
    'magnifier-width': '100%',
    'columns': smallImages.length,
    'images': smallImages.join(' '),
    'large-images': largeImages.join(' ')
  };
  var result = '';
  for (var o in options) {
    result += o + ': ' + options[o] + '; ';
  }
  return result;
};

exports.link = function(url, text) {
  if(!text) {
    text = url;
  }
  return '' + text + '';
};

exports.decimals = function(n, decimals) {
  if (typeof(n) !== 'undefined' && n !== null) {
    return parseFloat(n).toFixed(decimals).replace('.', ',');
  } else {
    return n;
  }
};

exports.filesizeMB = function(filesize) {
  if (filesize && filesize.value) {
    var mb = filesize.value / 1024 / 1024;
    // Formatted
    mb = exports.decimals(mb, 1);
    return mb + ' MB';
  } else {
    return undefined;
  }
};

// TODO (start) should maybe be moved somewhere inside natmus
exports.natmusFilesizeMB = function(filesize) {
  if (filesize) {
    var mb = filesize / 1024 / 1024;
    // Formatted
    mb = exports.decimals(mb, 1);
    return mb + ' MB';
  } else {
    return undefined;
  }
};

exports.creators = function(creators) {
  let creatorsList = [];
  creators.every(obj => creatorsList.push(obj.name));
  return creatorsList.join(', ');
};

// TODO (end) should maybe be moved somewhere inside natmus

exports.licenseLinked = function(license) {
  if (license) {
    var licenseMapped = config.licenseMapping[license.id];
    if (licenseMapped && licenseMapped.url && licenseMapped.short) {
      return exports.link(licenseMapped.url, licenseMapped.short);
    } else {
      return license.displaystring || 'Ukendt';
    }
  } else {
    return '';
  }
};

exports.catalogLinked = function(catalogs, catalogAlias) {
  if (catalogAlias) {
    var url = '/' + catalogAlias;
    var text = catalogs.reduce(function(name, catalog){
      if (name){
        return name;
      } else if (catalog.alias === catalogAlias) {
        return catalog.name;
      }
    }, null);
    return exports.link(url, text);
  } else {
    return '';
  }
};

exports.get = function(metadata, path) {
  return _.get(metadata, path);
};
<|endoftext|>


<commit_before>/*
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
Copyrights licensed under the New BSD License.
See the accompanying LICENSE file for terms.
*/

(function (root, factory) {
    'use strict';

    // Intl and IntlMessageFormat are dependencies of this package. The built-in
    // `Intl` is preferred, but when not available it looks for the polyfill.
    var Intl = root.Intl || root.IntlPolyfill,
        lib  = factory(root.JSON, Intl, root.IntlMessageFormat);

    /* istanbul ignore next */

    if (typeof define === 'function' && define.amd) {
        define(lib);
    }

    if (typeof exports === 'object') {
        module.exports = lib;
    }

    if (root.Handlebars) {
        lib.registerWith(root.Handlebars);
    }

})(typeof global !== 'undefined' ? global : this, function (JSON, Intl, IntlMessageFormat) {
    'use strict';

    if (!Intl) {
        throw new ReferenceError('Intl must be available.');
    }

    if (!IntlMessageFormat) {
        throw new ReferenceError('IntlMessageFormat must be available.');
    }

    var exports = {};

    // Export the helpers.
    exports.helpers = {
        intl       : intl,
        intlDate   : intlDate,
        intlNumber : intlNumber,
        intlGet    : intlGet,
        intlMessage: intlMessage
    };

    // Export utility function to register all the helpers.
    exports.registerWith = function (engine) {
        var helpers = exports.helpers,
            name;

        for (name in helpers) {
            if (helpers.hasOwnProperty(name)) {
                engine.registerHelper(name, helpers[name]);
            }
        }
    };

    // -- Internals ------------------------------------------------------------

    // Cache to hold NumberFormat and DateTimeFormat instances for reuse.
    var formats = {
        number: {},
        date  : {}
    };

    function getFormat(type, locales, options) {
        var orderedOptions, option, key, i, len, id, format;

        // When JSON is available in the environment, use it build a cache-id
        // to reuse formats for increased performance.
        if (JSON) {
            // Order the keys in `options` to create a serialized semantic
            // representation which is reproducible.
            if (options) {
                orderedOptions = [];

                for (key in options) {
                    if (options.hasOwnProperty(key)) {
                        orderedOptions.push(key);
                    }
                }

                orderedOptions.sort();

                for (i = 0, len = orderedOptions.length; i < len; i += 1) {
                    key    = orderedOptions[i];
                    option = {};

                    option[key] = options[key];
                    orderedOptions[i] = option;
                }
            }

            id = JSON.stringify([locales, orderedOptions]);
        }

        // Check for a cached format instance, and use it.
        format = formats[type][id];
        if (format) { return format; }

        switch (type) {
            case 'number':
                format = new Intl.NumberFormat(locales, options);
                break;
            case 'date':
                format = new Intl.DateTimeFormat(locales, options);
                break;
        }

        // Cache format for reuse.
        if (id) {
            formats[type][id] = format;
        }

        return format;
    }

    // -- Helpers --------------------------------------------------------------

    function intl(options) {
        /* jshint validthis:true */

        if (!options.fn) {
            throw new ReferenceError('intl must be invoked as a block helper');
        }

        var hash = options.hash,
            data = options.data;

        // Assign the `hash` values to `data.intl` to pass them to everything
        // within the {{#intl}} block.
        return options.fn(this, {
            data: extend({}, data, {
                intl: extend({}, data.intl, hash)
            })
        });
    }

    function intlDate(date, options) {
        date = new Date(date);

        // Determine if the `date` is valid.
        if (!(date && date.getTime())) {
            throw new TypeError('A date must be provided.');
        }

        var hash    = options.hash,
            data    = options.data,
            locales = data.intl && data.intl.locales;

        return getFormat('date', locales, hash).format(date);
    }

    function intlNumber(num, options) {
        if (typeof num !== 'number') {
            throw new TypeError('A number must be provided.');
        }

        var hash    = options.hash,
            data    = options.data,
            locales = data.intl && data.intl.locales;

        return getFormat('number', locales, hash).format(num);
    }

    function intlGet(path, options) {
        var data      = options.data,
            messages  = data.intl && data.intl.messages,
            pathParts = path.split('.'),
            message, len, i;

        // Use the path to walk the Intl messages to find the message, and throw
        // a descriptive error the message is not found.
        try {
            for (i = 0, len = pathParts.length; i < len; i++) {
                message = messages = messages[pathParts[i]];
            }
        } finally {
            if (message === undefined) {
                throw new ReferenceError('Could not find Intl message: ' + path);
            }
        }

        return message;
    }

    function intlMessage(message, options) {
        if (!options) {
            options = message;
            message = null;
        }

        var hash = options.hash;

        // Support a message being passed as a string argument or pre-prased
        // array. When there's no `message` argument, ensure a message path name
        // was provided at `intlName` in the `hash`.
        //
        // TODO: remove support form `hash.intlName` once Handlebars bugs with
        // subexpressions are fixed.
        if (!(message || typeof message === 'string' || hash.intlName)) {
            throw new ReferenceError('A message or intlName must be provided');
        }

        var intlData = (options.data && options.data.intl) || {},
            locales  = intlData.locales,
            formats  = intlData.formats;

        // Lookup message by path name.
        if (!message && hash.intlName) {
            message = intlGet(hash.intlName, options);
        }

        // Assume that an object with a `format()` method is already an
        // IntlMessageFormat instance, and use it; otherwise create a new one.
        var messageFormat = typeof message.format === 'function' ? message :
                new IntlMessageFormat(message, locales, formats);

        return messageFormat.format(hash);
    }

    // -- Utilities ------------------------------------------------------------

    function extend(obj) {
        var sources = Array.prototype.slice.call(arguments, 1),
            i, len, source, key;

        for (i = 0, len = sources.length; i < len; i += 1) {
            source = sources[i];
            if (!source) { continue; }

            for (key in source) {
                if (source.hasOwnProperty(key)) {
                    obj[key] = source[key];
                }
            }
        }

        return obj;
    }

    return exports;
});
<commit_msg>Revert change to auto-register with Handlebars and instead expose global<commit_after>/*
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
Copyrights licensed under the New BSD License.
See the accompanying LICENSE file for terms.
*/

(function (root, factory) {
    'use strict';

    // Intl and IntlMessageFormat are dependencies of this package. The built-in
    // `Intl` is preferred, but when not available it looks for the polyfill.
    var Intl = root.Intl || root.IntlPolyfill,
        lib  = factory(root.JSON, Intl, root.IntlMessageFormat);

    /* istanbul ignore next */

    if (typeof define === 'function' && define.amd) {
        define(lib);
    }

    if (typeof exports === 'object') {
        module.exports = lib;
    }

    root.HandlebarsHelperIntl = lib;

})(typeof global !== 'undefined' ? global : this, function (JSON, Intl, IntlMessageFormat) {
    'use strict';

    if (!Intl) {
        throw new ReferenceError('Intl must be available.');
    }

    if (!IntlMessageFormat) {
        throw new ReferenceError('IntlMessageFormat must be available.');
    }

    var exports = {};

    // Export the helpers.
    exports.helpers = {
        intl       : intl,
        intlDate   : intlDate,
        intlNumber : intlNumber,
        intlGet    : intlGet,
        intlMessage: intlMessage
    };

    // Export utility function to register all the helpers.
    exports.registerWith = function (engine) {
        var helpers = exports.helpers,
            name;

        for (name in helpers) {
            if (helpers.hasOwnProperty(name)) {
                engine.registerHelper(name, helpers[name]);
            }
        }
    };

    // -- Internals ------------------------------------------------------------

    // Cache to hold NumberFormat and DateTimeFormat instances for reuse.
    var formats = {
        number: {},
        date  : {}
    };

    function getFormat(type, locales, options) {
        var orderedOptions, option, key, i, len, id, format;

        // When JSON is available in the environment, use it build a cache-id
        // to reuse formats for increased performance.
        if (JSON) {
            // Order the keys in `options` to create a serialized semantic
            // representation which is reproducible.
            if (options) {
                orderedOptions = [];

                for (key in options) {
                    if (options.hasOwnProperty(key)) {
                        orderedOptions.push(key);
                    }
                }

                orderedOptions.sort();

                for (i = 0, len = orderedOptions.length; i < len; i += 1) {
                    key    = orderedOptions[i];
                    option = {};

                    option[key] = options[key];
                    orderedOptions[i] = option;
                }
            }

            id = JSON.stringify([locales, orderedOptions]);
        }

        // Check for a cached format instance, and use it.
        format = formats[type][id];
        if (format) { return format; }

        switch (type) {
            case 'number':
                format = new Intl.NumberFormat(locales, options);
                break;
            case 'date':
                format = new Intl.DateTimeFormat(locales, options);
                break;
        }

        // Cache format for reuse.
        if (id) {
            formats[type][id] = format;
        }

        return format;
    }

    // -- Helpers --------------------------------------------------------------

    function intl(options) {
        /* jshint validthis:true */

        if (!options.fn) {
            throw new ReferenceError('intl must be invoked as a block helper');
        }

        var hash = options.hash,
            data = options.data;

        // Assign the `hash` values to `data.intl` to pass them to everything
        // within the {{#intl}} block.
        return options.fn(this, {
            data: extend({}, data, {
                intl: extend({}, data.intl, hash)
            })
        });
    }

    function intlDate(date, options) {
        date = new Date(date);

        // Determine if the `date` is valid.
        if (!(date && date.getTime())) {
            throw new TypeError('A date must be provided.');
        }

        var hash    = options.hash,
            data    = options.data,
            locales = data.intl && data.intl.locales;

        return getFormat('date', locales, hash).format(date);
    }

    function intlNumber(num, options) {
        if (typeof num !== 'number') {
            throw new TypeError('A number must be provided.');
        }

        var hash    = options.hash,
            data    = options.data,
            locales = data.intl && data.intl.locales;

        return getFormat('number', locales, hash).format(num);
    }

    function intlGet(path, options) {
        var data      = options.data,
            messages  = data.intl && data.intl.messages,
            pathParts = path.split('.'),
            message, len, i;

        // Use the path to walk the Intl messages to find the message, and throw
        // a descriptive error the message is not found.
        try {
            for (i = 0, len = pathParts.length; i < len; i++) {
                message = messages = messages[pathParts[i]];
            }
        } finally {
            if (message === undefined) {
                throw new ReferenceError('Could not find Intl message: ' + path);
            }
        }

        return message;
    }

    function intlMessage(message, options) {
        if (!options) {
            options = message;
            message = null;
        }

        var hash = options.hash;

        // Support a message being passed as a string argument or pre-prased
        // array. When there's no `message` argument, ensure a message path name
        // was provided at `intlName` in the `hash`.
        //
        // TODO: remove support form `hash.intlName` once Handlebars bugs with
        // subexpressions are fixed.
        if (!(message || typeof message === 'string' || hash.intlName)) {
            throw new ReferenceError('A message or intlName must be provided');
        }

        var intlData = (options.data && options.data.intl) || {},
            locales  = intlData.locales,
            formats  = intlData.formats;

        // Lookup message by path name.
        if (!message && hash.intlName) {
            message = intlGet(hash.intlName, options);
        }

        // Assume that an object with a `format()` method is already an
        // IntlMessageFormat instance, and use it; otherwise create a new one.
        var messageFormat = typeof message.format === 'function' ? message :
                new IntlMessageFormat(message, locales, formats);

        return messageFormat.format(hash);
    }

    // -- Utilities ------------------------------------------------------------

    function extend(obj) {
        var sources = Array.prototype.slice.call(arguments, 1),
            i, len, source, key;

        for (i = 0, len = sources.length; i < len; i += 1) {
            source = sources[i];
            if (!source) { continue; }

            for (key in source) {
                if (source.hasOwnProperty(key)) {
                    obj[key] = source[key];
                }
            }
        }

        return obj;
    }

    return exports;
});
<|endoftext|>


<commit_before>export default [
  '._*',
  '.hg',
  '.git',
  '.svn',
  '.next',
  '.dockerignore',
  '.gitignore',
  '.npmrc',
  '.*.swp',
  '.DS_Store',
  '.wafpickle-*',
  '.lock-wscript',
  'npm-debug.log',
  'config.gypi',
  'node_modules',
  'CVS',
  'README',
  'README.*',
  'CHANGELOG',
  'History.md',
  'LICENSE',
  'Readme',
  'Readme.*',
  'test',
  'tests'
];
<commit_msg>remove `test` and `tests` from ignored files<commit_after>export default [
  '._*',
  '.hg',
  '.git',
  '.svn',
  '.next',
  '.dockerignore',
  '.gitignore',
  '.npmrc',
  '.*.swp',
  '.DS_Store',
  '.wafpickle-*',
  '.lock-wscript',
  'npm-debug.log',
  'config.gypi',
  'node_modules',
  'CVS',
  'README',
  'README.*',
  'CHANGELOG',
  'History.md',
  'LICENSE',
  'Readme',
  'Readme.*'
];
<|endoftext|>


<commit_before>const { INSTALL_PATH } = require('./config.js')
const { fileExists } = require('./utils.js')

const PLATFORM_NAMES = {
  'darwin-amd64': ['darwin'],
  'linux-386': ['linux'],
  'linux-amd64': ['linux', 'x64'],
  'windows-386': ['win32'],
  'windows-amd64': ['win32', 'x64']
}

function download (pkg) {
  const { basename, dirname } = require('path')
  const { promisify } = require('util')
  const BinWrapper = require('bin-wrapper')
  const bin = new BinWrapper()
    .dest(dirname(INSTALL_PATH))
    .use(basename(INSTALL_PATH))

  // add platforms
  pkg.platforms.forEach(platform => {
    // e.g. ['windows-amd64', 'win32', 'x64']
    const args = [platform.url]
      .concat(PLATFORM_NAMES[platform.name])

    bin.src.apply(bin, args)
  })

  return promisify(bin.run.bind(bin))(['--help'])
}

async function getLatest () {
  const fetch = require('isomorphic-fetch')
  const uri = 'https://shopify-themekit.s3.amazonaws.com/releases/latest.json'
  const response = await fetch(uri)

  return response.json()
}

async function install () {
  const alreadyInstalled = await fileExists(INSTALL_PATH)

  if (!alreadyInstalled) {
    const ora = require('ora')
    const spinner = ora({ color: 'blue' })

    spinner.start('Fetching latest ThemeKit version...')
    const pkg = await getLatest()
    spinner.text = 'Downloading ThemeKit...'
    await download(pkg)
    spinner.stop()
  }
}

if (process.argv.includes('--st-install')) {
  install()
    .catch(console.error)
}

module.exports = install
<commit_msg>updating comments<commit_after>const { INSTALL_PATH } = require('./config.js')
const { fileExists } = require('./utils.js')

const PLATFORM_NAMES = {
  'darwin-amd64': ['darwin'],
  'linux-386': ['linux'],
  'linux-amd64': ['linux', 'x64'],
  'windows-386': ['win32'],
  'windows-amd64': ['win32', 'x64']
}

function download (pkg) {
  const { basename, dirname } = require('path')
  const { promisify } = require('util')
  const BinWrapper = require('bin-wrapper')
  const bin = new BinWrapper()
    .dest(dirname(INSTALL_PATH))
    .use(basename(INSTALL_PATH))

  // add platforms
  pkg.platforms.forEach(platform => {
    // e.g. ['https://.../darwin-amd64/theme', 'darwin']
    const args = [platform.url]
      .concat(PLATFORM_NAMES[platform.name])

    bin.src.apply(bin, args)
  })

  // download and verify with test command (--help)
  return promisify(bin.run.bind(bin))(['--help'])
}

async function getLatest () {
  const fetch = require('isomorphic-fetch')
  const uri = 'https://shopify-themekit.s3.amazonaws.com/releases/latest.json'
  const response = await fetch(uri)

  return response.json()
}

async function install () {
  const alreadyInstalled = await fileExists(INSTALL_PATH)

  if (!alreadyInstalled) {
    const ora = require('ora')
    const spinner = ora({ color: 'blue' })

    spinner.start('Fetching latest ThemeKit version...')
    const pkg = await getLatest()
    spinner.text = 'Downloading ThemeKit...'
    await download(pkg)
    spinner.stop()
  }
}

if (process.argv.includes('--st-install')) {
  install()
    .catch(console.error)
}

module.exports = install
<|endoftext|>


<commit_before>// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// ---------------------------------------------------------------------------
// Support for JS interoperability
// ---------------------------------------------------------------------------
function SendPortSync() {
}

function ReceivePortSync() {
  this.id = ReceivePortSync.id++;
  ReceivePortSync.map[this.id] = this;
}

// Type for remote proxies to Dart objects with dart2js.
function DartObject(o) {
  this.o = o;
}

(function() {
  // Serialize the following types as follows:
  //  - primitives / null: unchanged
  //  - lists: [ 'list', internal id, list of recursively serialized elements ]
  //  - maps: [ 'map', internal id, map of keys and recursively serialized values ]
  //  - send ports: [ 'sendport', type, isolate id, port id ]
  //
  // Note, internal id's are for cycle detection.
  function serialize(message) {
    var visited = [];
    function checkedSerialization(obj, serializer) {
      // Implementation detail: for now use linear search.
      // Another option is expando, but it may prohibit
      // VM optimizations (like putting object into slow mode
      // on property deletion.)
      var id = visited.indexOf(obj);
      if (id != -1) return [ 'ref', id ];
      var id = visited.length;
      visited.push(obj);
      return serializer(id);
    }

    function doSerialize(message) {
      if (message == null) {
        return null;  // Convert undefined to null.
      } else if (typeof(message) == 'string' ||
                 typeof(message) == 'number' ||
                 typeof(message) == 'boolean') {
        return message;
      } else if (message instanceof Array) {
        return checkedSerialization(message, function(id) {
          var values = new Array(message.length);
          for (var i = 0; i < message.length; i++) {
            values[i] = doSerialize(message[i]);
          }
          return [ 'list', id, values ];
        });
      } else if (message instanceof LocalSendPortSync) {
        return [ 'sendport', 'nativejs', message.receivePort.id ];
      } else if (message instanceof DartSendPortSync) {
        return [ 'sendport', 'dart', message.isolateId, message.portId ];
      } else {
        return checkedSerialization(message, function(id) {
          var keys = Object.getOwnPropertyNames(message);
          var values = new Array(keys.length);
          for (var i = 0; i < keys.length; i++) {
            values[i] = doSerialize(message[keys[i]]);
          }
          return [ 'map', id, keys, values ];
        });
      }
    }
    return doSerialize(message);
  }

  function deserialize(message) {
    return deserializeHelper(message);
  }

  function deserializeHelper(message) {
    if (message == null ||
        typeof(message) == 'string' ||
        typeof(message) == 'number' ||
        typeof(message) == 'boolean') {
      return message;
    }
    switch (message[0]) {
      case 'map': return deserializeMap(message);
      case 'sendport': return deserializeSendPort(message);
      case 'list': return deserializeList(message);
      default: throw 'unimplemented';
    }
  }

  function deserializeMap(message) {
    var result = { };
    var id = message[1];
    var keys = message[2];
    var values = message[3];
    for (var i = 0, length = keys.length; i < length; i++) {
      var key = deserializeHelper(keys[i]);
      var value = deserializeHelper(values[i]);
      result[key] = value;
    }
    return result;
  }

  function deserializeSendPort(message) {
    var tag = message[1];
    switch (tag) {
      case 'nativejs':
        var id = message[2];
        return new LocalSendPortSync(ReceivePortSync.map[id]);
      case 'dart':
        var isolateId = message[2];
        var portId = message[3];
        return new DartSendPortSync(isolateId, portId);
      default:
        throw 'Illegal SendPortSync type: $tag';
    }
  }

  function deserializeList(message) {
    var values = message[2];
    var length = values.length;
    var result = new Array(length);
    for (var i = 0; i < length; i++) {
      result[i] = deserializeHelper(values[i]);
    }
    return result;
  }

  window.registerPort = function(name, port) {
    var stringified = JSON.stringify(serialize(port));
    var attrName = 'dart-port:' + name;
    document.documentElement.setAttribute(attrName, stringified);
  };

  window.lookupPort = function(name) {
    var attrName = 'dart-port:' + name;
    var stringified = document.documentElement.getAttribute(attrName);
    return deserialize(JSON.parse(stringified));
  };

  ReceivePortSync.id = 0;
  ReceivePortSync.map = {};

  ReceivePortSync.dispatchCall = function(id, message) {
    // TODO(vsm): Handle and propagate exceptions.
    var deserialized = deserialize(message);
    var result = ReceivePortSync.map[id].callback(deserialized);
    return serialize(result);
  };

  ReceivePortSync.prototype.receive = function(callback) {
    this.callback = callback;
  };

  ReceivePortSync.prototype.toSendPort = function() {
    return new LocalSendPortSync(this);
  };

  ReceivePortSync.prototype.close = function() {
    delete ReceivePortSync.map[this.id];
  };

  if (navigator.userAgent.indexOf('(Dart)') !== -1) {
    window.addEventListener('js-sync-message', function(event) {
      var data = JSON.parse(getPortSyncEventData(event));
      var deserialized = deserialize(data.message);
      var result = ReceivePortSync.map[data.id].callback(deserialized);
      // TODO(vsm): Handle and propagate exceptions.
      dispatchEvent('js-result', serialize(result));
    }, false);
  }

  function LocalSendPortSync(receivePort) {
    this.receivePort = receivePort;
  }

  LocalSendPortSync.prototype = new SendPortSync();

  LocalSendPortSync.prototype.callSync = function(message) {
    // TODO(vsm): Do a direct deepcopy.
    message = deserialize(serialize(message));
    return this.receivePort.callback(message);
  }

  function DartSendPortSync(isolateId, portId) {
    this.isolateId = isolateId;
    this.portId = portId;
  }

  DartSendPortSync.prototype = new SendPortSync();

  function dispatchEvent(receiver, message) {
    var string = JSON.stringify(message);
    var event = document.createEvent('CustomEvent');
    event.initCustomEvent(receiver, false, false, string);
    window.dispatchEvent(event);
  }

  function getPortSyncEventData(event) {
    return event.detail;
  }

  DartSendPortSync.prototype.callSync = function(message) {
    var serialized = serialize(message);
    var target = 'dart-port-' + this.isolateId + '-' + this.portId;
    // TODO(vsm): Make this re-entrant.
    // TODO(vsm): Set this up set once, on the first call.
    var source = target + '-result';
    var result = null;
    var listener = function (e) {
      result = JSON.parse(getPortSyncEventData(e));
    };
    window.addEventListener(source, listener, false);
    dispatchEvent(target, [source, serialized]);
    window.removeEventListener(source, listener, false);
    return deserialize(result);
  }
})();

(function() {
  // Proxy support for js.dart.

  // We don't use 'window' because we might be in a web worker, but we don't
  // use 'self' because not all browsers support it
  var globalContext = function() { return this; }();

  // Table for local objects and functions that are proxied.
  function ProxiedObjectTable() {
    // Name for debugging.
    this.name = 'js-ref';

    // Table from IDs to JS objects.
    this.map = {};

    // Generator for new IDs.
    this._nextId = 0;

    // Ports for managing communication to proxies.
    this.port = new ReceivePortSync();
    this.sendPort = this.port.toSendPort();
  }

  // Number of valid IDs.  This is the number of objects (global and local)
  // kept alive by this table.
  ProxiedObjectTable.prototype.count = function () {
    return Object.keys(this.map).length;
  }

  var _dartRefPropertyName = "_$dart_ref";

  // attempts to add an unenumerable property to o. If that is not allowed
  // it silently fails.
  function _defineProperty(o, name, value) {
    if (Object.isExtensible(o)) {
      try {
        Object.defineProperty(o, name, { 'value': value });
      } catch (e) {
        // object is native and lies about being extensible
        // see https://bugzilla.mozilla.org/show_bug.cgi?id=775185
      }
    }
  }

  // Adds an object to the table and return an ID for serialization.
  ProxiedObjectTable.prototype.add = function (obj, id) {
    if (id != null) {
      this.map[id] = obj;
      return id;
    } else {
      var ref = obj[_dartRefPropertyName];
      if (ref == null) {
        ref = this.name + '-' + this._nextId++;
        this.map[ref] = obj;
        _defineProperty(obj, _dartRefPropertyName, ref);
      }
      return ref;
    }
  }

  // Gets the object or function corresponding to this ID.
  ProxiedObjectTable.prototype.contains = function (id) {
    return this.map.hasOwnProperty(id);
  }

  // Gets the object or function corresponding to this ID.
  ProxiedObjectTable.prototype.get = function (id) {
    if (!this.map.hasOwnProperty(id)) {
      throw 'Proxy ' + id + ' has been invalidated.'
    }
    return this.map[id];
  }

  ProxiedObjectTable.prototype._initialize = function () {
    // Configure this table's port to forward methods, getters, and setters
    // from the remote proxy to the local object.
    var table = this;

    this.port.receive(function (message) {
      // TODO(vsm): Support a mechanism to register a handler here.
      try {
        var receiver = table.get(message[0]);
        var member = message[1];
        var kind = message[2];
        var args = message[3].map(deserialize);
        if (kind == 'get') {
          // Getter.
          var field = member;
          if (field in receiver && args.length == 0) {
            return [ 'return', serialize(receiver[field]) ];
          }
        } else if (kind == 'set') {
          // Setter.
          var field = member;
          if (args.length == 1) {
            return [ 'return', serialize(receiver[field] = args[0]) ];
          }
        } else if (kind == 'hasProperty') {
          var field = member;
          return [ 'return', field in receiver ];
        } else if (kind == 'apply') {
          // Direct function invocation.
          return [ 'return',
              serialize(receiver.apply(args[0], args.slice(1))) ];
        } else if (member == '[]' && args.length == 1) {
          // Index getter.
          return [ 'return', serialize(receiver[args[0]]) ];
        } else if (member == '[]=' && args.length == 2) {
          // Index setter.
          return [ 'return', serialize(receiver[args[0]] = args[1]) ];
        } else {
          // Member function invocation.
          var f = receiver[member];
          if (f) {
            var result = f.apply(receiver, args);
            return [ 'return', serialize(result) ];
          }
        }
        return [ 'none' ];
      } catch (e) {
        return [ 'throws', e.toString() ];
      }
    });
  }

  // Singleton for local proxied objects.
  var proxiedObjectTable = new ProxiedObjectTable();
  proxiedObjectTable._initialize()

  // Type for remote proxies to Dart objects.
  function DartObject(id, sendPort) {
    this.id = id;
    this.port = sendPort;
  }

  // Serializes JS types to SendPortSync format:
  // - primitives -> primitives
  // - sendport -> sendport
  // - Function -> [ 'funcref', function-id, sendport ]
  // - Object -> [ 'objref', object-id, sendport ]
  function serialize(message) {
    if (message == null) {
      return null;  // Convert undefined to null.
    } else if (typeof(message) == 'string' ||
               typeof(message) == 'number' ||
               typeof(message) == 'boolean') {
      // Primitives are passed directly through.
      return message;
    } else if (message instanceof SendPortSync) {
      // Non-proxied objects are serialized.
      return message;
    } else if (typeof(message) == 'function') {
      if ('_dart_id' in message) {
        // Remote function proxy.
        var remoteId = message._dart_id;
        var remoteSendPort = message._dart_port;
        return [ 'funcref', remoteId, remoteSendPort ];
      } else {
        // Local function proxy.
        return [ 'funcref',
                 proxiedObjectTable.add(message),
                 proxiedObjectTable.sendPort ];
      }
    } else if (message instanceof DartObject) {
      // Remote object proxy.
      return [ 'objref', message.id, message.port ];
    } else {
      // Local object proxy.
      return [ 'objref',
               proxiedObjectTable.add(message),
               proxiedObjectTable.sendPort ];
    }
  }

  function deserialize(message) {
    if (message == null) {
      return null;  // Convert undefined to null.
    } else if (typeof(message) == 'string' ||
               typeof(message) == 'number' ||
               typeof(message) == 'boolean') {
      // Primitives are passed directly through.
      return message;
    } else if (message instanceof SendPortSync) {
      // Serialized type.
      return message;
    }
    var tag = message[0];
    switch (tag) {
      case 'funcref': return deserializeFunction(message);
      case 'objref': return deserializeObject(message);
    }
    throw 'Unsupported serialized data: ' + message;
  }

  // Create a local function that forwards to the remote function.
  function deserializeFunction(message) {
    var id = message[1];
    var port = message[2];
    // TODO(vsm): Add a more robust check for a local SendPortSync.
    if ("receivePort" in port) {
      // Local function.
      return proxiedObjectTable.get(id);
    } else {
      // Remote function.  Forward to its port.
      if (proxiedObjectTable.contains(id)) {
        return proxiedObjectTable.get(id);
      }
      var f = function () {
        var args = Array.prototype.slice.apply(arguments);
        args.splice(0, 0, this);
        args = args.map(serialize);
        var result = port.callSync([id, '#call', args]);
        if (result[0] == 'throws') throw deserialize(result[1]);
        return deserialize(result[1]);
      };
      // Cache the remote id and port.
      f._dart_id = id;
      f._dart_port = port;
      proxiedObjectTable.add(f, id);
      return f;
    }
  }

  // Creates a DartObject to forwards to the remote object.
  function deserializeObject(message) {
    var id = message[1];
    var port = message[2];
    // TODO(vsm): Add a more robust check for a local SendPortSync.
    if ("receivePort" in port) {
      // Local object.
      return proxiedObjectTable.get(id);
    } else {
      // Remote object.
      if (proxiedObjectTable.contains(id)) {
        return proxiedObjectTable.get(id);
      }
      var proxy = new DartObject(id, port);
      proxiedObjectTable.add(proxy, id);
      return proxy;
    }
  }

  // Remote handler to construct a new JavaScript object given its
  // serialized constructor and arguments.
  function construct(args) {
    args = args.map(deserialize);
    var constructor = args[0];

    // The following code solves the problem of invoking a JavaScript
    // constructor with an unknown number arguments.
    // First bind the constructor to the argument list using bind.apply().
    // The first argument to bind() is the binding of 'this', make it 'null'
    // After that, use the JavaScript 'new' operator which overrides any binding
    // of 'this' with the new instance.
    args[0] = null;
    var factoryFunction = constructor.bind.apply(constructor, args);
    return serialize(new factoryFunction());
  }

  // Remote handler to return the top-level JavaScript context.
  function context(data) {
    return serialize(globalContext);
  }

  // Return true if a JavaScript proxy is instance of a given type (instanceof).
  function proxyInstanceof(args) {
    var obj = deserialize(args[0]);
    var type = deserialize(args[1]);
    return obj instanceof type;
  }

  // Return true if a JavaScript proxy is instance of a given type (instanceof).
  function proxyDeleteProperty(args) {
    var obj = deserialize(args[0]);
    var member = deserialize(args[1]);
    delete obj[member];
  }

  function proxyConvert(args) {
    return serialize(deserializeDataTree(args));
  }

  function deserializeDataTree(data) {
    var type = data[0];
    var value = data[1];
    if (type === 'map') {
      var obj = {};
      for (var i = 0; i < value.length; i++) {
        obj[value[i][0]] = deserializeDataTree(value[i][1]);
      }
      return obj;
    } else if (type === 'list') {
      var list = [];
      for (var i = 0; i < value.length; i++) {
        list.push(deserializeDataTree(value[i]));
      }
      return list;
    } else /* 'simple' */ {
      return deserialize(value);
    }
  }

  function makeGlobalPort(name, f) {
    var port = new ReceivePortSync();
    port.receive(f);
    window.registerPort(name, port.toSendPort());
  }

  makeGlobalPort('dart-js-context', context);
  makeGlobalPort('dart-js-create', construct);
  makeGlobalPort('dart-js-instanceof', proxyInstanceof);
  makeGlobalPort('dart-js-delete-property', proxyDeleteProperty);
  makeGlobalPort('dart-js-convert', proxyConvert);
})();
<commit_msg>Remove sync ports from interop.js<commit_after>// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Type for remote proxies to Dart objects with dart2js.
// WARNING: do not call this constructor or rely on it being
// in the global namespace, as it may be removed.
function DartObject(o) {
  this.o = o;
}
<|endoftext|>


<commit_before>/**
 * Created by Corey600 on 2016/6/15.
 */

'use strict';

// require core modules
var url = require('url');
var querystring = require('querystring');

// require thirdpart modules
var debug = require('debug');

// require custom modules
var createPool = require('./pool');
var Codec = require('./codec');
var Register = require('./register');
var RandomLoadBalance = require('./loadbalance/RandomLoadBalance');

// print debug info
var error = debug('zoodubbo:error');
var warn = debug('zoodubbo:warn');

/**
 * Constructor of Invoker.
 *
 * @param {Client|String|Array} zk // The ZD instance or the uri of providers.
 * @param {Object} opt
 * {
 *  path: String // The path of service node.
 *  version: String, // The version of service.
 *  timeout: Number, // Timeout in milliseconds, defaults to 60 seconds.
 * }
 * @returns {Invoker}
 * @constructor
 */
function Invoker(zk, opt) {
  if (!(this instanceof Invoker)) return new Invoker(zk, opt);

  var option = opt || {};
  this._path = option.path;
  this._dubbo = option.dubbo;
  this._version = option.version;
  this._timeout = option.timeout;
  this._poolMax = option.poolMax;
  this._poolMin = option.poolMin;

  this._providers = null; // Array
  this._configurators = null; // Object
  this._loadBalance = new RandomLoadBalance();

  this._parseProviders = this._parseProviders.bind(this);
  this._parseConfigurators = this._parseConfigurators.bind(this);

  if ('string' === typeof zk) {
    this._uris = [zk];
  } else if (Array.isArray(zk)) {
    this._uris = zk;
  } else if (zk) {
    this._registerProviders = new Register(zk, '/dubbo/' + this._path + '/providers');
    this._registerConfigurators = new Register(zk, '/dubbo/' + this._path + '/configurators');
    this._registerProviders.on('change', this._parseProviders);
    this._registerConfigurators.on('change', this._parseConfigurators);
  }
}

/**
 * Excute the method
 *
 * @param {String} method
 * @param {Array|Object} args
 * {
 *  args: Array
 *  timeout: number
 * }
 * @param {Function} [cb]
 * @public
 */
Invoker.prototype.excute = function (method, args, cb) {
  var option = args;
  if (Array.isArray(option)) {
    option = { args: option };
  }
  option.method = method;

  var self = this;
  if ('function' !== typeof cb) {
    return new Promise(function (resolve, reject) {
      try {
        return self._excute(option, function (err, data) {
          if (err) {
            return reject(err);
          }
          return resolve(data);
        });
      } catch (err) {
        return reject(err);
      }
    });
  }
  try {
    return self._excute(option, cb);
  } catch (err) {
    return cb(err);
  }
};

/**
 * Excute the method.
 *
 * @param option
 * {
 *  method: String
 *  args: Array
 *  timeout: number
 * }
 * @param {Function} cb
 * @private
 */
Invoker.prototype._excute = function (option, cb) {
  var self = this;
  var method = option.method;
  var args = option.args;
  var reConnect = false;
  var codec = new Codec();

  var fetchData = function (err, provider) {
    if (err) {
      cb(err);
      return;
    }

    if (!~provider.methods.indexOf(method)) {
      cb(new Error('Can\'t find the method:' + method
        + ' form provider: ' + provider.host + ':' + provider.port
        + ', please check it!'));
      return;
    }

    var pool = provider.pool;
    var buffer = codec.encodeRequest(self, provider, method, args);

    var resource = pool.acquire();
    resource.then(function (client) {
      client.once('error', function (e) {
        error(err);
        client.removeAllListeners('data');
        pool.release(client);

        if (!reConnect) {
          reConnect = true;
          setTimeout(function () {
            self._getChildren(fetchData);
          }, 1000);
        } else {
          cb(e);
        }
      });

      client.on('data', function (chunk) {
        if (codec.pushChunk(chunk)) {
          client.removeAllListeners('data');
          pool.release(client);
          codec.decodeResponse(cb);
        }
      });

      codec.clearChunk();
      client.write(buffer);
    }).catch(function (e) {
      cb(e);
    });
  };

  return self._getChildren(fetchData);
};

/**
 * 获取注册中心 Providers 和 Configurators 信息
 *
 * @param cb
 * @private
 */
Invoker.prototype._getChildren = function (cb) {
  var self = this;
  Promise.all([
    this._getProviders(),
    this._getConfigurators()
  ])
    .then(function () {
      if (!self._providers || 0 >= self._providers.length) {
        throw new Error('Can\'t find children from the node: ' + self._path +
          ' ,please check the path!');
      }

      var providers = [];
      self._providers.forEach(function (item) {
        var configurator = self._configurators[item.host + ':' + item.port];
        providers.push(Object.assign({}, item, configurator));
      });

      var provider;
      provider = self._loadBalance.select(providers);
      if (!provider.pool) {
        provider.pool = createPool(provider.host, provider.port, {
          max: self._poolMax,
          min: self._poolMin
        });
      }

      cb(false, provider);
    })
    .catch(function (err) {
      cb(err);
    });
};

/**
 * 获取注册中心 Providers 信息
 *
 * @returns {Promise}
 * @private
 */
Invoker.prototype._getProviders = function () {
  var self = this;
  return new Promise(function (resolve, reject) {
    if (self._uris) {
      self._parseProviders(self._uris);
      self._uris = null;
    }
    if (self._registerProviders && self._registerProviders.isUnwatched()) {
      self._registerProviders.getChildren(function (err, uris) {
        if (err) return reject(err);
        var providers;
        try {
          providers = self._parseProviders(uris);
        } catch (e) {
          return reject(e);
        }
        return resolve(providers);
      });
    } else {
      resolve(self._providers);
    }
  });
};

/**
 * 获取注册中心 Configurators 信息
 *
 * @returns {Promise}
 * @private
 */
Invoker.prototype._getConfigurators = function () {
  var self = this;
  return new Promise(function (resolve, reject) {
    if (self._registerConfigurators && self._registerConfigurators.isUnwatched()) {
      self._registerConfigurators.getChildren(function (err, uris) {
        if (err) return reject(err);
        var configurators;
        try {
          configurators = self._parseConfigurators(uris);
        } catch (e) {
          return reject(e);
        }
        return resolve(configurators);
      });
    } else {
      resolve(self._configurators);
    }
  });
};

/**
 * 解析 Providers 的 URIs
 *
 * @param uris
 * @returns {Array}
 * @private
 */
Invoker.prototype._parseProviders = function (uris) {
  var self = this;
  var providers = [];
  (uris || []).forEach(function (uri) {
    var provider;
    try {
      var parsed = url.parse(uri);
      provider = querystring.parse(parsed.query);
      provider.methods = provider.methods.split(',');
      provider.host = parsed.hostname;
      if (parsed.port) provider.port = parseInt(parsed.port, 10);
      if (provider.weight) provider.weight = parseInt(provider.weight, 10);
      if (provider.warmup) provider.warmup = parseInt(provider.warmup, 10);
      if (provider.timestamp) provider.timestamp = parseInt(provider.timestamp, 10);

      (self._providers || []).forEach(function (item) {
        if (item.host === provider.host && item.port === provider.port) {
          provider.pool = item.pool;
        } else if (item.pool) {
          item.pool.drain().then(function () {
            item.pool.clear();
          });
        }
      });
    } catch (err) {
      warn('Parse children of providers error. uri: ' + uri);
      return;
    }
    providers.push(provider);
  });
  self._providers = providers;
  return providers;
};

/**
 * 解析 Configurators 的 URIs
 *
 * @param uris
 * @returns {Object}
 * @private
 */
Invoker.prototype._parseConfigurators = function (uris) {
  var configurators = {};
  (uris || []).forEach(function (uri) {
    var configurator;
    try {
      var parsed = url.parse(uri);
      configurator = querystring.parse(parsed.query);
      configurator.host = parsed.hostname;
      if (parsed.port) configurator.port = parseInt(parsed.port, 10);
      if (configurator.weight) configurator.weight = parseInt(configurator.weight, 10);
      if (!configurator.enabled || 'false' === configurator.enabled) return;
    } catch (err) {
      warn('Parse children of configurators error. uri: ' + uri);
      return;
    }
    configurators[configurator.host + ':' + configurator.port] = configurator;
  });
  this._configurators = configurators;
  return configurators;
};

/**
 * Expose `Invoker`.
 *
 * @type {Invoker}
 */
module.exports = Invoker;
<commit_msg>fix issues #9<commit_after>/**
 * Created by Corey600 on 2016/6/15.
 */

'use strict';

// require core modules
var url = require('url');
var querystring = require('querystring');

// require thirdpart modules
var debug = require('debug');

// require custom modules
var createPool = require('./pool');
var Codec = require('./codec');
var Register = require('./register');
var RandomLoadBalance = require('./loadbalance/RandomLoadBalance');

// print debug info
var error = debug('zoodubbo:error');
var warn = debug('zoodubbo:warn');

/**
 * Constructor of Invoker.
 *
 * @param {Client|String|Array} zk // The ZD instance or the uri of providers.
 * @param {Object} opt
 * {
 *  path: String // The path of service node.
 *  version: String, // The version of service.
 *  timeout: Number, // Timeout in milliseconds, defaults to 60 seconds.
 * }
 * @returns {Invoker}
 * @constructor
 */
function Invoker(zk, opt) {
  if (!(this instanceof Invoker)) return new Invoker(zk, opt);

  var option = opt || {};
  this._path = option.path;
  this._dubbo = option.dubbo;
  this._version = option.version;
  this._timeout = option.timeout;
  this._poolMax = option.poolMax;
  this._poolMin = option.poolMin;

  this._providers = null; // Array
  this._configurators = null; // Object
  this._loadBalance = new RandomLoadBalance();

  this._parseProviders = this._parseProviders.bind(this);
  this._parseConfigurators = this._parseConfigurators.bind(this);

  if ('string' === typeof zk) {
    this._uris = [zk];
  } else if (Array.isArray(zk)) {
    this._uris = zk;
  } else if (zk) {
    this._registerProviders = new Register(zk, '/dubbo/' + this._path + '/providers');
    this._registerConfigurators = new Register(zk, '/dubbo/' + this._path + '/configurators');
    this._registerProviders.on('change', this._parseProviders);
    this._registerConfigurators.on('change', this._parseConfigurators);
  }
}

/**
 * Excute the method
 *
 * @param {String} method
 * @param {Array|Object} args
 * {
 *  args: Array
 *  timeout: number
 * }
 * @param {Function} [cb]
 * @public
 */
Invoker.prototype.excute = function (method, args, cb) {
  var option = args;
  if (Array.isArray(option)) {
    option = { args: option };
  }
  option.method = method;

  var self = this;
  if ('function' !== typeof cb) {
    return new Promise(function (resolve, reject) {
      try {
        return self._excute(option, function (err, data) {
          if (err) {
            return reject(err);
          }
          return resolve(data);
        });
      } catch (err) {
        return reject(err);
      }
    });
  }
  try {
    return self._excute(option, cb);
  } catch (err) {
    return cb(err);
  }
};

/**
 * Excute the method.
 *
 * @param option
 * {
 *  method: String
 *  args: Array
 *  timeout: number
 * }
 * @param {Function} cb
 * @private
 */
Invoker.prototype._excute = function (option, cb) {
  var self = this;
  var method = option.method;
  var args = option.args;
  var reConnect = false;
  var codec = new Codec();

  var fetchData = function (err, provider) {
    if (err) {
      cb(err);
      return;
    }

    if (!~provider.methods.indexOf(method)) {
      cb(new Error('Can\'t find the method:' + method
        + ' form provider: ' + provider.host + ':' + provider.port
        + ', please check it!'));
      return;
    }

    var pool = provider.pool;
    var buffer = codec.encodeRequest(self, provider, method, args);

    var resource = pool.acquire();
    resource.then(function (client) {
      client.once('error', function (e) {
        error(err);
        client.removeAllListeners('data');
        pool.release(client);

        if (!reConnect) {
          reConnect = true;
          setTimeout(function () {
            self._getChildren(fetchData);
          }, 1000);
        } else {
          cb(e);
        }
      });

      client.on('data', function (chunk) {
        if (codec.pushChunk(chunk)) {
          client.removeAllListeners('data');
          pool.release(client);
          codec.decodeResponse(cb);
        }
      });

      codec.clearChunk();
      client.write(buffer);
    }).catch(function (e) {
      cb(e);
    });
  };

  return self._getChildren(fetchData);
};

/**
 * 获取注册中心 Providers 和 Configurators 信息
 *
 * @param cb
 * @private
 */
Invoker.prototype._getChildren = function (cb) {
  var self = this;
  Promise.all([
    this._getProviders(),
    this._getConfigurators()
  ])
    .then(function () {
      if (!self._providers || 0 >= self._providers.length) {
        throw new Error('Can\'t find children from the node: ' + self._path +
          ' ,please check the path!');
      }

      var providers = [];
      self._providers.forEach(function (item) {
        var configurator = self._configurators
          ? self._configurators[item.host + ':' + item.port]
          : {};
        providers.push(Object.assign({}, item, configurator));
      });

      var provider;
      provider = self._loadBalance.select(providers);
      if (!provider.pool) {
        provider.pool = createPool(provider.host, provider.port, {
          max: self._poolMax,
          min: self._poolMin
        });
      }

      cb(false, provider);
    })
    .catch(function (err) {
      cb(err);
    });
};

/**
 * 获取注册中心 Providers 信息
 *
 * @returns {Promise}
 * @private
 */
Invoker.prototype._getProviders = function () {
  var self = this;
  return new Promise(function (resolve, reject) {
    if (self._uris) {
      self._parseProviders(self._uris);
      self._uris = null;
    }
    if (self._registerProviders && self._registerProviders.isUnwatched()) {
      self._registerProviders.getChildren(function (err, uris) {
        if (err) return reject(err);
        var providers;
        try {
          providers = self._parseProviders(uris);
        } catch (e) {
          return reject(e);
        }
        return resolve(providers);
      });
    } else {
      resolve(self._providers);
    }
  });
};

/**
 * 获取注册中心 Configurators 信息
 *
 * @returns {Promise}
 * @private
 */
Invoker.prototype._getConfigurators = function () {
  var self = this;
  return new Promise(function (resolve, reject) {
    if (self._registerConfigurators && self._registerConfigurators.isUnwatched()) {
      self._registerConfigurators.getChildren(function (err, uris) {
        if (err) return reject(err);
        var configurators;
        try {
          configurators = self._parseConfigurators(uris);
        } catch (e) {
          return reject(e);
        }
        return resolve(configurators);
      });
    } else {
      resolve(self._configurators);
    }
  });
};

/**
 * 解析 Providers 的 URIs
 *
 * @param uris
 * @returns {Array}
 * @private
 */
Invoker.prototype._parseProviders = function (uris) {
  var self = this;
  var providers = [];
  (uris || []).forEach(function (uri) {
    var provider;
    try {
      var parsed = url.parse(uri);
      provider = querystring.parse(parsed.query);
      provider.methods = provider.methods.split(',');
      provider.host = parsed.hostname;
      if (parsed.port) provider.port = parseInt(parsed.port, 10);
      if (provider.weight) provider.weight = parseInt(provider.weight, 10);
      if (provider.warmup) provider.warmup = parseInt(provider.warmup, 10);
      if (provider.timestamp) provider.timestamp = parseInt(provider.timestamp, 10);

      (self._providers || []).forEach(function (item) {
        if (item.host === provider.host && item.port === provider.port) {
          provider.pool = item.pool;
        } else if (item.pool) {
          item.pool.drain().then(function () {
            item.pool.clear();
          });
        }
      });
    } catch (err) {
      warn('Parse children of providers error. uri: ' + uri);
      return;
    }
    providers.push(provider);
  });
  self._providers = providers;
  return providers;
};

/**
 * 解析 Configurators 的 URIs
 *
 * @param uris
 * @returns {Object}
 * @private
 */
Invoker.prototype._parseConfigurators = function (uris) {
  var configurators = {};
  (uris || []).forEach(function (uri) {
    var configurator;
    try {
      var parsed = url.parse(uri);
      configurator = querystring.parse(parsed.query);
      configurator.host = parsed.hostname;
      if (parsed.port) configurator.port = parseInt(parsed.port, 10);
      if (configurator.weight) configurator.weight = parseInt(configurator.weight, 10);
      if (!configurator.enabled || 'false' === configurator.enabled) return;
    } catch (err) {
      warn('Parse children of configurators error. uri: ' + uri);
      return;
    }
    configurators[configurator.host + ':' + configurator.port] = configurator;
  });
  this._configurators = configurators;
  return configurators;
};

/**
 * Expose `Invoker`.
 *
 * @type {Invoker}
 */
module.exports = Invoker;
<|endoftext|>


<commit_before>var assert = require('assert')

var captureRE = /\[([A-Za-z_][A-Za-z_0-9]*)\]/g

function mergeCaptures(current, next, noOverwrite) {
    for (var key in next) {
        if (!next.hasOwnProperty(key)) continue
        if (current.hasOwnProperty(key) && noOverwrite)
            assert.deepEqual(current[key], next[key], "Got mismatching results for capture [" + key + "]")

        current[key] = next[key]
    }
    return current
}

function JSONExp(src, options) {
    var self = function() {
      return self.exec.apply(self, arguments)
    }

    self.src     = src
    self.pattern = JSONExp.parse(src)
    self.options = options || {}

    self.__proto__ = JSONExp.prototype
    self.constructor = JSONExp

    return self
}

var specials = function(obj) {
    var opts = {}

    if (obj.hasOwnProperty(specials.glob))    opts.glob    = obj[specials.glob]
    if (obj.hasOwnProperty(specials.capture)) opts.capture = obj[specials.capture]
    if (obj.hasOwnProperty(specials.any))     opts.any     = obj[specials.any]
    if (obj.hasOwnProperty(specials.many))    opts.many    = obj[specials.many]

    return opts
}

specials.glob    = "$JSONExp GLOB"
specials.capture = "$JSONExp CAPTURE"
specials.any     = "$JSONExp ANY"
specials.many    = "$JSONExp MANY"

function unicodeEscape(str) {
    var output = '', code
    for (var i = 0; i < str.length; i++) {
        code = str.charCodeAt(i).toString(16)
        while (code.length < 4) code = '0' + code
        output += '\\u' + code
    }
    return output
}

function escapeString(match, contents) {
    return '"' + unicodeEscape(contents) + '"'
}

JSONExp.preprocess = function(src) {
    return src
        .replace(/"(.*?[^\\])"/g, escapeString)
        .replace(/\*\*/g,   '{ "' + specials.many    + '": true }')
        .replace(/\*/g,     '{ "' + specials.any     + '": true }')
        .replace(/\.\.\./g,   '"' + specials.glob    + '": true')
        .replace(captureRE, '{ "' + specials.capture + '": "$1" }')
}

JSONExp.parse = function(src) {
    return JSON.parse(JSONExp.preprocess(src))
}

JSONExp.prototype = new Function

JSONExp.prototype.exec = function(obj, options) {
    try {
        var match = this.assert(obj, options)
        return match
    } catch (e) {
        if (!(e instanceof assert.AssertionError)) throw e
        return null
    }
}

JSONExp.prototype.assert = function(obj, options) {
    // tests obj for matching, throws assertion errors if mismatched
    options = options || {}
    if (!options.namespace) options.namespace = {}

    var match = this._match(this.pattern, obj, options, '/')

    if (options.merge) mergeCaptures(options.namespace, match, true)

    return match
}

JSONExp.prototype.test = function(obj, options) {
    return !!this.exec(obj, options)
}

JSONExp.prototype._match = function(pattern, given, options, path) {
    var match = {}

    function test(a, b, message) {
        assert.equal(a, b, "At key '" + path + "': expected " + a + ", got " + b)
    }

    if ('object' == typeof pattern && pattern) {
        if (specials(pattern).capture) {
            match[specials(pattern).capture] = given
        } else if (specials(pattern).any) {
            // anything will do
        } else {
            test(typeof pattern, typeof given)

            var globbing = false
            if (specials(pattern).glob) {
                delete pattern[specials.glob]
                globbing = true
            }

            assert.ok(given, "Got null at " + path)
            assert.equal(given.constructor, pattern.constructor, "Not the same type")

            for (var key in pattern) {
                if (!pattern.hasOwnProperty(key)) continue
                assert.ok(given.hasOwnProperty(key), "At " + path + ", key '" + key + "' not found")
                var m = this._match(pattern[key], given[key], options, path + '/' + key)
                mergeCaptures(match, m)
            }

            if (!globbing) {
                for (var key in given) {
                    if (!given.hasOwnProperty(key)) continue
                    assert.ok(pattern.hasOwnProperty(key), "At " + path + ", unexpected key \"" + key + "\": " + JSON.stringify(given[key]) )
                }
            }
        }
    } else { // non-objects
        test(typeof pattern, typeof given)
        test(pattern, given)
    }
    return match
}

JSONExp.prototype.toString = function() {
    return '' + this.src
}

JSONExp.prototype.compile = function() {
    // precompile for better performance
    console.log("precompiling not yet implemented")

    /* Someday:
    this.exec = function() {
        // precompiled version
    }
    */
    return this
}

JSONExp.compile = function(pattern, options) {
    var exp = new JSONExp(pattern, options)
    return exp.compile()
}

module.exports = JSONExp
<commit_msg>Minor code rearrangement<commit_after>var assert = require('assert')

var captureRE = /\[([A-Za-z_][A-Za-z_0-9]*)\]/g

function mergeCaptures(current, next, noOverwrite) {
    for (var key in next) {
        if (!next.hasOwnProperty(key)) continue
        if (current.hasOwnProperty(key) && noOverwrite)
            assert.deepEqual(current[key], next[key], "Got mismatching results for capture [" + key + "]")

        current[key] = next[key]
    }
    return current
}

function JSONExp(src, options) {
    var self = function() {
      return self.exec.apply(self, arguments)
    }

    self.src     = src
    self.pattern = JSONExp.parse(src)
    self.options = options || {}

    self.__proto__ = JSONExp.prototype
    self.constructor = JSONExp

    return self
}

var specials = function(obj) {
    var opts = {}

    if (obj.hasOwnProperty(specials.glob))    opts.glob    = obj[specials.glob]
    if (obj.hasOwnProperty(specials.capture)) opts.capture = obj[specials.capture]
    if (obj.hasOwnProperty(specials.any))     opts.any     = obj[specials.any]
    if (obj.hasOwnProperty(specials.many))    opts.many    = obj[specials.many]

    return opts
}

specials.glob    = "$JSONExp GLOB"
specials.capture = "$JSONExp CAPTURE"
specials.any     = "$JSONExp ANY"
specials.many    = "$JSONExp MANY"

function unicodeEscape(str) {
    var output = '', code
    for (var i = 0; i < str.length; i++) {
        code = str.charCodeAt(i).toString(16)
        while (code.length < 4) code = '0' + code
        output += '\\u' + code
    }
    return output
}

function escapeString(match, contents) {
    return '"' + unicodeEscape(contents) + '"'
}

JSONExp.preprocess = function(src) {
    return src
        .replace(/"(.*?[^\\])"/g, escapeString)
        .replace(/\*\*/g,   '{ "' + specials.many    + '": true }')
        .replace(/\*/g,     '{ "' + specials.any     + '": true }')
        .replace(/\.\.\./g,   '"' + specials.glob    + '": true')
        .replace(captureRE, '{ "' + specials.capture + '": "$1" }')
}

JSONExp.parse = function(src) {
    return JSON.parse(JSONExp.preprocess(src))
}

JSONExp.prototype = new Function

JSONExp.prototype.exec = function(obj, options) {
    try {
        var match = this.assert(obj, options)
        return match
    } catch (e) {
        if (!(e instanceof assert.AssertionError)) throw e
        return null
    }
}

JSONExp.prototype.assert = function(obj, options) {
    // tests obj for matching, throws assertion errors if mismatched
    options = options || {}
    if (!options.namespace) options.namespace = {}

    var match = this._match(this.pattern, obj, options, '/')

    if (options.merge) mergeCaptures(options.namespace, match, true)

    return match
}

JSONExp.prototype.test = function(obj, options) {
    return !!this.exec(obj, options)
}

JSONExp.prototype._match = function(pattern, given, options, path) {
    var match = {}

    function test(a, b, message) {
        assert.equal(a, b, "At key '" + path + "': expected " + a + ", got " + b)
    }

    if ('object' == typeof pattern && pattern) {
        if (specials(pattern).capture) {
            match[specials(pattern).capture] = given
        } else if (specials(pattern).any) {
            // anything will do
        } else {
            test(typeof pattern, typeof given)

            if (specials(pattern).glob) {
                delete pattern[specials.glob]
            } else {
                for (var key in given) {
                    if (!given.hasOwnProperty(key)) continue
                    assert.ok(pattern.hasOwnProperty(key), "At " + path + ", unexpected key \"" + key + "\": " + JSON.stringify(given[key]) )
                }
            }

            assert.ok(given, "Got null at " + path)
            assert.equal(given.constructor, pattern.constructor, "Not the same type")

            for (var key in pattern) {
                if (!pattern.hasOwnProperty(key)) continue
                assert.ok(given.hasOwnProperty(key), "At " + path + ", key '" + key + "' not found")
                var m = this._match(pattern[key], given[key], options, path + '/' + key)
                mergeCaptures(match, m)
            }
        }
    } else { // non-objects
        test(typeof pattern, typeof given)
        test(pattern, given)
    }
    return match
}

JSONExp.prototype.toString = function() {
    return '' + this.src
}

JSONExp.prototype.compile = function() {
    // precompile for better performance
    console.log("precompiling not yet implemented")

    /* Someday:
    this.exec = function() {
        // precompiled version
    }
    */
    return this
}

JSONExp.compile = function(pattern, options) {
    var exp = new JSONExp(pattern, options)
    return exp.compile()
}

module.exports = JSONExp
<|endoftext|>