<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

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

var BIDI_RTL_LANGS = ['ar', 'fa', 'he'],
    translations = {},
    default_lang = 'en-US',
    default_locale = 'en_US';

function gettext(sid, locale) {
  if (translations[locale][sid] && translations[locale][sid].length) {
    return translations[locale][sid];
  }
  // Return the default lang's string if missing in the translation.
  return (translations[default_locale][sid]) || sid;
}

function qualityCmp(a, b) {
  if (a.quality === b.quality) {
    return 0;
  } else if (a.quality < b.quality) {
    return 1;
  } else {
    return -1;
  }
}

/**
 * Parses the HTTP accept-language header and returns a
 * sorted array of objects. Example object:
 * {
 *   lang: 'pl', quality: 0.7
 * }
 **/
function parseAcceptLanguage(header) {
  // pl,fr-FR;q=0.3,en-US;q=0.1
  if (! header || ! header.split) {
    return [];
  }
  var raw_langs = header.split(',');
  var langs = raw_langs.map(function(raw_lang) {
    var parts = raw_lang.split(';');
    var q = 1;
    if (parts.length > 1 && parts[1].indexOf('q=') === 0) {
      var qval = parseFloat(parts[1].split('=')[1]);
      if (isNaN(qval) === false) {
        q = qval;
      }
    }
    return { lang: parts[0].trim(), quality: q };
  });
  langs.sort(qualityCmp);
  return langs;
}

/**
 * Given the user's prefered languages and a list of currently
 * supported languages, returns the best match or a default language.
 * languages must be a sorted list, the first match is returned.
 **/
function bestLanguage(languages, supported_languages, defaultLanguage) {
  var lower = supported_languages.map(function(l) { return l.toLowerCase(); });
  for(var i=0; i < languages.length; i++) {
    var lq = languages[i];
    if (lower.indexOf(lq.lang.toLowerCase()) !== -1) {
      return lq.lang;
    // Issue#1128 match locale, even if region isn't supported
    } else if (lower.indexOf(lq.lang.split('-')[0].toLowerCase()) !== -1) {
      return lq.lang.split('-')[0];
    }
  }
  return defaultLanguage;
}

/**
 * Given a language code, return a locale code the OS understands.
 *
 * language: en-US
 * locale:   en_US
 **/
function localeFrom(language) {
  if (! language || ! language.split) {
    return "";
  }
  var parts = language.split('-');
  if (parts.length === 1) {
    return parts[0].toLowerCase();
  } else if (parts.length === 2) {
    return util.format('%s_%s', parts[0].toLowerCase(), parts[1].toUpperCase());
  } else if (parts.length === 3) {
    // sr-Cyrl-RS should be sr_RS
    return util.format('%s_%s', parts[0].toLowerCase(), parts[2].toUpperCase());
  } else {
    console.error(
      util.format("Unable to map a local from language code [%s]", language));
    return language;
  }
}

/**
 * Given a locale code, return a language code
 **/
function languageFrom(locale) {
  if (!locale || !locale.split) {
    return "";
  }
  var parts = locale.split('_');
  if (parts.length === 1) {
    return parts[0].toLowerCase();
  } else if (parts.length === 2) {
    return util.format('%s-%s', parts[0].toLowerCase(), parts[1].toUpperCase());
  } else if (parts.length === 3) {
    // sr_RS should be sr-RS
    return util.format('%s-%s', parts[0].toLowerCase(), parts[2].toUpperCase());
  } else {
    console.error(util.format("Unable to map a language from locale code [%s]", locale));
    return locale;
  }
}

/**
 * The format function provides string interpolation on the client and server side.
 * It can be used with either an object for named variables, or an array
 * of values for positional replacement.
 *
 * Named Example:
 * format("%(salutation)s %(place)s", {salutation: "Hello", place: "World"}, true);
 * Positional Example:
 * format("%s %s", ["Hello", "World"]);
 **/
exports.format = format = function(fmt, obj, named) {
  if (!fmt) return "";
  if (Array.isArray(obj) || named === false) {
    return fmt.replace(/%s/g, function(){return String(obj.shift());});
  } else if (typeof obj === 'object' || named === true) {
    return fmt.replace(/%\(\s*([^)]+)\s*\)s/g, function(m, v){
      return String(obj[v.trim()]);
    });
  } else {
    return fmt;
  }
};

/**
 * Returns the list of translations abide is currently configured to support.
 **/
exports.getLocales = function() {
  return Object.keys(translations);
};

/**
 * Returns a copy of the translated strings for the given language.
 **/
exports.getStrings = function(lang) {
  var locale = localeFrom(lang),
      strings = {};
  if (!translations[locale]) {
    return strings;
  }

  // Copy the translation pairs and return. We copy vs. simply returning
  // so we can maintain the translations internally, and count on them.
  // In order to get all strings (including those that exist in the default
  // lang but not a translation), we use the keys from the default lang.
  Object.keys(translations[default_locale]).forEach(function(key) {
    strings[key] = gettext(key, locale);
  });
  return strings;
};

/**
 * Middleware function for Express web apps, which deals with locales on
 * headers or URLs, and provides `gettext` and `format` to other middleware functions.
 */
exports.middleware = function(options) {
  options = options || {};
  options.supported_languages = options.supported_languages || ['en-US'];
  options.translation_directory = options.translation_directory || 'locale/';
  options.locale_on_url = !options.locale_on_url ? false : true;

  default_lang = options.default_lang || 'en-US';
  default_locale = localeFrom(options.default_lang);
  default_lang = options.default_lang;

  function messages_file_path(locale) {
    return path.resolve(path.join(__dirname, '..', '..', '..'),
                        options.translation_directory,
                        path.join(locale, 'messages.json'));
  }

  function parse_messages_file(locale) {
    return require(messages_file_path(locale));
  }

  options.supported_languages.forEach(function(lang) {
    var locale = localeFrom(lang);

    try {
      translations[locale] = parse_messages_file(locale);
    } catch (e) {
      console.error(util.format(
        'Bad locale=[%s] missing .json files in [%s]. See locale/README (%s)',
        locale, messages_file_path(locale), e
      ));
      throw msg;
    }
  });

  function checkUrlLocale(req) {
    if (!options.locale_on_url) {
      return;
    }

    // Given a URL, http://foo.com/ab/xyz/, we check to see if the first directory
    // is actually a locale we know about, and if so, we strip it out of the URL
    // (i.e., URL becomes http://foo.com/xyz/) and store that locale info on the
    // request's accept-header.
    var matches = req.url.match(/^\/([^\/]+)(\/|$)/);
    if (!(matches && matches[1])) {
      return;
    }

    // Look for a lang we know about, and if found, strip it off the URL so routes
    // continue to work. If we don't find it (i.e., comes back "unknown") then bail.
    // We do this so that we don't falsely consume more of the URL than we should
    // and stip things that aren't actually locales we know about.
    var lang = bestLanguage(parseAcceptLanguage(matches[1]),
                            options.supported_languages,
                            "unknown");
    if (lang === "unknown") {
      return;
    }

    req.url = req.url.replace(matches[0], '/');
    req.headers['accept-language'] = lang;
  }

  return function(req, resp, next) {
    checkUrlLocale(req);

    var langs = parseAcceptLanguage(req.headers['accept-language']),
        lang_dir,
        lang = bestLanguage(langs, options.supported_languages,
                            options.default_lang),
        debug_lang = options.debug_lang.toLowerCase(),
        locale,
        locals = {},
        gt;

    locals.lang = lang;

    // BIDI support, which direction does text flow?
    lang_dir = BIDI_RTL_LANGS.indexOf(lang) >= 0 ? 'rtl' : 'ltr';
    locals.lang_dir = lang_dir;
    req.lang = lang;

    locale = localeFrom(lang);

    locals.locale = locale;
    req.locale = locale;

    var formatFnName = 'format';
    if (!! locals.format || !! req.format) {
      if (!! options.format_fn_name) {
        formatFnName = options.format_fn_name;
      } else {
        console.error("It appears you are using middleware which " +
          "already sets a variable 'format' on either the request " +
          "or reponse. Please use format_fn_name in options to " +
          "override this setting.");
        throw new Error("Bad Config - override format_fn_name");
      }

    }
    locals[formatFnName] = format;
    req[formatFnName] = format;

    locals.setLocale = function(assignedLocale) {
      if (translations[assignedLocale]) {
        locale = assignedLocale;

        var newLocals = {};

        newLocals.locale = assignedLocale;
        req.locale = assignedLocale;

        newLocals.lang = languageFrom(assignedLocale);
        req.lang = newLocals.lang;

        newLocals.lang_dir = BIDI_RTL_LANGS.indexOf(newLocals.lang) >= 0 ? 'rtl' : 'ltr';
        req.lang_dir = newLocals.lang_dir;

        resp.locals(newLocals);
      }
    };
    req.setLocale = locals.setLocale;

    if (translations[locale]) {
      gt = function(sid) {
        return gettext(sid, locale);
      };
    } else {
      // default lang in a non gettext environment... fake it
      gt = function(a) { return a; };
    }
    req.gettext = gt;

    // resp.locals(string, value) doesn't seem to work with EJS
    resp.locals(locals);

    next();
  };
};
<commit_msg>Remove reference to debug_lang in i18n.js<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

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

var BIDI_RTL_LANGS = ['ar', 'fa', 'he'],
    translations = {},
    default_lang = 'en-US',
    default_locale = 'en_US';

function gettext(sid, locale) {
  if (translations[locale][sid] && translations[locale][sid].length) {
    return translations[locale][sid];
  }
  // Return the default lang's string if missing in the translation.
  return (translations[default_locale][sid]) || sid;
}

function qualityCmp(a, b) {
  if (a.quality === b.quality) {
    return 0;
  } else if (a.quality < b.quality) {
    return 1;
  } else {
    return -1;
  }
}

/**
 * Parses the HTTP accept-language header and returns a
 * sorted array of objects. Example object:
 * {
 *   lang: 'pl', quality: 0.7
 * }
 **/
function parseAcceptLanguage(header) {
  // pl,fr-FR;q=0.3,en-US;q=0.1
  if (! header || ! header.split) {
    return [];
  }
  var raw_langs = header.split(',');
  var langs = raw_langs.map(function(raw_lang) {
    var parts = raw_lang.split(';');
    var q = 1;
    if (parts.length > 1 && parts[1].indexOf('q=') === 0) {
      var qval = parseFloat(parts[1].split('=')[1]);
      if (isNaN(qval) === false) {
        q = qval;
      }
    }
    return { lang: parts[0].trim(), quality: q };
  });
  langs.sort(qualityCmp);
  return langs;
}

/**
 * Given the user's prefered languages and a list of currently
 * supported languages, returns the best match or a default language.
 * languages must be a sorted list, the first match is returned.
 **/
function bestLanguage(languages, supported_languages, defaultLanguage) {
  var lower = supported_languages.map(function(l) { return l.toLowerCase(); });
  for(var i=0; i < languages.length; i++) {
    var lq = languages[i];
    if (lower.indexOf(lq.lang.toLowerCase()) !== -1) {
      return lq.lang;
    // Issue#1128 match locale, even if region isn't supported
    } else if (lower.indexOf(lq.lang.split('-')[0].toLowerCase()) !== -1) {
      return lq.lang.split('-')[0];
    }
  }
  return defaultLanguage;
}

/**
 * Given a language code, return a locale code the OS understands.
 *
 * language: en-US
 * locale:   en_US
 **/
function localeFrom(language) {
  if (! language || ! language.split) {
    return "";
  }
  var parts = language.split('-');
  if (parts.length === 1) {
    return parts[0].toLowerCase();
  } else if (parts.length === 2) {
    return util.format('%s_%s', parts[0].toLowerCase(), parts[1].toUpperCase());
  } else if (parts.length === 3) {
    // sr-Cyrl-RS should be sr_RS
    return util.format('%s_%s', parts[0].toLowerCase(), parts[2].toUpperCase());
  } else {
    console.error(
      util.format("Unable to map a local from language code [%s]", language));
    return language;
  }
}

/**
 * Given a locale code, return a language code
 **/
function languageFrom(locale) {
  if (!locale || !locale.split) {
    return "";
  }
  var parts = locale.split('_');
  if (parts.length === 1) {
    return parts[0].toLowerCase();
  } else if (parts.length === 2) {
    return util.format('%s-%s', parts[0].toLowerCase(), parts[1].toUpperCase());
  } else if (parts.length === 3) {
    // sr_RS should be sr-RS
    return util.format('%s-%s', parts[0].toLowerCase(), parts[2].toUpperCase());
  } else {
    console.error(util.format("Unable to map a language from locale code [%s]", locale));
    return locale;
  }
}

/**
 * The format function provides string interpolation on the client and server side.
 * It can be used with either an object for named variables, or an array
 * of values for positional replacement.
 *
 * Named Example:
 * format("%(salutation)s %(place)s", {salutation: "Hello", place: "World"}, true);
 * Positional Example:
 * format("%s %s", ["Hello", "World"]);
 **/
exports.format = format = function(fmt, obj, named) {
  if (!fmt) return "";
  if (Array.isArray(obj) || named === false) {
    return fmt.replace(/%s/g, function(){return String(obj.shift());});
  } else if (typeof obj === 'object' || named === true) {
    return fmt.replace(/%\(\s*([^)]+)\s*\)s/g, function(m, v){
      return String(obj[v.trim()]);
    });
  } else {
    return fmt;
  }
};

/**
 * Returns the list of translations abide is currently configured to support.
 **/
exports.getLocales = function() {
  return Object.keys(translations);
};

/**
 * Returns a copy of the translated strings for the given language.
 **/
exports.getStrings = function(lang) {
  var locale = localeFrom(lang),
      strings = {};
  if (!translations[locale]) {
    return strings;
  }

  // Copy the translation pairs and return. We copy vs. simply returning
  // so we can maintain the translations internally, and count on them.
  // In order to get all strings (including those that exist in the default
  // lang but not a translation), we use the keys from the default lang.
  Object.keys(translations[default_locale]).forEach(function(key) {
    strings[key] = gettext(key, locale);
  });
  return strings;
};

/**
 * Middleware function for Express web apps, which deals with locales on
 * headers or URLs, and provides `gettext` and `format` to other middleware functions.
 */
exports.middleware = function(options) {
  options = options || {};
  options.supported_languages = options.supported_languages || ['en-US'];
  options.translation_directory = options.translation_directory || 'locale/';
  options.locale_on_url = !options.locale_on_url ? false : true;

  default_lang = options.default_lang || 'en-US';
  default_locale = localeFrom(options.default_lang);
  default_lang = options.default_lang;

  function messages_file_path(locale) {
    return path.resolve(path.join(__dirname, '..', '..', '..'),
                        options.translation_directory,
                        path.join(locale, 'messages.json'));
  }

  function parse_messages_file(locale) {
    return require(messages_file_path(locale));
  }

  options.supported_languages.forEach(function(lang) {
    var locale = localeFrom(lang);

    try {
      translations[locale] = parse_messages_file(locale);
    } catch (e) {
      console.error(util.format(
        'Bad locale=[%s] missing .json files in [%s]. See locale/README (%s)',
        locale, messages_file_path(locale), e
      ));
      throw msg;
    }
  });

  function checkUrlLocale(req) {
    if (!options.locale_on_url) {
      return;
    }

    // Given a URL, http://foo.com/ab/xyz/, we check to see if the first directory
    // is actually a locale we know about, and if so, we strip it out of the URL
    // (i.e., URL becomes http://foo.com/xyz/) and store that locale info on the
    // request's accept-header.
    var matches = req.url.match(/^\/([^\/]+)(\/|$)/);
    if (!(matches && matches[1])) {
      return;
    }

    // Look for a lang we know about, and if found, strip it off the URL so routes
    // continue to work. If we don't find it (i.e., comes back "unknown") then bail.
    // We do this so that we don't falsely consume more of the URL than we should
    // and stip things that aren't actually locales we know about.
    var lang = bestLanguage(parseAcceptLanguage(matches[1]),
                            options.supported_languages,
                            "unknown");
    if (lang === "unknown") {
      return;
    }

    req.url = req.url.replace(matches[0], '/');
    req.headers['accept-language'] = lang;
  }

  return function(req, resp, next) {
    checkUrlLocale(req);

    var langs = parseAcceptLanguage(req.headers['accept-language']),
        lang_dir,
        lang = bestLanguage(langs, options.supported_languages,
                            options.default_lang),
        locale,
        locals = {},
        gt;

    locals.lang = lang;

    // BIDI support, which direction does text flow?
    lang_dir = BIDI_RTL_LANGS.indexOf(lang) >= 0 ? 'rtl' : 'ltr';
    locals.lang_dir = lang_dir;
    req.lang = lang;

    locale = localeFrom(lang);

    locals.locale = locale;
    req.locale = locale;

    var formatFnName = 'format';
    if (!! locals.format || !! req.format) {
      if (!! options.format_fn_name) {
        formatFnName = options.format_fn_name;
      } else {
        console.error("It appears you are using middleware which " +
          "already sets a variable 'format' on either the request " +
          "or reponse. Please use format_fn_name in options to " +
          "override this setting.");
        throw new Error("Bad Config - override format_fn_name");
      }

    }
    locals[formatFnName] = format;
    req[formatFnName] = format;

    locals.setLocale = function(assignedLocale) {
      if (translations[assignedLocale]) {
        locale = assignedLocale;

        var newLocals = {};

        newLocals.locale = assignedLocale;
        req.locale = assignedLocale;

        newLocals.lang = languageFrom(assignedLocale);
        req.lang = newLocals.lang;

        newLocals.lang_dir = BIDI_RTL_LANGS.indexOf(newLocals.lang) >= 0 ? 'rtl' : 'ltr';
        req.lang_dir = newLocals.lang_dir;

        resp.locals(newLocals);
      }
    };
    req.setLocale = locals.setLocale;

    if (translations[locale]) {
      gt = function(sid) {
        return gettext(sid, locale);
      };
    } else {
      // default lang in a non gettext environment... fake it
      gt = function(a) { return a; };
    }
    req.gettext = gt;

    // resp.locals(string, value) doesn't seem to work with EJS
    resp.locals(locals);

    next();
  };
};
<|endoftext|>


<commit_before>/**
 * dependencies
 */
var utils = require('./utils'),
	Translation = require('./translation'),
	debug = utils.debug;

/**
 * Auto-load bundled dependencies (plugins).
 */
I18n.prototype.strategies = utils.loadDependencies('strategy');
I18n.prototype.storages = utils.loadDependencies('storage');
I18n.prototype.storage = I18n.prototype.storages.file; //default storage: file

/**
 * module variables
 */
I18n.prototype.localeCache = {};
I18n.prototype.resMethods = ['t', 'tn', '__', '__n', 'getLocale', 'isPreferredLocale'];
I18n.prototype.defaultLocale = 'en';
I18n.prototype.extension = '.json';
I18n.prototype.directory = './locales';
I18n.prototype.tags;
I18n.prototype.ctx;

/**
 * Expose `I18n`.
 */
exports = module.exports = i18n;

function i18n(options) {
	return new I18n(options);
};

/**
 * I18n constructor function
 * @param {Object} options
 */

function I18n(options) {
	var self = this;
	options = options || {};

	// Put into dev or production mode
	I18n.prototype.devMode = process.env.NODE_ENV !== 'production';

	// Copy over options
	for (var prop in options) {
		I18n.prototype[prop] = options[prop];
	}

	debug("properties", this.excludeList, this.storage, this.locales);

	// get storage implementation
	if (options.storage) {
		storage = this.getStorage(options.storage);
	}

	this.ctx = this.getContexts();

	// get defined locale this.tags
	this.tags = this.getLocales();
	debug("this.tags", this.tags);

	// cache all locale definitions
	if (self.tags && self.tags.length && self.tags.length > 0) {
		self.tags.forEach(function(locale) {
			self.ctx.forEach(function(context) {
				var keyPath = utils.getKeyPath(locale, context);
				if (!utils.existsObj(self.localeCache, keyPath)) {
					var success = storage.read(locale.toLowerCase(), self.directory, context, self.localeCache, self.extension);
					if (!success) {
						// unable to read, so create that definition
						self.write(locale, context);
					}
				}
			});
		});

		// set the this.defaultLocale (first element of the locales option array, if not set explicitely as option)
		if (!this.defaultLocale) {
			this.defaultLocale = this.tags[0].toLowerCase();
		}
		if (!this.localeCache[this.defaultLocale]) {
			debug('Not a valid default locale: ' + this.defaultLocale);
		}

	}
	debug("localeCache", JSON.stringify(this.localeCache));

	return this;
};

/**
 * i18n middleware bundle: it adds the init and the pathRewrite middleware.
 * It adds helper function middleware as well as path rewrite middleware when the 'path' strategy is selecte.
 * It also adds the functions defined in resMethods to the res.locals object. e.g. res.locals.getLocale()
 * @param  {Object} app     express instance
 */
I18n.prototype.bind = function(app) {
	var self = this;
	if (!app) {
		return;
	}

	app.use(this.loc());

	// request path rewrite middleware
	if (this.getLocaleFrom && ~this.getLocaleFrom.join(' ').indexOf('path')) {
		app.use(this.pathRewrite());
	}

	app.use(this.urlTranslation());
};

/**
 * i18n Initialization middleware
 * @return {Object} Connect middleware
 */
I18n.prototype.loc = function() {
	var self = this;
	return function loc(req, res, next) {
		var translation = new Translation(self, req, res);
		req.i18n = translation;
		next();
	}
};

/**
 * i18n pathRewrite middleware for the path strategy.
 * @return {Object} Connect middleware
 */
I18n.prototype.pathRewrite = function() {
	var self = this;
	return Translation.pathRewrite(self);
};

I18n.prototype.urlTranslation = function() {
	var self = this;

	return function urlTranslation(req, res, next) {
		var translatedUrl = req.url;

		// split url segments
		var url = req.url.replace(/\//g, '.').replace(/\?/g, '.').replace(/\&/g, '.').replace(/\=/g, '.').replace(/\#/g, '.');
		var segments = url.split('.');
		var context = 'url/';
		debug('urlTranslation', url, req.i18n.locale, translatedUrl);

		// try translate each segment
		segments.forEach(function(segment) {
			var translation = req.i18n.translate(req.i18n.locale, context + segment);
			debug("segmentTranslation", req.i18n.locale, context + segment, translation);
			if (translation && 'string' == typeof translation) translatedUrl = translatedUrl.replace(segment, translation);
		})
		debug('urlTranslation after', translatedUrl);

		req.url = translatedUrl;
		next();
	}
};

I18n.prototype.getLocales = function() {
	if (this.tags && this.tags.forEach) return this.tags;
	if (this.locales && this.locales.forEach) return this.locales;
	return storage.getLocales(this.directory, this.extension);
};

I18n.prototype.getContexts = function() {
	var ctx;
	if (this.ctx && this.ctx.forEach) ctx = this.ctx;
	else if (this.contexts && this.contexts.forEach) ctx = this.contexts;
	else ctx = storage.getContexts(this.directory, this.contextSeparator);
	if (!ctx) ctx = [], ctx.push(this.contextSeparator);
	return ctx;
};

I18n.prototype.write = function(locale, context, value) {
	// write locale due to new locale entry
	if (this.devMode) {
		value = value || {};
		var keyPath = utils.getKeyPath(locale, context);

		if (!utils.existsObj(this.localeCache, keyPath)) {
			utils.setObj(this.localeCache, keyPath, value);
			debug("i18n.write.setObj", locale, context, keyPath);
		}
		debug("writeContext", locale, context, this.localeCache);
		storage.write(locale, this.directory, context, this.localeCache, this.extension);
	}
};

/**
 * Writes the locale definition from the `localeCache` to the file system for debugging.
 * This method is ment to be called from the application optionally for debug purposes.
 *
 * @param  {String} file   		The the file including the path to store the file. If it is not provided it defaults to localeDirectory/localeCache.json
 * @return {Boolean}             	`true`, if the operation was successful. Otherwise `false`
 */
I18n.prototype.writeLocaleCache = function(file) {
	var p = require('path'),
		fs = require('fs');

	file = file || this.directory + p.sep + 'localeCache' + this.extension;
	file = p.normalize(file);

	// create directory, if it does not exist
	var dir = p.dirname(file);
	if (!fs.existsSync(dir)) {
		debug('creating locales directory: ' + dir);
		mkdirp.sync(dir, 0755);
	}

	// writing to tmp and rename on success
	try {
		fs.writeFileSync(file, JSON.stringify(this.localeCache, null, '\t'), 'utf8');
	} catch (e) {
		debug('unexpected error writing localeCache (is ' + file + ' not writeable?): ', e);
		return false;
	}
	return true;
};

I18n.prototype.getStorage = function(storage) {
	// try loading the storage
	try {
		return utils.getDependency(this.storages, storage);
	} catch (err) {
		debug('storage could not be loaded', storage);
		// load the file storage (default)
		return utils.getDependency(this.storages, 'file');
	}
};<commit_msg>fixed global var problem<commit_after>/**
 * dependencies
 */
var utils = require('./utils'),
	Translation = require('./translation'),
	debug = utils.debug;

/**
 * Auto-load bundled dependencies (plugins).
 */
I18n.prototype.strategies = utils.loadDependencies('strategy');
I18n.prototype.storages = utils.loadDependencies('storage');
I18n.prototype.storage = I18n.prototype.storages.file; //default storage: file

/**
 * module variables
 */
I18n.prototype.localeCache = {};
I18n.prototype.resMethods = ['t', 'tn', '__', '__n', 'getLocale', 'isPreferredLocale'];
I18n.prototype.defaultLocale = 'en';
I18n.prototype.extension = '.json';
I18n.prototype.directory = './locales';
I18n.prototype.tags;
I18n.prototype.ctx;

/**
 * Expose `I18n`.
 */
exports = module.exports = i18n;

function i18n(options) {
	return new I18n(options);
};

/**
 * I18n constructor function
 * @param {Object} options
 */

function I18n(options) {
	var self = this;
	options = options || {};

	// Put into dev or production mode
	I18n.prototype.devMode = process.env.NODE_ENV !== 'production';

	// Copy over options
	for (var prop in options) {
		I18n.prototype[prop] = options[prop];
	}

	debug("properties", this.excludeList, this.storage, this.locales);

	// get storage implementation
	if (options.storage) {
		this.storage = this.getStorage(options.storage);
	} else {
		this.storage = this.storages.file; // default storage: file
	}

	this.ctx = this.getContexts();

	// get defined locale this.tags
	this.tags = this.getLocales();
	debug("this.tags", this.tags);

	// cache all locale definitions
	if (self.tags && self.tags.length && self.tags.length > 0) {
		self.tags.forEach(function(locale) {
			self.ctx.forEach(function(context) {
				var keyPath = utils.getKeyPath(locale, context);
				if (!utils.existsObj(self.localeCache, keyPath)) {
					var success = self.storage.read(locale.toLowerCase(), self.directory, context, self.localeCache, self.extension);
					if (!success) {
						// unable to read, so create that definition
						self.write(locale, context);
					}
				}
			});
		});

		// set the this.defaultLocale (first element of the locales option array, if not set explicitely as option)
		if (!this.defaultLocale) {
			this.defaultLocale = this.tags[0].toLowerCase();
		}
		if (!this.localeCache[this.defaultLocale]) {
			debug('Not a valid default locale: ' + this.defaultLocale);
		}

	}
	debug("localeCache", JSON.stringify(this.localeCache));

	return this;
};

/**
 * i18n middleware bundle: it adds the init and the pathRewrite middleware.
 * It adds helper function middleware as well as path rewrite middleware when the 'path' strategy is selecte.
 * It also adds the functions defined in resMethods to the res.locals object. e.g. res.locals.getLocale()
 * @param  {Object} app     express instance
 */
I18n.prototype.bind = function(app) {
	var self = this;
	if (!app) {
		return;
	}

	app.use(this.loc());

	// request path rewrite middleware
	if (this.getLocaleFrom && ~this.getLocaleFrom.join(' ').indexOf('path')) {
		app.use(this.pathRewrite());
	}

	app.use(this.urlTranslation());
};

/**
 * i18n Initialization middleware
 * @return {Object} Connect middleware
 */
I18n.prototype.loc = function() {
	var self = this;
	return function loc(req, res, next) {
		var translation = new Translation(self, req, res);
		req.i18n = translation;
		next();
	}
};

/**
 * i18n pathRewrite middleware for the path strategy.
 * @return {Object} Connect middleware
 */
I18n.prototype.pathRewrite = function() {
	var self = this;
	return Translation.pathRewrite(self);
};

I18n.prototype.urlTranslation = function() {
	var self = this;

	return function urlTranslation(req, res, next) {
		var translatedUrl = req.url;

		// split url segments
		var url = req.url.replace(/\//g, '.').replace(/\?/g, '.').replace(/\&/g, '.').replace(/\=/g, '.').replace(/\#/g, '.');
		var segments = url.split('.');
		var context = 'url/';
		debug('urlTranslation', url, req.i18n.locale, translatedUrl);

		// try translate each segment
		segments.forEach(function(segment) {
			var translation = req.i18n.translate(req.i18n.locale, context + segment);
			debug("segmentTranslation", req.i18n.locale, context + segment, translation);
			if (translation && 'string' == typeof translation) translatedUrl = translatedUrl.replace(segment, translation);
		})
		debug('urlTranslation after', translatedUrl);

		req.url = translatedUrl;
		next();
	}
};

I18n.prototype.getLocales = function() {
	if (this.tags && this.tags.forEach) return this.tags;
	if (this.locales && this.locales.forEach) return this.locales;
	return this.storage.getLocales(this.directory, this.extension);
};

I18n.prototype.getContexts = function() {
	var ctx;
	if (this.ctx && this.ctx.forEach) ctx = this.ctx;
	else if (this.contexts && this.contexts.forEach) ctx = this.contexts;
	else ctx = this.storage.getContexts(this.directory, this.contextSeparator);
	if (!ctx) ctx = [], ctx.push(this.contextSeparator);
	return ctx;
};

I18n.prototype.write = function(locale, context, value) {
	// write locale due to new locale entry
	if (this.devMode) {
		value = value || {};
		var keyPath = utils.getKeyPath(locale, context);

		if (!utils.existsObj(this.localeCache, keyPath)) {
			utils.setObj(this.localeCache, keyPath, value);
			debug("i18n.write.setObj", locale, context, keyPath);
		}
		debug("writeContext", locale, context, this.localeCache);
		this.storage.write(locale, this.directory, context, this.localeCache, this.extension);
	}
};

/**
 * Writes the locale definition from the `localeCache` to the file system for debugging.
 * This method is ment to be called from the application optionally for debug purposes.
 *
 * @param  {String} file   		The the file including the path to store the file. If it is not provided it defaults to localeDirectory/localeCache.json
 * @return {Boolean}             	`true`, if the operation was successful. Otherwise `false`
 */
I18n.prototype.writeLocaleCache = function(file) {
	var p = require('path'),
		fs = require('fs');

	file = file || this.directory + p.sep + 'localeCache' + this.extension;
	file = p.normalize(file);

	// create directory, if it does not exist
	var dir = p.dirname(file);
	if (!fs.existsSync(dir)) {
		debug('creating locales directory: ' + dir);
		mkdirp.sync(dir, 0755);
	}

	// writing to tmp and rename on success
	try {
		fs.writeFileSync(file, JSON.stringify(this.localeCache, null, '\t'), 'utf8');
	} catch (e) {
		debug('unexpected error writing localeCache (is ' + file + ' not writeable?): ', e);
		return false;
	}
	return true;
};

I18n.prototype.getStorage = function(storage) {
	// try loading the storage
	try {
		return utils.getDependency(this.storages, storage);
	} catch (err) {
		debug('storage could not be loaded', storage);
		// load the file storage (default)
		return utils.getDependency(this.storages, 'file');
	}
};<|endoftext|>


<commit_before>var PipeStream = require('pipestream');
var util = require('./util');
var config = require('./config');

function addErrorEvents(req, res) {
  var clientReq;
  req.on('dest', function(_req) {
    clientReq = _req.on('error', util.noop);
  }).on('error', abort).on('close', abort);

  res.on('src', function(_res) {
    _res.on('error', abort);
  }).on('error', abort);

  function abort(err) {
    if (clientReq) {
      if (clientReq.abort) {
        clientReq.abort();
      } else if (clientReq.destroy) {
        clientReq.destroy();
      }
    }
    res.destroy();
  }
}

function addTransforms(req, res) {
  var reqTextPipeStream, resZipPipeStream, resIconvPipeStream, svrRes;

  req.addTextTransform = function(transform) {
    if (!reqTextPipeStream) {
      reqTextPipeStream = util.getPipeIconvStream(req.headers, true);
      delete req.headers['content-length'];
      req.add(reqTextPipeStream);
    }
    reqTextPipeStream.add(transform);
    return req;
  };

  function initZipTransform() {
    if (!resZipPipeStream) {
      resZipPipeStream = new PipeStream();
      removeContentLength();
      res.add(function(src, next) {
        var pipeZipStream = util.getPipeZipStream(res.headers);
        pipeZipStream.addHead(resZipPipeStream);
        if (resIconvPipeStream) {
          var pipeIconvStream = util.getPipeIconvStream(res.headers);
          pipeIconvStream.add(resIconvPipeStream);
          pipeZipStream.add(pipeIconvStream);
        }
        next(src.pipe(pipeZipStream));
      });
    }

    return resZipPipeStream;
  }

  res.addZipTransform = function(transform, head, tail) {
    initZipTransform()[head ? 'addHead' : (tail ? 'addTail' : 'add')](transform);
    return res;
  };
  res.addTextTransform = function(transform, head, tail) {
    if (!resIconvPipeStream) {
      initZipTransform();
      resIconvPipeStream = new PipeStream();
    }
    resIconvPipeStream[head ? 'addHead' : (tail ? 'addTail' : 'add')](transform);
    return res;
  };

  res.on('src', function(_res) {
    svrRes = _res;
    removeContentLength();
  });

  function removeContentLength() {
    if (svrRes && (resZipPipeStream || resIconvPipeStream)) {
      delete svrRes.headers['content-length'];
    }
  }
}

module.exports = function(req, res, next) {
  PipeStream.wrapSrc(req);
  PipeStream.wrapDest(res);
  addTransforms(req, res);
  addErrorEvents(req, res);
  var socket = req.socket || {};
  socket[config.CLIENT_IP_HEAD] = req.clientIp
    = socket[config.CLIENT_IP_HEAD] || util.getClientIp(req) || '127.0.0.1';
  delete req.headers[config.CLIENT_IP_HEAD];
  if (req.headers[config.HTTPS_FIELD] || req.socket.isHttps) {//防止socket长连接导致新请求的头部无法加util.HTTPS_FIELD
    req.socket.isHttps = true;
    req.isHttps = true;
    delete req.headers[config.HTTPS_FIELD];
  }
  if (req.headers['proxy-connection']) {
    req.headers['connection'] = req.headers['proxy-connection'];
  }
  delete req.headers['proxy-connection'];

  var responsed;
  res.response = function(_res) {
    if (responsed) {
      return;
    }
    responsed = true;
    if (_res.realUrl) {
      req.realUrl = res.realUrl = _res.realUrl;
    }
    res.headers = _res.headers;
    res.trailers = _res.trailers;
    res.statusCode = _res.statusCode = util.getStatusCode(_res.statusCode);

    util.drain(req, function() {
      if (util.getStatusCode(_res.statusCode)) {
        res.writeHead(_res.statusCode, _res.headers);
        res.src(_res);
        _res.trailers && res.addTrailers(_res.trailers);
      } else {
        util.sendStatusCodeError(res, _res);
      }
    });
  };

  next();
};
<commit_msg>refactor: init.js<commit_after>var PipeStream = require('pipestream');
var util = require('./util');
var config = require('./config');

function addErrorEvents(req, res) {
  var clientReq;
  req.on('dest', function(_req) {
    clientReq = _req.on('error', util.noop);
  }).on('error', abort).on('close', abort);

  res.on('src', function(_res) {
    _res.on('error', abort);
  }).on('error', abort);

  function abort(err) {
    if (clientReq) {
      if (clientReq.abort) {
        clientReq.abort();
      } else if (clientReq.destroy) {
        clientReq.destroy();
      }
    }
    res.destroy();
  }
}

function addTransforms(req, res) {
  var reqTextPipeStream, resZipPipeStream, resIconvPipeStream, svrRes;

  req.addTextTransform = function(transform) {
    if (!reqTextPipeStream) {
      reqTextPipeStream = util.getPipeIconvStream(req.headers, true);
      delete req.headers['content-length'];
      req.add(reqTextPipeStream);
    }
    reqTextPipeStream.add(transform);
    return req;
  };

  function initResZipTransform() {
    if (!resZipPipeStream) {
      resZipPipeStream = new PipeStream();
      removeContentLength();
      res.add(function(src, next) {
        var pipeZipStream = util.getPipeZipStream(res.headers);
        pipeZipStream.addHead(resZipPipeStream);
        if (resIconvPipeStream) {
          var pipeIconvStream = util.getPipeIconvStream(res.headers);
          pipeIconvStream.add(resIconvPipeStream);
          pipeZipStream.add(pipeIconvStream);
        }
        next(src.pipe(pipeZipStream));
      });
    }

    return resZipPipeStream;
  }

  res.addZipTransform = function(transform, head, tail) {
    initResZipTransform()[head ? 'addHead' : (tail ? 'addTail' : 'add')](transform);
    return res;
  };
  res.addTextTransform = function(transform, head, tail) {
    if (!resIconvPipeStream) {
      initResZipTransform();
      resIconvPipeStream = new PipeStream();
    }
    resIconvPipeStream[head ? 'addHead' : (tail ? 'addTail' : 'add')](transform);
    return res;
  };

  res.on('src', function(_res) {
    svrRes = _res;
    removeContentLength();
  });

  function removeContentLength() {
    if (svrRes && (resZipPipeStream || resIconvPipeStream)) {
      delete svrRes.headers['content-length'];
    }
  }
}

module.exports = function(req, res, next) {
  PipeStream.wrapSrc(req);
  PipeStream.wrapDest(res);
  addTransforms(req, res);
  addErrorEvents(req, res);
  var socket = req.socket || {};
  socket[config.CLIENT_IP_HEAD] = req.clientIp
    = socket[config.CLIENT_IP_HEAD] || util.getClientIp(req) || '127.0.0.1';
  delete req.headers[config.CLIENT_IP_HEAD];
  if (req.headers[config.HTTPS_FIELD] || req.socket.isHttps) {//防止socket长连接导致新请求的头部无法加util.HTTPS_FIELD
    req.socket.isHttps = true;
    req.isHttps = true;
    delete req.headers[config.HTTPS_FIELD];
  }
  if (req.headers['proxy-connection']) {
    req.headers['connection'] = req.headers['proxy-connection'];
  }
  delete req.headers['proxy-connection'];

  var responsed;
  res.response = function(_res) {
    if (responsed) {
      return;
    }
    responsed = true;
    if (_res.realUrl) {
      req.realUrl = res.realUrl = _res.realUrl;
    }
    res.headers = _res.headers;
    res.trailers = _res.trailers;
    res.statusCode = _res.statusCode = util.getStatusCode(_res.statusCode);

    util.drain(req, function() {
      if (util.getStatusCode(_res.statusCode)) {
        res.writeHead(_res.statusCode, _res.headers);
        res.src(_res);
        _res.trailers && res.addTrailers(_res.trailers);
      } else {
        util.sendStatusCodeError(res, _res);
      }
    });
  };

  next();
};
<|endoftext|>


<commit_before>if (typeof define !== 'function') { var define = require('amdefine')(module);}

define([
    './compiler.js',
    './sprite.js',
    './ui.js',
    './utils.js'
    ], function(compiler, sprite, ui, utils) {

var spr_editor = $('#sprite-editor')[0];

var loader = new ui.SpriteLoader(spr_editor);

var ide = {
    files_opened: [],
    init: function(){
        ide.codemirror = CodeMirror.fromTextArea($("#asm")[0], {
            lineNumbers: true,
            matchBrackets: true,
            useCPP: true,
            mode: "text/x-asm"
            });
        ide.codemirror.on("change", scheduleUpdate);

        utils.default_open_file = utils.open_file;
        utils.open_file = ide.utils_open_file;

        utils.on_write_end = function (filename, url){
            add_status_msg('Build success', '' + filename + ' file is now available', 'success');
        };
    },
    filename: null,
    load_file: function(file){
        $.get(file, function(data) {
            var regex = /([a-z\d\-\/]+\/)([a-z\d\-\d]+\.(asm|s))/;
            var m  = regex.exec(file);
            if (m) {
                ide.files_opened = []; //
                utils.path = m[1];
                ide.filename = m[2];
                ide.codemirror.setValue(data);
                build();
                for (var f in ide.files_opened){
                    if (ide.files_opened[f].match(/\.chr$/)){
                        loader.load(ide.files_opened[f]);
                        break;
                    }
                }
                ide.codemirror.refresh();
            }
        });
    },
    utils_open_file: function(file){
        ide.files_opened.push(file);
        if (loader.file == file){
            return String.fromCharCode.apply(undefined, loader.sprites);
        } else {
            return utils.default_open_file(file);
        }
    }
};

ide.init();

function add_status_msg(name, msg, type){
    var div = $('
');
    div.addClass('alert');
    div.addClass('alert-'+type);
    div.append("" + name + " " + msg);
    $('#status').append(div);
}

function build(){
    clearTimeout(_idleTimer);
    $('#status').empty();
    try {
        var data = compiler.nes_compiler(ide.codemirror.getValue());
        var regex = /([a-z\-\d]+)(\.asm|\.s)$/;
        var m = regex.exec(ide.filename);
        if (m){
            utils.write_file(m[1]+'.nes', data);
        } else {
            console.log('regex error');
        }
        _nes.loadRom(data);
        _nes.start();
        add_status_msg('Succefully compiled','OK', 'success');
    } catch (e){
        for (var err in e){
            add_status_msg(e[err].type,e[err].message, 'error');
        }
    }
}

var _idleTimer = null;

function scheduleUpdate(){
    if (_idleTimer !== null){
        clearTimeout(_idleTimer);
    }
    _idleTimer = setTimeout(build, 3000);
}

var _nes;

function emulatorUI () {
    var parent = this;
    var UI = function(nes) {
        this.nes = nes;
        _nes = nes;
        this.screen = $('#nes-screen')[0];
        this.zoomed = false;

        this.canvasContext = this.screen.getContext('2d');
        this.canvasImageData = this.canvasContext.getImageData(0, 0, 256, 240);
        this.resetCanvas();
        /*
        TODO: by enabling it. The source editor stops working
        $(document).
            bind('keydown', function(evt) {
                _nes.keyboard.keyDown(evt);
            }).
            bind('keyup', function(evt) {
                _nes.keyboard.keyUp(evt);
            }).
            bind('keypress', function(evt) {
                _nes.keyboard.keyPress(evt);
            });
        */
        this.dynamicaudio = new DynamicAudio({
            swf: nes.opts.swfPath+'dynamicaudio.swf'
        });
    };

    UI.prototype = {
        resetCanvas: function() {
            this.canvasContext.fillStyle = 'black';
            this.canvasContext.fillRect(0, 0, 256, 240);
            for (var i = 3; i < this.canvasImageData.data.length-3; i += 4) {
                this.canvasImageData.data[i] = 0xFF;
            }
        },
        enable: function() {
        },
        updateStatus: function(s) {
        },
        writeAudio: function(samples) {
            return this.dynamicaudio.writeInt(samples);
        },
        writeFrame: function(buffer, prevBuffer) {
            var imageData = this.canvasImageData.data;
            var pixel, i, j;

            for (i=0; i<256*240; i++) {
                pixel = buffer[i];

                if (pixel != prevBuffer[i]) {
                    j = i*4;
                    imageData[j] = pixel & 0xFF;
                    imageData[j+1] = (pixel >> 8) & 0xFF;
                    imageData[j+2] = (pixel >> 16) & 0xFF;
                    prevBuffer[i] = pixel;
                }
            }
            this.canvasContext.putImageData(this.canvasImageData, 0, 0);
        }
    };
    return UI;
}

$(function() {
    new JSNES({
        'swfPath': '',
        'ui': emulatorUI()
    });
});

// window resize

function onResize() {
    var tab_content = $('.tab-content')[0];
    var left = tab_content.offsetLeft;
    var top = tab_content.offsetTop;
    var width = document.documentElement.clientWidth - left;
    var height = document.documentElement.clientHeight - top;
    tab_content.style.width = width + "px";
    tab_content.style.height = height + "px";
}

window.onresize = onResize;
onResize();

//sprite editor

var sprites = sprite.load_sprites('example/scrolling/mario.chr');

var options = {
    sprites: sprites,
    palette: [0x22,0x16,0x27,0x18],
    sprite_x: 8,
    sprite_y: 16
};

var pixel_editor = new ui.PixelEditor(spr_editor, 165, 0, options);
var selector = new ui.SpriteSelector(spr_editor, 440, 0, options);
var palette = new ui.Palette(spr_editor, 0 , 325, options);
var color_picker = new ui.ColorPicker(spr_editor, 165,270);
var preview = new ui.Preview(spr_editor, 0, 0, options);

pixel_editor.addColorChangeListener(palette);
palette.addColorChangeListener(selector);
palette.addColorChangeListener(preview);
palette.addColorChangeListener(pixel_editor);
color_picker.addColorChangeListener(palette);
selector.addPreviousPageButton("fast_backward.png", 440, 315);
selector.addNextPageButton("fast_forward.png", 475, 315);

//TODO selector sprite one by one
//TODO selector sprite 8 by 8

selector.addSpriteChangedListener(preview);
preview.addSpriteChangedListener(pixel_editor);
pixel_editor.addRedrawListener(preview);
pixel_editor.addRedrawListener(selector);

loader.addRedrawListener(selector);
loader.addRedrawListener(preview);
loader.addRedrawListener(pixel_editor);
loader.updater = build;
loader.addUpdateCompileButton("check.png", 510, 315);

function getCursorPosition(canvas, event) {
    var totalOffsetX = 0;
    var totalOffsetY = 0;
    var canvasX = 0;
    var canvasY = 0;

    var element = canvas;

    do {
        totalOffsetX += element.offsetLeft;
        totalOffsetY += element.offsetTop;
        element = element.offsetParent;
    }
    while (element !== null);

    canvasX = event.pageX - totalOffsetX;
    canvasY = event.pageY - totalOffsetY;

    canvasX = Math.round( canvasX * (canvas.width / canvas.offsetWidth) );
    canvasY = Math.round( canvasY * (canvas.height / canvas.offsetHeight) );

    return {x:canvasX, y:canvasY};
}

$('#sprite-editor').click(
    function(e) {
        var canvas = $(this)[0];
        var pos = getCursorPosition(canvas, e);
        var widgets = [pixel_editor, palette, preview, color_picker, selector, loader];
        for (var w in widgets){
            if (widgets[w].was_clicked(pos.x, pos.y)){
                widgets[w].click(pos.x, pos.y);
                break;
            }
        }
    }
);

//Bootstrap

$('#tabs li:eq(0) a').tab('show');
$('.dropdown-toggle').dropdown();

var AppRouter = Backbone.Router.extend({

  routes: {
    "example/:path/:file": "example",
    "source": "source",
    "sprites": "sprites"
  },

  example: function(path, file) {
    $('#tabs li:eq(0) a').tab('show');
    ide.load_file('example/'+path+'/'+file);
  },
  source: function() {
    $('#tabs li:eq(0) a').tab('show');
  },
  sprites: function(){
    $('#tabs li:eq(1) a').tab('show');
  },
  about: function(){
    $('#tabs li:eq(2) a').tab('show');
  }
});

var app_router = new AppRouter();

app_router.on('route:defaultRoute', function(actions) {
    alert(actions);
});

Backbone.history.start();

});
<commit_msg>fixed dynamicaudio loading<commit_after>if (typeof define !== 'function') { var define = require('amdefine')(module);}

define([
    './compiler.js',
    './sprite.js',
    './ui.js',
    './utils.js'
    ], function(compiler, sprite, ui, utils) {

var spr_editor = $('#sprite-editor')[0];

var loader = new ui.SpriteLoader(spr_editor);

var ide = {
    files_opened: [],
    init: function(){
        ide.codemirror = CodeMirror.fromTextArea($("#asm")[0], {
            lineNumbers: true,
            matchBrackets: true,
            useCPP: true,
            mode: "text/x-asm"
            });
        ide.codemirror.on("change", scheduleUpdate);

        utils.default_open_file = utils.open_file;
        utils.open_file = ide.utils_open_file;

        utils.on_write_end = function (filename, url){
            add_status_msg('Build success', '' + filename + ' file is now available', 'success');
        };
    },
    filename: null,
    load_file: function(file){
        $.get(file, function(data) {
            var regex = /([a-z\d\-\/]+\/)([a-z\d\-\d]+\.(asm|s))/;
            var m  = regex.exec(file);
            if (m) {
                ide.files_opened = []; //
                utils.path = m[1];
                ide.filename = m[2];
                ide.codemirror.setValue(data);
                build();
                for (var f in ide.files_opened){
                    if (ide.files_opened[f].match(/\.chr$/)){
                        loader.load(ide.files_opened[f]);
                        break;
                    }
                }
                ide.codemirror.refresh();
            }
        });
    },
    utils_open_file: function(file){
        ide.files_opened.push(file);
        if (loader.file == file){
            return String.fromCharCode.apply(undefined, loader.sprites);
        } else {
            return utils.default_open_file(file);
        }
    }
};

ide.init();

function add_status_msg(name, msg, type){
    var div = $('
');
    div.addClass('alert');
    div.addClass('alert-'+type);
    div.append("" + name + " " + msg);
    $('#status').append(div);
}

function build(){
    clearTimeout(_idleTimer);
    $('#status').empty();
    try {
        var data = compiler.nes_compiler(ide.codemirror.getValue());
        var regex = /([a-z\-\d]+)(\.asm|\.s)$/;
        var m = regex.exec(ide.filename);
        if (m){
            utils.write_file(m[1]+'.nes', data);
        } else {
            console.log('regex error');
        }
        _nes.loadRom(data);
        _nes.start();
        add_status_msg('Succefully compiled','OK', 'success');
    } catch (e){
        for (var err in e){
            add_status_msg(e[err].type,e[err].message, 'error');
        }
    }
}

var _idleTimer = null;

function scheduleUpdate(){
    if (_idleTimer !== null){
        clearTimeout(_idleTimer);
    }
    _idleTimer = setTimeout(build, 3000);
}

var _nes;

function emulatorUI () {
    var parent = this;
    var UI = function(nes) {
        this.nes = nes;
        _nes = nes;
        this.screen = $('#nes-screen')[0];
        this.zoomed = false;

        this.canvasContext = this.screen.getContext('2d');
        this.canvasImageData = this.canvasContext.getImageData(0, 0, 256, 240);
        this.resetCanvas();
        /*
        TODO: by enabling it. The source editor stops working
        $(document).
            bind('keydown', function(evt) {
                _nes.keyboard.keyDown(evt);
            }).
            bind('keyup', function(evt) {
                _nes.keyboard.keyUp(evt);
            }).
            bind('keypress', function(evt) {
                _nes.keyboard.keyPress(evt);
            });
        */
        this.dynamicaudio = new DynamicAudio({
            swf: nes.opts.swfPath+'/component/jsnes/swf/dynamicaudio.swf'
        });
    };

    UI.prototype = {
        resetCanvas: function() {
            this.canvasContext.fillStyle = 'black';
            this.canvasContext.fillRect(0, 0, 256, 240);
            for (var i = 3; i < this.canvasImageData.data.length-3; i += 4) {
                this.canvasImageData.data[i] = 0xFF;
            }
        },
        enable: function() {
        },
        updateStatus: function(s) {
        },
        writeAudio: function(samples) {
            return this.dynamicaudio.writeInt(samples);
        },
        writeFrame: function(buffer, prevBuffer) {
            var imageData = this.canvasImageData.data;
            var pixel, i, j;

            for (i=0; i<256*240; i++) {
                pixel = buffer[i];

                if (pixel != prevBuffer[i]) {
                    j = i*4;
                    imageData[j] = pixel & 0xFF;
                    imageData[j+1] = (pixel >> 8) & 0xFF;
                    imageData[j+2] = (pixel >> 16) & 0xFF;
                    prevBuffer[i] = pixel;
                }
            }
            this.canvasContext.putImageData(this.canvasImageData, 0, 0);
        }
    };
    return UI;
}

$(function() {
    new JSNES({
        'swfPath': '',
        'ui': emulatorUI()
    });
});

// window resize

function onResize() {
    var tab_content = $('.tab-content')[0];
    var left = tab_content.offsetLeft;
    var top = tab_content.offsetTop;
    var width = document.documentElement.clientWidth - left;
    var height = document.documentElement.clientHeight - top;
    tab_content.style.width = width + "px";
    tab_content.style.height = height + "px";
}

window.onresize = onResize;
onResize();

//sprite editor

var sprites = sprite.load_sprites('example/scrolling/mario.chr');

var options = {
    sprites: sprites,
    palette: [0x22,0x16,0x27,0x18],
    sprite_x: 8,
    sprite_y: 16
};

var pixel_editor = new ui.PixelEditor(spr_editor, 165, 0, options);
var selector = new ui.SpriteSelector(spr_editor, 440, 0, options);
var palette = new ui.Palette(spr_editor, 0 , 325, options);
var color_picker = new ui.ColorPicker(spr_editor, 165,270);
var preview = new ui.Preview(spr_editor, 0, 0, options);

pixel_editor.addColorChangeListener(palette);
palette.addColorChangeListener(selector);
palette.addColorChangeListener(preview);
palette.addColorChangeListener(pixel_editor);
color_picker.addColorChangeListener(palette);
selector.addPreviousPageButton("fast_backward.png", 440, 315);
selector.addNextPageButton("fast_forward.png", 475, 315);

//TODO selector sprite one by one
//TODO selector sprite 8 by 8

selector.addSpriteChangedListener(preview);
preview.addSpriteChangedListener(pixel_editor);
pixel_editor.addRedrawListener(preview);
pixel_editor.addRedrawListener(selector);

loader.addRedrawListener(selector);
loader.addRedrawListener(preview);
loader.addRedrawListener(pixel_editor);
loader.updater = build;
loader.addUpdateCompileButton("check.png", 510, 315);

function getCursorPosition(canvas, event) {
    var totalOffsetX = 0;
    var totalOffsetY = 0;
    var canvasX = 0;
    var canvasY = 0;

    var element = canvas;

    do {
        totalOffsetX += element.offsetLeft;
        totalOffsetY += element.offsetTop;
        element = element.offsetParent;
    }
    while (element !== null);

    canvasX = event.pageX - totalOffsetX;
    canvasY = event.pageY - totalOffsetY;

    canvasX = Math.round( canvasX * (canvas.width / canvas.offsetWidth) );
    canvasY = Math.round( canvasY * (canvas.height / canvas.offsetHeight) );

    return {x:canvasX, y:canvasY};
}

$('#sprite-editor').click(
    function(e) {
        var canvas = $(this)[0];
        var pos = getCursorPosition(canvas, e);
        var widgets = [pixel_editor, palette, preview, color_picker, selector, loader];
        for (var w in widgets){
            if (widgets[w].was_clicked(pos.x, pos.y)){
                widgets[w].click(pos.x, pos.y);
                break;
            }
        }
    }
);

//Bootstrap

$('#tabs li:eq(0) a').tab('show');
$('.dropdown-toggle').dropdown();

var AppRouter = Backbone.Router.extend({

  routes: {
    "example/:path/:file": "example",
    "source": "source",
    "sprites": "sprites"
  },

  example: function(path, file) {
    $('#tabs li:eq(0) a').tab('show');
    ide.load_file('example/'+path+'/'+file);
  },
  source: function() {
    $('#tabs li:eq(0) a').tab('show');
  },
  sprites: function(){
    $('#tabs li:eq(1) a').tab('show');
  },
  about: function(){
    $('#tabs li:eq(2) a').tab('show');
  }
});

var app_router = new AppRouter();

app_router.on('route:defaultRoute', function(actions) {
    alert(actions);
});

Backbone.history.start();

});
<|endoftext|>


<commit_before>'use strict';

var Persistence = require('./Persistence');

module.exports = function $initPersistence(context, config) {

    var _logger = context.getLogger('persistence');

    _logger.info('Persistence module booting...');

    if(!config.logger) config.logger = _logger;
    if(!config.dispatcher) config.dispatcher = context;

    var persistence = new Persistence(config);

    return new Promise(function(resolve, reject) {
        /*
         * Start our ORM framework. We will
         * autoload models from models directory,
         * when ready, we'll get notified.
         */
        persistence.connect().then((orm)=>{
            context.models = {};

            /*
             * Make all models available under
             * Model User will be available as:
             * context.models.user
             */
            context.models = persistence.collections;

            /*
             * Export using export name.
             * Model User will be available as:
             * context.models.User
             * Where User is User.exportName
             */
            persistence.export(persistence.collections, context.models);

            _logger.info('Persistence module ready.');
            // context.emit('persistence.ready');
            resolve(persistence);

        }).catch(function(err) {
            _logError(err);
            reject(err);
        });
    });

    function _logError(err){
        _logger.error('---------------------');
        _logger.error('ORM ERR');
        _logger.error(err.message);
        _logger.error(err.stack);
        _logger.error('---------------------');
    }
};
<commit_msg>decorate context with iterateModels function<commit_after>'use strict';

const Persistence = require('./Persistence');

module.exports = function $initPersistence(context, config) {

    const _logger = context.getLogger(context.moduleid);

    _logger.info('Persistence module booting...');

    if (!config.logger) config.logger = _logger;
    if (!config.dispatcher) config.dispatcher = context;

    const persistence = new Persistence(config);

    return new Promise((resolve, reject) => {
        /*
         * Start our ORM framework. We will
         * autoload models from models directory,
         * when ready, we'll get notified.
         */
        persistence.connect().then(orm => {
            context.models = {};

            /*
             * Make all models available under
             * Model User will be available as:
             * context.models.user
             */
            context.provide('models', persistence.collections);

            /**
             * Expose a function to iterate over all registered
             * entities.
             */
            context.provide('iterateModels', persistence.iterateModels);

            /*
             * Export using export name.
             * Model User will be available as:
             * context.models.User
             * Where User is User.exportName
             */
            persistence.export(persistence.collections, context.models);

            _logger.info('Persistence module ready.');
            resolve(persistence);

        }).catch(err => {
            _logError(err);
            reject(err);
        });
    });

    function _logError(err) {
        _logger.error('---------------------');
        _logger.error('ORM ERR');
        _logger.error(err.message);
        _logger.error(err.stack);
        _logger.error('---------------------');
    }
};<|endoftext|>


<commit_before>"use babel"

import { CompositeDisposable } from 'atom'
import { execFile } from 'child_process'
import pandocBin from 'pandoc-bin'
import targets from './targets'
import SelectPathView from './views/select-path-view'

const cache = {}

module.exports = {

  config: {
    pandocBinary: {
      description: 'Path to your `pandoc` binary. Default is the packaged version of pandoc.',
      type: 'string',
      default: ''
    }
  },

  activate() {
    if (atom.inDevMode() && !atom.inSpecMode()) {
      console.log('activate pandoc-convert')
    }

    this.subscriptions = new CompositeDisposable()
    this.selectPathView = new SelectPathView()

    Object.keys(targets).forEach(target => {
      const action = `pandoc-convert:${target.replace(/_/g, '-')}`

      this.subscriptions.add(atom.commands.add('atom-workspace', action, () => {

        const format = targets[target].format || target
        const ext = targets[target].ext
        const dpath = this.defaultOuputPath(ext)
        const ipath = this.inputPath()

        this.selectPathView.show(dpath, opath => {
          cache[dpath] = opath
          this.convert(format, ipath, opath)
        })
      }))
    })
  },

  deactivate() {
    this.subscriptions.dispose()
  },

  convert(format, ipath, opath) {

    const pandocPath = atom.config.get('pandoc-convert.pandocBinary') || pandocBin.path

    execFile(pandocPath, [
      '--standalone',
      `--to=${format}`,
      `--output=${opath}`,
      ipath
    ], (error) => {
      if (error) {
        atom.notifications.addError(`[pandoc-convert]<br>${error.message}`)
      } else {
        atom.notifications.addSuccess(`[pandoc-convert]<br>**Created:** \`${opath}\``)
      }
    })
  },

  inputPath(ext) {
    const editor = atom.workspace.getActiveTextEditor()
    return editor.getPath()
  },

  defaultOuputPath(ext) {
    const editor = atom.workspace.getActiveTextEditor()
    const dpath = `${editor.getPath()}.${ext}`

    if (cache[dpath]) {
      return cache[dpath]
    }

    return dpath
  }
}
<commit_msg>Better error messages<commit_after>"use babel"

import { existsSync } from 'fs'
import { CompositeDisposable } from 'atom'
import { execFile } from 'child_process'
import pandocBin from 'pandoc-bin'
import targets from './targets'
import SelectPathView from './views/select-path-view'

const cache = {}

module.exports = {

  config: {
    pandocBinary: {
      description: 'Path to your `pandoc` binary. Default is the packaged version of pandoc.',
      type: 'string',
      default: ''
    }
  },

  activate() {
    if (atom.inDevMode() && !atom.inSpecMode()) {
      console.log('activate pandoc-convert')
    }

    this.subscriptions = new CompositeDisposable()
    this.selectPathView = new SelectPathView()

    Object.keys(targets).forEach(target => {
      const action = `pandoc-convert:${target.replace(/_/g, '-')}`

      this.subscriptions.add(atom.commands.add('atom-workspace', action, () => {
        this.convertCommand(target)
      }))
    })
  },

  deactivate() {
    this.subscriptions.dispose()
  },

  convertCommand(target) {

    const editor = atom.workspace.getActiveTextEditor()

    if (!editor) {
      return this.error(`Current item is not an editor.`)
    }

    if (editor.isModified() || !editor.getPath()) {
      return this.error(`Text is modified. Please safe first.`)
    }

    const format = targets[target].format || target
    const ext = targets[target].ext
    const dpath = this.defaultOuputPath(ext)
    const ipath = editor.getPath()

    this.selectPathView.show(dpath, opath => {
      cache[dpath] = opath
      this.convert(format, ipath, opath)
    })
  },

  error(message) {
    atom.notifications.addError(`[pandoc-convert]<br>${message}`)
  },

  success(message) {
    atom.notifications.addSuccess(`[pandoc-convert]<br>${message}`)
  },

  convert(format, ipath, opath) {

    const pandocPath = atom.config.get('pandoc-convert.pandocBinary') || pandocBin.path

    if (!existsSync(pandocPath)) {
      return this.error(`Binary \`${pandocPath}\` does not exist.`)
    }

    execFile(pandocPath, [
      '--standalone',
      `--to=${format}`,
      `--output=${opath}`,
      ipath
    ], (error) => {
      if (error) {
        this.error(error.message)
      } else {
        this.success(`**Created:** \`${opath}\``)
      }
    })
  },

  defaultOuputPath(ext) {
    const editor = atom.workspace.getActiveTextEditor()
    const dpath = `${editor.getPath()}.${ext}`

    if (cache[dpath]) {
      return cache[dpath]
    }

    return dpath
  }
}
<|endoftext|>


<commit_before>var fs = require('fs')
var path = require('path')
var init = require('init-package-json')
var argv = require('minimist')(process.argv.slice(2))

module.exports = function () {
  argv.name = argv.name || argv._.slice(1)[0]
  var dir = path.join(process.cwd(), argv.name ? argv.name : '')

  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir)
  }

  init(dir, __dirname + '/init-input.js', {dir: dir, argv: argv},
    function (err) {
      if (err) throw err

      var deck = path.join(dir, '/deck.md')
      var readme = path.join(dir, '/README.md')

      fs.symlink(deck, readme, function () {
        console.log('symlink created')
      })
    })
}
<commit_msg>exit after symlink<commit_after>var fs = require('fs')
var path = require('path')
var init = require('init-package-json')
var argv = require('minimist')(process.argv.slice(2))

module.exports = function () {
  argv.name = argv.name || argv._.slice(1)[0]
  var dir = path.join(process.cwd(), argv.name ? argv.name : '')

  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir)
  }

  init(dir, __dirname + '/init-input.js', {dir: dir, argv: argv},
    function (err) {
      if (err) throw err

      var deck = path.join(dir, '/deck.md')
      var readme = path.join(dir, '/README.md')

      fs.symlinkSync(deck, readme)
      process.exit()

    })
}
<|endoftext|>


<commit_before>var nconf = require('nconf');
var ldap  = require('ldapjs');
var exit  = require('./exit');
var client, binder;
var crypto = require('./crypto');
var cb = require('cb');
var https = require('https');

function createConnection () {
  var clientOpts = {
    url: nconf.get("LDAP_URL")
  };

  if (nconf.get('LDAP_URL').toLowerCase().substr(0, 5) === 'ldaps') {
    clientOpts.tlsOptions = { ca: https.globalAgent.options.ca };
  }

  return ldap.createClient(clientOpts);
}

function initializeConnection () {
  var connection = createConnection();

  connection.on('close', function () {
    console.error('Connection to ldap was closed.');
    exit(1);
  });

  connection.heartbeat = function (callback) {
    var opts = {
      scope: 'sub',
      filter: nconf.get('LDAP_HEARTBEAT_SEARCH_QUERY'),
      sizeLimit: 1
    };
    connection.search(nconf.get('LDAP_BASE'), opts, function (err, res) {
      if (err) {
        return callback(err);
      }

      var abort = setTimeout(function () {
        client.removeAllListeners('end');
        client.removeAllListeners('error');
        callback(new Error('No heartbeat response'));
      }, 5000);

      res.once('error', function(err) {
        client.removeAllListeners('end');
        clearTimeout(abort);
        callback(err);
      }).once('end', function () {
        client.removeAllListeners('error');
        clearTimeout(abort);
        callback();
      });
    });
  };

  function protect_with_timeout (func) {
    var original = connection[func];
    connection[func] = function () {
      var args = [].slice.call(arguments);
      var original_callback = args.pop();
      var timeoutable_callback = cb(original_callback).timeout(450000);
      var new_args = args.concat([timeoutable_callback]);
      return original.apply(this, new_args);
    };
  }
  protect_with_timeout('bind');
  protect_with_timeout('search');

  function ping_recurse () {
    connection.heartbeat(function (err) {
      if (err) {
        console.error('Error on heartbeat response from LDAP: ', err.message);
        return exit(1);
      }
      setTimeout(ping_recurse, nconf.get('LDAP_HEARTBEAT_SECONDS') * 1000);
    });
  }

  if (!nconf.get('ANONYMOUS_SEARCH_ENABLED')) {
    var LDAP_BIND_CREDENTIALS = nconf.get('LDAP_BIND_PASSWORD') || crypto.decrypt(nconf.get('LDAP_BIND_CREDENTIALS'));
    connection.bind(nconf.get("LDAP_BIND_USER"), LDAP_BIND_CREDENTIALS, function(err) {
      if(err){
        console.error("Error binding to LDAP", 'dn: ' + err.dn + '\n code: ' + err.code + '\n message: ' + err.message);
        return exit(1);
      }
      ping_recurse();
    });
  } else {
    ping_recurse();
  }

  return connection;
}

module.exports.createConnection = createConnection;

Object.defineProperty(module.exports, 'client', {
  enumerable: true,
  configurable: false,
  get: function () {
    client = client || initializeConnection();
    return client;
  }
});

Object.defineProperty(module.exports, 'binder', {
  enumerable: true,
  configurable: false,
  get: function () {
    binder = binder || initializeConnection();
    return binder;
  }
});
<commit_msg>remove sizeLimit<commit_after>var nconf = require('nconf');
var ldap  = require('ldapjs');
var exit  = require('./exit');
var client, binder;
var crypto = require('./crypto');
var cb = require('cb');
var https = require('https');

function createConnection () {
  var clientOpts = {
    url: nconf.get("LDAP_URL")
  };

  if (nconf.get('LDAP_URL').toLowerCase().substr(0, 5) === 'ldaps') {
    clientOpts.tlsOptions = { ca: https.globalAgent.options.ca };
  }

  return ldap.createClient(clientOpts);
}

function initializeConnection () {
  var connection = createConnection();

  connection.on('close', function () {
    console.error('Connection to ldap was closed.');
    exit(1);
  });

  connection.heartbeat = function (callback) {
    connection.search(nconf.get('LDAP_BASE'), nconf.get('LDAP_HEARTBEAT_SEARCH_QUERY'), function (err, res) {
      if (err) {
        return callback(err);
      }

      var abort = setTimeout(function () {
        client.removeAllListeners('end');
        client.removeAllListeners('error');
        callback(new Error('No heartbeat response'));
      }, 5000);

      res.once('error', function(err) {
        client.removeAllListeners('end');
        clearTimeout(abort);
        callback(err);
      }).once('end', function () {
        client.removeAllListeners('error');
        clearTimeout(abort);
        callback();
      });
    });
  };

  function protect_with_timeout (func) {
    var original = connection[func];
    connection[func] = function () {
      var args = [].slice.call(arguments);
      var original_callback = args.pop();
      var timeoutable_callback = cb(original_callback).timeout(450000);
      var new_args = args.concat([timeoutable_callback]);
      return original.apply(this, new_args);
    };
  }
  protect_with_timeout('bind');
  protect_with_timeout('search');

  function ping_recurse () {
    connection.heartbeat(function (err) {
      if (err) {
        console.error('Error on heartbeat response from LDAP: ', err.message);
        return exit(1);
      }
      setTimeout(ping_recurse, nconf.get('LDAP_HEARTBEAT_SECONDS') * 1000);
    });
  }

  if (!nconf.get('ANONYMOUS_SEARCH_ENABLED')) {
    var LDAP_BIND_CREDENTIALS = nconf.get('LDAP_BIND_PASSWORD') || crypto.decrypt(nconf.get('LDAP_BIND_CREDENTIALS'));
    connection.bind(nconf.get("LDAP_BIND_USER"), LDAP_BIND_CREDENTIALS, function(err) {
      if(err){
        console.error("Error binding to LDAP", 'dn: ' + err.dn + '\n code: ' + err.code + '\n message: ' + err.message);
        return exit(1);
      }
      ping_recurse();
    });
  } else {
    ping_recurse();
  }

  return connection;
}

module.exports.createConnection = createConnection;

Object.defineProperty(module.exports, 'client', {
  enumerable: true,
  configurable: false,
  get: function () {
    client = client || initializeConnection();
    return client;
  }
});

Object.defineProperty(module.exports, 'binder', {
  enumerable: true,
  configurable: false,
  get: function () {
    binder = binder || initializeConnection();
    return binder;
  }
});
<|endoftext|>


<commit_before>var async = require("async");
var debug = require("debug")("lego");
var Brick = require("./brick");
var _ = require("underscore");
var domain = require("domain");

var log = require("./logger");
var transProperty = require("./trans");

var Lego = function (options) {
    //each time pipe calls , add an item
    this._chain = [];

    //start params
    this.params = {};

    //options
    this.options = _.extend({
        timeout: 0        //timeout for each brick, default 0 , no timeout control
    }, options || {});

    //properties to be transformed
    this.propertyQueue = [];

};

Lego.prototype.start = function (params) {
    this.params = params || {};
    return this;
};

/**
 *
 * parallel handle bricks
 *
 * */
Lego.prototype.pipe = function () {
    var args = Array.prototype.slice.call(arguments, 0);

    args = args.filter(function (brick) {
        if (!(brick.prototype instanceof  Brick)) {
            log("error", "Lego.pipe param ", brick, " is not a Brick");
            return false;
        }
        return true;
    });

    if (!args.length) {
        log("error", "Lego.combo none bricks provide");
        throw new Error("function Lego.pipe : Bricks must provide");
    }

    this._chain.push(this._makePiper(args));

    return this;
};

Lego.prototype.done = function (cb) {
    if (!cb) {
        //只warning一下
        log("warn", "Lego.done need a callback function");
    }
    var self = this;
    if (this._chain.length) {
        async.waterfall(this._chain, function (err, result) {
            cb && cb.call(self, result);
        });
        //TODO clear chain , done之后可以继续start pipe done
        self._chain = [];
    } else {
        cb && cb.call(self, self.params);
    }
    return this;
};

/**
 * 在一次pipe的全部Brick处理完以后，提供此函数，可以把部分数据提到data的顶层，
 * 方便后续pipe的模块依赖顶层的数据，而不是Brick返回的内部数据
 *
 * @param brick {Brick|String}
 * @param property{String}
 * @param toProperty{String}
 * */
Lego.prototype.top = function (brick, property, toProperty) {
    if (_.isString(brick)) {
        //Name of brick , try find by name
        brick = Brick.find(brick);
    }

    if (!brick || !(brick.prototype instanceof(Brick))) {
        log("error", "Lego.top param brick must be a Brick");
        return this;
    }

    if (!_.isString(property) || !_.isString(toProperty)) {
        log("error", "Lego.top param property,toProperty must be String");
        return this;
    }

    //add to Queue
    this.propertyQueue.push({
        brick: brick,
        property: property,
        toProperty: toProperty
    });

    return this;
};

/**
 * make a piper to put in the chain
 * */
Lego.prototype._makePiper = function (bricks) {
    var params = this.params;
    var options = this.options;
    var propertyQueue = this.propertyQueue;

    return function () {
        //parallel call
        var tasks = {};
        var self = this;

        //async waterfall, 回调函数是最后一个参数
        var finish = arguments[arguments.length - 1];

        //pass tasks as `this` to _domainBrick
        bricks.forEach(function (ModuleBrick) {
            //以Brick的名字命名
            tasks[ModuleBrick.Name] = self._domainBrick(new ModuleBrick());
        });

        async.parallel(tasks, function (err, results) {
            /**
             * results 会变成 {
             *      BrickName1: xxx,
             *      BrickName1View:html,
             *      BrickName2: xxx,
             *      BrickName2View:html
             * }
             * 所以当2个名字一样的Brick在同一个Lego中使用的时候会出现覆盖的情况
             *
             * */

            /**
             * 和params合并
             * */
            Object.keys(results).forEach(function (brickName) {
                var data = results[brickName];
                if (data) {
                    params[brickName] = data.data;
                    params[brickName + "View"] = data.html;
                } else {
                    params[brickName] = null;
                }
            });

            //trans property
            propertyQueue.forEach(function (trans) {
                if (trans.brick && trans.brick.Name && trans.brick.Name in results) {
                    transProperty(results[trans.brick.Name] ? results[trans.brick.Name].data : null, params, trans.property, trans.toProperty);
                }
            });

            //传给下一个pipe
            finish(null, params);
        });
    }.bind(this);
};

/**
 * call brick handler in domain
 * */
Lego.prototype._domainBrick = function (brick) {
    var params = this.params;
    var timeout = this.options.timeout;

    return function (cb) {
        //async.parallel cb
        brick.handle(params, function (status, data, html) {
            debug("brick handle finish , status=" + status + ",data=" + JSON.stringify(data || {}) + ",html=" + html);
            if (status == Brick.SUCCESS) {
                cb(null, {
                    data: data,
                    html: html
                });
            } else {
                cb(null, null);
            }
        }, timeout);
    }

};

module.exports = Lego;

Lego.Brick = Brick;
Lego.setting = require("./setting");
Lego.switchLog = function (enable) {
    log.enable = enable;
};<commit_msg>finish<commit_after>var async = require("async");
var debug = require("debug")("lego");
var Brick = require("./brick");
var _ = require("underscore");
var domain = require("domain");

var log = require("./logger");
var transProperty = require("./trans");

var Lego = function (options) {
    //each time pipe calls , add an item
    this._chain = [];

    //start params
    this.params = {};

    //options
    this.options = _.extend({
        timeout: 0        //timeout for each brick, default 0 , no timeout control
    }, options || {});

    //properties to be transformed
    this.propertyQueue = [];

};

Lego.prototype.start = function (params) {
    this.params = params || {};
    return this;
};

/**
 *
 * parallel handle bricks
 *
 * */
Lego.prototype.pipe = function () {
    var args = Array.prototype.slice.call(arguments, 0);

    args = args.filter(function (brick) {
        if (!(brick.prototype instanceof  Brick)) {
            log("error", "Lego.pipe param ", brick, " is not a Brick");
            return false;
        }
        return true;
    });

    if (!args.length) {
        log("error", "Lego.combo none bricks provide");
        throw new Error("function Lego.pipe : Bricks must provide");
    }

    this._chain.push(this._makePiper(args));

    return this;
};

Lego.prototype.done = function (cb) {
    if (!cb) {
        //只warning一下
        log("warn", "Lego.done need a callback function");
    }
    var finish = function () {
        cb && cb.call(this, this.params);
    }.bind(this);

    if (this._chain.length) {
        async.waterfall(this._chain, finish);
        //TODO clear chain , done之后可以继续start pipe done
        this._chain = [];
    } else {
        finish();
    }
    return this;
};

/**
 * 在一次pipe的全部Brick处理完以后，提供此函数，可以把部分数据提到data的顶层，
 * 方便后续pipe的模块依赖顶层的数据，而不是Brick返回的内部数据
 *
 * @param brick {Brick|String}
 * @param property{String}
 * @param toProperty{String}
 * */
Lego.prototype.top = function (brick, property, toProperty) {
    if (_.isString(brick)) {
        //Name of brick , try find by name
        brick = Brick.find(brick);
    }

    if (!brick || !(brick.prototype instanceof(Brick))) {
        log("error", "Lego.top param brick must be a Brick");
        return this;
    }

    if (!_.isString(property) || !_.isString(toProperty)) {
        log("error", "Lego.top param property,toProperty must be String");
        return this;
    }

    //add to Queue
    this.propertyQueue.push({
        brick: brick,
        property: property,
        toProperty: toProperty
    });

    return this;
};

/**
 * make a piper to put in the chain
 * */
Lego.prototype._makePiper = function (bricks) {
    var params = this.params;
    var propertyQueue = this.propertyQueue;

    return function () {
        //parallel call
        var tasks = {};
        var self = this;

        //async waterfall, 回调函数是最后一个参数
        var finish = arguments[arguments.length - 1];

        //pass tasks as `this` to _domainBrick
        bricks.forEach(function (ModuleBrick) {
            //以Brick的名字命名
            tasks[ModuleBrick.Name] = self._domainBrick(new ModuleBrick());
        });

        async.parallel(tasks, function (err, results) {
            /**
             * results 会变成 {
             *      BrickName1: xxx,
             *      BrickName1View:html,
             *      BrickName2: xxx,
             *      BrickName2View:html
             * }
             * 所以当2个名字一样的Brick在同一个Lego中使用的时候会出现覆盖的情况
             *
             * */

            /**
             * 和params合并
             * */
            Object.keys(results).forEach(function (brickName) {
                var data = results[brickName];
                if (data) {
                    params[brickName] = data.data;
                    params[brickName + "View"] = data.html;
                } else {
                    params[brickName] = null;
                }
            });

            //trans property
            propertyQueue.forEach(function (trans) {
                if (trans.brick && trans.brick.Name && trans.brick.Name in results) {
                    transProperty(results[trans.brick.Name] ? results[trans.brick.Name].data : null, params, trans.property, trans.toProperty);
                }
            });

            //pass to next pipe
            finish(null);
        });
    }.bind(this);
};

/**
 * call brick handler in domain
 * */
Lego.prototype._domainBrick = function (brick) {
    var params = this.params;
    var timeout = this.options.timeout;

    return function (cb) {
        //async.parallel cb
        brick.handle(params, function (status, data, html) {
            debug("brick handle finish , status=" + status + ",data=" + JSON.stringify(data || {}) + ",html=" + html);
            if (status == Brick.SUCCESS) {
                cb(null, {
                    data: data,
                    html: html
                });
            } else {
                cb(null, null);
            }
        }, timeout);
    }

};

module.exports = Lego;

Lego.Brick = Brick;
Lego.setting = require("./setting");
Lego.switchLog = function (enable) {
    log.enable = enable;
};<|endoftext|>


<commit_before>var eslint = require('eslint');
var ESLINT_CONFIG = require('./eslint_config');
var glob = require('glob');
var path = require('path');

var base = require('./base');

var A = base.A;
var sub = base.sub;

var customRules = {};

var convertNameToRuleId = function(item) {
	var baseName = path.basename(item, '.js');

	return 'csf-' + baseName.replace(/_/g, '-');
};

var runLinter = function(contents, file, customRules, config) {
	eslint.linter.defineRules(customRules);

	if (A.Lang.isObject(config)) {
		Object.keys(ESLINT_CONFIG).forEach(
			function(item, index) {
				var configItem = A.namespace.call(config, item);

				A.mix(configItem, ESLINT_CONFIG[item]);
			}
		);
	}
	else {
		config = ESLINT_CONFIG;
	}

	return eslint.linter.verify(contents, config, file)
};

var globOptions = {
	cwd: __dirname
};

module.exports = function(contents, file, config) {
	glob.sync(
		'./rules/*.js',
		globOptions
	).forEach(
		function(item, index) {
			var id = convertNameToRuleId(item);

			customRules[id] = require(item);
		}
	);

	eslint.linter.defineRules(customRules);

	return runLinter(contents, file, customRules, config);
};<commit_msg>Source formatting<commit_after>var eslint = require('eslint');
var ESLINT_CONFIG = require('./eslint_config');
var glob = require('glob');
var path = require('path');

var base = require('./base');

var A = base.A;
var sub = base.sub;

var customRules = {};

var convertNameToRuleId = function(item) {
	var baseName = path.basename(item, '.js');

	return 'csf-' + baseName.replace(/_/g, '-');
};

var runLinter = function(contents, file, customRules, config) {
	eslint.linter.defineRules(customRules);

	if (A.Lang.isObject(config)) {
		Object.keys(ESLINT_CONFIG).forEach(
			function(item, index) {
				var configItem = A.namespace.call(config, item);

				A.mix(configItem, ESLINT_CONFIG[item]);
			}
		);
	}
	else {
		config = ESLINT_CONFIG;
	}

	return eslint.linter.verify(contents, config, file);
};

var globOptions = {
	cwd: __dirname
};

module.exports = function(contents, file, config) {
	glob.sync(
		'./rules/*.js',
		globOptions
	).forEach(
		function(item, index) {
			var id = convertNameToRuleId(item);

			customRules[id] = require(item);
		}
	);

	eslint.linter.defineRules(customRules);

	return runLinter(contents, file, customRules, config);
};<|endoftext|>