(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/*!
  Copyright (c) 2016 Jed Watson.
  Licensed under the MIT License (MIT), see
  http://jedwatson.github.io/classnames
*/
/* global define */

(function () {
	'use strict';

	var hasOwn = {}.hasOwnProperty;

	function classNames () {
		var classes = [];

		for (var i = 0; i < arguments.length; i++) {
			var arg = arguments[i];
			if (!arg) continue;

			var argType = typeof arg;

			if (argType === 'string' || argType === 'number') {
				classes.push(arg);
			} else if (Array.isArray(arg)) {
				classes.push(classNames.apply(null, arg));
			} else if (argType === 'object') {
				for (var key in arg) {
					if (hasOwn.call(arg, key) && arg[key]) {
						classes.push(key);
					}
				}
			}
		}

		return classes.join(' ');
	}

	if (typeof module !== 'undefined' && module.exports) {
		module.exports = classNames;
	} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
		// register as 'classnames', consistent with npm package name
		define('classnames', [], function () {
			return classNames;
		});
	} else {
		window.classNames = classNames;
	}
}());

},{}],2:[function(require,module,exports){
//  Import support https://stackoverflow.com/questions/13673346/supporting-both-commonjs-and-amd
(function(name, definition) {
    if (typeof module !== "undefined") { module.exports = definition(); }
    else if (typeof define === "function" && typeof define.amd === "object") { define(definition); }
    else { this[name] = definition(); }
}("clipboard", function() {
  if (!document.addEventListener) {
    return null;
  }

  var clipboard = {};

  clipboard.copy = (function() {
    var _intercept = false;
    var _data = null; // Map from data type (e.g. "text/html") to value.
    var _bogusSelection = false;

    function cleanup() {
      _intercept = false;
      _data = null;
      if (_bogusSelection) {
        window.getSelection().removeAllRanges();
      }
      _bogusSelection = false;
    }

    document.addEventListener("copy", function(e) {
      if (_intercept) {
        for (var key in _data) {
          e.clipboardData.setData(key, _data[key]);
        }
        e.preventDefault();
      }
    });

    // Workaround for Safari: https://bugs.webkit.org/show_bug.cgi?id=156529
    function bogusSelect() {
      var sel = document.getSelection();
      // If "nothing" is selected...
      if (!document.queryCommandEnabled("copy") && sel.isCollapsed) {
        // ... temporarily select the entire body.
        //
        // We select the entire body because:
        // - it's guaranteed to exist,
        // - it works (unlike, say, document.head, or phantom element that is
        //   not inserted into the DOM),
        // - it doesn't seem to flicker (due to the synchronous copy event), and
        // - it avoids modifying the DOM (can trigger mutation observers).
        //
        // Because we can't do proper feature detection (we already checked
        // document.queryCommandEnabled("copy") , which actually gives a false
        // negative for Blink when nothing is selected) and UA sniffing is not
        // reliable (a lot of UA strings contain "Safari"), this will also
        // happen for some browsers other than Safari. :-()
        var range = document.createRange();
        range.selectNodeContents(document.body);
        sel.addRange(range);
        _bogusSelection = true;
      }
    };

    return function(data) {
      return new Promise(function(resolve, reject) {
        _intercept = true;
        if (typeof data === "string") {
          _data = {"text/plain": data};
        } else if (data instanceof Node) {
          _data = {"text/html": new XMLSerializer().serializeToString(data)};
        } else {
          _data = data;
        }
        try {
          bogusSelect();
          if (document.execCommand("copy")) {
            // document.execCommand is synchronous: http://www.w3.org/TR/2015/WD-clipboard-apis-20150421/#integration-with-rich-text-editing-apis
            // So we can call resolve() back here.
            cleanup();
            resolve();
          }
          else {
            throw new Error("Unable to copy. Perhaps it's not available in your browser?");
          }
        } catch (e) {
          cleanup();
          reject(e);
        }
      });
    };
  })();

  clipboard.paste = (function() {
    var _intercept = false;
    var _resolve;
    var _dataType;

    document.addEventListener("paste", function(e) {
      if (_intercept) {
        _intercept = false;
        e.preventDefault();
        var resolve = _resolve;
        _resolve = null;
        resolve(e.clipboardData.getData(_dataType));
      }
    });

    return function(dataType) {
      return new Promise(function(resolve, reject) {
        _intercept = true;
        _resolve = resolve;
        _dataType = dataType || "text/plain";
        try {
          if (!document.execCommand("paste")) {
            _intercept = false;
            reject(new Error("Unable to paste. Pasting only works in Internet Explorer at the moment."));
          }
        } catch (e) {
          _intercept = false;
          reject(new Error(e));
        }
      });
    };
  })();

  // Handle IE behaviour.
  if (typeof ClipboardEvent === "undefined" &&
      typeof window.clipboardData !== "undefined" &&
      typeof window.clipboardData.setData !== "undefined") {

    /*! promise-polyfill 2.0.1 */
    (function(a){function b(a,b){return function(){a.apply(b,arguments)}}function c(a){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof a)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],i(a,b(e,this),b(f,this))}function d(a){var b=this;return null===this._state?void this._deferreds.push(a):void j(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(e){return void a.reject(e)}a.resolve(d)})}function e(a){try{if(a===this)throw new TypeError("A promise cannot be resolved with itself.");if(a&&("object"==typeof a||"function"==typeof a)){var c=a.then;if("function"==typeof c)return void i(b(c,a),b(e,this),b(f,this))}this._state=!0,this._value=a,g.call(this)}catch(d){f.call(this,d)}}function f(a){this._state=!1,this._value=a,g.call(this)}function g(){for(var a=0,b=this._deferreds.length;b>a;a++)d.call(this,this._deferreds[a]);this._deferreds=null}function h(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function i(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){if(d)return;d=!0,c(e)}}var j=c.immediateFn||"function"==typeof setImmediate&&setImmediate||function(a){setTimeout(a,1)},k=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};c.prototype["catch"]=function(a){return this.then(null,a)},c.prototype.then=function(a,b){var e=this;return new c(function(c,f){d.call(e,new h(a,b,c,f))})},c.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&k(arguments[0])?arguments[0]:arguments);return new c(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}a[f]=g,0===--e&&b(a)}catch(i){c(i)}}if(0===a.length)return b([]);for(var e=a.length,f=0;f<a.length;f++)d(f,a[f])})},c.resolve=function(a){return a&&"object"==typeof a&&a.constructor===c?a:new c(function(b){b(a)})},c.reject=function(a){return new c(function(b,c){c(a)})},c.race=function(a){return new c(function(b,c){for(var d=0,e=a.length;e>d;d++)a[d].then(b,c)})},"undefined"!=typeof module&&module.exports?module.exports=c:a.Promise||(a.Promise=c)})(this);

    clipboard.copy = function(data) {
      return new Promise(function(resolve, reject) {
        // IE supports string and URL types: https://msdn.microsoft.com/en-us/library/ms536744(v=vs.85).aspx
        // We only support the string type for now.
        if (typeof data !== "string" && !("text/plain" in data)) {
          throw new Error("You must provide a text/plain type.");
        }

        var strData = (typeof data === "string" ? data : data["text/plain"]);
        var copySucceeded = window.clipboardData.setData("Text", strData);
        if (copySucceeded) {
          resolve();
        } else {
          reject(new Error("Copying was rejected."));
        }
      });
    };

    clipboard.paste = function() {
      return new Promise(function(resolve, reject) {
        var strData = window.clipboardData.getData("Text");
        if (strData) {
          resolve(strData);
        } else {
          // The user rejected the paste request.
          reject(new Error("Pasting was rejected."));
        }
      });
    };
  }

  return clipboard;
}));

},{}],3:[function(require,module,exports){
'use strict';

var extend  = require('extend');
var isArray = require('util').isArray;
var isDate  = require('util').isDate;
var sprintf = require("sprintf-js").sprintf;
var events  = require('events');
var except  = require('except');

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

var translationScope = 'counterpart';

function isString(val) {
  return typeof val === 'string' || Object.prototype.toString.call(val) === '[object String]';
}

function isFunction(val) {
  return typeof val === 'function' || Object.prototype.toString.call(val) === '[object Function]';
}

function isPlainObject(val) {
  //Deal with older browsers (IE8) that don't return [object Null] in this case.
  if (val === null) {
    return false;
  }
  return Object.prototype.toString.call(val) === '[object Object]';
}

function isSymbol(key) {
  return isString(key) && key[0] === ':';
}

function hasOwnProp(obj, key) {
  return Object.prototype.hasOwnProperty.call(obj, key);
}

function getEntry(translations, keys) {
  return keys.reduce(function(result, key) {
    if (isPlainObject(result) && hasOwnProp(result, key)) {
      return result[key];
    } else {
      return null;
    }
  }, translations);
}

function Counterpart() {
  this._registry = {
    locale: 'en',
    interpolate: true,
    fallbackLocale: null,
    scope: null,
    translations: {},
    interpolations: {},
    normalizedKeys: {},
    separator: '.',
    keepTrailingDot: false,
    keyTransformer: function(key) { return key; }
  };

  this.registerTranslations('en', require('./locales/en'));
  this.setMaxListeners(0);
}

extend(Counterpart.prototype, events.EventEmitter.prototype);

Counterpart.prototype.getLocale = function() {
  return this._registry.locale;
};

Counterpart.prototype.setLocale = function(value) {
  var previous = this._registry.locale;

  if (previous != value) {
    this._registry.locale = value;
    this.emit('localechange', value, previous);
  }

  return previous;
};

Counterpart.prototype.getFallbackLocale = function() {
  return this._registry.fallbackLocale;
};

Counterpart.prototype.setFallbackLocale = function(value) {
  var previous = this._registry.fallbackLocale;
  this._registry.fallbackLocale = value;
  return previous;
};

Counterpart.prototype.getAvailableLocales = function() {
  return this._registry.availableLocales || Object.keys(this._registry.translations);
};

Counterpart.prototype.setAvailableLocales = function(value) {
  var previous = this.getAvailableLocales();
  this._registry.availableLocales = value;
  return previous;
};

Counterpart.prototype.getSeparator = function() {
  return this._registry.separator;
};

Counterpart.prototype.setSeparator = function(value) {
  var previous = this._registry.separator;
  this._registry.separator = value;
  return previous;
};

Counterpart.prototype.setInterpolate = function(value) {
  var previous = this._registry.interpolate;
  this._registry.interpolate = value;
  return previous;
};

Counterpart.prototype.getInterpolate = function() {
  return this._registry.interpolate;
};

Counterpart.prototype.setKeyTransformer = function(value) {
  var previous = this._registry.keyTransformer;
  this._registry.keyTransformer = value;
  return previous;
};

Counterpart.prototype.getKeyTransformer = function() {
  return this._registry.keyTransformer;
};

Counterpart.prototype.registerTranslations = function(locale, data) {
  var translations = {};
  translations[locale] = data;
  extend(true, this._registry.translations, translations);
  return translations;
};

Counterpart.prototype.registerInterpolations = function(data) {
  return extend(true, this._registry.interpolations, data);
};

Counterpart.prototype.onLocaleChange =
Counterpart.prototype.addLocaleChangeListener = function(callback) {
  this.addListener('localechange', callback);
};

Counterpart.prototype.offLocaleChange =
Counterpart.prototype.removeLocaleChangeListener = function(callback) {
  this.removeListener('localechange', callback);
};

Counterpart.prototype.onTranslationNotFound =
Counterpart.prototype.addTranslationNotFoundListener = function(callback) {
  this.addListener('translationnotfound', callback);
};

Counterpart.prototype.offTranslationNotFound =
Counterpart.prototype.removeTranslationNotFoundListener = function(callback) {
  this.removeListener('translationnotfound', callback);
};

Counterpart.prototype.translate = function(key, options) {
  if (!isArray(key) && !isString(key) || !key.length) {
    throw new Error('invalid argument: key');
  }

  if (isSymbol(key)) {
    key = key.substr(1);
  }

  key = this._registry.keyTransformer(key, options);

  options = extend(true, {}, options);

  var locale = options.locale || this._registry.locale;
  delete options.locale;

  var scope = options.scope || this._registry.scope;
  delete options.scope;

  var separator = options.separator || this._registry.separator;
  delete options.separator;

  var fallbackLocale = options.fallbackLocale || this._registry.fallbackLocale;
  delete options.fallbackLocale;

  var keys = this._normalizeKeys(locale, scope, key, separator);

  var entry = getEntry(this._registry.translations, keys);

  if (entry === null && options.fallback) {
    this.emit('translationnotfound', locale, key, options.fallback);
    entry = this._fallback(locale, scope, key, options.fallback, options);
  }

  if (entry === null && fallbackLocale && locale !== fallbackLocale) {
    var fallbackKeys = this._normalizeKeys(fallbackLocale, scope, key, separator);
    entry = getEntry(this._registry.translations, fallbackKeys);

    if (entry) {
      locale = fallbackLocale;
    }
  }

  if (entry === null) {
    entry = 'missing translation: ' + keys.join(separator);
  }

  entry = this._pluralize(locale, entry, options.count);

  if (this._registry.interpolate !== false && options.interpolate !== false) {
    entry = this._interpolate(entry, options);
  }

  return entry;
};

Counterpart.prototype.localize = function(object, options) {
  if (!isDate(object)) {
    throw new Error('invalid argument: object must be a date');
  }

  options = extend(true, {}, options);

  var locale  = options.locale  || this._registry.locale;
  var scope   = options.scope   || translationScope;
  var type    = options.type    || 'datetime';
  var format  = options.format  || 'default';

  options = { locale: locale, scope: scope, interpolate: false };
  format  = this.translate(['formats', type, format], extend(true, {}, options));

  return strftime(object, format, this.translate('names', options));
};

Counterpart.prototype._pluralize = function(locale, entry, count) {
  if (typeof entry !== 'object' || entry === null || typeof count !== 'number') {
    return entry;
  }

  var pluralizeFunc = this.translate('pluralize', { locale: locale, scope: translationScope });

  if (Object.prototype.toString.call(pluralizeFunc) !== '[object Function]') {
    return pluralizeFunc;
  }

  return pluralizeFunc(entry, count);
};

Counterpart.prototype.withLocale = function(locale, callback, context) {
  var previous = this._registry.locale;
  this._registry.locale = locale;
  var result = callback.call(context);
  this._registry.locale = previous;
  return result;
};

Counterpart.prototype.withScope = function(scope, callback, context) {
  var previous = this._registry.scope;
  this._registry.scope = scope;
  var result = callback.call(context);
  this._registry.scope = previous;
  return result;
};

Counterpart.prototype.withSeparator = function(separator, callback, context) {
  var previous = this.setSeparator(separator);
  var result = callback.call(context);
  this.setSeparator(previous);
  return result;
};

Counterpart.prototype._normalizeKeys = function(locale, scope, key, separator) {
  var keys = [];

  keys = keys.concat(this._normalizeKey(locale, separator));
  keys = keys.concat(this._normalizeKey(scope, separator));
  keys = keys.concat(this._normalizeKey(key, separator));

  return keys;
};

Counterpart.prototype._normalizeKey = function(key, separator) {
  this._registry.normalizedKeys[separator] = this._registry.normalizedKeys[separator] || {};

  this._registry.normalizedKeys[separator][key] = this._registry.normalizedKeys[separator][key] || (function(key) {
    if (isArray(key)) {
      var normalizedKeyArray = key.map(function(k) { return this._normalizeKey(k, separator); }.bind(this));

      return [].concat.apply([], normalizedKeyArray);
    } else {
      if (typeof key === 'undefined' || key === null) {
        return [];
      }

      var keys = key.split(separator);

      for (var i = keys.length - 1; i >= 0; i--) {
        if (keys[i] === '') {
          keys.splice(i, 1);

          if (this._registry.keepTrailingDot === true && i == keys.length) {
            keys[keys.length - 1] += '' + separator;
          }
        }
      }

      return keys;
    }
  }.bind(this))(key);

  return this._registry.normalizedKeys[separator][key];
};

Counterpart.prototype._interpolate = function(entry, values) {
  if (typeof entry !== 'string') {
    return entry;
  }

  return sprintf(entry, extend({}, this._registry.interpolations, values));
};

Counterpart.prototype._resolve = function(locale, scope, object, subject, options) {
  options = options || {};

  if (options.resolve === false) {
    return subject;
  }

  var result;

  if (isSymbol(subject)) {
    result = this.translate(subject, extend({}, options, { locale: locale, scope: scope }));
  } else if (isFunction(subject)) {
    var dateOrTime;

    if (options.object) {
      dateOrTime = options.object;
      delete options.object;
    } else {
      dateOrTime = object;
    }

    result = this._resolve(locale, scope, object, subject(dateOrTime, options));
  } else {
    result = subject;
  }

  return /^missing translation:/.test(result) ? null : result;
};

Counterpart.prototype._fallback = function(locale, scope, object, subject, options) {
  options = except(options, 'fallback');

  if (isArray(subject)) {
    for (var i = 0, ii = subject.length; i < ii; i++) {
      var result = this._resolve(locale, scope, object, subject[i], options);

      if (result) {
        return result;
      }
    }

    return null;
  } else {
    return this._resolve(locale, scope, object, subject, options);
  }
};

var instance = new Counterpart();

function translate() {
  return instance.translate.apply(instance, arguments);
}

extend(translate, instance, {
  Instance: Counterpart
});

module.exports = translate;

},{"./locales/en":4,"./strftime":12,"events":16,"except":7,"extend":9,"sprintf-js":11,"util":20}],4:[function(require,module,exports){
// The translations in this file are added by default.

'use strict';

module.exports = {
  counterpart: {
    names: require('date-names/en'),
    pluralize: require('pluralizers/en'),

    formats: {
      date: {
        'default':  '%a, %e %b %Y',
        long:       '%A, %B %o, %Y',
        short:      '%b %e'
      },

      time: {
        'default':  '%H:%M',
        long:       '%H:%M:%S %z',
        short:      '%H:%M'
      },

      datetime: {
        'default':  '%a, %e %b %Y %H:%M',
        long:       '%A, %B %o, %Y %H:%M:%S %z',
        short:      '%e %b %H:%M'
      }
    }
  }
};

},{"date-names/en":5,"pluralizers/en":10}],5:[function(require,module,exports){
"use strict";

module.exports = {
  __locale: "en",
  days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
  abbreviated_days: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
  months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
  abbreviated_months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
  am: 'AM',
  pm: 'PM'
};

},{}],6:[function(require,module,exports){
"use strict";
module.exports = require('./en');

},{"./en":5}],7:[function(require,module,exports){
'use strict';

var ap      = Array.prototype;
var concat  = ap.concat;
var slice   = ap.slice;
var indexOf = require('indexof');

function except(object) {
  var result = {};
  var keys = concat.apply(ap, slice.call(arguments, 1));

  for (var key in object) {
    if (indexOf(keys, key) === -1) {
      result[key] = object[key];
    }
  }

  return result;
}

module.exports = except;

},{"indexof":8}],8:[function(require,module,exports){

var indexOf = [].indexOf;

module.exports = function(arr, obj){
  if (indexOf) return arr.indexOf(obj);
  for (var i = 0; i < arr.length; ++i) {
    if (arr[i] === obj) return i;
  }
  return -1;
};
},{}],9:[function(require,module,exports){
'use strict';

var hasOwn = Object.prototype.hasOwnProperty;
var toStr = Object.prototype.toString;

var isArray = function isArray(arr) {
	if (typeof Array.isArray === 'function') {
		return Array.isArray(arr);
	}

	return toStr.call(arr) === '[object Array]';
};

var isPlainObject = function isPlainObject(obj) {
	if (!obj || toStr.call(obj) !== '[object Object]') {
		return false;
	}

	var hasOwnConstructor = hasOwn.call(obj, 'constructor');
	var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
	// Not own constructor property must be Object
	if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
		return false;
	}

	// Own properties are enumerated firstly, so to speed up,
	// if last one is own, then all properties are own.
	var key;
	for (key in obj) {/**/}

	return typeof key === 'undefined' || hasOwn.call(obj, key);
};

module.exports = function extend() {
	var options, name, src, copy, copyIsArray, clone,
		target = arguments[0],
		i = 1,
		length = arguments.length,
		deep = false;

	// Handle a deep copy situation
	if (typeof target === 'boolean') {
		deep = target;
		target = arguments[1] || {};
		// skip the boolean and the target
		i = 2;
	} else if ((typeof target !== 'object' && typeof target !== 'function') || target == null) {
		target = {};
	}

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

				// Prevent never-ending loop
				if (target !== copy) {
					// Recurse if we're merging plain objects or arrays
					if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
						if (copyIsArray) {
							copyIsArray = false;
							clone = src && isArray(src) ? src : [];
						} else {
							clone = src && isPlainObject(src) ? src : {};
						}

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

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

	// Return the modified object
	return target;
};


},{}],10:[function(require,module,exports){
'use strict';

module.exports = function(entry, count) {
  var key;

  if (count === 0 && 'zero' in entry) {
    key = 'zero';
  }

  key = key || (count === 1 ? 'one' : 'other');

  return entry[key];
};

},{}],11:[function(require,module,exports){
(function(window) {
    var re = {
        not_string: /[^s]/,
        number: /[diefg]/,
        json: /[j]/,
        not_json: /[^j]/,
        text: /^[^\x25]+/,
        modulo: /^\x25{2}/,
        placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosuxX])/,
        key: /^([a-z_][a-z_\d]*)/i,
        key_access: /^\.([a-z_][a-z_\d]*)/i,
        index_access: /^\[(\d+)\]/,
        sign: /^[\+\-]/
    }

    function sprintf() {
        var key = arguments[0], cache = sprintf.cache
        if (!(cache[key] && cache.hasOwnProperty(key))) {
            cache[key] = sprintf.parse(key)
        }
        return sprintf.format.call(null, cache[key], arguments)
    }

    sprintf.format = function(parse_tree, argv) {
        var cursor = 1, tree_length = parse_tree.length, node_type = "", arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = true, sign = ""
        for (i = 0; i < tree_length; i++) {
            node_type = get_type(parse_tree[i])
            if (node_type === "string") {
                output[output.length] = parse_tree[i]
            }
            else if (node_type === "array") {
                match = parse_tree[i] // convenience purposes only
                if (match[2]) { // keyword argument
                    arg = argv[cursor]
                    for (k = 0; k < match[2].length; k++) {
                        if (!arg.hasOwnProperty(match[2][k])) {
                            throw new Error(sprintf("[sprintf] property '%s' does not exist", match[2][k]))
                        }
                        arg = arg[match[2][k]]
                    }
                }
                else if (match[1]) { // positional argument (explicit)
                    arg = argv[match[1]]
                }
                else { // positional argument (implicit)
                    arg = argv[cursor++]
                }

                if (get_type(arg) == "function") {
                    arg = arg()
                }

                if (re.not_string.test(match[8]) && re.not_json.test(match[8]) && (get_type(arg) != "number" && isNaN(arg))) {
                    throw new TypeError(sprintf("[sprintf] expecting number but found %s", get_type(arg)))
                }

                if (re.number.test(match[8])) {
                    is_positive = arg >= 0
                }

                switch (match[8]) {
                    case "b":
                        arg = arg.toString(2)
                    break
                    case "c":
                        arg = String.fromCharCode(arg)
                    break
                    case "d":
                    case "i":
                        arg = parseInt(arg, 10)
                    break
                    case "j":
                        arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0)
                    break
                    case "e":
                        arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential()
                    break
                    case "f":
                        arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg)
                    break
                    case "g":
                        arg = match[7] ? parseFloat(arg).toPrecision(match[7]) : parseFloat(arg)
                    break
                    case "o":
                        arg = arg.toString(8)
                    break
                    case "s":
                        arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg)
                    break
                    case "u":
                        arg = arg >>> 0
                    break
                    case "x":
                        arg = arg.toString(16)
                    break
                    case "X":
                        arg = arg.toString(16).toUpperCase()
                    break
                }
                if (re.json.test(match[8])) {
                    output[output.length] = arg
                }
                else {
                    if (re.number.test(match[8]) && (!is_positive || match[3])) {
                        sign = is_positive ? "+" : "-"
                        arg = arg.toString().replace(re.sign, "")
                    }
                    else {
                        sign = ""
                    }
                    pad_character = match[4] ? match[4] === "0" ? "0" : match[4].charAt(1) : " "
                    pad_length = match[6] - (sign + arg).length
                    pad = match[6] ? (pad_length > 0 ? str_repeat(pad_character, pad_length) : "") : ""
                    output[output.length] = match[5] ? sign + arg + pad : (pad_character === "0" ? sign + pad + arg : pad + sign + arg)
                }
            }
        }
        return output.join("")
    }

    sprintf.cache = {}

    sprintf.parse = function(fmt) {
        var _fmt = fmt, match = [], parse_tree = [], arg_names = 0
        while (_fmt) {
            if ((match = re.text.exec(_fmt)) !== null) {
                parse_tree[parse_tree.length] = match[0]
            }
            else if ((match = re.modulo.exec(_fmt)) !== null) {
                parse_tree[parse_tree.length] = "%"
            }
            else if ((match = re.placeholder.exec(_fmt)) !== null) {
                if (match[2]) {
                    arg_names |= 1
                    var field_list = [], replacement_field = match[2], field_match = []
                    if ((field_match = re.key.exec(replacement_field)) !== null) {
                        field_list[field_list.length] = field_match[1]
                        while ((replacement_field = replacement_field.substring(field_match[0].length)) !== "") {
                            if ((field_match = re.key_access.exec(replacement_field)) !== null) {
                                field_list[field_list.length] = field_match[1]
                            }
                            else if ((field_match = re.index_access.exec(replacement_field)) !== null) {
                                field_list[field_list.length] = field_match[1]
                            }
                            else {
                                throw new SyntaxError("[sprintf] failed to parse named argument key")
                            }
                        }
                    }
                    else {
                        throw new SyntaxError("[sprintf] failed to parse named argument key")
                    }
                    match[2] = field_list
                }
                else {
                    arg_names |= 2
                }
                if (arg_names === 3) {
                    throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported")
                }
                parse_tree[parse_tree.length] = match
            }
            else {
                throw new SyntaxError("[sprintf] unexpected placeholder")
            }
            _fmt = _fmt.substring(match[0].length)
        }
        return parse_tree
    }

    var vsprintf = function(fmt, argv, _argv) {
        _argv = (argv || []).slice(0)
        _argv.splice(0, 0, fmt)
        return sprintf.apply(null, _argv)
    }

    /**
     * helpers
     */
    function get_type(variable) {
        return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase()
    }

    function str_repeat(input, multiplier) {
        return Array(multiplier + 1).join(input)
    }

    /**
     * export to either browser or node.js
     */
    if (typeof exports !== "undefined") {
        exports.sprintf = sprintf
        exports.vsprintf = vsprintf
    }
    else {
        window.sprintf = sprintf
        window.vsprintf = vsprintf

        if (typeof define === "function" && define.amd) {
            define(function() {
                return {
                    sprintf: sprintf,
                    vsprintf: vsprintf
                }
            })
        }
    }
})(typeof window === "undefined" ? this : window);

},{}],12:[function(require,module,exports){
'use strict';

var dateNames = require('date-names');

function strftime(date, format, names) {
  var timestamp = date.getTime();

  names = names || dateNames;

  return format.replace(/%([-_0]?.)/g, function(_, c) {
    var padding = null;

    if (c.length == 2) {
      switch (c[0]) {
        case '-': padding = '';  break;
        case '_': padding = ' '; break;
        case '0': padding = '0'; break;
        default: return _; // should never reach this one
      }

      c = c[1];
    }

    switch (c) {
      case 'A': return names.days[date.getDay()];
      case 'a': return names.abbreviated_days[date.getDay()];
      case 'B': return names.months[date.getMonth()];
      case 'b': return names.abbreviated_months[date.getMonth()];
      case 'C': return pad(Math.floor(date.getFullYear() / 100), padding);
      case 'D': return strftime(date, '%m/%d/%y');
      case 'd': return pad(date.getDate(), padding);
      case 'e': return date.getDate();
      case 'F': return strftime(date, '%Y-%m-%d');
      case 'H': return pad(date.getHours(), padding);
      case 'h': return names.abbreviated_months[date.getMonth()];
      case 'I': return pad(hours12(date), padding);
      case 'j': return pad(Math.ceil((date.getTime() - (new Date(date.getFullYear(), 0, 1)).getTime()) / (1000 * 60 * 60 * 24)), 3);
      case 'k': return pad(date.getHours(), padding === null ? ' ' : padding);
      case 'L': return pad(Math.floor(timestamp % 1000), 3);
      case 'l': return pad(hours12(date), padding === null ? ' ' : padding);
      case 'M': return pad(date.getMinutes(), padding);
      case 'm': return pad(date.getMonth() + 1, padding);
      case 'n': return '\n';
      case 'o': return String(date.getDate()) + ordinal(date.getDate());
      case 'P': return date.getHours() < 12 ? names.am.toLowerCase() : names.pm.toLowerCase();
      case 'p': return date.getHours() < 12 ? names.am.toUpperCase() : names.pm.toUpperCase();
      case 'R': return strftime(date, '%H:%M');
      case 'r': return strftime(date, '%I:%M:%S %p');
      case 'S': return pad(date.getSeconds(), padding);
      case 's': return Math.floor(timestamp / 1000);
      case 'T': return strftime(date, '%H:%M:%S');
      case 't': return '\t';
      case 'U': return pad(weekNumber(date, 'sunday'), padding);
      case 'u': return date.getDay() === 0 ? 7 : date.getDay();
      case 'v': return strftime(date, '%e-%b-%Y');
      case 'W': return pad(weekNumber(date, 'monday'), padding);
      case 'w': return date.getDay();
      case 'Y': return date.getFullYear();
      case 'y': var y = String(date.getFullYear()); return y.slice(y.length - 2);
      case 'Z': var tzString = date.toString().match(/\((\w+)\)/); return tzString && tzString[1] || '';
      case 'z': var off = date.getTimezoneOffset(); return (off > 0 ? '-' : '+') + pad(Math.round(Math.abs(off / 60)), 2) + ':' + pad(off % 60, 2);
      default: return c;
    }
  });
}

function pad(n, padding, length) {
  if (typeof padding === 'number') {
    length = padding;
    padding = '0';
  }

  if (padding === null) {
    padding = '0';
  }

  length = length || 2;

  var s = String(n);

  if (padding) {
    while (s.length < length) {
      s = padding + s;
    }
  }

  return s;
}

function hours12(date) {
  var hour = date.getHours();

  if (hour === 0) {
    hour = 12;
  } else if (hour > 12) {
    hour -= 12;
  }

  return hour;
}

function ordinal(n) {
  var i = n % 10, ii = n % 100;

  if ((ii >= 11 && ii <= 13) || i === 0 || i >= 4) {
    return 'th';
  }

  switch (i) {
    case 1: return 'st';
    case 2: return 'nd';
    case 3: return 'rd';
  }
}

function weekNumber(date, firstWeekday) {
  firstWeekday = firstWeekday || 'sunday';

  var wday = date.getDay();

  if (firstWeekday == 'monday') {
    if (wday === 0) { // Sunday
      wday = 6;
    } else {
      wday--;
    }
  }

  var
    firstDayOfYear = new Date(date.getFullYear(), 0, 1),
    yday = (date - firstDayOfYear) / 86400000,
    weekNum = (yday + 7 - wday) / 7;

  return Math.floor(weekNum);
}

module.exports = strftime;

},{"date-names":6}],13:[function(require,module,exports){
/*!
 * EventEmitter2
 * https://github.com/hij1nx/EventEmitter2
 *
 * Copyright (c) 2013 hij1nx
 * Licensed under the MIT license.
 */
;!function(undefined) {

  var isArray = Array.isArray ? Array.isArray : function _isArray(obj) {
    return Object.prototype.toString.call(obj) === "[object Array]";
  };
  var defaultMaxListeners = 10;

  function init() {
    this._events = {};
    if (this._conf) {
      configure.call(this, this._conf);
    }
  }

  function configure(conf) {
    if (conf) {

      this._conf = conf;

      conf.delimiter && (this.delimiter = conf.delimiter);
      conf.maxListeners && (this._events.maxListeners = conf.maxListeners);
      conf.wildcard && (this.wildcard = conf.wildcard);
      conf.newListener && (this.newListener = conf.newListener);

      if (this.wildcard) {
        this.listenerTree = {};
      }
    }
  }

  function EventEmitter(conf) {
    this._events = {};
    this.newListener = false;
    configure.call(this, conf);
  }

  //
  // Attention, function return type now is array, always !
  // It has zero elements if no any matches found and one or more
  // elements (leafs) if there are matches
  //
  function searchListenerTree(handlers, type, tree, i) {
    if (!tree) {
      return [];
    }
    var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached,
        typeLength = type.length, currentType = type[i], nextType = type[i+1];
    if (i === typeLength && tree._listeners) {
      //
      // If at the end of the event(s) list and the tree has listeners
      // invoke those listeners.
      //
      if (typeof tree._listeners === 'function') {
        handlers && handlers.push(tree._listeners);
        return [tree];
      } else {
        for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) {
          handlers && handlers.push(tree._listeners[leaf]);
        }
        return [tree];
      }
    }

    if ((currentType === '*' || currentType === '**') || tree[currentType]) {
      //
      // If the event emitted is '*' at this part
      // or there is a concrete match at this patch
      //
      if (currentType === '*') {
        for (branch in tree) {
          if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
            listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1));
          }
        }
        return listeners;
      } else if(currentType === '**') {
        endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*'));
        if(endReached && tree._listeners) {
          // The next element has a _listeners, add it to the handlers.
          listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength));
        }

        for (branch in tree) {
          if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
            if(branch === '*' || branch === '**') {
              if(tree[branch]._listeners && !endReached) {
                listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength));
              }
              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
            } else if(branch === nextType) {
              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2));
            } else {
              // No match on this one, shift into the tree but not in the type array.
              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
            }
          }
        }
        return listeners;
      }

      listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1));
    }

    xTree = tree['*'];
    if (xTree) {
      //
      // If the listener tree will allow any match for this part,
      // then recursively explore all branches of the tree
      //
      searchListenerTree(handlers, type, xTree, i+1);
    }

    xxTree = tree['**'];
    if(xxTree) {
      if(i < typeLength) {
        if(xxTree._listeners) {
          // If we have a listener on a '**', it will catch all, so add its handler.
          searchListenerTree(handlers, type, xxTree, typeLength);
        }

        // Build arrays of matching next branches and others.
        for(branch in xxTree) {
          if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) {
            if(branch === nextType) {
              // We know the next element will match, so jump twice.
              searchListenerTree(handlers, type, xxTree[branch], i+2);
            } else if(branch === currentType) {
              // Current node matches, move into the tree.
              searchListenerTree(handlers, type, xxTree[branch], i+1);
            } else {
              isolatedBranch = {};
              isolatedBranch[branch] = xxTree[branch];
              searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1);
            }
          }
        }
      } else if(xxTree._listeners) {
        // We have reached the end and still on a '**'
        searchListenerTree(handlers, type, xxTree, typeLength);
      } else if(xxTree['*'] && xxTree['*']._listeners) {
        searchListenerTree(handlers, type, xxTree['*'], typeLength);
      }
    }

    return listeners;
  }

  function growListenerTree(type, listener) {

    type = typeof type === 'string' ? type.split(this.delimiter) : type.slice();

    //
    // Looks for two consecutive '**', if so, don't add the event at all.
    //
    for(var i = 0, len = type.length; i+1 < len; i++) {
      if(type[i] === '**' && type[i+1] === '**') {
        return;
      }
    }

    var tree = this.listenerTree;
    var name = type.shift();

    while (name) {

      if (!tree[name]) {
        tree[name] = {};
      }

      tree = tree[name];

      if (type.length === 0) {

        if (!tree._listeners) {
          tree._listeners = listener;
        }
        else if(typeof tree._listeners === 'function') {
          tree._listeners = [tree._listeners, listener];
        }
        else if (isArray(tree._listeners)) {

          tree._listeners.push(listener);

          if (!tree._listeners.warned) {

            var m = defaultMaxListeners;

            if (typeof this._events.maxListeners !== 'undefined') {
              m = this._events.maxListeners;
            }

            if (m > 0 && tree._listeners.length > m) {

              tree._listeners.warned = true;
              console.error('(node) warning: possible EventEmitter memory ' +
                            'leak detected. %d listeners added. ' +
                            'Use emitter.setMaxListeners() to increase limit.',
                            tree._listeners.length);
              console.trace();
            }
          }
        }
        return true;
      }
      name = type.shift();
    }
    return true;
  }

  // By default EventEmitters will print a warning if more than
  // 10 listeners are added to it. This is a useful default which
  // helps finding memory leaks.
  //
  // Obviously not all Emitters should be limited to 10. This function allows
  // that to be increased. Set to zero for unlimited.

  EventEmitter.prototype.delimiter = '.';

  EventEmitter.prototype.setMaxListeners = function(n) {
    this._events || init.call(this);
    this._events.maxListeners = n;
    if (!this._conf) this._conf = {};
    this._conf.maxListeners = n;
  };

  EventEmitter.prototype.event = '';

  EventEmitter.prototype.once = function(event, fn) {
    this.many(event, 1, fn);
    return this;
  };

  EventEmitter.prototype.many = function(event, ttl, fn) {
    var self = this;

    if (typeof fn !== 'function') {
      throw new Error('many only accepts instances of Function');
    }

    function listener() {
      if (--ttl === 0) {
        self.off(event, listener);
      }
      fn.apply(this, arguments);
    }

    listener._origin = fn;

    this.on(event, listener);

    return self;
  };

  EventEmitter.prototype.emit = function() {

    this._events || init.call(this);

    var type = arguments[0];

    if (type === 'newListener' && !this.newListener) {
      if (!this._events.newListener) { return false; }
    }

    // Loop through the *_all* functions and invoke them.
    if (this._all) {
      var l = arguments.length;
      var args = new Array(l - 1);
      for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
      for (i = 0, l = this._all.length; i < l; i++) {
        this.event = type;
        this._all[i].apply(this, args);
      }
    }

    // If there is no 'error' event listener then throw.
    if (type === 'error') {

      if (!this._all &&
        !this._events.error &&
        !(this.wildcard && this.listenerTree.error)) {

        if (arguments[1] instanceof Error) {
          throw arguments[1]; // Unhandled 'error' event
        } else {
          throw new Error("Uncaught, unspecified 'error' event.");
        }
        return false;
      }
    }

    var handler;

    if(this.wildcard) {
      handler = [];
      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
      searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
    }
    else {
      handler = this._events[type];
    }

    if (typeof handler === 'function') {
      this.event = type;
      if (arguments.length === 1) {
        handler.call(this);
      }
      else if (arguments.length > 1)
        switch (arguments.length) {
          case 2:
            handler.call(this, arguments[1]);
            break;
          case 3:
            handler.call(this, arguments[1], arguments[2]);
            break;
          // slower
          default:
            var l = arguments.length;
            var args = new Array(l - 1);
            for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
            handler.apply(this, args);
        }
      return true;
    }
    else if (handler) {
      var l = arguments.length;
      var args = new Array(l - 1);
      for (var i = 1; i < l; i++) args[i - 1] = arguments[i];

      var listeners = handler.slice();
      for (var i = 0, l = listeners.length; i < l; i++) {
        this.event = type;
        listeners[i].apply(this, args);
      }
      return (listeners.length > 0) || !!this._all;
    }
    else {
      return !!this._all;
    }

  };

  EventEmitter.prototype.on = function(type, listener) {

    if (typeof type === 'function') {
      this.onAny(type);
      return this;
    }

    if (typeof listener !== 'function') {
      throw new Error('on only accepts instances of Function');
    }
    this._events || init.call(this);

    // To avoid recursion in the case that type == "newListeners"! Before
    // adding it to the listeners, first emit "newListeners".
    this.emit('newListener', type, listener);

    if(this.wildcard) {
      growListenerTree.call(this, type, listener);
      return this;
    }

    if (!this._events[type]) {
      // Optimize the case of one listener. Don't need the extra array object.
      this._events[type] = listener;
    }
    else if(typeof this._events[type] === 'function') {
      // Adding the second element, need to change to array.
      this._events[type] = [this._events[type], listener];
    }
    else if (isArray(this._events[type])) {
      // If we've already got an array, just append.
      this._events[type].push(listener);

      // Check for listener leak
      if (!this._events[type].warned) {

        var m = defaultMaxListeners;

        if (typeof this._events.maxListeners !== 'undefined') {
          m = this._events.maxListeners;
        }

        if (m > 0 && this._events[type].length > m) {

          this._events[type].warned = true;
          console.error('(node) warning: possible EventEmitter memory ' +
                        'leak detected. %d listeners added. ' +
                        'Use emitter.setMaxListeners() to increase limit.',
                        this._events[type].length);
          console.trace();
        }
      }
    }
    return this;
  };

  EventEmitter.prototype.onAny = function(fn) {

    if (typeof fn !== 'function') {
      throw new Error('onAny only accepts instances of Function');
    }

    if(!this._all) {
      this._all = [];
    }

    // Add the function to the event listener collection.
    this._all.push(fn);
    return this;
  };

  EventEmitter.prototype.addListener = EventEmitter.prototype.on;

  EventEmitter.prototype.off = function(type, listener) {
    if (typeof listener !== 'function') {
      throw new Error('removeListener only takes instances of Function');
    }

    var handlers,leafs=[];

    if(this.wildcard) {
      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
      leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
    }
    else {
      // does not use listeners(), so no side effect of creating _events[type]
      if (!this._events[type]) return this;
      handlers = this._events[type];
      leafs.push({_listeners:handlers});
    }

    for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) {
      var leaf = leafs[iLeaf];
      handlers = leaf._listeners;
      if (isArray(handlers)) {

        var position = -1;

        for (var i = 0, length = handlers.length; i < length; i++) {
          if (handlers[i] === listener ||
            (handlers[i].listener && handlers[i].listener === listener) ||
            (handlers[i]._origin && handlers[i]._origin === listener)) {
            position = i;
            break;
          }
        }

        if (position < 0) {
          continue;
        }

        if(this.wildcard) {
          leaf._listeners.splice(position, 1);
        }
        else {
          this._events[type].splice(position, 1);
        }

        if (handlers.length === 0) {
          if(this.wildcard) {
            delete leaf._listeners;
          }
          else {
            delete this._events[type];
          }
        }
        return this;
      }
      else if (handlers === listener ||
        (handlers.listener && handlers.listener === listener) ||
        (handlers._origin && handlers._origin === listener)) {
        if(this.wildcard) {
          delete leaf._listeners;
        }
        else {
          delete this._events[type];
        }
      }
    }

    return this;
  };

  EventEmitter.prototype.offAny = function(fn) {
    var i = 0, l = 0, fns;
    if (fn && this._all && this._all.length > 0) {
      fns = this._all;
      for(i = 0, l = fns.length; i < l; i++) {
        if(fn === fns[i]) {
          fns.splice(i, 1);
          return this;
        }
      }
    } else {
      this._all = [];
    }
    return this;
  };

  EventEmitter.prototype.removeListener = EventEmitter.prototype.off;

  EventEmitter.prototype.removeAllListeners = function(type) {
    if (arguments.length === 0) {
      !this._events || init.call(this);
      return this;
    }

    if(this.wildcard) {
      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
      var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);

      for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) {
        var leaf = leafs[iLeaf];
        leaf._listeners = null;
      }
    }
    else {
      if (!this._events[type]) return this;
      this._events[type] = null;
    }
    return this;
  };

  EventEmitter.prototype.listeners = function(type) {
    if(this.wildcard) {
      var handlers = [];
      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
      searchListenerTree.call(this, handlers, ns, this.listenerTree, 0);
      return handlers;
    }

    this._events || init.call(this);

    if (!this._events[type]) this._events[type] = [];
    if (!isArray(this._events[type])) {
      this._events[type] = [this._events[type]];
    }
    return this._events[type];
  };

  EventEmitter.prototype.listenersAny = function() {

    if(this._all) {
      return this._all;
    }
    else {
      return [];
    }

  };

  if (typeof define === 'function' && define.amd) {
     // AMD. Register as an anonymous module.
    define(function() {
      return EventEmitter;
    });
  } else if (typeof exports === 'object') {
    // CommonJS
    exports.EventEmitter2 = EventEmitter;
  }
  else {
    // Browser global.
    window.EventEmitter2 = EventEmitter;
  }
}();

},{}],14:[function(require,module,exports){
(function (process){
/**
 * Copyright 2013-2015, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule invariant
 */

"use strict";

/**
 * Use invariant() to assert state which your program assumes to be true.
 *
 * Provide sprintf-style format (only %s is supported) and arguments
 * to provide information about what broke and what you were
 * expecting.
 *
 * The invariant message will be stripped in production, but the invariant
 * will remain to ensure logic does not differ in production.
 */

var invariant = function (condition, format, a, b, c, d, e, f) {
  if (process.env.NODE_ENV !== 'production') {
    if (format === undefined) {
      throw new Error('invariant requires an error message argument');
    }
  }

  if (!condition) {
    var error;
    if (format === undefined) {
      error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
    } else {
      var args = [a, b, c, d, e, f];
      var argIndex = 0;
      error = new Error('Invariant Violation: ' + format.replace(/%s/g, function () {
        return args[argIndex++];
      }));
    }

    error.framesToPop = 1; // we don't care about invariant's own frame
    throw error;
  }
};

module.exports = invariant;
}).call(this,require('_process'))
},{"_process":18}],15:[function(require,module,exports){
/**
 * Copyright 2013-2015, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule keyOf
 */

/**
 * Allows extraction of a minified key. Let's the build system minify keys
 * without losing the ability to dynamically use key strings as values
 * themselves. Pass in an object with a single key/val pair and it will return
 * you the string key of that single record. Suppose you want to grab the
 * value for a key 'className' inside of an object. Key/val minification may
 * have aliased that key to be 'xa12'. keyOf({className: null}) will return
 * 'xa12' in that case. Resolve keys you want to use once at startup time, then
 * reuse those resolutions.
 */
"use strict";

var keyOf = function (oneKeyObj) {
  var key;
  for (key in oneKeyObj) {
    if (!oneKeyObj.hasOwnProperty(key)) {
      continue;
    }
    return key;
  }
  return null;
};

module.exports = keyOf;
},{}],16:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

function EventEmitter() {
  this._events = this._events || {};
  this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;

// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;

EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;

// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;

// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function(n) {
  if (!isNumber(n) || n < 0 || isNaN(n))
    throw TypeError('n must be a positive number');
  this._maxListeners = n;
  return this;
};

EventEmitter.prototype.emit = function(type) {
  var er, handler, len, args, i, listeners;

  if (!this._events)
    this._events = {};

  // If there is no 'error' event listener then throw.
  if (type === 'error') {
    if (!this._events.error ||
        (isObject(this._events.error) && !this._events.error.length)) {
      er = arguments[1];
      if (er instanceof Error) {
        throw er; // Unhandled 'error' event
      }
      throw TypeError('Uncaught, unspecified "error" event.');
    }
  }

  handler = this._events[type];

  if (isUndefined(handler))
    return false;

  if (isFunction(handler)) {
    switch (arguments.length) {
      // fast cases
      case 1:
        handler.call(this);
        break;
      case 2:
        handler.call(this, arguments[1]);
        break;
      case 3:
        handler.call(this, arguments[1], arguments[2]);
        break;
      // slower
      default:
        len = arguments.length;
        args = new Array(len - 1);
        for (i = 1; i < len; i++)
          args[i - 1] = arguments[i];
        handler.apply(this, args);
    }
  } else if (isObject(handler)) {
    len = arguments.length;
    args = new Array(len - 1);
    for (i = 1; i < len; i++)
      args[i - 1] = arguments[i];

    listeners = handler.slice();
    len = listeners.length;
    for (i = 0; i < len; i++)
      listeners[i].apply(this, args);
  }

  return true;
};

EventEmitter.prototype.addListener = function(type, listener) {
  var m;

  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  if (!this._events)
    this._events = {};

  // To avoid recursion in the case that type === "newListener"! Before
  // adding it to the listeners, first emit "newListener".
  if (this._events.newListener)
    this.emit('newListener', type,
              isFunction(listener.listener) ?
              listener.listener : listener);

  if (!this._events[type])
    // Optimize the case of one listener. Don't need the extra array object.
    this._events[type] = listener;
  else if (isObject(this._events[type]))
    // If we've already got an array, just append.
    this._events[type].push(listener);
  else
    // Adding the second element, need to change to array.
    this._events[type] = [this._events[type], listener];

  // Check for listener leak
  if (isObject(this._events[type]) && !this._events[type].warned) {
    var m;
    if (!isUndefined(this._maxListeners)) {
      m = this._maxListeners;
    } else {
      m = EventEmitter.defaultMaxListeners;
    }

    if (m && m > 0 && this._events[type].length > m) {
      this._events[type].warned = true;
      console.error('(node) warning: possible EventEmitter memory ' +
                    'leak detected. %d listeners added. ' +
                    'Use emitter.setMaxListeners() to increase limit.',
                    this._events[type].length);
      if (typeof console.trace === 'function') {
        // not supported in IE 10
        console.trace();
      }
    }
  }

  return this;
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

EventEmitter.prototype.once = function(type, listener) {
  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  var fired = false;

  function g() {
    this.removeListener(type, g);

    if (!fired) {
      fired = true;
      listener.apply(this, arguments);
    }
  }

  g.listener = listener;
  this.on(type, g);

  return this;
};

// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
  var list, position, length, i;

  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  if (!this._events || !this._events[type])
    return this;

  list = this._events[type];
  length = list.length;
  position = -1;

  if (list === listener ||
      (isFunction(list.listener) && list.listener === listener)) {
    delete this._events[type];
    if (this._events.removeListener)
      this.emit('removeListener', type, listener);

  } else if (isObject(list)) {
    for (i = length; i-- > 0;) {
      if (list[i] === listener ||
          (list[i].listener && list[i].listener === listener)) {
        position = i;
        break;
      }
    }

    if (position < 0)
      return this;

    if (list.length === 1) {
      list.length = 0;
      delete this._events[type];
    } else {
      list.splice(position, 1);
    }

    if (this._events.removeListener)
      this.emit('removeListener', type, listener);
  }

  return this;
};

EventEmitter.prototype.removeAllListeners = function(type) {
  var key, listeners;

  if (!this._events)
    return this;

  // not listening for removeListener, no need to emit
  if (!this._events.removeListener) {
    if (arguments.length === 0)
      this._events = {};
    else if (this._events[type])
      delete this._events[type];
    return this;
  }

  // emit removeListener for all listeners on all events
  if (arguments.length === 0) {
    for (key in this._events) {
      if (key === 'removeListener') continue;
      this.removeAllListeners(key);
    }
    this.removeAllListeners('removeListener');
    this._events = {};
    return this;
  }

  listeners = this._events[type];

  if (isFunction(listeners)) {
    this.removeListener(type, listeners);
  } else {
    // LIFO order
    while (listeners.length)
      this.removeListener(type, listeners[listeners.length - 1]);
  }
  delete this._events[type];

  return this;
};

EventEmitter.prototype.listeners = function(type) {
  var ret;
  if (!this._events || !this._events[type])
    ret = [];
  else if (isFunction(this._events[type]))
    ret = [this._events[type]];
  else
    ret = this._events[type].slice();
  return ret;
};

EventEmitter.listenerCount = function(emitter, type) {
  var ret;
  if (!emitter._events || !emitter._events[type])
    ret = 0;
  else if (isFunction(emitter._events[type]))
    ret = 1;
  else
    ret = emitter._events[type].length;
  return ret;
};

function isFunction(arg) {
  return typeof arg === 'function';
}

function isNumber(arg) {
  return typeof arg === 'number';
}

function isObject(arg) {
  return typeof arg === 'object' && arg !== null;
}

function isUndefined(arg) {
  return arg === void 0;
}

},{}],17:[function(require,module,exports){
if (typeof Object.create === 'function') {
  // implementation from standard node.js 'util' module
  module.exports = function inherits(ctor, superCtor) {
    ctor.super_ = superCtor
    ctor.prototype = Object.create(superCtor.prototype, {
      constructor: {
        value: ctor,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
  };
} else {
  // old school shim for old browsers
  module.exports = function inherits(ctor, superCtor) {
    ctor.super_ = superCtor
    var TempCtor = function () {}
    TempCtor.prototype = superCtor.prototype
    ctor.prototype = new TempCtor()
    ctor.prototype.constructor = ctor
  }
}

},{}],18:[function(require,module,exports){
// shim for using process in browser

var process = module.exports = {};
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;

function cleanUpNextTick() {
    draining = false;
    if (currentQueue.length) {
        queue = currentQueue.concat(queue);
    } else {
        queueIndex = -1;
    }
    if (queue.length) {
        drainQueue();
    }
}

function drainQueue() {
    if (draining) {
        return;
    }
    var timeout = setTimeout(cleanUpNextTick);
    draining = true;

    var len = queue.length;
    while(len) {
        currentQueue = queue;
        queue = [];
        while (++queueIndex < len) {
            if (currentQueue) {
                currentQueue[queueIndex].run();
            }
        }
        queueIndex = -1;
        len = queue.length;
    }
    currentQueue = null;
    draining = false;
    clearTimeout(timeout);
}

process.nextTick = function (fun) {
    var args = new Array(arguments.length - 1);
    if (arguments.length > 1) {
        for (var i = 1; i < arguments.length; i++) {
            args[i - 1] = arguments[i];
        }
    }
    queue.push(new Item(fun, args));
    if (queue.length === 1 && !draining) {
        setTimeout(drainQueue, 0);
    }
};

// v8 likes predictible objects
function Item(fun, array) {
    this.fun = fun;
    this.array = array;
}
Item.prototype.run = function () {
    this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};

function noop() {}

process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;

process.binding = function (name) {
    throw new Error('process.binding is not supported');
};

process.cwd = function () { return '/' };
process.chdir = function (dir) {
    throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };

},{}],19:[function(require,module,exports){
module.exports = function isBuffer(arg) {
  return arg && typeof arg === 'object'
    && typeof arg.copy === 'function'
    && typeof arg.fill === 'function'
    && typeof arg.readUInt8 === 'function';
}
},{}],20:[function(require,module,exports){
(function (process,global){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var formatRegExp = /%[sdj%]/g;
exports.format = function(f) {
  if (!isString(f)) {
    var objects = [];
    for (var i = 0; i < arguments.length; i++) {
      objects.push(inspect(arguments[i]));
    }
    return objects.join(' ');
  }

  var i = 1;
  var args = arguments;
  var len = args.length;
  var str = String(f).replace(formatRegExp, function(x) {
    if (x === '%%') return '%';
    if (i >= len) return x;
    switch (x) {
      case '%s': return String(args[i++]);
      case '%d': return Number(args[i++]);
      case '%j':
        try {
          return JSON.stringify(args[i++]);
        } catch (_) {
          return '[Circular]';
        }
      default:
        return x;
    }
  });
  for (var x = args[i]; i < len; x = args[++i]) {
    if (isNull(x) || !isObject(x)) {
      str += ' ' + x;
    } else {
      str += ' ' + inspect(x);
    }
  }
  return str;
};


// Mark that a method should not be used.
// Returns a modified function which warns once by default.
// If --no-deprecation is set, then it is a no-op.
exports.deprecate = function(fn, msg) {
  // Allow for deprecating things in the process of starting up.
  if (isUndefined(global.process)) {
    return function() {
      return exports.deprecate(fn, msg).apply(this, arguments);
    };
  }

  if (process.noDeprecation === true) {
    return fn;
  }

  var warned = false;
  function deprecated() {
    if (!warned) {
      if (process.throwDeprecation) {
        throw new Error(msg);
      } else if (process.traceDeprecation) {
        console.trace(msg);
      } else {
        console.error(msg);
      }
      warned = true;
    }
    return fn.apply(this, arguments);
  }

  return deprecated;
};


var debugs = {};
var debugEnviron;
exports.debuglog = function(set) {
  if (isUndefined(debugEnviron))
    debugEnviron = process.env.NODE_DEBUG || '';
  set = set.toUpperCase();
  if (!debugs[set]) {
    if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
      var pid = process.pid;
      debugs[set] = function() {
        var msg = exports.format.apply(exports, arguments);
        console.error('%s %d: %s', set, pid, msg);
      };
    } else {
      debugs[set] = function() {};
    }
  }
  return debugs[set];
};


/**
 * Echos the value of a value. Trys to print the value out
 * in the best way possible given the different types.
 *
 * @param {Object} obj The object to print out.
 * @param {Object} opts Optional options object that alters the output.
 */
/* legacy: obj, showHidden, depth, colors*/
function inspect(obj, opts) {
  // default options
  var ctx = {
    seen: [],
    stylize: stylizeNoColor
  };
  // legacy...
  if (arguments.length >= 3) ctx.depth = arguments[2];
  if (arguments.length >= 4) ctx.colors = arguments[3];
  if (isBoolean(opts)) {
    // legacy...
    ctx.showHidden = opts;
  } else if (opts) {
    // got an "options" object
    exports._extend(ctx, opts);
  }
  // set default options
  if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
  if (isUndefined(ctx.depth)) ctx.depth = 2;
  if (isUndefined(ctx.colors)) ctx.colors = false;
  if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
  if (ctx.colors) ctx.stylize = stylizeWithColor;
  return formatValue(ctx, obj, ctx.depth);
}
exports.inspect = inspect;


// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
inspect.colors = {
  'bold' : [1, 22],
  'italic' : [3, 23],
  'underline' : [4, 24],
  'inverse' : [7, 27],
  'white' : [37, 39],
  'grey' : [90, 39],
  'black' : [30, 39],
  'blue' : [34, 39],
  'cyan' : [36, 39],
  'green' : [32, 39],
  'magenta' : [35, 39],
  'red' : [31, 39],
  'yellow' : [33, 39]
};

// Don't use 'blue' not visible on cmd.exe
inspect.styles = {
  'special': 'cyan',
  'number': 'yellow',
  'boolean': 'yellow',
  'undefined': 'grey',
  'null': 'bold',
  'string': 'green',
  'date': 'magenta',
  // "name": intentionally not styling
  'regexp': 'red'
};


function stylizeWithColor(str, styleType) {
  var style = inspect.styles[styleType];

  if (style) {
    return '\u001b[' + inspect.colors[style][0] + 'm' + str +
           '\u001b[' + inspect.colors[style][1] + 'm';
  } else {
    return str;
  }
}


function stylizeNoColor(str, styleType) {
  return str;
}


function arrayToHash(array) {
  var hash = {};

  array.forEach(function(val, idx) {
    hash[val] = true;
  });

  return hash;
}


function formatValue(ctx, value, recurseTimes) {
  // Provide a hook for user-specified inspect functions.
  // Check that value is an object with an inspect function on it
  if (ctx.customInspect &&
      value &&
      isFunction(value.inspect) &&
      // Filter out the util module, it's inspect function is special
      value.inspect !== exports.inspect &&
      // Also filter out any prototype objects using the circular check.
      !(value.constructor && value.constructor.prototype === value)) {
    var ret = value.inspect(recurseTimes, ctx);
    if (!isString(ret)) {
      ret = formatValue(ctx, ret, recurseTimes);
    }
    return ret;
  }

  // Primitive types cannot have properties
  var primitive = formatPrimitive(ctx, value);
  if (primitive) {
    return primitive;
  }

  // Look up the keys of the object.
  var keys = Object.keys(value);
  var visibleKeys = arrayToHash(keys);

  if (ctx.showHidden) {
    keys = Object.getOwnPropertyNames(value);
  }

  // IE doesn't make error fields non-enumerable
  // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
  if (isError(value)
      && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
    return formatError(value);
  }

  // Some type of object without properties can be shortcutted.
  if (keys.length === 0) {
    if (isFunction(value)) {
      var name = value.name ? ': ' + value.name : '';
      return ctx.stylize('[Function' + name + ']', 'special');
    }
    if (isRegExp(value)) {
      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
    }
    if (isDate(value)) {
      return ctx.stylize(Date.prototype.toString.call(value), 'date');
    }
    if (isError(value)) {
      return formatError(value);
    }
  }

  var base = '', array = false, braces = ['{', '}'];

  // Make Array say that they are Array
  if (isArray(value)) {
    array = true;
    braces = ['[', ']'];
  }

  // Make functions say that they are functions
  if (isFunction(value)) {
    var n = value.name ? ': ' + value.name : '';
    base = ' [Function' + n + ']';
  }

  // Make RegExps say that they are RegExps
  if (isRegExp(value)) {
    base = ' ' + RegExp.prototype.toString.call(value);
  }

  // Make dates with properties first say the date
  if (isDate(value)) {
    base = ' ' + Date.prototype.toUTCString.call(value);
  }

  // Make error with message first say the error
  if (isError(value)) {
    base = ' ' + formatError(value);
  }

  if (keys.length === 0 && (!array || value.length == 0)) {
    return braces[0] + base + braces[1];
  }

  if (recurseTimes < 0) {
    if (isRegExp(value)) {
      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
    } else {
      return ctx.stylize('[Object]', 'special');
    }
  }

  ctx.seen.push(value);

  var output;
  if (array) {
    output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
  } else {
    output = keys.map(function(key) {
      return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
    });
  }

  ctx.seen.pop();

  return reduceToSingleString(output, base, braces);
}


function formatPrimitive(ctx, value) {
  if (isUndefined(value))
    return ctx.stylize('undefined', 'undefined');
  if (isString(value)) {
    var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
                                             .replace(/'/g, "\\'")
                                             .replace(/\\"/g, '"') + '\'';
    return ctx.stylize(simple, 'string');
  }
  if (isNumber(value))
    return ctx.stylize('' + value, 'number');
  if (isBoolean(value))
    return ctx.stylize('' + value, 'boolean');
  // For some reason typeof null is "object", so special case here.
  if (isNull(value))
    return ctx.stylize('null', 'null');
}


function formatError(value) {
  return '[' + Error.prototype.toString.call(value) + ']';
}


function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
  var output = [];
  for (var i = 0, l = value.length; i < l; ++i) {
    if (hasOwnProperty(value, String(i))) {
      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
          String(i), true));
    } else {
      output.push('');
    }
  }
  keys.forEach(function(key) {
    if (!key.match(/^\d+$/)) {
      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
          key, true));
    }
  });
  return output;
}


function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
  var name, str, desc;
  desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
  if (desc.get) {
    if (desc.set) {
      str = ctx.stylize('[Getter/Setter]', 'special');
    } else {
      str = ctx.stylize('[Getter]', 'special');
    }
  } else {
    if (desc.set) {
      str = ctx.stylize('[Setter]', 'special');
    }
  }
  if (!hasOwnProperty(visibleKeys, key)) {
    name = '[' + key + ']';
  }
  if (!str) {
    if (ctx.seen.indexOf(desc.value) < 0) {
      if (isNull(recurseTimes)) {
        str = formatValue(ctx, desc.value, null);
      } else {
        str = formatValue(ctx, desc.value, recurseTimes - 1);
      }
      if (str.indexOf('\n') > -1) {
        if (array) {
          str = str.split('\n').map(function(line) {
            return '  ' + line;
          }).join('\n').substr(2);
        } else {
          str = '\n' + str.split('\n').map(function(line) {
            return '   ' + line;
          }).join('\n');
        }
      }
    } else {
      str = ctx.stylize('[Circular]', 'special');
    }
  }
  if (isUndefined(name)) {
    if (array && key.match(/^\d+$/)) {
      return str;
    }
    name = JSON.stringify('' + key);
    if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
      name = name.substr(1, name.length - 2);
      name = ctx.stylize(name, 'name');
    } else {
      name = name.replace(/'/g, "\\'")
                 .replace(/\\"/g, '"')
                 .replace(/(^"|"$)/g, "'");
      name = ctx.stylize(name, 'string');
    }
  }

  return name + ': ' + str;
}


function reduceToSingleString(output, base, braces) {
  var numLinesEst = 0;
  var length = output.reduce(function(prev, cur) {
    numLinesEst++;
    if (cur.indexOf('\n') >= 0) numLinesEst++;
    return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
  }, 0);

  if (length > 60) {
    return braces[0] +
           (base === '' ? '' : base + '\n ') +
           ' ' +
           output.join(',\n  ') +
           ' ' +
           braces[1];
  }

  return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
}


// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
  return Array.isArray(ar);
}
exports.isArray = isArray;

function isBoolean(arg) {
  return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;

function isNull(arg) {
  return arg === null;
}
exports.isNull = isNull;

function isNullOrUndefined(arg) {
  return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;

function isNumber(arg) {
  return typeof arg === 'number';
}
exports.isNumber = isNumber;

function isString(arg) {
  return typeof arg === 'string';
}
exports.isString = isString;

function isSymbol(arg) {
  return typeof arg === 'symbol';
}
exports.isSymbol = isSymbol;

function isUndefined(arg) {
  return arg === void 0;
}
exports.isUndefined = isUndefined;

function isRegExp(re) {
  return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;

function isObject(arg) {
  return typeof arg === 'object' && arg !== null;
}
exports.isObject = isObject;

function isDate(d) {
  return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;

function isError(e) {
  return isObject(e) &&
      (objectToString(e) === '[object Error]' || e instanceof Error);
}
exports.isError = isError;

function isFunction(arg) {
  return typeof arg === 'function';
}
exports.isFunction = isFunction;

function isPrimitive(arg) {
  return arg === null ||
         typeof arg === 'boolean' ||
         typeof arg === 'number' ||
         typeof arg === 'string' ||
         typeof arg === 'symbol' ||  // ES6 symbol
         typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;

exports.isBuffer = require('./support/isBuffer');

function objectToString(o) {
  return Object.prototype.toString.call(o);
}


function pad(n) {
  return n < 10 ? '0' + n.toString(10) : n.toString(10);
}


var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
              'Oct', 'Nov', 'Dec'];

// 26 Feb 16:19:34
function timestamp() {
  var d = new Date();
  var time = [pad(d.getHours()),
              pad(d.getMinutes()),
              pad(d.getSeconds())].join(':');
  return [d.getDate(), months[d.getMonth()], time].join(' ');
}


// log is just a thin wrapper to console.log that prepends a timestamp
exports.log = function() {
  console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
};


/**
 * Inherit the prototype methods from one constructor into another.
 *
 * The Function.prototype.inherits from lang.js rewritten as a standalone
 * function (not on Function.prototype). NOTE: If this file is to be loaded
 * during bootstrapping this function needs to be rewritten using some native
 * functions as prototype setup using normal JavaScript does not work as
 * expected during bootstrapping (see mirror.js in r114903).
 *
 * @param {function} ctor Constructor function which needs to inherit the
 *     prototype.
 * @param {function} superCtor Constructor function to inherit prototype from.
 */
exports.inherits = require('inherits');

exports._extend = function(origin, add) {
  // Don't do anything if add isn't an object
  if (!add || !isObject(add)) return origin;

  var keys = Object.keys(add);
  var i = keys.length;
  while (i--) {
    origin[keys[i]] = add[keys[i]];
  }
  return origin;
};

function hasOwnProperty(obj, prop) {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}

}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./support/isBuffer":19,"_process":18,"inherits":17}],21:[function(require,module,exports){
/*global define:false */
/**
 * Copyright 2016 Craig Campbell
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Mousetrap is a simple keyboard shortcut library for Javascript with
 * no external dependencies
 *
 * @version 1.6.0
 * @url craig.is/killing/mice
 */
(function(window, document, undefined) {

    // Check if mousetrap is used inside browser, if not, return
    if (!window) {
        return;
    }

    /**
     * mapping of special keycodes to their corresponding keys
     *
     * everything in this dictionary cannot use keypress events
     * so it has to be here to map to the correct keycodes for
     * keyup/keydown events
     *
     * @type {Object}
     */
    var _MAP = {
        8: 'backspace',
        9: 'tab',
        13: 'enter',
        16: 'shift',
        17: 'ctrl',
        18: 'alt',
        20: 'capslock',
        27: 'esc',
        32: 'space',
        33: 'pageup',
        34: 'pagedown',
        35: 'end',
        36: 'home',
        37: 'left',
        38: 'up',
        39: 'right',
        40: 'down',
        45: 'ins',
        46: 'del',
        91: 'meta',
        93: 'meta',
        224: 'meta'
    };

    /**
     * mapping for special characters so they can support
     *
     * this dictionary is only used incase you want to bind a
     * keyup or keydown event to one of these keys
     *
     * @type {Object}
     */
    var _KEYCODE_MAP = {
        106: '*',
        107: '+',
        109: '-',
        110: '.',
        111 : '/',
        186: ';',
        187: '=',
        188: ',',
        189: '-',
        190: '.',
        191: '/',
        192: '`',
        219: '[',
        220: '\\',
        221: ']',
        222: '\''
    };

    /**
     * this is a mapping of keys that require shift on a US keypad
     * back to the non shift equivelents
     *
     * this is so you can use keyup events with these keys
     *
     * note that this will only work reliably on US keyboards
     *
     * @type {Object}
     */
    var _SHIFT_MAP = {
        '~': '`',
        '!': '1',
        '@': '2',
        '#': '3',
        '$': '4',
        '%': '5',
        '^': '6',
        '&': '7',
        '*': '8',
        '(': '9',
        ')': '0',
        '_': '-',
        '+': '=',
        ':': ';',
        '\"': '\'',
        '<': ',',
        '>': '.',
        '?': '/',
        '|': '\\'
    };

    /**
     * this is a list of special strings you can use to map
     * to modifier keys when you specify your keyboard shortcuts
     *
     * @type {Object}
     */
    var _SPECIAL_ALIASES = {
        'option': 'alt',
        'command': 'meta',
        'return': 'enter',
        'escape': 'esc',
        'plus': '+',
        'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl'
    };

    /**
     * variable to store the flipped version of _MAP from above
     * needed to check if we should use keypress or not when no action
     * is specified
     *
     * @type {Object|undefined}
     */
    var _REVERSE_MAP;

    /**
     * loop through the f keys, f1 to f19 and add them to the map
     * programatically
     */
    for (var i = 1; i < 20; ++i) {
        _MAP[111 + i] = 'f' + i;
    }

    /**
     * loop through to map numbers on the numeric keypad
     */
    for (i = 0; i <= 9; ++i) {
        _MAP[i + 96] = i;
    }

    /**
     * cross browser add event method
     *
     * @param {Element|HTMLDocument} object
     * @param {string} type
     * @param {Function} callback
     * @returns void
     */
    function _addEvent(object, type, callback) {
        if (object.addEventListener) {
            object.addEventListener(type, callback, false);
            return;
        }

        object.attachEvent('on' + type, callback);
    }

    /**
     * takes the event and returns the key character
     *
     * @param {Event} e
     * @return {string}
     */
    function _characterFromEvent(e) {

        // for keypress events we should return the character as is
        if (e.type == 'keypress') {
            var character = String.fromCharCode(e.which);

            // if the shift key is not pressed then it is safe to assume
            // that we want the character to be lowercase.  this means if
            // you accidentally have caps lock on then your key bindings
            // will continue to work
            //
            // the only side effect that might not be desired is if you
            // bind something like 'A' cause you want to trigger an
            // event when capital A is pressed caps lock will no longer
            // trigger the event.  shift+a will though.
            if (!e.shiftKey) {
                character = character.toLowerCase();
            }

            return character;
        }

        // for non keypress events the special maps are needed
        if (_MAP[e.which]) {
            return _MAP[e.which];
        }

        if (_KEYCODE_MAP[e.which]) {
            return _KEYCODE_MAP[e.which];
        }

        // if it is not in the special map

        // with keydown and keyup events the character seems to always
        // come in as an uppercase character whether you are pressing shift
        // or not.  we should make sure it is always lowercase for comparisons
        return String.fromCharCode(e.which).toLowerCase();
    }

    /**
     * checks if two arrays are equal
     *
     * @param {Array} modifiers1
     * @param {Array} modifiers2
     * @returns {boolean}
     */
    function _modifiersMatch(modifiers1, modifiers2) {
        return modifiers1.sort().join(',') === modifiers2.sort().join(',');
    }

    /**
     * takes a key event and figures out what the modifiers are
     *
     * @param {Event} e
     * @returns {Array}
     */
    function _eventModifiers(e) {
        var modifiers = [];

        if (e.shiftKey) {
            modifiers.push('shift');
        }

        if (e.altKey) {
            modifiers.push('alt');
        }

        if (e.ctrlKey) {
            modifiers.push('ctrl');
        }

        if (e.metaKey) {
            modifiers.push('meta');
        }

        return modifiers;
    }

    /**
     * prevents default for this event
     *
     * @param {Event} e
     * @returns void
     */
    function _preventDefault(e) {
        if (e.preventDefault) {
            e.preventDefault();
            return;
        }

        e.returnValue = false;
    }

    /**
     * stops propogation for this event
     *
     * @param {Event} e
     * @returns void
     */
    function _stopPropagation(e) {
        if (e.stopPropagation) {
            e.stopPropagation();
            return;
        }

        e.cancelBubble = true;
    }

    /**
     * determines if the keycode specified is a modifier key or not
     *
     * @param {string} key
     * @returns {boolean}
     */
    function _isModifier(key) {
        return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';
    }

    /**
     * reverses the map lookup so that we can look for specific keys
     * to see what can and can't use keypress
     *
     * @return {Object}
     */
    function _getReverseMap() {
        if (!_REVERSE_MAP) {
            _REVERSE_MAP = {};
            for (var key in _MAP) {

                // pull out the numeric keypad from here cause keypress should
                // be able to detect the keys from the character
                if (key > 95 && key < 112) {
                    continue;
                }

                if (_MAP.hasOwnProperty(key)) {
                    _REVERSE_MAP[_MAP[key]] = key;
                }
            }
        }
        return _REVERSE_MAP;
    }

    /**
     * picks the best action based on the key combination
     *
     * @param {string} key - character for key
     * @param {Array} modifiers
     * @param {string=} action passed in
     */
    function _pickBestAction(key, modifiers, action) {

        // if no action was picked in we should try to pick the one
        // that we think would work best for this key
        if (!action) {
            action = _getReverseMap()[key] ? 'keydown' : 'keypress';
        }

        // modifier keys don't work as expected with keypress,
        // switch to keydown
        if (action == 'keypress' && modifiers.length) {
            action = 'keydown';
        }

        return action;
    }

    /**
     * Converts from a string key combination to an array
     *
     * @param  {string} combination like "command+shift+l"
     * @return {Array}
     */
    function _keysFromString(combination) {
        if (combination === '+') {
            return ['+'];
        }

        combination = combination.replace(/\+{2}/g, '+plus');
        return combination.split('+');
    }

    /**
     * Gets info for a specific key combination
     *
     * @param  {string} combination key combination ("command+s" or "a" or "*")
     * @param  {string=} action
     * @returns {Object}
     */
    function _getKeyInfo(combination, action) {
        var keys;
        var key;
        var i;
        var modifiers = [];

        // take the keys from this pattern and figure out what the actual
        // pattern is all about
        keys = _keysFromString(combination);

        for (i = 0; i < keys.length; ++i) {
            key = keys[i];

            // normalize key names
            if (_SPECIAL_ALIASES[key]) {
                key = _SPECIAL_ALIASES[key];
            }

            // if this is not a keypress event then we should
            // be smart about using shift keys
            // this will only work for US keyboards however
            if (action && action != 'keypress' && _SHIFT_MAP[key]) {
                key = _SHIFT_MAP[key];
                modifiers.push('shift');
            }

            // if this key is a modifier then add it to the list of modifiers
            if (_isModifier(key)) {
                modifiers.push(key);
            }
        }

        // depending on what the key combination is
        // we will try to pick the best event for it
        action = _pickBestAction(key, modifiers, action);

        return {
            key: key,
            modifiers: modifiers,
            action: action
        };
    }

    function _belongsTo(element, ancestor) {
        if (element === null || element === document) {
            return false;
        }

        if (element === ancestor) {
            return true;
        }

        return _belongsTo(element.parentNode, ancestor);
    }

    function Mousetrap(targetElement) {
        var self = this;

        targetElement = targetElement || document;

        if (!(self instanceof Mousetrap)) {
            return new Mousetrap(targetElement);
        }

        /**
         * element to attach key events to
         *
         * @type {Element}
         */
        self.target = targetElement;

        /**
         * a list of all the callbacks setup via Mousetrap.bind()
         *
         * @type {Object}
         */
        self._callbacks = {};

        /**
         * direct map of string combinations to callbacks used for trigger()
         *
         * @type {Object}
         */
        self._directMap = {};

        /**
         * keeps track of what level each sequence is at since multiple
         * sequences can start out with the same sequence
         *
         * @type {Object}
         */
        var _sequenceLevels = {};

        /**
         * variable to store the setTimeout call
         *
         * @type {null|number}
         */
        var _resetTimer;

        /**
         * temporary state where we will ignore the next keyup
         *
         * @type {boolean|string}
         */
        var _ignoreNextKeyup = false;

        /**
         * temporary state where we will ignore the next keypress
         *
         * @type {boolean}
         */
        var _ignoreNextKeypress = false;

        /**
         * are we currently inside of a sequence?
         * type of action ("keyup" or "keydown" or "keypress") or false
         *
         * @type {boolean|string}
         */
        var _nextExpectedAction = false;

        /**
         * resets all sequence counters except for the ones passed in
         *
         * @param {Object} doNotReset
         * @returns void
         */
        function _resetSequences(doNotReset) {
            doNotReset = doNotReset || {};

            var activeSequences = false,
                key;

            for (key in _sequenceLevels) {
                if (doNotReset[key]) {
                    activeSequences = true;
                    continue;
                }
                _sequenceLevels[key] = 0;
            }

            if (!activeSequences) {
                _nextExpectedAction = false;
            }
        }

        /**
         * finds all callbacks that match based on the keycode, modifiers,
         * and action
         *
         * @param {string} character
         * @param {Array} modifiers
         * @param {Event|Object} e
         * @param {string=} sequenceName - name of the sequence we are looking for
         * @param {string=} combination
         * @param {number=} level
         * @returns {Array}
         */
        function _getMatches(character, modifiers, e, sequenceName, combination, level) {
            var i;
            var callback;
            var matches = [];
            var action = e.type;

            // if there are no events related to this keycode
            if (!self._callbacks[character]) {
                return [];
            }

            // if a modifier key is coming up on its own we should allow it
            if (action == 'keyup' && _isModifier(character)) {
                modifiers = [character];
            }

            // loop through all callbacks for the key that was pressed
            // and see if any of them match
            for (i = 0; i < self._callbacks[character].length; ++i) {
                callback = self._callbacks[character][i];

                // if a sequence name is not specified, but this is a sequence at
                // the wrong level then move onto the next match
                if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) {
                    continue;
                }

                // if the action we are looking for doesn't match the action we got
                // then we should keep going
                if (action != callback.action) {
                    continue;
                }

                // if this is a keypress event and the meta key and control key
                // are not pressed that means that we need to only look at the
                // character, otherwise check the modifiers as well
                //
                // chrome will not fire a keypress if meta or control is down
                // safari will fire a keypress if meta or meta+shift is down
                // firefox will fire a keypress if meta or control is down
                if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || _modifiersMatch(modifiers, callback.modifiers)) {

                    // when you bind a combination or sequence a second time it
                    // should overwrite the first one.  if a sequenceName or
                    // combination is specified in this call it does just that
                    //
                    // @todo make deleting its own method?
                    var deleteCombo = !sequenceName && callback.combo == combination;
                    var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level;
                    if (deleteCombo || deleteSequence) {
                        self._callbacks[character].splice(i, 1);
                    }

                    matches.push(callback);
                }
            }

            return matches;
        }

        /**
         * actually calls the callback function
         *
         * if your callback function returns false this will use the jquery
         * convention - prevent default and stop propogation on the event
         *
         * @param {Function} callback
         * @param {Event} e
         * @returns void
         */
        function _fireCallback(callback, e, combo, sequence) {

            // if this event should not happen stop here
            if (self.stopCallback(e, e.target || e.srcElement, combo, sequence)) {
                return;
            }

            if (callback(e, combo) === false) {
                _preventDefault(e);
                _stopPropagation(e);
            }
        }

        /**
         * handles a character key event
         *
         * @param {string} character
         * @param {Array} modifiers
         * @param {Event} e
         * @returns void
         */
        self._handleKey = function(character, modifiers, e) {
            var callbacks = _getMatches(character, modifiers, e);
            var i;
            var doNotReset = {};
            var maxLevel = 0;
            var processedSequenceCallback = false;

            // Calculate the maxLevel for sequences so we can only execute the longest callback sequence
            for (i = 0; i < callbacks.length; ++i) {
                if (callbacks[i].seq) {
                    maxLevel = Math.max(maxLevel, callbacks[i].level);
                }
            }

            // loop through matching callbacks for this key event
            for (i = 0; i < callbacks.length; ++i) {

                // fire for all sequence callbacks
                // this is because if for example you have multiple sequences
                // bound such as "g i" and "g t" they both need to fire the
                // callback for matching g cause otherwise you can only ever
                // match the first one
                if (callbacks[i].seq) {

                    // only fire callbacks for the maxLevel to prevent
                    // subsequences from also firing
                    //
                    // for example 'a option b' should not cause 'option b' to fire
                    // even though 'option b' is part of the other sequence
                    //
                    // any sequences that do not match here will be discarded
                    // below by the _resetSequences call
                    if (callbacks[i].level != maxLevel) {
                        continue;
                    }

                    processedSequenceCallback = true;

                    // keep a list of which sequences were matches for later
                    doNotReset[callbacks[i].seq] = 1;
                    _fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq);
                    continue;
                }

                // if there were no sequence matches but we are still here
                // that means this is a regular match so we should fire that
                if (!processedSequenceCallback) {
                    _fireCallback(callbacks[i].callback, e, callbacks[i].combo);
                }
            }

            // if the key you pressed matches the type of sequence without
            // being a modifier (ie "keyup" or "keypress") then we should
            // reset all sequences that were not matched by this event
            //
            // this is so, for example, if you have the sequence "h a t" and you
            // type "h e a r t" it does not match.  in this case the "e" will
            // cause the sequence to reset
            //
            // modifier keys are ignored because you can have a sequence
            // that contains modifiers such as "enter ctrl+space" and in most
            // cases the modifier key will be pressed before the next key
            //
            // also if you have a sequence such as "ctrl+b a" then pressing the
            // "b" key will trigger a "keypress" and a "keydown"
            //
            // the "keydown" is expected when there is a modifier, but the
            // "keypress" ends up matching the _nextExpectedAction since it occurs
            // after and that causes the sequence to reset
            //
            // we ignore keypresses in a sequence that directly follow a keydown
            // for the same character
            var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress;
            if (e.type == _nextExpectedAction && !_isModifier(character) && !ignoreThisKeypress) {
                _resetSequences(doNotReset);
            }

            _ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown';
        };

        /**
         * handles a keydown event
         *
         * @param {Event} e
         * @returns void
         */
        function _handleKeyEvent(e) {

            // normalize e.which for key events
            // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion
            if (typeof e.which !== 'number') {
                e.which = e.keyCode;
            }

            var character = _characterFromEvent(e);

            // no character found then stop
            if (!character) {
                return;
            }

            // need to use === for the character check because the character can be 0
            if (e.type == 'keyup' && _ignoreNextKeyup === character) {
                _ignoreNextKeyup = false;
                return;
            }

            self.handleKey(character, _eventModifiers(e), e);
        }

        /**
         * called to set a 1 second timeout on the specified sequence
         *
         * this is so after each key press in the sequence you have 1 second
         * to press the next key before you have to start over
         *
         * @returns void
         */
        function _resetSequenceTimer() {
            clearTimeout(_resetTimer);
            _resetTimer = setTimeout(_resetSequences, 1000);
        }

        /**
         * binds a key sequence to an event
         *
         * @param {string} combo - combo specified in bind call
         * @param {Array} keys
         * @param {Function} callback
         * @param {string=} action
         * @returns void
         */
        function _bindSequence(combo, keys, callback, action) {

            // start off by adding a sequence level record for this combination
            // and setting the level to 0
            _sequenceLevels[combo] = 0;

            /**
             * callback to increase the sequence level for this sequence and reset
             * all other sequences that were active
             *
             * @param {string} nextAction
             * @returns {Function}
             */
            function _increaseSequence(nextAction) {
                return function() {
                    _nextExpectedAction = nextAction;
                    ++_sequenceLevels[combo];
                    _resetSequenceTimer();
                };
            }

            /**
             * wraps the specified callback inside of another function in order
             * to reset all sequence counters as soon as this sequence is done
             *
             * @param {Event} e
             * @returns void
             */
            function _callbackAndReset(e) {
                _fireCallback(callback, e, combo);

                // we should ignore the next key up if the action is key down
                // or keypress.  this is so if you finish a sequence and
                // release the key the final key will not trigger a keyup
                if (action !== 'keyup') {
                    _ignoreNextKeyup = _characterFromEvent(e);
                }

                // weird race condition if a sequence ends with the key
                // another sequence begins with
                setTimeout(_resetSequences, 10);
            }

            // loop through keys one at a time and bind the appropriate callback
            // function.  for any key leading up to the final one it should
            // increase the sequence. after the final, it should reset all sequences
            //
            // if an action is specified in the original bind call then that will
            // be used throughout.  otherwise we will pass the action that the
            // next key in the sequence should match.  this allows a sequence
            // to mix and match keypress and keydown events depending on which
            // ones are better suited to the key provided
            for (var i = 0; i < keys.length; ++i) {
                var isFinal = i + 1 === keys.length;
                var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action);
                _bindSingle(keys[i], wrappedCallback, action, combo, i);
            }
        }

        /**
         * binds a single keyboard combination
         *
         * @param {string} combination
         * @param {Function} callback
         * @param {string=} action
         * @param {string=} sequenceName - name of sequence if part of sequence
         * @param {number=} level - what part of the sequence the command is
         * @returns void
         */
        function _bindSingle(combination, callback, action, sequenceName, level) {

            // store a direct mapped reference for use with Mousetrap.trigger
            self._directMap[combination + ':' + action] = callback;

            // make sure multiple spaces in a row become a single space
            combination = combination.replace(/\s+/g, ' ');

            var sequence = combination.split(' ');
            var info;

            // if this pattern is a sequence of keys then run through this method
            // to reprocess each pattern one key at a time
            if (sequence.length > 1) {
                _bindSequence(combination, sequence, callback, action);
                return;
            }

            info = _getKeyInfo(combination, action);

            // make sure to initialize array if this is the first time
            // a callback is added for this key
            self._callbacks[info.key] = self._callbacks[info.key] || [];

            // remove an existing match if there is one
            _getMatches(info.key, info.modifiers, {type: info.action}, sequenceName, combination, level);

            // add this call back to the array
            // if it is a sequence put it at the beginning
            // if not put it at the end
            //
            // this is important because the way these are processed expects
            // the sequence ones to come first
            self._callbacks[info.key][sequenceName ? 'unshift' : 'push']({
                callback: callback,
                modifiers: info.modifiers,
                action: info.action,
                seq: sequenceName,
                level: level,
                combo: combination
            });
        }

        /**
         * binds multiple combinations to the same callback
         *
         * @param {Array} combinations
         * @param {Function} callback
         * @param {string|undefined} action
         * @returns void
         */
        self._bindMultiple = function(combinations, callback, action) {
            for (var i = 0; i < combinations.length; ++i) {
                _bindSingle(combinations[i], callback, action);
            }
        };

        // start!
        _addEvent(targetElement, 'keypress', _handleKeyEvent);
        _addEvent(targetElement, 'keydown', _handleKeyEvent);
        _addEvent(targetElement, 'keyup', _handleKeyEvent);
    }

    /**
     * binds an event to mousetrap
     *
     * can be a single key, a combination of keys separated with +,
     * an array of keys, or a sequence of keys separated by spaces
     *
     * be sure to list the modifier keys first to make sure that the
     * correct key ends up getting bound (the last key in the pattern)
     *
     * @param {string|Array} keys
     * @param {Function} callback
     * @param {string=} action - 'keypress', 'keydown', or 'keyup'
     * @returns void
     */
    Mousetrap.prototype.bind = function(keys, callback, action) {
        var self = this;
        keys = keys instanceof Array ? keys : [keys];
        self._bindMultiple.call(self, keys, callback, action);
        return self;
    };

    /**
     * unbinds an event to mousetrap
     *
     * the unbinding sets the callback function of the specified key combo
     * to an empty function and deletes the corresponding key in the
     * _directMap dict.
     *
     * TODO: actually remove this from the _callbacks dictionary instead
     * of binding an empty function
     *
     * the keycombo+action has to be exactly the same as
     * it was defined in the bind method
     *
     * @param {string|Array} keys
     * @param {string} action
     * @returns void
     */
    Mousetrap.prototype.unbind = function(keys, action) {
        var self = this;
        return self.bind.call(self, keys, function() {}, action);
    };

    /**
     * triggers an event that has already been bound
     *
     * @param {string} keys
     * @param {string=} action
     * @returns void
     */
    Mousetrap.prototype.trigger = function(keys, action) {
        var self = this;
        if (self._directMap[keys + ':' + action]) {
            self._directMap[keys + ':' + action]({}, keys);
        }
        return self;
    };

    /**
     * resets the library back to its initial state.  this is useful
     * if you want to clear out the current keyboard shortcuts and bind
     * new ones - for example if you switch to another page
     *
     * @returns void
     */
    Mousetrap.prototype.reset = function() {
        var self = this;
        self._callbacks = {};
        self._directMap = {};
        return self;
    };

    /**
     * should we stop this event before firing off callbacks
     *
     * @param {Event} e
     * @param {Element} element
     * @return {boolean}
     */
    Mousetrap.prototype.stopCallback = function(e, element) {
        var self = this;

        // if the element has the class "mousetrap" then no need to stop
        if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
            return false;
        }

        if (_belongsTo(element, self.target)) {
            return false;
        }

        // stop for input, select, and textarea
        return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable;
    };

    /**
     * exposes _handleKey publicly so it can be overwritten by extensions
     */
    Mousetrap.prototype.handleKey = function() {
        var self = this;
        return self._handleKey.apply(self, arguments);
    };

    /**
     * allow custom key mappings
     */
    Mousetrap.addKeycodes = function(object) {
        for (var key in object) {
            if (object.hasOwnProperty(key)) {
                _MAP[key] = object[key];
            }
        }
        _REVERSE_MAP = null;
    };

    /**
     * Init the global mousetrap functions
     *
     * This method is needed to allow the global mousetrap functions to work
     * now that mousetrap is a constructor function.
     */
    Mousetrap.init = function() {
        var documentMousetrap = Mousetrap(document);
        for (var method in documentMousetrap) {
            if (method.charAt(0) !== '_') {
                Mousetrap[method] = (function(method) {
                    return function() {
                        return documentMousetrap[method].apply(documentMousetrap, arguments);
                    };
                } (method));
            }
        }
    };

    Mousetrap.init();

    // expose mousetrap to the global object
    window.Mousetrap = Mousetrap;

    // expose as a common js module
    if (typeof module !== 'undefined' && module.exports) {
        module.exports = Mousetrap;
    }

    // expose mousetrap as an AMD module
    if (typeof define === 'function' && define.amd) {
        define(function() {
            return Mousetrap;
        });
    }
}) (typeof window !== 'undefined' ? window : null, typeof  window !== 'undefined' ? document : null);

},{}],22:[function(require,module,exports){
'use strict';


var zlib_deflate = require('./zlib/deflate');
var utils        = require('./utils/common');
var strings      = require('./utils/strings');
var msg          = require('./zlib/messages');
var ZStream      = require('./zlib/zstream');

var toString = Object.prototype.toString;

/* Public constants ==========================================================*/
/* ===========================================================================*/

var Z_NO_FLUSH      = 0;
var Z_FINISH        = 4;

var Z_OK            = 0;
var Z_STREAM_END    = 1;
var Z_SYNC_FLUSH    = 2;

var Z_DEFAULT_COMPRESSION = -1;

var Z_DEFAULT_STRATEGY    = 0;

var Z_DEFLATED  = 8;

/* ===========================================================================*/


/**
 * class Deflate
 *
 * Generic JS-style wrapper for zlib calls. If you don't need
 * streaming behaviour - use more simple functions: [[deflate]],
 * [[deflateRaw]] and [[gzip]].
 **/

/* internal
 * Deflate.chunks -> Array
 *
 * Chunks of output data, if [[Deflate#onData]] not overriden.
 **/

/**
 * Deflate.result -> Uint8Array|Array
 *
 * Compressed result, generated by default [[Deflate#onData]]
 * and [[Deflate#onEnd]] handlers. Filled after you push last chunk
 * (call [[Deflate#push]] with `Z_FINISH` / `true` param)  or if you
 * push a chunk with explicit flush (call [[Deflate#push]] with
 * `Z_SYNC_FLUSH` param).
 **/

/**
 * Deflate.err -> Number
 *
 * Error code after deflate finished. 0 (Z_OK) on success.
 * You will not need it in real life, because deflate errors
 * are possible only on wrong options or bad `onData` / `onEnd`
 * custom handlers.
 **/

/**
 * Deflate.msg -> String
 *
 * Error message, if [[Deflate.err]] != 0
 **/


/**
 * new Deflate(options)
 * - options (Object): zlib deflate options.
 *
 * Creates new deflator instance with specified params. Throws exception
 * on bad params. Supported options:
 *
 * - `level`
 * - `windowBits`
 * - `memLevel`
 * - `strategy`
 * - `dictionary`
 *
 * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
 * for more information on these.
 *
 * Additional options, for internal needs:
 *
 * - `chunkSize` - size of generated data chunks (16K by default)
 * - `raw` (Boolean) - do raw deflate
 * - `gzip` (Boolean) - create gzip wrapper
 * - `to` (String) - if equal to 'string', then result will be "binary string"
 *    (each char code [0..255])
 * - `header` (Object) - custom header for gzip
 *   - `text` (Boolean) - true if compressed data believed to be text
 *   - `time` (Number) - modification time, unix timestamp
 *   - `os` (Number) - operation system code
 *   - `extra` (Array) - array of bytes with extra data (max 65536)
 *   - `name` (String) - file name (binary string)
 *   - `comment` (String) - comment (binary string)
 *   - `hcrc` (Boolean) - true if header crc should be added
 *
 * ##### Example:
 *
 * ```javascript
 * var pako = require('pako')
 *   , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
 *   , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
 *
 * var deflate = new pako.Deflate({ level: 3});
 *
 * deflate.push(chunk1, false);
 * deflate.push(chunk2, true);  // true -> last chunk
 *
 * if (deflate.err) { throw new Error(deflate.err); }
 *
 * console.log(deflate.result);
 * ```
 **/
function Deflate(options) {
  if (!(this instanceof Deflate)) return new Deflate(options);

  this.options = utils.assign({
    level: Z_DEFAULT_COMPRESSION,
    method: Z_DEFLATED,
    chunkSize: 16384,
    windowBits: 15,
    memLevel: 8,
    strategy: Z_DEFAULT_STRATEGY,
    to: ''
  }, options || {});

  var opt = this.options;

  if (opt.raw && (opt.windowBits > 0)) {
    opt.windowBits = -opt.windowBits;
  }

  else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) {
    opt.windowBits += 16;
  }

  this.err    = 0;      // error code, if happens (0 = Z_OK)
  this.msg    = '';     // error message
  this.ended  = false;  // used to avoid multiple onEnd() calls
  this.chunks = [];     // chunks of compressed data

  this.strm = new ZStream();
  this.strm.avail_out = 0;

  var status = zlib_deflate.deflateInit2(
    this.strm,
    opt.level,
    opt.method,
    opt.windowBits,
    opt.memLevel,
    opt.strategy
  );

  if (status !== Z_OK) {
    throw new Error(msg[status]);
  }

  if (opt.header) {
    zlib_deflate.deflateSetHeader(this.strm, opt.header);
  }

  if (opt.dictionary) {
    var dict;
    // Convert data if needed
    if (typeof opt.dictionary === 'string') {
      // If we need to compress text, change encoding to utf8.
      dict = strings.string2buf(opt.dictionary);
    } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {
      dict = new Uint8Array(opt.dictionary);
    } else {
      dict = opt.dictionary;
    }

    status = zlib_deflate.deflateSetDictionary(this.strm, dict);

    if (status !== Z_OK) {
      throw new Error(msg[status]);
    }

    this._dict_set = true;
  }
}

/**
 * Deflate#push(data[, mode]) -> Boolean
 * - data (Uint8Array|Array|ArrayBuffer|String): input data. Strings will be
 *   converted to utf8 byte sequence.
 * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
 *   See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH.
 *
 * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with
 * new compressed chunks. Returns `true` on success. The last data block must have
 * mode Z_FINISH (or `true`). That will flush internal pending buffers and call
 * [[Deflate#onEnd]]. For interim explicit flushes (without ending the stream) you
 * can use mode Z_SYNC_FLUSH, keeping the compression context.
 *
 * On fail call [[Deflate#onEnd]] with error code and return false.
 *
 * We strongly recommend to use `Uint8Array` on input for best speed (output
 * array format is detected automatically). Also, don't skip last param and always
 * use the same type in your code (boolean or number). That will improve JS speed.
 *
 * For regular `Array`-s make sure all elements are [0..255].
 *
 * ##### Example
 *
 * ```javascript
 * push(chunk, false); // push one of data chunks
 * ...
 * push(chunk, true);  // push last chunk
 * ```
 **/
Deflate.prototype.push = function (data, mode) {
  var strm = this.strm;
  var chunkSize = this.options.chunkSize;
  var status, _mode;

  if (this.ended) { return false; }

  _mode = (mode === ~~mode) ? mode : ((mode === true) ? Z_FINISH : Z_NO_FLUSH);

  // Convert data if needed
  if (typeof data === 'string') {
    // If we need to compress text, change encoding to utf8.
    strm.input = strings.string2buf(data);
  } else if (toString.call(data) === '[object ArrayBuffer]') {
    strm.input = new Uint8Array(data);
  } else {
    strm.input = data;
  }

  strm.next_in = 0;
  strm.avail_in = strm.input.length;

  do {
    if (strm.avail_out === 0) {
      strm.output = new utils.Buf8(chunkSize);
      strm.next_out = 0;
      strm.avail_out = chunkSize;
    }
    status = zlib_deflate.deflate(strm, _mode);    /* no bad return value */

    if (status !== Z_STREAM_END && status !== Z_OK) {
      this.onEnd(status);
      this.ended = true;
      return false;
    }
    if (strm.avail_out === 0 || (strm.avail_in === 0 && (_mode === Z_FINISH || _mode === Z_SYNC_FLUSH))) {
      if (this.options.to === 'string') {
        this.onData(strings.buf2binstring(utils.shrinkBuf(strm.output, strm.next_out)));
      } else {
        this.onData(utils.shrinkBuf(strm.output, strm.next_out));
      }
    }
  } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== Z_STREAM_END);

  // Finalize on the last chunk.
  if (_mode === Z_FINISH) {
    status = zlib_deflate.deflateEnd(this.strm);
    this.onEnd(status);
    this.ended = true;
    return status === Z_OK;
  }

  // callback interim results if Z_SYNC_FLUSH.
  if (_mode === Z_SYNC_FLUSH) {
    this.onEnd(Z_OK);
    strm.avail_out = 0;
    return true;
  }

  return true;
};


/**
 * Deflate#onData(chunk) -> Void
 * - chunk (Uint8Array|Array|String): ouput data. Type of array depends
 *   on js engine support. When string output requested, each chunk
 *   will be string.
 *
 * By default, stores data blocks in `chunks[]` property and glue
 * those in `onEnd`. Override this handler, if you need another behaviour.
 **/
Deflate.prototype.onData = function (chunk) {
  this.chunks.push(chunk);
};


/**
 * Deflate#onEnd(status) -> Void
 * - status (Number): deflate status. 0 (Z_OK) on success,
 *   other if not.
 *
 * Called once after you tell deflate that the input stream is
 * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)
 * or if an error happened. By default - join collected chunks,
 * free memory and fill `results` / `err` properties.
 **/
Deflate.prototype.onEnd = function (status) {
  // On success - join
  if (status === Z_OK) {
    if (this.options.to === 'string') {
      this.result = this.chunks.join('');
    } else {
      this.result = utils.flattenChunks(this.chunks);
    }
  }
  this.chunks = [];
  this.err = status;
  this.msg = this.strm.msg;
};


/**
 * deflate(data[, options]) -> Uint8Array|Array|String
 * - data (Uint8Array|Array|String): input data to compress.
 * - options (Object): zlib deflate options.
 *
 * Compress `data` with deflate algorithm and `options`.
 *
 * Supported options are:
 *
 * - level
 * - windowBits
 * - memLevel
 * - strategy
 * - dictionary
 *
 * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
 * for more information on these.
 *
 * Sugar (options):
 *
 * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
 *   negative windowBits implicitly.
 * - `to` (String) - if equal to 'string', then result will be "binary string"
 *    (each char code [0..255])
 *
 * ##### Example:
 *
 * ```javascript
 * var pako = require('pako')
 *   , data = Uint8Array([1,2,3,4,5,6,7,8,9]);
 *
 * console.log(pako.deflate(data));
 * ```
 **/
function deflate(input, options) {
  var deflator = new Deflate(options);

  deflator.push(input, true);

  // That will never happens, if you don't cheat with options :)
  if (deflator.err) { throw deflator.msg; }

  return deflator.result;
}


/**
 * deflateRaw(data[, options]) -> Uint8Array|Array|String
 * - data (Uint8Array|Array|String): input data to compress.
 * - options (Object): zlib deflate options.
 *
 * The same as [[deflate]], but creates raw data, without wrapper
 * (header and adler32 crc).
 **/
function deflateRaw(input, options) {
  options = options || {};
  options.raw = true;
  return deflate(input, options);
}


/**
 * gzip(data[, options]) -> Uint8Array|Array|String
 * - data (Uint8Array|Array|String): input data to compress.
 * - options (Object): zlib deflate options.
 *
 * The same as [[deflate]], but create gzip wrapper instead of
 * deflate one.
 **/
function gzip(input, options) {
  options = options || {};
  options.gzip = true;
  return deflate(input, options);
}


exports.Deflate = Deflate;
exports.deflate = deflate;
exports.deflateRaw = deflateRaw;
exports.gzip = gzip;

},{"./utils/common":23,"./utils/strings":24,"./zlib/deflate":27,"./zlib/messages":28,"./zlib/zstream":30}],23:[function(require,module,exports){
'use strict';


var TYPED_OK =  (typeof Uint8Array !== 'undefined') &&
                (typeof Uint16Array !== 'undefined') &&
                (typeof Int32Array !== 'undefined');


exports.assign = function (obj /*from1, from2, from3, ...*/) {
  var sources = Array.prototype.slice.call(arguments, 1);
  while (sources.length) {
    var source = sources.shift();
    if (!source) { continue; }

    if (typeof source !== 'object') {
      throw new TypeError(source + 'must be non-object');
    }

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

  return obj;
};


// reduce buffer size, avoiding mem copy
exports.shrinkBuf = function (buf, size) {
  if (buf.length === size) { return buf; }
  if (buf.subarray) { return buf.subarray(0, size); }
  buf.length = size;
  return buf;
};


var fnTyped = {
  arraySet: function (dest, src, src_offs, len, dest_offs) {
    if (src.subarray && dest.subarray) {
      dest.set(src.subarray(src_offs, src_offs + len), dest_offs);
      return;
    }
    // Fallback to ordinary array
    for (var i = 0; i < len; i++) {
      dest[dest_offs + i] = src[src_offs + i];
    }
  },
  // Join array of chunks to single array.
  flattenChunks: function (chunks) {
    var i, l, len, pos, chunk, result;

    // calculate data length
    len = 0;
    for (i = 0, l = chunks.length; i < l; i++) {
      len += chunks[i].length;
    }

    // join chunks
    result = new Uint8Array(len);
    pos = 0;
    for (i = 0, l = chunks.length; i < l; i++) {
      chunk = chunks[i];
      result.set(chunk, pos);
      pos += chunk.length;
    }

    return result;
  }
};

var fnUntyped = {
  arraySet: function (dest, src, src_offs, len, dest_offs) {
    for (var i = 0; i < len; i++) {
      dest[dest_offs + i] = src[src_offs + i];
    }
  },
  // Join array of chunks to single array.
  flattenChunks: function (chunks) {
    return [].concat.apply([], chunks);
  }
};


// Enable/Disable typed arrays use, for testing
//
exports.setTyped = function (on) {
  if (on) {
    exports.Buf8  = Uint8Array;
    exports.Buf16 = Uint16Array;
    exports.Buf32 = Int32Array;
    exports.assign(exports, fnTyped);
  } else {
    exports.Buf8  = Array;
    exports.Buf16 = Array;
    exports.Buf32 = Array;
    exports.assign(exports, fnUntyped);
  }
};

exports.setTyped(TYPED_OK);

},{}],24:[function(require,module,exports){
// String encode/decode helpers
'use strict';


var utils = require('./common');


// Quick check if we can use fast array to bin string conversion
//
// - apply(Array) can fail on Android 2.2
// - apply(Uint8Array) can fail on iOS 5.1 Safary
//
var STR_APPLY_OK = true;
var STR_APPLY_UIA_OK = true;

try { String.fromCharCode.apply(null, [ 0 ]); } catch (__) { STR_APPLY_OK = false; }
try { String.fromCharCode.apply(null, new Uint8Array(1)); } catch (__) { STR_APPLY_UIA_OK = false; }


// Table with utf8 lengths (calculated by first byte of sequence)
// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
// because max possible codepoint is 0x10ffff
var _utf8len = new utils.Buf8(256);
for (var q = 0; q < 256; q++) {
  _utf8len[q] = (q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1);
}
_utf8len[254] = _utf8len[254] = 1; // Invalid sequence start


// convert string to array (typed, when possible)
exports.string2buf = function (str) {
  var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0;

  // count binary size
  for (m_pos = 0; m_pos < str_len; m_pos++) {
    c = str.charCodeAt(m_pos);
    if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) {
      c2 = str.charCodeAt(m_pos + 1);
      if ((c2 & 0xfc00) === 0xdc00) {
        c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
        m_pos++;
      }
    }
    buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
  }

  // allocate buffer
  buf = new utils.Buf8(buf_len);

  // convert
  for (i = 0, m_pos = 0; i < buf_len; m_pos++) {
    c = str.charCodeAt(m_pos);
    if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) {
      c2 = str.charCodeAt(m_pos + 1);
      if ((c2 & 0xfc00) === 0xdc00) {
        c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
        m_pos++;
      }
    }
    if (c < 0x80) {
      /* one byte */
      buf[i++] = c;
    } else if (c < 0x800) {
      /* two bytes */
      buf[i++] = 0xC0 | (c >>> 6);
      buf[i++] = 0x80 | (c & 0x3f);
    } else if (c < 0x10000) {
      /* three bytes */
      buf[i++] = 0xE0 | (c >>> 12);
      buf[i++] = 0x80 | (c >>> 6 & 0x3f);
      buf[i++] = 0x80 | (c & 0x3f);
    } else {
      /* four bytes */
      buf[i++] = 0xf0 | (c >>> 18);
      buf[i++] = 0x80 | (c >>> 12 & 0x3f);
      buf[i++] = 0x80 | (c >>> 6 & 0x3f);
      buf[i++] = 0x80 | (c & 0x3f);
    }
  }

  return buf;
};

// Helper (used in 2 places)
function buf2binstring(buf, len) {
  // use fallback for big arrays to avoid stack overflow
  if (len < 65537) {
    if ((buf.subarray && STR_APPLY_UIA_OK) || (!buf.subarray && STR_APPLY_OK)) {
      return String.fromCharCode.apply(null, utils.shrinkBuf(buf, len));
    }
  }

  var result = '';
  for (var i = 0; i < len; i++) {
    result += String.fromCharCode(buf[i]);
  }
  return result;
}


// Convert byte array to binary string
exports.buf2binstring = function (buf) {
  return buf2binstring(buf, buf.length);
};


// Convert binary string (typed, when possible)
exports.binstring2buf = function (str) {
  var buf = new utils.Buf8(str.length);
  for (var i = 0, len = buf.length; i < len; i++) {
    buf[i] = str.charCodeAt(i);
  }
  return buf;
};


// convert array to string
exports.buf2string = function (buf, max) {
  var i, out, c, c_len;
  var len = max || buf.length;

  // Reserve max possible length (2 words per char)
  // NB: by unknown reasons, Array is significantly faster for
  //     String.fromCharCode.apply than Uint16Array.
  var utf16buf = new Array(len * 2);

  for (out = 0, i = 0; i < len;) {
    c = buf[i++];
    // quick process ascii
    if (c < 0x80) { utf16buf[out++] = c; continue; }

    c_len = _utf8len[c];
    // skip 5 & 6 byte codes
    if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len - 1; continue; }

    // apply mask on first byte
    c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;
    // join the rest
    while (c_len > 1 && i < len) {
      c = (c << 6) | (buf[i++] & 0x3f);
      c_len--;
    }

    // terminated by end of string?
    if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; }

    if (c < 0x10000) {
      utf16buf[out++] = c;
    } else {
      c -= 0x10000;
      utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff);
      utf16buf[out++] = 0xdc00 | (c & 0x3ff);
    }
  }

  return buf2binstring(utf16buf, out);
};


// Calculate max possible position in utf8 buffer,
// that will not break sequence. If that's not possible
// - (very small limits) return max size as is.
//
// buf[] - utf8 bytes array
// max   - length limit (mandatory);
exports.utf8border = function (buf, max) {
  var pos;

  max = max || buf.length;
  if (max > buf.length) { max = buf.length; }

  // go back from last position, until start of sequence found
  pos = max - 1;
  while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; }

  // Fuckup - very small and broken sequence,
  // return max, because we should return something anyway.
  if (pos < 0) { return max; }

  // If we came to start of buffer - that means vuffer is too small,
  // return max too.
  if (pos === 0) { return max; }

  return (pos + _utf8len[buf[pos]] > max) ? pos : max;
};

},{"./common":23}],25:[function(require,module,exports){
'use strict';

// Note: adler32 takes 12% for level 0 and 2% for level 6.
// It doesn't worth to make additional optimizationa as in original.
// Small size is preferable.

function adler32(adler, buf, len, pos) {
  var s1 = (adler & 0xffff) |0,
      s2 = ((adler >>> 16) & 0xffff) |0,
      n = 0;

  while (len !== 0) {
    // Set limit ~ twice less than 5552, to keep
    // s2 in 31-bits, because we force signed ints.
    // in other case %= will fail.
    n = len > 2000 ? 2000 : len;
    len -= n;

    do {
      s1 = (s1 + buf[pos++]) |0;
      s2 = (s2 + s1) |0;
    } while (--n);

    s1 %= 65521;
    s2 %= 65521;
  }

  return (s1 | (s2 << 16)) |0;
}


module.exports = adler32;

},{}],26:[function(require,module,exports){
'use strict';

// Note: we can't get significant speed boost here.
// So write code to minimize size - no pregenerated tables
// and array tools dependencies.


// Use ordinary array, since untyped makes no boost here
function makeTable() {
  var c, table = [];

  for (var n = 0; n < 256; n++) {
    c = n;
    for (var k = 0; k < 8; k++) {
      c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
    }
    table[n] = c;
  }

  return table;
}

// Create table on load. Just 255 signed longs. Not a problem.
var crcTable = makeTable();


function crc32(crc, buf, len, pos) {
  var t = crcTable,
      end = pos + len;

  crc ^= -1;

  for (var i = pos; i < end; i++) {
    crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
  }

  return (crc ^ (-1)); // >>> 0;
}


module.exports = crc32;

},{}],27:[function(require,module,exports){
'use strict';

var utils   = require('../utils/common');
var trees   = require('./trees');
var adler32 = require('./adler32');
var crc32   = require('./crc32');
var msg     = require('./messages');

/* Public constants ==========================================================*/
/* ===========================================================================*/


/* Allowed flush values; see deflate() and inflate() below for details */
var Z_NO_FLUSH      = 0;
var Z_PARTIAL_FLUSH = 1;
//var Z_SYNC_FLUSH    = 2;
var Z_FULL_FLUSH    = 3;
var Z_FINISH        = 4;
var Z_BLOCK         = 5;
//var Z_TREES         = 6;


/* Return codes for the compression/decompression functions. Negative values
 * are errors, positive values are used for special but normal events.
 */
var Z_OK            = 0;
var Z_STREAM_END    = 1;
//var Z_NEED_DICT     = 2;
//var Z_ERRNO         = -1;
var Z_STREAM_ERROR  = -2;
var Z_DATA_ERROR    = -3;
//var Z_MEM_ERROR     = -4;
var Z_BUF_ERROR     = -5;
//var Z_VERSION_ERROR = -6;


/* compression levels */
//var Z_NO_COMPRESSION      = 0;
//var Z_BEST_SPEED          = 1;
//var Z_BEST_COMPRESSION    = 9;
var Z_DEFAULT_COMPRESSION = -1;


var Z_FILTERED            = 1;
var Z_HUFFMAN_ONLY        = 2;
var Z_RLE                 = 3;
var Z_FIXED               = 4;
var Z_DEFAULT_STRATEGY    = 0;

/* Possible values of the data_type field (though see inflate()) */
//var Z_BINARY              = 0;
//var Z_TEXT                = 1;
//var Z_ASCII               = 1; // = Z_TEXT
var Z_UNKNOWN             = 2;


/* The deflate compression method */
var Z_DEFLATED  = 8;

/*============================================================================*/


var MAX_MEM_LEVEL = 9;
/* Maximum value for memLevel in deflateInit2 */
var MAX_WBITS = 15;
/* 32K LZ77 window */
var DEF_MEM_LEVEL = 8;


var LENGTH_CODES  = 29;
/* number of length codes, not counting the special END_BLOCK code */
var LITERALS      = 256;
/* number of literal bytes 0..255 */
var L_CODES       = LITERALS + 1 + LENGTH_CODES;
/* number of Literal or Length codes, including the END_BLOCK code */
var D_CODES       = 30;
/* number of distance codes */
var BL_CODES      = 19;
/* number of codes used to transfer the bit lengths */
var HEAP_SIZE     = 2 * L_CODES + 1;
/* maximum heap size */
var MAX_BITS  = 15;
/* All codes must not exceed MAX_BITS bits */

var MIN_MATCH = 3;
var MAX_MATCH = 258;
var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1);

var PRESET_DICT = 0x20;

var INIT_STATE = 42;
var EXTRA_STATE = 69;
var NAME_STATE = 73;
var COMMENT_STATE = 91;
var HCRC_STATE = 103;
var BUSY_STATE = 113;
var FINISH_STATE = 666;

var BS_NEED_MORE      = 1; /* block not completed, need more input or more output */
var BS_BLOCK_DONE     = 2; /* block flush performed */
var BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */
var BS_FINISH_DONE    = 4; /* finish done, accept no more input or output */

var OS_CODE = 0x03; // Unix :) . Don't detect, use this default.

function err(strm, errorCode) {
  strm.msg = msg[errorCode];
  return errorCode;
}

function rank(f) {
  return ((f) << 1) - ((f) > 4 ? 9 : 0);
}

function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }


/* =========================================================================
 * Flush as much pending output as possible. All deflate() output goes
 * through this function so some applications may wish to modify it
 * to avoid allocating a large strm->output buffer and copying into it.
 * (See also read_buf()).
 */
function flush_pending(strm) {
  var s = strm.state;

  //_tr_flush_bits(s);
  var len = s.pending;
  if (len > strm.avail_out) {
    len = strm.avail_out;
  }
  if (len === 0) { return; }

  utils.arraySet(strm.output, s.pending_buf, s.pending_out, len, strm.next_out);
  strm.next_out += len;
  s.pending_out += len;
  strm.total_out += len;
  strm.avail_out -= len;
  s.pending -= len;
  if (s.pending === 0) {
    s.pending_out = 0;
  }
}


function flush_block_only(s, last) {
  trees._tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last);
  s.block_start = s.strstart;
  flush_pending(s.strm);
}


function put_byte(s, b) {
  s.pending_buf[s.pending++] = b;
}


/* =========================================================================
 * Put a short in the pending buffer. The 16-bit value is put in MSB order.
 * IN assertion: the stream state is correct and there is enough room in
 * pending_buf.
 */
function putShortMSB(s, b) {
//  put_byte(s, (Byte)(b >> 8));
//  put_byte(s, (Byte)(b & 0xff));
  s.pending_buf[s.pending++] = (b >>> 8) & 0xff;
  s.pending_buf[s.pending++] = b & 0xff;
}


/* ===========================================================================
 * Read a new buffer from the current input stream, update the adler32
 * and total number of bytes read.  All deflate() input goes through
 * this function so some applications may wish to modify it to avoid
 * allocating a large strm->input buffer and copying from it.
 * (See also flush_pending()).
 */
function read_buf(strm, buf, start, size) {
  var len = strm.avail_in;

  if (len > size) { len = size; }
  if (len === 0) { return 0; }

  strm.avail_in -= len;

  // zmemcpy(buf, strm->next_in, len);
  utils.arraySet(buf, strm.input, strm.next_in, len, start);
  if (strm.state.wrap === 1) {
    strm.adler = adler32(strm.adler, buf, len, start);
  }

  else if (strm.state.wrap === 2) {
    strm.adler = crc32(strm.adler, buf, len, start);
  }

  strm.next_in += len;
  strm.total_in += len;

  return len;
}


/* ===========================================================================
 * Set match_start to the longest match starting at the given string and
 * return its length. Matches shorter or equal to prev_length are discarded,
 * in which case the result is equal to prev_length and match_start is
 * garbage.
 * IN assertions: cur_match is the head of the hash chain for the current
 *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
 * OUT assertion: the match length is not greater than s->lookahead.
 */
function longest_match(s, cur_match) {
  var chain_length = s.max_chain_length;      /* max hash chain length */
  var scan = s.strstart; /* current string */
  var match;                       /* matched string */
  var len;                           /* length of current match */
  var best_len = s.prev_length;              /* best match length so far */
  var nice_match = s.nice_match;             /* stop if match long enough */
  var limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ?
      s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/;

  var _win = s.window; // shortcut

  var wmask = s.w_mask;
  var prev  = s.prev;

  /* Stop when cur_match becomes <= limit. To simplify the code,
   * we prevent matches with the string of window index 0.
   */

  var strend = s.strstart + MAX_MATCH;
  var scan_end1  = _win[scan + best_len - 1];
  var scan_end   = _win[scan + best_len];

  /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
   * It is easy to get rid of this optimization if necessary.
   */
  // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");

  /* Do not waste too much time if we already have a good match: */
  if (s.prev_length >= s.good_match) {
    chain_length >>= 2;
  }
  /* Do not look for matches beyond the end of the input. This is necessary
   * to make deflate deterministic.
   */
  if (nice_match > s.lookahead) { nice_match = s.lookahead; }

  // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");

  do {
    // Assert(cur_match < s->strstart, "no future");
    match = cur_match;

    /* Skip to next match if the match length cannot increase
     * or if the match length is less than 2.  Note that the checks below
     * for insufficient lookahead only occur occasionally for performance
     * reasons.  Therefore uninitialized memory will be accessed, and
     * conditional jumps will be made that depend on those values.
     * However the length of the match is limited to the lookahead, so
     * the output of deflate is not affected by the uninitialized values.
     */

    if (_win[match + best_len]     !== scan_end  ||
        _win[match + best_len - 1] !== scan_end1 ||
        _win[match]                !== _win[scan] ||
        _win[++match]              !== _win[scan + 1]) {
      continue;
    }

    /* The check at best_len-1 can be removed because it will be made
     * again later. (This heuristic is not always a win.)
     * It is not necessary to compare scan[2] and match[2] since they
     * are always equal when the other bytes match, given that
     * the hash keys are equal and that HASH_BITS >= 8.
     */
    scan += 2;
    match++;
    // Assert(*scan == *match, "match[2]?");

    /* We check for insufficient lookahead only every 8th comparison;
     * the 256th check will be made at strstart+258.
     */
    do {
      /*jshint noempty:false*/
    } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
             scan < strend);

    // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");

    len = MAX_MATCH - (strend - scan);
    scan = strend - MAX_MATCH;

    if (len > best_len) {
      s.match_start = cur_match;
      best_len = len;
      if (len >= nice_match) {
        break;
      }
      scan_end1  = _win[scan + best_len - 1];
      scan_end   = _win[scan + best_len];
    }
  } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);

  if (best_len <= s.lookahead) {
    return best_len;
  }
  return s.lookahead;
}


/* ===========================================================================
 * Fill the window when the lookahead becomes insufficient.
 * Updates strstart and lookahead.
 *
 * IN assertion: lookahead < MIN_LOOKAHEAD
 * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
 *    At least one byte has been read, or avail_in == 0; reads are
 *    performed for at least two bytes (required for the zip translate_eol
 *    option -- not supported here).
 */
function fill_window(s) {
  var _w_size = s.w_size;
  var p, n, m, more, str;

  //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");

  do {
    more = s.window_size - s.lookahead - s.strstart;

    // JS ints have 32 bit, block below not needed
    /* Deal with !@#$% 64K limit: */
    //if (sizeof(int) <= 2) {
    //    if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
    //        more = wsize;
    //
    //  } else if (more == (unsigned)(-1)) {
    //        /* Very unlikely, but possible on 16 bit machine if
    //         * strstart == 0 && lookahead == 1 (input done a byte at time)
    //         */
    //        more--;
    //    }
    //}


    /* If the window is almost full and there is insufficient lookahead,
     * move the upper half to the lower one to make room in the upper half.
     */
    if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) {

      utils.arraySet(s.window, s.window, _w_size, _w_size, 0);
      s.match_start -= _w_size;
      s.strstart -= _w_size;
      /* we now have strstart >= MAX_DIST */
      s.block_start -= _w_size;

      /* Slide the hash table (could be avoided with 32 bit values
       at the expense of memory usage). We slide even when level == 0
       to keep the hash table consistent if we switch back to level > 0
       later. (Using level 0 permanently is not an optimal usage of
       zlib, so we don't care about this pathological case.)
       */

      n = s.hash_size;
      p = n;
      do {
        m = s.head[--p];
        s.head[p] = (m >= _w_size ? m - _w_size : 0);
      } while (--n);

      n = _w_size;
      p = n;
      do {
        m = s.prev[--p];
        s.prev[p] = (m >= _w_size ? m - _w_size : 0);
        /* If n is not on any hash chain, prev[n] is garbage but
         * its value will never be used.
         */
      } while (--n);

      more += _w_size;
    }
    if (s.strm.avail_in === 0) {
      break;
    }

    /* If there was no sliding:
     *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
     *    more == window_size - lookahead - strstart
     * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
     * => more >= window_size - 2*WSIZE + 2
     * In the BIG_MEM or MMAP case (not yet supported),
     *   window_size == input_size + MIN_LOOKAHEAD  &&
     *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
     * Otherwise, window_size == 2*WSIZE so more >= 2.
     * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
     */
    //Assert(more >= 2, "more < 2");
    n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more);
    s.lookahead += n;

    /* Initialize the hash value now that we have some input: */
    if (s.lookahead + s.insert >= MIN_MATCH) {
      str = s.strstart - s.insert;
      s.ins_h = s.window[str];

      /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */
      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask;
//#if MIN_MATCH != 3
//        Call update_hash() MIN_MATCH-3 more times
//#endif
      while (s.insert) {
        /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
        s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask;

        s.prev[str & s.w_mask] = s.head[s.ins_h];
        s.head[s.ins_h] = str;
        str++;
        s.insert--;
        if (s.lookahead + s.insert < MIN_MATCH) {
          break;
        }
      }
    }
    /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
     * but this is not important since only literal bytes will be emitted.
     */

  } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0);

  /* If the WIN_INIT bytes after the end of the current data have never been
   * written, then zero those bytes in order to avoid memory check reports of
   * the use of uninitialized (or uninitialised as Julian writes) bytes by
   * the longest match routines.  Update the high water mark for the next
   * time through here.  WIN_INIT is set to MAX_MATCH since the longest match
   * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
   */
//  if (s.high_water < s.window_size) {
//    var curr = s.strstart + s.lookahead;
//    var init = 0;
//
//    if (s.high_water < curr) {
//      /* Previous high water mark below current data -- zero WIN_INIT
//       * bytes or up to end of window, whichever is less.
//       */
//      init = s.window_size - curr;
//      if (init > WIN_INIT)
//        init = WIN_INIT;
//      zmemzero(s->window + curr, (unsigned)init);
//      s->high_water = curr + init;
//    }
//    else if (s->high_water < (ulg)curr + WIN_INIT) {
//      /* High water mark at or above current data, but below current data
//       * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
//       * to end of window, whichever is less.
//       */
//      init = (ulg)curr + WIN_INIT - s->high_water;
//      if (init > s->window_size - s->high_water)
//        init = s->window_size - s->high_water;
//      zmemzero(s->window + s->high_water, (unsigned)init);
//      s->high_water += init;
//    }
//  }
//
//  Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
//    "not enough room for search");
}

/* ===========================================================================
 * Copy without compression as much as possible from the input stream, return
 * the current block state.
 * This function does not insert new strings in the dictionary since
 * uncompressible data is probably not useful. This function is used
 * only for the level=0 compression option.
 * NOTE: this function should be optimized to avoid extra copying from
 * window to pending_buf.
 */
function deflate_stored(s, flush) {
  /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
   * to pending_buf_size, and each stored block has a 5 byte header:
   */
  var max_block_size = 0xffff;

  if (max_block_size > s.pending_buf_size - 5) {
    max_block_size = s.pending_buf_size - 5;
  }

  /* Copy as much as possible from input to output: */
  for (;;) {
    /* Fill the window as much as possible: */
    if (s.lookahead <= 1) {

      //Assert(s->strstart < s->w_size+MAX_DIST(s) ||
      //  s->block_start >= (long)s->w_size, "slide too late");
//      if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) ||
//        s.block_start >= s.w_size)) {
//        throw  new Error("slide too late");
//      }

      fill_window(s);
      if (s.lookahead === 0 && flush === Z_NO_FLUSH) {
        return BS_NEED_MORE;
      }

      if (s.lookahead === 0) {
        break;
      }
      /* flush the current block */
    }
    //Assert(s->block_start >= 0L, "block gone");
//    if (s.block_start < 0) throw new Error("block gone");

    s.strstart += s.lookahead;
    s.lookahead = 0;

    /* Emit a stored block if pending_buf will be full: */
    var max_start = s.block_start + max_block_size;

    if (s.strstart === 0 || s.strstart >= max_start) {
      /* strstart == 0 is possible when wraparound on 16-bit machine */
      s.lookahead = s.strstart - max_start;
      s.strstart = max_start;
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/


    }
    /* Flush if we may have to slide, otherwise block_start may become
     * negative and the data will be gone:
     */
    if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) {
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/
    }
  }

  s.insert = 0;

  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }

  if (s.strstart > s.block_start) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }

  return BS_NEED_MORE;
}

/* ===========================================================================
 * Compress as much as possible from the input stream, return the current
 * block state.
 * This function does not perform lazy evaluation of matches and inserts
 * new strings in the dictionary only for unmatched strings or for short
 * matches. It is used only for the fast compression options.
 */
function deflate_fast(s, flush) {
  var hash_head;        /* head of the hash chain */
  var bflush;           /* set if current block must be flushed */

  for (;;) {
    /* Make sure that we always have enough lookahead, except
     * at the end of the input file. We need MAX_MATCH bytes
     * for the next match, plus MIN_MATCH bytes to insert the
     * string following the next match.
     */
    if (s.lookahead < MIN_LOOKAHEAD) {
      fill_window(s);
      if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {
        return BS_NEED_MORE;
      }
      if (s.lookahead === 0) {
        break; /* flush the current block */
      }
    }

    /* Insert the string window[strstart .. strstart+2] in the
     * dictionary, and set hash_head to the head of the hash chain:
     */
    hash_head = 0/*NIL*/;
    if (s.lookahead >= MIN_MATCH) {
      /*** INSERT_STRING(s, s.strstart, hash_head); ***/
      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
      hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
      s.head[s.ins_h] = s.strstart;
      /***/
    }

    /* Find the longest match, discarding those <= prev_length.
     * At this point we have always match_length < MIN_MATCH
     */
    if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) {
      /* To simplify the code, we prevent matches with the string
       * of window index 0 (in particular we have to avoid a match
       * of the string with itself at the start of the input file).
       */
      s.match_length = longest_match(s, hash_head);
      /* longest_match() sets match_start */
    }
    if (s.match_length >= MIN_MATCH) {
      // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only

      /*** _tr_tally_dist(s, s.strstart - s.match_start,
                     s.match_length - MIN_MATCH, bflush); ***/
      bflush = trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH);

      s.lookahead -= s.match_length;

      /* Insert new strings in the hash table only if the match length
       * is not too large. This saves time but degrades compression.
       */
      if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) {
        s.match_length--; /* string at strstart already in table */
        do {
          s.strstart++;
          /*** INSERT_STRING(s, s.strstart, hash_head); ***/
          s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = s.strstart;
          /***/
          /* strstart never exceeds WSIZE-MAX_MATCH, so there are
           * always MIN_MATCH bytes ahead.
           */
        } while (--s.match_length !== 0);
        s.strstart++;
      } else
      {
        s.strstart += s.match_length;
        s.match_length = 0;
        s.ins_h = s.window[s.strstart];
        /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */
        s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask;

//#if MIN_MATCH != 3
//                Call UPDATE_HASH() MIN_MATCH-3 more times
//#endif
        /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
         * matter since it will be recomputed at next deflate call.
         */
      }
    } else {
      /* No match, output a literal byte */
      //Tracevv((stderr,"%c", s.window[s.strstart]));
      /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
      bflush = trees._tr_tally(s, 0, s.window[s.strstart]);

      s.lookahead--;
      s.strstart++;
    }
    if (bflush) {
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/
    }
  }
  s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1);
  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }
  if (s.last_lit) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }
  return BS_BLOCK_DONE;
}

/* ===========================================================================
 * Same as above, but achieves better compression. We use a lazy
 * evaluation for matches: a match is finally adopted only if there is
 * no better match at the next window position.
 */
function deflate_slow(s, flush) {
  var hash_head;          /* head of hash chain */
  var bflush;              /* set if current block must be flushed */

  var max_insert;

  /* Process the input block. */
  for (;;) {
    /* Make sure that we always have enough lookahead, except
     * at the end of the input file. We need MAX_MATCH bytes
     * for the next match, plus MIN_MATCH bytes to insert the
     * string following the next match.
     */
    if (s.lookahead < MIN_LOOKAHEAD) {
      fill_window(s);
      if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {
        return BS_NEED_MORE;
      }
      if (s.lookahead === 0) { break; } /* flush the current block */
    }

    /* Insert the string window[strstart .. strstart+2] in the
     * dictionary, and set hash_head to the head of the hash chain:
     */
    hash_head = 0/*NIL*/;
    if (s.lookahead >= MIN_MATCH) {
      /*** INSERT_STRING(s, s.strstart, hash_head); ***/
      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
      hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
      s.head[s.ins_h] = s.strstart;
      /***/
    }

    /* Find the longest match, discarding those <= prev_length.
     */
    s.prev_length = s.match_length;
    s.prev_match = s.match_start;
    s.match_length = MIN_MATCH - 1;

    if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match &&
        s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) {
      /* To simplify the code, we prevent matches with the string
       * of window index 0 (in particular we have to avoid a match
       * of the string with itself at the start of the input file).
       */
      s.match_length = longest_match(s, hash_head);
      /* longest_match() sets match_start */

      if (s.match_length <= 5 &&
         (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) {

        /* If prev_match is also MIN_MATCH, match_start is garbage
         * but we will ignore the current match anyway.
         */
        s.match_length = MIN_MATCH - 1;
      }
    }
    /* If there was a match at the previous step and the current
     * match is not better, output the previous match:
     */
    if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) {
      max_insert = s.strstart + s.lookahead - MIN_MATCH;
      /* Do not insert strings in hash table beyond this. */

      //check_match(s, s.strstart-1, s.prev_match, s.prev_length);

      /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match,
                     s.prev_length - MIN_MATCH, bflush);***/
      bflush = trees._tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH);
      /* Insert in hash table all strings up to the end of the match.
       * strstart-1 and strstart are already inserted. If there is not
       * enough lookahead, the last two strings are not inserted in
       * the hash table.
       */
      s.lookahead -= s.prev_length - 1;
      s.prev_length -= 2;
      do {
        if (++s.strstart <= max_insert) {
          /*** INSERT_STRING(s, s.strstart, hash_head); ***/
          s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = s.strstart;
          /***/
        }
      } while (--s.prev_length !== 0);
      s.match_available = 0;
      s.match_length = MIN_MATCH - 1;
      s.strstart++;

      if (bflush) {
        /*** FLUSH_BLOCK(s, 0); ***/
        flush_block_only(s, false);
        if (s.strm.avail_out === 0) {
          return BS_NEED_MORE;
        }
        /***/
      }

    } else if (s.match_available) {
      /* If there was no match at the previous position, output a
       * single literal. If there was a match but the current match
       * is longer, truncate the previous match to a single literal.
       */
      //Tracevv((stderr,"%c", s->window[s->strstart-1]));
      /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
      bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]);

      if (bflush) {
        /*** FLUSH_BLOCK_ONLY(s, 0) ***/
        flush_block_only(s, false);
        /***/
      }
      s.strstart++;
      s.lookahead--;
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
    } else {
      /* There is no previous match to compare with, wait for
       * the next step to decide.
       */
      s.match_available = 1;
      s.strstart++;
      s.lookahead--;
    }
  }
  //Assert (flush != Z_NO_FLUSH, "no flush?");
  if (s.match_available) {
    //Tracevv((stderr,"%c", s->window[s->strstart-1]));
    /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
    bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]);

    s.match_available = 0;
  }
  s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;
  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }
  if (s.last_lit) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }

  return BS_BLOCK_DONE;
}


/* ===========================================================================
 * For Z_RLE, simply look for runs of bytes, generate matches only of distance
 * one.  Do not maintain a hash table.  (It will be regenerated if this run of
 * deflate switches away from Z_RLE.)
 */
function deflate_rle(s, flush) {
  var bflush;            /* set if current block must be flushed */
  var prev;              /* byte at distance one to match */
  var scan, strend;      /* scan goes up to strend for length of run */

  var _win = s.window;

  for (;;) {
    /* Make sure that we always have enough lookahead, except
     * at the end of the input file. We need MAX_MATCH bytes
     * for the longest run, plus one for the unrolled loop.
     */
    if (s.lookahead <= MAX_MATCH) {
      fill_window(s);
      if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) {
        return BS_NEED_MORE;
      }
      if (s.lookahead === 0) { break; } /* flush the current block */
    }

    /* See how many times the previous byte repeats */
    s.match_length = 0;
    if (s.lookahead >= MIN_MATCH && s.strstart > 0) {
      scan = s.strstart - 1;
      prev = _win[scan];
      if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {
        strend = s.strstart + MAX_MATCH;
        do {
          /*jshint noempty:false*/
        } while (prev === _win[++scan] && prev === _win[++scan] &&
                 prev === _win[++scan] && prev === _win[++scan] &&
                 prev === _win[++scan] && prev === _win[++scan] &&
                 prev === _win[++scan] && prev === _win[++scan] &&
                 scan < strend);
        s.match_length = MAX_MATCH - (strend - scan);
        if (s.match_length > s.lookahead) {
          s.match_length = s.lookahead;
        }
      }
      //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
    }

    /* Emit match if have run of MIN_MATCH or longer, else emit literal */
    if (s.match_length >= MIN_MATCH) {
      //check_match(s, s.strstart, s.strstart - 1, s.match_length);

      /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/
      bflush = trees._tr_tally(s, 1, s.match_length - MIN_MATCH);

      s.lookahead -= s.match_length;
      s.strstart += s.match_length;
      s.match_length = 0;
    } else {
      /* No match, output a literal byte */
      //Tracevv((stderr,"%c", s->window[s->strstart]));
      /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
      bflush = trees._tr_tally(s, 0, s.window[s.strstart]);

      s.lookahead--;
      s.strstart++;
    }
    if (bflush) {
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/
    }
  }
  s.insert = 0;
  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }
  if (s.last_lit) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }
  return BS_BLOCK_DONE;
}

/* ===========================================================================
 * For Z_HUFFMAN_ONLY, do not look for matches.  Do not maintain a hash table.
 * (It will be regenerated if this run of deflate switches away from Huffman.)
 */
function deflate_huff(s, flush) {
  var bflush;             /* set if current block must be flushed */

  for (;;) {
    /* Make sure that we have a literal to write. */
    if (s.lookahead === 0) {
      fill_window(s);
      if (s.lookahead === 0) {
        if (flush === Z_NO_FLUSH) {
          return BS_NEED_MORE;
        }
        break;      /* flush the current block */
      }
    }

    /* Output a literal byte */
    s.match_length = 0;
    //Tracevv((stderr,"%c", s->window[s->strstart]));
    /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
    bflush = trees._tr_tally(s, 0, s.window[s.strstart]);
    s.lookahead--;
    s.strstart++;
    if (bflush) {
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/
    }
  }
  s.insert = 0;
  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }
  if (s.last_lit) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }
  return BS_BLOCK_DONE;
}

/* Values for max_lazy_match, good_match and max_chain_length, depending on
 * the desired pack level (0..9). The values given below have been tuned to
 * exclude worst case performance for pathological files. Better values may be
 * found for specific files.
 */
function Config(good_length, max_lazy, nice_length, max_chain, func) {
  this.good_length = good_length;
  this.max_lazy = max_lazy;
  this.nice_length = nice_length;
  this.max_chain = max_chain;
  this.func = func;
}

var configuration_table;

configuration_table = [
  /*      good lazy nice chain */
  new Config(0, 0, 0, 0, deflate_stored),          /* 0 store only */
  new Config(4, 4, 8, 4, deflate_fast),            /* 1 max speed, no lazy matches */
  new Config(4, 5, 16, 8, deflate_fast),           /* 2 */
  new Config(4, 6, 32, 32, deflate_fast),          /* 3 */

  new Config(4, 4, 16, 16, deflate_slow),          /* 4 lazy matches */
  new Config(8, 16, 32, 32, deflate_slow),         /* 5 */
  new Config(8, 16, 128, 128, deflate_slow),       /* 6 */
  new Config(8, 32, 128, 256, deflate_slow),       /* 7 */
  new Config(32, 128, 258, 1024, deflate_slow),    /* 8 */
  new Config(32, 258, 258, 4096, deflate_slow)     /* 9 max compression */
];


/* ===========================================================================
 * Initialize the "longest match" routines for a new zlib stream
 */
function lm_init(s) {
  s.window_size = 2 * s.w_size;

  /*** CLEAR_HASH(s); ***/
  zero(s.head); // Fill with NIL (= 0);

  /* Set the default configuration parameters:
   */
  s.max_lazy_match = configuration_table[s.level].max_lazy;
  s.good_match = configuration_table[s.level].good_length;
  s.nice_match = configuration_table[s.level].nice_length;
  s.max_chain_length = configuration_table[s.level].max_chain;

  s.strstart = 0;
  s.block_start = 0;
  s.lookahead = 0;
  s.insert = 0;
  s.match_length = s.prev_length = MIN_MATCH - 1;
  s.match_available = 0;
  s.ins_h = 0;
}


function DeflateState() {
  this.strm = null;            /* pointer back to this zlib stream */
  this.status = 0;            /* as the name implies */
  this.pending_buf = null;      /* output still pending */
  this.pending_buf_size = 0;  /* size of pending_buf */
  this.pending_out = 0;       /* next pending byte to output to the stream */
  this.pending = 0;           /* nb of bytes in the pending buffer */
  this.wrap = 0;              /* bit 0 true for zlib, bit 1 true for gzip */
  this.gzhead = null;         /* gzip header information to write */
  this.gzindex = 0;           /* where in extra, name, or comment */
  this.method = Z_DEFLATED; /* can only be DEFLATED */
  this.last_flush = -1;   /* value of flush param for previous deflate call */

  this.w_size = 0;  /* LZ77 window size (32K by default) */
  this.w_bits = 0;  /* log2(w_size)  (8..16) */
  this.w_mask = 0;  /* w_size - 1 */

  this.window = null;
  /* Sliding window. Input bytes are read into the second half of the window,
   * and move to the first half later to keep a dictionary of at least wSize
   * bytes. With this organization, matches are limited to a distance of
   * wSize-MAX_MATCH bytes, but this ensures that IO is always
   * performed with a length multiple of the block size.
   */

  this.window_size = 0;
  /* Actual size of window: 2*wSize, except when the user input buffer
   * is directly used as sliding window.
   */

  this.prev = null;
  /* Link to older string with same hash index. To limit the size of this
   * array to 64K, this link is maintained only for the last 32K strings.
   * An index in this array is thus a window index modulo 32K.
   */

  this.head = null;   /* Heads of the hash chains or NIL. */

  this.ins_h = 0;       /* hash index of string to be inserted */
  this.hash_size = 0;   /* number of elements in hash table */
  this.hash_bits = 0;   /* log2(hash_size) */
  this.hash_mask = 0;   /* hash_size-1 */

  this.hash_shift = 0;
  /* Number of bits by which ins_h must be shifted at each input
   * step. It must be such that after MIN_MATCH steps, the oldest
   * byte no longer takes part in the hash key, that is:
   *   hash_shift * MIN_MATCH >= hash_bits
   */

  this.block_start = 0;
  /* Window position at the beginning of the current output block. Gets
   * negative when the window is moved backwards.
   */

  this.match_length = 0;      /* length of best match */
  this.prev_match = 0;        /* previous match */
  this.match_available = 0;   /* set if previous match exists */
  this.strstart = 0;          /* start of string to insert */
  this.match_start = 0;       /* start of matching string */
  this.lookahead = 0;         /* number of valid bytes ahead in window */

  this.prev_length = 0;
  /* Length of the best match at previous step. Matches not greater than this
   * are discarded. This is used in the lazy match evaluation.
   */

  this.max_chain_length = 0;
  /* To speed up deflation, hash chains are never searched beyond this
   * length.  A higher limit improves compression ratio but degrades the
   * speed.
   */

  this.max_lazy_match = 0;
  /* Attempt to find a better match only when the current match is strictly
   * smaller than this value. This mechanism is used only for compression
   * levels >= 4.
   */
  // That's alias to max_lazy_match, don't use directly
  //this.max_insert_length = 0;
  /* Insert new strings in the hash table only if the match length is not
   * greater than this length. This saves time but degrades compression.
   * max_insert_length is used only for compression levels <= 3.
   */

  this.level = 0;     /* compression level (1..9) */
  this.strategy = 0;  /* favor or force Huffman coding*/

  this.good_match = 0;
  /* Use a faster search when the previous match is longer than this */

  this.nice_match = 0; /* Stop searching when current match exceeds this */

              /* used by trees.c: */

  /* Didn't use ct_data typedef below to suppress compiler warning */

  // struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */
  // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
  // struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */

  // Use flat array of DOUBLE size, with interleaved fata,
  // because JS does not support effective
  this.dyn_ltree  = new utils.Buf16(HEAP_SIZE * 2);
  this.dyn_dtree  = new utils.Buf16((2 * D_CODES + 1) * 2);
  this.bl_tree    = new utils.Buf16((2 * BL_CODES + 1) * 2);
  zero(this.dyn_ltree);
  zero(this.dyn_dtree);
  zero(this.bl_tree);

  this.l_desc   = null;         /* desc. for literal tree */
  this.d_desc   = null;         /* desc. for distance tree */
  this.bl_desc  = null;         /* desc. for bit length tree */

  //ush bl_count[MAX_BITS+1];
  this.bl_count = new utils.Buf16(MAX_BITS + 1);
  /* number of codes at each bit length for an optimal tree */

  //int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */
  this.heap = new utils.Buf16(2 * L_CODES + 1);  /* heap used to build the Huffman trees */
  zero(this.heap);

  this.heap_len = 0;               /* number of elements in the heap */
  this.heap_max = 0;               /* element of largest frequency */
  /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
   * The same heap array is used to build all trees.
   */

  this.depth = new utils.Buf16(2 * L_CODES + 1); //uch depth[2*L_CODES+1];
  zero(this.depth);
  /* Depth of each subtree used as tie breaker for trees of equal frequency
   */

  this.l_buf = 0;          /* buffer index for literals or lengths */

  this.lit_bufsize = 0;
  /* Size of match buffer for literals/lengths.  There are 4 reasons for
   * limiting lit_bufsize to 64K:
   *   - frequencies can be kept in 16 bit counters
   *   - if compression is not successful for the first block, all input
   *     data is still in the window so we can still emit a stored block even
   *     when input comes from standard input.  (This can also be done for
   *     all blocks if lit_bufsize is not greater than 32K.)
   *   - if compression is not successful for a file smaller than 64K, we can
   *     even emit a stored file instead of a stored block (saving 5 bytes).
   *     This is applicable only for zip (not gzip or zlib).
   *   - creating new Huffman trees less frequently may not provide fast
   *     adaptation to changes in the input data statistics. (Take for
   *     example a binary file with poorly compressible code followed by
   *     a highly compressible string table.) Smaller buffer sizes give
   *     fast adaptation but have of course the overhead of transmitting
   *     trees more frequently.
   *   - I can't count above 4
   */

  this.last_lit = 0;      /* running index in l_buf */

  this.d_buf = 0;
  /* Buffer index for distances. To simplify the code, d_buf and l_buf have
   * the same number of elements. To use different lengths, an extra flag
   * array would be necessary.
   */

  this.opt_len = 0;       /* bit length of current block with optimal trees */
  this.static_len = 0;    /* bit length of current block with static trees */
  this.matches = 0;       /* number of string matches in current block */
  this.insert = 0;        /* bytes at end of window left to insert */


  this.bi_buf = 0;
  /* Output buffer. bits are inserted starting at the bottom (least
   * significant bits).
   */
  this.bi_valid = 0;
  /* Number of valid bits in bi_buf.  All bits above the last valid bit
   * are always zero.
   */

  // Used for window memory init. We safely ignore it for JS. That makes
  // sense only for pointers and memory check tools.
  //this.high_water = 0;
  /* High water mark offset in window for initialized bytes -- bytes above
   * this are set to zero in order to avoid memory check warnings when
   * longest match routines access bytes past the input.  This is then
   * updated to the new high water mark.
   */
}


function deflateResetKeep(strm) {
  var s;

  if (!strm || !strm.state) {
    return err(strm, Z_STREAM_ERROR);
  }

  strm.total_in = strm.total_out = 0;
  strm.data_type = Z_UNKNOWN;

  s = strm.state;
  s.pending = 0;
  s.pending_out = 0;

  if (s.wrap < 0) {
    s.wrap = -s.wrap;
    /* was made negative by deflate(..., Z_FINISH); */
  }
  s.status = (s.wrap ? INIT_STATE : BUSY_STATE);
  strm.adler = (s.wrap === 2) ?
    0  // crc32(0, Z_NULL, 0)
  :
    1; // adler32(0, Z_NULL, 0)
  s.last_flush = Z_NO_FLUSH;
  trees._tr_init(s);
  return Z_OK;
}


function deflateReset(strm) {
  var ret = deflateResetKeep(strm);
  if (ret === Z_OK) {
    lm_init(strm.state);
  }
  return ret;
}


function deflateSetHeader(strm, head) {
  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
  if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; }
  strm.state.gzhead = head;
  return Z_OK;
}


function deflateInit2(strm, level, method, windowBits, memLevel, strategy) {
  if (!strm) { // === Z_NULL
    return Z_STREAM_ERROR;
  }
  var wrap = 1;

  if (level === Z_DEFAULT_COMPRESSION) {
    level = 6;
  }

  if (windowBits < 0) { /* suppress zlib wrapper */
    wrap = 0;
    windowBits = -windowBits;
  }

  else if (windowBits > 15) {
    wrap = 2;           /* write gzip wrapper instead */
    windowBits -= 16;
  }


  if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED ||
    windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
    strategy < 0 || strategy > Z_FIXED) {
    return err(strm, Z_STREAM_ERROR);
  }


  if (windowBits === 8) {
    windowBits = 9;
  }
  /* until 256-byte window bug fixed */

  var s = new DeflateState();

  strm.state = s;
  s.strm = strm;

  s.wrap = wrap;
  s.gzhead = null;
  s.w_bits = windowBits;
  s.w_size = 1 << s.w_bits;
  s.w_mask = s.w_size - 1;

  s.hash_bits = memLevel + 7;
  s.hash_size = 1 << s.hash_bits;
  s.hash_mask = s.hash_size - 1;
  s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH);

  s.window = new utils.Buf8(s.w_size * 2);
  s.head = new utils.Buf16(s.hash_size);
  s.prev = new utils.Buf16(s.w_size);

  // Don't need mem init magic for JS.
  //s.high_water = 0;  /* nothing written to s->window yet */

  s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */

  s.pending_buf_size = s.lit_bufsize * 4;
  s.pending_buf = new utils.Buf8(s.pending_buf_size);

  s.d_buf = s.lit_bufsize >> 1;
  s.l_buf = (1 + 2) * s.lit_bufsize;

  s.level = level;
  s.strategy = strategy;
  s.method = method;

  return deflateReset(strm);
}

function deflateInit(strm, level) {
  return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
}


function deflate(strm, flush) {
  var old_flush, s;
  var beg, val; // for gzip header write only

  if (!strm || !strm.state ||
    flush > Z_BLOCK || flush < 0) {
    return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR;
  }

  s = strm.state;

  if (!strm.output ||
      (!strm.input && strm.avail_in !== 0) ||
      (s.status === FINISH_STATE && flush !== Z_FINISH)) {
    return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR);
  }

  s.strm = strm; /* just in case */
  old_flush = s.last_flush;
  s.last_flush = flush;

  /* Write the header */
  if (s.status === INIT_STATE) {

    if (s.wrap === 2) { // GZIP header
      strm.adler = 0;  //crc32(0L, Z_NULL, 0);
      put_byte(s, 31);
      put_byte(s, 139);
      put_byte(s, 8);
      if (!s.gzhead) { // s->gzhead == Z_NULL
        put_byte(s, 0);
        put_byte(s, 0);
        put_byte(s, 0);
        put_byte(s, 0);
        put_byte(s, 0);
        put_byte(s, s.level === 9 ? 2 :
                    (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?
                     4 : 0));
        put_byte(s, OS_CODE);
        s.status = BUSY_STATE;
      }
      else {
        put_byte(s, (s.gzhead.text ? 1 : 0) +
                    (s.gzhead.hcrc ? 2 : 0) +
                    (!s.gzhead.extra ? 0 : 4) +
                    (!s.gzhead.name ? 0 : 8) +
                    (!s.gzhead.comment ? 0 : 16)
                );
        put_byte(s, s.gzhead.time & 0xff);
        put_byte(s, (s.gzhead.time >> 8) & 0xff);
        put_byte(s, (s.gzhead.time >> 16) & 0xff);
        put_byte(s, (s.gzhead.time >> 24) & 0xff);
        put_byte(s, s.level === 9 ? 2 :
                    (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?
                     4 : 0));
        put_byte(s, s.gzhead.os & 0xff);
        if (s.gzhead.extra && s.gzhead.extra.length) {
          put_byte(s, s.gzhead.extra.length & 0xff);
          put_byte(s, (s.gzhead.extra.length >> 8) & 0xff);
        }
        if (s.gzhead.hcrc) {
          strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0);
        }
        s.gzindex = 0;
        s.status = EXTRA_STATE;
      }
    }
    else // DEFLATE header
    {
      var header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8;
      var level_flags = -1;

      if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) {
        level_flags = 0;
      } else if (s.level < 6) {
        level_flags = 1;
      } else if (s.level === 6) {
        level_flags = 2;
      } else {
        level_flags = 3;
      }
      header |= (level_flags << 6);
      if (s.strstart !== 0) { header |= PRESET_DICT; }
      header += 31 - (header % 31);

      s.status = BUSY_STATE;
      putShortMSB(s, header);

      /* Save the adler32 of the preset dictionary: */
      if (s.strstart !== 0) {
        putShortMSB(s, strm.adler >>> 16);
        putShortMSB(s, strm.adler & 0xffff);
      }
      strm.adler = 1; // adler32(0L, Z_NULL, 0);
    }
  }

//#ifdef GZIP
  if (s.status === EXTRA_STATE) {
    if (s.gzhead.extra/* != Z_NULL*/) {
      beg = s.pending;  /* start of bytes to update crc */

      while (s.gzindex < (s.gzhead.extra.length & 0xffff)) {
        if (s.pending === s.pending_buf_size) {
          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
          }
          flush_pending(strm);
          beg = s.pending;
          if (s.pending === s.pending_buf_size) {
            break;
          }
        }
        put_byte(s, s.gzhead.extra[s.gzindex] & 0xff);
        s.gzindex++;
      }
      if (s.gzhead.hcrc && s.pending > beg) {
        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
      }
      if (s.gzindex === s.gzhead.extra.length) {
        s.gzindex = 0;
        s.status = NAME_STATE;
      }
    }
    else {
      s.status = NAME_STATE;
    }
  }
  if (s.status === NAME_STATE) {
    if (s.gzhead.name/* != Z_NULL*/) {
      beg = s.pending;  /* start of bytes to update crc */
      //int val;

      do {
        if (s.pending === s.pending_buf_size) {
          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
          }
          flush_pending(strm);
          beg = s.pending;
          if (s.pending === s.pending_buf_size) {
            val = 1;
            break;
          }
        }
        // JS specific: little magic to add zero terminator to end of string
        if (s.gzindex < s.gzhead.name.length) {
          val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;
        } else {
          val = 0;
        }
        put_byte(s, val);
      } while (val !== 0);

      if (s.gzhead.hcrc && s.pending > beg) {
        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
      }
      if (val === 0) {
        s.gzindex = 0;
        s.status = COMMENT_STATE;
      }
    }
    else {
      s.status = COMMENT_STATE;
    }
  }
  if (s.status === COMMENT_STATE) {
    if (s.gzhead.comment/* != Z_NULL*/) {
      beg = s.pending;  /* start of bytes to update crc */
      //int val;

      do {
        if (s.pending === s.pending_buf_size) {
          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
          }
          flush_pending(strm);
          beg = s.pending;
          if (s.pending === s.pending_buf_size) {
            val = 1;
            break;
          }
        }
        // JS specific: little magic to add zero terminator to end of string
        if (s.gzindex < s.gzhead.comment.length) {
          val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;
        } else {
          val = 0;
        }
        put_byte(s, val);
      } while (val !== 0);

      if (s.gzhead.hcrc && s.pending > beg) {
        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
      }
      if (val === 0) {
        s.status = HCRC_STATE;
      }
    }
    else {
      s.status = HCRC_STATE;
    }
  }
  if (s.status === HCRC_STATE) {
    if (s.gzhead.hcrc) {
      if (s.pending + 2 > s.pending_buf_size) {
        flush_pending(strm);
      }
      if (s.pending + 2 <= s.pending_buf_size) {
        put_byte(s, strm.adler & 0xff);
        put_byte(s, (strm.adler >> 8) & 0xff);
        strm.adler = 0; //crc32(0L, Z_NULL, 0);
        s.status = BUSY_STATE;
      }
    }
    else {
      s.status = BUSY_STATE;
    }
  }
//#endif

  /* Flush as much pending output as possible */
  if (s.pending !== 0) {
    flush_pending(strm);
    if (strm.avail_out === 0) {
      /* Since avail_out is 0, deflate will be called again with
       * more output space, but possibly with both pending and
       * avail_in equal to zero. There won't be anything to do,
       * but this is not an error situation so make sure we
       * return OK instead of BUF_ERROR at next call of deflate:
       */
      s.last_flush = -1;
      return Z_OK;
    }

    /* Make sure there is something to do and avoid duplicate consecutive
     * flushes. For repeated and useless calls with Z_FINISH, we keep
     * returning Z_STREAM_END instead of Z_BUF_ERROR.
     */
  } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) &&
    flush !== Z_FINISH) {
    return err(strm, Z_BUF_ERROR);
  }

  /* User must not provide more input after the first FINISH: */
  if (s.status === FINISH_STATE && strm.avail_in !== 0) {
    return err(strm, Z_BUF_ERROR);
  }

  /* Start a new block or continue the current one.
   */
  if (strm.avail_in !== 0 || s.lookahead !== 0 ||
    (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) {
    var bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) :
      (s.strategy === Z_RLE ? deflate_rle(s, flush) :
        configuration_table[s.level].func(s, flush));

    if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) {
      s.status = FINISH_STATE;
    }
    if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) {
      if (strm.avail_out === 0) {
        s.last_flush = -1;
        /* avoid BUF_ERROR next call, see above */
      }
      return Z_OK;
      /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
       * of deflate should use the same flush parameter to make sure
       * that the flush is complete. So we don't have to output an
       * empty block here, this will be done at next call. This also
       * ensures that for a very small output buffer, we emit at most
       * one empty block.
       */
    }
    if (bstate === BS_BLOCK_DONE) {
      if (flush === Z_PARTIAL_FLUSH) {
        trees._tr_align(s);
      }
      else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */

        trees._tr_stored_block(s, 0, 0, false);
        /* For a full flush, this empty block will be recognized
         * as a special marker by inflate_sync().
         */
        if (flush === Z_FULL_FLUSH) {
          /*** CLEAR_HASH(s); ***/             /* forget history */
          zero(s.head); // Fill with NIL (= 0);

          if (s.lookahead === 0) {
            s.strstart = 0;
            s.block_start = 0;
            s.insert = 0;
          }
        }
      }
      flush_pending(strm);
      if (strm.avail_out === 0) {
        s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */
        return Z_OK;
      }
    }
  }
  //Assert(strm->avail_out > 0, "bug2");
  //if (strm.avail_out <= 0) { throw new Error("bug2");}

  if (flush !== Z_FINISH) { return Z_OK; }
  if (s.wrap <= 0) { return Z_STREAM_END; }

  /* Write the trailer */
  if (s.wrap === 2) {
    put_byte(s, strm.adler & 0xff);
    put_byte(s, (strm.adler >> 8) & 0xff);
    put_byte(s, (strm.adler >> 16) & 0xff);
    put_byte(s, (strm.adler >> 24) & 0xff);
    put_byte(s, strm.total_in & 0xff);
    put_byte(s, (strm.total_in >> 8) & 0xff);
    put_byte(s, (strm.total_in >> 16) & 0xff);
    put_byte(s, (strm.total_in >> 24) & 0xff);
  }
  else
  {
    putShortMSB(s, strm.adler >>> 16);
    putShortMSB(s, strm.adler & 0xffff);
  }

  flush_pending(strm);
  /* If avail_out is zero, the application will call deflate again
   * to flush the rest.
   */
  if (s.wrap > 0) { s.wrap = -s.wrap; }
  /* write the trailer only once! */
  return s.pending !== 0 ? Z_OK : Z_STREAM_END;
}

function deflateEnd(strm) {
  var status;

  if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {
    return Z_STREAM_ERROR;
  }

  status = strm.state.status;
  if (status !== INIT_STATE &&
    status !== EXTRA_STATE &&
    status !== NAME_STATE &&
    status !== COMMENT_STATE &&
    status !== HCRC_STATE &&
    status !== BUSY_STATE &&
    status !== FINISH_STATE
  ) {
    return err(strm, Z_STREAM_ERROR);
  }

  strm.state = null;

  return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK;
}


/* =========================================================================
 * Initializes the compression dictionary from the given byte
 * sequence without producing any compressed output.
 */
function deflateSetDictionary(strm, dictionary) {
  var dictLength = dictionary.length;

  var s;
  var str, n;
  var wrap;
  var avail;
  var next;
  var input;
  var tmpDict;

  if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {
    return Z_STREAM_ERROR;
  }

  s = strm.state;
  wrap = s.wrap;

  if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) {
    return Z_STREAM_ERROR;
  }

  /* when using zlib wrappers, compute Adler-32 for provided dictionary */
  if (wrap === 1) {
    /* adler32(strm->adler, dictionary, dictLength); */
    strm.adler = adler32(strm.adler, dictionary, dictLength, 0);
  }

  s.wrap = 0;   /* avoid computing Adler-32 in read_buf */

  /* if dictionary would fill window, just replace the history */
  if (dictLength >= s.w_size) {
    if (wrap === 0) {            /* already empty otherwise */
      /*** CLEAR_HASH(s); ***/
      zero(s.head); // Fill with NIL (= 0);
      s.strstart = 0;
      s.block_start = 0;
      s.insert = 0;
    }
    /* use the tail */
    // dictionary = dictionary.slice(dictLength - s.w_size);
    tmpDict = new utils.Buf8(s.w_size);
    utils.arraySet(tmpDict, dictionary, dictLength - s.w_size, s.w_size, 0);
    dictionary = tmpDict;
    dictLength = s.w_size;
  }
  /* insert dictionary into window and hash */
  avail = strm.avail_in;
  next = strm.next_in;
  input = strm.input;
  strm.avail_in = dictLength;
  strm.next_in = 0;
  strm.input = dictionary;
  fill_window(s);
  while (s.lookahead >= MIN_MATCH) {
    str = s.strstart;
    n = s.lookahead - (MIN_MATCH - 1);
    do {
      /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask;

      s.prev[str & s.w_mask] = s.head[s.ins_h];

      s.head[s.ins_h] = str;
      str++;
    } while (--n);
    s.strstart = str;
    s.lookahead = MIN_MATCH - 1;
    fill_window(s);
  }
  s.strstart += s.lookahead;
  s.block_start = s.strstart;
  s.insert = s.lookahead;
  s.lookahead = 0;
  s.match_length = s.prev_length = MIN_MATCH - 1;
  s.match_available = 0;
  strm.next_in = next;
  strm.input = input;
  strm.avail_in = avail;
  s.wrap = wrap;
  return Z_OK;
}


exports.deflateInit = deflateInit;
exports.deflateInit2 = deflateInit2;
exports.deflateReset = deflateReset;
exports.deflateResetKeep = deflateResetKeep;
exports.deflateSetHeader = deflateSetHeader;
exports.deflate = deflate;
exports.deflateEnd = deflateEnd;
exports.deflateSetDictionary = deflateSetDictionary;
exports.deflateInfo = 'pako deflate (from Nodeca project)';

/* Not implemented
exports.deflateBound = deflateBound;
exports.deflateCopy = deflateCopy;
exports.deflateParams = deflateParams;
exports.deflatePending = deflatePending;
exports.deflatePrime = deflatePrime;
exports.deflateTune = deflateTune;
*/

},{"../utils/common":23,"./adler32":25,"./crc32":26,"./messages":28,"./trees":29}],28:[function(require,module,exports){
'use strict';

module.exports = {
  2:      'need dictionary',     /* Z_NEED_DICT       2  */
  1:      'stream end',          /* Z_STREAM_END      1  */
  0:      '',                    /* Z_OK              0  */
  '-1':   'file error',          /* Z_ERRNO         (-1) */
  '-2':   'stream error',        /* Z_STREAM_ERROR  (-2) */
  '-3':   'data error',          /* Z_DATA_ERROR    (-3) */
  '-4':   'insufficient memory', /* Z_MEM_ERROR     (-4) */
  '-5':   'buffer error',        /* Z_BUF_ERROR     (-5) */
  '-6':   'incompatible version' /* Z_VERSION_ERROR (-6) */
};

},{}],29:[function(require,module,exports){
'use strict';


var utils = require('../utils/common');

/* Public constants ==========================================================*/
/* ===========================================================================*/


//var Z_FILTERED          = 1;
//var Z_HUFFMAN_ONLY      = 2;
//var Z_RLE               = 3;
var Z_FIXED               = 4;
//var Z_DEFAULT_STRATEGY  = 0;

/* Possible values of the data_type field (though see inflate()) */
var Z_BINARY              = 0;
var Z_TEXT                = 1;
//var Z_ASCII             = 1; // = Z_TEXT
var Z_UNKNOWN             = 2;

/*============================================================================*/


function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }

// From zutil.h

var STORED_BLOCK = 0;
var STATIC_TREES = 1;
var DYN_TREES    = 2;
/* The three kinds of block type */

var MIN_MATCH    = 3;
var MAX_MATCH    = 258;
/* The minimum and maximum match lengths */

// From deflate.h
/* ===========================================================================
 * Internal compression state.
 */

var LENGTH_CODES  = 29;
/* number of length codes, not counting the special END_BLOCK code */

var LITERALS      = 256;
/* number of literal bytes 0..255 */

var L_CODES       = LITERALS + 1 + LENGTH_CODES;
/* number of Literal or Length codes, including the END_BLOCK code */

var D_CODES       = 30;
/* number of distance codes */

var BL_CODES      = 19;
/* number of codes used to transfer the bit lengths */

var HEAP_SIZE     = 2 * L_CODES + 1;
/* maximum heap size */

var MAX_BITS      = 15;
/* All codes must not exceed MAX_BITS bits */

var Buf_size      = 16;
/* size of bit buffer in bi_buf */


/* ===========================================================================
 * Constants
 */

var MAX_BL_BITS = 7;
/* Bit length codes must not exceed MAX_BL_BITS bits */

var END_BLOCK   = 256;
/* end of block literal code */

var REP_3_6     = 16;
/* repeat previous bit length 3-6 times (2 bits of repeat count) */

var REPZ_3_10   = 17;
/* repeat a zero length 3-10 times  (3 bits of repeat count) */

var REPZ_11_138 = 18;
/* repeat a zero length 11-138 times  (7 bits of repeat count) */

/* eslint-disable comma-spacing,array-bracket-spacing */
var extra_lbits =   /* extra bits for each length code */
  [0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0];

var extra_dbits =   /* extra bits for each distance code */
  [0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];

var extra_blbits =  /* extra bits for each bit length code */
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7];

var bl_order =
  [16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];
/* eslint-enable comma-spacing,array-bracket-spacing */

/* The lengths of the bit length codes are sent in order of decreasing
 * probability, to avoid transmitting the lengths for unused bit length codes.
 */

/* ===========================================================================
 * Local data. These are initialized only once.
 */

// We pre-fill arrays with 0 to avoid uninitialized gaps

var DIST_CODE_LEN = 512; /* see definition of array dist_code below */

// !!!! Use flat array insdead of structure, Freq = i*2, Len = i*2+1
var static_ltree  = new Array((L_CODES + 2) * 2);
zero(static_ltree);
/* The static literal tree. Since the bit lengths are imposed, there is no
 * need for the L_CODES extra codes used during heap construction. However
 * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
 * below).
 */

var static_dtree  = new Array(D_CODES * 2);
zero(static_dtree);
/* The static distance tree. (Actually a trivial tree since all codes use
 * 5 bits.)
 */

var _dist_code    = new Array(DIST_CODE_LEN);
zero(_dist_code);
/* Distance codes. The first 256 values correspond to the distances
 * 3 .. 258, the last 256 values correspond to the top 8 bits of
 * the 15 bit distances.
 */

var _length_code  = new Array(MAX_MATCH - MIN_MATCH + 1);
zero(_length_code);
/* length code for each normalized match length (0 == MIN_MATCH) */

var base_length   = new Array(LENGTH_CODES);
zero(base_length);
/* First normalized length for each code (0 = MIN_MATCH) */

var base_dist     = new Array(D_CODES);
zero(base_dist);
/* First normalized distance for each code (0 = distance of 1) */


function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) {

  this.static_tree  = static_tree;  /* static tree or NULL */
  this.extra_bits   = extra_bits;   /* extra bits for each code or NULL */
  this.extra_base   = extra_base;   /* base index for extra_bits */
  this.elems        = elems;        /* max number of elements in the tree */
  this.max_length   = max_length;   /* max bit length for the codes */

  // show if `static_tree` has data or dummy - needed for monomorphic objects
  this.has_stree    = static_tree && static_tree.length;
}


var static_l_desc;
var static_d_desc;
var static_bl_desc;


function TreeDesc(dyn_tree, stat_desc) {
  this.dyn_tree = dyn_tree;     /* the dynamic tree */
  this.max_code = 0;            /* largest code with non zero frequency */
  this.stat_desc = stat_desc;   /* the corresponding static tree */
}



function d_code(dist) {
  return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];
}


/* ===========================================================================
 * Output a short LSB first on the stream.
 * IN assertion: there is enough room in pendingBuf.
 */
function put_short(s, w) {
//    put_byte(s, (uch)((w) & 0xff));
//    put_byte(s, (uch)((ush)(w) >> 8));
  s.pending_buf[s.pending++] = (w) & 0xff;
  s.pending_buf[s.pending++] = (w >>> 8) & 0xff;
}


/* ===========================================================================
 * Send a value on a given number of bits.
 * IN assertion: length <= 16 and value fits in length bits.
 */
function send_bits(s, value, length) {
  if (s.bi_valid > (Buf_size - length)) {
    s.bi_buf |= (value << s.bi_valid) & 0xffff;
    put_short(s, s.bi_buf);
    s.bi_buf = value >> (Buf_size - s.bi_valid);
    s.bi_valid += length - Buf_size;
  } else {
    s.bi_buf |= (value << s.bi_valid) & 0xffff;
    s.bi_valid += length;
  }
}


function send_code(s, c, tree) {
  send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/);
}


/* ===========================================================================
 * Reverse the first len bits of a code, using straightforward code (a faster
 * method would use a table)
 * IN assertion: 1 <= len <= 15
 */
function bi_reverse(code, len) {
  var res = 0;
  do {
    res |= code & 1;
    code >>>= 1;
    res <<= 1;
  } while (--len > 0);
  return res >>> 1;
}


/* ===========================================================================
 * Flush the bit buffer, keeping at most 7 bits in it.
 */
function bi_flush(s) {
  if (s.bi_valid === 16) {
    put_short(s, s.bi_buf);
    s.bi_buf = 0;
    s.bi_valid = 0;

  } else if (s.bi_valid >= 8) {
    s.pending_buf[s.pending++] = s.bi_buf & 0xff;
    s.bi_buf >>= 8;
    s.bi_valid -= 8;
  }
}


/* ===========================================================================
 * Compute the optimal bit lengths for a tree and update the total bit length
 * for the current block.
 * IN assertion: the fields freq and dad are set, heap[heap_max] and
 *    above are the tree nodes sorted by increasing frequency.
 * OUT assertions: the field len is set to the optimal bit length, the
 *     array bl_count contains the frequencies for each bit length.
 *     The length opt_len is updated; static_len is also updated if stree is
 *     not null.
 */
function gen_bitlen(s, desc)
//    deflate_state *s;
//    tree_desc *desc;    /* the tree descriptor */
{
  var tree            = desc.dyn_tree;
  var max_code        = desc.max_code;
  var stree           = desc.stat_desc.static_tree;
  var has_stree       = desc.stat_desc.has_stree;
  var extra           = desc.stat_desc.extra_bits;
  var base            = desc.stat_desc.extra_base;
  var max_length      = desc.stat_desc.max_length;
  var h;              /* heap index */
  var n, m;           /* iterate over the tree elements */
  var bits;           /* bit length */
  var xbits;          /* extra bits */
  var f;              /* frequency */
  var overflow = 0;   /* number of elements with bit length too large */

  for (bits = 0; bits <= MAX_BITS; bits++) {
    s.bl_count[bits] = 0;
  }

  /* In a first pass, compute the optimal bit lengths (which may
   * overflow in the case of the bit length tree).
   */
  tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */

  for (h = s.heap_max + 1; h < HEAP_SIZE; h++) {
    n = s.heap[h];
    bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1;
    if (bits > max_length) {
      bits = max_length;
      overflow++;
    }
    tree[n * 2 + 1]/*.Len*/ = bits;
    /* We overwrite tree[n].Dad which is no longer needed */

    if (n > max_code) { continue; } /* not a leaf node */

    s.bl_count[bits]++;
    xbits = 0;
    if (n >= base) {
      xbits = extra[n - base];
    }
    f = tree[n * 2]/*.Freq*/;
    s.opt_len += f * (bits + xbits);
    if (has_stree) {
      s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits);
    }
  }
  if (overflow === 0) { return; }

  // Trace((stderr,"\nbit length overflow\n"));
  /* This happens for example on obj2 and pic of the Calgary corpus */

  /* Find the first bit length which could increase: */
  do {
    bits = max_length - 1;
    while (s.bl_count[bits] === 0) { bits--; }
    s.bl_count[bits]--;      /* move one leaf down the tree */
    s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */
    s.bl_count[max_length]--;
    /* The brother of the overflow item also moves one step up,
     * but this does not affect bl_count[max_length]
     */
    overflow -= 2;
  } while (overflow > 0);

  /* Now recompute all bit lengths, scanning in increasing frequency.
   * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
   * lengths instead of fixing only the wrong ones. This idea is taken
   * from 'ar' written by Haruhiko Okumura.)
   */
  for (bits = max_length; bits !== 0; bits--) {
    n = s.bl_count[bits];
    while (n !== 0) {
      m = s.heap[--h];
      if (m > max_code) { continue; }
      if (tree[m * 2 + 1]/*.Len*/ !== bits) {
        // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
        s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/;
        tree[m * 2 + 1]/*.Len*/ = bits;
      }
      n--;
    }
  }
}


/* ===========================================================================
 * Generate the codes for a given tree and bit counts (which need not be
 * optimal).
 * IN assertion: the array bl_count contains the bit length statistics for
 * the given tree and the field len is set for all tree elements.
 * OUT assertion: the field code is set for all tree elements of non
 *     zero code length.
 */
function gen_codes(tree, max_code, bl_count)
//    ct_data *tree;             /* the tree to decorate */
//    int max_code;              /* largest code with non zero frequency */
//    ushf *bl_count;            /* number of codes at each bit length */
{
  var next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */
  var code = 0;              /* running code value */
  var bits;                  /* bit index */
  var n;                     /* code index */

  /* The distribution counts are first used to generate the code values
   * without bit reversal.
   */
  for (bits = 1; bits <= MAX_BITS; bits++) {
    next_code[bits] = code = (code + bl_count[bits - 1]) << 1;
  }
  /* Check that the bit counts in bl_count are consistent. The last code
   * must be all ones.
   */
  //Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
  //        "inconsistent bit counts");
  //Tracev((stderr,"\ngen_codes: max_code %d ", max_code));

  for (n = 0;  n <= max_code; n++) {
    var len = tree[n * 2 + 1]/*.Len*/;
    if (len === 0) { continue; }
    /* Now reverse the bits */
    tree[n * 2]/*.Code*/ = bi_reverse(next_code[len]++, len);

    //Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
    //     n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
  }
}


/* ===========================================================================
 * Initialize the various 'constant' tables.
 */
function tr_static_init() {
  var n;        /* iterates over tree elements */
  var bits;     /* bit counter */
  var length;   /* length value */
  var code;     /* code value */
  var dist;     /* distance index */
  var bl_count = new Array(MAX_BITS + 1);
  /* number of codes at each bit length for an optimal tree */

  // do check in _tr_init()
  //if (static_init_done) return;

  /* For some embedded targets, global variables are not initialized: */
/*#ifdef NO_INIT_GLOBAL_POINTERS
  static_l_desc.static_tree = static_ltree;
  static_l_desc.extra_bits = extra_lbits;
  static_d_desc.static_tree = static_dtree;
  static_d_desc.extra_bits = extra_dbits;
  static_bl_desc.extra_bits = extra_blbits;
#endif*/

  /* Initialize the mapping length (0..255) -> length code (0..28) */
  length = 0;
  for (code = 0; code < LENGTH_CODES - 1; code++) {
    base_length[code] = length;
    for (n = 0; n < (1 << extra_lbits[code]); n++) {
      _length_code[length++] = code;
    }
  }
  //Assert (length == 256, "tr_static_init: length != 256");
  /* Note that the length 255 (match length 258) can be represented
   * in two different ways: code 284 + 5 bits or code 285, so we
   * overwrite length_code[255] to use the best encoding:
   */
  _length_code[length - 1] = code;

  /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
  dist = 0;
  for (code = 0; code < 16; code++) {
    base_dist[code] = dist;
    for (n = 0; n < (1 << extra_dbits[code]); n++) {
      _dist_code[dist++] = code;
    }
  }
  //Assert (dist == 256, "tr_static_init: dist != 256");
  dist >>= 7; /* from now on, all distances are divided by 128 */
  for (; code < D_CODES; code++) {
    base_dist[code] = dist << 7;
    for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) {
      _dist_code[256 + dist++] = code;
    }
  }
  //Assert (dist == 256, "tr_static_init: 256+dist != 512");

  /* Construct the codes of the static literal tree */
  for (bits = 0; bits <= MAX_BITS; bits++) {
    bl_count[bits] = 0;
  }

  n = 0;
  while (n <= 143) {
    static_ltree[n * 2 + 1]/*.Len*/ = 8;
    n++;
    bl_count[8]++;
  }
  while (n <= 255) {
    static_ltree[n * 2 + 1]/*.Len*/ = 9;
    n++;
    bl_count[9]++;
  }
  while (n <= 279) {
    static_ltree[n * 2 + 1]/*.Len*/ = 7;
    n++;
    bl_count[7]++;
  }
  while (n <= 287) {
    static_ltree[n * 2 + 1]/*.Len*/ = 8;
    n++;
    bl_count[8]++;
  }
  /* Codes 286 and 287 do not exist, but we must include them in the
   * tree construction to get a canonical Huffman tree (longest code
   * all ones)
   */
  gen_codes(static_ltree, L_CODES + 1, bl_count);

  /* The static distance tree is trivial: */
  for (n = 0; n < D_CODES; n++) {
    static_dtree[n * 2 + 1]/*.Len*/ = 5;
    static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5);
  }

  // Now data ready and we can init static trees
  static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS);
  static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS);
  static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0,         BL_CODES, MAX_BL_BITS);

  //static_init_done = true;
}


/* ===========================================================================
 * Initialize a new block.
 */
function init_block(s) {
  var n; /* iterates over tree elements */

  /* Initialize the trees. */
  for (n = 0; n < L_CODES;  n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; }
  for (n = 0; n < D_CODES;  n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; }
  for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; }

  s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1;
  s.opt_len = s.static_len = 0;
  s.last_lit = s.matches = 0;
}


/* ===========================================================================
 * Flush the bit buffer and align the output on a byte boundary
 */
function bi_windup(s)
{
  if (s.bi_valid > 8) {
    put_short(s, s.bi_buf);
  } else if (s.bi_valid > 0) {
    //put_byte(s, (Byte)s->bi_buf);
    s.pending_buf[s.pending++] = s.bi_buf;
  }
  s.bi_buf = 0;
  s.bi_valid = 0;
}

/* ===========================================================================
 * Copy a stored block, storing first the length and its
 * one's complement if requested.
 */
function copy_block(s, buf, len, header)
//DeflateState *s;
//charf    *buf;    /* the input data */
//unsigned len;     /* its length */
//int      header;  /* true if block header must be written */
{
  bi_windup(s);        /* align on byte boundary */

  if (header) {
    put_short(s, len);
    put_short(s, ~len);
  }
//  while (len--) {
//    put_byte(s, *buf++);
//  }
  utils.arraySet(s.pending_buf, s.window, buf, len, s.pending);
  s.pending += len;
}

/* ===========================================================================
 * Compares to subtrees, using the tree depth as tie breaker when
 * the subtrees have equal frequency. This minimizes the worst case length.
 */
function smaller(tree, n, m, depth) {
  var _n2 = n * 2;
  var _m2 = m * 2;
  return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ ||
         (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m]));
}

/* ===========================================================================
 * Restore the heap property by moving down the tree starting at node k,
 * exchanging a node with the smallest of its two sons if necessary, stopping
 * when the heap property is re-established (each father smaller than its
 * two sons).
 */
function pqdownheap(s, tree, k)
//    deflate_state *s;
//    ct_data *tree;  /* the tree to restore */
//    int k;               /* node to move down */
{
  var v = s.heap[k];
  var j = k << 1;  /* left son of k */
  while (j <= s.heap_len) {
    /* Set j to the smallest of the two sons: */
    if (j < s.heap_len &&
      smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) {
      j++;
    }
    /* Exit if v is smaller than both sons */
    if (smaller(tree, v, s.heap[j], s.depth)) { break; }

    /* Exchange v with the smallest son */
    s.heap[k] = s.heap[j];
    k = j;

    /* And continue down the tree, setting j to the left son of k */
    j <<= 1;
  }
  s.heap[k] = v;
}


// inlined manually
// var SMALLEST = 1;

/* ===========================================================================
 * Send the block data compressed using the given Huffman trees
 */
function compress_block(s, ltree, dtree)
//    deflate_state *s;
//    const ct_data *ltree; /* literal tree */
//    const ct_data *dtree; /* distance tree */
{
  var dist;           /* distance of matched string */
  var lc;             /* match length or unmatched char (if dist == 0) */
  var lx = 0;         /* running index in l_buf */
  var code;           /* the code to send */
  var extra;          /* number of extra bits to send */

  if (s.last_lit !== 0) {
    do {
      dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | (s.pending_buf[s.d_buf + lx * 2 + 1]);
      lc = s.pending_buf[s.l_buf + lx];
      lx++;

      if (dist === 0) {
        send_code(s, lc, ltree); /* send a literal byte */
        //Tracecv(isgraph(lc), (stderr," '%c' ", lc));
      } else {
        /* Here, lc is the match length - MIN_MATCH */
        code = _length_code[lc];
        send_code(s, code + LITERALS + 1, ltree); /* send the length code */
        extra = extra_lbits[code];
        if (extra !== 0) {
          lc -= base_length[code];
          send_bits(s, lc, extra);       /* send the extra length bits */
        }
        dist--; /* dist is now the match distance - 1 */
        code = d_code(dist);
        //Assert (code < D_CODES, "bad d_code");

        send_code(s, code, dtree);       /* send the distance code */
        extra = extra_dbits[code];
        if (extra !== 0) {
          dist -= base_dist[code];
          send_bits(s, dist, extra);   /* send the extra distance bits */
        }
      } /* literal or match pair ? */

      /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
      //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
      //       "pendingBuf overflow");

    } while (lx < s.last_lit);
  }

  send_code(s, END_BLOCK, ltree);
}


/* ===========================================================================
 * Construct one Huffman tree and assigns the code bit strings and lengths.
 * Update the total bit length for the current block.
 * IN assertion: the field freq is set for all tree elements.
 * OUT assertions: the fields len and code are set to the optimal bit length
 *     and corresponding code. The length opt_len is updated; static_len is
 *     also updated if stree is not null. The field max_code is set.
 */
function build_tree(s, desc)
//    deflate_state *s;
//    tree_desc *desc; /* the tree descriptor */
{
  var tree     = desc.dyn_tree;
  var stree    = desc.stat_desc.static_tree;
  var has_stree = desc.stat_desc.has_stree;
  var elems    = desc.stat_desc.elems;
  var n, m;          /* iterate over heap elements */
  var max_code = -1; /* largest code with non zero frequency */
  var node;          /* new node being created */

  /* Construct the initial heap, with least frequent element in
   * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
   * heap[0] is not used.
   */
  s.heap_len = 0;
  s.heap_max = HEAP_SIZE;

  for (n = 0; n < elems; n++) {
    if (tree[n * 2]/*.Freq*/ !== 0) {
      s.heap[++s.heap_len] = max_code = n;
      s.depth[n] = 0;

    } else {
      tree[n * 2 + 1]/*.Len*/ = 0;
    }
  }

  /* The pkzip format requires that at least one distance code exists,
   * and that at least one bit should be sent even if there is only one
   * possible code. So to avoid special checks later on we force at least
   * two codes of non zero frequency.
   */
  while (s.heap_len < 2) {
    node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0);
    tree[node * 2]/*.Freq*/ = 1;
    s.depth[node] = 0;
    s.opt_len--;

    if (has_stree) {
      s.static_len -= stree[node * 2 + 1]/*.Len*/;
    }
    /* node is 0 or 1 so it does not have extra bits */
  }
  desc.max_code = max_code;

  /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
   * establish sub-heaps of increasing lengths:
   */
  for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); }

  /* Construct the Huffman tree by repeatedly combining the least two
   * frequent nodes.
   */
  node = elems;              /* next internal node of the tree */
  do {
    //pqremove(s, tree, n);  /* n = node of least frequency */
    /*** pqremove ***/
    n = s.heap[1/*SMALLEST*/];
    s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--];
    pqdownheap(s, tree, 1/*SMALLEST*/);
    /***/

    m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */

    s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */
    s.heap[--s.heap_max] = m;

    /* Create a new node father of n and m */
    tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/;
    s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;
    tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node;

    /* and insert the new node in the heap */
    s.heap[1/*SMALLEST*/] = node++;
    pqdownheap(s, tree, 1/*SMALLEST*/);

  } while (s.heap_len >= 2);

  s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/];

  /* At this point, the fields freq and dad are set. We can now
   * generate the bit lengths.
   */
  gen_bitlen(s, desc);

  /* The field len is now set, we can generate the bit codes */
  gen_codes(tree, max_code, s.bl_count);
}


/* ===========================================================================
 * Scan a literal or distance tree to determine the frequencies of the codes
 * in the bit length tree.
 */
function scan_tree(s, tree, max_code)
//    deflate_state *s;
//    ct_data *tree;   /* the tree to be scanned */
//    int max_code;    /* and its largest code of non zero frequency */
{
  var n;                     /* iterates over all tree elements */
  var prevlen = -1;          /* last emitted length */
  var curlen;                /* length of current code */

  var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */

  var count = 0;             /* repeat count of the current code */
  var max_count = 7;         /* max repeat count */
  var min_count = 4;         /* min repeat count */

  if (nextlen === 0) {
    max_count = 138;
    min_count = 3;
  }
  tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */

  for (n = 0; n <= max_code; n++) {
    curlen = nextlen;
    nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;

    if (++count < max_count && curlen === nextlen) {
      continue;

    } else if (count < min_count) {
      s.bl_tree[curlen * 2]/*.Freq*/ += count;

    } else if (curlen !== 0) {

      if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; }
      s.bl_tree[REP_3_6 * 2]/*.Freq*/++;

    } else if (count <= 10) {
      s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++;

    } else {
      s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++;
    }

    count = 0;
    prevlen = curlen;

    if (nextlen === 0) {
      max_count = 138;
      min_count = 3;

    } else if (curlen === nextlen) {
      max_count = 6;
      min_count = 3;

    } else {
      max_count = 7;
      min_count = 4;
    }
  }
}


/* ===========================================================================
 * Send a literal or distance tree in compressed form, using the codes in
 * bl_tree.
 */
function send_tree(s, tree, max_code)
//    deflate_state *s;
//    ct_data *tree; /* the tree to be scanned */
//    int max_code;       /* and its largest code of non zero frequency */
{
  var n;                     /* iterates over all tree elements */
  var prevlen = -1;          /* last emitted length */
  var curlen;                /* length of current code */

  var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */

  var count = 0;             /* repeat count of the current code */
  var max_count = 7;         /* max repeat count */
  var min_count = 4;         /* min repeat count */

  /* tree[max_code+1].Len = -1; */  /* guard already set */
  if (nextlen === 0) {
    max_count = 138;
    min_count = 3;
  }

  for (n = 0; n <= max_code; n++) {
    curlen = nextlen;
    nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;

    if (++count < max_count && curlen === nextlen) {
      continue;

    } else if (count < min_count) {
      do { send_code(s, curlen, s.bl_tree); } while (--count !== 0);

    } else if (curlen !== 0) {
      if (curlen !== prevlen) {
        send_code(s, curlen, s.bl_tree);
        count--;
      }
      //Assert(count >= 3 && count <= 6, " 3_6?");
      send_code(s, REP_3_6, s.bl_tree);
      send_bits(s, count - 3, 2);

    } else if (count <= 10) {
      send_code(s, REPZ_3_10, s.bl_tree);
      send_bits(s, count - 3, 3);

    } else {
      send_code(s, REPZ_11_138, s.bl_tree);
      send_bits(s, count - 11, 7);
    }

    count = 0;
    prevlen = curlen;
    if (nextlen === 0) {
      max_count = 138;
      min_count = 3;

    } else if (curlen === nextlen) {
      max_count = 6;
      min_count = 3;

    } else {
      max_count = 7;
      min_count = 4;
    }
  }
}


/* ===========================================================================
 * Construct the Huffman tree for the bit lengths and return the index in
 * bl_order of the last bit length code to send.
 */
function build_bl_tree(s) {
  var max_blindex;  /* index of last bit length code of non zero freq */

  /* Determine the bit length frequencies for literal and distance trees */
  scan_tree(s, s.dyn_ltree, s.l_desc.max_code);
  scan_tree(s, s.dyn_dtree, s.d_desc.max_code);

  /* Build the bit length tree: */
  build_tree(s, s.bl_desc);
  /* opt_len now includes the length of the tree representations, except
   * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
   */

  /* Determine the number of bit length codes to send. The pkzip format
   * requires that at least 4 bit length codes be sent. (appnote.txt says
   * 3 but the actual value used is 4.)
   */
  for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) {
    if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) {
      break;
    }
  }
  /* Update opt_len to include the bit length tree and counts */
  s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
  //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
  //        s->opt_len, s->static_len));

  return max_blindex;
}


/* ===========================================================================
 * Send the header for a block using dynamic Huffman trees: the counts, the
 * lengths of the bit length codes, the literal tree and the distance tree.
 * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
 */
function send_all_trees(s, lcodes, dcodes, blcodes)
//    deflate_state *s;
//    int lcodes, dcodes, blcodes; /* number of codes for each tree */
{
  var rank;                    /* index in bl_order */

  //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
  //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
  //        "too many codes");
  //Tracev((stderr, "\nbl counts: "));
  send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */
  send_bits(s, dcodes - 1,   5);
  send_bits(s, blcodes - 4,  4); /* not -3 as stated in appnote.txt */
  for (rank = 0; rank < blcodes; rank++) {
    //Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
    send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3);
  }
  //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));

  send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */
  //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));

  send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */
  //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
}


/* ===========================================================================
 * Check if the data type is TEXT or BINARY, using the following algorithm:
 * - TEXT if the two conditions below are satisfied:
 *    a) There are no non-portable control characters belonging to the
 *       "black list" (0..6, 14..25, 28..31).
 *    b) There is at least one printable character belonging to the
 *       "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
 * - BINARY otherwise.
 * - The following partially-portable control characters form a
 *   "gray list" that is ignored in this detection algorithm:
 *   (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
 * IN assertion: the fields Freq of dyn_ltree are set.
 */
function detect_data_type(s) {
  /* black_mask is the bit mask of black-listed bytes
   * set bits 0..6, 14..25, and 28..31
   * 0xf3ffc07f = binary 11110011111111111100000001111111
   */
  var black_mask = 0xf3ffc07f;
  var n;

  /* Check for non-textual ("black-listed") bytes. */
  for (n = 0; n <= 31; n++, black_mask >>>= 1) {
    if ((black_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) {
      return Z_BINARY;
    }
  }

  /* Check for textual ("white-listed") bytes. */
  if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 ||
      s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) {
    return Z_TEXT;
  }
  for (n = 32; n < LITERALS; n++) {
    if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) {
      return Z_TEXT;
    }
  }

  /* There are no "black-listed" or "white-listed" bytes:
   * this stream either is empty or has tolerated ("gray-listed") bytes only.
   */
  return Z_BINARY;
}


var static_init_done = false;

/* ===========================================================================
 * Initialize the tree data structures for a new zlib stream.
 */
function _tr_init(s)
{

  if (!static_init_done) {
    tr_static_init();
    static_init_done = true;
  }

  s.l_desc  = new TreeDesc(s.dyn_ltree, static_l_desc);
  s.d_desc  = new TreeDesc(s.dyn_dtree, static_d_desc);
  s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc);

  s.bi_buf = 0;
  s.bi_valid = 0;

  /* Initialize the first block of the first file: */
  init_block(s);
}


/* ===========================================================================
 * Send a stored block
 */
function _tr_stored_block(s, buf, stored_len, last)
//DeflateState *s;
//charf *buf;       /* input block */
//ulg stored_len;   /* length of input block */
//int last;         /* one if this is the last block for a file */
{
  send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3);    /* send block type */
  copy_block(s, buf, stored_len, true); /* with header */
}


/* ===========================================================================
 * Send one empty static block to give enough lookahead for inflate.
 * This takes 10 bits, of which 7 may remain in the bit buffer.
 */
function _tr_align(s) {
  send_bits(s, STATIC_TREES << 1, 3);
  send_code(s, END_BLOCK, static_ltree);
  bi_flush(s);
}


/* ===========================================================================
 * Determine the best encoding for the current block: dynamic trees, static
 * trees or store, and output the encoded block to the zip file.
 */
function _tr_flush_block(s, buf, stored_len, last)
//DeflateState *s;
//charf *buf;       /* input block, or NULL if too old */
//ulg stored_len;   /* length of input block */
//int last;         /* one if this is the last block for a file */
{
  var opt_lenb, static_lenb;  /* opt_len and static_len in bytes */
  var max_blindex = 0;        /* index of last bit length code of non zero freq */

  /* Build the Huffman trees unless a stored block is forced */
  if (s.level > 0) {

    /* Check if the file is binary or text */
    if (s.strm.data_type === Z_UNKNOWN) {
      s.strm.data_type = detect_data_type(s);
    }

    /* Construct the literal and distance trees */
    build_tree(s, s.l_desc);
    // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
    //        s->static_len));

    build_tree(s, s.d_desc);
    // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
    //        s->static_len));
    /* At this point, opt_len and static_len are the total bit lengths of
     * the compressed block data, excluding the tree representations.
     */

    /* Build the bit length tree for the above two trees, and get the index
     * in bl_order of the last bit length code to send.
     */
    max_blindex = build_bl_tree(s);

    /* Determine the best encoding. Compute the block lengths in bytes. */
    opt_lenb = (s.opt_len + 3 + 7) >>> 3;
    static_lenb = (s.static_len + 3 + 7) >>> 3;

    // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
    //        opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
    //        s->last_lit));

    if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; }

  } else {
    // Assert(buf != (char*)0, "lost buf");
    opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
  }

  if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) {
    /* 4: two words for the lengths */

    /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
     * Otherwise we can't have processed more than WSIZE input bytes since
     * the last block flush, because compression would have been
     * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
     * transform a block into a stored block.
     */
    _tr_stored_block(s, buf, stored_len, last);

  } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) {

    send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3);
    compress_block(s, static_ltree, static_dtree);

  } else {
    send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3);
    send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1);
    compress_block(s, s.dyn_ltree, s.dyn_dtree);
  }
  // Assert (s->compressed_len == s->bits_sent, "bad compressed size");
  /* The above check is made mod 2^32, for files larger than 512 MB
   * and uLong implemented on 32 bits.
   */
  init_block(s);

  if (last) {
    bi_windup(s);
  }
  // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
  //       s->compressed_len-7*last));
}

/* ===========================================================================
 * Save the match info and tally the frequency counts. Return true if
 * the current block must be flushed.
 */
function _tr_tally(s, dist, lc)
//    deflate_state *s;
//    unsigned dist;  /* distance of matched string */
//    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */
{
  //var out_length, in_length, dcode;

  s.pending_buf[s.d_buf + s.last_lit * 2]     = (dist >>> 8) & 0xff;
  s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff;

  s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff;
  s.last_lit++;

  if (dist === 0) {
    /* lc is the unmatched char */
    s.dyn_ltree[lc * 2]/*.Freq*/++;
  } else {
    s.matches++;
    /* Here, lc is the match length - MIN_MATCH */
    dist--;             /* dist = match distance - 1 */
    //Assert((ush)dist < (ush)MAX_DIST(s) &&
    //       (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
    //       (ush)d_code(dist) < (ush)D_CODES,  "_tr_tally: bad match");

    s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++;
    s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++;
  }

// (!) This block is disabled in zlib defailts,
// don't enable it for binary compatibility

//#ifdef TRUNCATE_BLOCK
//  /* Try to guess if it is profitable to stop the current block here */
//  if ((s.last_lit & 0x1fff) === 0 && s.level > 2) {
//    /* Compute an upper bound for the compressed length */
//    out_length = s.last_lit*8;
//    in_length = s.strstart - s.block_start;
//
//    for (dcode = 0; dcode < D_CODES; dcode++) {
//      out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]);
//    }
//    out_length >>>= 3;
//    //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
//    //       s->last_lit, in_length, out_length,
//    //       100L - out_length*100L/in_length));
//    if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) {
//      return true;
//    }
//  }
//#endif

  return (s.last_lit === s.lit_bufsize - 1);
  /* We avoid equality with lit_bufsize because of wraparound at 64K
   * on 16 bit machines and because stored blocks are restricted to
   * 64K-1 bytes.
   */
}

exports._tr_init  = _tr_init;
exports._tr_stored_block = _tr_stored_block;
exports._tr_flush_block  = _tr_flush_block;
exports._tr_tally = _tr_tally;
exports._tr_align = _tr_align;

},{"../utils/common":23}],30:[function(require,module,exports){
'use strict';


function ZStream() {
  /* next input byte */
  this.input = null; // JS specific, because we have no pointers
  this.next_in = 0;
  /* number of bytes available at input */
  this.avail_in = 0;
  /* total number of input bytes read so far */
  this.total_in = 0;
  /* next output byte should be put there */
  this.output = null; // JS specific, because we have no pointers
  this.next_out = 0;
  /* remaining free space at output */
  this.avail_out = 0;
  /* total number of bytes output so far */
  this.total_out = 0;
  /* last error message, NULL if no error */
  this.msg = ''/*Z_NULL*/;
  /* not visible by applications */
  this.state = null;
  /* best guess about the data type: binary or text */
  this.data_type = 2/*Z_UNKNOWN*/;
  /* adler32 value of the uncompressed data */
  this.adler = 0;
}

module.exports = ZStream;

},{}],31:[function(require,module,exports){
module.exports = require('react/lib/ReactCSSTransitionGroup');
},{"react/lib/ReactCSSTransitionGroup":66}],32:[function(require,module,exports){
module.exports = require('react/lib/ReactComponentWithPureRenderMixin');
},{"react/lib/ReactComponentWithPureRenderMixin":76}],33:[function(require,module,exports){
'use strict';

exports.__esModule = true;
exports['default'] = deepForceUpdate;
function traverseRenderedChildren(internalInstance, callback, argument) {
  callback(internalInstance, argument);

  if (internalInstance._renderedComponent) {
    traverseRenderedChildren(internalInstance._renderedComponent, callback, argument);
  } else {
    for (var key in internalInstance._renderedChildren) {
      if (internalInstance._renderedChildren.hasOwnProperty(key)) {
        traverseRenderedChildren(internalInstance._renderedChildren[key], callback, argument);
      }
    }
  }
}

function setPendingForceUpdate(internalInstance) {
  if (internalInstance._pendingForceUpdate === false) {
    internalInstance._pendingForceUpdate = true;
  }
}

function forceUpdateIfPending(internalInstance) {
  if (internalInstance._pendingForceUpdate === true) {
    var publicInstance = internalInstance._instance;
    var updater = publicInstance.updater;

    if (typeof publicInstance.forceUpdate === 'function') {
      publicInstance.forceUpdate();
    } else if (updater && typeof updater.enqueueForceUpdate === 'function') {
      updater.enqueueForceUpdate(publicInstance);
    }
  }
}

function deepForceUpdate(instance) {
  var internalInstance = instance._reactInternalInstance;
  traverseRenderedChildren(internalInstance, setPendingForceUpdate);
  traverseRenderedChildren(internalInstance, forceUpdateIfPending);
}

module.exports = exports['default'];
},{}],34:[function(require,module,exports){
'use strict';

module.exports = require('react/lib/ReactDOM');

},{"react/lib/ReactDOM":79}],35:[function(require,module,exports){
/**
 * Copyright 2013-2014 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @providesModule TapEventPlugin
 * @typechecks static-only
 */

"use strict";

var EventConstants = require('react/lib/EventConstants');
var EventPluginUtils = require('react/lib/EventPluginUtils');
var EventPropagators = require('react/lib/EventPropagators');
var SyntheticUIEvent = require('react/lib/SyntheticUIEvent');
var TouchEventUtils = require('./TouchEventUtils');
var ViewportMetrics = require('react/lib/ViewportMetrics');

var keyOf = require('fbjs/lib/keyOf');
var topLevelTypes = EventConstants.topLevelTypes;

var isStartish = EventPluginUtils.isStartish;
var isEndish = EventPluginUtils.isEndish;

var isTouch = function(topLevelType) {
  var touchTypes = [
    topLevelTypes.topTouchCancel,
    topLevelTypes.topTouchEnd,
    topLevelTypes.topTouchStart,
    topLevelTypes.topTouchMove
  ];
  return touchTypes.indexOf(topLevelType) >= 0;
}

/**
 * Number of pixels that are tolerated in between a `touchStart` and `touchEnd`
 * in order to still be considered a 'tap' event.
 */
var tapMoveThreshold = 10;
var ignoreMouseThreshold = 750;
var startCoords = {x: null, y: null};
var lastTouchEvent = null;

var Axis = {
  x: {page: 'pageX', client: 'clientX', envScroll: 'currentPageScrollLeft'},
  y: {page: 'pageY', client: 'clientY', envScroll: 'currentPageScrollTop'}
};

function getAxisCoordOfEvent(axis, nativeEvent) {
  var singleTouch = TouchEventUtils.extractSingleTouch(nativeEvent);
  if (singleTouch) {
    return singleTouch[axis.page];
  }
  return axis.page in nativeEvent ?
    nativeEvent[axis.page] :
    nativeEvent[axis.client] + ViewportMetrics[axis.envScroll];
}

function getDistance(coords, nativeEvent) {
  var pageX = getAxisCoordOfEvent(Axis.x, nativeEvent);
  var pageY = getAxisCoordOfEvent(Axis.y, nativeEvent);
  return Math.pow(
    Math.pow(pageX - coords.x, 2) + Math.pow(pageY - coords.y, 2),
    0.5
  );
}

var touchEvents = [
  topLevelTypes.topTouchStart,
  topLevelTypes.topTouchCancel,
  topLevelTypes.topTouchEnd,
  topLevelTypes.topTouchMove,
];

var dependencies = [
  topLevelTypes.topMouseDown,
  topLevelTypes.topMouseMove,
  topLevelTypes.topMouseUp,
].concat(touchEvents);

var eventTypes = {
  touchTap: {
    phasedRegistrationNames: {
      bubbled: keyOf({onTouchTap: null}),
      captured: keyOf({onTouchTapCapture: null})
    },
    dependencies: dependencies
  }
};

var now = (function() {
  if (Date.now) {
    return Date.now;
  } else {
    // IE8 support: http://stackoverflow.com/questions/9430357/please-explain-why-and-how-new-date-works-as-workaround-for-date-now-in
    return function () {
      return +new Date;
    }
  }
})();

function createTapEventPlugin(shouldRejectClick) {
  return {

    tapMoveThreshold: tapMoveThreshold,

    ignoreMouseThreshold: ignoreMouseThreshold,

    eventTypes: eventTypes,

    /**
     * @param {string} topLevelType Record from `EventConstants`.
     * @param {DOMEventTarget} targetInst The listening component root node.
     * @param {object} nativeEvent Native browser event.
     * @return {*} An accumulation of synthetic events.
     * @see {EventPluginHub.extractEvents}
     */
    extractEvents: function(
      topLevelType,
      targetInst,
      nativeEvent,
      nativeEventTarget
    ) {

      if (isTouch(topLevelType)) {
        lastTouchEvent = now();
      } else {
        if (shouldRejectClick(lastTouchEvent, now())) {
          return null;
        }
      }

      if (!isStartish(topLevelType) && !isEndish(topLevelType)) {
        return null;
      }
      var event = null;
      var distance = getDistance(startCoords, nativeEvent);
      if (isEndish(topLevelType) && distance < tapMoveThreshold) {
        event = SyntheticUIEvent.getPooled(
          eventTypes.touchTap,
          targetInst,
          nativeEvent,
          nativeEventTarget
        );
      }
      if (isStartish(topLevelType)) {
        startCoords.x = getAxisCoordOfEvent(Axis.x, nativeEvent);
        startCoords.y = getAxisCoordOfEvent(Axis.y, nativeEvent);
      } else if (isEndish(topLevelType)) {
        startCoords.x = 0;
        startCoords.y = 0;
      }
      EventPropagators.accumulateTwoPhaseDispatches(event);
      return event;
    }

  };
}

module.exports = createTapEventPlugin;

},{"./TouchEventUtils":36,"fbjs/lib/keyOf":15,"react/lib/EventConstants":54,"react/lib/EventPluginUtils":57,"react/lib/EventPropagators":58,"react/lib/SyntheticUIEvent":152,"react/lib/ViewportMetrics":155}],36:[function(require,module,exports){
/**
 * Copyright 2013-2014 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @providesModule TouchEventUtils
 */

var TouchEventUtils = {
  /**
   * Utility function for common case of extracting out the primary touch from a
   * touch event.
   * - `touchEnd` events usually do not have the `touches` property.
   *   http://stackoverflow.com/questions/3666929/
   *   mobile-sarai-touchend-event-not-firing-when-last-touch-is-removed
   *
   * @param {Event} nativeEvent Native event that may or may not be a touch.
   * @return {TouchesObject?} an object with pageX and pageY or null.
   */
  extractSingleTouch: function(nativeEvent) {
    var touches = nativeEvent.touches;
    var changedTouches = nativeEvent.changedTouches;
    var hasTouches = touches && touches.length > 0;
    var hasChangedTouches = changedTouches && changedTouches.length > 0;

    return !hasTouches && hasChangedTouches ? changedTouches[0] :
           hasTouches ? touches[0] :
           nativeEvent;
  }
};

module.exports = TouchEventUtils;

},{}],37:[function(require,module,exports){
module.exports = function(lastTouchEvent, clickTimestamp) {
  if (lastTouchEvent && (clickTimestamp - lastTouchEvent) < 750) {
    return true;
  }
};

},{}],38:[function(require,module,exports){
(function (process){
var invariant = require('fbjs/lib/invariant');
var defaultClickRejectionStrategy = require('./defaultClickRejectionStrategy');

var alreadyInjected = false;

module.exports = function injectTapEventPlugin (strategyOverrides) {
  strategyOverrides = strategyOverrides || {}
  var shouldRejectClick = strategyOverrides.shouldRejectClick || defaultClickRejectionStrategy;

  if (process.env.NODE_ENV !== 'production') {
    invariant(
      !alreadyInjected,
      'injectTapEventPlugin(): Can only be called once per application lifecycle.\n\n\
It is recommended to call injectTapEventPlugin() just before you call \
ReactDOM.render(). If you are using an external library which calls injectTapEventPlugin() \
itself, please contact the maintainer as it shouldn\'t be called in library code and \
should be injected by the application.'
    )
  }

  alreadyInjected = true;

  require('react/lib/EventPluginHub').injection.injectEventPluginsByName({
    'TapEventPlugin':       require('./TapEventPlugin.js')(shouldRejectClick)
  });
};

}).call(this,require('_process'))
},{"./TapEventPlugin.js":35,"./defaultClickRejectionStrategy":37,"_process":18,"fbjs/lib/invariant":14,"react/lib/EventPluginHub":55}],39:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule AutoFocusUtils
 */

'use strict';

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

var focusNode = require('fbjs/lib/focusNode');

var AutoFocusUtils = {
  focusDOMComponent: function () {
    focusNode(ReactDOMComponentTree.getNodeFromInstance(this));
  }
};

module.exports = AutoFocusUtils;
},{"./ReactDOMComponentTree":83,"fbjs/lib/focusNode":198}],40:[function(require,module,exports){
/**
 * Copyright 2013-present Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule BeforeInputEventPlugin
 */

'use strict';

var EventConstants = require('./EventConstants');
var EventPropagators = require('./EventPropagators');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
var FallbackCompositionState = require('./FallbackCompositionState');
var SyntheticCompositionEvent = require('./SyntheticCompositionEvent');
var SyntheticInputEvent = require('./SyntheticInputEvent');

var keyOf = require('fbjs/lib/keyOf');

var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
var START_KEYCODE = 229;

var canUseCompositionEvent = ExecutionEnvironment.canUseDOM && 'CompositionEvent' in window;

var documentMode = null;
if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) {
  documentMode = document.documentMode;
}

// Webkit offers a very useful `textInput` event that can be used to
// directly represent `beforeInput`. The IE `textinput` event is not as
// useful, so we don't use it.
var canUseTextInputEvent = ExecutionEnvironment.canUseDOM && 'TextEvent' in window && !documentMode && !isPresto();

// In IE9+, we have access to composition events, but the data supplied
// by the native compositionend event may be incorrect. Japanese ideographic
// spaces, for instance (\u3000) are not recorded correctly.
var useFallbackCompositionData = ExecutionEnvironment.canUseDOM && (!canUseCompositionEvent || documentMode && documentMode > 8 && documentMode <= 11);

/**
 * Opera <= 12 includes TextEvent in window, but does not fire
 * text input events. Rely on keypress instead.
 */
function isPresto() {
  var opera = window.opera;
  return typeof opera === 'object' && typeof opera.version === 'function' && parseInt(opera.version(), 10) <= 12;
}

var SPACEBAR_CODE = 32;
var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);

var topLevelTypes = EventConstants.topLevelTypes;

// Events and their corresponding property names.
var eventTypes = {
  beforeInput: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onBeforeInput: null }),
      captured: keyOf({ onBeforeInputCapture: null })
    },
    dependencies: [topLevelTypes.topCompositionEnd, topLevelTypes.topKeyPress, topLevelTypes.topTextInput, topLevelTypes.topPaste]
  },
  compositionEnd: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onCompositionEnd: null }),
      captured: keyOf({ onCompositionEndCapture: null })
    },
    dependencies: [topLevelTypes.topBlur, topLevelTypes.topCompositionEnd, topLevelTypes.topKeyDown, topLevelTypes.topKeyPress, topLevelTypes.topKeyUp, topLevelTypes.topMouseDown]
  },
  compositionStart: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onCompositionStart: null }),
      captured: keyOf({ onCompositionStartCapture: null })
    },
    dependencies: [topLevelTypes.topBlur, topLevelTypes.topCompositionStart, topLevelTypes.topKeyDown, topLevelTypes.topKeyPress, topLevelTypes.topKeyUp, topLevelTypes.topMouseDown]
  },
  compositionUpdate: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onCompositionUpdate: null }),
      captured: keyOf({ onCompositionUpdateCapture: null })
    },
    dependencies: [topLevelTypes.topBlur, topLevelTypes.topCompositionUpdate, topLevelTypes.topKeyDown, topLevelTypes.topKeyPress, topLevelTypes.topKeyUp, topLevelTypes.topMouseDown]
  }
};

// Track whether we've ever handled a keypress on the space key.
var hasSpaceKeypress = false;

/**
 * Return whether a native keypress event is assumed to be a command.
 * This is required because Firefox fires `keypress` events for key commands
 * (cut, copy, select-all, etc.) even though no character is inserted.
 */
function isKeypressCommand(nativeEvent) {
  return (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) &&
  // ctrlKey && altKey is equivalent to AltGr, and is not a command.
  !(nativeEvent.ctrlKey && nativeEvent.altKey);
}

/**
 * Translate native top level events into event types.
 *
 * @param {string} topLevelType
 * @return {object}
 */
function getCompositionEventType(topLevelType) {
  switch (topLevelType) {
    case topLevelTypes.topCompositionStart:
      return eventTypes.compositionStart;
    case topLevelTypes.topCompositionEnd:
      return eventTypes.compositionEnd;
    case topLevelTypes.topCompositionUpdate:
      return eventTypes.compositionUpdate;
  }
}

/**
 * Does our fallback best-guess model think this event signifies that
 * composition has begun?
 *
 * @param {string} topLevelType
 * @param {object} nativeEvent
 * @return {boolean}
 */
function isFallbackCompositionStart(topLevelType, nativeEvent) {
  return topLevelType === topLevelTypes.topKeyDown && nativeEvent.keyCode === START_KEYCODE;
}

/**
 * Does our fallback mode think that this event is the end of composition?
 *
 * @param {string} topLevelType
 * @param {object} nativeEvent
 * @return {boolean}
 */
function isFallbackCompositionEnd(topLevelType, nativeEvent) {
  switch (topLevelType) {
    case topLevelTypes.topKeyUp:
      // Command keys insert or clear IME input.
      return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
    case topLevelTypes.topKeyDown:
      // Expect IME keyCode on each keydown. If we get any other
      // code we must have exited earlier.
      return nativeEvent.keyCode !== START_KEYCODE;
    case topLevelTypes.topKeyPress:
    case topLevelTypes.topMouseDown:
    case topLevelTypes.topBlur:
      // Events are not possible without cancelling IME.
      return true;
    default:
      return false;
  }
}

/**
 * Google Input Tools provides composition data via a CustomEvent,
 * with the `data` property populated in the `detail` object. If this
 * is available on the event object, use it. If not, this is a plain
 * composition event and we have nothing special to extract.
 *
 * @param {object} nativeEvent
 * @return {?string}
 */
function getDataFromCustomEvent(nativeEvent) {
  var detail = nativeEvent.detail;
  if (typeof detail === 'object' && 'data' in detail) {
    return detail.data;
  }
  return null;
}

// Track the current IME composition fallback object, if any.
var currentComposition = null;

/**
 * @return {?object} A SyntheticCompositionEvent.
 */
function extractCompositionEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
  var eventType;
  var fallbackData;

  if (canUseCompositionEvent) {
    eventType = getCompositionEventType(topLevelType);
  } else if (!currentComposition) {
    if (isFallbackCompositionStart(topLevelType, nativeEvent)) {
      eventType = eventTypes.compositionStart;
    }
  } else if (isFallbackCompositionEnd(topLevelType, nativeEvent)) {
    eventType = eventTypes.compositionEnd;
  }

  if (!eventType) {
    return null;
  }

  if (useFallbackCompositionData) {
    // The current composition is stored statically and must not be
    // overwritten while composition continues.
    if (!currentComposition && eventType === eventTypes.compositionStart) {
      currentComposition = FallbackCompositionState.getPooled(nativeEventTarget);
    } else if (eventType === eventTypes.compositionEnd) {
      if (currentComposition) {
        fallbackData = currentComposition.getData();
      }
    }
  }

  var event = SyntheticCompositionEvent.getPooled(eventType, targetInst, nativeEvent, nativeEventTarget);

  if (fallbackData) {
    // Inject data generated from fallback path into the synthetic event.
    // This matches the property of native CompositionEventInterface.
    event.data = fallbackData;
  } else {
    var customData = getDataFromCustomEvent(nativeEvent);
    if (customData !== null) {
      event.data = customData;
    }
  }

  EventPropagators.accumulateTwoPhaseDispatches(event);
  return event;
}

/**
 * @param {string} topLevelType Record from `EventConstants`.
 * @param {object} nativeEvent Native browser event.
 * @return {?string} The string corresponding to this `beforeInput` event.
 */
function getNativeBeforeInputChars(topLevelType, nativeEvent) {
  switch (topLevelType) {
    case topLevelTypes.topCompositionEnd:
      return getDataFromCustomEvent(nativeEvent);
    case topLevelTypes.topKeyPress:
      /**
       * If native `textInput` events are available, our goal is to make
       * use of them. However, there is a special case: the spacebar key.
       * In Webkit, preventing default on a spacebar `textInput` event
       * cancels character insertion, but it *also* causes the browser
       * to fall back to its default spacebar behavior of scrolling the
       * page.
       *
       * Tracking at:
       * https://code.google.com/p/chromium/issues/detail?id=355103
       *
       * To avoid this issue, use the keypress event as if no `textInput`
       * event is available.
       */
      var which = nativeEvent.which;
      if (which !== SPACEBAR_CODE) {
        return null;
      }

      hasSpaceKeypress = true;
      return SPACEBAR_CHAR;

    case topLevelTypes.topTextInput:
      // Record the characters to be added to the DOM.
      var chars = nativeEvent.data;

      // If it's a spacebar character, assume that we have already handled
      // it at the keypress level and bail immediately. Android Chrome
      // doesn't give us keycodes, so we need to blacklist it.
      if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
        return null;
      }

      return chars;

    default:
      // For other native event types, do nothing.
      return null;
  }
}

/**
 * For browsers that do not provide the `textInput` event, extract the
 * appropriate string to use for SyntheticInputEvent.
 *
 * @param {string} topLevelType Record from `EventConstants`.
 * @param {object} nativeEvent Native browser event.
 * @return {?string} The fallback string for this `beforeInput` event.
 */
function getFallbackBeforeInputChars(topLevelType, nativeEvent) {
  // If we are currently composing (IME) and using a fallback to do so,
  // try to extract the composed characters from the fallback object.
  // If composition event is available, we extract a string only at
  // compositionevent, otherwise extract it at fallback events.
  if (currentComposition) {
    if (topLevelType === topLevelTypes.topCompositionEnd || !canUseCompositionEvent && isFallbackCompositionEnd(topLevelType, nativeEvent)) {
      var chars = currentComposition.getData();
      FallbackCompositionState.release(currentComposition);
      currentComposition = null;
      return chars;
    }
    return null;
  }

  switch (topLevelType) {
    case topLevelTypes.topPaste:
      // If a paste event occurs after a keypress, throw out the input
      // chars. Paste events should not lead to BeforeInput events.
      return null;
    case topLevelTypes.topKeyPress:
      /**
       * As of v27, Firefox may fire keypress events even when no character
       * will be inserted. A few possibilities:
       *
       * - `which` is `0`. Arrow keys, Esc key, etc.
       *
       * - `which` is the pressed key code, but no char is available.
       *   Ex: 'AltGr + d` in Polish. There is no modified character for
       *   this key combination and no character is inserted into the
       *   document, but FF fires the keypress for char code `100` anyway.
       *   No `input` event will occur.
       *
       * - `which` is the pressed key code, but a command combination is
       *   being used. Ex: `Cmd+C`. No character is inserted, and no
       *   `input` event will occur.
       */
      if (nativeEvent.which && !isKeypressCommand(nativeEvent)) {
        return String.fromCharCode(nativeEvent.which);
      }
      return null;
    case topLevelTypes.topCompositionEnd:
      return useFallbackCompositionData ? null : nativeEvent.data;
    default:
      return null;
  }
}

/**
 * Extract a SyntheticInputEvent for `beforeInput`, based on either native
 * `textInput` or fallback behavior.
 *
 * @return {?object} A SyntheticInputEvent.
 */
function extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
  var chars;

  if (canUseTextInputEvent) {
    chars = getNativeBeforeInputChars(topLevelType, nativeEvent);
  } else {
    chars = getFallbackBeforeInputChars(topLevelType, nativeEvent);
  }

  // If no characters are being inserted, no BeforeInput event should
  // be fired.
  if (!chars) {
    return null;
  }

  var event = SyntheticInputEvent.getPooled(eventTypes.beforeInput, targetInst, nativeEvent, nativeEventTarget);

  event.data = chars;
  EventPropagators.accumulateTwoPhaseDispatches(event);
  return event;
}

/**
 * Create an `onBeforeInput` event to match
 * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
 *
 * This event plugin is based on the native `textInput` event
 * available in Chrome, Safari, Opera, and IE. This event fires after
 * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
 *
 * `beforeInput` is spec'd but not implemented in any browsers, and
 * the `input` event does not provide any useful information about what has
 * actually been added, contrary to the spec. Thus, `textInput` is the best
 * available event to identify the characters that have actually been inserted
 * into the target node.
 *
 * This plugin is also responsible for emitting `composition` events, thus
 * allowing us to share composition fallback code for both `beforeInput` and
 * `composition` event types.
 */
var BeforeInputEventPlugin = {

  eventTypes: eventTypes,

  extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
    return [extractCompositionEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget), extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget)];
  }
};

module.exports = BeforeInputEventPlugin;
},{"./EventConstants":54,"./EventPropagators":58,"./FallbackCompositionState":59,"./SyntheticCompositionEvent":143,"./SyntheticInputEvent":147,"fbjs/lib/ExecutionEnvironment":190,"fbjs/lib/keyOf":208}],41:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule CSSProperty
 */

'use strict';

/**
 * CSS properties which accept numbers but are not in units of "px".
 */

var isUnitlessNumber = {
  animationIterationCount: true,
  borderImageOutset: true,
  borderImageSlice: true,
  borderImageWidth: true,
  boxFlex: true,
  boxFlexGroup: true,
  boxOrdinalGroup: true,
  columnCount: true,
  flex: true,
  flexGrow: true,
  flexPositive: true,
  flexShrink: true,
  flexNegative: true,
  flexOrder: true,
  gridRow: true,
  gridColumn: true,
  fontWeight: true,
  lineClamp: true,
  lineHeight: true,
  opacity: true,
  order: true,
  orphans: true,
  tabSize: true,
  widows: true,
  zIndex: true,
  zoom: true,

  // SVG-related properties
  fillOpacity: true,
  floodOpacity: true,
  stopOpacity: true,
  strokeDasharray: true,
  strokeDashoffset: true,
  strokeMiterlimit: true,
  strokeOpacity: true,
  strokeWidth: true
};

/**
 * @param {string} prefix vendor-specific prefix, eg: Webkit
 * @param {string} key style name, eg: transitionDuration
 * @return {string} style name prefixed with `prefix`, properly camelCased, eg:
 * WebkitTransitionDuration
 */
function prefixKey(prefix, key) {
  return prefix + key.charAt(0).toUpperCase() + key.substring(1);
}

/**
 * Support style names that may come passed in prefixed by adding permutations
 * of vendor prefixes.
 */
var prefixes = ['Webkit', 'ms', 'Moz', 'O'];

// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
// infinite loop, because it iterates over the newly added props too.
Object.keys(isUnitlessNumber).forEach(function (prop) {
  prefixes.forEach(function (prefix) {
    isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
  });
});

/**
 * Most style properties can be unset by doing .style[prop] = '' but IE8
 * doesn't like doing that with shorthand properties so for the properties that
 * IE8 breaks on, which are listed here, we instead unset each of the
 * individual properties. See http://bugs.jquery.com/ticket/12385.
 * The 4-value 'clock' properties like margin, padding, border-width seem to
 * behave without any problems. Curiously, list-style works too without any
 * special prodding.
 */
var shorthandPropertyExpansions = {
  background: {
    backgroundAttachment: true,
    backgroundColor: true,
    backgroundImage: true,
    backgroundPositionX: true,
    backgroundPositionY: true,
    backgroundRepeat: true
  },
  backgroundPosition: {
    backgroundPositionX: true,
    backgroundPositionY: true
  },
  border: {
    borderWidth: true,
    borderStyle: true,
    borderColor: true
  },
  borderBottom: {
    borderBottomWidth: true,
    borderBottomStyle: true,
    borderBottomColor: true
  },
  borderLeft: {
    borderLeftWidth: true,
    borderLeftStyle: true,
    borderLeftColor: true
  },
  borderRight: {
    borderRightWidth: true,
    borderRightStyle: true,
    borderRightColor: true
  },
  borderTop: {
    borderTopWidth: true,
    borderTopStyle: true,
    borderTopColor: true
  },
  font: {
    fontStyle: true,
    fontVariant: true,
    fontWeight: true,
    fontSize: true,
    lineHeight: true,
    fontFamily: true
  },
  outline: {
    outlineWidth: true,
    outlineStyle: true,
    outlineColor: true
  }
};

var CSSProperty = {
  isUnitlessNumber: isUnitlessNumber,
  shorthandPropertyExpansions: shorthandPropertyExpansions
};

module.exports = CSSProperty;
},{}],42:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule CSSPropertyOperations
 */

'use strict';

var CSSProperty = require('./CSSProperty');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
var ReactInstrumentation = require('./ReactInstrumentation');

var camelizeStyleName = require('fbjs/lib/camelizeStyleName');
var dangerousStyleValue = require('./dangerousStyleValue');
var hyphenateStyleName = require('fbjs/lib/hyphenateStyleName');
var memoizeStringOnly = require('fbjs/lib/memoizeStringOnly');
var warning = require('fbjs/lib/warning');

var processStyleName = memoizeStringOnly(function (styleName) {
  return hyphenateStyleName(styleName);
});

var hasShorthandPropertyBug = false;
var styleFloatAccessor = 'cssFloat';
if (ExecutionEnvironment.canUseDOM) {
  var tempStyle = document.createElement('div').style;
  try {
    // IE8 throws "Invalid argument." if resetting shorthand style properties.
    tempStyle.font = '';
  } catch (e) {
    hasShorthandPropertyBug = true;
  }
  // IE8 only supports accessing cssFloat (standard) as styleFloat
  if (document.documentElement.style.cssFloat === undefined) {
    styleFloatAccessor = 'styleFloat';
  }
}

if ("production" !== 'production') {
  // 'msTransform' is correct, but the other prefixes should be capitalized
  var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/;

  // style values shouldn't contain a semicolon
  var badStyleValueWithSemicolonPattern = /;\s*$/;

  var warnedStyleNames = {};
  var warnedStyleValues = {};
  var warnedForNaNValue = false;

  var warnHyphenatedStyleName = function (name, owner) {
    if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
      return;
    }

    warnedStyleNames[name] = true;
    "production" !== 'production' ? warning(false, 'Unsupported style property %s. Did you mean %s?%s', name, camelizeStyleName(name), checkRenderMessage(owner)) : void 0;
  };

  var warnBadVendoredStyleName = function (name, owner) {
    if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
      return;
    }

    warnedStyleNames[name] = true;
    "production" !== 'production' ? warning(false, 'Unsupported vendor-prefixed style property %s. Did you mean %s?%s', name, name.charAt(0).toUpperCase() + name.slice(1), checkRenderMessage(owner)) : void 0;
  };

  var warnStyleValueWithSemicolon = function (name, value, owner) {
    if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) {
      return;
    }

    warnedStyleValues[value] = true;
    "production" !== 'production' ? warning(false, 'Style property values shouldn\'t contain a semicolon.%s ' + 'Try "%s: %s" instead.', checkRenderMessage(owner), name, value.replace(badStyleValueWithSemicolonPattern, '')) : void 0;
  };

  var warnStyleValueIsNaN = function (name, value, owner) {
    if (warnedForNaNValue) {
      return;
    }

    warnedForNaNValue = true;
    "production" !== 'production' ? warning(false, '`NaN` is an invalid value for the `%s` css style property.%s', name, checkRenderMessage(owner)) : void 0;
  };

  var checkRenderMessage = function (owner) {
    if (owner) {
      var name = owner.getName();
      if (name) {
        return ' Check the render method of `' + name + '`.';
      }
    }
    return '';
  };

  /**
   * @param {string} name
   * @param {*} value
   * @param {ReactDOMComponent} component
   */
  var warnValidStyle = function (name, value, component) {
    var owner;
    if (component) {
      owner = component._currentElement._owner;
    }
    if (name.indexOf('-') > -1) {
      warnHyphenatedStyleName(name, owner);
    } else if (badVendoredStyleNamePattern.test(name)) {
      warnBadVendoredStyleName(name, owner);
    } else if (badStyleValueWithSemicolonPattern.test(value)) {
      warnStyleValueWithSemicolon(name, value, owner);
    }

    if (typeof value === 'number' && isNaN(value)) {
      warnStyleValueIsNaN(name, value, owner);
    }
  };
}

/**
 * Operations for dealing with CSS properties.
 */
var CSSPropertyOperations = {

  /**
   * Serializes a mapping of style properties for use as inline styles:
   *
   *   > createMarkupForStyles({width: '200px', height: 0})
   *   "width:200px;height:0;"
   *
   * Undefined values are ignored so that declarative programming is easier.
   * The result should be HTML-escaped before insertion into the DOM.
   *
   * @param {object} styles
   * @param {ReactDOMComponent} component
   * @return {?string}
   */
  createMarkupForStyles: function (styles, component) {
    var serialized = '';
    for (var styleName in styles) {
      if (!styles.hasOwnProperty(styleName)) {
        continue;
      }
      var styleValue = styles[styleName];
      if ("production" !== 'production') {
        warnValidStyle(styleName, styleValue, component);
      }
      if (styleValue != null) {
        serialized += processStyleName(styleName) + ':';
        serialized += dangerousStyleValue(styleName, styleValue, component) + ';';
      }
    }
    return serialized || null;
  },

  /**
   * Sets the value for multiple styles on a node.  If a value is specified as
   * '' (empty string), the corresponding style property will be unset.
   *
   * @param {DOMElement} node
   * @param {object} styles
   * @param {ReactDOMComponent} component
   */
  setValueForStyles: function (node, styles, component) {
    if ("production" !== 'production') {
      ReactInstrumentation.debugTool.onHostOperation(component._debugID, 'update styles', styles);
    }

    var style = node.style;
    for (var styleName in styles) {
      if (!styles.hasOwnProperty(styleName)) {
        continue;
      }
      if ("production" !== 'production') {
        warnValidStyle(styleName, styles[styleName], component);
      }
      var styleValue = dangerousStyleValue(styleName, styles[styleName], component);
      if (styleName === 'float' || styleName === 'cssFloat') {
        styleName = styleFloatAccessor;
      }
      if (styleValue) {
        style[styleName] = styleValue;
      } else {
        var expansion = hasShorthandPropertyBug && CSSProperty.shorthandPropertyExpansions[styleName];
        if (expansion) {
          // Shorthand property that IE8 won't like unsetting, so unset each
          // component to placate it
          for (var individualStyleName in expansion) {
            style[individualStyleName] = '';
          }
        } else {
          style[styleName] = '';
        }
      }
    }
  }

};

module.exports = CSSPropertyOperations;
},{"./CSSProperty":41,"./ReactInstrumentation":113,"./dangerousStyleValue":161,"fbjs/lib/ExecutionEnvironment":190,"fbjs/lib/camelizeStyleName":192,"fbjs/lib/hyphenateStyleName":203,"fbjs/lib/memoizeStringOnly":209,"fbjs/lib/warning":213}],43:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule CallbackQueue
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant'),
    _assign = require('object-assign');

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

var invariant = require('fbjs/lib/invariant');

/**
 * A specialized pseudo-event module to help keep track of components waiting to
 * be notified when their DOM representations are available for use.
 *
 * This implements `PooledClass`, so you should never need to instantiate this.
 * Instead, use `CallbackQueue.getPooled()`.
 *
 * @class ReactMountReady
 * @implements PooledClass
 * @internal
 */
function CallbackQueue() {
  this._callbacks = null;
  this._contexts = null;
}

_assign(CallbackQueue.prototype, {

  /**
   * Enqueues a callback to be invoked when `notifyAll` is invoked.
   *
   * @param {function} callback Invoked when `notifyAll` is invoked.
   * @param {?object} context Context to call `callback` with.
   * @internal
   */
  enqueue: function (callback, context) {
    this._callbacks = this._callbacks || [];
    this._contexts = this._contexts || [];
    this._callbacks.push(callback);
    this._contexts.push(context);
  },

  /**
   * Invokes all enqueued callbacks and clears the queue. This is invoked after
   * the DOM representation of a component has been created or updated.
   *
   * @internal
   */
  notifyAll: function () {
    var callbacks = this._callbacks;
    var contexts = this._contexts;
    if (callbacks) {
      !(callbacks.length === contexts.length) ? "production" !== 'production' ? invariant(false, 'Mismatched list of contexts in callback queue') : _prodInvariant('24') : void 0;
      this._callbacks = null;
      this._contexts = null;
      for (var i = 0; i < callbacks.length; i++) {
        callbacks[i].call(contexts[i]);
      }
      callbacks.length = 0;
      contexts.length = 0;
    }
  },

  checkpoint: function () {
    return this._callbacks ? this._callbacks.length : 0;
  },

  rollback: function (len) {
    if (this._callbacks) {
      this._callbacks.length = len;
      this._contexts.length = len;
    }
  },

  /**
   * Resets the internal queue.
   *
   * @internal
   */
  reset: function () {
    this._callbacks = null;
    this._contexts = null;
  },

  /**
   * `PooledClass` looks for this.
   */
  destructor: function () {
    this.reset();
  }

});

PooledClass.addPoolingTo(CallbackQueue);

module.exports = CallbackQueue;
},{"./PooledClass":63,"./reactProdInvariant":180,"fbjs/lib/invariant":204,"object-assign":214}],44:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ChangeEventPlugin
 */

'use strict';

var EventConstants = require('./EventConstants');
var EventPluginHub = require('./EventPluginHub');
var EventPropagators = require('./EventPropagators');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactUpdates = require('./ReactUpdates');
var SyntheticEvent = require('./SyntheticEvent');

var getEventTarget = require('./getEventTarget');
var isEventSupported = require('./isEventSupported');
var isTextInputElement = require('./isTextInputElement');
var keyOf = require('fbjs/lib/keyOf');

var topLevelTypes = EventConstants.topLevelTypes;

var eventTypes = {
  change: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onChange: null }),
      captured: keyOf({ onChangeCapture: null })
    },
    dependencies: [topLevelTypes.topBlur, topLevelTypes.topChange, topLevelTypes.topClick, topLevelTypes.topFocus, topLevelTypes.topInput, topLevelTypes.topKeyDown, topLevelTypes.topKeyUp, topLevelTypes.topSelectionChange]
  }
};

/**
 * For IE shims
 */
var activeElement = null;
var activeElementInst = null;
var activeElementValue = null;
var activeElementValueProp = null;

/**
 * SECTION: handle `change` event
 */
function shouldUseChangeEvent(elem) {
  var nodeName = elem.nodeName && elem.nodeName.toLowerCase();
  return nodeName === 'select' || nodeName === 'input' && elem.type === 'file';
}

var doesChangeEventBubble = false;
if (ExecutionEnvironment.canUseDOM) {
  // See `handleChange` comment below
  doesChangeEventBubble = isEventSupported('change') && (!document.documentMode || document.documentMode > 8);
}

function manualDispatchChangeEvent(nativeEvent) {
  var event = SyntheticEvent.getPooled(eventTypes.change, activeElementInst, nativeEvent, getEventTarget(nativeEvent));
  EventPropagators.accumulateTwoPhaseDispatches(event);

  // If change and propertychange bubbled, we'd just bind to it like all the
  // other events and have it go through ReactBrowserEventEmitter. Since it
  // doesn't, we manually listen for the events and so we have to enqueue and
  // process the abstract event manually.
  //
  // Batching is necessary here in order to ensure that all event handlers run
  // before the next rerender (including event handlers attached to ancestor
  // elements instead of directly on the input). Without this, controlled
  // components don't work properly in conjunction with event bubbling because
  // the component is rerendered and the value reverted before all the event
  // handlers can run. See https://github.com/facebook/react/issues/708.
  ReactUpdates.batchedUpdates(runEventInBatch, event);
}

function runEventInBatch(event) {
  EventPluginHub.enqueueEvents(event);
  EventPluginHub.processEventQueue(false);
}

function startWatchingForChangeEventIE8(target, targetInst) {
  activeElement = target;
  activeElementInst = targetInst;
  activeElement.attachEvent('onchange', manualDispatchChangeEvent);
}

function stopWatchingForChangeEventIE8() {
  if (!activeElement) {
    return;
  }
  activeElement.detachEvent('onchange', manualDispatchChangeEvent);
  activeElement = null;
  activeElementInst = null;
}

function getTargetInstForChangeEvent(topLevelType, targetInst) {
  if (topLevelType === topLevelTypes.topChange) {
    return targetInst;
  }
}
function handleEventsForChangeEventIE8(topLevelType, target, targetInst) {
  if (topLevelType === topLevelTypes.topFocus) {
    // stopWatching() should be a noop here but we call it just in case we
    // missed a blur event somehow.
    stopWatchingForChangeEventIE8();
    startWatchingForChangeEventIE8(target, targetInst);
  } else if (topLevelType === topLevelTypes.topBlur) {
    stopWatchingForChangeEventIE8();
  }
}

/**
 * SECTION: handle `input` event
 */
var isInputEventSupported = false;
if (ExecutionEnvironment.canUseDOM) {
  // IE9 claims to support the input event but fails to trigger it when
  // deleting text, so we ignore its input events.
  // IE10+ fire input events to often, such when a placeholder
  // changes or when an input with a placeholder is focused.
  isInputEventSupported = isEventSupported('input') && (!document.documentMode || document.documentMode > 11);
}

/**
 * (For IE <=11) Replacement getter/setter for the `value` property that gets
 * set on the active element.
 */
var newValueProp = {
  get: function () {
    return activeElementValueProp.get.call(this);
  },
  set: function (val) {
    // Cast to a string so we can do equality checks.
    activeElementValue = '' + val;
    activeElementValueProp.set.call(this, val);
  }
};

/**
 * (For IE <=11) Starts tracking propertychange events on the passed-in element
 * and override the value property so that we can distinguish user events from
 * value changes in JS.
 */
function startWatchingForValueChange(target, targetInst) {
  activeElement = target;
  activeElementInst = targetInst;
  activeElementValue = target.value;
  activeElementValueProp = Object.getOwnPropertyDescriptor(target.constructor.prototype, 'value');

  // Not guarded in a canDefineProperty check: IE8 supports defineProperty only
  // on DOM elements
  Object.defineProperty(activeElement, 'value', newValueProp);
  if (activeElement.attachEvent) {
    activeElement.attachEvent('onpropertychange', handlePropertyChange);
  } else {
    activeElement.addEventListener('propertychange', handlePropertyChange, false);
  }
}

/**
 * (For IE <=11) Removes the event listeners from the currently-tracked element,
 * if any exists.
 */
function stopWatchingForValueChange() {
  if (!activeElement) {
    return;
  }

  // delete restores the original property definition
  delete activeElement.value;

  if (activeElement.detachEvent) {
    activeElement.detachEvent('onpropertychange', handlePropertyChange);
  } else {
    activeElement.removeEventListener('propertychange', handlePropertyChange, false);
  }

  activeElement = null;
  activeElementInst = null;
  activeElementValue = null;
  activeElementValueProp = null;
}

/**
 * (For IE <=11) Handles a propertychange event, sending a `change` event if
 * the value of the active element has changed.
 */
function handlePropertyChange(nativeEvent) {
  if (nativeEvent.propertyName !== 'value') {
    return;
  }
  var value = nativeEvent.srcElement.value;
  if (value === activeElementValue) {
    return;
  }
  activeElementValue = value;

  manualDispatchChangeEvent(nativeEvent);
}

/**
 * If a `change` event should be fired, returns the target's ID.
 */
function getTargetInstForInputEvent(topLevelType, targetInst) {
  if (topLevelType === topLevelTypes.topInput) {
    // In modern browsers (i.e., not IE8 or IE9), the input event is exactly
    // what we want so fall through here and trigger an abstract event
    return targetInst;
  }
}

function handleEventsForInputEventIE(topLevelType, target, targetInst) {
  if (topLevelType === topLevelTypes.topFocus) {
    // In IE8, we can capture almost all .value changes by adding a
    // propertychange handler and looking for events with propertyName
    // equal to 'value'
    // In IE9-11, propertychange fires for most input events but is buggy and
    // doesn't fire when text is deleted, but conveniently, selectionchange
    // appears to fire in all of the remaining cases so we catch those and
    // forward the event if the value has changed
    // In either case, we don't want to call the event handler if the value
    // is changed from JS so we redefine a setter for `.value` that updates
    // our activeElementValue variable, allowing us to ignore those changes
    //
    // stopWatching() should be a noop here but we call it just in case we
    // missed a blur event somehow.
    stopWatchingForValueChange();
    startWatchingForValueChange(target, targetInst);
  } else if (topLevelType === topLevelTypes.topBlur) {
    stopWatchingForValueChange();
  }
}

// For IE8 and IE9.
function getTargetInstForInputEventIE(topLevelType, targetInst) {
  if (topLevelType === topLevelTypes.topSelectionChange || topLevelType === topLevelTypes.topKeyUp || topLevelType === topLevelTypes.topKeyDown) {
    // On the selectionchange event, the target is just document which isn't
    // helpful for us so just check activeElement instead.
    //
    // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire
    // propertychange on the first input event after setting `value` from a
    // script and fires only keydown, keypress, keyup. Catching keyup usually
    // gets it and catching keydown lets us fire an event for the first
    // keystroke if user does a key repeat (it'll be a little delayed: right
    // before the second keystroke). Other input methods (e.g., paste) seem to
    // fire selectionchange normally.
    if (activeElement && activeElement.value !== activeElementValue) {
      activeElementValue = activeElement.value;
      return activeElementInst;
    }
  }
}

/**
 * SECTION: handle `click` event
 */
function shouldUseClickEvent(elem) {
  // Use the `click` event to detect changes to checkbox and radio inputs.
  // This approach works across all browsers, whereas `change` does not fire
  // until `blur` in IE8.
  return elem.nodeName && elem.nodeName.toLowerCase() === 'input' && (elem.type === 'checkbox' || elem.type === 'radio');
}

function getTargetInstForClickEvent(topLevelType, targetInst) {
  if (topLevelType === topLevelTypes.topClick) {
    return targetInst;
  }
}

/**
 * This plugin creates an `onChange` event that normalizes change events
 * across form elements. This event fires at a time when it's possible to
 * change the element's value without seeing a flicker.
 *
 * Supported elements are:
 * - input (see `isTextInputElement`)
 * - textarea
 * - select
 */
var ChangeEventPlugin = {

  eventTypes: eventTypes,

  extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
    var targetNode = targetInst ? ReactDOMComponentTree.getNodeFromInstance(targetInst) : window;

    var getTargetInstFunc, handleEventFunc;
    if (shouldUseChangeEvent(targetNode)) {
      if (doesChangeEventBubble) {
        getTargetInstFunc = getTargetInstForChangeEvent;
      } else {
        handleEventFunc = handleEventsForChangeEventIE8;
      }
    } else if (isTextInputElement(targetNode)) {
      if (isInputEventSupported) {
        getTargetInstFunc = getTargetInstForInputEvent;
      } else {
        getTargetInstFunc = getTargetInstForInputEventIE;
        handleEventFunc = handleEventsForInputEventIE;
      }
    } else if (shouldUseClickEvent(targetNode)) {
      getTargetInstFunc = getTargetInstForClickEvent;
    }

    if (getTargetInstFunc) {
      var inst = getTargetInstFunc(topLevelType, targetInst);
      if (inst) {
        var event = SyntheticEvent.getPooled(eventTypes.change, inst, nativeEvent, nativeEventTarget);
        event.type = 'change';
        EventPropagators.accumulateTwoPhaseDispatches(event);
        return event;
      }
    }

    if (handleEventFunc) {
      handleEventFunc(topLevelType, targetNode, targetInst);
    }
  }

};

module.exports = ChangeEventPlugin;
},{"./EventConstants":54,"./EventPluginHub":55,"./EventPropagators":58,"./ReactDOMComponentTree":83,"./ReactUpdates":136,"./SyntheticEvent":145,"./getEventTarget":169,"./isEventSupported":176,"./isTextInputElement":177,"fbjs/lib/ExecutionEnvironment":190,"fbjs/lib/keyOf":208}],45:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule DOMChildrenOperations
 */

'use strict';

var DOMLazyTree = require('./DOMLazyTree');
var Danger = require('./Danger');
var ReactMultiChildUpdateTypes = require('./ReactMultiChildUpdateTypes');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactInstrumentation = require('./ReactInstrumentation');

var createMicrosoftUnsafeLocalFunction = require('./createMicrosoftUnsafeLocalFunction');
var setInnerHTML = require('./setInnerHTML');
var setTextContent = require('./setTextContent');

function getNodeAfter(parentNode, node) {
  // Special case for text components, which return [open, close] comments
  // from getHostNode.
  if (Array.isArray(node)) {
    node = node[1];
  }
  return node ? node.nextSibling : parentNode.firstChild;
}

/**
 * Inserts `childNode` as a child of `parentNode` at the `index`.
 *
 * @param {DOMElement} parentNode Parent node in which to insert.
 * @param {DOMElement} childNode Child node to insert.
 * @param {number} index Index at which to insert the child.
 * @internal
 */
var insertChildAt = createMicrosoftUnsafeLocalFunction(function (parentNode, childNode, referenceNode) {
  // We rely exclusively on `insertBefore(node, null)` instead of also using
  // `appendChild(node)`. (Using `undefined` is not allowed by all browsers so
  // we are careful to use `null`.)
  parentNode.insertBefore(childNode, referenceNode);
});

function insertLazyTreeChildAt(parentNode, childTree, referenceNode) {
  DOMLazyTree.insertTreeBefore(parentNode, childTree, referenceNode);
}

function moveChild(parentNode, childNode, referenceNode) {
  if (Array.isArray(childNode)) {
    moveDelimitedText(parentNode, childNode[0], childNode[1], referenceNode);
  } else {
    insertChildAt(parentNode, childNode, referenceNode);
  }
}

function removeChild(parentNode, childNode) {
  if (Array.isArray(childNode)) {
    var closingComment = childNode[1];
    childNode = childNode[0];
    removeDelimitedText(parentNode, childNode, closingComment);
    parentNode.removeChild(closingComment);
  }
  parentNode.removeChild(childNode);
}

function moveDelimitedText(parentNode, openingComment, closingComment, referenceNode) {
  var node = openingComment;
  while (true) {
    var nextNode = node.nextSibling;
    insertChildAt(parentNode, node, referenceNode);
    if (node === closingComment) {
      break;
    }
    node = nextNode;
  }
}

function removeDelimitedText(parentNode, startNode, closingComment) {
  while (true) {
    var node = startNode.nextSibling;
    if (node === closingComment) {
      // The closing comment is removed by ReactMultiChild.
      break;
    } else {
      parentNode.removeChild(node);
    }
  }
}

function replaceDelimitedText(openingComment, closingComment, stringText) {
  var parentNode = openingComment.parentNode;
  var nodeAfterComment = openingComment.nextSibling;
  if (nodeAfterComment === closingComment) {
    // There are no text nodes between the opening and closing comments; insert
    // a new one if stringText isn't empty.
    if (stringText) {
      insertChildAt(parentNode, document.createTextNode(stringText), nodeAfterComment);
    }
  } else {
    if (stringText) {
      // Set the text content of the first node after the opening comment, and
      // remove all following nodes up until the closing comment.
      setTextContent(nodeAfterComment, stringText);
      removeDelimitedText(parentNode, nodeAfterComment, closingComment);
    } else {
      removeDelimitedText(parentNode, openingComment, closingComment);
    }
  }

  if ("production" !== 'production') {
    ReactInstrumentation.debugTool.onHostOperation(ReactDOMComponentTree.getInstanceFromNode(openingComment)._debugID, 'replace text', stringText);
  }
}

var dangerouslyReplaceNodeWithMarkup = Danger.dangerouslyReplaceNodeWithMarkup;
if ("production" !== 'production') {
  dangerouslyReplaceNodeWithMarkup = function (oldChild, markup, prevInstance) {
    Danger.dangerouslyReplaceNodeWithMarkup(oldChild, markup);
    if (prevInstance._debugID !== 0) {
      ReactInstrumentation.debugTool.onHostOperation(prevInstance._debugID, 'replace with', markup.toString());
    } else {
      var nextInstance = ReactDOMComponentTree.getInstanceFromNode(markup.node);
      if (nextInstance._debugID !== 0) {
        ReactInstrumentation.debugTool.onHostOperation(nextInstance._debugID, 'mount', markup.toString());
      }
    }
  };
}

/**
 * Operations for updating with DOM children.
 */
var DOMChildrenOperations = {

  dangerouslyReplaceNodeWithMarkup: dangerouslyReplaceNodeWithMarkup,

  replaceDelimitedText: replaceDelimitedText,

  /**
   * Updates a component's children by processing a series of updates. The
   * update configurations are each expected to have a `parentNode` property.
   *
   * @param {array<object>} updates List of update configurations.
   * @internal
   */
  processUpdates: function (parentNode, updates) {
    if ("production" !== 'production') {
      var parentNodeDebugID = ReactDOMComponentTree.getInstanceFromNode(parentNode)._debugID;
    }

    for (var k = 0; k < updates.length; k++) {
      var update = updates[k];
      switch (update.type) {
        case ReactMultiChildUpdateTypes.INSERT_MARKUP:
          insertLazyTreeChildAt(parentNode, update.content, getNodeAfter(parentNode, update.afterNode));
          if ("production" !== 'production') {
            ReactInstrumentation.debugTool.onHostOperation(parentNodeDebugID, 'insert child', { toIndex: update.toIndex, content: update.content.toString() });
          }
          break;
        case ReactMultiChildUpdateTypes.MOVE_EXISTING:
          moveChild(parentNode, update.fromNode, getNodeAfter(parentNode, update.afterNode));
          if ("production" !== 'production') {
            ReactInstrumentation.debugTool.onHostOperation(parentNodeDebugID, 'move child', { fromIndex: update.fromIndex, toIndex: update.toIndex });
          }
          break;
        case ReactMultiChildUpdateTypes.SET_MARKUP:
          setInnerHTML(parentNode, update.content);
          if ("production" !== 'production') {
            ReactInstrumentation.debugTool.onHostOperation(parentNodeDebugID, 'replace children', update.content.toString());
          }
          break;
        case ReactMultiChildUpdateTypes.TEXT_CONTENT:
          setTextContent(parentNode, update.content);
          if ("production" !== 'production') {
            ReactInstrumentation.debugTool.onHostOperation(parentNodeDebugID, 'replace text', update.content.toString());
          }
          break;
        case ReactMultiChildUpdateTypes.REMOVE_NODE:
          removeChild(parentNode, update.fromNode);
          if ("production" !== 'production') {
            ReactInstrumentation.debugTool.onHostOperation(parentNodeDebugID, 'remove child', { fromIndex: update.fromIndex });
          }
          break;
      }
    }
  }

};

module.exports = DOMChildrenOperations;
},{"./DOMLazyTree":46,"./Danger":50,"./ReactDOMComponentTree":83,"./ReactInstrumentation":113,"./ReactMultiChildUpdateTypes":118,"./createMicrosoftUnsafeLocalFunction":160,"./setInnerHTML":182,"./setTextContent":183}],46:[function(require,module,exports){
/**
 * Copyright 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule DOMLazyTree
 */

'use strict';

var DOMNamespaces = require('./DOMNamespaces');
var setInnerHTML = require('./setInnerHTML');

var createMicrosoftUnsafeLocalFunction = require('./createMicrosoftUnsafeLocalFunction');
var setTextContent = require('./setTextContent');

var ELEMENT_NODE_TYPE = 1;
var DOCUMENT_FRAGMENT_NODE_TYPE = 11;

/**
 * In IE (8-11) and Edge, appending nodes with no children is dramatically
 * faster than appending a full subtree, so we essentially queue up the
 * .appendChild calls here and apply them so each node is added to its parent
 * before any children are added.
 *
 * In other browsers, doing so is slower or neutral compared to the other order
 * (in Firefox, twice as slow) so we only do this inversion in IE.
 *
 * See https://github.com/spicyj/innerhtml-vs-createelement-vs-clonenode.
 */
var enableLazy = typeof document !== 'undefined' && typeof document.documentMode === 'number' || typeof navigator !== 'undefined' && typeof navigator.userAgent === 'string' && /\bEdge\/\d/.test(navigator.userAgent);

function insertTreeChildren(tree) {
  if (!enableLazy) {
    return;
  }
  var node = tree.node;
  var children = tree.children;
  if (children.length) {
    for (var i = 0; i < children.length; i++) {
      insertTreeBefore(node, children[i], null);
    }
  } else if (tree.html != null) {
    setInnerHTML(node, tree.html);
  } else if (tree.text != null) {
    setTextContent(node, tree.text);
  }
}

var insertTreeBefore = createMicrosoftUnsafeLocalFunction(function (parentNode, tree, referenceNode) {
  // DocumentFragments aren't actually part of the DOM after insertion so
  // appending children won't update the DOM. We need to ensure the fragment
  // is properly populated first, breaking out of our lazy approach for just
  // this level. Also, some <object> plugins (like Flash Player) will read
  // <param> nodes immediately upon insertion into the DOM, so <object>
  // must also be populated prior to insertion into the DOM.
  if (tree.node.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE || tree.node.nodeType === ELEMENT_NODE_TYPE && tree.node.nodeName.toLowerCase() === 'object' && (tree.node.namespaceURI == null || tree.node.namespaceURI === DOMNamespaces.html)) {
    insertTreeChildren(tree);
    parentNode.insertBefore(tree.node, referenceNode);
  } else {
    parentNode.insertBefore(tree.node, referenceNode);
    insertTreeChildren(tree);
  }
});

function replaceChildWithTree(oldNode, newTree) {
  oldNode.parentNode.replaceChild(newTree.node, oldNode);
  insertTreeChildren(newTree);
}

function queueChild(parentTree, childTree) {
  if (enableLazy) {
    parentTree.children.push(childTree);
  } else {
    parentTree.node.appendChild(childTree.node);
  }
}

function queueHTML(tree, html) {
  if (enableLazy) {
    tree.html = html;
  } else {
    setInnerHTML(tree.node, html);
  }
}

function queueText(tree, text) {
  if (enableLazy) {
    tree.text = text;
  } else {
    setTextContent(tree.node, text);
  }
}

function toString() {
  return this.node.nodeName;
}

function DOMLazyTree(node) {
  return {
    node: node,
    children: [],
    html: null,
    text: null,
    toString: toString
  };
}

DOMLazyTree.insertTreeBefore = insertTreeBefore;
DOMLazyTree.replaceChildWithTree = replaceChildWithTree;
DOMLazyTree.queueChild = queueChild;
DOMLazyTree.queueHTML = queueHTML;
DOMLazyTree.queueText = queueText;

module.exports = DOMLazyTree;
},{"./DOMNamespaces":47,"./createMicrosoftUnsafeLocalFunction":160,"./setInnerHTML":182,"./setTextContent":183}],47:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule DOMNamespaces
 */

'use strict';

var DOMNamespaces = {
  html: 'http://www.w3.org/1999/xhtml',
  mathml: 'http://www.w3.org/1998/Math/MathML',
  svg: 'http://www.w3.org/2000/svg'
};

module.exports = DOMNamespaces;
},{}],48:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule DOMProperty
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var invariant = require('fbjs/lib/invariant');

function checkMask(value, bitmask) {
  return (value & bitmask) === bitmask;
}

var DOMPropertyInjection = {
  /**
   * Mapping from normalized, camelcased property names to a configuration that
   * specifies how the associated DOM property should be accessed or rendered.
   */
  MUST_USE_PROPERTY: 0x1,
  HAS_BOOLEAN_VALUE: 0x4,
  HAS_NUMERIC_VALUE: 0x8,
  HAS_POSITIVE_NUMERIC_VALUE: 0x10 | 0x8,
  HAS_OVERLOADED_BOOLEAN_VALUE: 0x20,

  /**
   * Inject some specialized knowledge about the DOM. This takes a config object
   * with the following properties:
   *
   * isCustomAttribute: function that given an attribute name will return true
   * if it can be inserted into the DOM verbatim. Useful for data-* or aria-*
   * attributes where it's impossible to enumerate all of the possible
   * attribute names,
   *
   * Properties: object mapping DOM property name to one of the
   * DOMPropertyInjection constants or null. If your attribute isn't in here,
   * it won't get written to the DOM.
   *
   * DOMAttributeNames: object mapping React attribute name to the DOM
   * attribute name. Attribute names not specified use the **lowercase**
   * normalized name.
   *
   * DOMAttributeNamespaces: object mapping React attribute name to the DOM
   * attribute namespace URL. (Attribute names not specified use no namespace.)
   *
   * DOMPropertyNames: similar to DOMAttributeNames but for DOM properties.
   * Property names not specified use the normalized name.
   *
   * DOMMutationMethods: Properties that require special mutation methods. If
   * `value` is undefined, the mutation method should unset the property.
   *
   * @param {object} domPropertyConfig the config as described above.
   */
  injectDOMPropertyConfig: function (domPropertyConfig) {
    var Injection = DOMPropertyInjection;
    var Properties = domPropertyConfig.Properties || {};
    var DOMAttributeNamespaces = domPropertyConfig.DOMAttributeNamespaces || {};
    var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {};
    var DOMPropertyNames = domPropertyConfig.DOMPropertyNames || {};
    var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {};

    if (domPropertyConfig.isCustomAttribute) {
      DOMProperty._isCustomAttributeFunctions.push(domPropertyConfig.isCustomAttribute);
    }

    for (var propName in Properties) {
      !!DOMProperty.properties.hasOwnProperty(propName) ? "production" !== 'production' ? invariant(false, 'injectDOMPropertyConfig(...): You\'re trying to inject DOM property \'%s\' which has already been injected. You may be accidentally injecting the same DOM property config twice, or you may be injecting two configs that have conflicting property names.', propName) : _prodInvariant('48', propName) : void 0;

      var lowerCased = propName.toLowerCase();
      var propConfig = Properties[propName];

      var propertyInfo = {
        attributeName: lowerCased,
        attributeNamespace: null,
        propertyName: propName,
        mutationMethod: null,

        mustUseProperty: checkMask(propConfig, Injection.MUST_USE_PROPERTY),
        hasBooleanValue: checkMask(propConfig, Injection.HAS_BOOLEAN_VALUE),
        hasNumericValue: checkMask(propConfig, Injection.HAS_NUMERIC_VALUE),
        hasPositiveNumericValue: checkMask(propConfig, Injection.HAS_POSITIVE_NUMERIC_VALUE),
        hasOverloadedBooleanValue: checkMask(propConfig, Injection.HAS_OVERLOADED_BOOLEAN_VALUE)
      };
      !(propertyInfo.hasBooleanValue + propertyInfo.hasNumericValue + propertyInfo.hasOverloadedBooleanValue <= 1) ? "production" !== 'production' ? invariant(false, 'DOMProperty: Value can be one of boolean, overloaded boolean, or numeric value, but not a combination: %s', propName) : _prodInvariant('50', propName) : void 0;

      if ("production" !== 'production') {
        DOMProperty.getPossibleStandardName[lowerCased] = propName;
      }

      if (DOMAttributeNames.hasOwnProperty(propName)) {
        var attributeName = DOMAttributeNames[propName];
        propertyInfo.attributeName = attributeName;
        if ("production" !== 'production') {
          DOMProperty.getPossibleStandardName[attributeName] = propName;
        }
      }

      if (DOMAttributeNamespaces.hasOwnProperty(propName)) {
        propertyInfo.attributeNamespace = DOMAttributeNamespaces[propName];
      }

      if (DOMPropertyNames.hasOwnProperty(propName)) {
        propertyInfo.propertyName = DOMPropertyNames[propName];
      }

      if (DOMMutationMethods.hasOwnProperty(propName)) {
        propertyInfo.mutationMethod = DOMMutationMethods[propName];
      }

      DOMProperty.properties[propName] = propertyInfo;
    }
  }
};

/* eslint-disable max-len */
var ATTRIBUTE_NAME_START_CHAR = ':A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD';
/* eslint-enable max-len */

/**
 * DOMProperty exports lookup objects that can be used like functions:
 *
 *   > DOMProperty.isValid['id']
 *   true
 *   > DOMProperty.isValid['foobar']
 *   undefined
 *
 * Although this may be confusing, it performs better in general.
 *
 * @see http://jsperf.com/key-exists
 * @see http://jsperf.com/key-missing
 */
var DOMProperty = {

  ID_ATTRIBUTE_NAME: 'data-reactid',
  ROOT_ATTRIBUTE_NAME: 'data-reactroot',

  ATTRIBUTE_NAME_START_CHAR: ATTRIBUTE_NAME_START_CHAR,
  ATTRIBUTE_NAME_CHAR: ATTRIBUTE_NAME_START_CHAR + '\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040',

  /**
   * Map from property "standard name" to an object with info about how to set
   * the property in the DOM. Each object contains:
   *
   * attributeName:
   *   Used when rendering markup or with `*Attribute()`.
   * attributeNamespace
   * propertyName:
   *   Used on DOM node instances. (This includes properties that mutate due to
   *   external factors.)
   * mutationMethod:
   *   If non-null, used instead of the property or `setAttribute()` after
   *   initial render.
   * mustUseProperty:
   *   Whether the property must be accessed and mutated as an object property.
   * hasBooleanValue:
   *   Whether the property should be removed when set to a falsey value.
   * hasNumericValue:
   *   Whether the property must be numeric or parse as a numeric and should be
   *   removed when set to a falsey value.
   * hasPositiveNumericValue:
   *   Whether the property must be positive numeric or parse as a positive
   *   numeric and should be removed when set to a falsey value.
   * hasOverloadedBooleanValue:
   *   Whether the property can be used as a flag as well as with a value.
   *   Removed when strictly equal to false; present without a value when
   *   strictly equal to true; present with a value otherwise.
   */
  properties: {},

  /**
   * Mapping from lowercase property names to the properly cased version, used
   * to warn in the case of missing properties. Available only in __DEV__.
   * @type {Object}
   */
  getPossibleStandardName: "production" !== 'production' ? {} : null,

  /**
   * All of the isCustomAttribute() functions that have been injected.
   */
  _isCustomAttributeFunctions: [],

  /**
   * Checks whether a property name is a custom attribute.
   * @method
   */
  isCustomAttribute: function (attributeName) {
    for (var i = 0; i < DOMProperty._isCustomAttributeFunctions.length; i++) {
      var isCustomAttributeFn = DOMProperty._isCustomAttributeFunctions[i];
      if (isCustomAttributeFn(attributeName)) {
        return true;
      }
    }
    return false;
  },

  injection: DOMPropertyInjection
};

module.exports = DOMProperty;
},{"./reactProdInvariant":180,"fbjs/lib/invariant":204}],49:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule DOMPropertyOperations
 */

'use strict';

var DOMProperty = require('./DOMProperty');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactInstrumentation = require('./ReactInstrumentation');

var quoteAttributeValueForBrowser = require('./quoteAttributeValueForBrowser');
var warning = require('fbjs/lib/warning');

var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + DOMProperty.ATTRIBUTE_NAME_START_CHAR + '][' + DOMProperty.ATTRIBUTE_NAME_CHAR + ']*$');
var illegalAttributeNameCache = {};
var validatedAttributeNameCache = {};

function isAttributeNameSafe(attributeName) {
  if (validatedAttributeNameCache.hasOwnProperty(attributeName)) {
    return true;
  }
  if (illegalAttributeNameCache.hasOwnProperty(attributeName)) {
    return false;
  }
  if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) {
    validatedAttributeNameCache[attributeName] = true;
    return true;
  }
  illegalAttributeNameCache[attributeName] = true;
  "production" !== 'production' ? warning(false, 'Invalid attribute name: `%s`', attributeName) : void 0;
  return false;
}

function shouldIgnoreValue(propertyInfo, value) {
  return value == null || propertyInfo.hasBooleanValue && !value || propertyInfo.hasNumericValue && isNaN(value) || propertyInfo.hasPositiveNumericValue && value < 1 || propertyInfo.hasOverloadedBooleanValue && value === false;
}

/**
 * Operations for dealing with DOM properties.
 */
var DOMPropertyOperations = {

  /**
   * Creates markup for the ID property.
   *
   * @param {string} id Unescaped ID.
   * @return {string} Markup string.
   */
  createMarkupForID: function (id) {
    return DOMProperty.ID_ATTRIBUTE_NAME + '=' + quoteAttributeValueForBrowser(id);
  },

  setAttributeForID: function (node, id) {
    node.setAttribute(DOMProperty.ID_ATTRIBUTE_NAME, id);
  },

  createMarkupForRoot: function () {
    return DOMProperty.ROOT_ATTRIBUTE_NAME + '=""';
  },

  setAttributeForRoot: function (node) {
    node.setAttribute(DOMProperty.ROOT_ATTRIBUTE_NAME, '');
  },

  /**
   * Creates markup for a property.
   *
   * @param {string} name
   * @param {*} value
   * @return {?string} Markup string, or null if the property was invalid.
   */
  createMarkupForProperty: function (name, value) {
    var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null;
    if (propertyInfo) {
      if (shouldIgnoreValue(propertyInfo, value)) {
        return '';
      }
      var attributeName = propertyInfo.attributeName;
      if (propertyInfo.hasBooleanValue || propertyInfo.hasOverloadedBooleanValue && value === true) {
        return attributeName + '=""';
      }
      return attributeName + '=' + quoteAttributeValueForBrowser(value);
    } else if (DOMProperty.isCustomAttribute(name)) {
      if (value == null) {
        return '';
      }
      return name + '=' + quoteAttributeValueForBrowser(value);
    }
    return null;
  },

  /**
   * Creates markup for a custom property.
   *
   * @param {string} name
   * @param {*} value
   * @return {string} Markup string, or empty string if the property was invalid.
   */
  createMarkupForCustomAttribute: function (name, value) {
    if (!isAttributeNameSafe(name) || value == null) {
      return '';
    }
    return name + '=' + quoteAttributeValueForBrowser(value);
  },

  /**
   * Sets the value for a property on a node.
   *
   * @param {DOMElement} node
   * @param {string} name
   * @param {*} value
   */
  setValueForProperty: function (node, name, value) {
    var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null;
    if (propertyInfo) {
      var mutationMethod = propertyInfo.mutationMethod;
      if (mutationMethod) {
        mutationMethod(node, value);
      } else if (shouldIgnoreValue(propertyInfo, value)) {
        this.deleteValueForProperty(node, name);
        return;
      } else if (propertyInfo.mustUseProperty) {
        // Contrary to `setAttribute`, object properties are properly
        // `toString`ed by IE8/9.
        node[propertyInfo.propertyName] = value;
      } else {
        var attributeName = propertyInfo.attributeName;
        var namespace = propertyInfo.attributeNamespace;
        // `setAttribute` with objects becomes only `[object]` in IE8/9,
        // ('' + value) makes it output the correct toString()-value.
        if (namespace) {
          node.setAttributeNS(namespace, attributeName, '' + value);
        } else if (propertyInfo.hasBooleanValue || propertyInfo.hasOverloadedBooleanValue && value === true) {
          node.setAttribute(attributeName, '');
        } else {
          node.setAttribute(attributeName, '' + value);
        }
      }
    } else if (DOMProperty.isCustomAttribute(name)) {
      DOMPropertyOperations.setValueForAttribute(node, name, value);
      return;
    }

    if ("production" !== 'production') {
      var payload = {};
      payload[name] = value;
      ReactInstrumentation.debugTool.onHostOperation(ReactDOMComponentTree.getInstanceFromNode(node)._debugID, 'update attribute', payload);
    }
  },

  setValueForAttribute: function (node, name, value) {
    if (!isAttributeNameSafe(name)) {
      return;
    }
    if (value == null) {
      node.removeAttribute(name);
    } else {
      node.setAttribute(name, '' + value);
    }

    if ("production" !== 'production') {
      var payload = {};
      payload[name] = value;
      ReactInstrumentation.debugTool.onHostOperation(ReactDOMComponentTree.getInstanceFromNode(node)._debugID, 'update attribute', payload);
    }
  },

  /**
   * Deletes an attributes from a node.
   *
   * @param {DOMElement} node
   * @param {string} name
   */
  deleteValueForAttribute: function (node, name) {
    node.removeAttribute(name);
    if ("production" !== 'production') {
      ReactInstrumentation.debugTool.onHostOperation(ReactDOMComponentTree.getInstanceFromNode(node)._debugID, 'remove attribute', name);
    }
  },

  /**
   * Deletes the value for a property on a node.
   *
   * @param {DOMElement} node
   * @param {string} name
   */
  deleteValueForProperty: function (node, name) {
    var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null;
    if (propertyInfo) {
      var mutationMethod = propertyInfo.mutationMethod;
      if (mutationMethod) {
        mutationMethod(node, undefined);
      } else if (propertyInfo.mustUseProperty) {
        var propName = propertyInfo.propertyName;
        if (propertyInfo.hasBooleanValue) {
          node[propName] = false;
        } else {
          node[propName] = '';
        }
      } else {
        node.removeAttribute(propertyInfo.attributeName);
      }
    } else if (DOMProperty.isCustomAttribute(name)) {
      node.removeAttribute(name);
    }

    if ("production" !== 'production') {
      ReactInstrumentation.debugTool.onHostOperation(ReactDOMComponentTree.getInstanceFromNode(node)._debugID, 'remove attribute', name);
    }
  }

};

module.exports = DOMPropertyOperations;
},{"./DOMProperty":48,"./ReactDOMComponentTree":83,"./ReactInstrumentation":113,"./quoteAttributeValueForBrowser":179,"fbjs/lib/warning":213}],50:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule Danger
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var DOMLazyTree = require('./DOMLazyTree');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');

var createNodesFromMarkup = require('fbjs/lib/createNodesFromMarkup');
var emptyFunction = require('fbjs/lib/emptyFunction');
var invariant = require('fbjs/lib/invariant');

var Danger = {

  /**
   * Replaces a node with a string of markup at its current position within its
   * parent. The markup must render into a single root node.
   *
   * @param {DOMElement} oldChild Child node to replace.
   * @param {string} markup Markup to render in place of the child node.
   * @internal
   */
  dangerouslyReplaceNodeWithMarkup: function (oldChild, markup) {
    !ExecutionEnvironment.canUseDOM ? "production" !== 'production' ? invariant(false, 'dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a worker thread. Make sure `window` and `document` are available globally before requiring React when unit testing or use ReactDOMServer.renderToString() for server rendering.') : _prodInvariant('56') : void 0;
    !markup ? "production" !== 'production' ? invariant(false, 'dangerouslyReplaceNodeWithMarkup(...): Missing markup.') : _prodInvariant('57') : void 0;
    !(oldChild.nodeName !== 'HTML') ? "production" !== 'production' ? invariant(false, 'dangerouslyReplaceNodeWithMarkup(...): Cannot replace markup of the <html> node. This is because browser quirks make this unreliable and/or slow. If you want to render to the root you must use server rendering. See ReactDOMServer.renderToString().') : _prodInvariant('58') : void 0;

    if (typeof markup === 'string') {
      var newChild = createNodesFromMarkup(markup, emptyFunction)[0];
      oldChild.parentNode.replaceChild(newChild, oldChild);
    } else {
      DOMLazyTree.replaceChildWithTree(oldChild, markup);
    }
  }

};

module.exports = Danger;
},{"./DOMLazyTree":46,"./reactProdInvariant":180,"fbjs/lib/ExecutionEnvironment":190,"fbjs/lib/createNodesFromMarkup":195,"fbjs/lib/emptyFunction":196,"fbjs/lib/invariant":204}],51:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule DefaultEventPluginOrder
 */

'use strict';

var keyOf = require('fbjs/lib/keyOf');

/**
 * Module that is injectable into `EventPluginHub`, that specifies a
 * deterministic ordering of `EventPlugin`s. A convenient way to reason about
 * plugins, without having to package every one of them. This is better than
 * having plugins be ordered in the same order that they are injected because
 * that ordering would be influenced by the packaging order.
 * `ResponderEventPlugin` must occur before `SimpleEventPlugin` so that
 * preventing default on events is convenient in `SimpleEventPlugin` handlers.
 */
var DefaultEventPluginOrder = [keyOf({ ResponderEventPlugin: null }), keyOf({ SimpleEventPlugin: null }), keyOf({ TapEventPlugin: null }), keyOf({ EnterLeaveEventPlugin: null }), keyOf({ ChangeEventPlugin: null }), keyOf({ SelectEventPlugin: null }), keyOf({ BeforeInputEventPlugin: null })];

module.exports = DefaultEventPluginOrder;
},{"fbjs/lib/keyOf":208}],52:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule DisabledInputUtils
 */

'use strict';

var disableableMouseListenerNames = {
  onClick: true,
  onDoubleClick: true,
  onMouseDown: true,
  onMouseMove: true,
  onMouseUp: true,

  onClickCapture: true,
  onDoubleClickCapture: true,
  onMouseDownCapture: true,
  onMouseMoveCapture: true,
  onMouseUpCapture: true
};

/**
 * Implements a host component that does not receive mouse events
 * when `disabled` is set.
 */
var DisabledInputUtils = {
  getHostProps: function (inst, props) {
    if (!props.disabled) {
      return props;
    }

    // Copy the props, except the mouse listeners
    var hostProps = {};
    for (var key in props) {
      if (!disableableMouseListenerNames[key] && props.hasOwnProperty(key)) {
        hostProps[key] = props[key];
      }
    }

    return hostProps;
  }
};

module.exports = DisabledInputUtils;
},{}],53:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule EnterLeaveEventPlugin
 */

'use strict';

var EventConstants = require('./EventConstants');
var EventPropagators = require('./EventPropagators');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var SyntheticMouseEvent = require('./SyntheticMouseEvent');

var keyOf = require('fbjs/lib/keyOf');

var topLevelTypes = EventConstants.topLevelTypes;

var eventTypes = {
  mouseEnter: {
    registrationName: keyOf({ onMouseEnter: null }),
    dependencies: [topLevelTypes.topMouseOut, topLevelTypes.topMouseOver]
  },
  mouseLeave: {
    registrationName: keyOf({ onMouseLeave: null }),
    dependencies: [topLevelTypes.topMouseOut, topLevelTypes.topMouseOver]
  }
};

var EnterLeaveEventPlugin = {

  eventTypes: eventTypes,

  /**
   * For almost every interaction we care about, there will be both a top-level
   * `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that
   * we do not extract duplicate events. However, moving the mouse into the
   * browser from outside will not fire a `mouseout` event. In this case, we use
   * the `mouseover` top-level event.
   */
  extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
    if (topLevelType === topLevelTypes.topMouseOver && (nativeEvent.relatedTarget || nativeEvent.fromElement)) {
      return null;
    }
    if (topLevelType !== topLevelTypes.topMouseOut && topLevelType !== topLevelTypes.topMouseOver) {
      // Must not be a mouse in or mouse out - ignoring.
      return null;
    }

    var win;
    if (nativeEventTarget.window === nativeEventTarget) {
      // `nativeEventTarget` is probably a window object.
      win = nativeEventTarget;
    } else {
      // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
      var doc = nativeEventTarget.ownerDocument;
      if (doc) {
        win = doc.defaultView || doc.parentWindow;
      } else {
        win = window;
      }
    }

    var from;
    var to;
    if (topLevelType === topLevelTypes.topMouseOut) {
      from = targetInst;
      var related = nativeEvent.relatedTarget || nativeEvent.toElement;
      to = related ? ReactDOMComponentTree.getClosestInstanceFromNode(related) : null;
    } else {
      // Moving to a node from outside the window.
      from = null;
      to = targetInst;
    }

    if (from === to) {
      // Nothing pertains to our managed components.
      return null;
    }

    var fromNode = from == null ? win : ReactDOMComponentTree.getNodeFromInstance(from);
    var toNode = to == null ? win : ReactDOMComponentTree.getNodeFromInstance(to);

    var leave = SyntheticMouseEvent.getPooled(eventTypes.mouseLeave, from, nativeEvent, nativeEventTarget);
    leave.type = 'mouseleave';
    leave.target = fromNode;
    leave.relatedTarget = toNode;

    var enter = SyntheticMouseEvent.getPooled(eventTypes.mouseEnter, to, nativeEvent, nativeEventTarget);
    enter.type = 'mouseenter';
    enter.target = toNode;
    enter.relatedTarget = fromNode;

    EventPropagators.accumulateEnterLeaveDispatches(leave, enter, from, to);

    return [leave, enter];
  }

};

module.exports = EnterLeaveEventPlugin;
},{"./EventConstants":54,"./EventPropagators":58,"./ReactDOMComponentTree":83,"./SyntheticMouseEvent":149,"fbjs/lib/keyOf":208}],54:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule EventConstants
 */

'use strict';

var keyMirror = require('fbjs/lib/keyMirror');

var PropagationPhases = keyMirror({ bubbled: null, captured: null });

/**
 * Types of raw signals from the browser caught at the top level.
 */
var topLevelTypes = keyMirror({
  topAbort: null,
  topAnimationEnd: null,
  topAnimationIteration: null,
  topAnimationStart: null,
  topBlur: null,
  topCanPlay: null,
  topCanPlayThrough: null,
  topChange: null,
  topClick: null,
  topCompositionEnd: null,
  topCompositionStart: null,
  topCompositionUpdate: null,
  topContextMenu: null,
  topCopy: null,
  topCut: null,
  topDoubleClick: null,
  topDrag: null,
  topDragEnd: null,
  topDragEnter: null,
  topDragExit: null,
  topDragLeave: null,
  topDragOver: null,
  topDragStart: null,
  topDrop: null,
  topDurationChange: null,
  topEmptied: null,
  topEncrypted: null,
  topEnded: null,
  topError: null,
  topFocus: null,
  topInput: null,
  topInvalid: null,
  topKeyDown: null,
  topKeyPress: null,
  topKeyUp: null,
  topLoad: null,
  topLoadedData: null,
  topLoadedMetadata: null,
  topLoadStart: null,
  topMouseDown: null,
  topMouseMove: null,
  topMouseOut: null,
  topMouseOver: null,
  topMouseUp: null,
  topPaste: null,
  topPause: null,
  topPlay: null,
  topPlaying: null,
  topProgress: null,
  topRateChange: null,
  topReset: null,
  topScroll: null,
  topSeeked: null,
  topSeeking: null,
  topSelectionChange: null,
  topStalled: null,
  topSubmit: null,
  topSuspend: null,
  topTextInput: null,
  topTimeUpdate: null,
  topTouchCancel: null,
  topTouchEnd: null,
  topTouchMove: null,
  topTouchStart: null,
  topTransitionEnd: null,
  topVolumeChange: null,
  topWaiting: null,
  topWheel: null
});

var EventConstants = {
  topLevelTypes: topLevelTypes,
  PropagationPhases: PropagationPhases
};

module.exports = EventConstants;
},{"fbjs/lib/keyMirror":207}],55:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule EventPluginHub
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var EventPluginRegistry = require('./EventPluginRegistry');
var EventPluginUtils = require('./EventPluginUtils');
var ReactErrorUtils = require('./ReactErrorUtils');

var accumulateInto = require('./accumulateInto');
var forEachAccumulated = require('./forEachAccumulated');
var invariant = require('fbjs/lib/invariant');

/**
 * Internal store for event listeners
 */
var listenerBank = {};

/**
 * Internal queue of events that have accumulated their dispatches and are
 * waiting to have their dispatches executed.
 */
var eventQueue = null;

/**
 * Dispatches an event and releases it back into the pool, unless persistent.
 *
 * @param {?object} event Synthetic event to be dispatched.
 * @param {boolean} simulated If the event is simulated (changes exn behavior)
 * @private
 */
var executeDispatchesAndRelease = function (event, simulated) {
  if (event) {
    EventPluginUtils.executeDispatchesInOrder(event, simulated);

    if (!event.isPersistent()) {
      event.constructor.release(event);
    }
  }
};
var executeDispatchesAndReleaseSimulated = function (e) {
  return executeDispatchesAndRelease(e, true);
};
var executeDispatchesAndReleaseTopLevel = function (e) {
  return executeDispatchesAndRelease(e, false);
};

var getDictionaryKey = function (inst) {
  // Prevents V8 performance issue:
  // https://github.com/facebook/react/pull/7232
  return '.' + inst._rootNodeID;
};

/**
 * This is a unified interface for event plugins to be installed and configured.
 *
 * Event plugins can implement the following properties:
 *
 *   `extractEvents` {function(string, DOMEventTarget, string, object): *}
 *     Required. When a top-level event is fired, this method is expected to
 *     extract synthetic events that will in turn be queued and dispatched.
 *
 *   `eventTypes` {object}
 *     Optional, plugins that fire events must publish a mapping of registration
 *     names that are used to register listeners. Values of this mapping must
 *     be objects that contain `registrationName` or `phasedRegistrationNames`.
 *
 *   `executeDispatch` {function(object, function, string)}
 *     Optional, allows plugins to override how an event gets dispatched. By
 *     default, the listener is simply invoked.
 *
 * Each plugin that is injected into `EventsPluginHub` is immediately operable.
 *
 * @public
 */
var EventPluginHub = {

  /**
   * Methods for injecting dependencies.
   */
  injection: {

    /**
     * @param {array} InjectedEventPluginOrder
     * @public
     */
    injectEventPluginOrder: EventPluginRegistry.injectEventPluginOrder,

    /**
     * @param {object} injectedNamesToPlugins Map from names to plugin modules.
     */
    injectEventPluginsByName: EventPluginRegistry.injectEventPluginsByName

  },

  /**
   * Stores `listener` at `listenerBank[registrationName][key]`. Is idempotent.
   *
   * @param {object} inst The instance, which is the source of events.
   * @param {string} registrationName Name of listener (e.g. `onClick`).
   * @param {function} listener The callback to store.
   */
  putListener: function (inst, registrationName, listener) {
    !(typeof listener === 'function') ? "production" !== 'production' ? invariant(false, 'Expected %s listener to be a function, instead got type %s', registrationName, typeof listener) : _prodInvariant('94', registrationName, typeof listener) : void 0;

    var key = getDictionaryKey(inst);
    var bankForRegistrationName = listenerBank[registrationName] || (listenerBank[registrationName] = {});
    bankForRegistrationName[key] = listener;

    var PluginModule = EventPluginRegistry.registrationNameModules[registrationName];
    if (PluginModule && PluginModule.didPutListener) {
      PluginModule.didPutListener(inst, registrationName, listener);
    }
  },

  /**
   * @param {object} inst The instance, which is the source of events.
   * @param {string} registrationName Name of listener (e.g. `onClick`).
   * @return {?function} The stored callback.
   */
  getListener: function (inst, registrationName) {
    var bankForRegistrationName = listenerBank[registrationName];
    var key = getDictionaryKey(inst);
    return bankForRegistrationName && bankForRegistrationName[key];
  },

  /**
   * Deletes a listener from the registration bank.
   *
   * @param {object} inst The instance, which is the source of events.
   * @param {string} registrationName Name of listener (e.g. `onClick`).
   */
  deleteListener: function (inst, registrationName) {
    var PluginModule = EventPluginRegistry.registrationNameModules[registrationName];
    if (PluginModule && PluginModule.willDeleteListener) {
      PluginModule.willDeleteListener(inst, registrationName);
    }

    var bankForRegistrationName = listenerBank[registrationName];
    // TODO: This should never be null -- when is it?
    if (bankForRegistrationName) {
      var key = getDictionaryKey(inst);
      delete bankForRegistrationName[key];
    }
  },

  /**
   * Deletes all listeners for the DOM element with the supplied ID.
   *
   * @param {object} inst The instance, which is the source of events.
   */
  deleteAllListeners: function (inst) {
    var key = getDictionaryKey(inst);
    for (var registrationName in listenerBank) {
      if (!listenerBank.hasOwnProperty(registrationName)) {
        continue;
      }

      if (!listenerBank[registrationName][key]) {
        continue;
      }

      var PluginModule = EventPluginRegistry.registrationNameModules[registrationName];
      if (PluginModule && PluginModule.willDeleteListener) {
        PluginModule.willDeleteListener(inst, registrationName);
      }

      delete listenerBank[registrationName][key];
    }
  },

  /**
   * Allows registered plugins an opportunity to extract events from top-level
   * native browser events.
   *
   * @return {*} An accumulation of synthetic events.
   * @internal
   */
  extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
    var events;
    var plugins = EventPluginRegistry.plugins;
    for (var i = 0; i < plugins.length; i++) {
      // Not every plugin in the ordering may be loaded at runtime.
      var possiblePlugin = plugins[i];
      if (possiblePlugin) {
        var extractedEvents = possiblePlugin.extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget);
        if (extractedEvents) {
          events = accumulateInto(events, extractedEvents);
        }
      }
    }
    return events;
  },

  /**
   * Enqueues a synthetic event that should be dispatched when
   * `processEventQueue` is invoked.
   *
   * @param {*} events An accumulation of synthetic events.
   * @internal
   */
  enqueueEvents: function (events) {
    if (events) {
      eventQueue = accumulateInto(eventQueue, events);
    }
  },

  /**
   * Dispatches all synthetic events on the event queue.
   *
   * @internal
   */
  processEventQueue: function (simulated) {
    // Set `eventQueue` to null before processing it so that we can tell if more
    // events get enqueued while processing.
    var processingEventQueue = eventQueue;
    eventQueue = null;
    if (simulated) {
      forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseSimulated);
    } else {
      forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
    }
    !!eventQueue ? "production" !== 'production' ? invariant(false, 'processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented.') : _prodInvariant('95') : void 0;
    // This would be a good time to rethrow if any of the event handlers threw.
    ReactErrorUtils.rethrowCaughtError();
  },

  /**
   * These are needed for tests only. Do not use!
   */
  __purge: function () {
    listenerBank = {};
  },

  __getListenerBank: function () {
    return listenerBank;
  }

};

module.exports = EventPluginHub;
},{"./EventPluginRegistry":56,"./EventPluginUtils":57,"./ReactErrorUtils":104,"./accumulateInto":156,"./forEachAccumulated":165,"./reactProdInvariant":180,"fbjs/lib/invariant":204}],56:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule EventPluginRegistry
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var invariant = require('fbjs/lib/invariant');

/**
 * Injectable ordering of event plugins.
 */
var EventPluginOrder = null;

/**
 * Injectable mapping from names to event plugin modules.
 */
var namesToPlugins = {};

/**
 * Recomputes the plugin list using the injected plugins and plugin ordering.
 *
 * @private
 */
function recomputePluginOrdering() {
  if (!EventPluginOrder) {
    // Wait until an `EventPluginOrder` is injected.
    return;
  }
  for (var pluginName in namesToPlugins) {
    var PluginModule = namesToPlugins[pluginName];
    var pluginIndex = EventPluginOrder.indexOf(pluginName);
    !(pluginIndex > -1) ? "production" !== 'production' ? invariant(false, 'EventPluginRegistry: Cannot inject event plugins that do not exist in the plugin ordering, `%s`.', pluginName) : _prodInvariant('96', pluginName) : void 0;
    if (EventPluginRegistry.plugins[pluginIndex]) {
      continue;
    }
    !PluginModule.extractEvents ? "production" !== 'production' ? invariant(false, 'EventPluginRegistry: Event plugins must implement an `extractEvents` method, but `%s` does not.', pluginName) : _prodInvariant('97', pluginName) : void 0;
    EventPluginRegistry.plugins[pluginIndex] = PluginModule;
    var publishedEvents = PluginModule.eventTypes;
    for (var eventName in publishedEvents) {
      !publishEventForPlugin(publishedEvents[eventName], PluginModule, eventName) ? "production" !== 'production' ? invariant(false, 'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.', eventName, pluginName) : _prodInvariant('98', eventName, pluginName) : void 0;
    }
  }
}

/**
 * Publishes an event so that it can be dispatched by the supplied plugin.
 *
 * @param {object} dispatchConfig Dispatch configuration for the event.
 * @param {object} PluginModule Plugin publishing the event.
 * @return {boolean} True if the event was successfully published.
 * @private
 */
function publishEventForPlugin(dispatchConfig, PluginModule, eventName) {
  !!EventPluginRegistry.eventNameDispatchConfigs.hasOwnProperty(eventName) ? "production" !== 'production' ? invariant(false, 'EventPluginHub: More than one plugin attempted to publish the same event name, `%s`.', eventName) : _prodInvariant('99', eventName) : void 0;
  EventPluginRegistry.eventNameDispatchConfigs[eventName] = dispatchConfig;

  var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
  if (phasedRegistrationNames) {
    for (var phaseName in phasedRegistrationNames) {
      if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
        var phasedRegistrationName = phasedRegistrationNames[phaseName];
        publishRegistrationName(phasedRegistrationName, PluginModule, eventName);
      }
    }
    return true;
  } else if (dispatchConfig.registrationName) {
    publishRegistrationName(dispatchConfig.registrationName, PluginModule, eventName);
    return true;
  }
  return false;
}

/**
 * Publishes a registration name that is used to identify dispatched events and
 * can be used with `EventPluginHub.putListener` to register listeners.
 *
 * @param {string} registrationName Registration name to add.
 * @param {object} PluginModule Plugin publishing the event.
 * @private
 */
function publishRegistrationName(registrationName, PluginModule, eventName) {
  !!EventPluginRegistry.registrationNameModules[registrationName] ? "production" !== 'production' ? invariant(false, 'EventPluginHub: More than one plugin attempted to publish the same registration name, `%s`.', registrationName) : _prodInvariant('100', registrationName) : void 0;
  EventPluginRegistry.registrationNameModules[registrationName] = PluginModule;
  EventPluginRegistry.registrationNameDependencies[registrationName] = PluginModule.eventTypes[eventName].dependencies;

  if ("production" !== 'production') {
    var lowerCasedName = registrationName.toLowerCase();
    EventPluginRegistry.possibleRegistrationNames[lowerCasedName] = registrationName;

    if (registrationName === 'onDoubleClick') {
      EventPluginRegistry.possibleRegistrationNames.ondblclick = registrationName;
    }
  }
}

/**
 * Registers plugins so that they can extract and dispatch events.
 *
 * @see {EventPluginHub}
 */
var EventPluginRegistry = {

  /**
   * Ordered list of injected plugins.
   */
  plugins: [],

  /**
   * Mapping from event name to dispatch config
   */
  eventNameDispatchConfigs: {},

  /**
   * Mapping from registration name to plugin module
   */
  registrationNameModules: {},

  /**
   * Mapping from registration name to event name
   */
  registrationNameDependencies: {},

  /**
   * Mapping from lowercase registration names to the properly cased version,
   * used to warn in the case of missing event handlers. Available
   * only in __DEV__.
   * @type {Object}
   */
  possibleRegistrationNames: "production" !== 'production' ? {} : null,

  /**
   * Injects an ordering of plugins (by plugin name). This allows the ordering
   * to be decoupled from injection of the actual plugins so that ordering is
   * always deterministic regardless of packaging, on-the-fly injection, etc.
   *
   * @param {array} InjectedEventPluginOrder
   * @internal
   * @see {EventPluginHub.injection.injectEventPluginOrder}
   */
  injectEventPluginOrder: function (InjectedEventPluginOrder) {
    !!EventPluginOrder ? "production" !== 'production' ? invariant(false, 'EventPluginRegistry: Cannot inject event plugin ordering more than once. You are likely trying to load more than one copy of React.') : _prodInvariant('101') : void 0;
    // Clone the ordering so it cannot be dynamically mutated.
    EventPluginOrder = Array.prototype.slice.call(InjectedEventPluginOrder);
    recomputePluginOrdering();
  },

  /**
   * Injects plugins to be used by `EventPluginHub`. The plugin names must be
   * in the ordering injected by `injectEventPluginOrder`.
   *
   * Plugins can be injected as part of page initialization or on-the-fly.
   *
   * @param {object} injectedNamesToPlugins Map from names to plugin modules.
   * @internal
   * @see {EventPluginHub.injection.injectEventPluginsByName}
   */
  injectEventPluginsByName: function (injectedNamesToPlugins) {
    var isOrderingDirty = false;
    for (var pluginName in injectedNamesToPlugins) {
      if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {
        continue;
      }
      var PluginModule = injectedNamesToPlugins[pluginName];
      if (!namesToPlugins.hasOwnProperty(pluginName) || namesToPlugins[pluginName] !== PluginModule) {
        !!namesToPlugins[pluginName] ? "production" !== 'production' ? invariant(false, 'EventPluginRegistry: Cannot inject two different event plugins using the same name, `%s`.', pluginName) : _prodInvariant('102', pluginName) : void 0;
        namesToPlugins[pluginName] = PluginModule;
        isOrderingDirty = true;
      }
    }
    if (isOrderingDirty) {
      recomputePluginOrdering();
    }
  },

  /**
   * Looks up the plugin for the supplied event.
   *
   * @param {object} event A synthetic event.
   * @return {?object} The plugin that created the supplied event.
   * @internal
   */
  getPluginModuleForEvent: function (event) {
    var dispatchConfig = event.dispatchConfig;
    if (dispatchConfig.registrationName) {
      return EventPluginRegistry.registrationNameModules[dispatchConfig.registrationName] || null;
    }
    for (var phase in dispatchConfig.phasedRegistrationNames) {
      if (!dispatchConfig.phasedRegistrationNames.hasOwnProperty(phase)) {
        continue;
      }
      var PluginModule = EventPluginRegistry.registrationNameModules[dispatchConfig.phasedRegistrationNames[phase]];
      if (PluginModule) {
        return PluginModule;
      }
    }
    return null;
  },

  /**
   * Exposed for unit testing.
   * @private
   */
  _resetEventPlugins: function () {
    EventPluginOrder = null;
    for (var pluginName in namesToPlugins) {
      if (namesToPlugins.hasOwnProperty(pluginName)) {
        delete namesToPlugins[pluginName];
      }
    }
    EventPluginRegistry.plugins.length = 0;

    var eventNameDispatchConfigs = EventPluginRegistry.eventNameDispatchConfigs;
    for (var eventName in eventNameDispatchConfigs) {
      if (eventNameDispatchConfigs.hasOwnProperty(eventName)) {
        delete eventNameDispatchConfigs[eventName];
      }
    }

    var registrationNameModules = EventPluginRegistry.registrationNameModules;
    for (var registrationName in registrationNameModules) {
      if (registrationNameModules.hasOwnProperty(registrationName)) {
        delete registrationNameModules[registrationName];
      }
    }

    if ("production" !== 'production') {
      var possibleRegistrationNames = EventPluginRegistry.possibleRegistrationNames;
      for (var lowerCasedName in possibleRegistrationNames) {
        if (possibleRegistrationNames.hasOwnProperty(lowerCasedName)) {
          delete possibleRegistrationNames[lowerCasedName];
        }
      }
    }
  }

};

module.exports = EventPluginRegistry;
},{"./reactProdInvariant":180,"fbjs/lib/invariant":204}],57:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule EventPluginUtils
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var EventConstants = require('./EventConstants');
var ReactErrorUtils = require('./ReactErrorUtils');

var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');

/**
 * Injected dependencies:
 */

/**
 * - `ComponentTree`: [required] Module that can convert between React instances
 *   and actual node references.
 */
var ComponentTree;
var TreeTraversal;
var injection = {
  injectComponentTree: function (Injected) {
    ComponentTree = Injected;
    if ("production" !== 'production') {
      "production" !== 'production' ? warning(Injected && Injected.getNodeFromInstance && Injected.getInstanceFromNode, 'EventPluginUtils.injection.injectComponentTree(...): Injected ' + 'module is missing getNodeFromInstance or getInstanceFromNode.') : void 0;
    }
  },
  injectTreeTraversal: function (Injected) {
    TreeTraversal = Injected;
    if ("production" !== 'production') {
      "production" !== 'production' ? warning(Injected && Injected.isAncestor && Injected.getLowestCommonAncestor, 'EventPluginUtils.injection.injectTreeTraversal(...): Injected ' + 'module is missing isAncestor or getLowestCommonAncestor.') : void 0;
    }
  }
};

var topLevelTypes = EventConstants.topLevelTypes;

function isEndish(topLevelType) {
  return topLevelType === topLevelTypes.topMouseUp || topLevelType === topLevelTypes.topTouchEnd || topLevelType === topLevelTypes.topTouchCancel;
}

function isMoveish(topLevelType) {
  return topLevelType === topLevelTypes.topMouseMove || topLevelType === topLevelTypes.topTouchMove;
}
function isStartish(topLevelType) {
  return topLevelType === topLevelTypes.topMouseDown || topLevelType === topLevelTypes.topTouchStart;
}

var validateEventDispatches;
if ("production" !== 'production') {
  validateEventDispatches = function (event) {
    var dispatchListeners = event._dispatchListeners;
    var dispatchInstances = event._dispatchInstances;

    var listenersIsArr = Array.isArray(dispatchListeners);
    var listenersLen = listenersIsArr ? dispatchListeners.length : dispatchListeners ? 1 : 0;

    var instancesIsArr = Array.isArray(dispatchInstances);
    var instancesLen = instancesIsArr ? dispatchInstances.length : dispatchInstances ? 1 : 0;

    "production" !== 'production' ? warning(instancesIsArr === listenersIsArr && instancesLen === listenersLen, 'EventPluginUtils: Invalid `event`.') : void 0;
  };
}

/**
 * Dispatch the event to the listener.
 * @param {SyntheticEvent} event SyntheticEvent to handle
 * @param {boolean} simulated If the event is simulated (changes exn behavior)
 * @param {function} listener Application-level callback
 * @param {*} inst Internal component instance
 */
function executeDispatch(event, simulated, listener, inst) {
  var type = event.type || 'unknown-event';
  event.currentTarget = EventPluginUtils.getNodeFromInstance(inst);
  if (simulated) {
    ReactErrorUtils.invokeGuardedCallbackWithCatch(type, listener, event);
  } else {
    ReactErrorUtils.invokeGuardedCallback(type, listener, event);
  }
  event.currentTarget = null;
}

/**
 * Standard/simple iteration through an event's collected dispatches.
 */
function executeDispatchesInOrder(event, simulated) {
  var dispatchListeners = event._dispatchListeners;
  var dispatchInstances = event._dispatchInstances;
  if ("production" !== 'production') {
    validateEventDispatches(event);
  }
  if (Array.isArray(dispatchListeners)) {
    for (var i = 0; i < dispatchListeners.length; i++) {
      if (event.isPropagationStopped()) {
        break;
      }
      // Listeners and Instances are two parallel arrays that are always in sync.
      executeDispatch(event, simulated, dispatchListeners[i], dispatchInstances[i]);
    }
  } else if (dispatchListeners) {
    executeDispatch(event, simulated, dispatchListeners, dispatchInstances);
  }
  event._dispatchListeners = null;
  event._dispatchInstances = null;
}

/**
 * Standard/simple iteration through an event's collected dispatches, but stops
 * at the first dispatch execution returning true, and returns that id.
 *
 * @return {?string} id of the first dispatch execution who's listener returns
 * true, or null if no listener returned true.
 */
function executeDispatchesInOrderStopAtTrueImpl(event) {
  var dispatchListeners = event._dispatchListeners;
  var dispatchInstances = event._dispatchInstances;
  if ("production" !== 'production') {
    validateEventDispatches(event);
  }
  if (Array.isArray(dispatchListeners)) {
    for (var i = 0; i < dispatchListeners.length; i++) {
      if (event.isPropagationStopped()) {
        break;
      }
      // Listeners and Instances are two parallel arrays that are always in sync.
      if (dispatchListeners[i](event, dispatchInstances[i])) {
        return dispatchInstances[i];
      }
    }
  } else if (dispatchListeners) {
    if (dispatchListeners(event, dispatchInstances)) {
      return dispatchInstances;
    }
  }
  return null;
}

/**
 * @see executeDispatchesInOrderStopAtTrueImpl
 */
function executeDispatchesInOrderStopAtTrue(event) {
  var ret = executeDispatchesInOrderStopAtTrueImpl(event);
  event._dispatchInstances = null;
  event._dispatchListeners = null;
  return ret;
}

/**
 * Execution of a "direct" dispatch - there must be at most one dispatch
 * accumulated on the event or it is considered an error. It doesn't really make
 * sense for an event with multiple dispatches (bubbled) to keep track of the
 * return values at each dispatch execution, but it does tend to make sense when
 * dealing with "direct" dispatches.
 *
 * @return {*} The return value of executing the single dispatch.
 */
function executeDirectDispatch(event) {
  if ("production" !== 'production') {
    validateEventDispatches(event);
  }
  var dispatchListener = event._dispatchListeners;
  var dispatchInstance = event._dispatchInstances;
  !!Array.isArray(dispatchListener) ? "production" !== 'production' ? invariant(false, 'executeDirectDispatch(...): Invalid `event`.') : _prodInvariant('103') : void 0;
  event.currentTarget = dispatchListener ? EventPluginUtils.getNodeFromInstance(dispatchInstance) : null;
  var res = dispatchListener ? dispatchListener(event) : null;
  event.currentTarget = null;
  event._dispatchListeners = null;
  event._dispatchInstances = null;
  return res;
}

/**
 * @param {SyntheticEvent} event
 * @return {boolean} True iff number of dispatches accumulated is greater than 0.
 */
function hasDispatches(event) {
  return !!event._dispatchListeners;
}

/**
 * General utilities that are useful in creating custom Event Plugins.
 */
var EventPluginUtils = {
  isEndish: isEndish,
  isMoveish: isMoveish,
  isStartish: isStartish,

  executeDirectDispatch: executeDirectDispatch,
  executeDispatchesInOrder: executeDispatchesInOrder,
  executeDispatchesInOrderStopAtTrue: executeDispatchesInOrderStopAtTrue,
  hasDispatches: hasDispatches,

  getInstanceFromNode: function (node) {
    return ComponentTree.getInstanceFromNode(node);
  },
  getNodeFromInstance: function (node) {
    return ComponentTree.getNodeFromInstance(node);
  },
  isAncestor: function (a, b) {
    return TreeTraversal.isAncestor(a, b);
  },
  getLowestCommonAncestor: function (a, b) {
    return TreeTraversal.getLowestCommonAncestor(a, b);
  },
  getParentInstance: function (inst) {
    return TreeTraversal.getParentInstance(inst);
  },
  traverseTwoPhase: function (target, fn, arg) {
    return TreeTraversal.traverseTwoPhase(target, fn, arg);
  },
  traverseEnterLeave: function (from, to, fn, argFrom, argTo) {
    return TreeTraversal.traverseEnterLeave(from, to, fn, argFrom, argTo);
  },

  injection: injection
};

module.exports = EventPluginUtils;
},{"./EventConstants":54,"./ReactErrorUtils":104,"./reactProdInvariant":180,"fbjs/lib/invariant":204,"fbjs/lib/warning":213}],58:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule EventPropagators
 */

'use strict';

var EventConstants = require('./EventConstants');
var EventPluginHub = require('./EventPluginHub');
var EventPluginUtils = require('./EventPluginUtils');

var accumulateInto = require('./accumulateInto');
var forEachAccumulated = require('./forEachAccumulated');
var warning = require('fbjs/lib/warning');

var PropagationPhases = EventConstants.PropagationPhases;
var getListener = EventPluginHub.getListener;

/**
 * Some event types have a notion of different registration names for different
 * "phases" of propagation. This finds listeners by a given phase.
 */
function listenerAtPhase(inst, event, propagationPhase) {
  var registrationName = event.dispatchConfig.phasedRegistrationNames[propagationPhase];
  return getListener(inst, registrationName);
}

/**
 * Tags a `SyntheticEvent` with dispatched listeners. Creating this function
 * here, allows us to not have to bind or create functions for each event.
 * Mutating the event's members allows us to not have to create a wrapping
 * "dispatch" object that pairs the event with the listener.
 */
function accumulateDirectionalDispatches(inst, upwards, event) {
  if ("production" !== 'production') {
    "production" !== 'production' ? warning(inst, 'Dispatching inst must not be null') : void 0;
  }
  var phase = upwards ? PropagationPhases.bubbled : PropagationPhases.captured;
  var listener = listenerAtPhase(inst, event, phase);
  if (listener) {
    event._dispatchListeners = accumulateInto(event._dispatchListeners, listener);
    event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
  }
}

/**
 * Collect dispatches (must be entirely collected before dispatching - see unit
 * tests). Lazily allocate the array to conserve memory.  We must loop through
 * each event and perform the traversal for each one. We cannot perform a
 * single traversal for the entire collection of events because each event may
 * have a different target.
 */
function accumulateTwoPhaseDispatchesSingle(event) {
  if (event && event.dispatchConfig.phasedRegistrationNames) {
    EventPluginUtils.traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
  }
}

/**
 * Same as `accumulateTwoPhaseDispatchesSingle`, but skips over the targetID.
 */
function accumulateTwoPhaseDispatchesSingleSkipTarget(event) {
  if (event && event.dispatchConfig.phasedRegistrationNames) {
    var targetInst = event._targetInst;
    var parentInst = targetInst ? EventPluginUtils.getParentInstance(targetInst) : null;
    EventPluginUtils.traverseTwoPhase(parentInst, accumulateDirectionalDispatches, event);
  }
}

/**
 * Accumulates without regard to direction, does not look for phased
 * registration names. Same as `accumulateDirectDispatchesSingle` but without
 * requiring that the `dispatchMarker` be the same as the dispatched ID.
 */
function accumulateDispatches(inst, ignoredDirection, event) {
  if (event && event.dispatchConfig.registrationName) {
    var registrationName = event.dispatchConfig.registrationName;
    var listener = getListener(inst, registrationName);
    if (listener) {
      event._dispatchListeners = accumulateInto(event._dispatchListeners, listener);
      event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
    }
  }
}

/**
 * Accumulates dispatches on an `SyntheticEvent`, but only for the
 * `dispatchMarker`.
 * @param {SyntheticEvent} event
 */
function accumulateDirectDispatchesSingle(event) {
  if (event && event.dispatchConfig.registrationName) {
    accumulateDispatches(event._targetInst, null, event);
  }
}

function accumulateTwoPhaseDispatches(events) {
  forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
}

function accumulateTwoPhaseDispatchesSkipTarget(events) {
  forEachAccumulated(events, accumulateTwoPhaseDispatchesSingleSkipTarget);
}

function accumulateEnterLeaveDispatches(leave, enter, from, to) {
  EventPluginUtils.traverseEnterLeave(from, to, accumulateDispatches, leave, enter);
}

function accumulateDirectDispatches(events) {
  forEachAccumulated(events, accumulateDirectDispatchesSingle);
}

/**
 * A small set of propagation patterns, each of which will accept a small amount
 * of information, and generate a set of "dispatch ready event objects" - which
 * are sets of events that have already been annotated with a set of dispatched
 * listener functions/ids. The API is designed this way to discourage these
 * propagation strategies from actually executing the dispatches, since we
 * always want to collect the entire set of dispatches before executing event a
 * single one.
 *
 * @constructor EventPropagators
 */
var EventPropagators = {
  accumulateTwoPhaseDispatches: accumulateTwoPhaseDispatches,
  accumulateTwoPhaseDispatchesSkipTarget: accumulateTwoPhaseDispatchesSkipTarget,
  accumulateDirectDispatches: accumulateDirectDispatches,
  accumulateEnterLeaveDispatches: accumulateEnterLeaveDispatches
};

module.exports = EventPropagators;
},{"./EventConstants":54,"./EventPluginHub":55,"./EventPluginUtils":57,"./accumulateInto":156,"./forEachAccumulated":165,"fbjs/lib/warning":213}],59:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule FallbackCompositionState
 */

'use strict';

var _assign = require('object-assign');

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

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

/**
 * This helper class stores information about text content of a target node,
 * allowing comparison of content before and after a given event.
 *
 * Identify the node where selection currently begins, then observe
 * both its text content and its current position in the DOM. Since the
 * browser may natively replace the target node during composition, we can
 * use its position to find its replacement.
 *
 * @param {DOMEventTarget} root
 */
function FallbackCompositionState(root) {
  this._root = root;
  this._startText = this.getText();
  this._fallbackText = null;
}

_assign(FallbackCompositionState.prototype, {
  destructor: function () {
    this._root = null;
    this._startText = null;
    this._fallbackText = null;
  },

  /**
   * Get current text of input.
   *
   * @return {string}
   */
  getText: function () {
    if ('value' in this._root) {
      return this._root.value;
    }
    return this._root[getTextContentAccessor()];
  },

  /**
   * Determine the differing substring between the initially stored
   * text content and the current content.
   *
   * @return {string}
   */
  getData: function () {
    if (this._fallbackText) {
      return this._fallbackText;
    }

    var start;
    var startValue = this._startText;
    var startLength = startValue.length;
    var end;
    var endValue = this.getText();
    var endLength = endValue.length;

    for (start = 0; start < startLength; start++) {
      if (startValue[start] !== endValue[start]) {
        break;
      }
    }

    var minEnd = startLength - start;
    for (end = 1; end <= minEnd; end++) {
      if (startValue[startLength - end] !== endValue[endLength - end]) {
        break;
      }
    }

    var sliceTail = end > 1 ? 1 - end : undefined;
    this._fallbackText = endValue.slice(start, sliceTail);
    return this._fallbackText;
  }
});

PooledClass.addPoolingTo(FallbackCompositionState);

module.exports = FallbackCompositionState;
},{"./PooledClass":63,"./getTextContentAccessor":173,"object-assign":214}],60:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule HTMLDOMPropertyConfig
 */

'use strict';

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

var MUST_USE_PROPERTY = DOMProperty.injection.MUST_USE_PROPERTY;
var HAS_BOOLEAN_VALUE = DOMProperty.injection.HAS_BOOLEAN_VALUE;
var HAS_NUMERIC_VALUE = DOMProperty.injection.HAS_NUMERIC_VALUE;
var HAS_POSITIVE_NUMERIC_VALUE = DOMProperty.injection.HAS_POSITIVE_NUMERIC_VALUE;
var HAS_OVERLOADED_BOOLEAN_VALUE = DOMProperty.injection.HAS_OVERLOADED_BOOLEAN_VALUE;

var HTMLDOMPropertyConfig = {
  isCustomAttribute: RegExp.prototype.test.bind(new RegExp('^(data|aria)-[' + DOMProperty.ATTRIBUTE_NAME_CHAR + ']*$')),
  Properties: {
    /**
     * Standard Properties
     */
    accept: 0,
    acceptCharset: 0,
    accessKey: 0,
    action: 0,
    allowFullScreen: HAS_BOOLEAN_VALUE,
    allowTransparency: 0,
    alt: 0,
    // specifies target context for links with `preload` type
    as: 0,
    async: HAS_BOOLEAN_VALUE,
    autoComplete: 0,
    // autoFocus is polyfilled/normalized by AutoFocusUtils
    // autoFocus: HAS_BOOLEAN_VALUE,
    autoPlay: HAS_BOOLEAN_VALUE,
    capture: HAS_BOOLEAN_VALUE,
    cellPadding: 0,
    cellSpacing: 0,
    charSet: 0,
    challenge: 0,
    checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
    cite: 0,
    classID: 0,
    className: 0,
    cols: HAS_POSITIVE_NUMERIC_VALUE,
    colSpan: 0,
    content: 0,
    contentEditable: 0,
    contextMenu: 0,
    controls: HAS_BOOLEAN_VALUE,
    coords: 0,
    crossOrigin: 0,
    data: 0, // For `<object />` acts as `src`.
    dateTime: 0,
    'default': HAS_BOOLEAN_VALUE,
    defer: HAS_BOOLEAN_VALUE,
    dir: 0,
    disabled: HAS_BOOLEAN_VALUE,
    download: HAS_OVERLOADED_BOOLEAN_VALUE,
    draggable: 0,
    encType: 0,
    form: 0,
    formAction: 0,
    formEncType: 0,
    formMethod: 0,
    formNoValidate: HAS_BOOLEAN_VALUE,
    formTarget: 0,
    frameBorder: 0,
    headers: 0,
    height: 0,
    hidden: HAS_BOOLEAN_VALUE,
    high: 0,
    href: 0,
    hrefLang: 0,
    htmlFor: 0,
    httpEquiv: 0,
    icon: 0,
    id: 0,
    inputMode: 0,
    integrity: 0,
    is: 0,
    keyParams: 0,
    keyType: 0,
    kind: 0,
    label: 0,
    lang: 0,
    list: 0,
    loop: HAS_BOOLEAN_VALUE,
    low: 0,
    manifest: 0,
    marginHeight: 0,
    marginWidth: 0,
    max: 0,
    maxLength: 0,
    media: 0,
    mediaGroup: 0,
    method: 0,
    min: 0,
    minLength: 0,
    // Caution; `option.selected` is not updated if `select.multiple` is
    // disabled with `removeAttribute`.
    multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
    muted: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
    name: 0,
    nonce: 0,
    noValidate: HAS_BOOLEAN_VALUE,
    open: HAS_BOOLEAN_VALUE,
    optimum: 0,
    pattern: 0,
    placeholder: 0,
    playsInline: HAS_BOOLEAN_VALUE,
    poster: 0,
    preload: 0,
    profile: 0,
    radioGroup: 0,
    readOnly: HAS_BOOLEAN_VALUE,
    referrerPolicy: 0,
    rel: 0,
    required: HAS_BOOLEAN_VALUE,
    reversed: HAS_BOOLEAN_VALUE,
    role: 0,
    rows: HAS_POSITIVE_NUMERIC_VALUE,
    rowSpan: HAS_NUMERIC_VALUE,
    sandbox: 0,
    scope: 0,
    scoped: HAS_BOOLEAN_VALUE,
    scrolling: 0,
    seamless: HAS_BOOLEAN_VALUE,
    selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
    shape: 0,
    size: HAS_POSITIVE_NUMERIC_VALUE,
    sizes: 0,
    span: HAS_POSITIVE_NUMERIC_VALUE,
    spellCheck: 0,
    src: 0,
    srcDoc: 0,
    srcLang: 0,
    srcSet: 0,
    start: HAS_NUMERIC_VALUE,
    step: 0,
    style: 0,
    summary: 0,
    tabIndex: 0,
    target: 0,
    title: 0,
    // Setting .type throws on non-<input> tags
    type: 0,
    useMap: 0,
    value: 0,
    width: 0,
    wmode: 0,
    wrap: 0,

    /**
     * RDFa Properties
     */
    about: 0,
    datatype: 0,
    inlist: 0,
    prefix: 0,
    // property is also supported for OpenGraph in meta tags.
    property: 0,
    resource: 0,
    'typeof': 0,
    vocab: 0,

    /**
     * Non-standard Properties
     */
    // autoCapitalize and autoCorrect are supported in Mobile Safari for
    // keyboard hints.
    autoCapitalize: 0,
    autoCorrect: 0,
    // autoSave allows WebKit/Blink to persist values of input fields on page reloads
    autoSave: 0,
    // color is for Safari mask-icon link
    color: 0,
    // itemProp, itemScope, itemType are for
    // Microdata support. See http://schema.org/docs/gs.html
    itemProp: 0,
    itemScope: HAS_BOOLEAN_VALUE,
    itemType: 0,
    // itemID and itemRef are for Microdata support as well but
    // only specified in the WHATWG spec document. See
    // https://html.spec.whatwg.org/multipage/microdata.html#microdata-dom-api
    itemID: 0,
    itemRef: 0,
    // results show looking glass icon and recent searches on input
    // search fields in WebKit/Blink
    results: 0,
    // IE-only attribute that specifies security restrictions on an iframe
    // as an alternative to the sandbox attribute on IE<10
    security: 0,
    // IE-only attribute that controls focus behavior
    unselectable: 0
  },
  DOMAttributeNames: {
    acceptCharset: 'accept-charset',
    className: 'class',
    htmlFor: 'for',
    httpEquiv: 'http-equiv'
  },
  DOMPropertyNames: {}
};

module.exports = HTMLDOMPropertyConfig;
},{"./DOMProperty":48}],61:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule KeyEscapeUtils
 * 
 */

'use strict';

/**
 * Escape and wrap key so it is safe to use as a reactid
 *
 * @param {string} key to be escaped.
 * @return {string} the escaped key.
 */

function escape(key) {
  var escapeRegex = /[=:]/g;
  var escaperLookup = {
    '=': '=0',
    ':': '=2'
  };
  var escapedString = ('' + key).replace(escapeRegex, function (match) {
    return escaperLookup[match];
  });

  return '$' + escapedString;
}

/**
 * Unescape and unwrap key for human-readable display
 *
 * @param {string} key to unescape.
 * @return {string} the unescaped key.
 */
function unescape(key) {
  var unescapeRegex = /(=0|=2)/g;
  var unescaperLookup = {
    '=0': '=',
    '=2': ':'
  };
  var keySubstring = key[0] === '.' && key[1] === '$' ? key.substring(2) : key.substring(1);

  return ('' + keySubstring).replace(unescapeRegex, function (match) {
    return unescaperLookup[match];
  });
}

var KeyEscapeUtils = {
  escape: escape,
  unescape: unescape
};

module.exports = KeyEscapeUtils;
},{}],62:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule LinkedValueUtils
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var ReactPropTypes = require('./ReactPropTypes');
var ReactPropTypeLocations = require('./ReactPropTypeLocations');
var ReactPropTypesSecret = require('./ReactPropTypesSecret');

var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');

var hasReadOnlyValue = {
  'button': true,
  'checkbox': true,
  'image': true,
  'hidden': true,
  'radio': true,
  'reset': true,
  'submit': true
};

function _assertSingleLink(inputProps) {
  !(inputProps.checkedLink == null || inputProps.valueLink == null) ? "production" !== 'production' ? invariant(false, 'Cannot provide a checkedLink and a valueLink. If you want to use checkedLink, you probably don\'t want to use valueLink and vice versa.') : _prodInvariant('87') : void 0;
}
function _assertValueLink(inputProps) {
  _assertSingleLink(inputProps);
  !(inputProps.value == null && inputProps.onChange == null) ? "production" !== 'production' ? invariant(false, 'Cannot provide a valueLink and a value or onChange event. If you want to use value or onChange, you probably don\'t want to use valueLink.') : _prodInvariant('88') : void 0;
}

function _assertCheckedLink(inputProps) {
  _assertSingleLink(inputProps);
  !(inputProps.checked == null && inputProps.onChange == null) ? "production" !== 'production' ? invariant(false, 'Cannot provide a checkedLink and a checked property or onChange event. If you want to use checked or onChange, you probably don\'t want to use checkedLink') : _prodInvariant('89') : void 0;
}

var propTypes = {
  value: function (props, propName, componentName) {
    if (!props[propName] || hasReadOnlyValue[props.type] || props.onChange || props.readOnly || props.disabled) {
      return null;
    }
    return new Error('You provided a `value` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultValue`. Otherwise, ' + 'set either `onChange` or `readOnly`.');
  },
  checked: function (props, propName, componentName) {
    if (!props[propName] || props.onChange || props.readOnly || props.disabled) {
      return null;
    }
    return new Error('You provided a `checked` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultChecked`. Otherwise, ' + 'set either `onChange` or `readOnly`.');
  },
  onChange: ReactPropTypes.func
};

var loggedTypeFailures = {};
function getDeclarationErrorAddendum(owner) {
  if (owner) {
    var name = owner.getName();
    if (name) {
      return ' Check the render method of `' + name + '`.';
    }
  }
  return '';
}

/**
 * Provide a linked `value` attribute for controlled forms. You should not use
 * this outside of the ReactDOM controlled form components.
 */
var LinkedValueUtils = {
  checkPropTypes: function (tagName, props, owner) {
    for (var propName in propTypes) {
      if (propTypes.hasOwnProperty(propName)) {
        var error = propTypes[propName](props, propName, tagName, ReactPropTypeLocations.prop, null, ReactPropTypesSecret);
      }
      if (error instanceof Error && !(error.message in loggedTypeFailures)) {
        // Only monitor this failure once because there tends to be a lot of the
        // same error.
        loggedTypeFailures[error.message] = true;

        var addendum = getDeclarationErrorAddendum(owner);
        "production" !== 'production' ? warning(false, 'Failed form propType: %s%s', error.message, addendum) : void 0;
      }
    }
  },

  /**
   * @param {object} inputProps Props for form component
   * @return {*} current value of the input either from value prop or link.
   */
  getValue: function (inputProps) {
    if (inputProps.valueLink) {
      _assertValueLink(inputProps);
      return inputProps.valueLink.value;
    }
    return inputProps.value;
  },

  /**
   * @param {object} inputProps Props for form component
   * @return {*} current checked status of the input either from checked prop
   *             or link.
   */
  getChecked: function (inputProps) {
    if (inputProps.checkedLink) {
      _assertCheckedLink(inputProps);
      return inputProps.checkedLink.value;
    }
    return inputProps.checked;
  },

  /**
   * @param {object} inputProps Props for form component
   * @param {SyntheticEvent} event change event to handle
   */
  executeOnChange: function (inputProps, event) {
    if (inputProps.valueLink) {
      _assertValueLink(inputProps);
      return inputProps.valueLink.requestChange(event.target.value);
    } else if (inputProps.checkedLink) {
      _assertCheckedLink(inputProps);
      return inputProps.checkedLink.requestChange(event.target.checked);
    } else if (inputProps.onChange) {
      return inputProps.onChange.call(undefined, event);
    }
  }
};

module.exports = LinkedValueUtils;
},{"./ReactPropTypeLocations":123,"./ReactPropTypes":124,"./ReactPropTypesSecret":125,"./reactProdInvariant":180,"fbjs/lib/invariant":204,"fbjs/lib/warning":213}],63:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule PooledClass
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var invariant = require('fbjs/lib/invariant');

/**
 * Static poolers. Several custom versions for each potential number of
 * arguments. A completely generic pooler is easy to implement, but would
 * require accessing the `arguments` object. In each of these, `this` refers to
 * the Class itself, not an instance. If any others are needed, simply add them
 * here, or in their own files.
 */
var oneArgumentPooler = function (copyFieldsFrom) {
  var Klass = this;
  if (Klass.instancePool.length) {
    var instance = Klass.instancePool.pop();
    Klass.call(instance, copyFieldsFrom);
    return instance;
  } else {
    return new Klass(copyFieldsFrom);
  }
};

var twoArgumentPooler = function (a1, a2) {
  var Klass = this;
  if (Klass.instancePool.length) {
    var instance = Klass.instancePool.pop();
    Klass.call(instance, a1, a2);
    return instance;
  } else {
    return new Klass(a1, a2);
  }
};

var threeArgumentPooler = function (a1, a2, a3) {
  var Klass = this;
  if (Klass.instancePool.length) {
    var instance = Klass.instancePool.pop();
    Klass.call(instance, a1, a2, a3);
    return instance;
  } else {
    return new Klass(a1, a2, a3);
  }
};

var fourArgumentPooler = function (a1, a2, a3, a4) {
  var Klass = this;
  if (Klass.instancePool.length) {
    var instance = Klass.instancePool.pop();
    Klass.call(instance, a1, a2, a3, a4);
    return instance;
  } else {
    return new Klass(a1, a2, a3, a4);
  }
};

var fiveArgumentPooler = function (a1, a2, a3, a4, a5) {
  var Klass = this;
  if (Klass.instancePool.length) {
    var instance = Klass.instancePool.pop();
    Klass.call(instance, a1, a2, a3, a4, a5);
    return instance;
  } else {
    return new Klass(a1, a2, a3, a4, a5);
  }
};

var standardReleaser = function (instance) {
  var Klass = this;
  !(instance instanceof Klass) ? "production" !== 'production' ? invariant(false, 'Trying to release an instance into a pool of a different type.') : _prodInvariant('25') : void 0;
  instance.destructor();
  if (Klass.instancePool.length < Klass.poolSize) {
    Klass.instancePool.push(instance);
  }
};

var DEFAULT_POOL_SIZE = 10;
var DEFAULT_POOLER = oneArgumentPooler;

/**
 * Augments `CopyConstructor` to be a poolable class, augmenting only the class
 * itself (statically) not adding any prototypical fields. Any CopyConstructor
 * you give this may have a `poolSize` property, and will look for a
 * prototypical `destructor` on instances.
 *
 * @param {Function} CopyConstructor Constructor that can be used to reset.
 * @param {Function} pooler Customizable pooler.
 */
var addPoolingTo = function (CopyConstructor, pooler) {
  var NewKlass = CopyConstructor;
  NewKlass.instancePool = [];
  NewKlass.getPooled = pooler || DEFAULT_POOLER;
  if (!NewKlass.poolSize) {
    NewKlass.poolSize = DEFAULT_POOL_SIZE;
  }
  NewKlass.release = standardReleaser;
  return NewKlass;
};

var PooledClass = {
  addPoolingTo: addPoolingTo,
  oneArgumentPooler: oneArgumentPooler,
  twoArgumentPooler: twoArgumentPooler,
  threeArgumentPooler: threeArgumentPooler,
  fourArgumentPooler: fourArgumentPooler,
  fiveArgumentPooler: fiveArgumentPooler
};

module.exports = PooledClass;
},{"./reactProdInvariant":180,"fbjs/lib/invariant":204}],64:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule React
 */

'use strict';

var _assign = require('object-assign');

var ReactChildren = require('./ReactChildren');
var ReactComponent = require('./ReactComponent');
var ReactPureComponent = require('./ReactPureComponent');
var ReactClass = require('./ReactClass');
var ReactDOMFactories = require('./ReactDOMFactories');
var ReactElement = require('./ReactElement');
var ReactPropTypes = require('./ReactPropTypes');
var ReactVersion = require('./ReactVersion');

var onlyChild = require('./onlyChild');
var warning = require('fbjs/lib/warning');

var createElement = ReactElement.createElement;
var createFactory = ReactElement.createFactory;
var cloneElement = ReactElement.cloneElement;

if ("production" !== 'production') {
  var ReactElementValidator = require('./ReactElementValidator');
  createElement = ReactElementValidator.createElement;
  createFactory = ReactElementValidator.createFactory;
  cloneElement = ReactElementValidator.cloneElement;
}

var __spread = _assign;

if ("production" !== 'production') {
  var warned = false;
  __spread = function () {
    "production" !== 'production' ? warning(warned, 'React.__spread is deprecated and should not be used. Use ' + 'Object.assign directly or another helper function with similar ' + 'semantics. You may be seeing this warning due to your compiler. ' + 'See https://fb.me/react-spread-deprecation for more details.') : void 0;
    warned = true;
    return _assign.apply(null, arguments);
  };
}

var React = {

  // Modern

  Children: {
    map: ReactChildren.map,
    forEach: ReactChildren.forEach,
    count: ReactChildren.count,
    toArray: ReactChildren.toArray,
    only: onlyChild
  },

  Component: ReactComponent,
  PureComponent: ReactPureComponent,

  createElement: createElement,
  cloneElement: cloneElement,
  isValidElement: ReactElement.isValidElement,

  // Classic

  PropTypes: ReactPropTypes,
  createClass: ReactClass.createClass,
  createFactory: createFactory,
  createMixin: function (mixin) {
    // Currently a noop. Will be used to validate and trace mixins.
    return mixin;
  },

  // This looks DOM specific but these are actually isomorphic helpers
  // since they are just generating DOM strings.
  DOM: ReactDOMFactories,

  version: ReactVersion,

  // Deprecated hook for JSX spread, don't use this for anything.
  __spread: __spread
};

module.exports = React;
},{"./ReactChildren":69,"./ReactClass":71,"./ReactComponent":72,"./ReactDOMFactories":86,"./ReactElement":101,"./ReactElementValidator":102,"./ReactPropTypes":124,"./ReactPureComponent":126,"./ReactVersion":137,"./onlyChild":178,"fbjs/lib/warning":213,"object-assign":214}],65:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactBrowserEventEmitter
 */

'use strict';

var _assign = require('object-assign');

var EventConstants = require('./EventConstants');
var EventPluginRegistry = require('./EventPluginRegistry');
var ReactEventEmitterMixin = require('./ReactEventEmitterMixin');
var ViewportMetrics = require('./ViewportMetrics');

var getVendorPrefixedEventName = require('./getVendorPrefixedEventName');
var isEventSupported = require('./isEventSupported');

/**
 * Summary of `ReactBrowserEventEmitter` event handling:
 *
 *  - Top-level delegation is used to trap most native browser events. This
 *    may only occur in the main thread and is the responsibility of
 *    ReactEventListener, which is injected and can therefore support pluggable
 *    event sources. This is the only work that occurs in the main thread.
 *
 *  - We normalize and de-duplicate events to account for browser quirks. This
 *    may be done in the worker thread.
 *
 *  - Forward these native events (with the associated top-level type used to
 *    trap it) to `EventPluginHub`, which in turn will ask plugins if they want
 *    to extract any synthetic events.
 *
 *  - The `EventPluginHub` will then process each event by annotating them with
 *    "dispatches", a sequence of listeners and IDs that care about that event.
 *
 *  - The `EventPluginHub` then dispatches the events.
 *
 * Overview of React and the event system:
 *
 * +------------+    .
 * |    DOM     |    .
 * +------------+    .
 *       |           .
 *       v           .
 * +------------+    .
 * | ReactEvent |    .
 * |  Listener  |    .
 * +------------+    .                         +-----------+
 *       |           .               +--------+|SimpleEvent|
 *       |           .               |         |Plugin     |
 * +-----|------+    .               v         +-----------+
 * |     |      |    .    +--------------+                    +------------+
 * |     +-----------.--->|EventPluginHub|                    |    Event   |
 * |            |    .    |              |     +-----------+  | Propagators|
 * | ReactEvent |    .    |              |     |TapEvent   |  |------------|
 * |  Emitter   |    .    |              |<---+|Plugin     |  |other plugin|
 * |            |    .    |              |     +-----------+  |  utilities |
 * |     +-----------.--->|              |                    +------------+
 * |     |      |    .    +--------------+
 * +-----|------+    .                ^        +-----------+
 *       |           .                |        |Enter/Leave|
 *       +           .                +-------+|Plugin     |
 * +-------------+   .                         +-----------+
 * | application |   .
 * |-------------|   .
 * |             |   .
 * |             |   .
 * +-------------+   .
 *                   .
 *    React Core     .  General Purpose Event Plugin System
 */

var hasEventPageXY;
var alreadyListeningTo = {};
var isMonitoringScrollValue = false;
var reactTopListenersCounter = 0;

// For events like 'submit' which don't consistently bubble (which we trap at a
// lower node than `document`), binding at `document` would cause duplicate
// events so we don't include them here
var topEventMapping = {
  topAbort: 'abort',
  topAnimationEnd: getVendorPrefixedEventName('animationend') || 'animationend',
  topAnimationIteration: getVendorPrefixedEventName('animationiteration') || 'animationiteration',
  topAnimationStart: getVendorPrefixedEventName('animationstart') || 'animationstart',
  topBlur: 'blur',
  topCanPlay: 'canplay',
  topCanPlayThrough: 'canplaythrough',
  topChange: 'change',
  topClick: 'click',
  topCompositionEnd: 'compositionend',
  topCompositionStart: 'compositionstart',
  topCompositionUpdate: 'compositionupdate',
  topContextMenu: 'contextmenu',
  topCopy: 'copy',
  topCut: 'cut',
  topDoubleClick: 'dblclick',
  topDrag: 'drag',
  topDragEnd: 'dragend',
  topDragEnter: 'dragenter',
  topDragExit: 'dragexit',
  topDragLeave: 'dragleave',
  topDragOver: 'dragover',
  topDragStart: 'dragstart',
  topDrop: 'drop',
  topDurationChange: 'durationchange',
  topEmptied: 'emptied',
  topEncrypted: 'encrypted',
  topEnded: 'ended',
  topError: 'error',
  topFocus: 'focus',
  topInput: 'input',
  topKeyDown: 'keydown',
  topKeyPress: 'keypress',
  topKeyUp: 'keyup',
  topLoadedData: 'loadeddata',
  topLoadedMetadata: 'loadedmetadata',
  topLoadStart: 'loadstart',
  topMouseDown: 'mousedown',
  topMouseMove: 'mousemove',
  topMouseOut: 'mouseout',
  topMouseOver: 'mouseover',
  topMouseUp: 'mouseup',
  topPaste: 'paste',
  topPause: 'pause',
  topPlay: 'play',
  topPlaying: 'playing',
  topProgress: 'progress',
  topRateChange: 'ratechange',
  topScroll: 'scroll',
  topSeeked: 'seeked',
  topSeeking: 'seeking',
  topSelectionChange: 'selectionchange',
  topStalled: 'stalled',
  topSuspend: 'suspend',
  topTextInput: 'textInput',
  topTimeUpdate: 'timeupdate',
  topTouchCancel: 'touchcancel',
  topTouchEnd: 'touchend',
  topTouchMove: 'touchmove',
  topTouchStart: 'touchstart',
  topTransitionEnd: getVendorPrefixedEventName('transitionend') || 'transitionend',
  topVolumeChange: 'volumechange',
  topWaiting: 'waiting',
  topWheel: 'wheel'
};

/**
 * To ensure no conflicts with other potential React instances on the page
 */
var topListenersIDKey = '_reactListenersID' + String(Math.random()).slice(2);

function getListeningForDocument(mountAt) {
  // In IE8, `mountAt` is a host object and doesn't have `hasOwnProperty`
  // directly.
  if (!Object.prototype.hasOwnProperty.call(mountAt, topListenersIDKey)) {
    mountAt[topListenersIDKey] = reactTopListenersCounter++;
    alreadyListeningTo[mountAt[topListenersIDKey]] = {};
  }
  return alreadyListeningTo[mountAt[topListenersIDKey]];
}

/**
 * `ReactBrowserEventEmitter` is used to attach top-level event listeners. For
 * example:
 *
 *   EventPluginHub.putListener('myID', 'onClick', myFunction);
 *
 * This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
 *
 * @internal
 */
var ReactBrowserEventEmitter = _assign({}, ReactEventEmitterMixin, {

  /**
   * Injectable event backend
   */
  ReactEventListener: null,

  injection: {
    /**
     * @param {object} ReactEventListener
     */
    injectReactEventListener: function (ReactEventListener) {
      ReactEventListener.setHandleTopLevel(ReactBrowserEventEmitter.handleTopLevel);
      ReactBrowserEventEmitter.ReactEventListener = ReactEventListener;
    }
  },

  /**
   * Sets whether or not any created callbacks should be enabled.
   *
   * @param {boolean} enabled True if callbacks should be enabled.
   */
  setEnabled: function (enabled) {
    if (ReactBrowserEventEmitter.ReactEventListener) {
      ReactBrowserEventEmitter.ReactEventListener.setEnabled(enabled);
    }
  },

  /**
   * @return {boolean} True if callbacks are enabled.
   */
  isEnabled: function () {
    return !!(ReactBrowserEventEmitter.ReactEventListener && ReactBrowserEventEmitter.ReactEventListener.isEnabled());
  },

  /**
   * We listen for bubbled touch events on the document object.
   *
   * Firefox v8.01 (and possibly others) exhibited strange behavior when
   * mounting `onmousemove` events at some node that was not the document
   * element. The symptoms were that if your mouse is not moving over something
   * contained within that mount point (for example on the background) the
   * top-level listeners for `onmousemove` won't be called. However, if you
   * register the `mousemove` on the document object, then it will of course
   * catch all `mousemove`s. This along with iOS quirks, justifies restricting
   * top-level listeners to the document object only, at least for these
   * movement types of events and possibly all events.
   *
   * @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
   *
   * Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but
   * they bubble to document.
   *
   * @param {string} registrationName Name of listener (e.g. `onClick`).
   * @param {object} contentDocumentHandle Document which owns the container
   */
  listenTo: function (registrationName, contentDocumentHandle) {
    var mountAt = contentDocumentHandle;
    var isListening = getListeningForDocument(mountAt);
    var dependencies = EventPluginRegistry.registrationNameDependencies[registrationName];

    var topLevelTypes = EventConstants.topLevelTypes;
    for (var i = 0; i < dependencies.length; i++) {
      var dependency = dependencies[i];
      if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
        if (dependency === topLevelTypes.topWheel) {
          if (isEventSupported('wheel')) {
            ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelTypes.topWheel, 'wheel', mountAt);
          } else if (isEventSupported('mousewheel')) {
            ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelTypes.topWheel, 'mousewheel', mountAt);
          } else {
            // Firefox needs to capture a different mouse scroll event.
            // @see http://www.quirksmode.org/dom/events/tests/scroll.html
            ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelTypes.topWheel, 'DOMMouseScroll', mountAt);
          }
        } else if (dependency === topLevelTypes.topScroll) {

          if (isEventSupported('scroll', true)) {
            ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(topLevelTypes.topScroll, 'scroll', mountAt);
          } else {
            ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelTypes.topScroll, 'scroll', ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE);
          }
        } else if (dependency === topLevelTypes.topFocus || dependency === topLevelTypes.topBlur) {

          if (isEventSupported('focus', true)) {
            ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(topLevelTypes.topFocus, 'focus', mountAt);
            ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(topLevelTypes.topBlur, 'blur', mountAt);
          } else if (isEventSupported('focusin')) {
            // IE has `focusin` and `focusout` events which bubble.
            // @see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
            ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelTypes.topFocus, 'focusin', mountAt);
            ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelTypes.topBlur, 'focusout', mountAt);
          }

          // to make sure blur and focus event listeners are only attached once
          isListening[topLevelTypes.topBlur] = true;
          isListening[topLevelTypes.topFocus] = true;
        } else if (topEventMapping.hasOwnProperty(dependency)) {
          ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(dependency, topEventMapping[dependency], mountAt);
        }

        isListening[dependency] = true;
      }
    }
  },

  trapBubbledEvent: function (topLevelType, handlerBaseName, handle) {
    return ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelType, handlerBaseName, handle);
  },

  trapCapturedEvent: function (topLevelType, handlerBaseName, handle) {
    return ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(topLevelType, handlerBaseName, handle);
  },

  /**
   * Protect against document.createEvent() returning null
   * Some popup blocker extensions appear to do this:
   * https://github.com/facebook/react/issues/6887
   */
  supportsEventPageXY: function () {
    if (!document.createEvent) {
      return false;
    }
    var ev = document.createEvent('MouseEvent');
    return ev != null && 'pageX' in ev;
  },

  /**
   * Listens to window scroll and resize events. We cache scroll values so that
   * application code can access them without triggering reflows.
   *
   * ViewportMetrics is only used by SyntheticMouse/TouchEvent and only when
   * pageX/pageY isn't supported (legacy browsers).
   *
   * NOTE: Scroll events do not bubble.
   *
   * @see http://www.quirksmode.org/dom/events/scroll.html
   */
  ensureScrollValueMonitoring: function () {
    if (hasEventPageXY === undefined) {
      hasEventPageXY = ReactBrowserEventEmitter.supportsEventPageXY();
    }
    if (!hasEventPageXY && !isMonitoringScrollValue) {
      var refresh = ViewportMetrics.refreshScrollValues;
      ReactBrowserEventEmitter.ReactEventListener.monitorScrollValue(refresh);
      isMonitoringScrollValue = true;
    }
  }

});

module.exports = ReactBrowserEventEmitter;
},{"./EventConstants":54,"./EventPluginRegistry":56,"./ReactEventEmitterMixin":105,"./ViewportMetrics":155,"./getVendorPrefixedEventName":174,"./isEventSupported":176,"object-assign":214}],66:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactCSSTransitionGroup
 */

'use strict';

var _assign = require('object-assign');

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

var ReactTransitionGroup = require('./ReactTransitionGroup');
var ReactCSSTransitionGroupChild = require('./ReactCSSTransitionGroupChild');

function createTransitionTimeoutPropValidator(transitionType) {
  var timeoutPropName = 'transition' + transitionType + 'Timeout';
  var enabledPropName = 'transition' + transitionType;

  return function (props) {
    // If the transition is enabled
    if (props[enabledPropName]) {
      // If no timeout duration is provided
      if (props[timeoutPropName] == null) {
        return new Error(timeoutPropName + ' wasn\'t supplied to ReactCSSTransitionGroup: ' + 'this can cause unreliable animations and won\'t be supported in ' + 'a future version of React. See ' + 'https://fb.me/react-animation-transition-group-timeout for more ' + 'information.');

        // If the duration isn't a number
      } else if (typeof props[timeoutPropName] !== 'number') {
          return new Error(timeoutPropName + ' must be a number (in milliseconds)');
        }
    }
  };
}

/**
 * An easy way to perform CSS transitions and animations when a React component
 * enters or leaves the DOM.
 * See https://facebook.github.io/react/docs/animation.html#high-level-api-reactcsstransitiongroup
 */
var ReactCSSTransitionGroup = React.createClass({
  displayName: 'ReactCSSTransitionGroup',

  propTypes: {
    transitionName: ReactCSSTransitionGroupChild.propTypes.name,

    transitionAppear: React.PropTypes.bool,
    transitionEnter: React.PropTypes.bool,
    transitionLeave: React.PropTypes.bool,
    transitionAppearTimeout: createTransitionTimeoutPropValidator('Appear'),
    transitionEnterTimeout: createTransitionTimeoutPropValidator('Enter'),
    transitionLeaveTimeout: createTransitionTimeoutPropValidator('Leave')
  },

  getDefaultProps: function () {
    return {
      transitionAppear: false,
      transitionEnter: true,
      transitionLeave: true
    };
  },

  _wrapChild: function (child) {
    // We need to provide this childFactory so that
    // ReactCSSTransitionGroupChild can receive updates to name, enter, and
    // leave while it is leaving.
    return React.createElement(ReactCSSTransitionGroupChild, {
      name: this.props.transitionName,
      appear: this.props.transitionAppear,
      enter: this.props.transitionEnter,
      leave: this.props.transitionLeave,
      appearTimeout: this.props.transitionAppearTimeout,
      enterTimeout: this.props.transitionEnterTimeout,
      leaveTimeout: this.props.transitionLeaveTimeout
    }, child);
  },

  render: function () {
    return React.createElement(ReactTransitionGroup, _assign({}, this.props, { childFactory: this._wrapChild }));
  }
});

module.exports = ReactCSSTransitionGroup;
},{"./React":64,"./ReactCSSTransitionGroupChild":67,"./ReactTransitionGroup":134,"object-assign":214}],67:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactCSSTransitionGroupChild
 */

'use strict';

var React = require('./React');
var ReactDOM = require('./ReactDOM');

var CSSCore = require('fbjs/lib/CSSCore');
var ReactTransitionEvents = require('./ReactTransitionEvents');

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

var TICK = 17;

var ReactCSSTransitionGroupChild = React.createClass({
  displayName: 'ReactCSSTransitionGroupChild',

  propTypes: {
    name: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.shape({
      enter: React.PropTypes.string,
      leave: React.PropTypes.string,
      active: React.PropTypes.string
    }), React.PropTypes.shape({
      enter: React.PropTypes.string,
      enterActive: React.PropTypes.string,
      leave: React.PropTypes.string,
      leaveActive: React.PropTypes.string,
      appear: React.PropTypes.string,
      appearActive: React.PropTypes.string
    })]).isRequired,

    // Once we require timeouts to be specified, we can remove the
    // boolean flags (appear etc.) and just accept a number
    // or a bool for the timeout flags (appearTimeout etc.)
    appear: React.PropTypes.bool,
    enter: React.PropTypes.bool,
    leave: React.PropTypes.bool,
    appearTimeout: React.PropTypes.number,
    enterTimeout: React.PropTypes.number,
    leaveTimeout: React.PropTypes.number
  },

  transition: function (animationType, finishCallback, userSpecifiedDelay) {
    var node = ReactDOM.findDOMNode(this);

    if (!node) {
      if (finishCallback) {
        finishCallback();
      }
      return;
    }

    var className = this.props.name[animationType] || this.props.name + '-' + animationType;
    var activeClassName = this.props.name[animationType + 'Active'] || className + '-active';
    var timeout = null;

    var endListener = function (e) {
      if (e && e.target !== node) {
        return;
      }

      clearTimeout(timeout);

      CSSCore.removeClass(node, className);
      CSSCore.removeClass(node, activeClassName);

      ReactTransitionEvents.removeEndEventListener(node, endListener);

      // Usually this optional callback is used for informing an owner of
      // a leave animation and telling it to remove the child.
      if (finishCallback) {
        finishCallback();
      }
    };

    CSSCore.addClass(node, className);

    // Need to do this to actually trigger a transition.
    this.queueClassAndNode(activeClassName, node);

    // If the user specified a timeout delay.
    if (userSpecifiedDelay) {
      // Clean-up the animation after the specified delay
      timeout = setTimeout(endListener, userSpecifiedDelay);
      this.transitionTimeouts.push(timeout);
    } else {
      // DEPRECATED: this listener will be removed in a future version of react
      ReactTransitionEvents.addEndEventListener(node, endListener);
    }
  },

  queueClassAndNode: function (className, node) {
    this.classNameAndNodeQueue.push({
      className: className,
      node: node
    });

    if (!this.timeout) {
      this.timeout = setTimeout(this.flushClassNameAndNodeQueue, TICK);
    }
  },

  flushClassNameAndNodeQueue: function () {
    if (this.isMounted()) {
      this.classNameAndNodeQueue.forEach(function (obj) {
        CSSCore.addClass(obj.node, obj.className);
      });
    }
    this.classNameAndNodeQueue.length = 0;
    this.timeout = null;
  },

  componentWillMount: function () {
    this.classNameAndNodeQueue = [];
    this.transitionTimeouts = [];
  },

  componentWillUnmount: function () {
    if (this.timeout) {
      clearTimeout(this.timeout);
    }
    this.transitionTimeouts.forEach(function (timeout) {
      clearTimeout(timeout);
    });

    this.classNameAndNodeQueue.length = 0;
  },

  componentWillAppear: function (done) {
    if (this.props.appear) {
      this.transition('appear', done, this.props.appearTimeout);
    } else {
      done();
    }
  },

  componentWillEnter: function (done) {
    if (this.props.enter) {
      this.transition('enter', done, this.props.enterTimeout);
    } else {
      done();
    }
  },

  componentWillLeave: function (done) {
    if (this.props.leave) {
      this.transition('leave', done, this.props.leaveTimeout);
    } else {
      done();
    }
  },

  render: function () {
    return onlyChild(this.props.children);
  }
});

module.exports = ReactCSSTransitionGroupChild;
},{"./React":64,"./ReactDOM":79,"./ReactTransitionEvents":133,"./onlyChild":178,"fbjs/lib/CSSCore":188}],68:[function(require,module,exports){
(function (process){
/**
 * Copyright 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactChildReconciler
 */

'use strict';

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

var instantiateReactComponent = require('./instantiateReactComponent');
var KeyEscapeUtils = require('./KeyEscapeUtils');
var shouldUpdateReactComponent = require('./shouldUpdateReactComponent');
var traverseAllChildren = require('./traverseAllChildren');
var warning = require('fbjs/lib/warning');

var ReactComponentTreeHook;

if (typeof process !== 'undefined' && process.env && "production" === 'test') {
  // Temporary hack.
  // Inline requires don't work well with Jest:
  // https://github.com/facebook/react/issues/7240
  // Remove the inline requires when we don't need them anymore:
  // https://github.com/facebook/react/pull/7178
  ReactComponentTreeHook = require('./ReactComponentTreeHook');
}

function instantiateChild(childInstances, child, name, selfDebugID) {
  // We found a component instance.
  var keyUnique = childInstances[name] === undefined;
  if ("production" !== 'production') {
    if (!ReactComponentTreeHook) {
      ReactComponentTreeHook = require('./ReactComponentTreeHook');
    }
    if (!keyUnique) {
      "production" !== 'production' ? warning(false, 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), ReactComponentTreeHook.getStackAddendumByID(selfDebugID)) : void 0;
    }
  }
  if (child != null && keyUnique) {
    childInstances[name] = instantiateReactComponent(child, true);
  }
}

/**
 * ReactChildReconciler provides helpers for initializing or updating a set of
 * children. Its output is suitable for passing it onto ReactMultiChild which
 * does diffed reordering and insertion.
 */
var ReactChildReconciler = {
  /**
   * Generates a "mount image" for each of the supplied children. In the case
   * of `ReactDOMComponent`, a mount image is a string of markup.
   *
   * @param {?object} nestedChildNodes Nested child maps.
   * @return {?object} A set of child instances.
   * @internal
   */
  instantiateChildren: function (nestedChildNodes, transaction, context, selfDebugID // 0 in production and for roots
  ) {
    if (nestedChildNodes == null) {
      return null;
    }
    var childInstances = {};

    if ("production" !== 'production') {
      traverseAllChildren(nestedChildNodes, function (childInsts, child, name) {
        return instantiateChild(childInsts, child, name, selfDebugID);
      }, childInstances);
    } else {
      traverseAllChildren(nestedChildNodes, instantiateChild, childInstances);
    }
    return childInstances;
  },

  /**
   * Updates the rendered children and returns a new set of children.
   *
   * @param {?object} prevChildren Previously initialized set of children.
   * @param {?object} nextChildren Flat child element maps.
   * @param {ReactReconcileTransaction} transaction
   * @param {object} context
   * @return {?object} A new set of child instances.
   * @internal
   */
  updateChildren: function (prevChildren, nextChildren, mountImages, removedNodes, transaction, hostParent, hostContainerInfo, context, selfDebugID // 0 in production and for roots
  ) {
    // We currently don't have a way to track moves here but if we use iterators
    // instead of for..in we can zip the iterators and check if an item has
    // moved.
    // TODO: If nothing has changed, return the prevChildren object so that we
    // can quickly bailout if nothing has changed.
    if (!nextChildren && !prevChildren) {
      return;
    }
    var name;
    var prevChild;
    for (name in nextChildren) {
      if (!nextChildren.hasOwnProperty(name)) {
        continue;
      }
      prevChild = prevChildren && prevChildren[name];
      var prevElement = prevChild && prevChild._currentElement;
      var nextElement = nextChildren[name];
      if (prevChild != null && shouldUpdateReactComponent(prevElement, nextElement)) {
        ReactReconciler.receiveComponent(prevChild, nextElement, transaction, context);
        nextChildren[name] = prevChild;
      } else {
        if (prevChild) {
          removedNodes[name] = ReactReconciler.getHostNode(prevChild);
          ReactReconciler.unmountComponent(prevChild, false);
        }
        // The child must be instantiated before it's mounted.
        var nextChildInstance = instantiateReactComponent(nextElement, true);
        nextChildren[name] = nextChildInstance;
        // Creating mount image now ensures refs are resolved in right order
        // (see https://github.com/facebook/react/pull/7101 for explanation).
        var nextChildMountImage = ReactReconciler.mountComponent(nextChildInstance, transaction, hostParent, hostContainerInfo, context, selfDebugID);
        mountImages.push(nextChildMountImage);
      }
    }
    // Unmount children that are no longer present.
    for (name in prevChildren) {
      if (prevChildren.hasOwnProperty(name) && !(nextChildren && nextChildren.hasOwnProperty(name))) {
        prevChild = prevChildren[name];
        removedNodes[name] = ReactReconciler.getHostNode(prevChild);
        ReactReconciler.unmountComponent(prevChild, false);
      }
    }
  },

  /**
   * Unmounts all rendered children. This should be used to clean up children
   * when this component is unmounted.
   *
   * @param {?object} renderedChildren Previously initialized set of children.
   * @internal
   */
  unmountChildren: function (renderedChildren, safely) {
    for (var name in renderedChildren) {
      if (renderedChildren.hasOwnProperty(name)) {
        var renderedChild = renderedChildren[name];
        ReactReconciler.unmountComponent(renderedChild, safely);
      }
    }
  }

};

module.exports = ReactChildReconciler;
}).call(this,require('_process'))
},{"./KeyEscapeUtils":61,"./ReactComponentTreeHook":75,"./ReactReconciler":128,"./instantiateReactComponent":175,"./shouldUpdateReactComponent":185,"./traverseAllChildren":186,"_process":18,"fbjs/lib/warning":213}],69:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactChildren
 */

'use strict';

var PooledClass = require('./PooledClass');
var ReactElement = require('./ReactElement');

var emptyFunction = require('fbjs/lib/emptyFunction');
var traverseAllChildren = require('./traverseAllChildren');

var twoArgumentPooler = PooledClass.twoArgumentPooler;
var fourArgumentPooler = PooledClass.fourArgumentPooler;

var userProvidedKeyEscapeRegex = /\/+/g;
function escapeUserProvidedKey(text) {
  return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
}

/**
 * PooledClass representing the bookkeeping associated with performing a child
 * traversal. Allows avoiding binding callbacks.
 *
 * @constructor ForEachBookKeeping
 * @param {!function} forEachFunction Function to perform traversal with.
 * @param {?*} forEachContext Context to perform context with.
 */
function ForEachBookKeeping(forEachFunction, forEachContext) {
  this.func = forEachFunction;
  this.context = forEachContext;
  this.count = 0;
}
ForEachBookKeeping.prototype.destructor = function () {
  this.func = null;
  this.context = null;
  this.count = 0;
};
PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler);

function forEachSingleChild(bookKeeping, child, name) {
  var func = bookKeeping.func;
  var context = bookKeeping.context;

  func.call(context, child, bookKeeping.count++);
}

/**
 * Iterates through children that are typically specified as `props.children`.
 *
 * See https://facebook.github.io/react/docs/top-level-api.html#react.children.foreach
 *
 * The provided forEachFunc(child, index) will be called for each
 * leaf child.
 *
 * @param {?*} children Children tree container.
 * @param {function(*, int)} forEachFunc
 * @param {*} forEachContext Context for forEachContext.
 */
function forEachChildren(children, forEachFunc, forEachContext) {
  if (children == null) {
    return children;
  }
  var traverseContext = ForEachBookKeeping.getPooled(forEachFunc, forEachContext);
  traverseAllChildren(children, forEachSingleChild, traverseContext);
  ForEachBookKeeping.release(traverseContext);
}

/**
 * PooledClass representing the bookkeeping associated with performing a child
 * mapping. Allows avoiding binding callbacks.
 *
 * @constructor MapBookKeeping
 * @param {!*} mapResult Object containing the ordered map of results.
 * @param {!function} mapFunction Function to perform mapping with.
 * @param {?*} mapContext Context to perform mapping with.
 */
function MapBookKeeping(mapResult, keyPrefix, mapFunction, mapContext) {
  this.result = mapResult;
  this.keyPrefix = keyPrefix;
  this.func = mapFunction;
  this.context = mapContext;
  this.count = 0;
}
MapBookKeeping.prototype.destructor = function () {
  this.result = null;
  this.keyPrefix = null;
  this.func = null;
  this.context = null;
  this.count = 0;
};
PooledClass.addPoolingTo(MapBookKeeping, fourArgumentPooler);

function mapSingleChildIntoContext(bookKeeping, child, childKey) {
  var result = bookKeeping.result;
  var keyPrefix = bookKeeping.keyPrefix;
  var func = bookKeeping.func;
  var context = bookKeeping.context;


  var mappedChild = func.call(context, child, bookKeeping.count++);
  if (Array.isArray(mappedChild)) {
    mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, emptyFunction.thatReturnsArgument);
  } else if (mappedChild != null) {
    if (ReactElement.isValidElement(mappedChild)) {
      mappedChild = ReactElement.cloneAndReplaceKey(mappedChild,
      // Keep both the (mapped) and old keys if they differ, just as
      // traverseAllChildren used to do for objects as children
      keyPrefix + (mappedChild.key && (!child || child.key !== mappedChild.key) ? escapeUserProvidedKey(mappedChild.key) + '/' : '') + childKey);
    }
    result.push(mappedChild);
  }
}

function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
  var escapedPrefix = '';
  if (prefix != null) {
    escapedPrefix = escapeUserProvidedKey(prefix) + '/';
  }
  var traverseContext = MapBookKeeping.getPooled(array, escapedPrefix, func, context);
  traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
  MapBookKeeping.release(traverseContext);
}

/**
 * Maps children that are typically specified as `props.children`.
 *
 * See https://facebook.github.io/react/docs/top-level-api.html#react.children.map
 *
 * The provided mapFunction(child, key, index) will be called for each
 * leaf child.
 *
 * @param {?*} children Children tree container.
 * @param {function(*, int)} func The map function.
 * @param {*} context Context for mapFunction.
 * @return {object} Object containing the ordered map of results.
 */
function mapChildren(children, func, context) {
  if (children == null) {
    return children;
  }
  var result = [];
  mapIntoWithKeyPrefixInternal(children, result, null, func, context);
  return result;
}

function forEachSingleChildDummy(traverseContext, child, name) {
  return null;
}

/**
 * Count the number of children that are typically specified as
 * `props.children`.
 *
 * See https://facebook.github.io/react/docs/top-level-api.html#react.children.count
 *
 * @param {?*} children Children tree container.
 * @return {number} The number of children.
 */
function countChildren(children, context) {
  return traverseAllChildren(children, forEachSingleChildDummy, null);
}

/**
 * Flatten a children object (typically specified as `props.children`) and
 * return an array with appropriately re-keyed children.
 *
 * See https://facebook.github.io/react/docs/top-level-api.html#react.children.toarray
 */
function toArray(children) {
  var result = [];
  mapIntoWithKeyPrefixInternal(children, result, null, emptyFunction.thatReturnsArgument);
  return result;
}

var ReactChildren = {
  forEach: forEachChildren,
  map: mapChildren,
  mapIntoWithKeyPrefixInternal: mapIntoWithKeyPrefixInternal,
  count: countChildren,
  toArray: toArray
};

module.exports = ReactChildren;
},{"./PooledClass":63,"./ReactElement":101,"./traverseAllChildren":186,"fbjs/lib/emptyFunction":196}],70:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactChildrenMutationWarningHook
 */

'use strict';

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

var warning = require('fbjs/lib/warning');

function handleElement(debugID, element) {
  if (element == null) {
    return;
  }
  if (element._shadowChildren === undefined) {
    return;
  }
  if (element._shadowChildren === element.props.children) {
    return;
  }
  var isMutated = false;
  if (Array.isArray(element._shadowChildren)) {
    if (element._shadowChildren.length === element.props.children.length) {
      for (var i = 0; i < element._shadowChildren.length; i++) {
        if (element._shadowChildren[i] !== element.props.children[i]) {
          isMutated = true;
        }
      }
    } else {
      isMutated = true;
    }
  }
  if (!Array.isArray(element._shadowChildren) || isMutated) {
    "production" !== 'production' ? warning(false, 'Component\'s children should not be mutated.%s', ReactComponentTreeHook.getStackAddendumByID(debugID)) : void 0;
  }
}

var ReactChildrenMutationWarningHook = {
  onMountComponent: function (debugID) {
    handleElement(debugID, ReactComponentTreeHook.getElement(debugID));
  },
  onUpdateComponent: function (debugID) {
    handleElement(debugID, ReactComponentTreeHook.getElement(debugID));
  }
};

module.exports = ReactChildrenMutationWarningHook;
},{"./ReactComponentTreeHook":75,"fbjs/lib/warning":213}],71:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactClass
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant'),
    _assign = require('object-assign');

var ReactComponent = require('./ReactComponent');
var ReactElement = require('./ReactElement');
var ReactPropTypeLocations = require('./ReactPropTypeLocations');
var ReactPropTypeLocationNames = require('./ReactPropTypeLocationNames');
var ReactNoopUpdateQueue = require('./ReactNoopUpdateQueue');

var emptyObject = require('fbjs/lib/emptyObject');
var invariant = require('fbjs/lib/invariant');
var keyMirror = require('fbjs/lib/keyMirror');
var keyOf = require('fbjs/lib/keyOf');
var warning = require('fbjs/lib/warning');

var MIXINS_KEY = keyOf({ mixins: null });

/**
 * Policies that describe methods in `ReactClassInterface`.
 */
var SpecPolicy = keyMirror({
  /**
   * These methods may be defined only once by the class specification or mixin.
   */
  DEFINE_ONCE: null,
  /**
   * These methods may be defined by both the class specification and mixins.
   * Subsequent definitions will be chained. These methods must return void.
   */
  DEFINE_MANY: null,
  /**
   * These methods are overriding the base class.
   */
  OVERRIDE_BASE: null,
  /**
   * These methods are similar to DEFINE_MANY, except we assume they return
   * objects. We try to merge the keys of the return values of all the mixed in
   * functions. If there is a key conflict we throw.
   */
  DEFINE_MANY_MERGED: null
});

var injectedMixins = [];

/**
 * Composite components are higher-level components that compose other composite
 * or host components.
 *
 * To create a new type of `ReactClass`, pass a specification of
 * your new class to `React.createClass`. The only requirement of your class
 * specification is that you implement a `render` method.
 *
 *   var MyComponent = React.createClass({
 *     render: function() {
 *       return <div>Hello World</div>;
 *     }
 *   });
 *
 * The class specification supports a specific protocol of methods that have
 * special meaning (e.g. `render`). See `ReactClassInterface` for
 * more the comprehensive protocol. Any other properties and methods in the
 * class specification will be available on the prototype.
 *
 * @interface ReactClassInterface
 * @internal
 */
var ReactClassInterface = {

  /**
   * An array of Mixin objects to include when defining your component.
   *
   * @type {array}
   * @optional
   */
  mixins: SpecPolicy.DEFINE_MANY,

  /**
   * An object containing properties and methods that should be defined on
   * the component's constructor instead of its prototype (static methods).
   *
   * @type {object}
   * @optional
   */
  statics: SpecPolicy.DEFINE_MANY,

  /**
   * Definition of prop types for this component.
   *
   * @type {object}
   * @optional
   */
  propTypes: SpecPolicy.DEFINE_MANY,

  /**
   * Definition of context types for this component.
   *
   * @type {object}
   * @optional
   */
  contextTypes: SpecPolicy.DEFINE_MANY,

  /**
   * Definition of context types this component sets for its children.
   *
   * @type {object}
   * @optional
   */
  childContextTypes: SpecPolicy.DEFINE_MANY,

  // ==== Definition methods ====

  /**
   * Invoked when the component is mounted. Values in the mapping will be set on
   * `this.props` if that prop is not specified (i.e. using an `in` check).
   *
   * This method is invoked before `getInitialState` and therefore cannot rely
   * on `this.state` or use `this.setState`.
   *
   * @return {object}
   * @optional
   */
  getDefaultProps: SpecPolicy.DEFINE_MANY_MERGED,

  /**
   * Invoked once before the component is mounted. The return value will be used
   * as the initial value of `this.state`.
   *
   *   getInitialState: function() {
   *     return {
   *       isOn: false,
   *       fooBaz: new BazFoo()
   *     }
   *   }
   *
   * @return {object}
   * @optional
   */
  getInitialState: SpecPolicy.DEFINE_MANY_MERGED,

  /**
   * @return {object}
   * @optional
   */
  getChildContext: SpecPolicy.DEFINE_MANY_MERGED,

  /**
   * Uses props from `this.props` and state from `this.state` to render the
   * structure of the component.
   *
   * No guarantees are made about when or how often this method is invoked, so
   * it must not have side effects.
   *
   *   render: function() {
   *     var name = this.props.name;
   *     return <div>Hello, {name}!</div>;
   *   }
   *
   * @return {ReactComponent}
   * @nosideeffects
   * @required
   */
  render: SpecPolicy.DEFINE_ONCE,

  // ==== Delegate methods ====

  /**
   * Invoked when the component is initially created and about to be mounted.
   * This may have side effects, but any external subscriptions or data created
   * by this method must be cleaned up in `componentWillUnmount`.
   *
   * @optional
   */
  componentWillMount: SpecPolicy.DEFINE_MANY,

  /**
   * Invoked when the component has been mounted and has a DOM representation.
   * However, there is no guarantee that the DOM node is in the document.
   *
   * Use this as an opportunity to operate on the DOM when the component has
   * been mounted (initialized and rendered) for the first time.
   *
   * @param {DOMElement} rootNode DOM element representing the component.
   * @optional
   */
  componentDidMount: SpecPolicy.DEFINE_MANY,

  /**
   * Invoked before the component receives new props.
   *
   * Use this as an opportunity to react to a prop transition by updating the
   * state using `this.setState`. Current props are accessed via `this.props`.
   *
   *   componentWillReceiveProps: function(nextProps, nextContext) {
   *     this.setState({
   *       likesIncreasing: nextProps.likeCount > this.props.likeCount
   *     });
   *   }
   *
   * NOTE: There is no equivalent `componentWillReceiveState`. An incoming prop
   * transition may cause a state change, but the opposite is not true. If you
   * need it, you are probably looking for `componentWillUpdate`.
   *
   * @param {object} nextProps
   * @optional
   */
  componentWillReceiveProps: SpecPolicy.DEFINE_MANY,

  /**
   * Invoked while deciding if the component should be updated as a result of
   * receiving new props, state and/or context.
   *
   * Use this as an opportunity to `return false` when you're certain that the
   * transition to the new props/state/context will not require a component
   * update.
   *
   *   shouldComponentUpdate: function(nextProps, nextState, nextContext) {
   *     return !equal(nextProps, this.props) ||
   *       !equal(nextState, this.state) ||
   *       !equal(nextContext, this.context);
   *   }
   *
   * @param {object} nextProps
   * @param {?object} nextState
   * @param {?object} nextContext
   * @return {boolean} True if the component should update.
   * @optional
   */
  shouldComponentUpdate: SpecPolicy.DEFINE_ONCE,

  /**
   * Invoked when the component is about to update due to a transition from
   * `this.props`, `this.state` and `this.context` to `nextProps`, `nextState`
   * and `nextContext`.
   *
   * Use this as an opportunity to perform preparation before an update occurs.
   *
   * NOTE: You **cannot** use `this.setState()` in this method.
   *
   * @param {object} nextProps
   * @param {?object} nextState
   * @param {?object} nextContext
   * @param {ReactReconcileTransaction} transaction
   * @optional
   */
  componentWillUpdate: SpecPolicy.DEFINE_MANY,

  /**
   * Invoked when the component's DOM representation has been updated.
   *
   * Use this as an opportunity to operate on the DOM when the component has
   * been updated.
   *
   * @param {object} prevProps
   * @param {?object} prevState
   * @param {?object} prevContext
   * @param {DOMElement} rootNode DOM element representing the component.
   * @optional
   */
  componentDidUpdate: SpecPolicy.DEFINE_MANY,

  /**
   * Invoked when the component is about to be removed from its parent and have
   * its DOM representation destroyed.
   *
   * Use this as an opportunity to deallocate any external resources.
   *
   * NOTE: There is no `componentDidUnmount` since your component will have been
   * destroyed by that point.
   *
   * @optional
   */
  componentWillUnmount: SpecPolicy.DEFINE_MANY,

  // ==== Advanced methods ====

  /**
   * Updates the component's currently mounted DOM representation.
   *
   * By default, this implements React's rendering and reconciliation algorithm.
   * Sophisticated clients may wish to override this.
   *
   * @param {ReactReconcileTransaction} transaction
   * @internal
   * @overridable
   */
  updateComponent: SpecPolicy.OVERRIDE_BASE

};

/**
 * Mapping from class specification keys to special processing functions.
 *
 * Although these are declared like instance properties in the specification
 * when defining classes using `React.createClass`, they are actually static
 * and are accessible on the constructor instead of the prototype. Despite
 * being static, they must be defined outside of the "statics" key under
 * which all other static methods are defined.
 */
var RESERVED_SPEC_KEYS = {
  displayName: function (Constructor, displayName) {
    Constructor.displayName = displayName;
  },
  mixins: function (Constructor, mixins) {
    if (mixins) {
      for (var i = 0; i < mixins.length; i++) {
        mixSpecIntoComponent(Constructor, mixins[i]);
      }
    }
  },
  childContextTypes: function (Constructor, childContextTypes) {
    if ("production" !== 'production') {
      validateTypeDef(Constructor, childContextTypes, ReactPropTypeLocations.childContext);
    }
    Constructor.childContextTypes = _assign({}, Constructor.childContextTypes, childContextTypes);
  },
  contextTypes: function (Constructor, contextTypes) {
    if ("production" !== 'production') {
      validateTypeDef(Constructor, contextTypes, ReactPropTypeLocations.context);
    }
    Constructor.contextTypes = _assign({}, Constructor.contextTypes, contextTypes);
  },
  /**
   * Special case getDefaultProps which should move into statics but requires
   * automatic merging.
   */
  getDefaultProps: function (Constructor, getDefaultProps) {
    if (Constructor.getDefaultProps) {
      Constructor.getDefaultProps = createMergedResultFunction(Constructor.getDefaultProps, getDefaultProps);
    } else {
      Constructor.getDefaultProps = getDefaultProps;
    }
  },
  propTypes: function (Constructor, propTypes) {
    if ("production" !== 'production') {
      validateTypeDef(Constructor, propTypes, ReactPropTypeLocations.prop);
    }
    Constructor.propTypes = _assign({}, Constructor.propTypes, propTypes);
  },
  statics: function (Constructor, statics) {
    mixStaticSpecIntoComponent(Constructor, statics);
  },
  autobind: function () {} };

// noop
function validateTypeDef(Constructor, typeDef, location) {
  for (var propName in typeDef) {
    if (typeDef.hasOwnProperty(propName)) {
      // use a warning instead of an invariant so components
      // don't show up in prod but only in __DEV__
      "production" !== 'production' ? warning(typeof typeDef[propName] === 'function', '%s: %s type `%s` is invalid; it must be a function, usually from ' + 'React.PropTypes.', Constructor.displayName || 'ReactClass', ReactPropTypeLocationNames[location], propName) : void 0;
    }
  }
}

function validateMethodOverride(isAlreadyDefined, name) {
  var specPolicy = ReactClassInterface.hasOwnProperty(name) ? ReactClassInterface[name] : null;

  // Disallow overriding of base class methods unless explicitly allowed.
  if (ReactClassMixin.hasOwnProperty(name)) {
    !(specPolicy === SpecPolicy.OVERRIDE_BASE) ? "production" !== 'production' ? invariant(false, 'ReactClassInterface: You are attempting to override `%s` from your class specification. Ensure that your method names do not overlap with React methods.', name) : _prodInvariant('73', name) : void 0;
  }

  // Disallow defining methods more than once unless explicitly allowed.
  if (isAlreadyDefined) {
    !(specPolicy === SpecPolicy.DEFINE_MANY || specPolicy === SpecPolicy.DEFINE_MANY_MERGED) ? "production" !== 'production' ? invariant(false, 'ReactClassInterface: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.', name) : _prodInvariant('74', name) : void 0;
  }
}

/**
 * Mixin helper which handles policy validation and reserved
 * specification keys when building React classes.
 */
function mixSpecIntoComponent(Constructor, spec) {
  if (!spec) {
    if ("production" !== 'production') {
      var typeofSpec = typeof spec;
      var isMixinValid = typeofSpec === 'object' && spec !== null;

      "production" !== 'production' ? warning(isMixinValid, '%s: You\'re attempting to include a mixin that is either null ' + 'or not an object. Check the mixins included by the component, ' + 'as well as any mixins they include themselves. ' + 'Expected object but got %s.', Constructor.displayName || 'ReactClass', spec === null ? null : typeofSpec) : void 0;
    }

    return;
  }

  !(typeof spec !== 'function') ? "production" !== 'production' ? invariant(false, 'ReactClass: You\'re attempting to use a component class or function as a mixin. Instead, just use a regular object.') : _prodInvariant('75') : void 0;
  !!ReactElement.isValidElement(spec) ? "production" !== 'production' ? invariant(false, 'ReactClass: You\'re attempting to use a component as a mixin. Instead, just use a regular object.') : _prodInvariant('76') : void 0;

  var proto = Constructor.prototype;
  var autoBindPairs = proto.__reactAutoBindPairs;

  // By handling mixins before any other properties, we ensure the same
  // chaining order is applied to methods with DEFINE_MANY policy, whether
  // mixins are listed before or after these methods in the spec.
  if (spec.hasOwnProperty(MIXINS_KEY)) {
    RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins);
  }

  for (var name in spec) {
    if (!spec.hasOwnProperty(name)) {
      continue;
    }

    if (name === MIXINS_KEY) {
      // We have already handled mixins in a special case above.
      continue;
    }

    var property = spec[name];
    var isAlreadyDefined = proto.hasOwnProperty(name);
    validateMethodOverride(isAlreadyDefined, name);

    if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) {
      RESERVED_SPEC_KEYS[name](Constructor, property);
    } else {
      // Setup methods on prototype:
      // The following member methods should not be automatically bound:
      // 1. Expected ReactClass methods (in the "interface").
      // 2. Overridden methods (that were mixed in).
      var isReactClassMethod = ReactClassInterface.hasOwnProperty(name);
      var isFunction = typeof property === 'function';
      var shouldAutoBind = isFunction && !isReactClassMethod && !isAlreadyDefined && spec.autobind !== false;

      if (shouldAutoBind) {
        autoBindPairs.push(name, property);
        proto[name] = property;
      } else {
        if (isAlreadyDefined) {
          var specPolicy = ReactClassInterface[name];

          // These cases should already be caught by validateMethodOverride.
          !(isReactClassMethod && (specPolicy === SpecPolicy.DEFINE_MANY_MERGED || specPolicy === SpecPolicy.DEFINE_MANY)) ? "production" !== 'production' ? invariant(false, 'ReactClass: Unexpected spec policy %s for key %s when mixing in component specs.', specPolicy, name) : _prodInvariant('77', specPolicy, name) : void 0;

          // For methods which are defined more than once, call the existing
          // methods before calling the new property, merging if appropriate.
          if (specPolicy === SpecPolicy.DEFINE_MANY_MERGED) {
            proto[name] = createMergedResultFunction(proto[name], property);
          } else if (specPolicy === SpecPolicy.DEFINE_MANY) {
            proto[name] = createChainedFunction(proto[name], property);
          }
        } else {
          proto[name] = property;
          if ("production" !== 'production') {
            // Add verbose displayName to the function, which helps when looking
            // at profiling tools.
            if (typeof property === 'function' && spec.displayName) {
              proto[name].displayName = spec.displayName + '_' + name;
            }
          }
        }
      }
    }
  }
}

function mixStaticSpecIntoComponent(Constructor, statics) {
  if (!statics) {
    return;
  }
  for (var name in statics) {
    var property = statics[name];
    if (!statics.hasOwnProperty(name)) {
      continue;
    }

    var isReserved = name in RESERVED_SPEC_KEYS;
    !!isReserved ? "production" !== 'production' ? invariant(false, 'ReactClass: You are attempting to define a reserved property, `%s`, that shouldn\'t be on the "statics" key. Define it as an instance property instead; it will still be accessible on the constructor.', name) : _prodInvariant('78', name) : void 0;

    var isInherited = name in Constructor;
    !!isInherited ? "production" !== 'production' ? invariant(false, 'ReactClass: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.', name) : _prodInvariant('79', name) : void 0;
    Constructor[name] = property;
  }
}

/**
 * Merge two objects, but throw if both contain the same key.
 *
 * @param {object} one The first object, which is mutated.
 * @param {object} two The second object
 * @return {object} one after it has been mutated to contain everything in two.
 */
function mergeIntoWithNoDuplicateKeys(one, two) {
  !(one && two && typeof one === 'object' && typeof two === 'object') ? "production" !== 'production' ? invariant(false, 'mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.') : _prodInvariant('80') : void 0;

  for (var key in two) {
    if (two.hasOwnProperty(key)) {
      !(one[key] === undefined) ? "production" !== 'production' ? invariant(false, 'mergeIntoWithNoDuplicateKeys(): Tried to merge two objects with the same key: `%s`. This conflict may be due to a mixin; in particular, this may be caused by two getInitialState() or getDefaultProps() methods returning objects with clashing keys.', key) : _prodInvariant('81', key) : void 0;
      one[key] = two[key];
    }
  }
  return one;
}

/**
 * Creates a function that invokes two functions and merges their return values.
 *
 * @param {function} one Function to invoke first.
 * @param {function} two Function to invoke second.
 * @return {function} Function that invokes the two argument functions.
 * @private
 */
function createMergedResultFunction(one, two) {
  return function mergedResult() {
    var a = one.apply(this, arguments);
    var b = two.apply(this, arguments);
    if (a == null) {
      return b;
    } else if (b == null) {
      return a;
    }
    var c = {};
    mergeIntoWithNoDuplicateKeys(c, a);
    mergeIntoWithNoDuplicateKeys(c, b);
    return c;
  };
}

/**
 * Creates a function that invokes two functions and ignores their return vales.
 *
 * @param {function} one Function to invoke first.
 * @param {function} two Function to invoke second.
 * @return {function} Function that invokes the two argument functions.
 * @private
 */
function createChainedFunction(one, two) {
  return function chainedFunction() {
    one.apply(this, arguments);
    two.apply(this, arguments);
  };
}

/**
 * Binds a method to the component.
 *
 * @param {object} component Component whose method is going to be bound.
 * @param {function} method Method to be bound.
 * @return {function} The bound method.
 */
function bindAutoBindMethod(component, method) {
  var boundMethod = method.bind(component);
  if ("production" !== 'production') {
    boundMethod.__reactBoundContext = component;
    boundMethod.__reactBoundMethod = method;
    boundMethod.__reactBoundArguments = null;
    var componentName = component.constructor.displayName;
    var _bind = boundMethod.bind;
    boundMethod.bind = function (newThis) {
      for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
        args[_key - 1] = arguments[_key];
      }

      // User is trying to bind() an autobound method; we effectively will
      // ignore the value of "this" that the user is trying to use, so
      // let's warn.
      if (newThis !== component && newThis !== null) {
        "production" !== 'production' ? warning(false, 'bind(): React component methods may only be bound to the ' + 'component instance. See %s', componentName) : void 0;
      } else if (!args.length) {
        "production" !== 'production' ? warning(false, 'bind(): You are binding a component method to the component. ' + 'React does this for you automatically in a high-performance ' + 'way, so you can safely remove this call. See %s', componentName) : void 0;
        return boundMethod;
      }
      var reboundMethod = _bind.apply(boundMethod, arguments);
      reboundMethod.__reactBoundContext = component;
      reboundMethod.__reactBoundMethod = method;
      reboundMethod.__reactBoundArguments = args;
      return reboundMethod;
    };
  }
  return boundMethod;
}

/**
 * Binds all auto-bound methods in a component.
 *
 * @param {object} component Component whose method is going to be bound.
 */
function bindAutoBindMethods(component) {
  var pairs = component.__reactAutoBindPairs;
  for (var i = 0; i < pairs.length; i += 2) {
    var autoBindKey = pairs[i];
    var method = pairs[i + 1];
    component[autoBindKey] = bindAutoBindMethod(component, method);
  }
}

/**
 * Add more to the ReactClass base class. These are all legacy features and
 * therefore not already part of the modern ReactComponent.
 */
var ReactClassMixin = {

  /**
   * TODO: This will be deprecated because state should always keep a consistent
   * type signature and the only use case for this, is to avoid that.
   */
  replaceState: function (newState, callback) {
    this.updater.enqueueReplaceState(this, newState);
    if (callback) {
      this.updater.enqueueCallback(this, callback, 'replaceState');
    }
  },

  /**
   * Checks whether or not this composite component is mounted.
   * @return {boolean} True if mounted, false otherwise.
   * @protected
   * @final
   */
  isMounted: function () {
    return this.updater.isMounted(this);
  }
};

var ReactClassComponent = function () {};
_assign(ReactClassComponent.prototype, ReactComponent.prototype, ReactClassMixin);

/**
 * Module for creating composite components.
 *
 * @class ReactClass
 */
var ReactClass = {

  /**
   * Creates a composite component class given a class specification.
   * See https://facebook.github.io/react/docs/top-level-api.html#react.createclass
   *
   * @param {object} spec Class specification (which must define `render`).
   * @return {function} Component constructor function.
   * @public
   */
  createClass: function (spec) {
    var Constructor = function (props, context, updater) {
      // This constructor gets overridden by mocks. The argument is used
      // by mocks to assert on what gets mounted.

      if ("production" !== 'production') {
        "production" !== 'production' ? warning(this instanceof Constructor, 'Something is calling a React component directly. Use a factory or ' + 'JSX instead. See: https://fb.me/react-legacyfactory') : void 0;
      }

      // Wire up auto-binding
      if (this.__reactAutoBindPairs.length) {
        bindAutoBindMethods(this);
      }

      this.props = props;
      this.context = context;
      this.refs = emptyObject;
      this.updater = updater || ReactNoopUpdateQueue;

      this.state = null;

      // ReactClasses doesn't have constructors. Instead, they use the
      // getInitialState and componentWillMount methods for initialization.

      var initialState = this.getInitialState ? this.getInitialState() : null;
      if ("production" !== 'production') {
        // We allow auto-mocks to proceed as if they're returning null.
        if (initialState === undefined && this.getInitialState._isMockFunction) {
          // This is probably bad practice. Consider warning here and
          // deprecating this convenience.
          initialState = null;
        }
      }
      !(typeof initialState === 'object' && !Array.isArray(initialState)) ? "production" !== 'production' ? invariant(false, '%s.getInitialState(): must return an object or null', Constructor.displayName || 'ReactCompositeComponent') : _prodInvariant('82', Constructor.displayName || 'ReactCompositeComponent') : void 0;

      this.state = initialState;
    };
    Constructor.prototype = new ReactClassComponent();
    Constructor.prototype.constructor = Constructor;
    Constructor.prototype.__reactAutoBindPairs = [];

    injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor));

    mixSpecIntoComponent(Constructor, spec);

    // Initialize the defaultProps property after all mixins have been merged.
    if (Constructor.getDefaultProps) {
      Constructor.defaultProps = Constructor.getDefaultProps();
    }

    if ("production" !== 'production') {
      // This is a tag to indicate that the use of these method names is ok,
      // since it's used with createClass. If it's not, then it's likely a
      // mistake so we'll warn you to use the static property, property
      // initializer or constructor respectively.
      if (Constructor.getDefaultProps) {
        Constructor.getDefaultProps.isReactClassApproved = {};
      }
      if (Constructor.prototype.getInitialState) {
        Constructor.prototype.getInitialState.isReactClassApproved = {};
      }
    }

    !Constructor.prototype.render ? "production" !== 'production' ? invariant(false, 'createClass(...): Class specification must implement a `render` method.') : _prodInvariant('83') : void 0;

    if ("production" !== 'production') {
      "production" !== 'production' ? warning(!Constructor.prototype.componentShouldUpdate, '%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', spec.displayName || 'A component') : void 0;
      "production" !== 'production' ? warning(!Constructor.prototype.componentWillRecieveProps, '%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', spec.displayName || 'A component') : void 0;
    }

    // Reduce time spent doing lookups by setting these on the prototype.
    for (var methodName in ReactClassInterface) {
      if (!Constructor.prototype[methodName]) {
        Constructor.prototype[methodName] = null;
      }
    }

    return Constructor;
  },

  injection: {
    injectMixin: function (mixin) {
      injectedMixins.push(mixin);
    }
  }

};

module.exports = ReactClass;
},{"./ReactComponent":72,"./ReactElement":101,"./ReactNoopUpdateQueue":120,"./ReactPropTypeLocationNames":122,"./ReactPropTypeLocations":123,"./reactProdInvariant":180,"fbjs/lib/emptyObject":197,"fbjs/lib/invariant":204,"fbjs/lib/keyMirror":207,"fbjs/lib/keyOf":208,"fbjs/lib/warning":213,"object-assign":214}],72:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactComponent
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

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

var canDefineProperty = require('./canDefineProperty');
var emptyObject = require('fbjs/lib/emptyObject');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');

/**
 * Base class helpers for the updating state of a component.
 */
function ReactComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}

ReactComponent.prototype.isReactComponent = {};

/**
 * Sets a subset of the state. Always use this to mutate
 * state. You should treat `this.state` as immutable.
 *
 * There is no guarantee that `this.state` will be immediately updated, so
 * accessing `this.state` after calling this method may return the old value.
 *
 * There is no guarantee that calls to `setState` will run synchronously,
 * as they may eventually be batched together.  You can provide an optional
 * callback that will be executed when the call to setState is actually
 * completed.
 *
 * When a function is provided to setState, it will be called at some point in
 * the future (not synchronously). It will be called with the up to date
 * component arguments (state, props, context). These values can be different
 * from this.* because your function may be called after receiveProps but before
 * shouldComponentUpdate, and this new state, props, and context will not yet be
 * assigned to this.
 *
 * @param {object|function} partialState Next partial state or function to
 *        produce next partial state to be merged with current state.
 * @param {?function} callback Called after state is updated.
 * @final
 * @protected
 */
ReactComponent.prototype.setState = function (partialState, callback) {
  !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? "production" !== 'production' ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : _prodInvariant('85') : void 0;
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};

/**
 * Forces an update. This should only be invoked when it is known with
 * certainty that we are **not** in a DOM transaction.
 *
 * You may want to call this when you know that some deeper aspect of the
 * component's state has changed but `setState` was not called.
 *
 * This will not invoke `shouldComponentUpdate`, but it will invoke
 * `componentWillUpdate` and `componentDidUpdate`.
 *
 * @param {?function} callback Called after update is complete.
 * @final
 * @protected
 */
ReactComponent.prototype.forceUpdate = function (callback) {
  this.updater.enqueueForceUpdate(this);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'forceUpdate');
  }
};

/**
 * Deprecated APIs. These APIs used to exist on classic React classes but since
 * we would like to deprecate them, we're not going to move them over to this
 * modern base class. Instead, we define a getter that warns if it's accessed.
 */
if ("production" !== 'production') {
  var deprecatedAPIs = {
    isMounted: ['isMounted', 'Instead, make sure to clean up subscriptions and pending requests in ' + 'componentWillUnmount to prevent memory leaks.'],
    replaceState: ['replaceState', 'Refactor your code to use setState instead (see ' + 'https://github.com/facebook/react/issues/3236).']
  };
  var defineDeprecationWarning = function (methodName, info) {
    if (canDefineProperty) {
      Object.defineProperty(ReactComponent.prototype, methodName, {
        get: function () {
          "production" !== 'production' ? warning(false, '%s(...) is deprecated in plain JavaScript React classes. %s', info[0], info[1]) : void 0;
          return undefined;
        }
      });
    }
  };
  for (var fnName in deprecatedAPIs) {
    if (deprecatedAPIs.hasOwnProperty(fnName)) {
      defineDeprecationWarning(fnName, deprecatedAPIs[fnName]);
    }
  }
}

module.exports = ReactComponent;
},{"./ReactNoopUpdateQueue":120,"./canDefineProperty":158,"./reactProdInvariant":180,"fbjs/lib/emptyObject":197,"fbjs/lib/invariant":204,"fbjs/lib/warning":213}],73:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactComponentBrowserEnvironment
 */

'use strict';

var DOMChildrenOperations = require('./DOMChildrenOperations');
var ReactDOMIDOperations = require('./ReactDOMIDOperations');

/**
 * Abstracts away all functionality of the reconciler that requires knowledge of
 * the browser context. TODO: These callers should be refactored to avoid the
 * need for this injection.
 */
var ReactComponentBrowserEnvironment = {

  processChildrenUpdates: ReactDOMIDOperations.dangerouslyProcessChildrenUpdates,

  replaceNodeWithMarkup: DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup

};

module.exports = ReactComponentBrowserEnvironment;
},{"./DOMChildrenOperations":45,"./ReactDOMIDOperations":88}],74:[function(require,module,exports){
/**
 * Copyright 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactComponentEnvironment
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var invariant = require('fbjs/lib/invariant');

var injected = false;

var ReactComponentEnvironment = {

  /**
   * Optionally injectable hook for swapping out mount images in the middle of
   * the tree.
   */
  replaceNodeWithMarkup: null,

  /**
   * Optionally injectable hook for processing a queue of child updates. Will
   * later move into MultiChildComponents.
   */
  processChildrenUpdates: null,

  injection: {
    injectEnvironment: function (environment) {
      !!injected ? "production" !== 'production' ? invariant(false, 'ReactCompositeComponent: injectEnvironment() can only be called once.') : _prodInvariant('104') : void 0;
      ReactComponentEnvironment.replaceNodeWithMarkup = environment.replaceNodeWithMarkup;
      ReactComponentEnvironment.processChildrenUpdates = environment.processChildrenUpdates;
      injected = true;
    }
  }

};

module.exports = ReactComponentEnvironment;
},{"./reactProdInvariant":180,"fbjs/lib/invariant":204}],75:[function(require,module,exports){
/**
 * Copyright 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactComponentTreeHook
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

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

var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');

function isNative(fn) {
  // Based on isNative() from Lodash
  var funcToString = Function.prototype.toString;
  var hasOwnProperty = Object.prototype.hasOwnProperty;
  var reIsNative = RegExp('^' + funcToString
  // Take an example native function source for comparison
  .call(hasOwnProperty)
  // Strip regex characters so we can use it for regex
  .replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')
  // Remove hasOwnProperty from the template to make it generic
  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$');
  try {
    var source = funcToString.call(fn);
    return reIsNative.test(source);
  } catch (err) {
    return false;
  }
}

var canUseCollections =
// Array.from
typeof Array.from === 'function' &&
// Map
typeof Map === 'function' && isNative(Map) &&
// Map.prototype.keys
Map.prototype != null && typeof Map.prototype.keys === 'function' && isNative(Map.prototype.keys) &&
// Set
typeof Set === 'function' && isNative(Set) &&
// Set.prototype.keys
Set.prototype != null && typeof Set.prototype.keys === 'function' && isNative(Set.prototype.keys);

var itemMap;
var rootIDSet;

var itemByKey;
var rootByKey;

if (canUseCollections) {
  itemMap = new Map();
  rootIDSet = new Set();
} else {
  itemByKey = {};
  rootByKey = {};
}

var unmountedIDs = [];

// Use non-numeric keys to prevent V8 performance issues:
// https://github.com/facebook/react/pull/7232
function getKeyFromID(id) {
  return '.' + id;
}
function getIDFromKey(key) {
  return parseInt(key.substr(1), 10);
}

function get(id) {
  if (canUseCollections) {
    return itemMap.get(id);
  } else {
    var key = getKeyFromID(id);
    return itemByKey[key];
  }
}

function remove(id) {
  if (canUseCollections) {
    itemMap['delete'](id);
  } else {
    var key = getKeyFromID(id);
    delete itemByKey[key];
  }
}

function create(id, element, parentID) {
  var item = {
    element: element,
    parentID: parentID,
    text: null,
    childIDs: [],
    isMounted: false,
    updateCount: 0
  };

  if (canUseCollections) {
    itemMap.set(id, item);
  } else {
    var key = getKeyFromID(id);
    itemByKey[key] = item;
  }
}

function addRoot(id) {
  if (canUseCollections) {
    rootIDSet.add(id);
  } else {
    var key = getKeyFromID(id);
    rootByKey[key] = true;
  }
}

function removeRoot(id) {
  if (canUseCollections) {
    rootIDSet['delete'](id);
  } else {
    var key = getKeyFromID(id);
    delete rootByKey[key];
  }
}

function getRegisteredIDs() {
  if (canUseCollections) {
    return Array.from(itemMap.keys());
  } else {
    return Object.keys(itemByKey).map(getIDFromKey);
  }
}

function getRootIDs() {
  if (canUseCollections) {
    return Array.from(rootIDSet.keys());
  } else {
    return Object.keys(rootByKey).map(getIDFromKey);
  }
}

function purgeDeep(id) {
  var item = get(id);
  if (item) {
    var childIDs = item.childIDs;

    remove(id);
    childIDs.forEach(purgeDeep);
  }
}

function describeComponentFrame(name, source, ownerName) {
  return '\n    in ' + name + (source ? ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + source.lineNumber + ')' : ownerName ? ' (created by ' + ownerName + ')' : '');
}

function getDisplayName(element) {
  if (element == null) {
    return '#empty';
  } else if (typeof element === 'string' || typeof element === 'number') {
    return '#text';
  } else if (typeof element.type === 'string') {
    return element.type;
  } else {
    return element.type.displayName || element.type.name || 'Unknown';
  }
}

function describeID(id) {
  var name = ReactComponentTreeHook.getDisplayName(id);
  var element = ReactComponentTreeHook.getElement(id);
  var ownerID = ReactComponentTreeHook.getOwnerID(id);
  var ownerName;
  if (ownerID) {
    ownerName = ReactComponentTreeHook.getDisplayName(ownerID);
  }
  "production" !== 'production' ? warning(element, 'ReactComponentTreeHook: Missing React element for debugID %s when ' + 'building stack', id) : void 0;
  return describeComponentFrame(name, element && element._source, ownerName);
}

var ReactComponentTreeHook = {
  onSetChildren: function (id, nextChildIDs) {
    var item = get(id);
    item.childIDs = nextChildIDs;

    for (var i = 0; i < nextChildIDs.length; i++) {
      var nextChildID = nextChildIDs[i];
      var nextChild = get(nextChildID);
      !nextChild ? "production" !== 'production' ? invariant(false, 'Expected hook events to fire for the child before its parent includes it in onSetChildren().') : _prodInvariant('140') : void 0;
      !(nextChild.childIDs != null || typeof nextChild.element !== 'object' || nextChild.element == null) ? "production" !== 'production' ? invariant(false, 'Expected onSetChildren() to fire for a container child before its parent includes it in onSetChildren().') : _prodInvariant('141') : void 0;
      !nextChild.isMounted ? "production" !== 'production' ? invariant(false, 'Expected onMountComponent() to fire for the child before its parent includes it in onSetChildren().') : _prodInvariant('71') : void 0;
      if (nextChild.parentID == null) {
        nextChild.parentID = id;
        // TODO: This shouldn't be necessary but mounting a new root during in
        // componentWillMount currently causes not-yet-mounted components to
        // be purged from our tree data so their parent ID is missing.
      }
      !(nextChild.parentID === id) ? "production" !== 'production' ? invariant(false, 'Expected onBeforeMountComponent() parent and onSetChildren() to be consistent (%s has parents %s and %s).', nextChildID, nextChild.parentID, id) : _prodInvariant('142', nextChildID, nextChild.parentID, id) : void 0;
    }
  },
  onBeforeMountComponent: function (id, element, parentID) {
    create(id, element, parentID);
  },
  onBeforeUpdateComponent: function (id, element) {
    var item = get(id);
    if (!item || !item.isMounted) {
      // We may end up here as a result of setState() in componentWillUnmount().
      // In this case, ignore the element.
      return;
    }
    item.element = element;
  },
  onMountComponent: function (id) {
    var item = get(id);
    item.isMounted = true;
    var isRoot = item.parentID === 0;
    if (isRoot) {
      addRoot(id);
    }
  },
  onUpdateComponent: function (id) {
    var item = get(id);
    if (!item || !item.isMounted) {
      // We may end up here as a result of setState() in componentWillUnmount().
      // In this case, ignore the element.
      return;
    }
    item.updateCount++;
  },
  onUnmountComponent: function (id) {
    var item = get(id);
    if (item) {
      // We need to check if it exists.
      // `item` might not exist if it is inside an error boundary, and a sibling
      // error boundary child threw while mounting. Then this instance never
      // got a chance to mount, but it still gets an unmounting event during
      // the error boundary cleanup.
      item.isMounted = false;
      var isRoot = item.parentID === 0;
      if (isRoot) {
        removeRoot(id);
      }
    }
    unmountedIDs.push(id);
  },
  purgeUnmountedComponents: function () {
    if (ReactComponentTreeHook._preventPurging) {
      // Should only be used for testing.
      return;
    }

    for (var i = 0; i < unmountedIDs.length; i++) {
      var id = unmountedIDs[i];
      purgeDeep(id);
    }
    unmountedIDs.length = 0;
  },
  isMounted: function (id) {
    var item = get(id);
    return item ? item.isMounted : false;
  },
  getCurrentStackAddendum: function (topElement) {
    var info = '';
    if (topElement) {
      var type = topElement.type;
      var name = typeof type === 'function' ? type.displayName || type.name : type;
      var owner = topElement._owner;
      info += describeComponentFrame(name || 'Unknown', topElement._source, owner && owner.getName());
    }

    var currentOwner = ReactCurrentOwner.current;
    var id = currentOwner && currentOwner._debugID;

    info += ReactComponentTreeHook.getStackAddendumByID(id);
    return info;
  },
  getStackAddendumByID: function (id) {
    var info = '';
    while (id) {
      info += describeID(id);
      id = ReactComponentTreeHook.getParentID(id);
    }
    return info;
  },
  getChildIDs: function (id) {
    var item = get(id);
    return item ? item.childIDs : [];
  },
  getDisplayName: function (id) {
    var element = ReactComponentTreeHook.getElement(id);
    if (!element) {
      return null;
    }
    return getDisplayName(element);
  },
  getElement: function (id) {
    var item = get(id);
    return item ? item.element : null;
  },
  getOwnerID: function (id) {
    var element = ReactComponentTreeHook.getElement(id);
    if (!element || !element._owner) {
      return null;
    }
    return element._owner._debugID;
  },
  getParentID: function (id) {
    var item = get(id);
    return item ? item.parentID : null;
  },
  getSource: function (id) {
    var item = get(id);
    var element = item ? item.element : null;
    var source = element != null ? element._source : null;
    return source;
  },
  getText: function (id) {
    var element = ReactComponentTreeHook.getElement(id);
    if (typeof element === 'string') {
      return element;
    } else if (typeof element === 'number') {
      return '' + element;
    } else {
      return null;
    }
  },
  getUpdateCount: function (id) {
    var item = get(id);
    return item ? item.updateCount : 0;
  },


  getRegisteredIDs: getRegisteredIDs,

  getRootIDs: getRootIDs
};

module.exports = ReactComponentTreeHook;
},{"./ReactCurrentOwner":78,"./reactProdInvariant":180,"fbjs/lib/invariant":204,"fbjs/lib/warning":213}],76:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactComponentWithPureRenderMixin
 */

'use strict';

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

/**
 * If your React component's render function is "pure", e.g. it will render the
 * same result given the same props and state, provide this mixin for a
 * considerable performance boost.
 *
 * Most React components have pure render functions.
 *
 * Example:
 *
 *   var ReactComponentWithPureRenderMixin =
 *     require('ReactComponentWithPureRenderMixin');
 *   React.createClass({
 *     mixins: [ReactComponentWithPureRenderMixin],
 *
 *     render: function() {
 *       return <div className={this.props.className}>foo</div>;
 *     }
 *   });
 *
 * Note: This only checks shallow equality for props and state. If these contain
 * complex data structures this mixin may have false-negatives for deeper
 * differences. Only mixin to components which have simple props and state, or
 * use `forceUpdate()` when you know deep data structures have changed.
 *
 * See https://facebook.github.io/react/docs/pure-render-mixin.html
 */
var ReactComponentWithPureRenderMixin = {
  shouldComponentUpdate: function (nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState);
  }
};

module.exports = ReactComponentWithPureRenderMixin;
},{"./shallowCompare":184}],77:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactCompositeComponent
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant'),
    _assign = require('object-assign');

var ReactComponentEnvironment = require('./ReactComponentEnvironment');
var ReactCurrentOwner = require('./ReactCurrentOwner');
var ReactElement = require('./ReactElement');
var ReactErrorUtils = require('./ReactErrorUtils');
var ReactInstanceMap = require('./ReactInstanceMap');
var ReactInstrumentation = require('./ReactInstrumentation');
var ReactNodeTypes = require('./ReactNodeTypes');
var ReactPropTypeLocations = require('./ReactPropTypeLocations');
var ReactReconciler = require('./ReactReconciler');

var checkReactTypeSpec = require('./checkReactTypeSpec');
var emptyObject = require('fbjs/lib/emptyObject');
var invariant = require('fbjs/lib/invariant');
var shallowEqual = require('fbjs/lib/shallowEqual');
var shouldUpdateReactComponent = require('./shouldUpdateReactComponent');
var warning = require('fbjs/lib/warning');

var CompositeTypes = {
  ImpureClass: 0,
  PureClass: 1,
  StatelessFunctional: 2
};

function StatelessComponent(Component) {}
StatelessComponent.prototype.render = function () {
  var Component = ReactInstanceMap.get(this)._currentElement.type;
  var element = Component(this.props, this.context, this.updater);
  warnIfInvalidElement(Component, element);
  return element;
};

function warnIfInvalidElement(Component, element) {
  if ("production" !== 'production') {
    "production" !== 'production' ? warning(element === null || element === false || ReactElement.isValidElement(element), '%s(...): A valid React element (or null) must be returned. You may have ' + 'returned undefined, an array or some other invalid object.', Component.displayName || Component.name || 'Component') : void 0;
    "production" !== 'production' ? warning(!Component.childContextTypes, '%s(...): childContextTypes cannot be defined on a functional component.', Component.displayName || Component.name || 'Component') : void 0;
  }
}

function shouldConstruct(Component) {
  return !!(Component.prototype && Component.prototype.isReactComponent);
}

function isPureComponent(Component) {
  return !!(Component.prototype && Component.prototype.isPureReactComponent);
}

// Separated into a function to contain deoptimizations caused by try/finally.
function measureLifeCyclePerf(fn, debugID, timerType) {
  if (debugID === 0) {
    // Top-level wrappers (see ReactMount) and empty components (see
    // ReactDOMEmptyComponent) are invisible to hooks and devtools.
    // Both are implementation details that should go away in the future.
    return fn();
  }

  ReactInstrumentation.debugTool.onBeginLifeCycleTimer(debugID, timerType);
  try {
    return fn();
  } finally {
    ReactInstrumentation.debugTool.onEndLifeCycleTimer(debugID, timerType);
  }
}

/**
 * ------------------ The Life-Cycle of a Composite Component ------------------
 *
 * - constructor: Initialization of state. The instance is now retained.
 *   - componentWillMount
 *   - render
 *   - [children's constructors]
 *     - [children's componentWillMount and render]
 *     - [children's componentDidMount]
 *     - componentDidMount
 *
 *       Update Phases:
 *       - componentWillReceiveProps (only called if parent updated)
 *       - shouldComponentUpdate
 *         - componentWillUpdate
 *           - render
 *           - [children's constructors or receive props phases]
 *         - componentDidUpdate
 *
 *     - componentWillUnmount
 *     - [children's componentWillUnmount]
 *   - [children destroyed]
 * - (destroyed): The instance is now blank, released by React and ready for GC.
 *
 * -----------------------------------------------------------------------------
 */

/**
 * An incrementing ID assigned to each component when it is mounted. This is
 * used to enforce the order in which `ReactUpdates` updates dirty components.
 *
 * @private
 */
var nextMountID = 1;

/**
 * @lends {ReactCompositeComponent.prototype}
 */
var ReactCompositeComponentMixin = {

  /**
   * Base constructor for all composite component.
   *
   * @param {ReactElement} element
   * @final
   * @internal
   */
  construct: function (element) {
    this._currentElement = element;
    this._rootNodeID = 0;
    this._compositeType = null;
    this._instance = null;
    this._hostParent = null;
    this._hostContainerInfo = null;

    // See ReactUpdateQueue
    this._updateBatchNumber = null;
    this._pendingElement = null;
    this._pendingStateQueue = null;
    this._pendingReplaceState = false;
    this._pendingForceUpdate = false;

    this._renderedNodeType = null;
    this._renderedComponent = null;
    this._context = null;
    this._mountOrder = 0;
    this._topLevelWrapper = null;

    // See ReactUpdates and ReactUpdateQueue.
    this._pendingCallbacks = null;

    // ComponentWillUnmount shall only be called once
    this._calledComponentWillUnmount = false;

    if ("production" !== 'production') {
      this._warnedAboutRefsInRender = false;
    }
  },

  /**
   * Initializes the component, renders markup, and registers event listeners.
   *
   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
   * @param {?object} hostParent
   * @param {?object} hostContainerInfo
   * @param {?object} context
   * @return {?string} Rendered markup to be inserted into the DOM.
   * @final
   * @internal
   */
  mountComponent: function (transaction, hostParent, hostContainerInfo, context) {
    var _this = this;

    this._context = context;
    this._mountOrder = nextMountID++;
    this._hostParent = hostParent;
    this._hostContainerInfo = hostContainerInfo;

    var publicProps = this._currentElement.props;
    var publicContext = this._processContext(context);

    var Component = this._currentElement.type;

    var updateQueue = transaction.getUpdateQueue();

    // Initialize the public class
    var doConstruct = shouldConstruct(Component);
    var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);
    var renderedElement;

    // Support functional components
    if (!doConstruct && (inst == null || inst.render == null)) {
      renderedElement = inst;
      warnIfInvalidElement(Component, renderedElement);
      !(inst === null || inst === false || ReactElement.isValidElement(inst)) ? "production" !== 'production' ? invariant(false, '%s(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.', Component.displayName || Component.name || 'Component') : _prodInvariant('105', Component.displayName || Component.name || 'Component') : void 0;
      inst = new StatelessComponent(Component);
      this._compositeType = CompositeTypes.StatelessFunctional;
    } else {
      if (isPureComponent(Component)) {
        this._compositeType = CompositeTypes.PureClass;
      } else {
        this._compositeType = CompositeTypes.ImpureClass;
      }
    }

    if ("production" !== 'production') {
      // This will throw later in _renderValidatedComponent, but add an early
      // warning now to help debugging
      if (inst.render == null) {
        "production" !== 'production' ? warning(false, '%s(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render`.', Component.displayName || Component.name || 'Component') : void 0;
      }

      var propsMutated = inst.props !== publicProps;
      var componentName = Component.displayName || Component.name || 'Component';

      "production" !== 'production' ? warning(inst.props === undefined || !propsMutated, '%s(...): When calling super() in `%s`, make sure to pass ' + 'up the same props that your component\'s constructor was passed.', componentName, componentName) : void 0;
    }

    // These should be set up in the constructor, but as a convenience for
    // simpler class abstractions, we set them up after the fact.
    inst.props = publicProps;
    inst.context = publicContext;
    inst.refs = emptyObject;
    inst.updater = updateQueue;

    this._instance = inst;

    // Store a reference from the instance back to the internal representation
    ReactInstanceMap.set(inst, this);

    if ("production" !== 'production') {
      // Since plain JS classes are defined without any special initialization
      // logic, we can not catch common errors early. Therefore, we have to
      // catch them here, at initialization time, instead.
      "production" !== 'production' ? warning(!inst.getInitialState || inst.getInitialState.isReactClassApproved, 'getInitialState was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Did you mean to define a state property instead?', this.getName() || 'a component') : void 0;
      "production" !== 'production' ? warning(!inst.getDefaultProps || inst.getDefaultProps.isReactClassApproved, 'getDefaultProps was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Use a static property to define defaultProps instead.', this.getName() || 'a component') : void 0;
      "production" !== 'production' ? warning(!inst.propTypes, 'propTypes was defined as an instance property on %s. Use a static ' + 'property to define propTypes instead.', this.getName() || 'a component') : void 0;
      "production" !== 'production' ? warning(!inst.contextTypes, 'contextTypes was defined as an instance property on %s. Use a ' + 'static property to define contextTypes instead.', this.getName() || 'a component') : void 0;
      "production" !== 'production' ? warning(typeof inst.componentShouldUpdate !== 'function', '%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', this.getName() || 'A component') : void 0;
      "production" !== 'production' ? warning(typeof inst.componentDidUnmount !== 'function', '%s has a method called ' + 'componentDidUnmount(). But there is no such lifecycle method. ' + 'Did you mean componentWillUnmount()?', this.getName() || 'A component') : void 0;
      "production" !== 'production' ? warning(typeof inst.componentWillRecieveProps !== 'function', '%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', this.getName() || 'A component') : void 0;
    }

    var initialState = inst.state;
    if (initialState === undefined) {
      inst.state = initialState = null;
    }
    !(typeof initialState === 'object' && !Array.isArray(initialState)) ? "production" !== 'production' ? invariant(false, '%s.state: must be set to an object or null', this.getName() || 'ReactCompositeComponent') : _prodInvariant('106', this.getName() || 'ReactCompositeComponent') : void 0;

    this._pendingStateQueue = null;
    this._pendingReplaceState = false;
    this._pendingForceUpdate = false;

    var markup;
    if (inst.unstable_handleError) {
      markup = this.performInitialMountWithErrorHandling(renderedElement, hostParent, hostContainerInfo, transaction, context);
    } else {
      markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
    }

    if (inst.componentDidMount) {
      if ("production" !== 'production') {
        transaction.getReactMountReady().enqueue(function () {
          measureLifeCyclePerf(function () {
            return inst.componentDidMount();
          }, _this._debugID, 'componentDidMount');
        });
      } else {
        transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
      }
    }

    return markup;
  },

  _constructComponent: function (doConstruct, publicProps, publicContext, updateQueue) {
    if ("production" !== 'production') {
      ReactCurrentOwner.current = this;
      try {
        return this._constructComponentWithoutOwner(doConstruct, publicProps, publicContext, updateQueue);
      } finally {
        ReactCurrentOwner.current = null;
      }
    } else {
      return this._constructComponentWithoutOwner(doConstruct, publicProps, publicContext, updateQueue);
    }
  },

  _constructComponentWithoutOwner: function (doConstruct, publicProps, publicContext, updateQueue) {
    var Component = this._currentElement.type;

    if (doConstruct) {
      if ("production" !== 'production') {
        return measureLifeCyclePerf(function () {
          return new Component(publicProps, publicContext, updateQueue);
        }, this._debugID, 'ctor');
      } else {
        return new Component(publicProps, publicContext, updateQueue);
      }
    }

    // This can still be an instance in case of factory components
    // but we'll count this as time spent rendering as the more common case.
    if ("production" !== 'production') {
      return measureLifeCyclePerf(function () {
        return Component(publicProps, publicContext, updateQueue);
      }, this._debugID, 'render');
    } else {
      return Component(publicProps, publicContext, updateQueue);
    }
  },

  performInitialMountWithErrorHandling: function (renderedElement, hostParent, hostContainerInfo, transaction, context) {
    var markup;
    var checkpoint = transaction.checkpoint();
    try {
      markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
    } catch (e) {
      // Roll back to checkpoint, handle error (which may add items to the transaction), and take a new checkpoint
      transaction.rollback(checkpoint);
      this._instance.unstable_handleError(e);
      if (this._pendingStateQueue) {
        this._instance.state = this._processPendingState(this._instance.props, this._instance.context);
      }
      checkpoint = transaction.checkpoint();

      this._renderedComponent.unmountComponent(true);
      transaction.rollback(checkpoint);

      // Try again - we've informed the component about the error, so they can render an error message this time.
      // If this throws again, the error will bubble up (and can be caught by a higher error boundary).
      markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
    }
    return markup;
  },

  performInitialMount: function (renderedElement, hostParent, hostContainerInfo, transaction, context) {
    var inst = this._instance;

    var debugID = 0;
    if ("production" !== 'production') {
      debugID = this._debugID;
    }

    if (inst.componentWillMount) {
      if ("production" !== 'production') {
        measureLifeCyclePerf(function () {
          return inst.componentWillMount();
        }, debugID, 'componentWillMount');
      } else {
        inst.componentWillMount();
      }
      // When mounting, calls to `setState` by `componentWillMount` will set
      // `this._pendingStateQueue` without triggering a re-render.
      if (this._pendingStateQueue) {
        inst.state = this._processPendingState(inst.props, inst.context);
      }
    }

    // If not a stateless component, we now render
    if (renderedElement === undefined) {
      renderedElement = this._renderValidatedComponent();
    }

    var nodeType = ReactNodeTypes.getType(renderedElement);
    this._renderedNodeType = nodeType;
    var child = this._instantiateReactComponent(renderedElement, nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
    );
    this._renderedComponent = child;

    var markup = ReactReconciler.mountComponent(child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), debugID);

    if ("production" !== 'production') {
      if (debugID !== 0) {
        var childDebugIDs = child._debugID !== 0 ? [child._debugID] : [];
        ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs);
      }
    }

    return markup;
  },

  getHostNode: function () {
    return ReactReconciler.getHostNode(this._renderedComponent);
  },

  /**
   * Releases any resources allocated by `mountComponent`.
   *
   * @final
   * @internal
   */
  unmountComponent: function (safely) {
    if (!this._renderedComponent) {
      return;
    }

    var inst = this._instance;

    if (inst.componentWillUnmount && !inst._calledComponentWillUnmount) {
      inst._calledComponentWillUnmount = true;

      if (safely) {
        var name = this.getName() + '.componentWillUnmount()';
        ReactErrorUtils.invokeGuardedCallback(name, inst.componentWillUnmount.bind(inst));
      } else {
        if ("production" !== 'production') {
          measureLifeCyclePerf(function () {
            return inst.componentWillUnmount();
          }, this._debugID, 'componentWillUnmount');
        } else {
          inst.componentWillUnmount();
        }
      }
    }

    if (this._renderedComponent) {
      ReactReconciler.unmountComponent(this._renderedComponent, safely);
      this._renderedNodeType = null;
      this._renderedComponent = null;
      this._instance = null;
    }

    // Reset pending fields
    // Even if this component is scheduled for another update in ReactUpdates,
    // it would still be ignored because these fields are reset.
    this._pendingStateQueue = null;
    this._pendingReplaceState = false;
    this._pendingForceUpdate = false;
    this._pendingCallbacks = null;
    this._pendingElement = null;

    // These fields do not really need to be reset since this object is no
    // longer accessible.
    this._context = null;
    this._rootNodeID = 0;
    this._topLevelWrapper = null;

    // Delete the reference from the instance to this internal representation
    // which allow the internals to be properly cleaned up even if the user
    // leaks a reference to the public instance.
    ReactInstanceMap.remove(inst);

    // Some existing components rely on inst.props even after they've been
    // destroyed (in event handlers).
    // TODO: inst.props = null;
    // TODO: inst.state = null;
    // TODO: inst.context = null;
  },

  /**
   * Filters the context object to only contain keys specified in
   * `contextTypes`
   *
   * @param {object} context
   * @return {?object}
   * @private
   */
  _maskContext: function (context) {
    var Component = this._currentElement.type;
    var contextTypes = Component.contextTypes;
    if (!contextTypes) {
      return emptyObject;
    }
    var maskedContext = {};
    for (var contextName in contextTypes) {
      maskedContext[contextName] = context[contextName];
    }
    return maskedContext;
  },

  /**
   * Filters the context object to only contain keys specified in
   * `contextTypes`, and asserts that they are valid.
   *
   * @param {object} context
   * @return {?object}
   * @private
   */
  _processContext: function (context) {
    var maskedContext = this._maskContext(context);
    if ("production" !== 'production') {
      var Component = this._currentElement.type;
      if (Component.contextTypes) {
        this._checkContextTypes(Component.contextTypes, maskedContext, ReactPropTypeLocations.context);
      }
    }
    return maskedContext;
  },

  /**
   * @param {object} currentContext
   * @return {object}
   * @private
   */
  _processChildContext: function (currentContext) {
    var Component = this._currentElement.type;
    var inst = this._instance;
    var childContext;

    if (inst.getChildContext) {
      if ("production" !== 'production') {
        ReactInstrumentation.debugTool.onBeginProcessingChildContext();
        try {
          childContext = inst.getChildContext();
        } finally {
          ReactInstrumentation.debugTool.onEndProcessingChildContext();
        }
      } else {
        childContext = inst.getChildContext();
      }
    }

    if (childContext) {
      !(typeof Component.childContextTypes === 'object') ? "production" !== 'production' ? invariant(false, '%s.getChildContext(): childContextTypes must be defined in order to use getChildContext().', this.getName() || 'ReactCompositeComponent') : _prodInvariant('107', this.getName() || 'ReactCompositeComponent') : void 0;
      if ("production" !== 'production') {
        this._checkContextTypes(Component.childContextTypes, childContext, ReactPropTypeLocations.childContext);
      }
      for (var name in childContext) {
        !(name in Component.childContextTypes) ? "production" !== 'production' ? invariant(false, '%s.getChildContext(): key "%s" is not defined in childContextTypes.', this.getName() || 'ReactCompositeComponent', name) : _prodInvariant('108', this.getName() || 'ReactCompositeComponent', name) : void 0;
      }
      return _assign({}, currentContext, childContext);
    }
    return currentContext;
  },

  /**
   * Assert that the context types are valid
   *
   * @param {object} typeSpecs Map of context field to a ReactPropType
   * @param {object} values Runtime values that need to be type-checked
   * @param {string} location e.g. "prop", "context", "child context"
   * @private
   */
  _checkContextTypes: function (typeSpecs, values, location) {
    checkReactTypeSpec(typeSpecs, values, location, this.getName(), null, this._debugID);
  },

  receiveComponent: function (nextElement, transaction, nextContext) {
    var prevElement = this._currentElement;
    var prevContext = this._context;

    this._pendingElement = null;

    this.updateComponent(transaction, prevElement, nextElement, prevContext, nextContext);
  },

  /**
   * If any of `_pendingElement`, `_pendingStateQueue`, or `_pendingForceUpdate`
   * is set, update the component.
   *
   * @param {ReactReconcileTransaction} transaction
   * @internal
   */
  performUpdateIfNecessary: function (transaction) {
    if (this._pendingElement != null) {
      ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context);
    } else if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
      this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
    } else {
      this._updateBatchNumber = null;
    }
  },

  /**
   * Perform an update to a mounted component. The componentWillReceiveProps and
   * shouldComponentUpdate methods are called, then (assuming the update isn't
   * skipped) the remaining update lifecycle methods are called and the DOM
   * representation is updated.
   *
   * By default, this implements React's rendering and reconciliation algorithm.
   * Sophisticated clients may wish to override this.
   *
   * @param {ReactReconcileTransaction} transaction
   * @param {ReactElement} prevParentElement
   * @param {ReactElement} nextParentElement
   * @internal
   * @overridable
   */
  updateComponent: function (transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext) {
    var inst = this._instance;
    !(inst != null) ? "production" !== 'production' ? invariant(false, 'Attempted to update component `%s` that has already been unmounted (or failed to mount).', this.getName() || 'ReactCompositeComponent') : _prodInvariant('136', this.getName() || 'ReactCompositeComponent') : void 0;

    var willReceive = false;
    var nextContext;

    // Determine if the context has changed or not
    if (this._context === nextUnmaskedContext) {
      nextContext = inst.context;
    } else {
      nextContext = this._processContext(nextUnmaskedContext);
      willReceive = true;
    }

    var prevProps = prevParentElement.props;
    var nextProps = nextParentElement.props;

    // Not a simple state update but a props update
    if (prevParentElement !== nextParentElement) {
      willReceive = true;
    }

    // An update here will schedule an update but immediately set
    // _pendingStateQueue which will ensure that any state updates gets
    // immediately reconciled instead of waiting for the next batch.
    if (willReceive && inst.componentWillReceiveProps) {
      if ("production" !== 'production') {
        measureLifeCyclePerf(function () {
          return inst.componentWillReceiveProps(nextProps, nextContext);
        }, this._debugID, 'componentWillReceiveProps');
      } else {
        inst.componentWillReceiveProps(nextProps, nextContext);
      }
    }

    var nextState = this._processPendingState(nextProps, nextContext);
    var shouldUpdate = true;

    if (!this._pendingForceUpdate) {
      if (inst.shouldComponentUpdate) {
        if ("production" !== 'production') {
          shouldUpdate = measureLifeCyclePerf(function () {
            return inst.shouldComponentUpdate(nextProps, nextState, nextContext);
          }, this._debugID, 'shouldComponentUpdate');
        } else {
          shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
        }
      } else {
        if (this._compositeType === CompositeTypes.PureClass) {
          shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState);
        }
      }
    }

    if ("production" !== 'production') {
      "production" !== 'production' ? warning(shouldUpdate !== undefined, '%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', this.getName() || 'ReactCompositeComponent') : void 0;
    }

    this._updateBatchNumber = null;
    if (shouldUpdate) {
      this._pendingForceUpdate = false;
      // Will set `this.props`, `this.state` and `this.context`.
      this._performComponentUpdate(nextParentElement, nextProps, nextState, nextContext, transaction, nextUnmaskedContext);
    } else {
      // If it's determined that a component should not update, we still want
      // to set props and state but we shortcut the rest of the update.
      this._currentElement = nextParentElement;
      this._context = nextUnmaskedContext;
      inst.props = nextProps;
      inst.state = nextState;
      inst.context = nextContext;
    }
  },

  _processPendingState: function (props, context) {
    var inst = this._instance;
    var queue = this._pendingStateQueue;
    var replace = this._pendingReplaceState;
    this._pendingReplaceState = false;
    this._pendingStateQueue = null;

    if (!queue) {
      return inst.state;
    }

    if (replace && queue.length === 1) {
      return queue[0];
    }

    var nextState = _assign({}, replace ? queue[0] : inst.state);
    for (var i = replace ? 1 : 0; i < queue.length; i++) {
      var partial = queue[i];
      _assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);
    }

    return nextState;
  },

  /**
   * Merges new props and state, notifies delegate methods of update and
   * performs update.
   *
   * @param {ReactElement} nextElement Next element
   * @param {object} nextProps Next public object to set as properties.
   * @param {?object} nextState Next object to set as state.
   * @param {?object} nextContext Next public object to set as context.
   * @param {ReactReconcileTransaction} transaction
   * @param {?object} unmaskedContext
   * @private
   */
  _performComponentUpdate: function (nextElement, nextProps, nextState, nextContext, transaction, unmaskedContext) {
    var _this2 = this;

    var inst = this._instance;

    var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
    var prevProps;
    var prevState;
    var prevContext;
    if (hasComponentDidUpdate) {
      prevProps = inst.props;
      prevState = inst.state;
      prevContext = inst.context;
    }

    if (inst.componentWillUpdate) {
      if ("production" !== 'production') {
        measureLifeCyclePerf(function () {
          return inst.componentWillUpdate(nextProps, nextState, nextContext);
        }, this._debugID, 'componentWillUpdate');
      } else {
        inst.componentWillUpdate(nextProps, nextState, nextContext);
      }
    }

    this._currentElement = nextElement;
    this._context = unmaskedContext;
    inst.props = nextProps;
    inst.state = nextState;
    inst.context = nextContext;

    this._updateRenderedComponent(transaction, unmaskedContext);

    if (hasComponentDidUpdate) {
      if ("production" !== 'production') {
        transaction.getReactMountReady().enqueue(function () {
          measureLifeCyclePerf(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), _this2._debugID, 'componentDidUpdate');
        });
      } else {
        transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), inst);
      }
    }
  },

  /**
   * Call the component's `render` method and update the DOM accordingly.
   *
   * @param {ReactReconcileTransaction} transaction
   * @internal
   */
  _updateRenderedComponent: function (transaction, context) {
    var prevComponentInstance = this._renderedComponent;
    var prevRenderedElement = prevComponentInstance._currentElement;
    var nextRenderedElement = this._renderValidatedComponent();

    var debugID = 0;
    if ("production" !== 'production') {
      debugID = this._debugID;
    }

    if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
      ReactReconciler.receiveComponent(prevComponentInstance, nextRenderedElement, transaction, this._processChildContext(context));
    } else {
      var oldHostNode = ReactReconciler.getHostNode(prevComponentInstance);
      ReactReconciler.unmountComponent(prevComponentInstance, false);

      var nodeType = ReactNodeTypes.getType(nextRenderedElement);
      this._renderedNodeType = nodeType;
      var child = this._instantiateReactComponent(nextRenderedElement, nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
      );
      this._renderedComponent = child;

      var nextMarkup = ReactReconciler.mountComponent(child, transaction, this._hostParent, this._hostContainerInfo, this._processChildContext(context), debugID);

      if ("production" !== 'production') {
        if (debugID !== 0) {
          var childDebugIDs = child._debugID !== 0 ? [child._debugID] : [];
          ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs);
        }
      }

      this._replaceNodeWithMarkup(oldHostNode, nextMarkup, prevComponentInstance);
    }
  },

  /**
   * Overridden in shallow rendering.
   *
   * @protected
   */
  _replaceNodeWithMarkup: function (oldHostNode, nextMarkup, prevInstance) {
    ReactComponentEnvironment.replaceNodeWithMarkup(oldHostNode, nextMarkup, prevInstance);
  },

  /**
   * @protected
   */
  _renderValidatedComponentWithoutOwnerOrContext: function () {
    var inst = this._instance;
    var renderedComponent;

    if ("production" !== 'production') {
      renderedComponent = measureLifeCyclePerf(function () {
        return inst.render();
      }, this._debugID, 'render');
    } else {
      renderedComponent = inst.render();
    }

    if ("production" !== 'production') {
      // We allow auto-mocks to proceed as if they're returning null.
      if (renderedComponent === undefined && inst.render._isMockFunction) {
        // This is probably bad practice. Consider warning here and
        // deprecating this convenience.
        renderedComponent = null;
      }
    }

    return renderedComponent;
  },

  /**
   * @private
   */
  _renderValidatedComponent: function () {
    var renderedComponent;
    if ("production" !== 'production' || this._compositeType !== CompositeTypes.StatelessFunctional) {
      ReactCurrentOwner.current = this;
      try {
        renderedComponent = this._renderValidatedComponentWithoutOwnerOrContext();
      } finally {
        ReactCurrentOwner.current = null;
      }
    } else {
      renderedComponent = this._renderValidatedComponentWithoutOwnerOrContext();
    }
    !(
    // TODO: An `isValidNode` function would probably be more appropriate
    renderedComponent === null || renderedComponent === false || ReactElement.isValidElement(renderedComponent)) ? "production" !== 'production' ? invariant(false, '%s.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.', this.getName() || 'ReactCompositeComponent') : _prodInvariant('109', this.getName() || 'ReactCompositeComponent') : void 0;

    return renderedComponent;
  },

  /**
   * Lazily allocates the refs object and stores `component` as `ref`.
   *
   * @param {string} ref Reference name.
   * @param {component} component Component to store as `ref`.
   * @final
   * @private
   */
  attachRef: function (ref, component) {
    var inst = this.getPublicInstance();
    !(inst != null) ? "production" !== 'production' ? invariant(false, 'Stateless function components cannot have refs.') : _prodInvariant('110') : void 0;
    var publicComponentInstance = component.getPublicInstance();
    if ("production" !== 'production') {
      var componentName = component && component.getName ? component.getName() : 'a component';
      "production" !== 'production' ? warning(publicComponentInstance != null || component._compositeType !== CompositeTypes.StatelessFunctional, 'Stateless function components cannot be given refs ' + '(See ref "%s" in %s created by %s). ' + 'Attempts to access this ref will fail.', ref, componentName, this.getName()) : void 0;
    }
    var refs = inst.refs === emptyObject ? inst.refs = {} : inst.refs;
    refs[ref] = publicComponentInstance;
  },

  /**
   * Detaches a reference name.
   *
   * @param {string} ref Name to dereference.
   * @final
   * @private
   */
  detachRef: function (ref) {
    var refs = this.getPublicInstance().refs;
    delete refs[ref];
  },

  /**
   * Get a text description of the component that can be used to identify it
   * in error messages.
   * @return {string} The name or null.
   * @internal
   */
  getName: function () {
    var type = this._currentElement.type;
    var constructor = this._instance && this._instance.constructor;
    return type.displayName || constructor && constructor.displayName || type.name || constructor && constructor.name || null;
  },

  /**
   * Get the publicly accessible representation of this component - i.e. what
   * is exposed by refs and returned by render. Can be null for stateless
   * components.
   *
   * @return {ReactComponent} the public component instance.
   * @internal
   */
  getPublicInstance: function () {
    var inst = this._instance;
    if (this._compositeType === CompositeTypes.StatelessFunctional) {
      return null;
    }
    return inst;
  },

  // Stub
  _instantiateReactComponent: null

};

var ReactCompositeComponent = {

  Mixin: ReactCompositeComponentMixin

};

module.exports = ReactCompositeComponent;
},{"./ReactComponentEnvironment":74,"./ReactCurrentOwner":78,"./ReactElement":101,"./ReactErrorUtils":104,"./ReactInstanceMap":112,"./ReactInstrumentation":113,"./ReactNodeTypes":119,"./ReactPropTypeLocations":123,"./ReactReconciler":128,"./checkReactTypeSpec":159,"./reactProdInvariant":180,"./shouldUpdateReactComponent":185,"fbjs/lib/emptyObject":197,"fbjs/lib/invariant":204,"fbjs/lib/shallowEqual":212,"fbjs/lib/warning":213,"object-assign":214}],78:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactCurrentOwner
 */

'use strict';

/**
 * Keeps track of the current owner.
 *
 * The current owner is the component who should own any components that are
 * currently being constructed.
 */

var ReactCurrentOwner = {

  /**
   * @internal
   * @type {ReactComponent}
   */
  current: null

};

module.exports = ReactCurrentOwner;
},{}],79:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOM
 */

/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__*/

'use strict';

var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactDefaultInjection = require('./ReactDefaultInjection');
var ReactMount = require('./ReactMount');
var ReactReconciler = require('./ReactReconciler');
var ReactUpdates = require('./ReactUpdates');
var ReactVersion = require('./ReactVersion');

var findDOMNode = require('./findDOMNode');
var getHostComponentFromComposite = require('./getHostComponentFromComposite');
var renderSubtreeIntoContainer = require('./renderSubtreeIntoContainer');
var warning = require('fbjs/lib/warning');

ReactDefaultInjection.inject();

var ReactDOM = {
  findDOMNode: findDOMNode,
  render: ReactMount.render,
  unmountComponentAtNode: ReactMount.unmountComponentAtNode,
  version: ReactVersion,

  /* eslint-disable camelcase */
  unstable_batchedUpdates: ReactUpdates.batchedUpdates,
  unstable_renderSubtreeIntoContainer: renderSubtreeIntoContainer
};

// Inject the runtime into a devtools global hook regardless of browser.
// Allows for debugging when the hook is injected on the page.
/* eslint-enable camelcase */
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') {
  __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
    ComponentTree: {
      getClosestInstanceFromNode: ReactDOMComponentTree.getClosestInstanceFromNode,
      getNodeFromInstance: function (inst) {
        // inst is an internal instance (but could be a composite)
        if (inst._renderedComponent) {
          inst = getHostComponentFromComposite(inst);
        }
        if (inst) {
          return ReactDOMComponentTree.getNodeFromInstance(inst);
        } else {
          return null;
        }
      }
    },
    Mount: ReactMount,
    Reconciler: ReactReconciler
  });
}

if ("production" !== 'production') {
  var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
  if (ExecutionEnvironment.canUseDOM && window.top === window.self) {

    // First check if devtools is not installed
    if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
      // If we're in Chrome or Firefox, provide a download link if not installed.
      if (navigator.userAgent.indexOf('Chrome') > -1 && navigator.userAgent.indexOf('Edge') === -1 || navigator.userAgent.indexOf('Firefox') > -1) {
        // Firefox does not have the issue with devtools loaded over file://
        var showFileUrlMessage = window.location.protocol.indexOf('http') === -1 && navigator.userAgent.indexOf('Firefox') === -1;
        console.debug('Download the React DevTools ' + (showFileUrlMessage ? 'and use an HTTP server (instead of a file: URL) ' : '') + 'for a better development experience: ' + 'https://fb.me/react-devtools');
      }
    }

    var testFunc = function testFn() {};
    "production" !== 'production' ? warning((testFunc.name || testFunc.toString()).indexOf('testFn') !== -1, 'It looks like you\'re using a minified copy of the development build ' + 'of React. When deploying React apps to production, make sure to use ' + 'the production build which skips development warnings and is faster. ' + 'See https://fb.me/react-minification for more details.') : void 0;

    // If we're in IE8, check to see if we are in compatibility mode and provide
    // information on preventing compatibility mode
    var ieCompatibilityMode = document.documentMode && document.documentMode < 8;

    "production" !== 'production' ? warning(!ieCompatibilityMode, 'Internet Explorer is running in compatibility mode; please add the ' + 'following tag to your HTML to prevent this from happening: ' + '<meta http-equiv="X-UA-Compatible" content="IE=edge" />') : void 0;

    var expectedFeatures = [
    // shims
    Array.isArray, Array.prototype.every, Array.prototype.forEach, Array.prototype.indexOf, Array.prototype.map, Date.now, Function.prototype.bind, Object.keys, String.prototype.split, String.prototype.trim];

    for (var i = 0; i < expectedFeatures.length; i++) {
      if (!expectedFeatures[i]) {
        "production" !== 'production' ? warning(false, 'One or more ES5 shims expected by React are not available: ' + 'https://fb.me/react-warning-polyfills') : void 0;
        break;
      }
    }
  }
}

if ("production" !== 'production') {
  var ReactInstrumentation = require('./ReactInstrumentation');
  var ReactDOMUnknownPropertyHook = require('./ReactDOMUnknownPropertyHook');
  var ReactDOMNullInputValuePropHook = require('./ReactDOMNullInputValuePropHook');

  ReactInstrumentation.debugTool.addHook(ReactDOMUnknownPropertyHook);
  ReactInstrumentation.debugTool.addHook(ReactDOMNullInputValuePropHook);
}

module.exports = ReactDOM;
},{"./ReactDOMComponentTree":83,"./ReactDOMNullInputValuePropHook":90,"./ReactDOMUnknownPropertyHook":97,"./ReactDefaultInjection":100,"./ReactInstrumentation":113,"./ReactMount":116,"./ReactReconciler":128,"./ReactUpdates":136,"./ReactVersion":137,"./findDOMNode":163,"./getHostComponentFromComposite":170,"./renderSubtreeIntoContainer":181,"fbjs/lib/ExecutionEnvironment":190,"fbjs/lib/warning":213}],80:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMButton
 */

'use strict';

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

/**
 * Implements a <button> host component that does not receive mouse events
 * when `disabled` is set.
 */
var ReactDOMButton = {
  getHostProps: DisabledInputUtils.getHostProps
};

module.exports = ReactDOMButton;
},{"./DisabledInputUtils":52}],81:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMComponent
 */

/* global hasOwnProperty:true */

'use strict';

var _prodInvariant = require('./reactProdInvariant'),
    _assign = require('object-assign');

var AutoFocusUtils = require('./AutoFocusUtils');
var CSSPropertyOperations = require('./CSSPropertyOperations');
var DOMLazyTree = require('./DOMLazyTree');
var DOMNamespaces = require('./DOMNamespaces');
var DOMProperty = require('./DOMProperty');
var DOMPropertyOperations = require('./DOMPropertyOperations');
var EventConstants = require('./EventConstants');
var EventPluginHub = require('./EventPluginHub');
var EventPluginRegistry = require('./EventPluginRegistry');
var ReactBrowserEventEmitter = require('./ReactBrowserEventEmitter');
var ReactDOMButton = require('./ReactDOMButton');
var ReactDOMComponentFlags = require('./ReactDOMComponentFlags');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactDOMInput = require('./ReactDOMInput');
var ReactDOMOption = require('./ReactDOMOption');
var ReactDOMSelect = require('./ReactDOMSelect');
var ReactDOMTextarea = require('./ReactDOMTextarea');
var ReactInstrumentation = require('./ReactInstrumentation');
var ReactMultiChild = require('./ReactMultiChild');
var ReactServerRenderingTransaction = require('./ReactServerRenderingTransaction');

var emptyFunction = require('fbjs/lib/emptyFunction');
var escapeTextContentForBrowser = require('./escapeTextContentForBrowser');
var invariant = require('fbjs/lib/invariant');
var isEventSupported = require('./isEventSupported');
var keyOf = require('fbjs/lib/keyOf');
var shallowEqual = require('fbjs/lib/shallowEqual');
var validateDOMNesting = require('./validateDOMNesting');
var warning = require('fbjs/lib/warning');

var Flags = ReactDOMComponentFlags;
var deleteListener = EventPluginHub.deleteListener;
var getNode = ReactDOMComponentTree.getNodeFromInstance;
var listenTo = ReactBrowserEventEmitter.listenTo;
var registrationNameModules = EventPluginRegistry.registrationNameModules;

// For quickly matching children type, to test if can be treated as content.
var CONTENT_TYPES = { 'string': true, 'number': true };

var STYLE = keyOf({ style: null });
var HTML = keyOf({ __html: null });
var RESERVED_PROPS = {
  children: null,
  dangerouslySetInnerHTML: null,
  suppressContentEditableWarning: null
};

// Node type for document fragments (Node.DOCUMENT_FRAGMENT_NODE).
var DOC_FRAGMENT_TYPE = 11;

function getDeclarationErrorAddendum(internalInstance) {
  if (internalInstance) {
    var owner = internalInstance._currentElement._owner || null;
    if (owner) {
      var name = owner.getName();
      if (name) {
        return ' This DOM node was rendered by `' + name + '`.';
      }
    }
  }
  return '';
}

function friendlyStringify(obj) {
  if (typeof obj === 'object') {
    if (Array.isArray(obj)) {
      return '[' + obj.map(friendlyStringify).join(', ') + ']';
    } else {
      var pairs = [];
      for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
          var keyEscaped = /^[a-z$_][\w$_]*$/i.test(key) ? key : JSON.stringify(key);
          pairs.push(keyEscaped + ': ' + friendlyStringify(obj[key]));
        }
      }
      return '{' + pairs.join(', ') + '}';
    }
  } else if (typeof obj === 'string') {
    return JSON.stringify(obj);
  } else if (typeof obj === 'function') {
    return '[function object]';
  }
  // Differs from JSON.stringify in that undefined because undefined and that
  // inf and nan don't become null
  return String(obj);
}

var styleMutationWarning = {};

function checkAndWarnForMutatedStyle(style1, style2, component) {
  if (style1 == null || style2 == null) {
    return;
  }
  if (shallowEqual(style1, style2)) {
    return;
  }

  var componentName = component._tag;
  var owner = component._currentElement._owner;
  var ownerName;
  if (owner) {
    ownerName = owner.getName();
  }

  var hash = ownerName + '|' + componentName;

  if (styleMutationWarning.hasOwnProperty(hash)) {
    return;
  }

  styleMutationWarning[hash] = true;

  "production" !== 'production' ? warning(false, '`%s` was passed a style object that has previously been mutated. ' + 'Mutating `style` is deprecated. Consider cloning it beforehand. Check ' + 'the `render` %s. Previous style: %s. Mutated style: %s.', componentName, owner ? 'of `' + ownerName + '`' : 'using <' + componentName + '>', friendlyStringify(style1), friendlyStringify(style2)) : void 0;
}

/**
 * @param {object} component
 * @param {?object} props
 */
function assertValidProps(component, props) {
  if (!props) {
    return;
  }
  // Note the use of `==` which checks for null or undefined.
  if (voidElementTags[component._tag]) {
    !(props.children == null && props.dangerouslySetInnerHTML == null) ? "production" !== 'production' ? invariant(false, '%s is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.%s', component._tag, component._currentElement._owner ? ' Check the render method of ' + component._currentElement._owner.getName() + '.' : '') : _prodInvariant('137', component._tag, component._currentElement._owner ? ' Check the render method of ' + component._currentElement._owner.getName() + '.' : '') : void 0;
  }
  if (props.dangerouslySetInnerHTML != null) {
    !(props.children == null) ? "production" !== 'production' ? invariant(false, 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.') : _prodInvariant('60') : void 0;
    !(typeof props.dangerouslySetInnerHTML === 'object' && HTML in props.dangerouslySetInnerHTML) ? "production" !== 'production' ? invariant(false, '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. Please visit https://fb.me/react-invariant-dangerously-set-inner-html for more information.') : _prodInvariant('61') : void 0;
  }
  if ("production" !== 'production') {
    "production" !== 'production' ? warning(props.innerHTML == null, 'Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.') : void 0;
    "production" !== 'production' ? warning(props.suppressContentEditableWarning || !props.contentEditable || props.children == null, 'A component is `contentEditable` and contains `children` managed by ' + 'React. It is now your responsibility to guarantee that none of ' + 'those nodes are unexpectedly modified or duplicated. This is ' + 'probably not intentional.') : void 0;
    "production" !== 'production' ? warning(props.onFocusIn == null && props.onFocusOut == null, 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut. ' + 'All React events are normalized to bubble, so onFocusIn and onFocusOut ' + 'are not needed/supported by React.') : void 0;
  }
  !(props.style == null || typeof props.style === 'object') ? "production" !== 'production' ? invariant(false, 'The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + \'em\'}} when using JSX.%s', getDeclarationErrorAddendum(component)) : _prodInvariant('62', getDeclarationErrorAddendum(component)) : void 0;
}

function enqueuePutListener(inst, registrationName, listener, transaction) {
  if (transaction instanceof ReactServerRenderingTransaction) {
    return;
  }
  if ("production" !== 'production') {
    // IE8 has no API for event capturing and the `onScroll` event doesn't
    // bubble.
    "production" !== 'production' ? warning(registrationName !== 'onScroll' || isEventSupported('scroll', true), 'This browser doesn\'t support the `onScroll` event') : void 0;
  }
  var containerInfo = inst._hostContainerInfo;
  var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;
  var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument;
  listenTo(registrationName, doc);
  transaction.getReactMountReady().enqueue(putListener, {
    inst: inst,
    registrationName: registrationName,
    listener: listener
  });
}

function putListener() {
  var listenerToPut = this;
  EventPluginHub.putListener(listenerToPut.inst, listenerToPut.registrationName, listenerToPut.listener);
}

function inputPostMount() {
  var inst = this;
  ReactDOMInput.postMountWrapper(inst);
}

function textareaPostMount() {
  var inst = this;
  ReactDOMTextarea.postMountWrapper(inst);
}

function optionPostMount() {
  var inst = this;
  ReactDOMOption.postMountWrapper(inst);
}

var setAndValidateContentChildDev = emptyFunction;
if ("production" !== 'production') {
  setAndValidateContentChildDev = function (content) {
    var hasExistingContent = this._contentDebugID != null;
    var debugID = this._debugID;
    // This ID represents the inlined child that has no backing instance:
    var contentDebugID = -debugID;

    if (content == null) {
      if (hasExistingContent) {
        ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID);
      }
      this._contentDebugID = null;
      return;
    }

    validateDOMNesting(null, String(content), this, this._ancestorInfo);
    this._contentDebugID = contentDebugID;
    if (hasExistingContent) {
      ReactInstrumentation.debugTool.onBeforeUpdateComponent(contentDebugID, content);
      ReactInstrumentation.debugTool.onUpdateComponent(contentDebugID);
    } else {
      ReactInstrumentation.debugTool.onBeforeMountComponent(contentDebugID, content, debugID);
      ReactInstrumentation.debugTool.onMountComponent(contentDebugID);
      ReactInstrumentation.debugTool.onSetChildren(debugID, [contentDebugID]);
    }
  };
}

// There are so many media events, it makes sense to just
// maintain a list rather than create a `trapBubbledEvent` for each
var mediaEvents = {
  topAbort: 'abort',
  topCanPlay: 'canplay',
  topCanPlayThrough: 'canplaythrough',
  topDurationChange: 'durationchange',
  topEmptied: 'emptied',
  topEncrypted: 'encrypted',
  topEnded: 'ended',
  topError: 'error',
  topLoadedData: 'loadeddata',
  topLoadedMetadata: 'loadedmetadata',
  topLoadStart: 'loadstart',
  topPause: 'pause',
  topPlay: 'play',
  topPlaying: 'playing',
  topProgress: 'progress',
  topRateChange: 'ratechange',
  topSeeked: 'seeked',
  topSeeking: 'seeking',
  topStalled: 'stalled',
  topSuspend: 'suspend',
  topTimeUpdate: 'timeupdate',
  topVolumeChange: 'volumechange',
  topWaiting: 'waiting'
};

function trapBubbledEventsLocal() {
  var inst = this;
  // If a component renders to null or if another component fatals and causes
  // the state of the tree to be corrupted, `node` here can be null.
  !inst._rootNodeID ? "production" !== 'production' ? invariant(false, 'Must be mounted to trap events') : _prodInvariant('63') : void 0;
  var node = getNode(inst);
  !node ? "production" !== 'production' ? invariant(false, 'trapBubbledEvent(...): Requires node to be rendered.') : _prodInvariant('64') : void 0;

  switch (inst._tag) {
    case 'iframe':
    case 'object':
      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes.topLoad, 'load', node)];
      break;
    case 'video':
    case 'audio':

      inst._wrapperState.listeners = [];
      // Create listener for each media event
      for (var event in mediaEvents) {
        if (mediaEvents.hasOwnProperty(event)) {
          inst._wrapperState.listeners.push(ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes[event], mediaEvents[event], node));
        }
      }
      break;
    case 'source':
      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes.topError, 'error', node)];
      break;
    case 'img':
      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes.topError, 'error', node), ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes.topLoad, 'load', node)];
      break;
    case 'form':
      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes.topReset, 'reset', node), ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes.topSubmit, 'submit', node)];
      break;
    case 'input':
    case 'select':
    case 'textarea':
      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes.topInvalid, 'invalid', node)];
      break;
  }
}

function postUpdateSelectWrapper() {
  ReactDOMSelect.postUpdateWrapper(this);
}

// For HTML, certain tags should omit their close tag. We keep a whitelist for
// those special-case tags.

var omittedCloseTags = {
  'area': true,
  'base': true,
  'br': true,
  'col': true,
  'embed': true,
  'hr': true,
  'img': true,
  'input': true,
  'keygen': true,
  'link': true,
  'meta': true,
  'param': true,
  'source': true,
  'track': true,
  'wbr': true
};

// NOTE: menuitem's close tag should be omitted, but that causes problems.
var newlineEatingTags = {
  'listing': true,
  'pre': true,
  'textarea': true
};

// For HTML, certain tags cannot have children. This has the same purpose as
// `omittedCloseTags` except that `menuitem` should still have its closing tag.

var voidElementTags = _assign({
  'menuitem': true
}, omittedCloseTags);

// We accept any tag to be rendered but since this gets injected into arbitrary
// HTML, we want to make sure that it's a safe tag.
// http://www.w3.org/TR/REC-xml/#NT-Name

var VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/; // Simplified subset
var validatedTagCache = {};
var hasOwnProperty = {}.hasOwnProperty;

function validateDangerousTag(tag) {
  if (!hasOwnProperty.call(validatedTagCache, tag)) {
    !VALID_TAG_REGEX.test(tag) ? "production" !== 'production' ? invariant(false, 'Invalid tag: %s', tag) : _prodInvariant('65', tag) : void 0;
    validatedTagCache[tag] = true;
  }
}

function isCustomComponent(tagName, props) {
  return tagName.indexOf('-') >= 0 || props.is != null;
}

var globalIdCounter = 1;

/**
 * Creates a new React class that is idempotent and capable of containing other
 * React components. It accepts event listeners and DOM properties that are
 * valid according to `DOMProperty`.
 *
 *  - Event listeners: `onClick`, `onMouseDown`, etc.
 *  - DOM properties: `className`, `name`, `title`, etc.
 *
 * The `style` property functions differently from the DOM API. It accepts an
 * object mapping of style properties to values.
 *
 * @constructor ReactDOMComponent
 * @extends ReactMultiChild
 */
function ReactDOMComponent(element) {
  var tag = element.type;
  validateDangerousTag(tag);
  this._currentElement = element;
  this._tag = tag.toLowerCase();
  this._namespaceURI = null;
  this._renderedChildren = null;
  this._previousStyle = null;
  this._previousStyleCopy = null;
  this._hostNode = null;
  this._hostParent = null;
  this._rootNodeID = 0;
  this._domID = 0;
  this._hostContainerInfo = null;
  this._wrapperState = null;
  this._topLevelWrapper = null;
  this._flags = 0;
  if ("production" !== 'production') {
    this._ancestorInfo = null;
    setAndValidateContentChildDev.call(this, null);
  }
}

ReactDOMComponent.displayName = 'ReactDOMComponent';

ReactDOMComponent.Mixin = {

  /**
   * Generates root tag markup then recurses. This method has side effects and
   * is not idempotent.
   *
   * @internal
   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
   * @param {?ReactDOMComponent} the parent component instance
   * @param {?object} info about the host container
   * @param {object} context
   * @return {string} The computed markup.
   */
  mountComponent: function (transaction, hostParent, hostContainerInfo, context) {
    this._rootNodeID = globalIdCounter++;
    this._domID = hostContainerInfo._idCounter++;
    this._hostParent = hostParent;
    this._hostContainerInfo = hostContainerInfo;

    var props = this._currentElement.props;

    switch (this._tag) {
      case 'audio':
      case 'form':
      case 'iframe':
      case 'img':
      case 'link':
      case 'object':
      case 'source':
      case 'video':
        this._wrapperState = {
          listeners: null
        };
        transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
        break;
      case 'button':
        props = ReactDOMButton.getHostProps(this, props, hostParent);
        break;
      case 'input':
        ReactDOMInput.mountWrapper(this, props, hostParent);
        props = ReactDOMInput.getHostProps(this, props);
        transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
        break;
      case 'option':
        ReactDOMOption.mountWrapper(this, props, hostParent);
        props = ReactDOMOption.getHostProps(this, props);
        break;
      case 'select':
        ReactDOMSelect.mountWrapper(this, props, hostParent);
        props = ReactDOMSelect.getHostProps(this, props);
        transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
        break;
      case 'textarea':
        ReactDOMTextarea.mountWrapper(this, props, hostParent);
        props = ReactDOMTextarea.getHostProps(this, props);
        transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
        break;
    }

    assertValidProps(this, props);

    // We create tags in the namespace of their parent container, except HTML
    // tags get no namespace.
    var namespaceURI;
    var parentTag;
    if (hostParent != null) {
      namespaceURI = hostParent._namespaceURI;
      parentTag = hostParent._tag;
    } else if (hostContainerInfo._tag) {
      namespaceURI = hostContainerInfo._namespaceURI;
      parentTag = hostContainerInfo._tag;
    }
    if (namespaceURI == null || namespaceURI === DOMNamespaces.svg && parentTag === 'foreignobject') {
      namespaceURI = DOMNamespaces.html;
    }
    if (namespaceURI === DOMNamespaces.html) {
      if (this._tag === 'svg') {
        namespaceURI = DOMNamespaces.svg;
      } else if (this._tag === 'math') {
        namespaceURI = DOMNamespaces.mathml;
      }
    }
    this._namespaceURI = namespaceURI;

    if ("production" !== 'production') {
      var parentInfo;
      if (hostParent != null) {
        parentInfo = hostParent._ancestorInfo;
      } else if (hostContainerInfo._tag) {
        parentInfo = hostContainerInfo._ancestorInfo;
      }
      if (parentInfo) {
        // parentInfo should always be present except for the top-level
        // component when server rendering
        validateDOMNesting(this._tag, null, this, parentInfo);
      }
      this._ancestorInfo = validateDOMNesting.updatedAncestorInfo(parentInfo, this._tag, this);
    }

    var mountImage;
    if (transaction.useCreateElement) {
      var ownerDocument = hostContainerInfo._ownerDocument;
      var el;
      if (namespaceURI === DOMNamespaces.html) {
        if (this._tag === 'script') {
          // Create the script via .innerHTML so its "parser-inserted" flag is
          // set to true and it does not execute
          var div = ownerDocument.createElement('div');
          var type = this._currentElement.type;
          div.innerHTML = '<' + type + '></' + type + '>';
          el = div.removeChild(div.firstChild);
        } else if (props.is) {
          el = ownerDocument.createElement(this._currentElement.type, props.is);
        } else {
          // Separate else branch instead of using `props.is || undefined` above becuase of a Firefox bug.
          // See discussion in https://github.com/facebook/react/pull/6896
          // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240
          el = ownerDocument.createElement(this._currentElement.type);
        }
      } else {
        el = ownerDocument.createElementNS(namespaceURI, this._currentElement.type);
      }
      ReactDOMComponentTree.precacheNode(this, el);
      this._flags |= Flags.hasCachedChildNodes;
      if (!this._hostParent) {
        DOMPropertyOperations.setAttributeForRoot(el);
      }
      this._updateDOMProperties(null, props, transaction);
      var lazyTree = DOMLazyTree(el);
      this._createInitialChildren(transaction, props, context, lazyTree);
      mountImage = lazyTree;
    } else {
      var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction, props);
      var tagContent = this._createContentMarkup(transaction, props, context);
      if (!tagContent && omittedCloseTags[this._tag]) {
        mountImage = tagOpen + '/>';
      } else {
        mountImage = tagOpen + '>' + tagContent + '</' + this._currentElement.type + '>';
      }
    }

    switch (this._tag) {
      case 'input':
        transaction.getReactMountReady().enqueue(inputPostMount, this);
        if (props.autoFocus) {
          transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);
        }
        break;
      case 'textarea':
        transaction.getReactMountReady().enqueue(textareaPostMount, this);
        if (props.autoFocus) {
          transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);
        }
        break;
      case 'select':
        if (props.autoFocus) {
          transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);
        }
        break;
      case 'button':
        if (props.autoFocus) {
          transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);
        }
        break;
      case 'option':
        transaction.getReactMountReady().enqueue(optionPostMount, this);
        break;
    }

    return mountImage;
  },

  /**
   * Creates markup for the open tag and all attributes.
   *
   * This method has side effects because events get registered.
   *
   * Iterating over object properties is faster than iterating over arrays.
   * @see http://jsperf.com/obj-vs-arr-iteration
   *
   * @private
   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
   * @param {object} props
   * @return {string} Markup of opening tag.
   */
  _createOpenTagMarkupAndPutListeners: function (transaction, props) {
    var ret = '<' + this._currentElement.type;

    for (var propKey in props) {
      if (!props.hasOwnProperty(propKey)) {
        continue;
      }
      var propValue = props[propKey];
      if (propValue == null) {
        continue;
      }
      if (registrationNameModules.hasOwnProperty(propKey)) {
        if (propValue) {
          enqueuePutListener(this, propKey, propValue, transaction);
        }
      } else {
        if (propKey === STYLE) {
          if (propValue) {
            if ("production" !== 'production') {
              // See `_updateDOMProperties`. style block
              this._previousStyle = propValue;
            }
            propValue = this._previousStyleCopy = _assign({}, props.style);
          }
          propValue = CSSPropertyOperations.createMarkupForStyles(propValue, this);
        }
        var markup = null;
        if (this._tag != null && isCustomComponent(this._tag, props)) {
          if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
            markup = DOMPropertyOperations.createMarkupForCustomAttribute(propKey, propValue);
          }
        } else {
          markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue);
        }
        if (markup) {
          ret += ' ' + markup;
        }
      }
    }

    // For static pages, no need to put React ID and checksum. Saves lots of
    // bytes.
    if (transaction.renderToStaticMarkup) {
      return ret;
    }

    if (!this._hostParent) {
      ret += ' ' + DOMPropertyOperations.createMarkupForRoot();
    }
    ret += ' ' + DOMPropertyOperations.createMarkupForID(this._domID);
    return ret;
  },

  /**
   * Creates markup for the content between the tags.
   *
   * @private
   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
   * @param {object} props
   * @param {object} context
   * @return {string} Content markup.
   */
  _createContentMarkup: function (transaction, props, context) {
    var ret = '';

    // Intentional use of != to avoid catching zero/false.
    var innerHTML = props.dangerouslySetInnerHTML;
    if (innerHTML != null) {
      if (innerHTML.__html != null) {
        ret = innerHTML.__html;
      }
    } else {
      var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null;
      var childrenToUse = contentToUse != null ? null : props.children;
      if (contentToUse != null) {
        // TODO: Validate that text is allowed as a child of this node
        ret = escapeTextContentForBrowser(contentToUse);
        if ("production" !== 'production') {
          setAndValidateContentChildDev.call(this, contentToUse);
        }
      } else if (childrenToUse != null) {
        var mountImages = this.mountChildren(childrenToUse, transaction, context);
        ret = mountImages.join('');
      }
    }
    if (newlineEatingTags[this._tag] && ret.charAt(0) === '\n') {
      // text/html ignores the first character in these tags if it's a newline
      // Prefer to break application/xml over text/html (for now) by adding
      // a newline specifically to get eaten by the parser. (Alternately for
      // textareas, replacing "^\n" with "\r\n" doesn't get eaten, and the first
      // \r is normalized out by HTMLTextAreaElement#value.)
      // See: <http://www.w3.org/TR/html-polyglot/#newlines-in-textarea-and-pre>
      // See: <http://www.w3.org/TR/html5/syntax.html#element-restrictions>
      // See: <http://www.w3.org/TR/html5/syntax.html#newlines>
      // See: Parsing of "textarea" "listing" and "pre" elements
      //  from <http://www.w3.org/TR/html5/syntax.html#parsing-main-inbody>
      return '\n' + ret;
    } else {
      return ret;
    }
  },

  _createInitialChildren: function (transaction, props, context, lazyTree) {
    // Intentional use of != to avoid catching zero/false.
    var innerHTML = props.dangerouslySetInnerHTML;
    if (innerHTML != null) {
      if (innerHTML.__html != null) {
        DOMLazyTree.queueHTML(lazyTree, innerHTML.__html);
      }
    } else {
      var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null;
      var childrenToUse = contentToUse != null ? null : props.children;
      if (contentToUse != null) {
        // TODO: Validate that text is allowed as a child of this node
        if ("production" !== 'production') {
          setAndValidateContentChildDev.call(this, contentToUse);
        }
        DOMLazyTree.queueText(lazyTree, contentToUse);
      } else if (childrenToUse != null) {
        var mountImages = this.mountChildren(childrenToUse, transaction, context);
        for (var i = 0; i < mountImages.length; i++) {
          DOMLazyTree.queueChild(lazyTree, mountImages[i]);
        }
      }
    }
  },

  /**
   * Receives a next element and updates the component.
   *
   * @internal
   * @param {ReactElement} nextElement
   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
   * @param {object} context
   */
  receiveComponent: function (nextElement, transaction, context) {
    var prevElement = this._currentElement;
    this._currentElement = nextElement;
    this.updateComponent(transaction, prevElement, nextElement, context);
  },

  /**
   * Updates a DOM component after it has already been allocated and
   * attached to the DOM. Reconciles the root DOM node, then recurses.
   *
   * @param {ReactReconcileTransaction} transaction
   * @param {ReactElement} prevElement
   * @param {ReactElement} nextElement
   * @internal
   * @overridable
   */
  updateComponent: function (transaction, prevElement, nextElement, context) {
    var lastProps = prevElement.props;
    var nextProps = this._currentElement.props;

    switch (this._tag) {
      case 'button':
        lastProps = ReactDOMButton.getHostProps(this, lastProps);
        nextProps = ReactDOMButton.getHostProps(this, nextProps);
        break;
      case 'input':
        lastProps = ReactDOMInput.getHostProps(this, lastProps);
        nextProps = ReactDOMInput.getHostProps(this, nextProps);
        break;
      case 'option':
        lastProps = ReactDOMOption.getHostProps(this, lastProps);
        nextProps = ReactDOMOption.getHostProps(this, nextProps);
        break;
      case 'select':
        lastProps = ReactDOMSelect.getHostProps(this, lastProps);
        nextProps = ReactDOMSelect.getHostProps(this, nextProps);
        break;
      case 'textarea':
        lastProps = ReactDOMTextarea.getHostProps(this, lastProps);
        nextProps = ReactDOMTextarea.getHostProps(this, nextProps);
        break;
    }

    assertValidProps(this, nextProps);
    this._updateDOMProperties(lastProps, nextProps, transaction);
    this._updateDOMChildren(lastProps, nextProps, transaction, context);

    switch (this._tag) {
      case 'input':
        // Update the wrapper around inputs *after* updating props. This has to
        // happen after `_updateDOMProperties`. Otherwise HTML5 input validations
        // raise warnings and prevent the new value from being assigned.
        ReactDOMInput.updateWrapper(this);
        break;
      case 'textarea':
        ReactDOMTextarea.updateWrapper(this);
        break;
      case 'select':
        // <select> value update needs to occur after <option> children
        // reconciliation
        transaction.getReactMountReady().enqueue(postUpdateSelectWrapper, this);
        break;
    }
  },

  /**
   * Reconciles the properties by detecting differences in property values and
   * updating the DOM as necessary. This function is probably the single most
   * critical path for performance optimization.
   *
   * TODO: Benchmark whether checking for changed values in memory actually
   *       improves performance (especially statically positioned elements).
   * TODO: Benchmark the effects of putting this at the top since 99% of props
   *       do not change for a given reconciliation.
   * TODO: Benchmark areas that can be improved with caching.
   *
   * @private
   * @param {object} lastProps
   * @param {object} nextProps
   * @param {?DOMElement} node
   */
  _updateDOMProperties: function (lastProps, nextProps, transaction) {
    var propKey;
    var styleName;
    var styleUpdates;
    for (propKey in lastProps) {
      if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey) || lastProps[propKey] == null) {
        continue;
      }
      if (propKey === STYLE) {
        var lastStyle = this._previousStyleCopy;
        for (styleName in lastStyle) {
          if (lastStyle.hasOwnProperty(styleName)) {
            styleUpdates = styleUpdates || {};
            styleUpdates[styleName] = '';
          }
        }
        this._previousStyleCopy = null;
      } else if (registrationNameModules.hasOwnProperty(propKey)) {
        if (lastProps[propKey]) {
          // Only call deleteListener if there was a listener previously or
          // else willDeleteListener gets called when there wasn't actually a
          // listener (e.g., onClick={null})
          deleteListener(this, propKey);
        }
      } else if (isCustomComponent(this._tag, lastProps)) {
        if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
          DOMPropertyOperations.deleteValueForAttribute(getNode(this), propKey);
        }
      } else if (DOMProperty.properties[propKey] || DOMProperty.isCustomAttribute(propKey)) {
        DOMPropertyOperations.deleteValueForProperty(getNode(this), propKey);
      }
    }
    for (propKey in nextProps) {
      var nextProp = nextProps[propKey];
      var lastProp = propKey === STYLE ? this._previousStyleCopy : lastProps != null ? lastProps[propKey] : undefined;
      if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp || nextProp == null && lastProp == null) {
        continue;
      }
      if (propKey === STYLE) {
        if (nextProp) {
          if ("production" !== 'production') {
            checkAndWarnForMutatedStyle(this._previousStyleCopy, this._previousStyle, this);
            this._previousStyle = nextProp;
          }
          nextProp = this._previousStyleCopy = _assign({}, nextProp);
        } else {
          this._previousStyleCopy = null;
        }
        if (lastProp) {
          // Unset styles on `lastProp` but not on `nextProp`.
          for (styleName in lastProp) {
            if (lastProp.hasOwnProperty(styleName) && (!nextProp || !nextProp.hasOwnProperty(styleName))) {
              styleUpdates = styleUpdates || {};
              styleUpdates[styleName] = '';
            }
          }
          // Update styles that changed since `lastProp`.
          for (styleName in nextProp) {
            if (nextProp.hasOwnProperty(styleName) && lastProp[styleName] !== nextProp[styleName]) {
              styleUpdates = styleUpdates || {};
              styleUpdates[styleName] = nextProp[styleName];
            }
          }
        } else {
          // Relies on `updateStylesByID` not mutating `styleUpdates`.
          styleUpdates = nextProp;
        }
      } else if (registrationNameModules.hasOwnProperty(propKey)) {
        if (nextProp) {
          enqueuePutListener(this, propKey, nextProp, transaction);
        } else if (lastProp) {
          deleteListener(this, propKey);
        }
      } else if (isCustomComponent(this._tag, nextProps)) {
        if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
          DOMPropertyOperations.setValueForAttribute(getNode(this), propKey, nextProp);
        }
      } else if (DOMProperty.properties[propKey] || DOMProperty.isCustomAttribute(propKey)) {
        var node = getNode(this);
        // If we're updating to null or undefined, we should remove the property
        // from the DOM node instead of inadvertently setting to a string. This
        // brings us in line with the same behavior we have on initial render.
        if (nextProp != null) {
          DOMPropertyOperations.setValueForProperty(node, propKey, nextProp);
        } else {
          DOMPropertyOperations.deleteValueForProperty(node, propKey);
        }
      }
    }
    if (styleUpdates) {
      CSSPropertyOperations.setValueForStyles(getNode(this), styleUpdates, this);
    }
  },

  /**
   * Reconciles the children with the various properties that affect the
   * children content.
   *
   * @param {object} lastProps
   * @param {object} nextProps
   * @param {ReactReconcileTransaction} transaction
   * @param {object} context
   */
  _updateDOMChildren: function (lastProps, nextProps, transaction, context) {
    var lastContent = CONTENT_TYPES[typeof lastProps.children] ? lastProps.children : null;
    var nextContent = CONTENT_TYPES[typeof nextProps.children] ? nextProps.children : null;

    var lastHtml = lastProps.dangerouslySetInnerHTML && lastProps.dangerouslySetInnerHTML.__html;
    var nextHtml = nextProps.dangerouslySetInnerHTML && nextProps.dangerouslySetInnerHTML.__html;

    // Note the use of `!=` which checks for null or undefined.
    var lastChildren = lastContent != null ? null : lastProps.children;
    var nextChildren = nextContent != null ? null : nextProps.children;

    // If we're switching from children to content/html or vice versa, remove
    // the old content
    var lastHasContentOrHtml = lastContent != null || lastHtml != null;
    var nextHasContentOrHtml = nextContent != null || nextHtml != null;
    if (lastChildren != null && nextChildren == null) {
      this.updateChildren(null, transaction, context);
    } else if (lastHasContentOrHtml && !nextHasContentOrHtml) {
      this.updateTextContent('');
      if ("production" !== 'production') {
        ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);
      }
    }

    if (nextContent != null) {
      if (lastContent !== nextContent) {
        this.updateTextContent('' + nextContent);
        if ("production" !== 'production') {
          setAndValidateContentChildDev.call(this, nextContent);
        }
      }
    } else if (nextHtml != null) {
      if (lastHtml !== nextHtml) {
        this.updateMarkup('' + nextHtml);
      }
      if ("production" !== 'production') {
        ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);
      }
    } else if (nextChildren != null) {
      if ("production" !== 'production') {
        setAndValidateContentChildDev.call(this, null);
      }

      this.updateChildren(nextChildren, transaction, context);
    }
  },

  getHostNode: function () {
    return getNode(this);
  },

  /**
   * Destroys all event registrations for this instance. Does not remove from
   * the DOM. That must be done by the parent.
   *
   * @internal
   */
  unmountComponent: function (safely) {
    switch (this._tag) {
      case 'audio':
      case 'form':
      case 'iframe':
      case 'img':
      case 'link':
      case 'object':
      case 'source':
      case 'video':
        var listeners = this._wrapperState.listeners;
        if (listeners) {
          for (var i = 0; i < listeners.length; i++) {
            listeners[i].remove();
          }
        }
        break;
      case 'html':
      case 'head':
      case 'body':
        /**
         * Components like <html> <head> and <body> can't be removed or added
         * easily in a cross-browser way, however it's valuable to be able to
         * take advantage of React's reconciliation for styling and <title>
         * management. So we just document it and throw in dangerous cases.
         */
        !false ? "production" !== 'production' ? invariant(false, '<%s> tried to unmount. Because of cross-browser quirks it is impossible to unmount some top-level components (eg <html>, <head>, and <body>) reliably and efficiently. To fix this, have a single top-level component that never unmounts render these elements.', this._tag) : _prodInvariant('66', this._tag) : void 0;
        break;
    }

    this.unmountChildren(safely);
    ReactDOMComponentTree.uncacheNode(this);
    EventPluginHub.deleteAllListeners(this);
    this._rootNodeID = 0;
    this._domID = 0;
    this._wrapperState = null;

    if ("production" !== 'production') {
      setAndValidateContentChildDev.call(this, null);
    }
  },

  getPublicInstance: function () {
    return getNode(this);
  }

};

_assign(ReactDOMComponent.prototype, ReactDOMComponent.Mixin, ReactMultiChild.Mixin);

module.exports = ReactDOMComponent;
},{"./AutoFocusUtils":39,"./CSSPropertyOperations":42,"./DOMLazyTree":46,"./DOMNamespaces":47,"./DOMProperty":48,"./DOMPropertyOperations":49,"./EventConstants":54,"./EventPluginHub":55,"./EventPluginRegistry":56,"./ReactBrowserEventEmitter":65,"./ReactDOMButton":80,"./ReactDOMComponentFlags":82,"./ReactDOMComponentTree":83,"./ReactDOMInput":89,"./ReactDOMOption":91,"./ReactDOMSelect":92,"./ReactDOMTextarea":95,"./ReactInstrumentation":113,"./ReactMultiChild":117,"./ReactServerRenderingTransaction":130,"./escapeTextContentForBrowser":162,"./isEventSupported":176,"./reactProdInvariant":180,"./validateDOMNesting":187,"fbjs/lib/emptyFunction":196,"fbjs/lib/invariant":204,"fbjs/lib/keyOf":208,"fbjs/lib/shallowEqual":212,"fbjs/lib/warning":213,"object-assign":214}],82:[function(require,module,exports){
/**
 * Copyright 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMComponentFlags
 */

'use strict';

var ReactDOMComponentFlags = {
  hasCachedChildNodes: 1 << 0
};

module.exports = ReactDOMComponentFlags;
},{}],83:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMComponentTree
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var DOMProperty = require('./DOMProperty');
var ReactDOMComponentFlags = require('./ReactDOMComponentFlags');

var invariant = require('fbjs/lib/invariant');

var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
var Flags = ReactDOMComponentFlags;

var internalInstanceKey = '__reactInternalInstance$' + Math.random().toString(36).slice(2);

/**
 * Drill down (through composites and empty components) until we get a host or
 * host text component.
 *
 * This is pretty polymorphic but unavoidable with the current structure we have
 * for `_renderedChildren`.
 */
function getRenderedHostOrTextFromComponent(component) {
  var rendered;
  while (rendered = component._renderedComponent) {
    component = rendered;
  }
  return component;
}

/**
 * Populate `_hostNode` on the rendered host/text component with the given
 * DOM node. The passed `inst` can be a composite.
 */
function precacheNode(inst, node) {
  var hostInst = getRenderedHostOrTextFromComponent(inst);
  hostInst._hostNode = node;
  node[internalInstanceKey] = hostInst;
}

function uncacheNode(inst) {
  var node = inst._hostNode;
  if (node) {
    delete node[internalInstanceKey];
    inst._hostNode = null;
  }
}

/**
 * Populate `_hostNode` on each child of `inst`, assuming that the children
 * match up with the DOM (element) children of `node`.
 *
 * We cache entire levels at once to avoid an n^2 problem where we access the
 * children of a node sequentially and have to walk from the start to our target
 * node every time.
 *
 * Since we update `_renderedChildren` and the actual DOM at (slightly)
 * different times, we could race here and see a newer `_renderedChildren` than
 * the DOM nodes we see. To avoid this, ReactMultiChild calls
 * `prepareToManageChildren` before we change `_renderedChildren`, at which
 * time the container's child nodes are always cached (until it unmounts).
 */
function precacheChildNodes(inst, node) {
  if (inst._flags & Flags.hasCachedChildNodes) {
    return;
  }
  var children = inst._renderedChildren;
  var childNode = node.firstChild;
  outer: for (var name in children) {
    if (!children.hasOwnProperty(name)) {
      continue;
    }
    var childInst = children[name];
    var childID = getRenderedHostOrTextFromComponent(childInst)._domID;
    if (childID === 0) {
      // We're currently unmounting this child in ReactMultiChild; skip it.
      continue;
    }
    // We assume the child nodes are in the same order as the child instances.
    for (; childNode !== null; childNode = childNode.nextSibling) {
      if (childNode.nodeType === 1 && childNode.getAttribute(ATTR_NAME) === String(childID) || childNode.nodeType === 8 && childNode.nodeValue === ' react-text: ' + childID + ' ' || childNode.nodeType === 8 && childNode.nodeValue === ' react-empty: ' + childID + ' ') {
        precacheNode(childInst, childNode);
        continue outer;
      }
    }
    // We reached the end of the DOM children without finding an ID match.
    !false ? "production" !== 'production' ? invariant(false, 'Unable to find element with ID %s.', childID) : _prodInvariant('32', childID) : void 0;
  }
  inst._flags |= Flags.hasCachedChildNodes;
}

/**
 * Given a DOM node, return the closest ReactDOMComponent or
 * ReactDOMTextComponent instance ancestor.
 */
function getClosestInstanceFromNode(node) {
  if (node[internalInstanceKey]) {
    return node[internalInstanceKey];
  }

  // Walk up the tree until we find an ancestor whose instance we have cached.
  var parents = [];
  while (!node[internalInstanceKey]) {
    parents.push(node);
    if (node.parentNode) {
      node = node.parentNode;
    } else {
      // Top of the tree. This node must not be part of a React tree (or is
      // unmounted, potentially).
      return null;
    }
  }

  var closest;
  var inst;
  for (; node && (inst = node[internalInstanceKey]); node = parents.pop()) {
    closest = inst;
    if (parents.length) {
      precacheChildNodes(inst, node);
    }
  }

  return closest;
}

/**
 * Given a DOM node, return the ReactDOMComponent or ReactDOMTextComponent
 * instance, or null if the node was not rendered by this React.
 */
function getInstanceFromNode(node) {
  var inst = getClosestInstanceFromNode(node);
  if (inst != null && inst._hostNode === node) {
    return inst;
  } else {
    return null;
  }
}

/**
 * Given a ReactDOMComponent or ReactDOMTextComponent, return the corresponding
 * DOM node.
 */
function getNodeFromInstance(inst) {
  // Without this first invariant, passing a non-DOM-component triggers the next
  // invariant for a missing parent, which is super confusing.
  !(inst._hostNode !== undefined) ? "production" !== 'production' ? invariant(false, 'getNodeFromInstance: Invalid argument.') : _prodInvariant('33') : void 0;

  if (inst._hostNode) {
    return inst._hostNode;
  }

  // Walk up the tree until we find an ancestor whose DOM node we have cached.
  var parents = [];
  while (!inst._hostNode) {
    parents.push(inst);
    !inst._hostParent ? "production" !== 'production' ? invariant(false, 'React DOM tree root should always have a node reference.') : _prodInvariant('34') : void 0;
    inst = inst._hostParent;
  }

  // Now parents contains each ancestor that does *not* have a cached native
  // node, and `inst` is the deepest ancestor that does.
  for (; parents.length; inst = parents.pop()) {
    precacheChildNodes(inst, inst._hostNode);
  }

  return inst._hostNode;
}

var ReactDOMComponentTree = {
  getClosestInstanceFromNode: getClosestInstanceFromNode,
  getInstanceFromNode: getInstanceFromNode,
  getNodeFromInstance: getNodeFromInstance,
  precacheChildNodes: precacheChildNodes,
  precacheNode: precacheNode,
  uncacheNode: uncacheNode
};

module.exports = ReactDOMComponentTree;
},{"./DOMProperty":48,"./ReactDOMComponentFlags":82,"./reactProdInvariant":180,"fbjs/lib/invariant":204}],84:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMContainerInfo
 */

'use strict';

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

var DOC_NODE_TYPE = 9;

function ReactDOMContainerInfo(topLevelWrapper, node) {
  var info = {
    _topLevelWrapper: topLevelWrapper,
    _idCounter: 1,
    _ownerDocument: node ? node.nodeType === DOC_NODE_TYPE ? node : node.ownerDocument : null,
    _node: node,
    _tag: node ? node.nodeName.toLowerCase() : null,
    _namespaceURI: node ? node.namespaceURI : null
  };
  if ("production" !== 'production') {
    info._ancestorInfo = node ? validateDOMNesting.updatedAncestorInfo(null, info._tag, null) : null;
  }
  return info;
}

module.exports = ReactDOMContainerInfo;
},{"./validateDOMNesting":187}],85:[function(require,module,exports){
/**
 * Copyright 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMEmptyComponent
 */

'use strict';

var _assign = require('object-assign');

var DOMLazyTree = require('./DOMLazyTree');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');

var ReactDOMEmptyComponent = function (instantiate) {
  // ReactCompositeComponent uses this:
  this._currentElement = null;
  // ReactDOMComponentTree uses these:
  this._hostNode = null;
  this._hostParent = null;
  this._hostContainerInfo = null;
  this._domID = 0;
};
_assign(ReactDOMEmptyComponent.prototype, {
  mountComponent: function (transaction, hostParent, hostContainerInfo, context) {
    var domID = hostContainerInfo._idCounter++;
    this._domID = domID;
    this._hostParent = hostParent;
    this._hostContainerInfo = hostContainerInfo;

    var nodeValue = ' react-empty: ' + this._domID + ' ';
    if (transaction.useCreateElement) {
      var ownerDocument = hostContainerInfo._ownerDocument;
      var node = ownerDocument.createComment(nodeValue);
      ReactDOMComponentTree.precacheNode(this, node);
      return DOMLazyTree(node);
    } else {
      if (transaction.renderToStaticMarkup) {
        // Normally we'd insert a comment node, but since this is a situation
        // where React won't take over (static pages), we can simply return
        // nothing.
        return '';
      }
      return '<!--' + nodeValue + '-->';
    }
  },
  receiveComponent: function () {},
  getHostNode: function () {
    return ReactDOMComponentTree.getNodeFromInstance(this);
  },
  unmountComponent: function () {
    ReactDOMComponentTree.uncacheNode(this);
  }
});

module.exports = ReactDOMEmptyComponent;
},{"./DOMLazyTree":46,"./ReactDOMComponentTree":83,"object-assign":214}],86:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMFactories
 */

'use strict';

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

/**
 * Create a factory that creates HTML tag elements.
 *
 * @private
 */
var createDOMFactory = ReactElement.createFactory;
if ("production" !== 'production') {
  var ReactElementValidator = require('./ReactElementValidator');
  createDOMFactory = ReactElementValidator.createFactory;
}

/**
 * Creates a mapping from supported HTML tags to `ReactDOMComponent` classes.
 * This is also accessible via `React.DOM`.
 *
 * @public
 */
var ReactDOMFactories = {
  a: createDOMFactory('a'),
  abbr: createDOMFactory('abbr'),
  address: createDOMFactory('address'),
  area: createDOMFactory('area'),
  article: createDOMFactory('article'),
  aside: createDOMFactory('aside'),
  audio: createDOMFactory('audio'),
  b: createDOMFactory('b'),
  base: createDOMFactory('base'),
  bdi: createDOMFactory('bdi'),
  bdo: createDOMFactory('bdo'),
  big: createDOMFactory('big'),
  blockquote: createDOMFactory('blockquote'),
  body: createDOMFactory('body'),
  br: createDOMFactory('br'),
  button: createDOMFactory('button'),
  canvas: createDOMFactory('canvas'),
  caption: createDOMFactory('caption'),
  cite: createDOMFactory('cite'),
  code: createDOMFactory('code'),
  col: createDOMFactory('col'),
  colgroup: createDOMFactory('colgroup'),
  data: createDOMFactory('data'),
  datalist: createDOMFactory('datalist'),
  dd: createDOMFactory('dd'),
  del: createDOMFactory('del'),
  details: createDOMFactory('details'),
  dfn: createDOMFactory('dfn'),
  dialog: createDOMFactory('dialog'),
  div: createDOMFactory('div'),
  dl: createDOMFactory('dl'),
  dt: createDOMFactory('dt'),
  em: createDOMFactory('em'),
  embed: createDOMFactory('embed'),
  fieldset: createDOMFactory('fieldset'),
  figcaption: createDOMFactory('figcaption'),
  figure: createDOMFactory('figure'),
  footer: createDOMFactory('footer'),
  form: createDOMFactory('form'),
  h1: createDOMFactory('h1'),
  h2: createDOMFactory('h2'),
  h3: createDOMFactory('h3'),
  h4: createDOMFactory('h4'),
  h5: createDOMFactory('h5'),
  h6: createDOMFactory('h6'),
  head: createDOMFactory('head'),
  header: createDOMFactory('header'),
  hgroup: createDOMFactory('hgroup'),
  hr: createDOMFactory('hr'),
  html: createDOMFactory('html'),
  i: createDOMFactory('i'),
  iframe: createDOMFactory('iframe'),
  img: createDOMFactory('img'),
  input: createDOMFactory('input'),
  ins: createDOMFactory('ins'),
  kbd: createDOMFactory('kbd'),
  keygen: createDOMFactory('keygen'),
  label: createDOMFactory('label'),
  legend: createDOMFactory('legend'),
  li: createDOMFactory('li'),
  link: createDOMFactory('link'),
  main: createDOMFactory('main'),
  map: createDOMFactory('map'),
  mark: createDOMFactory('mark'),
  menu: createDOMFactory('menu'),
  menuitem: createDOMFactory('menuitem'),
  meta: createDOMFactory('meta'),
  meter: createDOMFactory('meter'),
  nav: createDOMFactory('nav'),
  noscript: createDOMFactory('noscript'),
  object: createDOMFactory('object'),
  ol: createDOMFactory('ol'),
  optgroup: createDOMFactory('optgroup'),
  option: createDOMFactory('option'),
  output: createDOMFactory('output'),
  p: createDOMFactory('p'),
  param: createDOMFactory('param'),
  picture: createDOMFactory('picture'),
  pre: createDOMFactory('pre'),
  progress: createDOMFactory('progress'),
  q: createDOMFactory('q'),
  rp: createDOMFactory('rp'),
  rt: createDOMFactory('rt'),
  ruby: createDOMFactory('ruby'),
  s: createDOMFactory('s'),
  samp: createDOMFactory('samp'),
  script: createDOMFactory('script'),
  section: createDOMFactory('section'),
  select: createDOMFactory('select'),
  small: createDOMFactory('small'),
  source: createDOMFactory('source'),
  span: createDOMFactory('span'),
  strong: createDOMFactory('strong'),
  style: createDOMFactory('style'),
  sub: createDOMFactory('sub'),
  summary: createDOMFactory('summary'),
  sup: createDOMFactory('sup'),
  table: createDOMFactory('table'),
  tbody: createDOMFactory('tbody'),
  td: createDOMFactory('td'),
  textarea: createDOMFactory('textarea'),
  tfoot: createDOMFactory('tfoot'),
  th: createDOMFactory('th'),
  thead: createDOMFactory('thead'),
  time: createDOMFactory('time'),
  title: createDOMFactory('title'),
  tr: createDOMFactory('tr'),
  track: createDOMFactory('track'),
  u: createDOMFactory('u'),
  ul: createDOMFactory('ul'),
  'var': createDOMFactory('var'),
  video: createDOMFactory('video'),
  wbr: createDOMFactory('wbr'),

  // SVG
  circle: createDOMFactory('circle'),
  clipPath: createDOMFactory('clipPath'),
  defs: createDOMFactory('defs'),
  ellipse: createDOMFactory('ellipse'),
  g: createDOMFactory('g'),
  image: createDOMFactory('image'),
  line: createDOMFactory('line'),
  linearGradient: createDOMFactory('linearGradient'),
  mask: createDOMFactory('mask'),
  path: createDOMFactory('path'),
  pattern: createDOMFactory('pattern'),
  polygon: createDOMFactory('polygon'),
  polyline: createDOMFactory('polyline'),
  radialGradient: createDOMFactory('radialGradient'),
  rect: createDOMFactory('rect'),
  stop: createDOMFactory('stop'),
  svg: createDOMFactory('svg'),
  text: createDOMFactory('text'),
  tspan: createDOMFactory('tspan')
};

module.exports = ReactDOMFactories;
},{"./ReactElement":101,"./ReactElementValidator":102}],87:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMFeatureFlags
 */

'use strict';

var ReactDOMFeatureFlags = {
  useCreateElement: true
};

module.exports = ReactDOMFeatureFlags;
},{}],88:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMIDOperations
 */

'use strict';

var DOMChildrenOperations = require('./DOMChildrenOperations');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');

/**
 * Operations used to process updates to DOM nodes.
 */
var ReactDOMIDOperations = {

  /**
   * Updates a component's children by processing a series of updates.
   *
   * @param {array<object>} updates List of update configurations.
   * @internal
   */
  dangerouslyProcessChildrenUpdates: function (parentInst, updates) {
    var node = ReactDOMComponentTree.getNodeFromInstance(parentInst);
    DOMChildrenOperations.processUpdates(node, updates);
  }
};

module.exports = ReactDOMIDOperations;
},{"./DOMChildrenOperations":45,"./ReactDOMComponentTree":83}],89:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMInput
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant'),
    _assign = require('object-assign');

var DisabledInputUtils = require('./DisabledInputUtils');
var DOMPropertyOperations = require('./DOMPropertyOperations');
var LinkedValueUtils = require('./LinkedValueUtils');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactUpdates = require('./ReactUpdates');

var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');

var didWarnValueLink = false;
var didWarnCheckedLink = false;
var didWarnValueDefaultValue = false;
var didWarnCheckedDefaultChecked = false;
var didWarnControlledToUncontrolled = false;
var didWarnUncontrolledToControlled = false;

function forceUpdateIfMounted() {
  if (this._rootNodeID) {
    // DOM component is still mounted; update
    ReactDOMInput.updateWrapper(this);
  }
}

function isControlled(props) {
  var usesChecked = props.type === 'checkbox' || props.type === 'radio';
  return usesChecked ? props.checked != null : props.value != null;
}

/**
 * Implements an <input> host component that allows setting these optional
 * props: `checked`, `value`, `defaultChecked`, and `defaultValue`.
 *
 * If `checked` or `value` are not supplied (or null/undefined), user actions
 * that affect the checked state or value will trigger updates to the element.
 *
 * If they are supplied (and not null/undefined), the rendered element will not
 * trigger updates to the element. Instead, the props must change in order for
 * the rendered element to be updated.
 *
 * The rendered element will be initialized as unchecked (or `defaultChecked`)
 * with an empty value (or `defaultValue`).
 *
 * @see http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
 */
var ReactDOMInput = {
  getHostProps: function (inst, props) {
    var value = LinkedValueUtils.getValue(props);
    var checked = LinkedValueUtils.getChecked(props);

    var hostProps = _assign({
      // Make sure we set .type before any other properties (setting .value
      // before .type means .value is lost in IE11 and below)
      type: undefined,
      // Make sure we set .step before .value (setting .value before .step
      // means .value is rounded on mount, based upon step precision)
      step: undefined,
      // Make sure we set .min & .max before .value (to ensure proper order
      // in corner cases such as min or max deriving from value, e.g. Issue #7170)
      min: undefined,
      max: undefined
    }, DisabledInputUtils.getHostProps(inst, props), {
      defaultChecked: undefined,
      defaultValue: undefined,
      value: value != null ? value : inst._wrapperState.initialValue,
      checked: checked != null ? checked : inst._wrapperState.initialChecked,
      onChange: inst._wrapperState.onChange
    });

    return hostProps;
  },

  mountWrapper: function (inst, props) {
    if ("production" !== 'production') {
      LinkedValueUtils.checkPropTypes('input', props, inst._currentElement._owner);

      var owner = inst._currentElement._owner;

      if (props.valueLink !== undefined && !didWarnValueLink) {
        "production" !== 'production' ? warning(false, '`valueLink` prop on `input` is deprecated; set `value` and `onChange` instead.') : void 0;
        didWarnValueLink = true;
      }
      if (props.checkedLink !== undefined && !didWarnCheckedLink) {
        "production" !== 'production' ? warning(false, '`checkedLink` prop on `input` is deprecated; set `value` and `onChange` instead.') : void 0;
        didWarnCheckedLink = true;
      }
      if (props.checked !== undefined && props.defaultChecked !== undefined && !didWarnCheckedDefaultChecked) {
        "production" !== 'production' ? warning(false, '%s contains an input of type %s with both checked and defaultChecked props. ' + 'Input elements must be either controlled or uncontrolled ' + '(specify either the checked prop, or the defaultChecked prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components', owner && owner.getName() || 'A component', props.type) : void 0;
        didWarnCheckedDefaultChecked = true;
      }
      if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValueDefaultValue) {
        "production" !== 'production' ? warning(false, '%s contains an input of type %s with both value and defaultValue props. ' + 'Input elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components', owner && owner.getName() || 'A component', props.type) : void 0;
        didWarnValueDefaultValue = true;
      }
    }

    var defaultValue = props.defaultValue;
    inst._wrapperState = {
      initialChecked: props.checked != null ? props.checked : props.defaultChecked,
      initialValue: props.value != null ? props.value : defaultValue,
      listeners: null,
      onChange: _handleChange.bind(inst)
    };

    if ("production" !== 'production') {
      inst._wrapperState.controlled = isControlled(props);
    }
  },

  updateWrapper: function (inst) {
    var props = inst._currentElement.props;

    if ("production" !== 'production') {
      var controlled = isControlled(props);
      var owner = inst._currentElement._owner;

      if (!inst._wrapperState.controlled && controlled && !didWarnUncontrolledToControlled) {
        "production" !== 'production' ? warning(false, '%s is changing an uncontrolled input of type %s to be controlled. ' + 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' + 'Decide between using a controlled or uncontrolled input ' + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components', owner && owner.getName() || 'A component', props.type) : void 0;
        didWarnUncontrolledToControlled = true;
      }
      if (inst._wrapperState.controlled && !controlled && !didWarnControlledToUncontrolled) {
        "production" !== 'production' ? warning(false, '%s is changing a controlled input of type %s to be uncontrolled. ' + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' + 'Decide between using a controlled or uncontrolled input ' + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components', owner && owner.getName() || 'A component', props.type) : void 0;
        didWarnControlledToUncontrolled = true;
      }
    }

    // TODO: Shouldn't this be getChecked(props)?
    var checked = props.checked;
    if (checked != null) {
      DOMPropertyOperations.setValueForProperty(ReactDOMComponentTree.getNodeFromInstance(inst), 'checked', checked || false);
    }

    var node = ReactDOMComponentTree.getNodeFromInstance(inst);
    var value = LinkedValueUtils.getValue(props);
    if (value != null) {

      // Cast `value` to a string to ensure the value is set correctly. While
      // browsers typically do this as necessary, jsdom doesn't.
      var newValue = '' + value;

      // To avoid side effects (such as losing text selection), only set value if changed
      if (newValue !== node.value) {
        node.value = newValue;
      }
    } else {
      if (props.value == null && props.defaultValue != null) {
        node.defaultValue = '' + props.defaultValue;
      }
      if (props.checked == null && props.defaultChecked != null) {
        node.defaultChecked = !!props.defaultChecked;
      }
    }
  },

  postMountWrapper: function (inst) {
    var props = inst._currentElement.props;

    // This is in postMount because we need access to the DOM node, which is not
    // available until after the component has mounted.
    var node = ReactDOMComponentTree.getNodeFromInstance(inst);

    // Detach value from defaultValue. We won't do anything if we're working on
    // submit or reset inputs as those values & defaultValues are linked. They
    // are not resetable nodes so this operation doesn't matter and actually
    // removes browser-default values (eg "Submit Query") when no value is
    // provided.

    switch (props.type) {
      case 'submit':
      case 'reset':
        break;
      case 'color':
      case 'date':
      case 'datetime':
      case 'datetime-local':
      case 'month':
      case 'time':
      case 'week':
        // This fixes the no-show issue on iOS Safari and Android Chrome:
        // https://github.com/facebook/react/issues/7233
        node.value = '';
        node.value = node.defaultValue;
        break;
      default:
        node.value = node.value;
        break;
    }

    // Normally, we'd just do `node.checked = node.checked` upon initial mount, less this bug
    // this is needed to work around a chrome bug where setting defaultChecked
    // will sometimes influence the value of checked (even after detachment).
    // Reference: https://bugs.chromium.org/p/chromium/issues/detail?id=608416
    // We need to temporarily unset name to avoid disrupting radio button groups.
    var name = node.name;
    if (name !== '') {
      node.name = '';
    }
    node.defaultChecked = !node.defaultChecked;
    node.defaultChecked = !node.defaultChecked;
    if (name !== '') {
      node.name = name;
    }
  }
};

function _handleChange(event) {
  var props = this._currentElement.props;

  var returnValue = LinkedValueUtils.executeOnChange(props, event);

  // Here we use asap to wait until all updates have propagated, which
  // is important when using controlled components within layers:
  // https://github.com/facebook/react/issues/1698
  ReactUpdates.asap(forceUpdateIfMounted, this);

  var name = props.name;
  if (props.type === 'radio' && name != null) {
    var rootNode = ReactDOMComponentTree.getNodeFromInstance(this);
    var queryRoot = rootNode;

    while (queryRoot.parentNode) {
      queryRoot = queryRoot.parentNode;
    }

    // If `rootNode.form` was non-null, then we could try `form.elements`,
    // but that sometimes behaves strangely in IE8. We could also try using
    // `form.getElementsByName`, but that will only return direct children
    // and won't include inputs that use the HTML5 `form=` attribute. Since
    // the input might not even be in a form, let's just use the global
    // `querySelectorAll` to ensure we don't miss anything.
    var group = queryRoot.querySelectorAll('input[name=' + JSON.stringify('' + name) + '][type="radio"]');

    for (var i = 0; i < group.length; i++) {
      var otherNode = group[i];
      if (otherNode === rootNode || otherNode.form !== rootNode.form) {
        continue;
      }
      // This will throw if radio buttons rendered by different copies of React
      // and the same name are rendered into the same form (same as #1939).
      // That's probably okay; we don't support it just as we don't support
      // mixing React radio buttons with non-React ones.
      var otherInstance = ReactDOMComponentTree.getInstanceFromNode(otherNode);
      !otherInstance ? "production" !== 'production' ? invariant(false, 'ReactDOMInput: Mixing React and non-React radio inputs with the same `name` is not supported.') : _prodInvariant('90') : void 0;
      // If this is a controlled radio button group, forcing the input that
      // was previously checked to update will cause it to be come re-checked
      // as appropriate.
      ReactUpdates.asap(forceUpdateIfMounted, otherInstance);
    }
  }

  return returnValue;
}

module.exports = ReactDOMInput;
},{"./DOMPropertyOperations":49,"./DisabledInputUtils":52,"./LinkedValueUtils":62,"./ReactDOMComponentTree":83,"./ReactUpdates":136,"./reactProdInvariant":180,"fbjs/lib/invariant":204,"fbjs/lib/warning":213,"object-assign":214}],90:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMNullInputValuePropHook
 */

'use strict';

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

var warning = require('fbjs/lib/warning');

var didWarnValueNull = false;

function handleElement(debugID, element) {
  if (element == null) {
    return;
  }
  if (element.type !== 'input' && element.type !== 'textarea' && element.type !== 'select') {
    return;
  }
  if (element.props != null && element.props.value === null && !didWarnValueNull) {
    "production" !== 'production' ? warning(false, '`value` prop on `%s` should not be null. ' + 'Consider using the empty string to clear the component or `undefined` ' + 'for uncontrolled components.%s', element.type, ReactComponentTreeHook.getStackAddendumByID(debugID)) : void 0;

    didWarnValueNull = true;
  }
}

var ReactDOMNullInputValuePropHook = {
  onBeforeMountComponent: function (debugID, element) {
    handleElement(debugID, element);
  },
  onBeforeUpdateComponent: function (debugID, element) {
    handleElement(debugID, element);
  }
};

module.exports = ReactDOMNullInputValuePropHook;
},{"./ReactComponentTreeHook":75,"fbjs/lib/warning":213}],91:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMOption
 */

'use strict';

var _assign = require('object-assign');

var ReactChildren = require('./ReactChildren');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactDOMSelect = require('./ReactDOMSelect');

var warning = require('fbjs/lib/warning');
var didWarnInvalidOptionChildren = false;

function flattenChildren(children) {
  var content = '';

  // Flatten children and warn if they aren't strings or numbers;
  // invalid types are ignored.
  ReactChildren.forEach(children, function (child) {
    if (child == null) {
      return;
    }
    if (typeof child === 'string' || typeof child === 'number') {
      content += child;
    } else if (!didWarnInvalidOptionChildren) {
      didWarnInvalidOptionChildren = true;
      "production" !== 'production' ? warning(false, 'Only strings and numbers are supported as <option> children.') : void 0;
    }
  });

  return content;
}

/**
 * Implements an <option> host component that warns when `selected` is set.
 */
var ReactDOMOption = {
  mountWrapper: function (inst, props, hostParent) {
    // TODO (yungsters): Remove support for `selected` in <option>.
    if ("production" !== 'production') {
      "production" !== 'production' ? warning(props.selected == null, 'Use the `defaultValue` or `value` props on <select> instead of ' + 'setting `selected` on <option>.') : void 0;
    }

    // Look up whether this option is 'selected'
    var selectValue = null;
    if (hostParent != null) {
      var selectParent = hostParent;

      if (selectParent._tag === 'optgroup') {
        selectParent = selectParent._hostParent;
      }

      if (selectParent != null && selectParent._tag === 'select') {
        selectValue = ReactDOMSelect.getSelectValueContext(selectParent);
      }
    }

    // If the value is null (e.g., no specified value or after initial mount)
    // or missing (e.g., for <datalist>), we don't change props.selected
    var selected = null;
    if (selectValue != null) {
      var value;
      if (props.value != null) {
        value = props.value + '';
      } else {
        value = flattenChildren(props.children);
      }
      selected = false;
      if (Array.isArray(selectValue)) {
        // multiple
        for (var i = 0; i < selectValue.length; i++) {
          if ('' + selectValue[i] === value) {
            selected = true;
            break;
          }
        }
      } else {
        selected = '' + selectValue === value;
      }
    }

    inst._wrapperState = { selected: selected };
  },

  postMountWrapper: function (inst) {
    // value="" should make a value attribute (#6219)
    var props = inst._currentElement.props;
    if (props.value != null) {
      var node = ReactDOMComponentTree.getNodeFromInstance(inst);
      node.setAttribute('value', props.value);
    }
  },

  getHostProps: function (inst, props) {
    var hostProps = _assign({ selected: undefined, children: undefined }, props);

    // Read state only from initial mount because <select> updates value
    // manually; we need the initial state only for server rendering
    if (inst._wrapperState.selected != null) {
      hostProps.selected = inst._wrapperState.selected;
    }

    var content = flattenChildren(props.children);

    if (content) {
      hostProps.children = content;
    }

    return hostProps;
  }

};

module.exports = ReactDOMOption;
},{"./ReactChildren":69,"./ReactDOMComponentTree":83,"./ReactDOMSelect":92,"fbjs/lib/warning":213,"object-assign":214}],92:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMSelect
 */

'use strict';

var _assign = require('object-assign');

var DisabledInputUtils = require('./DisabledInputUtils');
var LinkedValueUtils = require('./LinkedValueUtils');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactUpdates = require('./ReactUpdates');

var warning = require('fbjs/lib/warning');

var didWarnValueLink = false;
var didWarnValueDefaultValue = false;

function updateOptionsIfPendingUpdateAndMounted() {
  if (this._rootNodeID && this._wrapperState.pendingUpdate) {
    this._wrapperState.pendingUpdate = false;

    var props = this._currentElement.props;
    var value = LinkedValueUtils.getValue(props);

    if (value != null) {
      updateOptions(this, Boolean(props.multiple), value);
    }
  }
}

function getDeclarationErrorAddendum(owner) {
  if (owner) {
    var name = owner.getName();
    if (name) {
      return ' Check the render method of `' + name + '`.';
    }
  }
  return '';
}

var valuePropNames = ['value', 'defaultValue'];

/**
 * Validation function for `value` and `defaultValue`.
 * @private
 */
function checkSelectPropTypes(inst, props) {
  var owner = inst._currentElement._owner;
  LinkedValueUtils.checkPropTypes('select', props, owner);

  if (props.valueLink !== undefined && !didWarnValueLink) {
    "production" !== 'production' ? warning(false, '`valueLink` prop on `select` is deprecated; set `value` and `onChange` instead.') : void 0;
    didWarnValueLink = true;
  }

  for (var i = 0; i < valuePropNames.length; i++) {
    var propName = valuePropNames[i];
    if (props[propName] == null) {
      continue;
    }
    var isArray = Array.isArray(props[propName]);
    if (props.multiple && !isArray) {
      "production" !== 'production' ? warning(false, 'The `%s` prop supplied to <select> must be an array if ' + '`multiple` is true.%s', propName, getDeclarationErrorAddendum(owner)) : void 0;
    } else if (!props.multiple && isArray) {
      "production" !== 'production' ? warning(false, 'The `%s` prop supplied to <select> must be a scalar ' + 'value if `multiple` is false.%s', propName, getDeclarationErrorAddendum(owner)) : void 0;
    }
  }
}

/**
 * @param {ReactDOMComponent} inst
 * @param {boolean} multiple
 * @param {*} propValue A stringable (with `multiple`, a list of stringables).
 * @private
 */
function updateOptions(inst, multiple, propValue) {
  var selectedValue, i;
  var options = ReactDOMComponentTree.getNodeFromInstance(inst).options;

  if (multiple) {
    selectedValue = {};
    for (i = 0; i < propValue.length; i++) {
      selectedValue['' + propValue[i]] = true;
    }
    for (i = 0; i < options.length; i++) {
      var selected = selectedValue.hasOwnProperty(options[i].value);
      if (options[i].selected !== selected) {
        options[i].selected = selected;
      }
    }
  } else {
    // Do not set `select.value` as exact behavior isn't consistent across all
    // browsers for all cases.
    selectedValue = '' + propValue;
    for (i = 0; i < options.length; i++) {
      if (options[i].value === selectedValue) {
        options[i].selected = true;
        return;
      }
    }
    if (options.length) {
      options[0].selected = true;
    }
  }
}

/**
 * Implements a <select> host component that allows optionally setting the
 * props `value` and `defaultValue`. If `multiple` is false, the prop must be a
 * stringable. If `multiple` is true, the prop must be an array of stringables.
 *
 * If `value` is not supplied (or null/undefined), user actions that change the
 * selected option will trigger updates to the rendered options.
 *
 * If it is supplied (and not null/undefined), the rendered options will not
 * update in response to user actions. Instead, the `value` prop must change in
 * order for the rendered options to update.
 *
 * If `defaultValue` is provided, any options with the supplied values will be
 * selected.
 */
var ReactDOMSelect = {
  getHostProps: function (inst, props) {
    return _assign({}, DisabledInputUtils.getHostProps(inst, props), {
      onChange: inst._wrapperState.onChange,
      value: undefined
    });
  },

  mountWrapper: function (inst, props) {
    if ("production" !== 'production') {
      checkSelectPropTypes(inst, props);
    }

    var value = LinkedValueUtils.getValue(props);
    inst._wrapperState = {
      pendingUpdate: false,
      initialValue: value != null ? value : props.defaultValue,
      listeners: null,
      onChange: _handleChange.bind(inst),
      wasMultiple: Boolean(props.multiple)
    };

    if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValueDefaultValue) {
      "production" !== 'production' ? warning(false, 'Select elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled select ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components') : void 0;
      didWarnValueDefaultValue = true;
    }
  },

  getSelectValueContext: function (inst) {
    // ReactDOMOption looks at this initial value so the initial generated
    // markup has correct `selected` attributes
    return inst._wrapperState.initialValue;
  },

  postUpdateWrapper: function (inst) {
    var props = inst._currentElement.props;

    // After the initial mount, we control selected-ness manually so don't pass
    // this value down
    inst._wrapperState.initialValue = undefined;

    var wasMultiple = inst._wrapperState.wasMultiple;
    inst._wrapperState.wasMultiple = Boolean(props.multiple);

    var value = LinkedValueUtils.getValue(props);
    if (value != null) {
      inst._wrapperState.pendingUpdate = false;
      updateOptions(inst, Boolean(props.multiple), value);
    } else if (wasMultiple !== Boolean(props.multiple)) {
      // For simplicity, reapply `defaultValue` if `multiple` is toggled.
      if (props.defaultValue != null) {
        updateOptions(inst, Boolean(props.multiple), props.defaultValue);
      } else {
        // Revert the select back to its default unselected state.
        updateOptions(inst, Boolean(props.multiple), props.multiple ? [] : '');
      }
    }
  }
};

function _handleChange(event) {
  var props = this._currentElement.props;
  var returnValue = LinkedValueUtils.executeOnChange(props, event);

  if (this._rootNodeID) {
    this._wrapperState.pendingUpdate = true;
  }
  ReactUpdates.asap(updateOptionsIfPendingUpdateAndMounted, this);
  return returnValue;
}

module.exports = ReactDOMSelect;
},{"./DisabledInputUtils":52,"./LinkedValueUtils":62,"./ReactDOMComponentTree":83,"./ReactUpdates":136,"fbjs/lib/warning":213,"object-assign":214}],93:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMSelection
 */

'use strict';

var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');

var getNodeForCharacterOffset = require('./getNodeForCharacterOffset');
var getTextContentAccessor = require('./getTextContentAccessor');

/**
 * While `isCollapsed` is available on the Selection object and `collapsed`
 * is available on the Range object, IE11 sometimes gets them wrong.
 * If the anchor/focus nodes and offsets are the same, the range is collapsed.
 */
function isCollapsed(anchorNode, anchorOffset, focusNode, focusOffset) {
  return anchorNode === focusNode && anchorOffset === focusOffset;
}

/**
 * Get the appropriate anchor and focus node/offset pairs for IE.
 *
 * The catch here is that IE's selection API doesn't provide information
 * about whether the selection is forward or backward, so we have to
 * behave as though it's always forward.
 *
 * IE text differs from modern selection in that it behaves as though
 * block elements end with a new line. This means character offsets will
 * differ between the two APIs.
 *
 * @param {DOMElement} node
 * @return {object}
 */
function getIEOffsets(node) {
  var selection = document.selection;
  var selectedRange = selection.createRange();
  var selectedLength = selectedRange.text.length;

  // Duplicate selection so we can move range without breaking user selection.
  var fromStart = selectedRange.duplicate();
  fromStart.moveToElementText(node);
  fromStart.setEndPoint('EndToStart', selectedRange);

  var startOffset = fromStart.text.length;
  var endOffset = startOffset + selectedLength;

  return {
    start: startOffset,
    end: endOffset
  };
}

/**
 * @param {DOMElement} node
 * @return {?object}
 */
function getModernOffsets(node) {
  var selection = window.getSelection && window.getSelection();

  if (!selection || selection.rangeCount === 0) {
    return null;
  }

  var anchorNode = selection.anchorNode;
  var anchorOffset = selection.anchorOffset;
  var focusNode = selection.focusNode;
  var focusOffset = selection.focusOffset;

  var currentRange = selection.getRangeAt(0);

  // In Firefox, range.startContainer and range.endContainer can be "anonymous
  // divs", e.g. the up/down buttons on an <input type="number">. Anonymous
  // divs do not seem to expose properties, triggering a "Permission denied
  // error" if any of its properties are accessed. The only seemingly possible
  // way to avoid erroring is to access a property that typically works for
  // non-anonymous divs and catch any error that may otherwise arise. See
  // https://bugzilla.mozilla.org/show_bug.cgi?id=208427
  try {
    /* eslint-disable no-unused-expressions */
    currentRange.startContainer.nodeType;
    currentRange.endContainer.nodeType;
    /* eslint-enable no-unused-expressions */
  } catch (e) {
    return null;
  }

  // If the node and offset values are the same, the selection is collapsed.
  // `Selection.isCollapsed` is available natively, but IE sometimes gets
  // this value wrong.
  var isSelectionCollapsed = isCollapsed(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset);

  var rangeLength = isSelectionCollapsed ? 0 : currentRange.toString().length;

  var tempRange = currentRange.cloneRange();
  tempRange.selectNodeContents(node);
  tempRange.setEnd(currentRange.startContainer, currentRange.startOffset);

  var isTempRangeCollapsed = isCollapsed(tempRange.startContainer, tempRange.startOffset, tempRange.endContainer, tempRange.endOffset);

  var start = isTempRangeCollapsed ? 0 : tempRange.toString().length;
  var end = start + rangeLength;

  // Detect whether the selection is backward.
  var detectionRange = document.createRange();
  detectionRange.setStart(anchorNode, anchorOffset);
  detectionRange.setEnd(focusNode, focusOffset);
  var isBackward = detectionRange.collapsed;

  return {
    start: isBackward ? end : start,
    end: isBackward ? start : end
  };
}

/**
 * @param {DOMElement|DOMTextNode} node
 * @param {object} offsets
 */
function setIEOffsets(node, offsets) {
  var range = document.selection.createRange().duplicate();
  var start, end;

  if (offsets.end === undefined) {
    start = offsets.start;
    end = start;
  } else if (offsets.start > offsets.end) {
    start = offsets.end;
    end = offsets.start;
  } else {
    start = offsets.start;
    end = offsets.end;
  }

  range.moveToElementText(node);
  range.moveStart('character', start);
  range.setEndPoint('EndToStart', range);
  range.moveEnd('character', end - start);
  range.select();
}

/**
 * In modern non-IE browsers, we can support both forward and backward
 * selections.
 *
 * Note: IE10+ supports the Selection object, but it does not support
 * the `extend` method, which means that even in modern IE, it's not possible
 * to programmatically create a backward selection. Thus, for all IE
 * versions, we use the old IE API to create our selections.
 *
 * @param {DOMElement|DOMTextNode} node
 * @param {object} offsets
 */
function setModernOffsets(node, offsets) {
  if (!window.getSelection) {
    return;
  }

  var selection = window.getSelection();
  var length = node[getTextContentAccessor()].length;
  var start = Math.min(offsets.start, length);
  var end = offsets.end === undefined ? start : Math.min(offsets.end, length);

  // IE 11 uses modern selection, but doesn't support the extend method.
  // Flip backward selections, so we can set with a single range.
  if (!selection.extend && start > end) {
    var temp = end;
    end = start;
    start = temp;
  }

  var startMarker = getNodeForCharacterOffset(node, start);
  var endMarker = getNodeForCharacterOffset(node, end);

  if (startMarker && endMarker) {
    var range = document.createRange();
    range.setStart(startMarker.node, startMarker.offset);
    selection.removeAllRanges();

    if (start > end) {
      selection.addRange(range);
      selection.extend(endMarker.node, endMarker.offset);
    } else {
      range.setEnd(endMarker.node, endMarker.offset);
      selection.addRange(range);
    }
  }
}

var useIEOffsets = ExecutionEnvironment.canUseDOM && 'selection' in document && !('getSelection' in window);

var ReactDOMSelection = {
  /**
   * @param {DOMElement} node
   */
  getOffsets: useIEOffsets ? getIEOffsets : getModernOffsets,

  /**
   * @param {DOMElement|DOMTextNode} node
   * @param {object} offsets
   */
  setOffsets: useIEOffsets ? setIEOffsets : setModernOffsets
};

module.exports = ReactDOMSelection;
},{"./getNodeForCharacterOffset":172,"./getTextContentAccessor":173,"fbjs/lib/ExecutionEnvironment":190}],94:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMTextComponent
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant'),
    _assign = require('object-assign');

var DOMChildrenOperations = require('./DOMChildrenOperations');
var DOMLazyTree = require('./DOMLazyTree');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');

var escapeTextContentForBrowser = require('./escapeTextContentForBrowser');
var invariant = require('fbjs/lib/invariant');
var validateDOMNesting = require('./validateDOMNesting');

/**
 * Text nodes violate a couple assumptions that React makes about components:
 *
 *  - When mounting text into the DOM, adjacent text nodes are merged.
 *  - Text nodes cannot be assigned a React root ID.
 *
 * This component is used to wrap strings between comment nodes so that they
 * can undergo the same reconciliation that is applied to elements.
 *
 * TODO: Investigate representing React components in the DOM with text nodes.
 *
 * @class ReactDOMTextComponent
 * @extends ReactComponent
 * @internal
 */
var ReactDOMTextComponent = function (text) {
  // TODO: This is really a ReactText (ReactNode), not a ReactElement
  this._currentElement = text;
  this._stringText = '' + text;
  // ReactDOMComponentTree uses these:
  this._hostNode = null;
  this._hostParent = null;

  // Properties
  this._domID = 0;
  this._mountIndex = 0;
  this._closingComment = null;
  this._commentNodes = null;
};

_assign(ReactDOMTextComponent.prototype, {

  /**
   * Creates the markup for this text node. This node is not intended to have
   * any features besides containing text content.
   *
   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
   * @return {string} Markup for this text node.
   * @internal
   */
  mountComponent: function (transaction, hostParent, hostContainerInfo, context) {
    if ("production" !== 'production') {
      var parentInfo;
      if (hostParent != null) {
        parentInfo = hostParent._ancestorInfo;
      } else if (hostContainerInfo != null) {
        parentInfo = hostContainerInfo._ancestorInfo;
      }
      if (parentInfo) {
        // parentInfo should always be present except for the top-level
        // component when server rendering
        validateDOMNesting(null, this._stringText, this, parentInfo);
      }
    }

    var domID = hostContainerInfo._idCounter++;
    var openingValue = ' react-text: ' + domID + ' ';
    var closingValue = ' /react-text ';
    this._domID = domID;
    this._hostParent = hostParent;
    if (transaction.useCreateElement) {
      var ownerDocument = hostContainerInfo._ownerDocument;
      var openingComment = ownerDocument.createComment(openingValue);
      var closingComment = ownerDocument.createComment(closingValue);
      var lazyTree = DOMLazyTree(ownerDocument.createDocumentFragment());
      DOMLazyTree.queueChild(lazyTree, DOMLazyTree(openingComment));
      if (this._stringText) {
        DOMLazyTree.queueChild(lazyTree, DOMLazyTree(ownerDocument.createTextNode(this._stringText)));
      }
      DOMLazyTree.queueChild(lazyTree, DOMLazyTree(closingComment));
      ReactDOMComponentTree.precacheNode(this, openingComment);
      this._closingComment = closingComment;
      return lazyTree;
    } else {
      var escapedText = escapeTextContentForBrowser(this._stringText);

      if (transaction.renderToStaticMarkup) {
        // Normally we'd wrap this between comment nodes for the reasons stated
        // above, but since this is a situation where React won't take over
        // (static pages), we can simply return the text as it is.
        return escapedText;
      }

      return '<!--' + openingValue + '-->' + escapedText + '<!--' + closingValue + '-->';
    }
  },

  /**
   * Updates this component by updating the text content.
   *
   * @param {ReactText} nextText The next text content
   * @param {ReactReconcileTransaction} transaction
   * @internal
   */
  receiveComponent: function (nextText, transaction) {
    if (nextText !== this._currentElement) {
      this._currentElement = nextText;
      var nextStringText = '' + nextText;
      if (nextStringText !== this._stringText) {
        // TODO: Save this as pending props and use performUpdateIfNecessary
        // and/or updateComponent to do the actual update for consistency with
        // other component types?
        this._stringText = nextStringText;
        var commentNodes = this.getHostNode();
        DOMChildrenOperations.replaceDelimitedText(commentNodes[0], commentNodes[1], nextStringText);
      }
    }
  },

  getHostNode: function () {
    var hostNode = this._commentNodes;
    if (hostNode) {
      return hostNode;
    }
    if (!this._closingComment) {
      var openingComment = ReactDOMComponentTree.getNodeFromInstance(this);
      var node = openingComment.nextSibling;
      while (true) {
        !(node != null) ? "production" !== 'production' ? invariant(false, 'Missing closing comment for text component %s', this._domID) : _prodInvariant('67', this._domID) : void 0;
        if (node.nodeType === 8 && node.nodeValue === ' /react-text ') {
          this._closingComment = node;
          break;
        }
        node = node.nextSibling;
      }
    }
    hostNode = [this._hostNode, this._closingComment];
    this._commentNodes = hostNode;
    return hostNode;
  },

  unmountComponent: function () {
    this._closingComment = null;
    this._commentNodes = null;
    ReactDOMComponentTree.uncacheNode(this);
  }

});

module.exports = ReactDOMTextComponent;
},{"./DOMChildrenOperations":45,"./DOMLazyTree":46,"./ReactDOMComponentTree":83,"./escapeTextContentForBrowser":162,"./reactProdInvariant":180,"./validateDOMNesting":187,"fbjs/lib/invariant":204,"object-assign":214}],95:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMTextarea
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant'),
    _assign = require('object-assign');

var DisabledInputUtils = require('./DisabledInputUtils');
var LinkedValueUtils = require('./LinkedValueUtils');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactUpdates = require('./ReactUpdates');

var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');

var didWarnValueLink = false;
var didWarnValDefaultVal = false;

function forceUpdateIfMounted() {
  if (this._rootNodeID) {
    // DOM component is still mounted; update
    ReactDOMTextarea.updateWrapper(this);
  }
}

/**
 * Implements a <textarea> host component that allows setting `value`, and
 * `defaultValue`. This differs from the traditional DOM API because value is
 * usually set as PCDATA children.
 *
 * If `value` is not supplied (or null/undefined), user actions that affect the
 * value will trigger updates to the element.
 *
 * If `value` is supplied (and not null/undefined), the rendered element will
 * not trigger updates to the element. Instead, the `value` prop must change in
 * order for the rendered element to be updated.
 *
 * The rendered element will be initialized with an empty value, the prop
 * `defaultValue` if specified, or the children content (deprecated).
 */
var ReactDOMTextarea = {
  getHostProps: function (inst, props) {
    !(props.dangerouslySetInnerHTML == null) ? "production" !== 'production' ? invariant(false, '`dangerouslySetInnerHTML` does not make sense on <textarea>.') : _prodInvariant('91') : void 0;

    // Always set children to the same thing. In IE9, the selection range will
    // get reset if `textContent` is mutated.  We could add a check in setTextContent
    // to only set the value if/when the value differs from the node value (which would
    // completely solve this IE9 bug), but Sebastian+Ben seemed to like this solution.
    // The value can be a boolean or object so that's why it's forced to be a string.
    var hostProps = _assign({}, DisabledInputUtils.getHostProps(inst, props), {
      value: undefined,
      defaultValue: undefined,
      children: '' + inst._wrapperState.initialValue,
      onChange: inst._wrapperState.onChange
    });

    return hostProps;
  },

  mountWrapper: function (inst, props) {
    if ("production" !== 'production') {
      LinkedValueUtils.checkPropTypes('textarea', props, inst._currentElement._owner);
      if (props.valueLink !== undefined && !didWarnValueLink) {
        "production" !== 'production' ? warning(false, '`valueLink` prop on `textarea` is deprecated; set `value` and `onChange` instead.') : void 0;
        didWarnValueLink = true;
      }
      if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValDefaultVal) {
        "production" !== 'production' ? warning(false, 'Textarea elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled textarea ' + 'and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components') : void 0;
        didWarnValDefaultVal = true;
      }
    }

    var value = LinkedValueUtils.getValue(props);
    var initialValue = value;

    // Only bother fetching default value if we're going to use it
    if (value == null) {
      var defaultValue = props.defaultValue;
      // TODO (yungsters): Remove support for children content in <textarea>.
      var children = props.children;
      if (children != null) {
        if ("production" !== 'production') {
          "production" !== 'production' ? warning(false, 'Use the `defaultValue` or `value` props instead of setting ' + 'children on <textarea>.') : void 0;
        }
        !(defaultValue == null) ? "production" !== 'production' ? invariant(false, 'If you supply `defaultValue` on a <textarea>, do not pass children.') : _prodInvariant('92') : void 0;
        if (Array.isArray(children)) {
          !(children.length <= 1) ? "production" !== 'production' ? invariant(false, '<textarea> can only have at most one child.') : _prodInvariant('93') : void 0;
          children = children[0];
        }

        defaultValue = '' + children;
      }
      if (defaultValue == null) {
        defaultValue = '';
      }
      initialValue = defaultValue;
    }

    inst._wrapperState = {
      initialValue: '' + initialValue,
      listeners: null,
      onChange: _handleChange.bind(inst)
    };
  },

  updateWrapper: function (inst) {
    var props = inst._currentElement.props;

    var node = ReactDOMComponentTree.getNodeFromInstance(inst);
    var value = LinkedValueUtils.getValue(props);
    if (value != null) {
      // Cast `value` to a string to ensure the value is set correctly. While
      // browsers typically do this as necessary, jsdom doesn't.
      var newValue = '' + value;

      // To avoid side effects (such as losing text selection), only set value if changed
      if (newValue !== node.value) {
        node.value = newValue;
      }
      if (props.defaultValue == null) {
        node.defaultValue = newValue;
      }
    }
    if (props.defaultValue != null) {
      node.defaultValue = props.defaultValue;
    }
  },

  postMountWrapper: function (inst) {
    // This is in postMount because we need access to the DOM node, which is not
    // available until after the component has mounted.
    var node = ReactDOMComponentTree.getNodeFromInstance(inst);

    // Warning: node.value may be the empty string at this point (IE11) if placeholder is set.
    node.value = node.textContent; // Detach value from defaultValue
  }
};

function _handleChange(event) {
  var props = this._currentElement.props;
  var returnValue = LinkedValueUtils.executeOnChange(props, event);
  ReactUpdates.asap(forceUpdateIfMounted, this);
  return returnValue;
}

module.exports = ReactDOMTextarea;
},{"./DisabledInputUtils":52,"./LinkedValueUtils":62,"./ReactDOMComponentTree":83,"./ReactUpdates":136,"./reactProdInvariant":180,"fbjs/lib/invariant":204,"fbjs/lib/warning":213,"object-assign":214}],96:[function(require,module,exports){
/**
 * Copyright 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMTreeTraversal
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var invariant = require('fbjs/lib/invariant');

/**
 * Return the lowest common ancestor of A and B, or null if they are in
 * different trees.
 */
function getLowestCommonAncestor(instA, instB) {
  !('_hostNode' in instA) ? "production" !== 'production' ? invariant(false, 'getNodeFromInstance: Invalid argument.') : _prodInvariant('33') : void 0;
  !('_hostNode' in instB) ? "production" !== 'production' ? invariant(false, 'getNodeFromInstance: Invalid argument.') : _prodInvariant('33') : void 0;

  var depthA = 0;
  for (var tempA = instA; tempA; tempA = tempA._hostParent) {
    depthA++;
  }
  var depthB = 0;
  for (var tempB = instB; tempB; tempB = tempB._hostParent) {
    depthB++;
  }

  // If A is deeper, crawl up.
  while (depthA - depthB > 0) {
    instA = instA._hostParent;
    depthA--;
  }

  // If B is deeper, crawl up.
  while (depthB - depthA > 0) {
    instB = instB._hostParent;
    depthB--;
  }

  // Walk in lockstep until we find a match.
  var depth = depthA;
  while (depth--) {
    if (instA === instB) {
      return instA;
    }
    instA = instA._hostParent;
    instB = instB._hostParent;
  }
  return null;
}

/**
 * Return if A is an ancestor of B.
 */
function isAncestor(instA, instB) {
  !('_hostNode' in instA) ? "production" !== 'production' ? invariant(false, 'isAncestor: Invalid argument.') : _prodInvariant('35') : void 0;
  !('_hostNode' in instB) ? "production" !== 'production' ? invariant(false, 'isAncestor: Invalid argument.') : _prodInvariant('35') : void 0;

  while (instB) {
    if (instB === instA) {
      return true;
    }
    instB = instB._hostParent;
  }
  return false;
}

/**
 * Return the parent instance of the passed-in instance.
 */
function getParentInstance(inst) {
  !('_hostNode' in inst) ? "production" !== 'production' ? invariant(false, 'getParentInstance: Invalid argument.') : _prodInvariant('36') : void 0;

  return inst._hostParent;
}

/**
 * Simulates the traversal of a two-phase, capture/bubble event dispatch.
 */
function traverseTwoPhase(inst, fn, arg) {
  var path = [];
  while (inst) {
    path.push(inst);
    inst = inst._hostParent;
  }
  var i;
  for (i = path.length; i-- > 0;) {
    fn(path[i], false, arg);
  }
  for (i = 0; i < path.length; i++) {
    fn(path[i], true, arg);
  }
}

/**
 * Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
 * should would receive a `mouseEnter` or `mouseLeave` event.
 *
 * Does not invoke the callback on the nearest common ancestor because nothing
 * "entered" or "left" that element.
 */
function traverseEnterLeave(from, to, fn, argFrom, argTo) {
  var common = from && to ? getLowestCommonAncestor(from, to) : null;
  var pathFrom = [];
  while (from && from !== common) {
    pathFrom.push(from);
    from = from._hostParent;
  }
  var pathTo = [];
  while (to && to !== common) {
    pathTo.push(to);
    to = to._hostParent;
  }
  var i;
  for (i = 0; i < pathFrom.length; i++) {
    fn(pathFrom[i], true, argFrom);
  }
  for (i = pathTo.length; i-- > 0;) {
    fn(pathTo[i], false, argTo);
  }
}

module.exports = {
  isAncestor: isAncestor,
  getLowestCommonAncestor: getLowestCommonAncestor,
  getParentInstance: getParentInstance,
  traverseTwoPhase: traverseTwoPhase,
  traverseEnterLeave: traverseEnterLeave
};
},{"./reactProdInvariant":180,"fbjs/lib/invariant":204}],97:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDOMUnknownPropertyHook
 */

'use strict';

var DOMProperty = require('./DOMProperty');
var EventPluginRegistry = require('./EventPluginRegistry');
var ReactComponentTreeHook = require('./ReactComponentTreeHook');

var warning = require('fbjs/lib/warning');

if ("production" !== 'production') {
  var reactProps = {
    children: true,
    dangerouslySetInnerHTML: true,
    key: true,
    ref: true,

    autoFocus: true,
    defaultValue: true,
    valueLink: true,
    defaultChecked: true,
    checkedLink: true,
    innerHTML: true,
    suppressContentEditableWarning: true,
    onFocusIn: true,
    onFocusOut: true
  };
  var warnedProperties = {};

  var validateProperty = function (tagName, name, debugID) {
    if (DOMProperty.properties.hasOwnProperty(name) || DOMProperty.isCustomAttribute(name)) {
      return true;
    }
    if (reactProps.hasOwnProperty(name) && reactProps[name] || warnedProperties.hasOwnProperty(name) && warnedProperties[name]) {
      return true;
    }
    if (EventPluginRegistry.registrationNameModules.hasOwnProperty(name)) {
      return true;
    }
    warnedProperties[name] = true;
    var lowerCasedName = name.toLowerCase();

    // data-* attributes should be lowercase; suggest the lowercase version
    var standardName = DOMProperty.isCustomAttribute(lowerCasedName) ? lowerCasedName : DOMProperty.getPossibleStandardName.hasOwnProperty(lowerCasedName) ? DOMProperty.getPossibleStandardName[lowerCasedName] : null;

    var registrationName = EventPluginRegistry.possibleRegistrationNames.hasOwnProperty(lowerCasedName) ? EventPluginRegistry.possibleRegistrationNames[lowerCasedName] : null;

    if (standardName != null) {
      "production" !== 'production' ? warning(false, 'Unknown DOM property %s. Did you mean %s?%s', name, standardName, ReactComponentTreeHook.getStackAddendumByID(debugID)) : void 0;
      return true;
    } else if (registrationName != null) {
      "production" !== 'production' ? warning(false, 'Unknown event handler property %s. Did you mean `%s`?%s', name, registrationName, ReactComponentTreeHook.getStackAddendumByID(debugID)) : void 0;
      return true;
    } else {
      // We were unable to guess which prop the user intended.
      // It is likely that the user was just blindly spreading/forwarding props
      // Components should be careful to only render valid props/attributes.
      // Warning will be invoked in warnUnknownProperties to allow grouping.
      return false;
    }
  };
}

var warnUnknownProperties = function (debugID, element) {
  var unknownProps = [];
  for (var key in element.props) {
    var isValid = validateProperty(element.type, key, debugID);
    if (!isValid) {
      unknownProps.push(key);
    }
  }

  var unknownPropString = unknownProps.map(function (prop) {
    return '`' + prop + '`';
  }).join(', ');

  if (unknownProps.length === 1) {
    "production" !== 'production' ? warning(false, 'Unknown prop %s on <%s> tag. Remove this prop from the element. ' + 'For details, see https://fb.me/react-unknown-prop%s', unknownPropString, element.type, ReactComponentTreeHook.getStackAddendumByID(debugID)) : void 0;
  } else if (unknownProps.length > 1) {
    "production" !== 'production' ? warning(false, 'Unknown props %s on <%s> tag. Remove these props from the element. ' + 'For details, see https://fb.me/react-unknown-prop%s', unknownPropString, element.type, ReactComponentTreeHook.getStackAddendumByID(debugID)) : void 0;
  }
};

function handleElement(debugID, element) {
  if (element == null || typeof element.type !== 'string') {
    return;
  }
  if (element.type.indexOf('-') >= 0 || element.props.is) {
    return;
  }
  warnUnknownProperties(debugID, element);
}

var ReactDOMUnknownPropertyHook = {
  onBeforeMountComponent: function (debugID, element) {
    handleElement(debugID, element);
  },
  onBeforeUpdateComponent: function (debugID, element) {
    handleElement(debugID, element);
  }
};

module.exports = ReactDOMUnknownPropertyHook;
},{"./DOMProperty":48,"./EventPluginRegistry":56,"./ReactComponentTreeHook":75,"fbjs/lib/warning":213}],98:[function(require,module,exports){
/**
 * Copyright 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDebugTool
 */

'use strict';

var ReactInvalidSetStateWarningHook = require('./ReactInvalidSetStateWarningHook');
var ReactHostOperationHistoryHook = require('./ReactHostOperationHistoryHook');
var ReactComponentTreeHook = require('./ReactComponentTreeHook');
var ReactChildrenMutationWarningHook = require('./ReactChildrenMutationWarningHook');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');

var performanceNow = require('fbjs/lib/performanceNow');
var warning = require('fbjs/lib/warning');

var hooks = [];
var didHookThrowForEvent = {};

function callHook(event, fn, context, arg1, arg2, arg3, arg4, arg5) {
  try {
    fn.call(context, arg1, arg2, arg3, arg4, arg5);
  } catch (e) {
    "production" !== 'production' ? warning(didHookThrowForEvent[event], 'Exception thrown by hook while handling %s: %s', event, e + '\n' + e.stack) : void 0;
    didHookThrowForEvent[event] = true;
  }
}

function emitEvent(event, arg1, arg2, arg3, arg4, arg5) {
  for (var i = 0; i < hooks.length; i++) {
    var hook = hooks[i];
    var fn = hook[event];
    if (fn) {
      callHook(event, fn, hook, arg1, arg2, arg3, arg4, arg5);
    }
  }
}

var isProfiling = false;
var flushHistory = [];
var lifeCycleTimerStack = [];
var currentFlushNesting = 0;
var currentFlushMeasurements = null;
var currentFlushStartTime = null;
var currentTimerDebugID = null;
var currentTimerStartTime = null;
var currentTimerNestedFlushDuration = null;
var currentTimerType = null;

var lifeCycleTimerHasWarned = false;

function clearHistory() {
  ReactComponentTreeHook.purgeUnmountedComponents();
  ReactHostOperationHistoryHook.clearHistory();
}

function getTreeSnapshot(registeredIDs) {
  return registeredIDs.reduce(function (tree, id) {
    var ownerID = ReactComponentTreeHook.getOwnerID(id);
    var parentID = ReactComponentTreeHook.getParentID(id);
    tree[id] = {
      displayName: ReactComponentTreeHook.getDisplayName(id),
      text: ReactComponentTreeHook.getText(id),
      updateCount: ReactComponentTreeHook.getUpdateCount(id),
      childIDs: ReactComponentTreeHook.getChildIDs(id),
      // Text nodes don't have owners but this is close enough.
      ownerID: ownerID || ReactComponentTreeHook.getOwnerID(parentID),
      parentID: parentID
    };
    return tree;
  }, {});
}

function resetMeasurements() {
  var previousStartTime = currentFlushStartTime;
  var previousMeasurements = currentFlushMeasurements || [];
  var previousOperations = ReactHostOperationHistoryHook.getHistory();

  if (currentFlushNesting === 0) {
    currentFlushStartTime = null;
    currentFlushMeasurements = null;
    clearHistory();
    return;
  }

  if (previousMeasurements.length || previousOperations.length) {
    var registeredIDs = ReactComponentTreeHook.getRegisteredIDs();
    flushHistory.push({
      duration: performanceNow() - previousStartTime,
      measurements: previousMeasurements || [],
      operations: previousOperations || [],
      treeSnapshot: getTreeSnapshot(registeredIDs)
    });
  }

  clearHistory();
  currentFlushStartTime = performanceNow();
  currentFlushMeasurements = [];
}

function checkDebugID(debugID) {
  var allowRoot = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];

  if (allowRoot && debugID === 0) {
    return;
  }
  if (!debugID) {
    "production" !== 'production' ? warning(false, 'ReactDebugTool: debugID may not be empty.') : void 0;
  }
}

function beginLifeCycleTimer(debugID, timerType) {
  if (currentFlushNesting === 0) {
    return;
  }
  if (currentTimerType && !lifeCycleTimerHasWarned) {
    "production" !== 'production' ? warning(false, 'There is an internal error in the React performance measurement code. ' + 'Did not expect %s timer to start while %s timer is still in ' + 'progress for %s instance.', timerType, currentTimerType || 'no', debugID === currentTimerDebugID ? 'the same' : 'another') : void 0;
    lifeCycleTimerHasWarned = true;
  }
  currentTimerStartTime = performanceNow();
  currentTimerNestedFlushDuration = 0;
  currentTimerDebugID = debugID;
  currentTimerType = timerType;
}

function endLifeCycleTimer(debugID, timerType) {
  if (currentFlushNesting === 0) {
    return;
  }
  if (currentTimerType !== timerType && !lifeCycleTimerHasWarned) {
    "production" !== 'production' ? warning(false, 'There is an internal error in the React performance measurement code. ' + 'We did not expect %s timer to stop while %s timer is still in ' + 'progress for %s instance. Please report this as a bug in React.', timerType, currentTimerType || 'no', debugID === currentTimerDebugID ? 'the same' : 'another') : void 0;
    lifeCycleTimerHasWarned = true;
  }
  if (isProfiling) {
    currentFlushMeasurements.push({
      timerType: timerType,
      instanceID: debugID,
      duration: performanceNow() - currentTimerStartTime - currentTimerNestedFlushDuration
    });
  }
  currentTimerStartTime = null;
  currentTimerNestedFlushDuration = null;
  currentTimerDebugID = null;
  currentTimerType = null;
}

function pauseCurrentLifeCycleTimer() {
  var currentTimer = {
    startTime: currentTimerStartTime,
    nestedFlushStartTime: performanceNow(),
    debugID: currentTimerDebugID,
    timerType: currentTimerType
  };
  lifeCycleTimerStack.push(currentTimer);
  currentTimerStartTime = null;
  currentTimerNestedFlushDuration = null;
  currentTimerDebugID = null;
  currentTimerType = null;
}

function resumeCurrentLifeCycleTimer() {
  var _lifeCycleTimerStack$ = lifeCycleTimerStack.pop();

  var startTime = _lifeCycleTimerStack$.startTime;
  var nestedFlushStartTime = _lifeCycleTimerStack$.nestedFlushStartTime;
  var debugID = _lifeCycleTimerStack$.debugID;
  var timerType = _lifeCycleTimerStack$.timerType;

  var nestedFlushDuration = performanceNow() - nestedFlushStartTime;
  currentTimerStartTime = startTime;
  currentTimerNestedFlushDuration += nestedFlushDuration;
  currentTimerDebugID = debugID;
  currentTimerType = timerType;
}

var ReactDebugTool = {
  addHook: function (hook) {
    hooks.push(hook);
  },
  removeHook: function (hook) {
    for (var i = 0; i < hooks.length; i++) {
      if (hooks[i] === hook) {
        hooks.splice(i, 1);
        i--;
      }
    }
  },
  isProfiling: function () {
    return isProfiling;
  },
  beginProfiling: function () {
    if (isProfiling) {
      return;
    }

    isProfiling = true;
    flushHistory.length = 0;
    resetMeasurements();
    ReactDebugTool.addHook(ReactHostOperationHistoryHook);
  },
  endProfiling: function () {
    if (!isProfiling) {
      return;
    }

    isProfiling = false;
    resetMeasurements();
    ReactDebugTool.removeHook(ReactHostOperationHistoryHook);
  },
  getFlushHistory: function () {
    return flushHistory;
  },
  onBeginFlush: function () {
    currentFlushNesting++;
    resetMeasurements();
    pauseCurrentLifeCycleTimer();
    emitEvent('onBeginFlush');
  },
  onEndFlush: function () {
    resetMeasurements();
    currentFlushNesting--;
    resumeCurrentLifeCycleTimer();
    emitEvent('onEndFlush');
  },
  onBeginLifeCycleTimer: function (debugID, timerType) {
    checkDebugID(debugID);
    emitEvent('onBeginLifeCycleTimer', debugID, timerType);
    beginLifeCycleTimer(debugID, timerType);
  },
  onEndLifeCycleTimer: function (debugID, timerType) {
    checkDebugID(debugID);
    endLifeCycleTimer(debugID, timerType);
    emitEvent('onEndLifeCycleTimer', debugID, timerType);
  },
  onBeginProcessingChildContext: function () {
    emitEvent('onBeginProcessingChildContext');
  },
  onEndProcessingChildContext: function () {
    emitEvent('onEndProcessingChildContext');
  },
  onHostOperation: function (debugID, type, payload) {
    checkDebugID(debugID);
    emitEvent('onHostOperation', debugID, type, payload);
  },
  onSetState: function () {
    emitEvent('onSetState');
  },
  onSetChildren: function (debugID, childDebugIDs) {
    checkDebugID(debugID);
    childDebugIDs.forEach(checkDebugID);
    emitEvent('onSetChildren', debugID, childDebugIDs);
  },
  onBeforeMountComponent: function (debugID, element, parentDebugID) {
    checkDebugID(debugID);
    checkDebugID(parentDebugID, true);
    emitEvent('onBeforeMountComponent', debugID, element, parentDebugID);
  },
  onMountComponent: function (debugID) {
    checkDebugID(debugID);
    emitEvent('onMountComponent', debugID);
  },
  onBeforeUpdateComponent: function (debugID, element) {
    checkDebugID(debugID);
    emitEvent('onBeforeUpdateComponent', debugID, element);
  },
  onUpdateComponent: function (debugID) {
    checkDebugID(debugID);
    emitEvent('onUpdateComponent', debugID);
  },
  onBeforeUnmountComponent: function (debugID) {
    checkDebugID(debugID);
    emitEvent('onBeforeUnmountComponent', debugID);
  },
  onUnmountComponent: function (debugID) {
    checkDebugID(debugID);
    emitEvent('onUnmountComponent', debugID);
  },
  onTestEvent: function () {
    emitEvent('onTestEvent');
  }
};

// TODO remove these when RN/www gets updated
ReactDebugTool.addDevtool = ReactDebugTool.addHook;
ReactDebugTool.removeDevtool = ReactDebugTool.removeHook;

ReactDebugTool.addHook(ReactInvalidSetStateWarningHook);
ReactDebugTool.addHook(ReactComponentTreeHook);
ReactDebugTool.addHook(ReactChildrenMutationWarningHook);
var url = ExecutionEnvironment.canUseDOM && window.location.href || '';
if (/[?&]react_perf\b/.test(url)) {
  ReactDebugTool.beginProfiling();
}

module.exports = ReactDebugTool;
},{"./ReactChildrenMutationWarningHook":70,"./ReactComponentTreeHook":75,"./ReactHostOperationHistoryHook":109,"./ReactInvalidSetStateWarningHook":114,"fbjs/lib/ExecutionEnvironment":190,"fbjs/lib/performanceNow":211,"fbjs/lib/warning":213}],99:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDefaultBatchingStrategy
 */

'use strict';

var _assign = require('object-assign');

var ReactUpdates = require('./ReactUpdates');
var Transaction = require('./Transaction');

var emptyFunction = require('fbjs/lib/emptyFunction');

var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function () {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  }
};

var FLUSH_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

function ReactDefaultBatchingStrategyTransaction() {
  this.reinitializeTransaction();
}

_assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction.Mixin, {
  getTransactionWrappers: function () {
    return TRANSACTION_WRAPPERS;
  }
});

var transaction = new ReactDefaultBatchingStrategyTransaction();

var ReactDefaultBatchingStrategy = {
  isBatchingUpdates: false,

  /**
   * Call the provided function in a context within which calls to `setState`
   * and friends are batched such that components aren't updated unnecessarily.
   */
  batchedUpdates: function (callback, a, b, c, d, e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;

    ReactDefaultBatchingStrategy.isBatchingUpdates = true;

    // The code is written this way to avoid extra allocations
    if (alreadyBatchingUpdates) {
      callback(a, b, c, d, e);
    } else {
      transaction.perform(callback, null, a, b, c, d, e);
    }
  }
};

module.exports = ReactDefaultBatchingStrategy;
},{"./ReactUpdates":136,"./Transaction":154,"fbjs/lib/emptyFunction":196,"object-assign":214}],100:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactDefaultInjection
 */

'use strict';

var BeforeInputEventPlugin = require('./BeforeInputEventPlugin');
var ChangeEventPlugin = require('./ChangeEventPlugin');
var DefaultEventPluginOrder = require('./DefaultEventPluginOrder');
var EnterLeaveEventPlugin = require('./EnterLeaveEventPlugin');
var HTMLDOMPropertyConfig = require('./HTMLDOMPropertyConfig');
var ReactComponentBrowserEnvironment = require('./ReactComponentBrowserEnvironment');
var ReactDOMComponent = require('./ReactDOMComponent');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactDOMEmptyComponent = require('./ReactDOMEmptyComponent');
var ReactDOMTreeTraversal = require('./ReactDOMTreeTraversal');
var ReactDOMTextComponent = require('./ReactDOMTextComponent');
var ReactDefaultBatchingStrategy = require('./ReactDefaultBatchingStrategy');
var ReactEventListener = require('./ReactEventListener');
var ReactInjection = require('./ReactInjection');
var ReactReconcileTransaction = require('./ReactReconcileTransaction');
var SVGDOMPropertyConfig = require('./SVGDOMPropertyConfig');
var SelectEventPlugin = require('./SelectEventPlugin');
var SimpleEventPlugin = require('./SimpleEventPlugin');

var alreadyInjected = false;

function inject() {
  if (alreadyInjected) {
    // TODO: This is currently true because these injections are shared between
    // the client and the server package. They should be built independently
    // and not share any injection state. Then this problem will be solved.
    return;
  }
  alreadyInjected = true;

  ReactInjection.EventEmitter.injectReactEventListener(ReactEventListener);

  /**
   * Inject modules for resolving DOM hierarchy and plugin ordering.
   */
  ReactInjection.EventPluginHub.injectEventPluginOrder(DefaultEventPluginOrder);
  ReactInjection.EventPluginUtils.injectComponentTree(ReactDOMComponentTree);
  ReactInjection.EventPluginUtils.injectTreeTraversal(ReactDOMTreeTraversal);

  /**
   * Some important event plugins included by default (without having to require
   * them).
   */
  ReactInjection.EventPluginHub.injectEventPluginsByName({
    SimpleEventPlugin: SimpleEventPlugin,
    EnterLeaveEventPlugin: EnterLeaveEventPlugin,
    ChangeEventPlugin: ChangeEventPlugin,
    SelectEventPlugin: SelectEventPlugin,
    BeforeInputEventPlugin: BeforeInputEventPlugin
  });

  ReactInjection.HostComponent.injectGenericComponentClass(ReactDOMComponent);

  ReactInjection.HostComponent.injectTextComponentClass(ReactDOMTextComponent);

  ReactInjection.DOMProperty.injectDOMPropertyConfig(HTMLDOMPropertyConfig);
  ReactInjection.DOMProperty.injectDOMPropertyConfig(SVGDOMPropertyConfig);

  ReactInjection.EmptyComponent.injectEmptyComponentFactory(function (instantiate) {
    return new ReactDOMEmptyComponent(instantiate);
  });

  ReactInjection.Updates.injectReconcileTransaction(ReactReconcileTransaction);
  ReactInjection.Updates.injectBatchingStrategy(ReactDefaultBatchingStrategy);

  ReactInjection.Component.injectEnvironment(ReactComponentBrowserEnvironment);
}

module.exports = {
  inject: inject
};
},{"./BeforeInputEventPlugin":40,"./ChangeEventPlugin":44,"./DefaultEventPluginOrder":51,"./EnterLeaveEventPlugin":53,"./HTMLDOMPropertyConfig":60,"./ReactComponentBrowserEnvironment":73,"./ReactDOMComponent":81,"./ReactDOMComponentTree":83,"./ReactDOMEmptyComponent":85,"./ReactDOMTextComponent":94,"./ReactDOMTreeTraversal":96,"./ReactDefaultBatchingStrategy":99,"./ReactEventListener":106,"./ReactInjection":110,"./ReactReconcileTransaction":127,"./SVGDOMPropertyConfig":138,"./SelectEventPlugin":139,"./SimpleEventPlugin":140}],101:[function(require,module,exports){
/**
 * Copyright 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactElement
 */

'use strict';

var _assign = require('object-assign');

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

var warning = require('fbjs/lib/warning');
var canDefineProperty = require('./canDefineProperty');
var hasOwnProperty = Object.prototype.hasOwnProperty;

// The Symbol used to tag the ReactElement type. If there is no native Symbol
// nor polyfill, then a plain number is used for performance.
var REACT_ELEMENT_TYPE = typeof Symbol === 'function' && Symbol['for'] && Symbol['for']('react.element') || 0xeac7;

var RESERVED_PROPS = {
  key: true,
  ref: true,
  __self: true,
  __source: true
};

var specialPropKeyWarningShown, specialPropRefWarningShown;

function hasValidRef(config) {
  if ("production" !== 'production') {
    if (hasOwnProperty.call(config, 'ref')) {
      var getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
      if (getter && getter.isReactWarning) {
        return false;
      }
    }
  }
  return config.ref !== undefined;
}

function hasValidKey(config) {
  if ("production" !== 'production') {
    if (hasOwnProperty.call(config, 'key')) {
      var getter = Object.getOwnPropertyDescriptor(config, 'key').get;
      if (getter && getter.isReactWarning) {
        return false;
      }
    }
  }
  return config.key !== undefined;
}

function defineKeyPropWarningGetter(props, displayName) {
  var warnAboutAccessingKey = function () {
    if (!specialPropKeyWarningShown) {
      specialPropKeyWarningShown = true;
      "production" !== 'production' ? warning(false, '%s: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://fb.me/react-special-props)', displayName) : void 0;
    }
  };
  warnAboutAccessingKey.isReactWarning = true;
  Object.defineProperty(props, 'key', {
    get: warnAboutAccessingKey,
    configurable: true
  });
}

function defineRefPropWarningGetter(props, displayName) {
  var warnAboutAccessingRef = function () {
    if (!specialPropRefWarningShown) {
      specialPropRefWarningShown = true;
      "production" !== 'production' ? warning(false, '%s: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://fb.me/react-special-props)', displayName) : void 0;
    }
  };
  warnAboutAccessingRef.isReactWarning = true;
  Object.defineProperty(props, 'ref', {
    get: warnAboutAccessingRef,
    configurable: true
  });
}

/**
 * Factory method to create a new React element. This no longer adheres to
 * the class pattern, so do not use new to call it. Also, no instanceof check
 * will work. Instead test $$typeof field against Symbol.for('react.element') to check
 * if something is a React Element.
 *
 * @param {*} type
 * @param {*} key
 * @param {string|object} ref
 * @param {*} self A *temporary* helper to detect places where `this` is
 * different from the `owner` when React.createElement is called, so that we
 * can warn. We want to get rid of owner and replace string `ref`s with arrow
 * functions, and as long as `this` and owner are the same, there will be no
 * change in behavior.
 * @param {*} source An annotation object (added by a transpiler or otherwise)
 * indicating filename, line number, and/or other information.
 * @param {*} owner
 * @param {*} props
 * @internal
 */
var ReactElement = function (type, key, ref, self, source, owner, props) {
  var element = {
    // This tag allow us to uniquely identify this as a React Element
    $$typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,

    // Record the component responsible for creating this element.
    _owner: owner
  };

  if ("production" !== 'production') {
    // The validation flag is currently mutative. We put it on
    // an external backing store so that we can freeze the whole object.
    // This can be replaced with a WeakMap once they are implemented in
    // commonly used development environments.
    element._store = {};
    var shadowChildren = Array.isArray(props.children) ? props.children.slice(0) : props.children;

    // To make comparing ReactElements easier for testing purposes, we make
    // the validation flag non-enumerable (where possible, which should
    // include every environment we run tests in), so the test framework
    // ignores it.
    if (canDefineProperty) {
      Object.defineProperty(element._store, 'validated', {
        configurable: false,
        enumerable: false,
        writable: true,
        value: false
      });
      // self and source are DEV only properties.
      Object.defineProperty(element, '_self', {
        configurable: false,
        enumerable: false,
        writable: false,
        value: self
      });
      Object.defineProperty(element, '_shadowChildren', {
        configurable: false,
        enumerable: false,
        writable: false,
        value: shadowChildren
      });
      // Two elements created in two different places should be considered
      // equal for testing purposes and therefore we hide it from enumeration.
      Object.defineProperty(element, '_source', {
        configurable: false,
        enumerable: false,
        writable: false,
        value: source
      });
    } else {
      element._store.validated = false;
      element._self = self;
      element._shadowChildren = shadowChildren;
      element._source = source;
    }
    if (Object.freeze) {
      Object.freeze(element.props);
      Object.freeze(element);
    }
  }

  return element;
};

/**
 * Create and return a new ReactElement of the given type.
 * See https://facebook.github.io/react/docs/top-level-api.html#react.createelement
 */
ReactElement.createElement = function (type, config, children) {
  var propName;

  // Reserved names are extracted
  var props = {};

  var key = null;
  var ref = null;
  var self = null;
  var source = null;

  if (config != null) {
    if (hasValidRef(config)) {
      ref = config.ref;
    }
    if (hasValidKey(config)) {
      key = '' + config.key;
    }

    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // Remaining properties are added to a new props object
    for (propName in config) {
      if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
        props[propName] = config[propName];
      }
    }
  }

  // Children can be more than one argument, and those are transferred onto
  // the newly allocated props object.
  var childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    var childArray = Array(childrenLength);
    for (var i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    props.children = childArray;
  }

  // Resolve default props
  if (type && type.defaultProps) {
    var defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  if ("production" !== 'production') {
    if (key || ref) {
      if (typeof props.$$typeof === 'undefined' || props.$$typeof !== REACT_ELEMENT_TYPE) {
        var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
        if (key) {
          defineKeyPropWarningGetter(props, displayName);
        }
        if (ref) {
          defineRefPropWarningGetter(props, displayName);
        }
      }
    }
  }
  return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
};

/**
 * Return a function that produces ReactElements of a given type.
 * See https://facebook.github.io/react/docs/top-level-api.html#react.createfactory
 */
ReactElement.createFactory = function (type) {
  var factory = ReactElement.createElement.bind(null, type);
  // Expose the type on the factory and the prototype so that it can be
  // easily accessed on elements. E.g. `<Foo />.type === Foo`.
  // This should not be named `constructor` since this may not be the function
  // that created the element, and it may not even be a constructor.
  // Legacy hook TODO: Warn if this is accessed
  factory.type = type;
  return factory;
};

ReactElement.cloneAndReplaceKey = function (oldElement, newKey) {
  var newElement = ReactElement(oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props);

  return newElement;
};

/**
 * Clone and return a new ReactElement using element as the starting point.
 * See https://facebook.github.io/react/docs/top-level-api.html#react.cloneelement
 */
ReactElement.cloneElement = function (element, config, children) {
  var propName;

  // Original props are copied
  var props = _assign({}, element.props);

  // Reserved names are extracted
  var key = element.key;
  var ref = element.ref;
  // Self is preserved since the owner is preserved.
  var self = element._self;
  // Source is preserved since cloneElement is unlikely to be targeted by a
  // transpiler, and the original source is probably a better indicator of the
  // true owner.
  var source = element._source;

  // Owner will be preserved, unless ref is overridden
  var owner = element._owner;

  if (config != null) {
    if (hasValidRef(config)) {
      // Silently steal the ref from the parent.
      ref = config.ref;
      owner = ReactCurrentOwner.current;
    }
    if (hasValidKey(config)) {
      key = '' + config.key;
    }

    // Remaining properties override existing props
    var defaultProps;
    if (element.type && element.type.defaultProps) {
      defaultProps = element.type.defaultProps;
    }
    for (propName in config) {
      if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
        if (config[propName] === undefined && defaultProps !== undefined) {
          // Resolve default props
          props[propName] = defaultProps[propName];
        } else {
          props[propName] = config[propName];
        }
      }
    }
  }

  // Children can be more than one argument, and those are transferred onto
  // the newly allocated props object.
  var childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    var childArray = Array(childrenLength);
    for (var i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    props.children = childArray;
  }

  return ReactElement(element.type, key, ref, self, source, owner, props);
};

/**
 * Verifies the object is a ReactElement.
 * See https://facebook.github.io/react/docs/top-level-api.html#react.isvalidelement
 * @param {?object} object
 * @return {boolean} True if `object` is a valid component.
 * @final
 */
ReactElement.isValidElement = function (object) {
  return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
};

ReactElement.REACT_ELEMENT_TYPE = REACT_ELEMENT_TYPE;

module.exports = ReactElement;
},{"./ReactCurrentOwner":78,"./canDefineProperty":158,"fbjs/lib/warning":213,"object-assign":214}],102:[function(require,module,exports){
/**
 * Copyright 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactElementValidator
 */

/**
 * ReactElementValidator provides a wrapper around a element factory
 * which validates the props passed to the element. This is intended to be
 * used only in DEV and could be replaced by a static type checker for languages
 * that support it.
 */

'use strict';

var ReactCurrentOwner = require('./ReactCurrentOwner');
var ReactComponentTreeHook = require('./ReactComponentTreeHook');
var ReactElement = require('./ReactElement');
var ReactPropTypeLocations = require('./ReactPropTypeLocations');

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

var canDefineProperty = require('./canDefineProperty');
var getIteratorFn = require('./getIteratorFn');
var warning = require('fbjs/lib/warning');

function getDeclarationErrorAddendum() {
  if (ReactCurrentOwner.current) {
    var name = ReactCurrentOwner.current.getName();
    if (name) {
      return ' Check the render method of `' + name + '`.';
    }
  }
  return '';
}

/**
 * Warn if there's no key explicitly set on dynamic arrays of children or
 * object keys are not valid. This allows us to keep track of children between
 * updates.
 */
var ownerHasKeyUseWarning = {};

function getCurrentComponentErrorInfo(parentType) {
  var info = getDeclarationErrorAddendum();

  if (!info) {
    var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name;
    if (parentName) {
      info = ' Check the top-level render call using <' + parentName + '>.';
    }
  }
  return info;
}

/**
 * Warn if the element doesn't have an explicit key assigned to it.
 * This element is in an array. The array could grow and shrink or be
 * reordered. All children that haven't already been validated are required to
 * have a "key" property assigned to it. Error statuses are cached so a warning
 * will only be shown once.
 *
 * @internal
 * @param {ReactElement} element Element that requires a key.
 * @param {*} parentType element's parent's type.
 */
function validateExplicitKey(element, parentType) {
  if (!element._store || element._store.validated || element.key != null) {
    return;
  }
  element._store.validated = true;

  var memoizer = ownerHasKeyUseWarning.uniqueKey || (ownerHasKeyUseWarning.uniqueKey = {});

  var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
  if (memoizer[currentComponentErrorInfo]) {
    return;
  }
  memoizer[currentComponentErrorInfo] = true;

  // Usually the current owner is the offender, but if it accepts children as a
  // property, it may be the creator of the child that's responsible for
  // assigning it a key.
  var childOwner = '';
  if (element && element._owner && element._owner !== ReactCurrentOwner.current) {
    // Give the component that originally created this child.
    childOwner = ' It was passed a child from ' + element._owner.getName() + '.';
  }

  "production" !== 'production' ? warning(false, 'Each child in an array or iterator should have a unique "key" prop.' + '%s%s See https://fb.me/react-warning-keys for more information.%s', currentComponentErrorInfo, childOwner, ReactComponentTreeHook.getCurrentStackAddendum(element)) : void 0;
}

/**
 * Ensure that every element either is passed in a static location, in an
 * array with an explicit keys property defined, or in an object literal
 * with valid key property.
 *
 * @internal
 * @param {ReactNode} node Statically passed child of any type.
 * @param {*} parentType node's parent's type.
 */
function validateChildKeys(node, parentType) {
  if (typeof node !== 'object') {
    return;
  }
  if (Array.isArray(node)) {
    for (var i = 0; i < node.length; i++) {
      var child = node[i];
      if (ReactElement.isValidElement(child)) {
        validateExplicitKey(child, parentType);
      }
    }
  } else if (ReactElement.isValidElement(node)) {
    // This element was passed in a valid location.
    if (node._store) {
      node._store.validated = true;
    }
  } else if (node) {
    var iteratorFn = getIteratorFn(node);
    // Entry iterators provide implicit keys.
    if (iteratorFn) {
      if (iteratorFn !== node.entries) {
        var iterator = iteratorFn.call(node);
        var step;
        while (!(step = iterator.next()).done) {
          if (ReactElement.isValidElement(step.value)) {
            validateExplicitKey(step.value, parentType);
          }
        }
      }
    }
  }
}

/**
 * Given an element, validate that its props follow the propTypes definition,
 * provided by the type.
 *
 * @param {ReactElement} element
 */
function validatePropTypes(element) {
  var componentClass = element.type;
  if (typeof componentClass !== 'function') {
    return;
  }
  var name = componentClass.displayName || componentClass.name;
  if (componentClass.propTypes) {
    checkReactTypeSpec(componentClass.propTypes, element.props, ReactPropTypeLocations.prop, name, element, null);
  }
  if (typeof componentClass.getDefaultProps === 'function') {
    "production" !== 'production' ? warning(componentClass.getDefaultProps.isReactClassApproved, 'getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.') : void 0;
  }
}

var ReactElementValidator = {

  createElement: function (type, props, children) {
    var validType = typeof type === 'string' || typeof type === 'function';
    // We warn in this case but don't throw. We expect the element creation to
    // succeed and there will likely be errors in render.
    if (!validType) {
      "production" !== 'production' ? warning(false, 'React.createElement: type should not be null, undefined, boolean, or ' + 'number. It should be a string (for DOM elements) or a ReactClass ' + '(for composite components).%s', getDeclarationErrorAddendum()) : void 0;
    }

    var element = ReactElement.createElement.apply(this, arguments);

    // The result can be nullish if a mock or a custom function is used.
    // TODO: Drop this when these are no longer allowed as the type argument.
    if (element == null) {
      return element;
    }

    // Skip key warning if the type isn't valid since our key validation logic
    // doesn't expect a non-string/function type and can throw confusing errors.
    // We don't want exception behavior to differ between dev and prod.
    // (Rendering will throw with a helpful message and as soon as the type is
    // fixed, the key warnings will appear.)
    if (validType) {
      for (var i = 2; i < arguments.length; i++) {
        validateChildKeys(arguments[i], type);
      }
    }

    validatePropTypes(element);

    return element;
  },

  createFactory: function (type) {
    var validatedFactory = ReactElementValidator.createElement.bind(null, type);
    // Legacy hook TODO: Warn if this is accessed
    validatedFactory.type = type;

    if ("production" !== 'production') {
      if (canDefineProperty) {
        Object.defineProperty(validatedFactory, 'type', {
          enumerable: false,
          get: function () {
            "production" !== 'production' ? warning(false, 'Factory.type is deprecated. Access the class directly ' + 'before passing it to createFactory.') : void 0;
            Object.defineProperty(this, 'type', {
              value: type
            });
            return type;
          }
        });
      }
    }

    return validatedFactory;
  },

  cloneElement: function (element, props, children) {
    var newElement = ReactElement.cloneElement.apply(this, arguments);
    for (var i = 2; i < arguments.length; i++) {
      validateChildKeys(arguments[i], newElement.type);
    }
    validatePropTypes(newElement);
    return newElement;
  }

};

module.exports = ReactElementValidator;
},{"./ReactComponentTreeHook":75,"./ReactCurrentOwner":78,"./ReactElement":101,"./ReactPropTypeLocations":123,"./canDefineProperty":158,"./checkReactTypeSpec":159,"./getIteratorFn":171,"fbjs/lib/warning":213}],103:[function(require,module,exports){
/**
 * Copyright 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactEmptyComponent
 */

'use strict';

var emptyComponentFactory;

var ReactEmptyComponentInjection = {
  injectEmptyComponentFactory: function (factory) {
    emptyComponentFactory = factory;
  }
};

var ReactEmptyComponent = {
  create: function (instantiate) {
    return emptyComponentFactory(instantiate);
  }
};

ReactEmptyComponent.injection = ReactEmptyComponentInjection;

module.exports = ReactEmptyComponent;
},{}],104:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactErrorUtils
 */

'use strict';

var caughtError = null;

/**
 * Call a function while guarding against errors that happens within it.
 *
 * @param {?String} name of the guard to use for logging or debugging
 * @param {Function} func The function to invoke
 * @param {*} a First argument
 * @param {*} b Second argument
 */
function invokeGuardedCallback(name, func, a, b) {
  try {
    return func(a, b);
  } catch (x) {
    if (caughtError === null) {
      caughtError = x;
    }
    return undefined;
  }
}

var ReactErrorUtils = {
  invokeGuardedCallback: invokeGuardedCallback,

  /**
   * Invoked by ReactTestUtils.Simulate so that any errors thrown by the event
   * handler are sure to be rethrown by rethrowCaughtError.
   */
  invokeGuardedCallbackWithCatch: invokeGuardedCallback,

  /**
   * During execution of guarded functions we will capture the first error which
   * we will rethrow to be handled by the top level error handler.
   */
  rethrowCaughtError: function () {
    if (caughtError) {
      var error = caughtError;
      caughtError = null;
      throw error;
    }
  }
};

if ("production" !== 'production') {
  /**
   * To help development we can get better devtools integration by simulating a
   * real browser event.
   */
  if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function' && typeof document !== 'undefined' && typeof document.createEvent === 'function') {
    var fakeNode = document.createElement('react');
    ReactErrorUtils.invokeGuardedCallback = function (name, func, a, b) {
      var boundFunc = func.bind(null, a, b);
      var evtType = 'react-' + name;
      fakeNode.addEventListener(evtType, boundFunc, false);
      var evt = document.createEvent('Event');
      evt.initEvent(evtType, false, false);
      fakeNode.dispatchEvent(evt);
      fakeNode.removeEventListener(evtType, boundFunc, false);
    };
  }
}

module.exports = ReactErrorUtils;
},{}],105:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactEventEmitterMixin
 */

'use strict';

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

function runEventQueueInBatch(events) {
  EventPluginHub.enqueueEvents(events);
  EventPluginHub.processEventQueue(false);
}

var ReactEventEmitterMixin = {

  /**
   * Streams a fired top-level event to `EventPluginHub` where plugins have the
   * opportunity to create `ReactEvent`s to be dispatched.
   */
  handleTopLevel: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
    var events = EventPluginHub.extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget);
    runEventQueueInBatch(events);
  }
};

module.exports = ReactEventEmitterMixin;
},{"./EventPluginHub":55}],106:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactEventListener
 */

'use strict';

var _assign = require('object-assign');

var EventListener = require('fbjs/lib/EventListener');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
var PooledClass = require('./PooledClass');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactUpdates = require('./ReactUpdates');

var getEventTarget = require('./getEventTarget');
var getUnboundedScrollPosition = require('fbjs/lib/getUnboundedScrollPosition');

/**
 * Find the deepest React component completely containing the root of the
 * passed-in instance (for use when entire React trees are nested within each
 * other). If React trees are not nested, returns null.
 */
function findParent(inst) {
  // TODO: It may be a good idea to cache this to prevent unnecessary DOM
  // traversal, but caching is difficult to do correctly without using a
  // mutation observer to listen for all DOM changes.
  while (inst._hostParent) {
    inst = inst._hostParent;
  }
  var rootNode = ReactDOMComponentTree.getNodeFromInstance(inst);
  var container = rootNode.parentNode;
  return ReactDOMComponentTree.getClosestInstanceFromNode(container);
}

// Used to store ancestor hierarchy in top level callback
function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) {
  this.topLevelType = topLevelType;
  this.nativeEvent = nativeEvent;
  this.ancestors = [];
}
_assign(TopLevelCallbackBookKeeping.prototype, {
  destructor: function () {
    this.topLevelType = null;
    this.nativeEvent = null;
    this.ancestors.length = 0;
  }
});
PooledClass.addPoolingTo(TopLevelCallbackBookKeeping, PooledClass.twoArgumentPooler);

function handleTopLevelImpl(bookKeeping) {
  var nativeEventTarget = getEventTarget(bookKeeping.nativeEvent);
  var targetInst = ReactDOMComponentTree.getClosestInstanceFromNode(nativeEventTarget);

  // Loop through the hierarchy, in case there's any nested components.
  // It's important that we build the array of ancestors before calling any
  // event handlers, because event handlers can modify the DOM, leading to
  // inconsistencies with ReactMount's node cache. See #1105.
  var ancestor = targetInst;
  do {
    bookKeeping.ancestors.push(ancestor);
    ancestor = ancestor && findParent(ancestor);
  } while (ancestor);

  for (var i = 0; i < bookKeeping.ancestors.length; i++) {
    targetInst = bookKeeping.ancestors[i];
    ReactEventListener._handleTopLevel(bookKeeping.topLevelType, targetInst, bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent));
  }
}

function scrollValueMonitor(cb) {
  var scrollPosition = getUnboundedScrollPosition(window);
  cb(scrollPosition);
}

var ReactEventListener = {
  _enabled: true,
  _handleTopLevel: null,

  WINDOW_HANDLE: ExecutionEnvironment.canUseDOM ? window : null,

  setHandleTopLevel: function (handleTopLevel) {
    ReactEventListener._handleTopLevel = handleTopLevel;
  },

  setEnabled: function (enabled) {
    ReactEventListener._enabled = !!enabled;
  },

  isEnabled: function () {
    return ReactEventListener._enabled;
  },

  /**
   * Traps top-level events by using event bubbling.
   *
   * @param {string} topLevelType Record from `EventConstants`.
   * @param {string} handlerBaseName Event name (e.g. "click").
   * @param {object} handle Element on which to attach listener.
   * @return {?object} An object with a remove function which will forcefully
   *                  remove the listener.
   * @internal
   */
  trapBubbledEvent: function (topLevelType, handlerBaseName, handle) {
    var element = handle;
    if (!element) {
      return null;
    }
    return EventListener.listen(element, handlerBaseName, ReactEventListener.dispatchEvent.bind(null, topLevelType));
  },

  /**
   * Traps a top-level event by using event capturing.
   *
   * @param {string} topLevelType Record from `EventConstants`.
   * @param {string} handlerBaseName Event name (e.g. "click").
   * @param {object} handle Element on which to attach listener.
   * @return {?object} An object with a remove function which will forcefully
   *                  remove the listener.
   * @internal
   */
  trapCapturedEvent: function (topLevelType, handlerBaseName, handle) {
    var element = handle;
    if (!element) {
      return null;
    }
    return EventListener.capture(element, handlerBaseName, ReactEventListener.dispatchEvent.bind(null, topLevelType));
  },

  monitorScrollValue: function (refresh) {
    var callback = scrollValueMonitor.bind(null, refresh);
    EventListener.listen(window, 'scroll', callback);
  },

  dispatchEvent: function (topLevelType, nativeEvent) {
    if (!ReactEventListener._enabled) {
      return;
    }

    var bookKeeping = TopLevelCallbackBookKeeping.getPooled(topLevelType, nativeEvent);
    try {
      // Event queue being processed in the same cycle allows
      // `preventDefault`.
      ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
    } finally {
      TopLevelCallbackBookKeeping.release(bookKeeping);
    }
  }
};

module.exports = ReactEventListener;
},{"./PooledClass":63,"./ReactDOMComponentTree":83,"./ReactUpdates":136,"./getEventTarget":169,"fbjs/lib/EventListener":189,"fbjs/lib/ExecutionEnvironment":190,"fbjs/lib/getUnboundedScrollPosition":201,"object-assign":214}],107:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactFeatureFlags
 * 
 */

'use strict';

var ReactFeatureFlags = {
  // When true, call console.time() before and .timeEnd() after each top-level
  // render (both initial renders and updates). Useful when looking at prod-mode
  // timeline profiles in Chrome, for example.
  logTopLevelRenders: false
};

module.exports = ReactFeatureFlags;
},{}],108:[function(require,module,exports){
/**
 * Copyright 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactHostComponent
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant'),
    _assign = require('object-assign');

var invariant = require('fbjs/lib/invariant');

var genericComponentClass = null;
// This registry keeps track of wrapper classes around host tags.
var tagToComponentClass = {};
var textComponentClass = null;

var ReactHostComponentInjection = {
  // This accepts a class that receives the tag string. This is a catch all
  // that can render any kind of tag.
  injectGenericComponentClass: function (componentClass) {
    genericComponentClass = componentClass;
  },
  // This accepts a text component class that takes the text string to be
  // rendered as props.
  injectTextComponentClass: function (componentClass) {
    textComponentClass = componentClass;
  },
  // This accepts a keyed object with classes as values. Each key represents a
  // tag. That particular tag will use this class instead of the generic one.
  injectComponentClasses: function (componentClasses) {
    _assign(tagToComponentClass, componentClasses);
  }
};

/**
 * Get a host internal component class for a specific tag.
 *
 * @param {ReactElement} element The element to create.
 * @return {function} The internal class constructor function.
 */
function createInternalComponent(element) {
  !genericComponentClass ? "production" !== 'production' ? invariant(false, 'There is no registered component for the tag %s', element.type) : _prodInvariant('111', element.type) : void 0;
  return new genericComponentClass(element);
}

/**
 * @param {ReactText} text
 * @return {ReactComponent}
 */
function createInstanceForText(text) {
  return new textComponentClass(text);
}

/**
 * @param {ReactComponent} component
 * @return {boolean}
 */
function isTextComponent(component) {
  return component instanceof textComponentClass;
}

var ReactHostComponent = {
  createInternalComponent: createInternalComponent,
  createInstanceForText: createInstanceForText,
  isTextComponent: isTextComponent,
  injection: ReactHostComponentInjection
};

module.exports = ReactHostComponent;
},{"./reactProdInvariant":180,"fbjs/lib/invariant":204,"object-assign":214}],109:[function(require,module,exports){
/**
 * Copyright 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactHostOperationHistoryHook
 */

'use strict';

var history = [];

var ReactHostOperationHistoryHook = {
  onHostOperation: function (debugID, type, payload) {
    history.push({
      instanceID: debugID,
      type: type,
      payload: payload
    });
  },
  clearHistory: function () {
    if (ReactHostOperationHistoryHook._preventClearing) {
      // Should only be used for tests.
      return;
    }

    history = [];
  },
  getHistory: function () {
    return history;
  }
};

module.exports = ReactHostOperationHistoryHook;
},{}],110:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactInjection
 */

'use strict';

var DOMProperty = require('./DOMProperty');
var EventPluginHub = require('./EventPluginHub');
var EventPluginUtils = require('./EventPluginUtils');
var ReactComponentEnvironment = require('./ReactComponentEnvironment');
var ReactClass = require('./ReactClass');
var ReactEmptyComponent = require('./ReactEmptyComponent');
var ReactBrowserEventEmitter = require('./ReactBrowserEventEmitter');
var ReactHostComponent = require('./ReactHostComponent');
var ReactUpdates = require('./ReactUpdates');

var ReactInjection = {
  Component: ReactComponentEnvironment.injection,
  Class: ReactClass.injection,
  DOMProperty: DOMProperty.injection,
  EmptyComponent: ReactEmptyComponent.injection,
  EventPluginHub: EventPluginHub.injection,
  EventPluginUtils: EventPluginUtils.injection,
  EventEmitter: ReactBrowserEventEmitter.injection,
  HostComponent: ReactHostComponent.injection,
  Updates: ReactUpdates.injection
};

module.exports = ReactInjection;
},{"./DOMProperty":48,"./EventPluginHub":55,"./EventPluginUtils":57,"./ReactBrowserEventEmitter":65,"./ReactClass":71,"./ReactComponentEnvironment":74,"./ReactEmptyComponent":103,"./ReactHostComponent":108,"./ReactUpdates":136}],111:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactInputSelection
 */

'use strict';

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

var containsNode = require('fbjs/lib/containsNode');
var focusNode = require('fbjs/lib/focusNode');
var getActiveElement = require('fbjs/lib/getActiveElement');

function isInDocument(node) {
  return containsNode(document.documentElement, node);
}

/**
 * @ReactInputSelection: React input selection module. Based on Selection.js,
 * but modified to be suitable for react and has a couple of bug fixes (doesn't
 * assume buttons have range selections allowed).
 * Input selection module for React.
 */
var ReactInputSelection = {

  hasSelectionCapabilities: function (elem) {
    var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
    return nodeName && (nodeName === 'input' && elem.type === 'text' || nodeName === 'textarea' || elem.contentEditable === 'true');
  },

  getSelectionInformation: function () {
    var focusedElem = getActiveElement();
    return {
      focusedElem: focusedElem,
      selectionRange: ReactInputSelection.hasSelectionCapabilities(focusedElem) ? ReactInputSelection.getSelection(focusedElem) : null
    };
  },

  /**
   * @restoreSelection: If any selection information was potentially lost,
   * restore it. This is useful when performing operations that could remove dom
   * nodes and place them back in, resulting in focus being lost.
   */
  restoreSelection: function (priorSelectionInformation) {
    var curFocusedElem = getActiveElement();
    var priorFocusedElem = priorSelectionInformation.focusedElem;
    var priorSelectionRange = priorSelectionInformation.selectionRange;
    if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
      if (ReactInputSelection.hasSelectionCapabilities(priorFocusedElem)) {
        ReactInputSelection.setSelection(priorFocusedElem, priorSelectionRange);
      }
      focusNode(priorFocusedElem);
    }
  },

  /**
   * @getSelection: Gets the selection bounds of a focused textarea, input or
   * contentEditable node.
   * -@input: Look up selection bounds of this input
   * -@return {start: selectionStart, end: selectionEnd}
   */
  getSelection: function (input) {
    var selection;

    if ('selectionStart' in input) {
      // Modern browser with input or textarea.
      selection = {
        start: input.selectionStart,
        end: input.selectionEnd
      };
    } else if (document.selection && input.nodeName && input.nodeName.toLowerCase() === 'input') {
      // IE8 input.
      var range = document.selection.createRange();
      // There can only be one selection per document in IE, so it must
      // be in our element.
      if (range.parentElement() === input) {
        selection = {
          start: -range.moveStart('character', -input.value.length),
          end: -range.moveEnd('character', -input.value.length)
        };
      }
    } else {
      // Content editable or old IE textarea.
      selection = ReactDOMSelection.getOffsets(input);
    }

    return selection || { start: 0, end: 0 };
  },

  /**
   * @setSelection: Sets the selection bounds of a textarea or input and focuses
   * the input.
   * -@input     Set selection bounds of this input or textarea
   * -@offsets   Object of same form that is returned from get*
   */
  setSelection: function (input, offsets) {
    var start = offsets.start;
    var end = offsets.end;
    if (end === undefined) {
      end = start;
    }

    if ('selectionStart' in input) {
      input.selectionStart = start;
      input.selectionEnd = Math.min(end, input.value.length);
    } else if (document.selection && input.nodeName && input.nodeName.toLowerCase() === 'input') {
      var range = input.createTextRange();
      range.collapse(true);
      range.moveStart('character', start);
      range.moveEnd('character', end - start);
      range.select();
    } else {
      ReactDOMSelection.setOffsets(input, offsets);
    }
  }
};

module.exports = ReactInputSelection;
},{"./ReactDOMSelection":93,"fbjs/lib/containsNode":193,"fbjs/lib/focusNode":198,"fbjs/lib/getActiveElement":199}],112:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactInstanceMap
 */

'use strict';

/**
 * `ReactInstanceMap` maintains a mapping from a public facing stateful
 * instance (key) and the internal representation (value). This allows public
 * methods to accept the user facing instance as an argument and map them back
 * to internal methods.
 */

// TODO: Replace this with ES6: var ReactInstanceMap = new Map();

var ReactInstanceMap = {

  /**
   * This API should be called `delete` but we'd have to make sure to always
   * transform these to strings for IE support. When this transform is fully
   * supported we can rename it.
   */
  remove: function (key) {
    key._reactInternalInstance = undefined;
  },

  get: function (key) {
    return key._reactInternalInstance;
  },

  has: function (key) {
    return key._reactInternalInstance !== undefined;
  },

  set: function (key, value) {
    key._reactInternalInstance = value;
  }

};

module.exports = ReactInstanceMap;
},{}],113:[function(require,module,exports){
/**
 * Copyright 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactInstrumentation
 */

'use strict';

var debugTool = null;

if ("production" !== 'production') {
  var ReactDebugTool = require('./ReactDebugTool');
  debugTool = ReactDebugTool;
}

module.exports = { debugTool: debugTool };
},{"./ReactDebugTool":98}],114:[function(require,module,exports){
/**
 * Copyright 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactInvalidSetStateWarningHook
 */

'use strict';

var warning = require('fbjs/lib/warning');

if ("production" !== 'production') {
  var processingChildContext = false;

  var warnInvalidSetState = function () {
    "production" !== 'production' ? warning(!processingChildContext, 'setState(...): Cannot call setState() inside getChildContext()') : void 0;
  };
}

var ReactInvalidSetStateWarningHook = {
  onBeginProcessingChildContext: function () {
    processingChildContext = true;
  },
  onEndProcessingChildContext: function () {
    processingChildContext = false;
  },
  onSetState: function () {
    warnInvalidSetState();
  }
};

module.exports = ReactInvalidSetStateWarningHook;
},{"fbjs/lib/warning":213}],115:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactMarkupChecksum
 */

'use strict';

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

var TAG_END = /\/?>/;
var COMMENT_START = /^<\!\-\-/;

var ReactMarkupChecksum = {
  CHECKSUM_ATTR_NAME: 'data-react-checksum',

  /**
   * @param {string} markup Markup string
   * @return {string} Markup string with checksum attribute attached
   */
  addChecksumToMarkup: function (markup) {
    var checksum = adler32(markup);

    // Add checksum (handle both parent tags, comments and self-closing tags)
    if (COMMENT_START.test(markup)) {
      return markup;
    } else {
      return markup.replace(TAG_END, ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="' + checksum + '"$&');
    }
  },

  /**
   * @param {string} markup to use
   * @param {DOMElement} element root React element
   * @returns {boolean} whether or not the markup is the same
   */
  canReuseMarkup: function (markup, element) {
    var existingChecksum = element.getAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
    existingChecksum = existingChecksum && parseInt(existingChecksum, 10);
    var markupChecksum = adler32(markup);
    return markupChecksum === existingChecksum;
  }
};

module.exports = ReactMarkupChecksum;
},{"./adler32":157}],116:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactMount
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var DOMLazyTree = require('./DOMLazyTree');
var DOMProperty = require('./DOMProperty');
var ReactBrowserEventEmitter = require('./ReactBrowserEventEmitter');
var ReactCurrentOwner = require('./ReactCurrentOwner');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactDOMContainerInfo = require('./ReactDOMContainerInfo');
var ReactDOMFeatureFlags = require('./ReactDOMFeatureFlags');
var ReactElement = require('./ReactElement');
var ReactFeatureFlags = require('./ReactFeatureFlags');
var ReactInstanceMap = require('./ReactInstanceMap');
var ReactInstrumentation = require('./ReactInstrumentation');
var ReactMarkupChecksum = require('./ReactMarkupChecksum');
var ReactReconciler = require('./ReactReconciler');
var ReactUpdateQueue = require('./ReactUpdateQueue');
var ReactUpdates = require('./ReactUpdates');

var emptyObject = require('fbjs/lib/emptyObject');
var instantiateReactComponent = require('./instantiateReactComponent');
var invariant = require('fbjs/lib/invariant');
var setInnerHTML = require('./setInnerHTML');
var shouldUpdateReactComponent = require('./shouldUpdateReactComponent');
var warning = require('fbjs/lib/warning');

var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
var ROOT_ATTR_NAME = DOMProperty.ROOT_ATTRIBUTE_NAME;

var ELEMENT_NODE_TYPE = 1;
var DOC_NODE_TYPE = 9;
var DOCUMENT_FRAGMENT_NODE_TYPE = 11;

var instancesByReactRootID = {};

/**
 * Finds the index of the first character
 * that's not common between the two given strings.
 *
 * @return {number} the index of the character where the strings diverge
 */
function firstDifferenceIndex(string1, string2) {
  var minLen = Math.min(string1.length, string2.length);
  for (var i = 0; i < minLen; i++) {
    if (string1.charAt(i) !== string2.charAt(i)) {
      return i;
    }
  }
  return string1.length === string2.length ? -1 : minLen;
}

/**
 * @param {DOMElement|DOMDocument} container DOM element that may contain
 * a React component
 * @return {?*} DOM element that may have the reactRoot ID, or null.
 */
function getReactRootElementInContainer(container) {
  if (!container) {
    return null;
  }

  if (container.nodeType === DOC_NODE_TYPE) {
    return container.documentElement;
  } else {
    return container.firstChild;
  }
}

function internalGetID(node) {
  // If node is something like a window, document, or text node, none of
  // which support attributes or a .getAttribute method, gracefully return
  // the empty string, as if the attribute were missing.
  return node.getAttribute && node.getAttribute(ATTR_NAME) || '';
}

/**
 * Mounts this component and inserts it into the DOM.
 *
 * @param {ReactComponent} componentInstance The instance to mount.
 * @param {DOMElement} container DOM element to mount into.
 * @param {ReactReconcileTransaction} transaction
 * @param {boolean} shouldReuseMarkup If true, do not insert markup
 */
function mountComponentIntoNode(wrapperInstance, container, transaction, shouldReuseMarkup, context) {
  var markerName;
  if (ReactFeatureFlags.logTopLevelRenders) {
    var wrappedElement = wrapperInstance._currentElement.props;
    var type = wrappedElement.type;
    markerName = 'React mount: ' + (typeof type === 'string' ? type : type.displayName || type.name);
    console.time(markerName);
  }

  var markup = ReactReconciler.mountComponent(wrapperInstance, transaction, null, ReactDOMContainerInfo(wrapperInstance, container), context, 0 /* parentDebugID */
  );

  if (markerName) {
    console.timeEnd(markerName);
  }

  wrapperInstance._renderedComponent._topLevelWrapper = wrapperInstance;
  ReactMount._mountImageIntoNode(markup, container, wrapperInstance, shouldReuseMarkup, transaction);
}

/**
 * Batched mount.
 *
 * @param {ReactComponent} componentInstance The instance to mount.
 * @param {DOMElement} container DOM element to mount into.
 * @param {boolean} shouldReuseMarkup If true, do not insert markup
 */
function batchedMountComponentIntoNode(componentInstance, container, shouldReuseMarkup, context) {
  var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
  /* useCreateElement */
  !shouldReuseMarkup && ReactDOMFeatureFlags.useCreateElement);
  transaction.perform(mountComponentIntoNode, null, componentInstance, container, transaction, shouldReuseMarkup, context);
  ReactUpdates.ReactReconcileTransaction.release(transaction);
}

/**
 * Unmounts a component and removes it from the DOM.
 *
 * @param {ReactComponent} instance React component instance.
 * @param {DOMElement} container DOM element to unmount from.
 * @final
 * @internal
 * @see {ReactMount.unmountComponentAtNode}
 */
function unmountComponentFromNode(instance, container, safely) {
  if ("production" !== 'production') {
    ReactInstrumentation.debugTool.onBeginFlush();
  }
  ReactReconciler.unmountComponent(instance, safely);
  if ("production" !== 'production') {
    ReactInstrumentation.debugTool.onEndFlush();
  }

  if (container.nodeType === DOC_NODE_TYPE) {
    container = container.documentElement;
  }

  // http://jsperf.com/emptying-a-node
  while (container.lastChild) {
    container.removeChild(container.lastChild);
  }
}

/**
 * True if the supplied DOM node has a direct React-rendered child that is
 * not a React root element. Useful for warning in `render`,
 * `unmountComponentAtNode`, etc.
 *
 * @param {?DOMElement} node The candidate DOM node.
 * @return {boolean} True if the DOM element contains a direct child that was
 * rendered by React but is not a root element.
 * @internal
 */
function hasNonRootReactChild(container) {
  var rootEl = getReactRootElementInContainer(container);
  if (rootEl) {
    var inst = ReactDOMComponentTree.getInstanceFromNode(rootEl);
    return !!(inst && inst._hostParent);
  }
}

/**
 * True if the supplied DOM node is a React DOM element and
 * it has been rendered by another copy of React.
 *
 * @param {?DOMElement} node The candidate DOM node.
 * @return {boolean} True if the DOM has been rendered by another copy of React
 * @internal
 */
function nodeIsRenderedByOtherInstance(container) {
  var rootEl = getReactRootElementInContainer(container);
  return !!(rootEl && isReactNode(rootEl) && !ReactDOMComponentTree.getInstanceFromNode(rootEl));
}

/**
 * True if the supplied DOM node is a valid node element.
 *
 * @param {?DOMElement} node The candidate DOM node.
 * @return {boolean} True if the DOM is a valid DOM node.
 * @internal
 */
function isValidContainer(node) {
  return !!(node && (node.nodeType === ELEMENT_NODE_TYPE || node.nodeType === DOC_NODE_TYPE || node.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE));
}

/**
 * True if the supplied DOM node is a valid React node element.
 *
 * @param {?DOMElement} node The candidate DOM node.
 * @return {boolean} True if the DOM is a valid React DOM node.
 * @internal
 */
function isReactNode(node) {
  return isValidContainer(node) && (node.hasAttribute(ROOT_ATTR_NAME) || node.hasAttribute(ATTR_NAME));
}

function getHostRootInstanceInContainer(container) {
  var rootEl = getReactRootElementInContainer(container);
  var prevHostInstance = rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl);
  return prevHostInstance && !prevHostInstance._hostParent ? prevHostInstance : null;
}

function getTopLevelWrapperInContainer(container) {
  var root = getHostRootInstanceInContainer(container);
  return root ? root._hostContainerInfo._topLevelWrapper : null;
}

/**
 * Temporary (?) hack so that we can store all top-level pending updates on
 * composites instead of having to worry about different types of components
 * here.
 */
var topLevelRootCounter = 1;
var TopLevelWrapper = function () {
  this.rootID = topLevelRootCounter++;
};
TopLevelWrapper.prototype.isReactComponent = {};
if ("production" !== 'production') {
  TopLevelWrapper.displayName = 'TopLevelWrapper';
}
TopLevelWrapper.prototype.render = function () {
  // this.props is actually a ReactElement
  return this.props;
};

/**
 * Mounting is the process of initializing a React component by creating its
 * representative DOM elements and inserting them into a supplied `container`.
 * Any prior content inside `container` is destroyed in the process.
 *
 *   ReactMount.render(
 *     component,
 *     document.getElementById('container')
 *   );
 *
 *   <div id="container">                   <-- Supplied `container`.
 *     <div data-reactid=".3">              <-- Rendered reactRoot of React
 *       // ...                                 component.
 *     </div>
 *   </div>
 *
 * Inside of `container`, the first element rendered is the "reactRoot".
 */
var ReactMount = {

  TopLevelWrapper: TopLevelWrapper,

  /**
   * Used by devtools. The keys are not important.
   */
  _instancesByReactRootID: instancesByReactRootID,

  /**
   * This is a hook provided to support rendering React components while
   * ensuring that the apparent scroll position of its `container` does not
   * change.
   *
   * @param {DOMElement} container The `container` being rendered into.
   * @param {function} renderCallback This must be called once to do the render.
   */
  scrollMonitor: function (container, renderCallback) {
    renderCallback();
  },

  /**
   * Take a component that's already mounted into the DOM and replace its props
   * @param {ReactComponent} prevComponent component instance already in the DOM
   * @param {ReactElement} nextElement component instance to render
   * @param {DOMElement} container container to render into
   * @param {?function} callback function triggered on completion
   */
  _updateRootComponent: function (prevComponent, nextElement, nextContext, container, callback) {
    ReactMount.scrollMonitor(container, function () {
      ReactUpdateQueue.enqueueElementInternal(prevComponent, nextElement, nextContext);
      if (callback) {
        ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
      }
    });

    return prevComponent;
  },

  /**
   * Render a new component into the DOM. Hooked by hooks!
   *
   * @param {ReactElement} nextElement element to render
   * @param {DOMElement} container container to render into
   * @param {boolean} shouldReuseMarkup if we should skip the markup insertion
   * @return {ReactComponent} nextComponent
   */
  _renderNewRootComponent: function (nextElement, container, shouldReuseMarkup, context) {
    // Various parts of our code (such as ReactCompositeComponent's
    // _renderValidatedComponent) assume that calls to render aren't nested;
    // verify that that's the case.
    "production" !== 'production' ? warning(ReactCurrentOwner.current == null, '_renderNewRootComponent(): Render methods should be a pure function ' + 'of props and state; triggering nested component updates from ' + 'render is not allowed. If necessary, trigger nested updates in ' + 'componentDidUpdate. Check the render method of %s.', ReactCurrentOwner.current && ReactCurrentOwner.current.getName() || 'ReactCompositeComponent') : void 0;

    !isValidContainer(container) ? "production" !== 'production' ? invariant(false, '_registerComponent(...): Target container is not a DOM element.') : _prodInvariant('37') : void 0;

    ReactBrowserEventEmitter.ensureScrollValueMonitoring();
    var componentInstance = instantiateReactComponent(nextElement, false);

    // The initial render is synchronous but any updates that happen during
    // rendering, in componentWillMount or componentDidMount, will be batched
    // according to the current batching strategy.

    ReactUpdates.batchedUpdates(batchedMountComponentIntoNode, componentInstance, container, shouldReuseMarkup, context);

    var wrapperID = componentInstance._instance.rootID;
    instancesByReactRootID[wrapperID] = componentInstance;

    return componentInstance;
  },

  /**
   * Renders a React component into the DOM in the supplied `container`.
   *
   * If the React component was previously rendered into `container`, this will
   * perform an update on it and only mutate the DOM as necessary to reflect the
   * latest React component.
   *
   * @param {ReactComponent} parentComponent The conceptual parent of this render tree.
   * @param {ReactElement} nextElement Component element to render.
   * @param {DOMElement} container DOM element to render into.
   * @param {?function} callback function triggered on completion
   * @return {ReactComponent} Component instance rendered in `container`.
   */
  renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback) {
    !(parentComponent != null && ReactInstanceMap.has(parentComponent)) ? "production" !== 'production' ? invariant(false, 'parentComponent must be a valid React Component') : _prodInvariant('38') : void 0;
    return ReactMount._renderSubtreeIntoContainer(parentComponent, nextElement, container, callback);
  },

  _renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback) {
    ReactUpdateQueue.validateCallback(callback, 'ReactDOM.render');
    !ReactElement.isValidElement(nextElement) ? "production" !== 'production' ? invariant(false, 'ReactDOM.render(): Invalid component element.%s', typeof nextElement === 'string' ? ' Instead of passing a string like \'div\', pass ' + 'React.createElement(\'div\') or <div />.' : typeof nextElement === 'function' ? ' Instead of passing a class like Foo, pass ' + 'React.createElement(Foo) or <Foo />.' :
    // Check if it quacks like an element
    nextElement != null && nextElement.props !== undefined ? ' This may be caused by unintentionally loading two independent ' + 'copies of React.' : '') : _prodInvariant('39', typeof nextElement === 'string' ? ' Instead of passing a string like \'div\', pass ' + 'React.createElement(\'div\') or <div />.' : typeof nextElement === 'function' ? ' Instead of passing a class like Foo, pass ' + 'React.createElement(Foo) or <Foo />.' : nextElement != null && nextElement.props !== undefined ? ' This may be caused by unintentionally loading two independent ' + 'copies of React.' : '') : void 0;

    "production" !== 'production' ? warning(!container || !container.tagName || container.tagName.toUpperCase() !== 'BODY', 'render(): Rendering components directly into document.body is ' + 'discouraged, since its children are often manipulated by third-party ' + 'scripts and browser extensions. This may lead to subtle ' + 'reconciliation issues. Try rendering into a container element created ' + 'for your app.') : void 0;

    var nextWrappedElement = ReactElement(TopLevelWrapper, null, null, null, null, null, nextElement);

    var nextContext;
    if (parentComponent) {
      var parentInst = ReactInstanceMap.get(parentComponent);
      nextContext = parentInst._processChildContext(parentInst._context);
    } else {
      nextContext = emptyObject;
    }

    var prevComponent = getTopLevelWrapperInContainer(container);

    if (prevComponent) {
      var prevWrappedElement = prevComponent._currentElement;
      var prevElement = prevWrappedElement.props;
      if (shouldUpdateReactComponent(prevElement, nextElement)) {
        var publicInst = prevComponent._renderedComponent.getPublicInstance();
        var updatedCallback = callback && function () {
          callback.call(publicInst);
        };
        ReactMount._updateRootComponent(prevComponent, nextWrappedElement, nextContext, container, updatedCallback);
        return publicInst;
      } else {
        ReactMount.unmountComponentAtNode(container);
      }
    }

    var reactRootElement = getReactRootElementInContainer(container);
    var containerHasReactMarkup = reactRootElement && !!internalGetID(reactRootElement);
    var containerHasNonRootReactChild = hasNonRootReactChild(container);

    if ("production" !== 'production') {
      "production" !== 'production' ? warning(!containerHasNonRootReactChild, 'render(...): Replacing React-rendered children with a new root ' + 'component. If you intended to update the children of this node, ' + 'you should instead have the existing children update their state ' + 'and render the new components instead of calling ReactDOM.render.') : void 0;

      if (!containerHasReactMarkup || reactRootElement.nextSibling) {
        var rootElementSibling = reactRootElement;
        while (rootElementSibling) {
          if (internalGetID(rootElementSibling)) {
            "production" !== 'production' ? warning(false, 'render(): Target node has markup rendered by React, but there ' + 'are unrelated nodes as well. This is most commonly caused by ' + 'white-space inserted around server-rendered markup.') : void 0;
            break;
          }
          rootElementSibling = rootElementSibling.nextSibling;
        }
      }
    }

    var shouldReuseMarkup = containerHasReactMarkup && !prevComponent && !containerHasNonRootReactChild;
    var component = ReactMount._renderNewRootComponent(nextWrappedElement, container, shouldReuseMarkup, nextContext)._renderedComponent.getPublicInstance();
    if (callback) {
      callback.call(component);
    }
    return component;
  },

  /**
   * Renders a React component into the DOM in the supplied `container`.
   * See https://facebook.github.io/react/docs/top-level-api.html#reactdom.render
   *
   * If the React component was previously rendered into `container`, this will
   * perform an update on it and only mutate the DOM as necessary to reflect the
   * latest React component.
   *
   * @param {ReactElement} nextElement Component element to render.
   * @param {DOMElement} container DOM element to render into.
   * @param {?function} callback function triggered on completion
   * @return {ReactComponent} Component instance rendered in `container`.
   */
  render: function (nextElement, container, callback) {
    return ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback);
  },

  /**
   * Unmounts and destroys the React component rendered in the `container`.
   * See https://facebook.github.io/react/docs/top-level-api.html#reactdom.unmountcomponentatnode
   *
   * @param {DOMElement} container DOM element containing a React component.
   * @return {boolean} True if a component was found in and unmounted from
   *                   `container`
   */
  unmountComponentAtNode: function (container) {
    // Various parts of our code (such as ReactCompositeComponent's
    // _renderValidatedComponent) assume that calls to render aren't nested;
    // verify that that's the case. (Strictly speaking, unmounting won't cause a
    // render but we still don't expect to be in a render call here.)
    "production" !== 'production' ? warning(ReactCurrentOwner.current == null, 'unmountComponentAtNode(): Render methods should be a pure function ' + 'of props and state; triggering nested component updates from render ' + 'is not allowed. If necessary, trigger nested updates in ' + 'componentDidUpdate. Check the render method of %s.', ReactCurrentOwner.current && ReactCurrentOwner.current.getName() || 'ReactCompositeComponent') : void 0;

    !isValidContainer(container) ? "production" !== 'production' ? invariant(false, 'unmountComponentAtNode(...): Target container is not a DOM element.') : _prodInvariant('40') : void 0;

    if ("production" !== 'production') {
      "production" !== 'production' ? warning(!nodeIsRenderedByOtherInstance(container), 'unmountComponentAtNode(): The node you\'re attempting to unmount ' + 'was rendered by another copy of React.') : void 0;
    }

    var prevComponent = getTopLevelWrapperInContainer(container);
    if (!prevComponent) {
      // Check if the node being unmounted was rendered by React, but isn't a
      // root node.
      var containerHasNonRootReactChild = hasNonRootReactChild(container);

      // Check if the container itself is a React root node.
      var isContainerReactRoot = container.nodeType === 1 && container.hasAttribute(ROOT_ATTR_NAME);

      if ("production" !== 'production') {
        "production" !== 'production' ? warning(!containerHasNonRootReactChild, 'unmountComponentAtNode(): The node you\'re attempting to unmount ' + 'was rendered by React and is not a top-level container. %s', isContainerReactRoot ? 'You may have accidentally passed in a React root node instead ' + 'of its container.' : 'Instead, have the parent component update its state and ' + 'rerender in order to remove this component.') : void 0;
      }

      return false;
    }
    delete instancesByReactRootID[prevComponent._instance.rootID];
    ReactUpdates.batchedUpdates(unmountComponentFromNode, prevComponent, container, false);
    return true;
  },

  _mountImageIntoNode: function (markup, container, instance, shouldReuseMarkup, transaction) {
    !isValidContainer(container) ? "production" !== 'production' ? invariant(false, 'mountComponentIntoNode(...): Target container is not valid.') : _prodInvariant('41') : void 0;

    if (shouldReuseMarkup) {
      var rootElement = getReactRootElementInContainer(container);
      if (ReactMarkupChecksum.canReuseMarkup(markup, rootElement)) {
        ReactDOMComponentTree.precacheNode(instance, rootElement);
        return;
      } else {
        var checksum = rootElement.getAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
        rootElement.removeAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);

        var rootMarkup = rootElement.outerHTML;
        rootElement.setAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME, checksum);

        var normalizedMarkup = markup;
        if ("production" !== 'production') {
          // because rootMarkup is retrieved from the DOM, various normalizations
          // will have occurred which will not be present in `markup`. Here,
          // insert markup into a <div> or <iframe> depending on the container
          // type to perform the same normalizations before comparing.
          var normalizer;
          if (container.nodeType === ELEMENT_NODE_TYPE) {
            normalizer = document.createElement('div');
            normalizer.innerHTML = markup;
            normalizedMarkup = normalizer.innerHTML;
          } else {
            normalizer = document.createElement('iframe');
            document.body.appendChild(normalizer);
            normalizer.contentDocument.write(markup);
            normalizedMarkup = normalizer.contentDocument.documentElement.outerHTML;
            document.body.removeChild(normalizer);
          }
        }

        var diffIndex = firstDifferenceIndex(normalizedMarkup, rootMarkup);
        var difference = ' (client) ' + normalizedMarkup.substring(diffIndex - 20, diffIndex + 20) + '\n (server) ' + rootMarkup.substring(diffIndex - 20, diffIndex + 20);

        !(container.nodeType !== DOC_NODE_TYPE) ? "production" !== 'production' ? invariant(false, 'You\'re trying to render a component to the document using server rendering but the checksum was invalid. This usually means you rendered a different component type or props on the client from the one on the server, or your render() methods are impure. React cannot handle this case due to cross-browser quirks by rendering at the document root. You should look for environment dependent code in your components and ensure the props are the same client and server side:\n%s', difference) : _prodInvariant('42', difference) : void 0;

        if ("production" !== 'production') {
          "production" !== 'production' ? warning(false, 'React attempted to reuse markup in a container but the ' + 'checksum was invalid. This generally means that you are ' + 'using server rendering and the markup generated on the ' + 'server was not what the client was expecting. React injected ' + 'new markup to compensate which works but you have lost many ' + 'of the benefits of server rendering. Instead, figure out ' + 'why the markup being generated is different on the client ' + 'or server:\n%s', difference) : void 0;
        }
      }
    }

    !(container.nodeType !== DOC_NODE_TYPE) ? "production" !== 'production' ? invariant(false, 'You\'re trying to render a component to the document but you didn\'t use server rendering. We can\'t do this without using server rendering due to cross-browser quirks. See ReactDOMServer.renderToString() for server rendering.') : _prodInvariant('43') : void 0;

    if (transaction.useCreateElement) {
      while (container.lastChild) {
        container.removeChild(container.lastChild);
      }
      DOMLazyTree.insertTreeBefore(container, markup, null);
    } else {
      setInnerHTML(container, markup);
      ReactDOMComponentTree.precacheNode(instance, container.firstChild);
    }

    if ("production" !== 'production') {
      var hostNode = ReactDOMComponentTree.getInstanceFromNode(container.firstChild);
      if (hostNode._debugID !== 0) {
        ReactInstrumentation.debugTool.onHostOperation(hostNode._debugID, 'mount', markup.toString());
      }
    }
  }
};

module.exports = ReactMount;
},{"./DOMLazyTree":46,"./DOMProperty":48,"./ReactBrowserEventEmitter":65,"./ReactCurrentOwner":78,"./ReactDOMComponentTree":83,"./ReactDOMContainerInfo":84,"./ReactDOMFeatureFlags":87,"./ReactElement":101,"./ReactFeatureFlags":107,"./ReactInstanceMap":112,"./ReactInstrumentation":113,"./ReactMarkupChecksum":115,"./ReactReconciler":128,"./ReactUpdateQueue":135,"./ReactUpdates":136,"./instantiateReactComponent":175,"./reactProdInvariant":180,"./setInnerHTML":182,"./shouldUpdateReactComponent":185,"fbjs/lib/emptyObject":197,"fbjs/lib/invariant":204,"fbjs/lib/warning":213}],117:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactMultiChild
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var ReactComponentEnvironment = require('./ReactComponentEnvironment');
var ReactInstanceMap = require('./ReactInstanceMap');
var ReactInstrumentation = require('./ReactInstrumentation');
var ReactMultiChildUpdateTypes = require('./ReactMultiChildUpdateTypes');

var ReactCurrentOwner = require('./ReactCurrentOwner');
var ReactReconciler = require('./ReactReconciler');
var ReactChildReconciler = require('./ReactChildReconciler');

var emptyFunction = require('fbjs/lib/emptyFunction');
var flattenChildren = require('./flattenChildren');
var invariant = require('fbjs/lib/invariant');

/**
 * Make an update for markup to be rendered and inserted at a supplied index.
 *
 * @param {string} markup Markup that renders into an element.
 * @param {number} toIndex Destination index.
 * @private
 */
function makeInsertMarkup(markup, afterNode, toIndex) {
  // NOTE: Null values reduce hidden classes.
  return {
    type: ReactMultiChildUpdateTypes.INSERT_MARKUP,
    content: markup,
    fromIndex: null,
    fromNode: null,
    toIndex: toIndex,
    afterNode: afterNode
  };
}

/**
 * Make an update for moving an existing element to another index.
 *
 * @param {number} fromIndex Source index of the existing element.
 * @param {number} toIndex Destination index of the element.
 * @private
 */
function makeMove(child, afterNode, toIndex) {
  // NOTE: Null values reduce hidden classes.
  return {
    type: ReactMultiChildUpdateTypes.MOVE_EXISTING,
    content: null,
    fromIndex: child._mountIndex,
    fromNode: ReactReconciler.getHostNode(child),
    toIndex: toIndex,
    afterNode: afterNode
  };
}

/**
 * Make an update for removing an element at an index.
 *
 * @param {number} fromIndex Index of the element to remove.
 * @private
 */
function makeRemove(child, node) {
  // NOTE: Null values reduce hidden classes.
  return {
    type: ReactMultiChildUpdateTypes.REMOVE_NODE,
    content: null,
    fromIndex: child._mountIndex,
    fromNode: node,
    toIndex: null,
    afterNode: null
  };
}

/**
 * Make an update for setting the markup of a node.
 *
 * @param {string} markup Markup that renders into an element.
 * @private
 */
function makeSetMarkup(markup) {
  // NOTE: Null values reduce hidden classes.
  return {
    type: ReactMultiChildUpdateTypes.SET_MARKUP,
    content: markup,
    fromIndex: null,
    fromNode: null,
    toIndex: null,
    afterNode: null
  };
}

/**
 * Make an update for setting the text content.
 *
 * @param {string} textContent Text content to set.
 * @private
 */
function makeTextContent(textContent) {
  // NOTE: Null values reduce hidden classes.
  return {
    type: ReactMultiChildUpdateTypes.TEXT_CONTENT,
    content: textContent,
    fromIndex: null,
    fromNode: null,
    toIndex: null,
    afterNode: null
  };
}

/**
 * Push an update, if any, onto the queue. Creates a new queue if none is
 * passed and always returns the queue. Mutative.
 */
function enqueue(queue, update) {
  if (update) {
    queue = queue || [];
    queue.push(update);
  }
  return queue;
}

/**
 * Processes any enqueued updates.
 *
 * @private
 */
function processQueue(inst, updateQueue) {
  ReactComponentEnvironment.processChildrenUpdates(inst, updateQueue);
}

var setChildrenForInstrumentation = emptyFunction;
if ("production" !== 'production') {
  var getDebugID = function (inst) {
    if (!inst._debugID) {
      // Check for ART-like instances. TODO: This is silly/gross.
      var internal;
      if (internal = ReactInstanceMap.get(inst)) {
        inst = internal;
      }
    }
    return inst._debugID;
  };
  setChildrenForInstrumentation = function (children) {
    var debugID = getDebugID(this);
    // TODO: React Native empty components are also multichild.
    // This means they still get into this method but don't have _debugID.
    if (debugID !== 0) {
      ReactInstrumentation.debugTool.onSetChildren(debugID, children ? Object.keys(children).map(function (key) {
        return children[key]._debugID;
      }) : []);
    }
  };
}

/**
 * ReactMultiChild are capable of reconciling multiple children.
 *
 * @class ReactMultiChild
 * @internal
 */
var ReactMultiChild = {

  /**
   * Provides common functionality for components that must reconcile multiple
   * children. This is used by `ReactDOMComponent` to mount, update, and
   * unmount child components.
   *
   * @lends {ReactMultiChild.prototype}
   */
  Mixin: {

    _reconcilerInstantiateChildren: function (nestedChildren, transaction, context) {
      if ("production" !== 'production') {
        var selfDebugID = getDebugID(this);
        if (this._currentElement) {
          try {
            ReactCurrentOwner.current = this._currentElement._owner;
            return ReactChildReconciler.instantiateChildren(nestedChildren, transaction, context, selfDebugID);
          } finally {
            ReactCurrentOwner.current = null;
          }
        }
      }
      return ReactChildReconciler.instantiateChildren(nestedChildren, transaction, context);
    },

    _reconcilerUpdateChildren: function (prevChildren, nextNestedChildrenElements, mountImages, removedNodes, transaction, context) {
      var nextChildren;
      var selfDebugID = 0;
      if ("production" !== 'production') {
        selfDebugID = getDebugID(this);
        if (this._currentElement) {
          try {
            ReactCurrentOwner.current = this._currentElement._owner;
            nextChildren = flattenChildren(nextNestedChildrenElements, selfDebugID);
          } finally {
            ReactCurrentOwner.current = null;
          }
          ReactChildReconciler.updateChildren(prevChildren, nextChildren, mountImages, removedNodes, transaction, this, this._hostContainerInfo, context, selfDebugID);
          return nextChildren;
        }
      }
      nextChildren = flattenChildren(nextNestedChildrenElements, selfDebugID);
      ReactChildReconciler.updateChildren(prevChildren, nextChildren, mountImages, removedNodes, transaction, this, this._hostContainerInfo, context, selfDebugID);
      return nextChildren;
    },

    /**
     * Generates a "mount image" for each of the supplied children. In the case
     * of `ReactDOMComponent`, a mount image is a string of markup.
     *
     * @param {?object} nestedChildren Nested child maps.
     * @return {array} An array of mounted representations.
     * @internal
     */
    mountChildren: function (nestedChildren, transaction, context) {
      var children = this._reconcilerInstantiateChildren(nestedChildren, transaction, context);
      this._renderedChildren = children;

      var mountImages = [];
      var index = 0;
      for (var name in children) {
        if (children.hasOwnProperty(name)) {
          var child = children[name];
          var selfDebugID = 0;
          if ("production" !== 'production') {
            selfDebugID = getDebugID(this);
          }
          var mountImage = ReactReconciler.mountComponent(child, transaction, this, this._hostContainerInfo, context, selfDebugID);
          child._mountIndex = index++;
          mountImages.push(mountImage);
        }
      }

      if ("production" !== 'production') {
        setChildrenForInstrumentation.call(this, children);
      }

      return mountImages;
    },

    /**
     * Replaces any rendered children with a text content string.
     *
     * @param {string} nextContent String of content.
     * @internal
     */
    updateTextContent: function (nextContent) {
      var prevChildren = this._renderedChildren;
      // Remove any rendered children.
      ReactChildReconciler.unmountChildren(prevChildren, false);
      for (var name in prevChildren) {
        if (prevChildren.hasOwnProperty(name)) {
          !false ? "production" !== 'production' ? invariant(false, 'updateTextContent called on non-empty component.') : _prodInvariant('118') : void 0;
        }
      }
      // Set new text content.
      var updates = [makeTextContent(nextContent)];
      processQueue(this, updates);
    },

    /**
     * Replaces any rendered children with a markup string.
     *
     * @param {string} nextMarkup String of markup.
     * @internal
     */
    updateMarkup: function (nextMarkup) {
      var prevChildren = this._renderedChildren;
      // Remove any rendered children.
      ReactChildReconciler.unmountChildren(prevChildren, false);
      for (var name in prevChildren) {
        if (prevChildren.hasOwnProperty(name)) {
          !false ? "production" !== 'production' ? invariant(false, 'updateTextContent called on non-empty component.') : _prodInvariant('118') : void 0;
        }
      }
      var updates = [makeSetMarkup(nextMarkup)];
      processQueue(this, updates);
    },

    /**
     * Updates the rendered children with new children.
     *
     * @param {?object} nextNestedChildrenElements Nested child element maps.
     * @param {ReactReconcileTransaction} transaction
     * @internal
     */
    updateChildren: function (nextNestedChildrenElements, transaction, context) {
      // Hook used by React ART
      this._updateChildren(nextNestedChildrenElements, transaction, context);
    },

    /**
     * @param {?object} nextNestedChildrenElements Nested child element maps.
     * @param {ReactReconcileTransaction} transaction
     * @final
     * @protected
     */
    _updateChildren: function (nextNestedChildrenElements, transaction, context) {
      var prevChildren = this._renderedChildren;
      var removedNodes = {};
      var mountImages = [];
      var nextChildren = this._reconcilerUpdateChildren(prevChildren, nextNestedChildrenElements, mountImages, removedNodes, transaction, context);
      if (!nextChildren && !prevChildren) {
        return;
      }
      var updates = null;
      var name;
      // `nextIndex` will increment for each child in `nextChildren`, but
      // `lastIndex` will be the last index visited in `prevChildren`.
      var nextIndex = 0;
      var lastIndex = 0;
      // `nextMountIndex` will increment for each newly mounted child.
      var nextMountIndex = 0;
      var lastPlacedNode = null;
      for (name in nextChildren) {
        if (!nextChildren.hasOwnProperty(name)) {
          continue;
        }
        var prevChild = prevChildren && prevChildren[name];
        var nextChild = nextChildren[name];
        if (prevChild === nextChild) {
          updates = enqueue(updates, this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex));
          lastIndex = Math.max(prevChild._mountIndex, lastIndex);
          prevChild._mountIndex = nextIndex;
        } else {
          if (prevChild) {
            // Update `lastIndex` before `_mountIndex` gets unset by unmounting.
            lastIndex = Math.max(prevChild._mountIndex, lastIndex);
            // The `removedNodes` loop below will actually remove the child.
          }
          // The child must be instantiated before it's mounted.
          updates = enqueue(updates, this._mountChildAtIndex(nextChild, mountImages[nextMountIndex], lastPlacedNode, nextIndex, transaction, context));
          nextMountIndex++;
        }
        nextIndex++;
        lastPlacedNode = ReactReconciler.getHostNode(nextChild);
      }
      // Remove children that are no longer present.
      for (name in removedNodes) {
        if (removedNodes.hasOwnProperty(name)) {
          updates = enqueue(updates, this._unmountChild(prevChildren[name], removedNodes[name]));
        }
      }
      if (updates) {
        processQueue(this, updates);
      }
      this._renderedChildren = nextChildren;

      if ("production" !== 'production') {
        setChildrenForInstrumentation.call(this, nextChildren);
      }
    },

    /**
     * Unmounts all rendered children. This should be used to clean up children
     * when this component is unmounted. It does not actually perform any
     * backend operations.
     *
     * @internal
     */
    unmountChildren: function (safely) {
      var renderedChildren = this._renderedChildren;
      ReactChildReconciler.unmountChildren(renderedChildren, safely);
      this._renderedChildren = null;
    },

    /**
     * Moves a child component to the supplied index.
     *
     * @param {ReactComponent} child Component to move.
     * @param {number} toIndex Destination index of the element.
     * @param {number} lastIndex Last index visited of the siblings of `child`.
     * @protected
     */
    moveChild: function (child, afterNode, toIndex, lastIndex) {
      // If the index of `child` is less than `lastIndex`, then it needs to
      // be moved. Otherwise, we do not need to move it because a child will be
      // inserted or moved before `child`.
      if (child._mountIndex < lastIndex) {
        return makeMove(child, afterNode, toIndex);
      }
    },

    /**
     * Creates a child component.
     *
     * @param {ReactComponent} child Component to create.
     * @param {string} mountImage Markup to insert.
     * @protected
     */
    createChild: function (child, afterNode, mountImage) {
      return makeInsertMarkup(mountImage, afterNode, child._mountIndex);
    },

    /**
     * Removes a child component.
     *
     * @param {ReactComponent} child Child to remove.
     * @protected
     */
    removeChild: function (child, node) {
      return makeRemove(child, node);
    },

    /**
     * Mounts a child with the supplied name.
     *
     * NOTE: This is part of `updateChildren` and is here for readability.
     *
     * @param {ReactComponent} child Component to mount.
     * @param {string} name Name of the child.
     * @param {number} index Index at which to insert the child.
     * @param {ReactReconcileTransaction} transaction
     * @private
     */
    _mountChildAtIndex: function (child, mountImage, afterNode, index, transaction, context) {
      child._mountIndex = index;
      return this.createChild(child, afterNode, mountImage);
    },

    /**
     * Unmounts a rendered child.
     *
     * NOTE: This is part of `updateChildren` and is here for readability.
     *
     * @param {ReactComponent} child Component to unmount.
     * @private
     */
    _unmountChild: function (child, node) {
      var update = this.removeChild(child, node);
      child._mountIndex = null;
      return update;
    }

  }

};

module.exports = ReactMultiChild;
},{"./ReactChildReconciler":68,"./ReactComponentEnvironment":74,"./ReactCurrentOwner":78,"./ReactInstanceMap":112,"./ReactInstrumentation":113,"./ReactMultiChildUpdateTypes":118,"./ReactReconciler":128,"./flattenChildren":164,"./reactProdInvariant":180,"fbjs/lib/emptyFunction":196,"fbjs/lib/invariant":204}],118:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactMultiChildUpdateTypes
 */

'use strict';

var keyMirror = require('fbjs/lib/keyMirror');

/**
 * When a component's children are updated, a series of update configuration
 * objects are created in order to batch and serialize the required changes.
 *
 * Enumerates all the possible types of update configurations.
 *
 * @internal
 */
var ReactMultiChildUpdateTypes = keyMirror({
  INSERT_MARKUP: null,
  MOVE_EXISTING: null,
  REMOVE_NODE: null,
  SET_MARKUP: null,
  TEXT_CONTENT: null
});

module.exports = ReactMultiChildUpdateTypes;
},{"fbjs/lib/keyMirror":207}],119:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactNodeTypes
 * 
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

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

var invariant = require('fbjs/lib/invariant');

var ReactNodeTypes = {
  HOST: 0,
  COMPOSITE: 1,
  EMPTY: 2,

  getType: function (node) {
    if (node === null || node === false) {
      return ReactNodeTypes.EMPTY;
    } else if (ReactElement.isValidElement(node)) {
      if (typeof node.type === 'function') {
        return ReactNodeTypes.COMPOSITE;
      } else {
        return ReactNodeTypes.HOST;
      }
    }
    !false ? "production" !== 'production' ? invariant(false, 'Unexpected node: %s', node) : _prodInvariant('26', node) : void 0;
  }
};

module.exports = ReactNodeTypes;
},{"./ReactElement":101,"./reactProdInvariant":180,"fbjs/lib/invariant":204}],120:[function(require,module,exports){
/**
 * Copyright 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactNoopUpdateQueue
 */

'use strict';

var warning = require('fbjs/lib/warning');

function warnNoop(publicInstance, callerName) {
  if ("production" !== 'production') {
    var constructor = publicInstance.constructor;
    "production" !== 'production' ? warning(false, '%s(...): Can only update a mounted or mounting component. ' + 'This usually means you called %s() on an unmounted component. ' + 'This is a no-op. Please check the code for the %s component.', callerName, callerName, constructor && (constructor.displayName || constructor.name) || 'ReactClass') : void 0;
  }
}

/**
 * This is the abstract API for an update queue.
 */
var ReactNoopUpdateQueue = {

  /**
   * Checks whether or not this composite component is mounted.
   * @param {ReactClass} publicInstance The instance we want to test.
   * @return {boolean} True if mounted, false otherwise.
   * @protected
   * @final
   */
  isMounted: function (publicInstance) {
    return false;
  },

  /**
   * Enqueue a callback that will be executed after all the pending updates
   * have processed.
   *
   * @param {ReactClass} publicInstance The instance to use as `this` context.
   * @param {?function} callback Called after state is updated.
   * @internal
   */
  enqueueCallback: function (publicInstance, callback) {},

  /**
   * Forces an update. This should only be invoked when it is known with
   * certainty that we are **not** in a DOM transaction.
   *
   * You may want to call this when you know that some deeper aspect of the
   * component's state has changed but `setState` was not called.
   *
   * This will not invoke `shouldComponentUpdate`, but it will invoke
   * `componentWillUpdate` and `componentDidUpdate`.
   *
   * @param {ReactClass} publicInstance The instance that should rerender.
   * @internal
   */
  enqueueForceUpdate: function (publicInstance) {
    warnNoop(publicInstance, 'forceUpdate');
  },

  /**
   * Replaces all of the state. Always use this or `setState` to mutate state.
   * You should treat `this.state` as immutable.
   *
   * There is no guarantee that `this.state` will be immediately updated, so
   * accessing `this.state` after calling this method may return the old value.
   *
   * @param {ReactClass} publicInstance The instance that should rerender.
   * @param {object} completeState Next state.
   * @internal
   */
  enqueueReplaceState: function (publicInstance, completeState) {
    warnNoop(publicInstance, 'replaceState');
  },

  /**
   * Sets a subset of the state. This only exists because _pendingState is
   * internal. This provides a merging strategy that is not available to deep
   * properties which is confusing. TODO: Expose pendingState or don't use it
   * during the merge.
   *
   * @param {ReactClass} publicInstance The instance that should rerender.
   * @param {object} partialState Next partial state to be merged with state.
   * @internal
   */
  enqueueSetState: function (publicInstance, partialState) {
    warnNoop(publicInstance, 'setState');
  }
};

module.exports = ReactNoopUpdateQueue;
},{"fbjs/lib/warning":213}],121:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactOwner
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var invariant = require('fbjs/lib/invariant');

/**
 * ReactOwners are capable of storing references to owned components.
 *
 * All components are capable of //being// referenced by owner components, but
 * only ReactOwner components are capable of //referencing// owned components.
 * The named reference is known as a "ref".
 *
 * Refs are available when mounted and updated during reconciliation.
 *
 *   var MyComponent = React.createClass({
 *     render: function() {
 *       return (
 *         <div onClick={this.handleClick}>
 *           <CustomComponent ref="custom" />
 *         </div>
 *       );
 *     },
 *     handleClick: function() {
 *       this.refs.custom.handleClick();
 *     },
 *     componentDidMount: function() {
 *       this.refs.custom.initialize();
 *     }
 *   });
 *
 * Refs should rarely be used. When refs are used, they should only be done to
 * control data that is not handled by React's data flow.
 *
 * @class ReactOwner
 */
var ReactOwner = {

  /**
   * @param {?object} object
   * @return {boolean} True if `object` is a valid owner.
   * @final
   */
  isValidOwner: function (object) {
    return !!(object && typeof object.attachRef === 'function' && typeof object.detachRef === 'function');
  },

  /**
   * Adds a component by ref to an owner component.
   *
   * @param {ReactComponent} component Component to reference.
   * @param {string} ref Name by which to refer to the component.
   * @param {ReactOwner} owner Component on which to record the ref.
   * @final
   * @internal
   */
  addComponentAsRefTo: function (component, ref, owner) {
    !ReactOwner.isValidOwner(owner) ? "production" !== 'production' ? invariant(false, 'addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component\'s `render` method, or you have multiple copies of React loaded (details: https://fb.me/react-refs-must-have-owner).') : _prodInvariant('119') : void 0;
    owner.attachRef(ref, component);
  },

  /**
   * Removes a component by ref from an owner component.
   *
   * @param {ReactComponent} component Component to dereference.
   * @param {string} ref Name of the ref to remove.
   * @param {ReactOwner} owner Component on which the ref is recorded.
   * @final
   * @internal
   */
  removeComponentAsRefFrom: function (component, ref, owner) {
    !ReactOwner.isValidOwner(owner) ? "production" !== 'production' ? invariant(false, 'removeComponentAsRefFrom(...): Only a ReactOwner can have refs. You might be removing a ref to a component that was not created inside a component\'s `render` method, or you have multiple copies of React loaded (details: https://fb.me/react-refs-must-have-owner).') : _prodInvariant('120') : void 0;
    var ownerPublicInstance = owner.getPublicInstance();
    // Check that `component`'s owner is still alive and that `component` is still the current ref
    // because we do not want to detach the ref if another component stole it.
    if (ownerPublicInstance && ownerPublicInstance.refs[ref] === component.getPublicInstance()) {
      owner.detachRef(ref);
    }
  }

};

module.exports = ReactOwner;
},{"./reactProdInvariant":180,"fbjs/lib/invariant":204}],122:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactPropTypeLocationNames
 */

'use strict';

var ReactPropTypeLocationNames = {};

if ("production" !== 'production') {
  ReactPropTypeLocationNames = {
    prop: 'prop',
    context: 'context',
    childContext: 'child context'
  };
}

module.exports = ReactPropTypeLocationNames;
},{}],123:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactPropTypeLocations
 */

'use strict';

var keyMirror = require('fbjs/lib/keyMirror');

var ReactPropTypeLocations = keyMirror({
  prop: null,
  context: null,
  childContext: null
});

module.exports = ReactPropTypeLocations;
},{"fbjs/lib/keyMirror":207}],124:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactPropTypes
 */

'use strict';

var ReactElement = require('./ReactElement');
var ReactPropTypeLocationNames = require('./ReactPropTypeLocationNames');
var ReactPropTypesSecret = require('./ReactPropTypesSecret');

var emptyFunction = require('fbjs/lib/emptyFunction');
var getIteratorFn = require('./getIteratorFn');
var warning = require('fbjs/lib/warning');

/**
 * Collection of methods that allow declaration and validation of props that are
 * supplied to React components. Example usage:
 *
 *   var Props = require('ReactPropTypes');
 *   var MyArticle = React.createClass({
 *     propTypes: {
 *       // An optional string prop named "description".
 *       description: Props.string,
 *
 *       // A required enum prop named "category".
 *       category: Props.oneOf(['News','Photos']).isRequired,
 *
 *       // A prop named "dialog" that requires an instance of Dialog.
 *       dialog: Props.instanceOf(Dialog).isRequired
 *     },
 *     render: function() { ... }
 *   });
 *
 * A more formal specification of how these methods are used:
 *
 *   type := array|bool|func|object|number|string|oneOf([...])|instanceOf(...)
 *   decl := ReactPropTypes.{type}(.isRequired)?
 *
 * Each and every declaration produces a function with the same signature. This
 * allows the creation of custom validation functions. For example:
 *
 *  var MyLink = React.createClass({
 *    propTypes: {
 *      // An optional string or URI prop named "href".
 *      href: function(props, propName, componentName) {
 *        var propValue = props[propName];
 *        if (propValue != null && typeof propValue !== 'string' &&
 *            !(propValue instanceof URI)) {
 *          return new Error(
 *            'Expected a string or an URI for ' + propName + ' in ' +
 *            componentName
 *          );
 *        }
 *      }
 *    },
 *    render: function() {...}
 *  });
 *
 * @internal
 */

var ANONYMOUS = '<<anonymous>>';

var ReactPropTypes = {
  array: createPrimitiveTypeChecker('array'),
  bool: createPrimitiveTypeChecker('boolean'),
  func: createPrimitiveTypeChecker('function'),
  number: createPrimitiveTypeChecker('number'),
  object: createPrimitiveTypeChecker('object'),
  string: createPrimitiveTypeChecker('string'),
  symbol: createPrimitiveTypeChecker('symbol'),

  any: createAnyTypeChecker(),
  arrayOf: createArrayOfTypeChecker,
  element: createElementTypeChecker(),
  instanceOf: createInstanceTypeChecker,
  node: createNodeChecker(),
  objectOf: createObjectOfTypeChecker,
  oneOf: createEnumTypeChecker,
  oneOfType: createUnionTypeChecker,
  shape: createShapeTypeChecker
};

/**
 * inlined Object.is polyfill to avoid requiring consumers ship their own
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
 */
/*eslint-disable no-self-compare*/
function is(x, y) {
  // SameValue algorithm
  if (x === y) {
    // Steps 1-5, 7-10
    // Steps 6.b-6.e: +0 != -0
    return x !== 0 || 1 / x === 1 / y;
  } else {
    // Step 6.a: NaN == NaN
    return x !== x && y !== y;
  }
}
/*eslint-enable no-self-compare*/

/**
 * We use an Error-like object for backward compatibility as people may call
 * PropTypes directly and inspect their output. However we don't use real
 * Errors anymore. We don't inspect their stack anyway, and creating them
 * is prohibitively expensive if they are created too often, such as what
 * happens in oneOfType() for any type before the one that matched.
 */
function PropTypeError(message) {
  this.message = message;
  this.stack = '';
}
// Make `instanceof Error` still work for returned errors.
PropTypeError.prototype = Error.prototype;

function createChainableTypeChecker(validate) {
  if ("production" !== 'production') {
    var manualPropTypeCallCache = {};
  }
  function checkType(isRequired, props, propName, componentName, location, propFullName, secret) {
    componentName = componentName || ANONYMOUS;
    propFullName = propFullName || propName;
    if ("production" !== 'production') {
      if (secret !== ReactPropTypesSecret && typeof console !== 'undefined') {
        var cacheKey = componentName + ':' + propName;
        if (!manualPropTypeCallCache[cacheKey]) {
          "production" !== 'production' ? warning(false, 'You are manually calling a React.PropTypes validation ' + 'function for the `%s` prop on `%s`. This is deprecated ' + 'and will not work in the next major version. You may be ' + 'seeing this warning due to a third-party PropTypes library. ' + 'See https://fb.me/react-warning-dont-call-proptypes for details.', propFullName, componentName) : void 0;
          manualPropTypeCallCache[cacheKey] = true;
        }
      }
    }
    if (props[propName] == null) {
      var locationName = ReactPropTypeLocationNames[location];
      if (isRequired) {
        return new PropTypeError('Required ' + locationName + ' `' + propFullName + '` was not specified in ' + ('`' + componentName + '`.'));
      }
      return null;
    } else {
      return validate(props, propName, componentName, location, propFullName);
    }
  }

  var chainedCheckType = checkType.bind(null, false);
  chainedCheckType.isRequired = checkType.bind(null, true);

  return chainedCheckType;
}

function createPrimitiveTypeChecker(expectedType) {
  function validate(props, propName, componentName, location, propFullName, secret) {
    var propValue = props[propName];
    var propType = getPropType(propValue);
    if (propType !== expectedType) {
      var locationName = ReactPropTypeLocationNames[location];
      // `propValue` being instance of, say, date/regexp, pass the 'object'
      // check, but we can offer a more precise error message here rather than
      // 'of type `object`'.
      var preciseType = getPreciseType(propValue);

      return new PropTypeError('Invalid ' + locationName + ' `' + propFullName + '` of type ' + ('`' + preciseType + '` supplied to `' + componentName + '`, expected ') + ('`' + expectedType + '`.'));
    }
    return null;
  }
  return createChainableTypeChecker(validate);
}

function createAnyTypeChecker() {
  return createChainableTypeChecker(emptyFunction.thatReturns(null));
}

function createArrayOfTypeChecker(typeChecker) {
  function validate(props, propName, componentName, location, propFullName) {
    if (typeof typeChecker !== 'function') {
      return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside arrayOf.');
    }
    var propValue = props[propName];
    if (!Array.isArray(propValue)) {
      var locationName = ReactPropTypeLocationNames[location];
      var propType = getPropType(propValue);
      return new PropTypeError('Invalid ' + locationName + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an array.'));
    }
    for (var i = 0; i < propValue.length; i++) {
      var error = typeChecker(propValue, i, componentName, location, propFullName + '[' + i + ']', ReactPropTypesSecret);
      if (error instanceof Error) {
        return error;
      }
    }
    return null;
  }
  return createChainableTypeChecker(validate);
}

function createElementTypeChecker() {
  function validate(props, propName, componentName, location, propFullName) {
    var propValue = props[propName];
    if (!ReactElement.isValidElement(propValue)) {
      var locationName = ReactPropTypeLocationNames[location];
      var propType = getPropType(propValue);
      return new PropTypeError('Invalid ' + locationName + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement.'));
    }
    return null;
  }
  return createChainableTypeChecker(validate);
}

function createInstanceTypeChecker(expectedClass) {
  function validate(props, propName, componentName, location, propFullName) {
    if (!(props[propName] instanceof expectedClass)) {
      var locationName = ReactPropTypeLocationNames[location];
      var expectedClassName = expectedClass.name || ANONYMOUS;
      var actualClassName = getClassName(props[propName]);
      return new PropTypeError('Invalid ' + locationName + ' `' + propFullName + '` of type ' + ('`' + actualClassName + '` supplied to `' + componentName + '`, expected ') + ('instance of `' + expectedClassName + '`.'));
    }
    return null;
  }
  return createChainableTypeChecker(validate);
}

function createEnumTypeChecker(expectedValues) {
  if (!Array.isArray(expectedValues)) {
    "production" !== 'production' ? warning(false, 'Invalid argument supplied to oneOf, expected an instance of array.') : void 0;
    return emptyFunction.thatReturnsNull;
  }

  function validate(props, propName, componentName, location, propFullName) {
    var propValue = props[propName];
    for (var i = 0; i < expectedValues.length; i++) {
      if (is(propValue, expectedValues[i])) {
        return null;
      }
    }

    var locationName = ReactPropTypeLocationNames[location];
    var valuesString = JSON.stringify(expectedValues);
    return new PropTypeError('Invalid ' + locationName + ' `' + propFullName + '` of value `' + propValue + '` ' + ('supplied to `' + componentName + '`, expected one of ' + valuesString + '.'));
  }
  return createChainableTypeChecker(validate);
}

function createObjectOfTypeChecker(typeChecker) {
  function validate(props, propName, componentName, location, propFullName) {
    if (typeof typeChecker !== 'function') {
      return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside objectOf.');
    }
    var propValue = props[propName];
    var propType = getPropType(propValue);
    if (propType !== 'object') {
      var locationName = ReactPropTypeLocationNames[location];
      return new PropTypeError('Invalid ' + locationName + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an object.'));
    }
    for (var key in propValue) {
      if (propValue.hasOwnProperty(key)) {
        var error = typeChecker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);
        if (error instanceof Error) {
          return error;
        }
      }
    }
    return null;
  }
  return createChainableTypeChecker(validate);
}

function createUnionTypeChecker(arrayOfTypeCheckers) {
  if (!Array.isArray(arrayOfTypeCheckers)) {
    "production" !== 'production' ? warning(false, 'Invalid argument supplied to oneOfType, expected an instance of array.') : void 0;
    return emptyFunction.thatReturnsNull;
  }

  function validate(props, propName, componentName, location, propFullName) {
    for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
      var checker = arrayOfTypeCheckers[i];
      if (checker(props, propName, componentName, location, propFullName, ReactPropTypesSecret) == null) {
        return null;
      }
    }

    var locationName = ReactPropTypeLocationNames[location];
    return new PropTypeError('Invalid ' + locationName + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`.'));
  }
  return createChainableTypeChecker(validate);
}

function createNodeChecker() {
  function validate(props, propName, componentName, location, propFullName) {
    if (!isNode(props[propName])) {
      var locationName = ReactPropTypeLocationNames[location];
      return new PropTypeError('Invalid ' + locationName + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`, expected a ReactNode.'));
    }
    return null;
  }
  return createChainableTypeChecker(validate);
}

function createShapeTypeChecker(shapeTypes) {
  function validate(props, propName, componentName, location, propFullName) {
    var propValue = props[propName];
    var propType = getPropType(propValue);
    if (propType !== 'object') {
      var locationName = ReactPropTypeLocationNames[location];
      return new PropTypeError('Invalid ' + locationName + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));
    }
    for (var key in shapeTypes) {
      var checker = shapeTypes[key];
      if (!checker) {
        continue;
      }
      var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);
      if (error) {
        return error;
      }
    }
    return null;
  }
  return createChainableTypeChecker(validate);
}

function isNode(propValue) {
  switch (typeof propValue) {
    case 'number':
    case 'string':
    case 'undefined':
      return true;
    case 'boolean':
      return !propValue;
    case 'object':
      if (Array.isArray(propValue)) {
        return propValue.every(isNode);
      }
      if (propValue === null || ReactElement.isValidElement(propValue)) {
        return true;
      }

      var iteratorFn = getIteratorFn(propValue);
      if (iteratorFn) {
        var iterator = iteratorFn.call(propValue);
        var step;
        if (iteratorFn !== propValue.entries) {
          while (!(step = iterator.next()).done) {
            if (!isNode(step.value)) {
              return false;
            }
          }
        } else {
          // Iterator will provide entry [k,v] tuples rather than values.
          while (!(step = iterator.next()).done) {
            var entry = step.value;
            if (entry) {
              if (!isNode(entry[1])) {
                return false;
              }
            }
          }
        }
      } else {
        return false;
      }

      return true;
    default:
      return false;
  }
}

function isSymbol(propType, propValue) {
  // Native Symbol.
  if (propType === 'symbol') {
    return true;
  }

  // 19.4.3.5 Symbol.prototype[@@toStringTag] === 'Symbol'
  if (propValue['@@toStringTag'] === 'Symbol') {
    return true;
  }

  // Fallback for non-spec compliant Symbols which are polyfilled.
  if (typeof Symbol === 'function' && propValue instanceof Symbol) {
    return true;
  }

  return false;
}

// Equivalent of `typeof` but with special handling for array and regexp.
function getPropType(propValue) {
  var propType = typeof propValue;
  if (Array.isArray(propValue)) {
    return 'array';
  }
  if (propValue instanceof RegExp) {
    // Old webkits (at least until Android 4.0) return 'function' rather than
    // 'object' for typeof a RegExp. We'll normalize this here so that /bla/
    // passes PropTypes.object.
    return 'object';
  }
  if (isSymbol(propType, propValue)) {
    return 'symbol';
  }
  return propType;
}

// This handles more types than `getPropType`. Only used for error messages.
// See `createPrimitiveTypeChecker`.
function getPreciseType(propValue) {
  var propType = getPropType(propValue);
  if (propType === 'object') {
    if (propValue instanceof Date) {
      return 'date';
    } else if (propValue instanceof RegExp) {
      return 'regexp';
    }
  }
  return propType;
}

// Returns class name of the object, if any.
function getClassName(propValue) {
  if (!propValue.constructor || !propValue.constructor.name) {
    return ANONYMOUS;
  }
  return propValue.constructor.name;
}

module.exports = ReactPropTypes;
},{"./ReactElement":101,"./ReactPropTypeLocationNames":122,"./ReactPropTypesSecret":125,"./getIteratorFn":171,"fbjs/lib/emptyFunction":196,"fbjs/lib/warning":213}],125:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactPropTypesSecret
 */

'use strict';

var ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';

module.exports = ReactPropTypesSecret;
},{}],126:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactPureComponent
 */

'use strict';

var _assign = require('object-assign');

var ReactComponent = require('./ReactComponent');
var ReactNoopUpdateQueue = require('./ReactNoopUpdateQueue');

var emptyObject = require('fbjs/lib/emptyObject');

/**
 * Base class helpers for the updating state of a component.
 */
function ReactPureComponent(props, context, updater) {
  // Duplicated from ReactComponent.
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}

function ComponentDummy() {}
ComponentDummy.prototype = ReactComponent.prototype;
ReactPureComponent.prototype = new ComponentDummy();
ReactPureComponent.prototype.constructor = ReactPureComponent;
// Avoid an extra prototype jump for these methods.
_assign(ReactPureComponent.prototype, ReactComponent.prototype);
ReactPureComponent.prototype.isPureReactComponent = true;

module.exports = ReactPureComponent;
},{"./ReactComponent":72,"./ReactNoopUpdateQueue":120,"fbjs/lib/emptyObject":197,"object-assign":214}],127:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactReconcileTransaction
 */

'use strict';

var _assign = require('object-assign');

var CallbackQueue = require('./CallbackQueue');
var PooledClass = require('./PooledClass');
var ReactBrowserEventEmitter = require('./ReactBrowserEventEmitter');
var ReactInputSelection = require('./ReactInputSelection');
var ReactInstrumentation = require('./ReactInstrumentation');
var Transaction = require('./Transaction');
var ReactUpdateQueue = require('./ReactUpdateQueue');

/**
 * Ensures that, when possible, the selection range (currently selected text
 * input) is not disturbed by performing the transaction.
 */
var SELECTION_RESTORATION = {
  /**
   * @return {Selection} Selection information.
   */
  initialize: ReactInputSelection.getSelectionInformation,
  /**
   * @param {Selection} sel Selection information returned from `initialize`.
   */
  close: ReactInputSelection.restoreSelection
};

/**
 * Suppresses events (blur/focus) that could be inadvertently dispatched due to
 * high level DOM manipulations (like temporarily removing a text input from the
 * DOM).
 */
var EVENT_SUPPRESSION = {
  /**
   * @return {boolean} The enabled status of `ReactBrowserEventEmitter` before
   * the reconciliation.
   */
  initialize: function () {
    var currentlyEnabled = ReactBrowserEventEmitter.isEnabled();
    ReactBrowserEventEmitter.setEnabled(false);
    return currentlyEnabled;
  },

  /**
   * @param {boolean} previouslyEnabled Enabled status of
   *   `ReactBrowserEventEmitter` before the reconciliation occurred. `close`
   *   restores the previous value.
   */
  close: function (previouslyEnabled) {
    ReactBrowserEventEmitter.setEnabled(previouslyEnabled);
  }
};

/**
 * Provides a queue for collecting `componentDidMount` and
 * `componentDidUpdate` callbacks during the transaction.
 */
var ON_DOM_READY_QUEUEING = {
  /**
   * Initializes the internal `onDOMReady` queue.
   */
  initialize: function () {
    this.reactMountReady.reset();
  },

  /**
   * After DOM is flushed, invoke all registered `onDOMReady` callbacks.
   */
  close: function () {
    this.reactMountReady.notifyAll();
  }
};

/**
 * Executed within the scope of the `Transaction` instance. Consider these as
 * being member methods, but with an implied ordering while being isolated from
 * each other.
 */
var TRANSACTION_WRAPPERS = [SELECTION_RESTORATION, EVENT_SUPPRESSION, ON_DOM_READY_QUEUEING];

if ("production" !== 'production') {
  TRANSACTION_WRAPPERS.push({
    initialize: ReactInstrumentation.debugTool.onBeginFlush,
    close: ReactInstrumentation.debugTool.onEndFlush
  });
}

/**
 * Currently:
 * - The order that these are listed in the transaction is critical:
 * - Suppresses events.
 * - Restores selection range.
 *
 * Future:
 * - Restore document/overflow scroll positions that were unintentionally
 *   modified via DOM insertions above the top viewport boundary.
 * - Implement/integrate with customized constraint based layout system and keep
 *   track of which dimensions must be remeasured.
 *
 * @class ReactReconcileTransaction
 */
function ReactReconcileTransaction(useCreateElement) {
  this.reinitializeTransaction();
  // Only server-side rendering really needs this option (see
  // `ReactServerRendering`), but server-side uses
  // `ReactServerRenderingTransaction` instead. This option is here so that it's
  // accessible and defaults to false when `ReactDOMComponent` and
  // `ReactDOMTextComponent` checks it in `mountComponent`.`
  this.renderToStaticMarkup = false;
  this.reactMountReady = CallbackQueue.getPooled(null);
  this.useCreateElement = useCreateElement;
}

var Mixin = {
  /**
   * @see Transaction
   * @abstract
   * @final
   * @return {array<object>} List of operation wrap procedures.
   *   TODO: convert to array<TransactionWrapper>
   */
  getTransactionWrappers: function () {
    return TRANSACTION_WRAPPERS;
  },

  /**
   * @return {object} The queue to collect `onDOMReady` callbacks with.
   */
  getReactMountReady: function () {
    return this.reactMountReady;
  },

  /**
   * @return {object} The queue to collect React async events.
   */
  getUpdateQueue: function () {
    return ReactUpdateQueue;
  },

  /**
   * Save current transaction state -- if the return value from this method is
   * passed to `rollback`, the transaction will be reset to that state.
   */
  checkpoint: function () {
    // reactMountReady is the our only stateful wrapper
    return this.reactMountReady.checkpoint();
  },

  rollback: function (checkpoint) {
    this.reactMountReady.rollback(checkpoint);
  },

  /**
   * `PooledClass` looks for this, and will invoke this before allowing this
   * instance to be reused.
   */
  destructor: function () {
    CallbackQueue.release(this.reactMountReady);
    this.reactMountReady = null;
  }
};

_assign(ReactReconcileTransaction.prototype, Transaction.Mixin, Mixin);

PooledClass.addPoolingTo(ReactReconcileTransaction);

module.exports = ReactReconcileTransaction;
},{"./CallbackQueue":43,"./PooledClass":63,"./ReactBrowserEventEmitter":65,"./ReactInputSelection":111,"./ReactInstrumentation":113,"./ReactUpdateQueue":135,"./Transaction":154,"object-assign":214}],128:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactReconciler
 */

'use strict';

var ReactRef = require('./ReactRef');
var ReactInstrumentation = require('./ReactInstrumentation');

var warning = require('fbjs/lib/warning');

/**
 * Helper to call ReactRef.attachRefs with this composite component, split out
 * to avoid allocations in the transaction mount-ready queue.
 */
function attachRefs() {
  ReactRef.attachRefs(this, this._currentElement);
}

var ReactReconciler = {

  /**
   * Initializes the component, renders markup, and registers event listeners.
   *
   * @param {ReactComponent} internalInstance
   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
   * @param {?object} the containing host component instance
   * @param {?object} info about the host container
   * @return {?string} Rendered markup to be inserted into the DOM.
   * @final
   * @internal
   */
  mountComponent: function (internalInstance, transaction, hostParent, hostContainerInfo, context, parentDebugID // 0 in production and for roots
  ) {
    if ("production" !== 'production') {
      if (internalInstance._debugID !== 0) {
        ReactInstrumentation.debugTool.onBeforeMountComponent(internalInstance._debugID, internalInstance._currentElement, parentDebugID);
      }
    }
    var markup = internalInstance.mountComponent(transaction, hostParent, hostContainerInfo, context, parentDebugID);
    if (internalInstance._currentElement && internalInstance._currentElement.ref != null) {
      transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
    }
    if ("production" !== 'production') {
      if (internalInstance._debugID !== 0) {
        ReactInstrumentation.debugTool.onMountComponent(internalInstance._debugID);
      }
    }
    return markup;
  },

  /**
   * Returns a value that can be passed to
   * ReactComponentEnvironment.replaceNodeWithMarkup.
   */
  getHostNode: function (internalInstance) {
    return internalInstance.getHostNode();
  },

  /**
   * Releases any resources allocated by `mountComponent`.
   *
   * @final
   * @internal
   */
  unmountComponent: function (internalInstance, safely) {
    if ("production" !== 'production') {
      if (internalInstance._debugID !== 0) {
        ReactInstrumentation.debugTool.onBeforeUnmountComponent(internalInstance._debugID);
      }
    }
    ReactRef.detachRefs(internalInstance, internalInstance._currentElement);
    internalInstance.unmountComponent(safely);
    if ("production" !== 'production') {
      if (internalInstance._debugID !== 0) {
        ReactInstrumentation.debugTool.onUnmountComponent(internalInstance._debugID);
      }
    }
  },

  /**
   * Update a component using a new element.
   *
   * @param {ReactComponent} internalInstance
   * @param {ReactElement} nextElement
   * @param {ReactReconcileTransaction} transaction
   * @param {object} context
   * @internal
   */
  receiveComponent: function (internalInstance, nextElement, transaction, context) {
    var prevElement = internalInstance._currentElement;

    if (nextElement === prevElement && context === internalInstance._context) {
      // Since elements are immutable after the owner is rendered,
      // we can do a cheap identity compare here to determine if this is a
      // superfluous reconcile. It's possible for state to be mutable but such
      // change should trigger an update of the owner which would recreate
      // the element. We explicitly check for the existence of an owner since
      // it's possible for an element created outside a composite to be
      // deeply mutated and reused.

      // TODO: Bailing out early is just a perf optimization right?
      // TODO: Removing the return statement should affect correctness?
      return;
    }

    if ("production" !== 'production') {
      if (internalInstance._debugID !== 0) {
        ReactInstrumentation.debugTool.onBeforeUpdateComponent(internalInstance._debugID, nextElement);
      }
    }

    var refsChanged = ReactRef.shouldUpdateRefs(prevElement, nextElement);

    if (refsChanged) {
      ReactRef.detachRefs(internalInstance, prevElement);
    }

    internalInstance.receiveComponent(nextElement, transaction, context);

    if (refsChanged && internalInstance._currentElement && internalInstance._currentElement.ref != null) {
      transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
    }

    if ("production" !== 'production') {
      if (internalInstance._debugID !== 0) {
        ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID);
      }
    }
  },

  /**
   * Flush any dirty changes in a component.
   *
   * @param {ReactComponent} internalInstance
   * @param {ReactReconcileTransaction} transaction
   * @internal
   */
  performUpdateIfNecessary: function (internalInstance, transaction, updateBatchNumber) {
    if (internalInstance._updateBatchNumber !== updateBatchNumber) {
      // The component's enqueued batch number should always be the current
      // batch or the following one.
      "production" !== 'production' ? warning(internalInstance._updateBatchNumber == null || internalInstance._updateBatchNumber === updateBatchNumber + 1, 'performUpdateIfNecessary: Unexpected batch number (current %s, ' + 'pending %s)', updateBatchNumber, internalInstance._updateBatchNumber) : void 0;
      return;
    }
    if ("production" !== 'production') {
      if (internalInstance._debugID !== 0) {
        ReactInstrumentation.debugTool.onBeforeUpdateComponent(internalInstance._debugID, internalInstance._currentElement);
      }
    }
    internalInstance.performUpdateIfNecessary(transaction);
    if ("production" !== 'production') {
      if (internalInstance._debugID !== 0) {
        ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID);
      }
    }
  }

};

module.exports = ReactReconciler;
},{"./ReactInstrumentation":113,"./ReactRef":129,"fbjs/lib/warning":213}],129:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactRef
 */

'use strict';

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

var ReactRef = {};

function attachRef(ref, component, owner) {
  if (typeof ref === 'function') {
    ref(component.getPublicInstance());
  } else {
    // Legacy ref
    ReactOwner.addComponentAsRefTo(component, ref, owner);
  }
}

function detachRef(ref, component, owner) {
  if (typeof ref === 'function') {
    ref(null);
  } else {
    // Legacy ref
    ReactOwner.removeComponentAsRefFrom(component, ref, owner);
  }
}

ReactRef.attachRefs = function (instance, element) {
  if (element === null || element === false) {
    return;
  }
  var ref = element.ref;
  if (ref != null) {
    attachRef(ref, instance, element._owner);
  }
};

ReactRef.shouldUpdateRefs = function (prevElement, nextElement) {
  // If either the owner or a `ref` has changed, make sure the newest owner
  // has stored a reference to `this`, and the previous owner (if different)
  // has forgotten the reference to `this`. We use the element instead
  // of the public this.props because the post processing cannot determine
  // a ref. The ref conceptually lives on the element.

  // TODO: Should this even be possible? The owner cannot change because
  // it's forbidden by shouldUpdateReactComponent. The ref can change
  // if you swap the keys of but not the refs. Reconsider where this check
  // is made. It probably belongs where the key checking and
  // instantiateReactComponent is done.

  var prevEmpty = prevElement === null || prevElement === false;
  var nextEmpty = nextElement === null || nextElement === false;

  return (
    // This has a few false positives w/r/t empty components.
    prevEmpty || nextEmpty || nextElement.ref !== prevElement.ref ||
    // If owner changes but we have an unchanged function ref, don't update refs
    typeof nextElement.ref === 'string' && nextElement._owner !== prevElement._owner
  );
};

ReactRef.detachRefs = function (instance, element) {
  if (element === null || element === false) {
    return;
  }
  var ref = element.ref;
  if (ref != null) {
    detachRef(ref, instance, element._owner);
  }
};

module.exports = ReactRef;
},{"./ReactOwner":121}],130:[function(require,module,exports){
/**
 * Copyright 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactServerRenderingTransaction
 */

'use strict';

var _assign = require('object-assign');

var PooledClass = require('./PooledClass');
var Transaction = require('./Transaction');
var ReactInstrumentation = require('./ReactInstrumentation');
var ReactServerUpdateQueue = require('./ReactServerUpdateQueue');

/**
 * Executed within the scope of the `Transaction` instance. Consider these as
 * being member methods, but with an implied ordering while being isolated from
 * each other.
 */
var TRANSACTION_WRAPPERS = [];

if ("production" !== 'production') {
  TRANSACTION_WRAPPERS.push({
    initialize: ReactInstrumentation.debugTool.onBeginFlush,
    close: ReactInstrumentation.debugTool.onEndFlush
  });
}

var noopCallbackQueue = {
  enqueue: function () {}
};

/**
 * @class ReactServerRenderingTransaction
 * @param {boolean} renderToStaticMarkup
 */
function ReactServerRenderingTransaction(renderToStaticMarkup) {
  this.reinitializeTransaction();
  this.renderToStaticMarkup = renderToStaticMarkup;
  this.useCreateElement = false;
  this.updateQueue = new ReactServerUpdateQueue(this);
}

var Mixin = {
  /**
   * @see Transaction
   * @abstract
   * @final
   * @return {array} Empty list of operation wrap procedures.
   */
  getTransactionWrappers: function () {
    return TRANSACTION_WRAPPERS;
  },

  /**
   * @return {object} The queue to collect `onDOMReady` callbacks with.
   */
  getReactMountReady: function () {
    return noopCallbackQueue;
  },

  /**
   * @return {object} The queue to collect React async events.
   */
  getUpdateQueue: function () {
    return this.updateQueue;
  },

  /**
   * `PooledClass` looks for this, and will invoke this before allowing this
   * instance to be reused.
   */
  destructor: function () {},

  checkpoint: function () {},

  rollback: function () {}
};

_assign(ReactServerRenderingTransaction.prototype, Transaction.Mixin, Mixin);

PooledClass.addPoolingTo(ReactServerRenderingTransaction);

module.exports = ReactServerRenderingTransaction;
},{"./PooledClass":63,"./ReactInstrumentation":113,"./ReactServerUpdateQueue":131,"./Transaction":154,"object-assign":214}],131:[function(require,module,exports){
/**
 * Copyright 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactServerUpdateQueue
 * 
 */

'use strict';

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var ReactUpdateQueue = require('./ReactUpdateQueue');
var Transaction = require('./Transaction');
var warning = require('fbjs/lib/warning');

function warnNoop(publicInstance, callerName) {
  if ("production" !== 'production') {
    var constructor = publicInstance.constructor;
    "production" !== 'production' ? warning(false, '%s(...): Can only update a mounting component. ' + 'This usually means you called %s() outside componentWillMount() on the server. ' + 'This is a no-op. Please check the code for the %s component.', callerName, callerName, constructor && (constructor.displayName || constructor.name) || 'ReactClass') : void 0;
  }
}

/**
 * This is the update queue used for server rendering.
 * It delegates to ReactUpdateQueue while server rendering is in progress and
 * switches to ReactNoopUpdateQueue after the transaction has completed.
 * @class ReactServerUpdateQueue
 * @param {Transaction} transaction
 */

var ReactServerUpdateQueue = function () {
  /* :: transaction: Transaction; */

  function ReactServerUpdateQueue(transaction) {
    _classCallCheck(this, ReactServerUpdateQueue);

    this.transaction = transaction;
  }

  /**
   * Checks whether or not this composite component is mounted.
   * @param {ReactClass} publicInstance The instance we want to test.
   * @return {boolean} True if mounted, false otherwise.
   * @protected
   * @final
   */


  ReactServerUpdateQueue.prototype.isMounted = function isMounted(publicInstance) {
    return false;
  };

  /**
   * Enqueue a callback that will be executed after all the pending updates
   * have processed.
   *
   * @param {ReactClass} publicInstance The instance to use as `this` context.
   * @param {?function} callback Called after state is updated.
   * @internal
   */


  ReactServerUpdateQueue.prototype.enqueueCallback = function enqueueCallback(publicInstance, callback, callerName) {
    if (this.transaction.isInTransaction()) {
      ReactUpdateQueue.enqueueCallback(publicInstance, callback, callerName);
    }
  };

  /**
   * Forces an update. This should only be invoked when it is known with
   * certainty that we are **not** in a DOM transaction.
   *
   * You may want to call this when you know that some deeper aspect of the
   * component's state has changed but `setState` was not called.
   *
   * This will not invoke `shouldComponentUpdate`, but it will invoke
   * `componentWillUpdate` and `componentDidUpdate`.
   *
   * @param {ReactClass} publicInstance The instance that should rerender.
   * @internal
   */


  ReactServerUpdateQueue.prototype.enqueueForceUpdate = function enqueueForceUpdate(publicInstance) {
    if (this.transaction.isInTransaction()) {
      ReactUpdateQueue.enqueueForceUpdate(publicInstance);
    } else {
      warnNoop(publicInstance, 'forceUpdate');
    }
  };

  /**
   * Replaces all of the state. Always use this or `setState` to mutate state.
   * You should treat `this.state` as immutable.
   *
   * There is no guarantee that `this.state` will be immediately updated, so
   * accessing `this.state` after calling this method may return the old value.
   *
   * @param {ReactClass} publicInstance The instance that should rerender.
   * @param {object|function} completeState Next state.
   * @internal
   */


  ReactServerUpdateQueue.prototype.enqueueReplaceState = function enqueueReplaceState(publicInstance, completeState) {
    if (this.transaction.isInTransaction()) {
      ReactUpdateQueue.enqueueReplaceState(publicInstance, completeState);
    } else {
      warnNoop(publicInstance, 'replaceState');
    }
  };

  /**
   * Sets a subset of the state. This only exists because _pendingState is
   * internal. This provides a merging strategy that is not available to deep
   * properties which is confusing. TODO: Expose pendingState or don't use it
   * during the merge.
   *
   * @param {ReactClass} publicInstance The instance that should rerender.
   * @param {object|function} partialState Next partial state to be merged with state.
   * @internal
   */


  ReactServerUpdateQueue.prototype.enqueueSetState = function enqueueSetState(publicInstance, partialState) {
    if (this.transaction.isInTransaction()) {
      ReactUpdateQueue.enqueueSetState(publicInstance, partialState);
    } else {
      warnNoop(publicInstance, 'setState');
    }
  };

  return ReactServerUpdateQueue;
}();

module.exports = ReactServerUpdateQueue;
},{"./ReactUpdateQueue":135,"./Transaction":154,"fbjs/lib/warning":213}],132:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactTransitionChildMapping
 */

'use strict';

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

var ReactTransitionChildMapping = {
  /**
   * Given `this.props.children`, return an object mapping key to child. Just
   * simple syntactic sugar around flattenChildren().
   *
   * @param {*} children `this.props.children`
   * @param {number=} selfDebugID Optional debugID of the current internal instance.
   * @return {object} Mapping of key to child
   */
  getChildMapping: function (children, selfDebugID) {
    if (!children) {
      return children;
    }

    if ("production" !== 'production') {
      return flattenChildren(children, selfDebugID);
    }

    return flattenChildren(children);
  },

  /**
   * When you're adding or removing children some may be added or removed in the
   * same render pass. We want to show *both* since we want to simultaneously
   * animate elements in and out. This function takes a previous set of keys
   * and a new set of keys and merges them with its best guess of the correct
   * ordering. In the future we may expose some of the utilities in
   * ReactMultiChild to make this easy, but for now React itself does not
   * directly have this concept of the union of prevChildren and nextChildren
   * so we implement it here.
   *
   * @param {object} prev prev children as returned from
   * `ReactTransitionChildMapping.getChildMapping()`.
   * @param {object} next next children as returned from
   * `ReactTransitionChildMapping.getChildMapping()`.
   * @return {object} a key set that contains all keys in `prev` and all keys
   * in `next` in a reasonable order.
   */
  mergeChildMappings: function (prev, next) {
    prev = prev || {};
    next = next || {};

    function getValueForKey(key) {
      if (next.hasOwnProperty(key)) {
        return next[key];
      } else {
        return prev[key];
      }
    }

    // For each key of `next`, the list of keys to insert before that key in
    // the combined list
    var nextKeysPending = {};

    var pendingKeys = [];
    for (var prevKey in prev) {
      if (next.hasOwnProperty(prevKey)) {
        if (pendingKeys.length) {
          nextKeysPending[prevKey] = pendingKeys;
          pendingKeys = [];
        }
      } else {
        pendingKeys.push(prevKey);
      }
    }

    var i;
    var childMapping = {};
    for (var nextKey in next) {
      if (nextKeysPending.hasOwnProperty(nextKey)) {
        for (i = 0; i < nextKeysPending[nextKey].length; i++) {
          var pendingNextKey = nextKeysPending[nextKey][i];
          childMapping[nextKeysPending[nextKey][i]] = getValueForKey(pendingNextKey);
        }
      }
      childMapping[nextKey] = getValueForKey(nextKey);
    }

    // Finally, add the keys which didn't appear before any key in `next`
    for (i = 0; i < pendingKeys.length; i++) {
      childMapping[pendingKeys[i]] = getValueForKey(pendingKeys[i]);
    }

    return childMapping;
  }
};

module.exports = ReactTransitionChildMapping;
},{"./flattenChildren":164}],133:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactTransitionEvents
 */

'use strict';

var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');

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

var endEvents = [];

function detectEvents() {
  var animEnd = getVendorPrefixedEventName('animationend');
  var transEnd = getVendorPrefixedEventName('transitionend');

  if (animEnd) {
    endEvents.push(animEnd);
  }

  if (transEnd) {
    endEvents.push(transEnd);
  }
}

if (ExecutionEnvironment.canUseDOM) {
  detectEvents();
}

// We use the raw {add|remove}EventListener() call because EventListener
// does not know how to remove event listeners and we really should
// clean up. Also, these events are not triggered in older browsers
// so we should be A-OK here.

function addEventListener(node, eventName, eventListener) {
  node.addEventListener(eventName, eventListener, false);
}

function removeEventListener(node, eventName, eventListener) {
  node.removeEventListener(eventName, eventListener, false);
}

var ReactTransitionEvents = {
  addEndEventListener: function (node, eventListener) {
    if (endEvents.length === 0) {
      // If CSS transitions are not supported, trigger an "end animation"
      // event immediately.
      window.setTimeout(eventListener, 0);
      return;
    }
    endEvents.forEach(function (endEvent) {
      addEventListener(node, endEvent, eventListener);
    });
  },

  removeEndEventListener: function (node, eventListener) {
    if (endEvents.length === 0) {
      return;
    }
    endEvents.forEach(function (endEvent) {
      removeEventListener(node, endEvent, eventListener);
    });
  }
};

module.exports = ReactTransitionEvents;
},{"./getVendorPrefixedEventName":174,"fbjs/lib/ExecutionEnvironment":190}],134:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactTransitionGroup
 */

'use strict';

var _assign = require('object-assign');

var React = require('./React');
var ReactInstanceMap = require('./ReactInstanceMap');
var ReactTransitionChildMapping = require('./ReactTransitionChildMapping');

var emptyFunction = require('fbjs/lib/emptyFunction');

/**
 * A basis for animations. When children are declaratively added or removed,
 * special lifecycle hooks are called.
 * See https://facebook.github.io/react/docs/animation.html#low-level-api-reacttransitiongroup
 */
var ReactTransitionGroup = React.createClass({
  displayName: 'ReactTransitionGroup',

  propTypes: {
    component: React.PropTypes.any,
    childFactory: React.PropTypes.func
  },

  getDefaultProps: function () {
    return {
      component: 'span',
      childFactory: emptyFunction.thatReturnsArgument
    };
  },

  getInitialState: function () {
    return {
      // TODO: can we get useful debug information to show at this point?
      children: ReactTransitionChildMapping.getChildMapping(this.props.children)
    };
  },

  componentWillMount: function () {
    this.currentlyTransitioningKeys = {};
    this.keysToEnter = [];
    this.keysToLeave = [];
  },

  componentDidMount: function () {
    var initialChildMapping = this.state.children;
    for (var key in initialChildMapping) {
      if (initialChildMapping[key]) {
        this.performAppear(key);
      }
    }
  },

  componentWillReceiveProps: function (nextProps) {
    var nextChildMapping;
    if ("production" !== 'production') {
      nextChildMapping = ReactTransitionChildMapping.getChildMapping(nextProps.children, ReactInstanceMap.get(this)._debugID);
    } else {
      nextChildMapping = ReactTransitionChildMapping.getChildMapping(nextProps.children);
    }
    var prevChildMapping = this.state.children;

    this.setState({
      children: ReactTransitionChildMapping.mergeChildMappings(prevChildMapping, nextChildMapping)
    });

    var key;

    for (key in nextChildMapping) {
      var hasPrev = prevChildMapping && prevChildMapping.hasOwnProperty(key);
      if (nextChildMapping[key] && !hasPrev && !this.currentlyTransitioningKeys[key]) {
        this.keysToEnter.push(key);
      }
    }

    for (key in prevChildMapping) {
      var hasNext = nextChildMapping && nextChildMapping.hasOwnProperty(key);
      if (prevChildMapping[key] && !hasNext && !this.currentlyTransitioningKeys[key]) {
        this.keysToLeave.push(key);
      }
    }

    // If we want to someday check for reordering, we could do it here.
  },

  componentDidUpdate: function () {
    var keysToEnter = this.keysToEnter;
    this.keysToEnter = [];
    keysToEnter.forEach(this.performEnter);

    var keysToLeave = this.keysToLeave;
    this.keysToLeave = [];
    keysToLeave.forEach(this.performLeave);
  },

  performAppear: function (key) {
    this.currentlyTransitioningKeys[key] = true;

    var component = this.refs[key];

    if (component.componentWillAppear) {
      component.componentWillAppear(this._handleDoneAppearing.bind(this, key));
    } else {
      this._handleDoneAppearing(key);
    }
  },

  _handleDoneAppearing: function (key) {
    var component = this.refs[key];
    if (component.componentDidAppear) {
      component.componentDidAppear();
    }

    delete this.currentlyTransitioningKeys[key];

    var currentChildMapping;
    if ("production" !== 'production') {
      currentChildMapping = ReactTransitionChildMapping.getChildMapping(this.props.children, ReactInstanceMap.get(this)._debugID);
    } else {
      currentChildMapping = ReactTransitionChildMapping.getChildMapping(this.props.children);
    }

    if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
      // This was removed before it had fully appeared. Remove it.
      this.performLeave(key);
    }
  },

  performEnter: function (key) {
    this.currentlyTransitioningKeys[key] = true;

    var component = this.refs[key];

    if (component.componentWillEnter) {
      component.componentWillEnter(this._handleDoneEntering.bind(this, key));
    } else {
      this._handleDoneEntering(key);
    }
  },

  _handleDoneEntering: function (key) {
    var component = this.refs[key];
    if (component.componentDidEnter) {
      component.componentDidEnter();
    }

    delete this.currentlyTransitioningKeys[key];

    var currentChildMapping;
    if ("production" !== 'production') {
      currentChildMapping = ReactTransitionChildMapping.getChildMapping(this.props.children, ReactInstanceMap.get(this)._debugID);
    } else {
      currentChildMapping = ReactTransitionChildMapping.getChildMapping(this.props.children);
    }

    if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
      // This was removed before it had fully entered. Remove it.
      this.performLeave(key);
    }
  },

  performLeave: function (key) {
    this.currentlyTransitioningKeys[key] = true;

    var component = this.refs[key];
    if (component.componentWillLeave) {
      component.componentWillLeave(this._handleDoneLeaving.bind(this, key));
    } else {
      // Note that this is somewhat dangerous b/c it calls setState()
      // again, effectively mutating the component before all the work
      // is done.
      this._handleDoneLeaving(key);
    }
  },

  _handleDoneLeaving: function (key) {
    var component = this.refs[key];

    if (component.componentDidLeave) {
      component.componentDidLeave();
    }

    delete this.currentlyTransitioningKeys[key];

    var currentChildMapping;
    if ("production" !== 'production') {
      currentChildMapping = ReactTransitionChildMapping.getChildMapping(this.props.children, ReactInstanceMap.get(this)._debugID);
    } else {
      currentChildMapping = ReactTransitionChildMapping.getChildMapping(this.props.children);
    }

    if (currentChildMapping && currentChildMapping.hasOwnProperty(key)) {
      // This entered again before it fully left. Add it again.
      this.performEnter(key);
    } else {
      this.setState(function (state) {
        var newChildren = _assign({}, state.children);
        delete newChildren[key];
        return { children: newChildren };
      });
    }
  },

  render: function () {
    // TODO: we could get rid of the need for the wrapper node
    // by cloning a single child
    var childrenToRender = [];
    for (var key in this.state.children) {
      var child = this.state.children[key];
      if (child) {
        // You may need to apply reactive updates to a child as it is leaving.
        // The normal React way to do it won't work since the child will have
        // already been removed. In case you need this behavior you can provide
        // a childFactory function to wrap every child, even the ones that are
        // leaving.
        childrenToRender.push(React.cloneElement(this.props.childFactory(child), { ref: key, key: key }));
      }
    }

    // Do not forward ReactTransitionGroup props to primitive DOM nodes
    var props = _assign({}, this.props);
    delete props.transitionLeave;
    delete props.transitionName;
    delete props.transitionAppear;
    delete props.transitionEnter;
    delete props.childFactory;
    delete props.transitionLeaveTimeout;
    delete props.transitionEnterTimeout;
    delete props.transitionAppearTimeout;
    delete props.component;

    return React.createElement(this.props.component, props, childrenToRender);
  }
});

module.exports = ReactTransitionGroup;
},{"./React":64,"./ReactInstanceMap":112,"./ReactTransitionChildMapping":132,"fbjs/lib/emptyFunction":196,"object-assign":214}],135:[function(require,module,exports){
/**
 * Copyright 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactUpdateQueue
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var ReactCurrentOwner = require('./ReactCurrentOwner');
var ReactInstanceMap = require('./ReactInstanceMap');
var ReactInstrumentation = require('./ReactInstrumentation');
var ReactUpdates = require('./ReactUpdates');

var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');

function enqueueUpdate(internalInstance) {
  ReactUpdates.enqueueUpdate(internalInstance);
}

function formatUnexpectedArgument(arg) {
  var type = typeof arg;
  if (type !== 'object') {
    return type;
  }
  var displayName = arg.constructor && arg.constructor.name || type;
  var keys = Object.keys(arg);
  if (keys.length > 0 && keys.length < 20) {
    return displayName + ' (keys: ' + keys.join(', ') + ')';
  }
  return displayName;
}

function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
  var internalInstance = ReactInstanceMap.get(publicInstance);
  if (!internalInstance) {
    if ("production" !== 'production') {
      var ctor = publicInstance.constructor;
      // Only warn when we have a callerName. Otherwise we should be silent.
      // We're probably calling from enqueueCallback. We don't want to warn
      // there because we already warned for the corresponding lifecycle method.
      "production" !== 'production' ? warning(!callerName, '%s(...): Can only update a mounted or mounting component. ' + 'This usually means you called %s() on an unmounted component. ' + 'This is a no-op. Please check the code for the %s component.', callerName, callerName, ctor && (ctor.displayName || ctor.name) || 'ReactClass') : void 0;
    }
    return null;
  }

  if ("production" !== 'production') {
    "production" !== 'production' ? warning(ReactCurrentOwner.current == null, '%s(...): Cannot update during an existing state transition (such as ' + 'within `render` or another component\'s constructor). Render methods ' + 'should be a pure function of props and state; constructor ' + 'side-effects are an anti-pattern, but can be moved to ' + '`componentWillMount`.', callerName) : void 0;
  }

  return internalInstance;
}

/**
 * ReactUpdateQueue allows for state updates to be scheduled into a later
 * reconciliation step.
 */
var ReactUpdateQueue = {

  /**
   * Checks whether or not this composite component is mounted.
   * @param {ReactClass} publicInstance The instance we want to test.
   * @return {boolean} True if mounted, false otherwise.
   * @protected
   * @final
   */
  isMounted: function (publicInstance) {
    if ("production" !== 'production') {
      var owner = ReactCurrentOwner.current;
      if (owner !== null) {
        "production" !== 'production' ? warning(owner._warnedAboutRefsInRender, '%s is accessing isMounted inside its render() function. ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', owner.getName() || 'A component') : void 0;
        owner._warnedAboutRefsInRender = true;
      }
    }
    var internalInstance = ReactInstanceMap.get(publicInstance);
    if (internalInstance) {
      // During componentWillMount and render this will still be null but after
      // that will always render to something. At least for now. So we can use
      // this hack.
      return !!internalInstance._renderedComponent;
    } else {
      return false;
    }
  },

  /**
   * Enqueue a callback that will be executed after all the pending updates
   * have processed.
   *
   * @param {ReactClass} publicInstance The instance to use as `this` context.
   * @param {?function} callback Called after state is updated.
   * @param {string} callerName Name of the calling function in the public API.
   * @internal
   */
  enqueueCallback: function (publicInstance, callback, callerName) {
    ReactUpdateQueue.validateCallback(callback, callerName);
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);

    // Previously we would throw an error if we didn't have an internal
    // instance. Since we want to make it a no-op instead, we mirror the same
    // behavior we have in other enqueue* methods.
    // We also need to ignore callbacks in componentWillMount. See
    // enqueueUpdates.
    if (!internalInstance) {
      return null;
    }

    if (internalInstance._pendingCallbacks) {
      internalInstance._pendingCallbacks.push(callback);
    } else {
      internalInstance._pendingCallbacks = [callback];
    }
    // TODO: The callback here is ignored when setState is called from
    // componentWillMount. Either fix it or disallow doing so completely in
    // favor of getInitialState. Alternatively, we can disallow
    // componentWillMount during server-side rendering.
    enqueueUpdate(internalInstance);
  },

  enqueueCallbackInternal: function (internalInstance, callback) {
    if (internalInstance._pendingCallbacks) {
      internalInstance._pendingCallbacks.push(callback);
    } else {
      internalInstance._pendingCallbacks = [callback];
    }
    enqueueUpdate(internalInstance);
  },

  /**
   * Forces an update. This should only be invoked when it is known with
   * certainty that we are **not** in a DOM transaction.
   *
   * You may want to call this when you know that some deeper aspect of the
   * component's state has changed but `setState` was not called.
   *
   * This will not invoke `shouldComponentUpdate`, but it will invoke
   * `componentWillUpdate` and `componentDidUpdate`.
   *
   * @param {ReactClass} publicInstance The instance that should rerender.
   * @internal
   */
  enqueueForceUpdate: function (publicInstance) {
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'forceUpdate');

    if (!internalInstance) {
      return;
    }

    internalInstance._pendingForceUpdate = true;

    enqueueUpdate(internalInstance);
  },

  /**
   * Replaces all of the state. Always use this or `setState` to mutate state.
   * You should treat `this.state` as immutable.
   *
   * There is no guarantee that `this.state` will be immediately updated, so
   * accessing `this.state` after calling this method may return the old value.
   *
   * @param {ReactClass} publicInstance The instance that should rerender.
   * @param {object} completeState Next state.
   * @internal
   */
  enqueueReplaceState: function (publicInstance, completeState) {
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'replaceState');

    if (!internalInstance) {
      return;
    }

    internalInstance._pendingStateQueue = [completeState];
    internalInstance._pendingReplaceState = true;

    enqueueUpdate(internalInstance);
  },

  /**
   * Sets a subset of the state. This only exists because _pendingState is
   * internal. This provides a merging strategy that is not available to deep
   * properties which is confusing. TODO: Expose pendingState or don't use it
   * during the merge.
   *
   * @param {ReactClass} publicInstance The instance that should rerender.
   * @param {object} partialState Next partial state to be merged with state.
   * @internal
   */
  enqueueSetState: function (publicInstance, partialState) {
    if ("production" !== 'production') {
      ReactInstrumentation.debugTool.onSetState();
      "production" !== 'production' ? warning(partialState != null, 'setState(...): You passed an undefined or null state object; ' + 'instead, use forceUpdate().') : void 0;
    }

    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');

    if (!internalInstance) {
      return;
    }

    var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
    queue.push(partialState);

    enqueueUpdate(internalInstance);
  },

  enqueueElementInternal: function (internalInstance, nextElement, nextContext) {
    internalInstance._pendingElement = nextElement;
    // TODO: introduce _pendingContext instead of setting it directly.
    internalInstance._context = nextContext;
    enqueueUpdate(internalInstance);
  },

  validateCallback: function (callback, callerName) {
    !(!callback || typeof callback === 'function') ? "production" !== 'production' ? invariant(false, '%s(...): Expected the last optional `callback` argument to be a function. Instead received: %s.', callerName, formatUnexpectedArgument(callback)) : _prodInvariant('122', callerName, formatUnexpectedArgument(callback)) : void 0;
  }

};

module.exports = ReactUpdateQueue;
},{"./ReactCurrentOwner":78,"./ReactInstanceMap":112,"./ReactInstrumentation":113,"./ReactUpdates":136,"./reactProdInvariant":180,"fbjs/lib/invariant":204,"fbjs/lib/warning":213}],136:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactUpdates
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant'),
    _assign = require('object-assign');

var CallbackQueue = require('./CallbackQueue');
var PooledClass = require('./PooledClass');
var ReactFeatureFlags = require('./ReactFeatureFlags');
var ReactReconciler = require('./ReactReconciler');
var Transaction = require('./Transaction');

var invariant = require('fbjs/lib/invariant');

var dirtyComponents = [];
var updateBatchNumber = 0;
var asapCallbackQueue = CallbackQueue.getPooled();
var asapEnqueued = false;

var batchingStrategy = null;

function ensureInjected() {
  !(ReactUpdates.ReactReconcileTransaction && batchingStrategy) ? "production" !== 'production' ? invariant(false, 'ReactUpdates: must inject a reconcile transaction class and batching strategy') : _prodInvariant('123') : void 0;
}

var NESTED_UPDATES = {
  initialize: function () {
    this.dirtyComponentsLength = dirtyComponents.length;
  },
  close: function () {
    if (this.dirtyComponentsLength !== dirtyComponents.length) {
      // Additional updates were enqueued by componentDidUpdate handlers or
      // similar; before our own UPDATE_QUEUEING wrapper closes, we want to run
      // these new updates so that if A's componentDidUpdate calls setState on
      // B, B will update before the callback A's updater provided when calling
      // setState.
      dirtyComponents.splice(0, this.dirtyComponentsLength);
      flushBatchedUpdates();
    } else {
      dirtyComponents.length = 0;
    }
  }
};

var UPDATE_QUEUEING = {
  initialize: function () {
    this.callbackQueue.reset();
  },
  close: function () {
    this.callbackQueue.notifyAll();
  }
};

var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];

function ReactUpdatesFlushTransaction() {
  this.reinitializeTransaction();
  this.dirtyComponentsLength = null;
  this.callbackQueue = CallbackQueue.getPooled();
  this.reconcileTransaction = ReactUpdates.ReactReconcileTransaction.getPooled(
  /* useCreateElement */true);
}

_assign(ReactUpdatesFlushTransaction.prototype, Transaction.Mixin, {
  getTransactionWrappers: function () {
    return TRANSACTION_WRAPPERS;
  },

  destructor: function () {
    this.dirtyComponentsLength = null;
    CallbackQueue.release(this.callbackQueue);
    this.callbackQueue = null;
    ReactUpdates.ReactReconcileTransaction.release(this.reconcileTransaction);
    this.reconcileTransaction = null;
  },

  perform: function (method, scope, a) {
    // Essentially calls `this.reconcileTransaction.perform(method, scope, a)`
    // with this transaction's wrappers around it.
    return Transaction.Mixin.perform.call(this, this.reconcileTransaction.perform, this.reconcileTransaction, method, scope, a);
  }
});

PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);

function batchedUpdates(callback, a, b, c, d, e) {
  ensureInjected();
  batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
}

/**
 * Array comparator for ReactComponents by mount ordering.
 *
 * @param {ReactComponent} c1 first component you're comparing
 * @param {ReactComponent} c2 second component you're comparing
 * @return {number} Return value usable by Array.prototype.sort().
 */
function mountOrderComparator(c1, c2) {
  return c1._mountOrder - c2._mountOrder;
}

function runBatchedUpdates(transaction) {
  var len = transaction.dirtyComponentsLength;
  !(len === dirtyComponents.length) ? "production" !== 'production' ? invariant(false, 'Expected flush transaction\'s stored dirty-components length (%s) to match dirty-components array length (%s).', len, dirtyComponents.length) : _prodInvariant('124', len, dirtyComponents.length) : void 0;

  // Since reconciling a component higher in the owner hierarchy usually (not
  // always -- see shouldComponentUpdate()) will reconcile children, reconcile
  // them before their children by sorting the array.
  dirtyComponents.sort(mountOrderComparator);

  // Any updates enqueued while reconciling must be performed after this entire
  // batch. Otherwise, if dirtyComponents is [A, B] where A has children B and
  // C, B could update twice in a single batch if C's render enqueues an update
  // to B (since B would have already updated, we should skip it, and the only
  // way we can know to do so is by checking the batch counter).
  updateBatchNumber++;

  for (var i = 0; i < len; i++) {
    // If a component is unmounted before pending changes apply, it will still
    // be here, but we assume that it has cleared its _pendingCallbacks and
    // that performUpdateIfNecessary is a noop.
    var component = dirtyComponents[i];

    // If performUpdateIfNecessary happens to enqueue any new updates, we
    // shouldn't execute the callbacks until the next render happens, so
    // stash the callbacks first
    var callbacks = component._pendingCallbacks;
    component._pendingCallbacks = null;

    var markerName;
    if (ReactFeatureFlags.logTopLevelRenders) {
      var namedComponent = component;
      // Duck type TopLevelWrapper. This is probably always true.
      if (component._currentElement.props === component._renderedComponent._currentElement) {
        namedComponent = component._renderedComponent;
      }
      markerName = 'React update: ' + namedComponent.getName();
      console.time(markerName);
    }

    ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction, updateBatchNumber);

    if (markerName) {
      console.timeEnd(markerName);
    }

    if (callbacks) {
      for (var j = 0; j < callbacks.length; j++) {
        transaction.callbackQueue.enqueue(callbacks[j], component.getPublicInstance());
      }
    }
  }
}

var flushBatchedUpdates = function () {
  // ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents
  // array and perform any updates enqueued by mount-ready handlers (i.e.,
  // componentDidUpdate) but we need to check here too in order to catch
  // updates enqueued by setState callbacks and asap calls.
  while (dirtyComponents.length || asapEnqueued) {
    if (dirtyComponents.length) {
      var transaction = ReactUpdatesFlushTransaction.getPooled();
      transaction.perform(runBatchedUpdates, null, transaction);
      ReactUpdatesFlushTransaction.release(transaction);
    }

    if (asapEnqueued) {
      asapEnqueued = false;
      var queue = asapCallbackQueue;
      asapCallbackQueue = CallbackQueue.getPooled();
      queue.notifyAll();
      CallbackQueue.release(queue);
    }
  }
};

/**
 * Mark a component as needing a rerender, adding an optional callback to a
 * list of functions which will be executed once the rerender occurs.
 */
function enqueueUpdate(component) {
  ensureInjected();

  // Various parts of our code (such as ReactCompositeComponent's
  // _renderValidatedComponent) assume that calls to render aren't nested;
  // verify that that's the case. (This is called by each top-level update
  // function, like setState, forceUpdate, etc.; creation and
  // destruction of top-level components is guarded in ReactMount.)

  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }

  dirtyComponents.push(component);
  if (component._updateBatchNumber == null) {
    component._updateBatchNumber = updateBatchNumber + 1;
  }
}

/**
 * Enqueue a callback to be run at the end of the current batching cycle. Throws
 * if no updates are currently being performed.
 */
function asap(callback, context) {
  !batchingStrategy.isBatchingUpdates ? "production" !== 'production' ? invariant(false, 'ReactUpdates.asap: Can\'t enqueue an asap callback in a context whereupdates are not being batched.') : _prodInvariant('125') : void 0;
  asapCallbackQueue.enqueue(callback, context);
  asapEnqueued = true;
}

var ReactUpdatesInjection = {
  injectReconcileTransaction: function (ReconcileTransaction) {
    !ReconcileTransaction ? "production" !== 'production' ? invariant(false, 'ReactUpdates: must provide a reconcile transaction class') : _prodInvariant('126') : void 0;
    ReactUpdates.ReactReconcileTransaction = ReconcileTransaction;
  },

  injectBatchingStrategy: function (_batchingStrategy) {
    !_batchingStrategy ? "production" !== 'production' ? invariant(false, 'ReactUpdates: must provide a batching strategy') : _prodInvariant('127') : void 0;
    !(typeof _batchingStrategy.batchedUpdates === 'function') ? "production" !== 'production' ? invariant(false, 'ReactUpdates: must provide a batchedUpdates() function') : _prodInvariant('128') : void 0;
    !(typeof _batchingStrategy.isBatchingUpdates === 'boolean') ? "production" !== 'production' ? invariant(false, 'ReactUpdates: must provide an isBatchingUpdates boolean attribute') : _prodInvariant('129') : void 0;
    batchingStrategy = _batchingStrategy;
  }
};

var ReactUpdates = {
  /**
   * React references `ReactReconcileTransaction` using this property in order
   * to allow dependency injection.
   *
   * @internal
   */
  ReactReconcileTransaction: null,

  batchedUpdates: batchedUpdates,
  enqueueUpdate: enqueueUpdate,
  flushBatchedUpdates: flushBatchedUpdates,
  injection: ReactUpdatesInjection,
  asap: asap
};

module.exports = ReactUpdates;
},{"./CallbackQueue":43,"./PooledClass":63,"./ReactFeatureFlags":107,"./ReactReconciler":128,"./Transaction":154,"./reactProdInvariant":180,"fbjs/lib/invariant":204,"object-assign":214}],137:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactVersion
 */

'use strict';

module.exports = '15.3.2';
},{}],138:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SVGDOMPropertyConfig
 */

'use strict';

var NS = {
  xlink: 'http://www.w3.org/1999/xlink',
  xml: 'http://www.w3.org/XML/1998/namespace'
};

// We use attributes for everything SVG so let's avoid some duplication and run
// code instead.
// The following are all specified in the HTML config already so we exclude here.
// - class (as className)
// - color
// - height
// - id
// - lang
// - max
// - media
// - method
// - min
// - name
// - style
// - target
// - type
// - width
var ATTRS = {
  accentHeight: 'accent-height',
  accumulate: 0,
  additive: 0,
  alignmentBaseline: 'alignment-baseline',
  allowReorder: 'allowReorder',
  alphabetic: 0,
  amplitude: 0,
  arabicForm: 'arabic-form',
  ascent: 0,
  attributeName: 'attributeName',
  attributeType: 'attributeType',
  autoReverse: 'autoReverse',
  azimuth: 0,
  baseFrequency: 'baseFrequency',
  baseProfile: 'baseProfile',
  baselineShift: 'baseline-shift',
  bbox: 0,
  begin: 0,
  bias: 0,
  by: 0,
  calcMode: 'calcMode',
  capHeight: 'cap-height',
  clip: 0,
  clipPath: 'clip-path',
  clipRule: 'clip-rule',
  clipPathUnits: 'clipPathUnits',
  colorInterpolation: 'color-interpolation',
  colorInterpolationFilters: 'color-interpolation-filters',
  colorProfile: 'color-profile',
  colorRendering: 'color-rendering',
  contentScriptType: 'contentScriptType',
  contentStyleType: 'contentStyleType',
  cursor: 0,
  cx: 0,
  cy: 0,
  d: 0,
  decelerate: 0,
  descent: 0,
  diffuseConstant: 'diffuseConstant',
  direction: 0,
  display: 0,
  divisor: 0,
  dominantBaseline: 'dominant-baseline',
  dur: 0,
  dx: 0,
  dy: 0,
  edgeMode: 'edgeMode',
  elevation: 0,
  enableBackground: 'enable-background',
  end: 0,
  exponent: 0,
  externalResourcesRequired: 'externalResourcesRequired',
  fill: 0,
  fillOpacity: 'fill-opacity',
  fillRule: 'fill-rule',
  filter: 0,
  filterRes: 'filterRes',
  filterUnits: 'filterUnits',
  floodColor: 'flood-color',
  floodOpacity: 'flood-opacity',
  focusable: 0,
  fontFamily: 'font-family',
  fontSize: 'font-size',
  fontSizeAdjust: 'font-size-adjust',
  fontStretch: 'font-stretch',
  fontStyle: 'font-style',
  fontVariant: 'font-variant',
  fontWeight: 'font-weight',
  format: 0,
  from: 0,
  fx: 0,
  fy: 0,
  g1: 0,
  g2: 0,
  glyphName: 'glyph-name',
  glyphOrientationHorizontal: 'glyph-orientation-horizontal',
  glyphOrientationVertical: 'glyph-orientation-vertical',
  glyphRef: 'glyphRef',
  gradientTransform: 'gradientTransform',
  gradientUnits: 'gradientUnits',
  hanging: 0,
  horizAdvX: 'horiz-adv-x',
  horizOriginX: 'horiz-origin-x',
  ideographic: 0,
  imageRendering: 'image-rendering',
  'in': 0,
  in2: 0,
  intercept: 0,
  k: 0,
  k1: 0,
  k2: 0,
  k3: 0,
  k4: 0,
  kernelMatrix: 'kernelMatrix',
  kernelUnitLength: 'kernelUnitLength',
  kerning: 0,
  keyPoints: 'keyPoints',
  keySplines: 'keySplines',
  keyTimes: 'keyTimes',
  lengthAdjust: 'lengthAdjust',
  letterSpacing: 'letter-spacing',
  lightingColor: 'lighting-color',
  limitingConeAngle: 'limitingConeAngle',
  local: 0,
  markerEnd: 'marker-end',
  markerMid: 'marker-mid',
  markerStart: 'marker-start',
  markerHeight: 'markerHeight',
  markerUnits: 'markerUnits',
  markerWidth: 'markerWidth',
  mask: 0,
  maskContentUnits: 'maskContentUnits',
  maskUnits: 'maskUnits',
  mathematical: 0,
  mode: 0,
  numOctaves: 'numOctaves',
  offset: 0,
  opacity: 0,
  operator: 0,
  order: 0,
  orient: 0,
  orientation: 0,
  origin: 0,
  overflow: 0,
  overlinePosition: 'overline-position',
  overlineThickness: 'overline-thickness',
  paintOrder: 'paint-order',
  panose1: 'panose-1',
  pathLength: 'pathLength',
  patternContentUnits: 'patternContentUnits',
  patternTransform: 'patternTransform',
  patternUnits: 'patternUnits',
  pointerEvents: 'pointer-events',
  points: 0,
  pointsAtX: 'pointsAtX',
  pointsAtY: 'pointsAtY',
  pointsAtZ: 'pointsAtZ',
  preserveAlpha: 'preserveAlpha',
  preserveAspectRatio: 'preserveAspectRatio',
  primitiveUnits: 'primitiveUnits',
  r: 0,
  radius: 0,
  refX: 'refX',
  refY: 'refY',
  renderingIntent: 'rendering-intent',
  repeatCount: 'repeatCount',
  repeatDur: 'repeatDur',
  requiredExtensions: 'requiredExtensions',
  requiredFeatures: 'requiredFeatures',
  restart: 0,
  result: 0,
  rotate: 0,
  rx: 0,
  ry: 0,
  scale: 0,
  seed: 0,
  shapeRendering: 'shape-rendering',
  slope: 0,
  spacing: 0,
  specularConstant: 'specularConstant',
  specularExponent: 'specularExponent',
  speed: 0,
  spreadMethod: 'spreadMethod',
  startOffset: 'startOffset',
  stdDeviation: 'stdDeviation',
  stemh: 0,
  stemv: 0,
  stitchTiles: 'stitchTiles',
  stopColor: 'stop-color',
  stopOpacity: 'stop-opacity',
  strikethroughPosition: 'strikethrough-position',
  strikethroughThickness: 'strikethrough-thickness',
  string: 0,
  stroke: 0,
  strokeDasharray: 'stroke-dasharray',
  strokeDashoffset: 'stroke-dashoffset',
  strokeLinecap: 'stroke-linecap',
  strokeLinejoin: 'stroke-linejoin',
  strokeMiterlimit: 'stroke-miterlimit',
  strokeOpacity: 'stroke-opacity',
  strokeWidth: 'stroke-width',
  surfaceScale: 'surfaceScale',
  systemLanguage: 'systemLanguage',
  tableValues: 'tableValues',
  targetX: 'targetX',
  targetY: 'targetY',
  textAnchor: 'text-anchor',
  textDecoration: 'text-decoration',
  textRendering: 'text-rendering',
  textLength: 'textLength',
  to: 0,
  transform: 0,
  u1: 0,
  u2: 0,
  underlinePosition: 'underline-position',
  underlineThickness: 'underline-thickness',
  unicode: 0,
  unicodeBidi: 'unicode-bidi',
  unicodeRange: 'unicode-range',
  unitsPerEm: 'units-per-em',
  vAlphabetic: 'v-alphabetic',
  vHanging: 'v-hanging',
  vIdeographic: 'v-ideographic',
  vMathematical: 'v-mathematical',
  values: 0,
  vectorEffect: 'vector-effect',
  version: 0,
  vertAdvY: 'vert-adv-y',
  vertOriginX: 'vert-origin-x',
  vertOriginY: 'vert-origin-y',
  viewBox: 'viewBox',
  viewTarget: 'viewTarget',
  visibility: 0,
  widths: 0,
  wordSpacing: 'word-spacing',
  writingMode: 'writing-mode',
  x: 0,
  xHeight: 'x-height',
  x1: 0,
  x2: 0,
  xChannelSelector: 'xChannelSelector',
  xlinkActuate: 'xlink:actuate',
  xlinkArcrole: 'xlink:arcrole',
  xlinkHref: 'xlink:href',
  xlinkRole: 'xlink:role',
  xlinkShow: 'xlink:show',
  xlinkTitle: 'xlink:title',
  xlinkType: 'xlink:type',
  xmlBase: 'xml:base',
  xmlns: 0,
  xmlnsXlink: 'xmlns:xlink',
  xmlLang: 'xml:lang',
  xmlSpace: 'xml:space',
  y: 0,
  y1: 0,
  y2: 0,
  yChannelSelector: 'yChannelSelector',
  z: 0,
  zoomAndPan: 'zoomAndPan'
};

var SVGDOMPropertyConfig = {
  Properties: {},
  DOMAttributeNamespaces: {
    xlinkActuate: NS.xlink,
    xlinkArcrole: NS.xlink,
    xlinkHref: NS.xlink,
    xlinkRole: NS.xlink,
    xlinkShow: NS.xlink,
    xlinkTitle: NS.xlink,
    xlinkType: NS.xlink,
    xmlBase: NS.xml,
    xmlLang: NS.xml,
    xmlSpace: NS.xml
  },
  DOMAttributeNames: {}
};

Object.keys(ATTRS).forEach(function (key) {
  SVGDOMPropertyConfig.Properties[key] = 0;
  if (ATTRS[key]) {
    SVGDOMPropertyConfig.DOMAttributeNames[key] = ATTRS[key];
  }
});

module.exports = SVGDOMPropertyConfig;
},{}],139:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SelectEventPlugin
 */

'use strict';

var EventConstants = require('./EventConstants');
var EventPropagators = require('./EventPropagators');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactInputSelection = require('./ReactInputSelection');
var SyntheticEvent = require('./SyntheticEvent');

var getActiveElement = require('fbjs/lib/getActiveElement');
var isTextInputElement = require('./isTextInputElement');
var keyOf = require('fbjs/lib/keyOf');
var shallowEqual = require('fbjs/lib/shallowEqual');

var topLevelTypes = EventConstants.topLevelTypes;

var skipSelectionChangeEvent = ExecutionEnvironment.canUseDOM && 'documentMode' in document && document.documentMode <= 11;

var eventTypes = {
  select: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onSelect: null }),
      captured: keyOf({ onSelectCapture: null })
    },
    dependencies: [topLevelTypes.topBlur, topLevelTypes.topContextMenu, topLevelTypes.topFocus, topLevelTypes.topKeyDown, topLevelTypes.topKeyUp, topLevelTypes.topMouseDown, topLevelTypes.topMouseUp, topLevelTypes.topSelectionChange]
  }
};

var activeElement = null;
var activeElementInst = null;
var lastSelection = null;
var mouseDown = false;

// Track whether a listener exists for this plugin. If none exist, we do
// not extract events. See #3639.
var hasListener = false;
var ON_SELECT_KEY = keyOf({ onSelect: null });

/**
 * Get an object which is a unique representation of the current selection.
 *
 * The return value will not be consistent across nodes or browsers, but
 * two identical selections on the same node will return identical objects.
 *
 * @param {DOMElement} node
 * @return {object}
 */
function getSelection(node) {
  if ('selectionStart' in node && ReactInputSelection.hasSelectionCapabilities(node)) {
    return {
      start: node.selectionStart,
      end: node.selectionEnd
    };
  } else if (window.getSelection) {
    var selection = window.getSelection();
    return {
      anchorNode: selection.anchorNode,
      anchorOffset: selection.anchorOffset,
      focusNode: selection.focusNode,
      focusOffset: selection.focusOffset
    };
  } else if (document.selection) {
    var range = document.selection.createRange();
    return {
      parentElement: range.parentElement(),
      text: range.text,
      top: range.boundingTop,
      left: range.boundingLeft
    };
  }
}

/**
 * Poll selection to see whether it's changed.
 *
 * @param {object} nativeEvent
 * @return {?SyntheticEvent}
 */
function constructSelectEvent(nativeEvent, nativeEventTarget) {
  // Ensure we have the right element, and that the user is not dragging a
  // selection (this matches native `select` event behavior). In HTML5, select
  // fires only on input and textarea thus if there's no focused element we
  // won't dispatch.
  if (mouseDown || activeElement == null || activeElement !== getActiveElement()) {
    return null;
  }

  // Only fire when selection has actually changed.
  var currentSelection = getSelection(activeElement);
  if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
    lastSelection = currentSelection;

    var syntheticEvent = SyntheticEvent.getPooled(eventTypes.select, activeElementInst, nativeEvent, nativeEventTarget);

    syntheticEvent.type = 'select';
    syntheticEvent.target = activeElement;

    EventPropagators.accumulateTwoPhaseDispatches(syntheticEvent);

    return syntheticEvent;
  }

  return null;
}

/**
 * This plugin creates an `onSelect` event that normalizes select events
 * across form elements.
 *
 * Supported elements are:
 * - input (see `isTextInputElement`)
 * - textarea
 * - contentEditable
 *
 * This differs from native browser implementations in the following ways:
 * - Fires on contentEditable fields as well as inputs.
 * - Fires for collapsed selection.
 * - Fires after user input.
 */
var SelectEventPlugin = {

  eventTypes: eventTypes,

  extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
    if (!hasListener) {
      return null;
    }

    var targetNode = targetInst ? ReactDOMComponentTree.getNodeFromInstance(targetInst) : window;

    switch (topLevelType) {
      // Track the input node that has focus.
      case topLevelTypes.topFocus:
        if (isTextInputElement(targetNode) || targetNode.contentEditable === 'true') {
          activeElement = targetNode;
          activeElementInst = targetInst;
          lastSelection = null;
        }
        break;
      case topLevelTypes.topBlur:
        activeElement = null;
        activeElementInst = null;
        lastSelection = null;
        break;

      // Don't fire the event while the user is dragging. This matches the
      // semantics of the native select event.
      case topLevelTypes.topMouseDown:
        mouseDown = true;
        break;
      case topLevelTypes.topContextMenu:
      case topLevelTypes.topMouseUp:
        mouseDown = false;
        return constructSelectEvent(nativeEvent, nativeEventTarget);

      // Chrome and IE fire non-standard event when selection is changed (and
      // sometimes when it hasn't). IE's event fires out of order with respect
      // to key and input events on deletion, so we discard it.
      //
      // Firefox doesn't support selectionchange, so check selection status
      // after each key entry. The selection changes after keydown and before
      // keyup, but we check on keydown as well in the case of holding down a
      // key, when multiple keydown events are fired but only one keyup is.
      // This is also our approach for IE handling, for the reason above.
      case topLevelTypes.topSelectionChange:
        if (skipSelectionChangeEvent) {
          break;
        }
      // falls through
      case topLevelTypes.topKeyDown:
      case topLevelTypes.topKeyUp:
        return constructSelectEvent(nativeEvent, nativeEventTarget);
    }

    return null;
  },

  didPutListener: function (inst, registrationName, listener) {
    if (registrationName === ON_SELECT_KEY) {
      hasListener = true;
    }
  }
};

module.exports = SelectEventPlugin;
},{"./EventConstants":54,"./EventPropagators":58,"./ReactDOMComponentTree":83,"./ReactInputSelection":111,"./SyntheticEvent":145,"./isTextInputElement":177,"fbjs/lib/ExecutionEnvironment":190,"fbjs/lib/getActiveElement":199,"fbjs/lib/keyOf":208,"fbjs/lib/shallowEqual":212}],140:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SimpleEventPlugin
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var EventConstants = require('./EventConstants');
var EventListener = require('fbjs/lib/EventListener');
var EventPropagators = require('./EventPropagators');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var SyntheticAnimationEvent = require('./SyntheticAnimationEvent');
var SyntheticClipboardEvent = require('./SyntheticClipboardEvent');
var SyntheticEvent = require('./SyntheticEvent');
var SyntheticFocusEvent = require('./SyntheticFocusEvent');
var SyntheticKeyboardEvent = require('./SyntheticKeyboardEvent');
var SyntheticMouseEvent = require('./SyntheticMouseEvent');
var SyntheticDragEvent = require('./SyntheticDragEvent');
var SyntheticTouchEvent = require('./SyntheticTouchEvent');
var SyntheticTransitionEvent = require('./SyntheticTransitionEvent');
var SyntheticUIEvent = require('./SyntheticUIEvent');
var SyntheticWheelEvent = require('./SyntheticWheelEvent');

var emptyFunction = require('fbjs/lib/emptyFunction');
var getEventCharCode = require('./getEventCharCode');
var invariant = require('fbjs/lib/invariant');
var keyOf = require('fbjs/lib/keyOf');

var topLevelTypes = EventConstants.topLevelTypes;

var eventTypes = {
  abort: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onAbort: true }),
      captured: keyOf({ onAbortCapture: true })
    }
  },
  animationEnd: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onAnimationEnd: true }),
      captured: keyOf({ onAnimationEndCapture: true })
    }
  },
  animationIteration: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onAnimationIteration: true }),
      captured: keyOf({ onAnimationIterationCapture: true })
    }
  },
  animationStart: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onAnimationStart: true }),
      captured: keyOf({ onAnimationStartCapture: true })
    }
  },
  blur: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onBlur: true }),
      captured: keyOf({ onBlurCapture: true })
    }
  },
  canPlay: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onCanPlay: true }),
      captured: keyOf({ onCanPlayCapture: true })
    }
  },
  canPlayThrough: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onCanPlayThrough: true }),
      captured: keyOf({ onCanPlayThroughCapture: true })
    }
  },
  click: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onClick: true }),
      captured: keyOf({ onClickCapture: true })
    }
  },
  contextMenu: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onContextMenu: true }),
      captured: keyOf({ onContextMenuCapture: true })
    }
  },
  copy: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onCopy: true }),
      captured: keyOf({ onCopyCapture: true })
    }
  },
  cut: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onCut: true }),
      captured: keyOf({ onCutCapture: true })
    }
  },
  doubleClick: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onDoubleClick: true }),
      captured: keyOf({ onDoubleClickCapture: true })
    }
  },
  drag: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onDrag: true }),
      captured: keyOf({ onDragCapture: true })
    }
  },
  dragEnd: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onDragEnd: true }),
      captured: keyOf({ onDragEndCapture: true })
    }
  },
  dragEnter: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onDragEnter: true }),
      captured: keyOf({ onDragEnterCapture: true })
    }
  },
  dragExit: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onDragExit: true }),
      captured: keyOf({ onDragExitCapture: true })
    }
  },
  dragLeave: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onDragLeave: true }),
      captured: keyOf({ onDragLeaveCapture: true })
    }
  },
  dragOver: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onDragOver: true }),
      captured: keyOf({ onDragOverCapture: true })
    }
  },
  dragStart: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onDragStart: true }),
      captured: keyOf({ onDragStartCapture: true })
    }
  },
  drop: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onDrop: true }),
      captured: keyOf({ onDropCapture: true })
    }
  },
  durationChange: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onDurationChange: true }),
      captured: keyOf({ onDurationChangeCapture: true })
    }
  },
  emptied: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onEmptied: true }),
      captured: keyOf({ onEmptiedCapture: true })
    }
  },
  encrypted: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onEncrypted: true }),
      captured: keyOf({ onEncryptedCapture: true })
    }
  },
  ended: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onEnded: true }),
      captured: keyOf({ onEndedCapture: true })
    }
  },
  error: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onError: true }),
      captured: keyOf({ onErrorCapture: true })
    }
  },
  focus: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onFocus: true }),
      captured: keyOf({ onFocusCapture: true })
    }
  },
  input: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onInput: true }),
      captured: keyOf({ onInputCapture: true })
    }
  },
  invalid: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onInvalid: true }),
      captured: keyOf({ onInvalidCapture: true })
    }
  },
  keyDown: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onKeyDown: true }),
      captured: keyOf({ onKeyDownCapture: true })
    }
  },
  keyPress: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onKeyPress: true }),
      captured: keyOf({ onKeyPressCapture: true })
    }
  },
  keyUp: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onKeyUp: true }),
      captured: keyOf({ onKeyUpCapture: true })
    }
  },
  load: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onLoad: true }),
      captured: keyOf({ onLoadCapture: true })
    }
  },
  loadedData: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onLoadedData: true }),
      captured: keyOf({ onLoadedDataCapture: true })
    }
  },
  loadedMetadata: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onLoadedMetadata: true }),
      captured: keyOf({ onLoadedMetadataCapture: true })
    }
  },
  loadStart: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onLoadStart: true }),
      captured: keyOf({ onLoadStartCapture: true })
    }
  },
  // Note: We do not allow listening to mouseOver events. Instead, use the
  // onMouseEnter/onMouseLeave created by `EnterLeaveEventPlugin`.
  mouseDown: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onMouseDown: true }),
      captured: keyOf({ onMouseDownCapture: true })
    }
  },
  mouseMove: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onMouseMove: true }),
      captured: keyOf({ onMouseMoveCapture: true })
    }
  },
  mouseOut: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onMouseOut: true }),
      captured: keyOf({ onMouseOutCapture: true })
    }
  },
  mouseOver: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onMouseOver: true }),
      captured: keyOf({ onMouseOverCapture: true })
    }
  },
  mouseUp: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onMouseUp: true }),
      captured: keyOf({ onMouseUpCapture: true })
    }
  },
  paste: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onPaste: true }),
      captured: keyOf({ onPasteCapture: true })
    }
  },
  pause: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onPause: true }),
      captured: keyOf({ onPauseCapture: true })
    }
  },
  play: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onPlay: true }),
      captured: keyOf({ onPlayCapture: true })
    }
  },
  playing: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onPlaying: true }),
      captured: keyOf({ onPlayingCapture: true })
    }
  },
  progress: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onProgress: true }),
      captured: keyOf({ onProgressCapture: true })
    }
  },
  rateChange: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onRateChange: true }),
      captured: keyOf({ onRateChangeCapture: true })
    }
  },
  reset: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onReset: true }),
      captured: keyOf({ onResetCapture: true })
    }
  },
  scroll: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onScroll: true }),
      captured: keyOf({ onScrollCapture: true })
    }
  },
  seeked: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onSeeked: true }),
      captured: keyOf({ onSeekedCapture: true })
    }
  },
  seeking: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onSeeking: true }),
      captured: keyOf({ onSeekingCapture: true })
    }
  },
  stalled: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onStalled: true }),
      captured: keyOf({ onStalledCapture: true })
    }
  },
  submit: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onSubmit: true }),
      captured: keyOf({ onSubmitCapture: true })
    }
  },
  suspend: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onSuspend: true }),
      captured: keyOf({ onSuspendCapture: true })
    }
  },
  timeUpdate: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onTimeUpdate: true }),
      captured: keyOf({ onTimeUpdateCapture: true })
    }
  },
  touchCancel: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onTouchCancel: true }),
      captured: keyOf({ onTouchCancelCapture: true })
    }
  },
  touchEnd: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onTouchEnd: true }),
      captured: keyOf({ onTouchEndCapture: true })
    }
  },
  touchMove: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onTouchMove: true }),
      captured: keyOf({ onTouchMoveCapture: true })
    }
  },
  touchStart: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onTouchStart: true }),
      captured: keyOf({ onTouchStartCapture: true })
    }
  },
  transitionEnd: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onTransitionEnd: true }),
      captured: keyOf({ onTransitionEndCapture: true })
    }
  },
  volumeChange: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onVolumeChange: true }),
      captured: keyOf({ onVolumeChangeCapture: true })
    }
  },
  waiting: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onWaiting: true }),
      captured: keyOf({ onWaitingCapture: true })
    }
  },
  wheel: {
    phasedRegistrationNames: {
      bubbled: keyOf({ onWheel: true }),
      captured: keyOf({ onWheelCapture: true })
    }
  }
};

var topLevelEventsToDispatchConfig = {
  topAbort: eventTypes.abort,
  topAnimationEnd: eventTypes.animationEnd,
  topAnimationIteration: eventTypes.animationIteration,
  topAnimationStart: eventTypes.animationStart,
  topBlur: eventTypes.blur,
  topCanPlay: eventTypes.canPlay,
  topCanPlayThrough: eventTypes.canPlayThrough,
  topClick: eventTypes.click,
  topContextMenu: eventTypes.contextMenu,
  topCopy: eventTypes.copy,
  topCut: eventTypes.cut,
  topDoubleClick: eventTypes.doubleClick,
  topDrag: eventTypes.drag,
  topDragEnd: eventTypes.dragEnd,
  topDragEnter: eventTypes.dragEnter,
  topDragExit: eventTypes.dragExit,
  topDragLeave: eventTypes.dragLeave,
  topDragOver: eventTypes.dragOver,
  topDragStart: eventTypes.dragStart,
  topDrop: eventTypes.drop,
  topDurationChange: eventTypes.durationChange,
  topEmptied: eventTypes.emptied,
  topEncrypted: eventTypes.encrypted,
  topEnded: eventTypes.ended,
  topError: eventTypes.error,
  topFocus: eventTypes.focus,
  topInput: eventTypes.input,
  topInvalid: eventTypes.invalid,
  topKeyDown: eventTypes.keyDown,
  topKeyPress: eventTypes.keyPress,
  topKeyUp: eventTypes.keyUp,
  topLoad: eventTypes.load,
  topLoadedData: eventTypes.loadedData,
  topLoadedMetadata: eventTypes.loadedMetadata,
  topLoadStart: eventTypes.loadStart,
  topMouseDown: eventTypes.mouseDown,
  topMouseMove: eventTypes.mouseMove,
  topMouseOut: eventTypes.mouseOut,
  topMouseOver: eventTypes.mouseOver,
  topMouseUp: eventTypes.mouseUp,
  topPaste: eventTypes.paste,
  topPause: eventTypes.pause,
  topPlay: eventTypes.play,
  topPlaying: eventTypes.playing,
  topProgress: eventTypes.progress,
  topRateChange: eventTypes.rateChange,
  topReset: eventTypes.reset,
  topScroll: eventTypes.scroll,
  topSeeked: eventTypes.seeked,
  topSeeking: eventTypes.seeking,
  topStalled: eventTypes.stalled,
  topSubmit: eventTypes.submit,
  topSuspend: eventTypes.suspend,
  topTimeUpdate: eventTypes.timeUpdate,
  topTouchCancel: eventTypes.touchCancel,
  topTouchEnd: eventTypes.touchEnd,
  topTouchMove: eventTypes.touchMove,
  topTouchStart: eventTypes.touchStart,
  topTransitionEnd: eventTypes.transitionEnd,
  topVolumeChange: eventTypes.volumeChange,
  topWaiting: eventTypes.waiting,
  topWheel: eventTypes.wheel
};

for (var type in topLevelEventsToDispatchConfig) {
  topLevelEventsToDispatchConfig[type].dependencies = [type];
}

var ON_CLICK_KEY = keyOf({ onClick: null });
var onClickListeners = {};

function getDictionaryKey(inst) {
  // Prevents V8 performance issue:
  // https://github.com/facebook/react/pull/7232
  return '.' + inst._rootNodeID;
}

var SimpleEventPlugin = {

  eventTypes: eventTypes,

  extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
    var dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];
    if (!dispatchConfig) {
      return null;
    }
    var EventConstructor;
    switch (topLevelType) {
      case topLevelTypes.topAbort:
      case topLevelTypes.topCanPlay:
      case topLevelTypes.topCanPlayThrough:
      case topLevelTypes.topDurationChange:
      case topLevelTypes.topEmptied:
      case topLevelTypes.topEncrypted:
      case topLevelTypes.topEnded:
      case topLevelTypes.topError:
      case topLevelTypes.topInput:
      case topLevelTypes.topInvalid:
      case topLevelTypes.topLoad:
      case topLevelTypes.topLoadedData:
      case topLevelTypes.topLoadedMetadata:
      case topLevelTypes.topLoadStart:
      case topLevelTypes.topPause:
      case topLevelTypes.topPlay:
      case topLevelTypes.topPlaying:
      case topLevelTypes.topProgress:
      case topLevelTypes.topRateChange:
      case topLevelTypes.topReset:
      case topLevelTypes.topSeeked:
      case topLevelTypes.topSeeking:
      case topLevelTypes.topStalled:
      case topLevelTypes.topSubmit:
      case topLevelTypes.topSuspend:
      case topLevelTypes.topTimeUpdate:
      case topLevelTypes.topVolumeChange:
      case topLevelTypes.topWaiting:
        // HTML Events
        // @see http://www.w3.org/TR/html5/index.html#events-0
        EventConstructor = SyntheticEvent;
        break;
      case topLevelTypes.topKeyPress:
        // Firefox creates a keypress event for function keys too. This removes
        // the unwanted keypress events. Enter is however both printable and
        // non-printable. One would expect Tab to be as well (but it isn't).
        if (getEventCharCode(nativeEvent) === 0) {
          return null;
        }
      /* falls through */
      case topLevelTypes.topKeyDown:
      case topLevelTypes.topKeyUp:
        EventConstructor = SyntheticKeyboardEvent;
        break;
      case topLevelTypes.topBlur:
      case topLevelTypes.topFocus:
        EventConstructor = SyntheticFocusEvent;
        break;
      case topLevelTypes.topClick:
        // Firefox creates a click event on right mouse clicks. This removes the
        // unwanted click events.
        if (nativeEvent.button === 2) {
          return null;
        }
      /* falls through */
      case topLevelTypes.topContextMenu:
      case topLevelTypes.topDoubleClick:
      case topLevelTypes.topMouseDown:
      case topLevelTypes.topMouseMove:
      case topLevelTypes.topMouseOut:
      case topLevelTypes.topMouseOver:
      case topLevelTypes.topMouseUp:
        EventConstructor = SyntheticMouseEvent;
        break;
      case topLevelTypes.topDrag:
      case topLevelTypes.topDragEnd:
      case topLevelTypes.topDragEnter:
      case topLevelTypes.topDragExit:
      case topLevelTypes.topDragLeave:
      case topLevelTypes.topDragOver:
      case topLevelTypes.topDragStart:
      case topLevelTypes.topDrop:
        EventConstructor = SyntheticDragEvent;
        break;
      case topLevelTypes.topTouchCancel:
      case topLevelTypes.topTouchEnd:
      case topLevelTypes.topTouchMove:
      case topLevelTypes.topTouchStart:
        EventConstructor = SyntheticTouchEvent;
        break;
      case topLevelTypes.topAnimationEnd:
      case topLevelTypes.topAnimationIteration:
      case topLevelTypes.topAnimationStart:
        EventConstructor = SyntheticAnimationEvent;
        break;
      case topLevelTypes.topTransitionEnd:
        EventConstructor = SyntheticTransitionEvent;
        break;
      case topLevelTypes.topScroll:
        EventConstructor = SyntheticUIEvent;
        break;
      case topLevelTypes.topWheel:
        EventConstructor = SyntheticWheelEvent;
        break;
      case topLevelTypes.topCopy:
      case topLevelTypes.topCut:
      case topLevelTypes.topPaste:
        EventConstructor = SyntheticClipboardEvent;
        break;
    }
    !EventConstructor ? "production" !== 'production' ? invariant(false, 'SimpleEventPlugin: Unhandled event type, `%s`.', topLevelType) : _prodInvariant('86', topLevelType) : void 0;
    var event = EventConstructor.getPooled(dispatchConfig, targetInst, nativeEvent, nativeEventTarget);
    EventPropagators.accumulateTwoPhaseDispatches(event);
    return event;
  },

  didPutListener: function (inst, registrationName, listener) {
    // Mobile Safari does not fire properly bubble click events on
    // non-interactive elements, which means delegated click listeners do not
    // fire. The workaround for this bug involves attaching an empty click
    // listener on the target node.
    if (registrationName === ON_CLICK_KEY) {
      var key = getDictionaryKey(inst);
      var node = ReactDOMComponentTree.getNodeFromInstance(inst);
      if (!onClickListeners[key]) {
        onClickListeners[key] = EventListener.listen(node, 'click', emptyFunction);
      }
    }
  },

  willDeleteListener: function (inst, registrationName) {
    if (registrationName === ON_CLICK_KEY) {
      var key = getDictionaryKey(inst);
      onClickListeners[key].remove();
      delete onClickListeners[key];
    }
  }

};

module.exports = SimpleEventPlugin;
},{"./EventConstants":54,"./EventPropagators":58,"./ReactDOMComponentTree":83,"./SyntheticAnimationEvent":141,"./SyntheticClipboardEvent":142,"./SyntheticDragEvent":144,"./SyntheticEvent":145,"./SyntheticFocusEvent":146,"./SyntheticKeyboardEvent":148,"./SyntheticMouseEvent":149,"./SyntheticTouchEvent":150,"./SyntheticTransitionEvent":151,"./SyntheticUIEvent":152,"./SyntheticWheelEvent":153,"./getEventCharCode":166,"./reactProdInvariant":180,"fbjs/lib/EventListener":189,"fbjs/lib/emptyFunction":196,"fbjs/lib/invariant":204,"fbjs/lib/keyOf":208}],141:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticAnimationEvent
 */

'use strict';

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

/**
 * @interface Event
 * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
 * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
 */
var AnimationEventInterface = {
  animationName: null,
  elapsedTime: null,
  pseudoElement: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticEvent}
 */
function SyntheticAnimationEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticAnimationEvent, AnimationEventInterface);

module.exports = SyntheticAnimationEvent;
},{"./SyntheticEvent":145}],142:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticClipboardEvent
 */

'use strict';

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

/**
 * @interface Event
 * @see http://www.w3.org/TR/clipboard-apis/
 */
var ClipboardEventInterface = {
  clipboardData: function (event) {
    return 'clipboardData' in event ? event.clipboardData : window.clipboardData;
  }
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticUIEvent}
 */
function SyntheticClipboardEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticClipboardEvent, ClipboardEventInterface);

module.exports = SyntheticClipboardEvent;
},{"./SyntheticEvent":145}],143:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticCompositionEvent
 */

'use strict';

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

/**
 * @interface Event
 * @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
 */
var CompositionEventInterface = {
  data: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticUIEvent}
 */
function SyntheticCompositionEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticCompositionEvent, CompositionEventInterface);

module.exports = SyntheticCompositionEvent;
},{"./SyntheticEvent":145}],144:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticDragEvent
 */

'use strict';

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

/**
 * @interface DragEvent
 * @see http://www.w3.org/TR/DOM-Level-3-Events/
 */
var DragEventInterface = {
  dataTransfer: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticUIEvent}
 */
function SyntheticDragEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticMouseEvent.augmentClass(SyntheticDragEvent, DragEventInterface);

module.exports = SyntheticDragEvent;
},{"./SyntheticMouseEvent":149}],145:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticEvent
 */

'use strict';

var _assign = require('object-assign');

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

var emptyFunction = require('fbjs/lib/emptyFunction');
var warning = require('fbjs/lib/warning');

var didWarnForAddedNewProperty = false;
var isProxySupported = typeof Proxy === 'function';

var shouldBeReleasedProperties = ['dispatchConfig', '_targetInst', 'nativeEvent', 'isDefaultPrevented', 'isPropagationStopped', '_dispatchListeners', '_dispatchInstances'];

/**
 * @interface Event
 * @see http://www.w3.org/TR/DOM-Level-3-Events/
 */
var EventInterface = {
  type: null,
  target: null,
  // currentTarget is set when dispatching; no use in copying it here
  currentTarget: emptyFunction.thatReturnsNull,
  eventPhase: null,
  bubbles: null,
  cancelable: null,
  timeStamp: function (event) {
    return event.timeStamp || Date.now();
  },
  defaultPrevented: null,
  isTrusted: null
};

/**
 * Synthetic events are dispatched by event plugins, typically in response to a
 * top-level event delegation handler.
 *
 * These systems should generally use pooling to reduce the frequency of garbage
 * collection. The system should check `isPersistent` to determine whether the
 * event should be released into the pool after being dispatched. Users that
 * need a persisted event should invoke `persist`.
 *
 * Synthetic events (and subclasses) implement the DOM Level 3 Events API by
 * normalizing browser quirks. Subclasses do not necessarily have to implement a
 * DOM interface; custom application-specific events can also subclass this.
 *
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {*} targetInst Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @param {DOMEventTarget} nativeEventTarget Target node.
 */
function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) {
  if ("production" !== 'production') {
    // these have a getter/setter for warnings
    delete this.nativeEvent;
    delete this.preventDefault;
    delete this.stopPropagation;
  }

  this.dispatchConfig = dispatchConfig;
  this._targetInst = targetInst;
  this.nativeEvent = nativeEvent;

  var Interface = this.constructor.Interface;
  for (var propName in Interface) {
    if (!Interface.hasOwnProperty(propName)) {
      continue;
    }
    if ("production" !== 'production') {
      delete this[propName]; // this has a getter/setter for warnings
    }
    var normalize = Interface[propName];
    if (normalize) {
      this[propName] = normalize(nativeEvent);
    } else {
      if (propName === 'target') {
        this.target = nativeEventTarget;
      } else {
        this[propName] = nativeEvent[propName];
      }
    }
  }

  var defaultPrevented = nativeEvent.defaultPrevented != null ? nativeEvent.defaultPrevented : nativeEvent.returnValue === false;
  if (defaultPrevented) {
    this.isDefaultPrevented = emptyFunction.thatReturnsTrue;
  } else {
    this.isDefaultPrevented = emptyFunction.thatReturnsFalse;
  }
  this.isPropagationStopped = emptyFunction.thatReturnsFalse;
  return this;
}

_assign(SyntheticEvent.prototype, {

  preventDefault: function () {
    this.defaultPrevented = true;
    var event = this.nativeEvent;
    if (!event) {
      return;
    }

    if (event.preventDefault) {
      event.preventDefault();
    } else if (typeof event.returnValue !== 'unknown') {
      // eslint-disable-line valid-typeof
      event.returnValue = false;
    }
    this.isDefaultPrevented = emptyFunction.thatReturnsTrue;
  },

  stopPropagation: function () {
    var event = this.nativeEvent;
    if (!event) {
      return;
    }

    if (event.stopPropagation) {
      event.stopPropagation();
    } else if (typeof event.cancelBubble !== 'unknown') {
      // eslint-disable-line valid-typeof
      // The ChangeEventPlugin registers a "propertychange" event for
      // IE. This event does not support bubbling or cancelling, and
      // any references to cancelBubble throw "Member not found".  A
      // typeof check of "unknown" circumvents this issue (and is also
      // IE specific).
      event.cancelBubble = true;
    }

    this.isPropagationStopped = emptyFunction.thatReturnsTrue;
  },

  /**
   * We release all dispatched `SyntheticEvent`s after each event loop, adding
   * them back into the pool. This allows a way to hold onto a reference that
   * won't be added back into the pool.
   */
  persist: function () {
    this.isPersistent = emptyFunction.thatReturnsTrue;
  },

  /**
   * Checks if this event should be released back into the pool.
   *
   * @return {boolean} True if this should not be released, false otherwise.
   */
  isPersistent: emptyFunction.thatReturnsFalse,

  /**
   * `PooledClass` looks for `destructor` on each instance it releases.
   */
  destructor: function () {
    var Interface = this.constructor.Interface;
    for (var propName in Interface) {
      if ("production" !== 'production') {
        Object.defineProperty(this, propName, getPooledWarningPropertyDefinition(propName, Interface[propName]));
      } else {
        this[propName] = null;
      }
    }
    for (var i = 0; i < shouldBeReleasedProperties.length; i++) {
      this[shouldBeReleasedProperties[i]] = null;
    }
    if ("production" !== 'production') {
      Object.defineProperty(this, 'nativeEvent', getPooledWarningPropertyDefinition('nativeEvent', null));
      Object.defineProperty(this, 'preventDefault', getPooledWarningPropertyDefinition('preventDefault', emptyFunction));
      Object.defineProperty(this, 'stopPropagation', getPooledWarningPropertyDefinition('stopPropagation', emptyFunction));
    }
  }

});

SyntheticEvent.Interface = EventInterface;

if ("production" !== 'production') {
  if (isProxySupported) {
    /*eslint-disable no-func-assign */
    SyntheticEvent = new Proxy(SyntheticEvent, {
      construct: function (target, args) {
        return this.apply(target, Object.create(target.prototype), args);
      },
      apply: function (constructor, that, args) {
        return new Proxy(constructor.apply(that, args), {
          set: function (target, prop, value) {
            if (prop !== 'isPersistent' && !target.constructor.Interface.hasOwnProperty(prop) && shouldBeReleasedProperties.indexOf(prop) === -1) {
              "production" !== 'production' ? warning(didWarnForAddedNewProperty || target.isPersistent(), 'This synthetic event is reused for performance reasons. If you\'re ' + 'seeing this, you\'re adding a new property in the synthetic event object. ' + 'The property is never released. See ' + 'https://fb.me/react-event-pooling for more information.') : void 0;
              didWarnForAddedNewProperty = true;
            }
            target[prop] = value;
            return true;
          }
        });
      }
    });
    /*eslint-enable no-func-assign */
  }
}
/**
 * Helper to reduce boilerplate when creating subclasses.
 *
 * @param {function} Class
 * @param {?object} Interface
 */
SyntheticEvent.augmentClass = function (Class, Interface) {
  var Super = this;

  var E = function () {};
  E.prototype = Super.prototype;
  var prototype = new E();

  _assign(prototype, Class.prototype);
  Class.prototype = prototype;
  Class.prototype.constructor = Class;

  Class.Interface = _assign({}, Super.Interface, Interface);
  Class.augmentClass = Super.augmentClass;

  PooledClass.addPoolingTo(Class, PooledClass.fourArgumentPooler);
};

PooledClass.addPoolingTo(SyntheticEvent, PooledClass.fourArgumentPooler);

module.exports = SyntheticEvent;

/**
  * Helper to nullify syntheticEvent instance properties when destructing
  *
  * @param {object} SyntheticEvent
  * @param {String} propName
  * @return {object} defineProperty object
  */
function getPooledWarningPropertyDefinition(propName, getVal) {
  var isFunction = typeof getVal === 'function';
  return {
    configurable: true,
    set: set,
    get: get
  };

  function set(val) {
    var action = isFunction ? 'setting the method' : 'setting the property';
    warn(action, 'This is effectively a no-op');
    return val;
  }

  function get() {
    var action = isFunction ? 'accessing the method' : 'accessing the property';
    var result = isFunction ? 'This is a no-op function' : 'This is set to null';
    warn(action, result);
    return getVal;
  }

  function warn(action, result) {
    var warningCondition = false;
    "production" !== 'production' ? warning(warningCondition, 'This synthetic event is reused for performance reasons. If you\'re seeing this, ' + 'you\'re %s `%s` on a released/nullified synthetic event. %s. ' + 'If you must keep the original synthetic event around, use event.persist(). ' + 'See https://fb.me/react-event-pooling for more information.', action, propName, result) : void 0;
  }
}
},{"./PooledClass":63,"fbjs/lib/emptyFunction":196,"fbjs/lib/warning":213,"object-assign":214}],146:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticFocusEvent
 */

'use strict';

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

/**
 * @interface FocusEvent
 * @see http://www.w3.org/TR/DOM-Level-3-Events/
 */
var FocusEventInterface = {
  relatedTarget: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticUIEvent}
 */
function SyntheticFocusEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticUIEvent.augmentClass(SyntheticFocusEvent, FocusEventInterface);

module.exports = SyntheticFocusEvent;
},{"./SyntheticUIEvent":152}],147:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticInputEvent
 */

'use strict';

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

/**
 * @interface Event
 * @see http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105
 *      /#events-inputevents
 */
var InputEventInterface = {
  data: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticUIEvent}
 */
function SyntheticInputEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticInputEvent, InputEventInterface);

module.exports = SyntheticInputEvent;
},{"./SyntheticEvent":145}],148:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticKeyboardEvent
 */

'use strict';

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

var getEventCharCode = require('./getEventCharCode');
var getEventKey = require('./getEventKey');
var getEventModifierState = require('./getEventModifierState');

/**
 * @interface KeyboardEvent
 * @see http://www.w3.org/TR/DOM-Level-3-Events/
 */
var KeyboardEventInterface = {
  key: getEventKey,
  location: null,
  ctrlKey: null,
  shiftKey: null,
  altKey: null,
  metaKey: null,
  repeat: null,
  locale: null,
  getModifierState: getEventModifierState,
  // Legacy Interface
  charCode: function (event) {
    // `charCode` is the result of a KeyPress event and represents the value of
    // the actual printable character.

    // KeyPress is deprecated, but its replacement is not yet final and not
    // implemented in any major browser. Only KeyPress has charCode.
    if (event.type === 'keypress') {
      return getEventCharCode(event);
    }
    return 0;
  },
  keyCode: function (event) {
    // `keyCode` is the result of a KeyDown/Up event and represents the value of
    // physical keyboard key.

    // The actual meaning of the value depends on the users' keyboard layout
    // which cannot be detected. Assuming that it is a US keyboard layout
    // provides a surprisingly accurate mapping for US and European users.
    // Due to this, it is left to the user to implement at this time.
    if (event.type === 'keydown' || event.type === 'keyup') {
      return event.keyCode;
    }
    return 0;
  },
  which: function (event) {
    // `which` is an alias for either `keyCode` or `charCode` depending on the
    // type of the event.
    if (event.type === 'keypress') {
      return getEventCharCode(event);
    }
    if (event.type === 'keydown' || event.type === 'keyup') {
      return event.keyCode;
    }
    return 0;
  }
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticUIEvent}
 */
function SyntheticKeyboardEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticUIEvent.augmentClass(SyntheticKeyboardEvent, KeyboardEventInterface);

module.exports = SyntheticKeyboardEvent;
},{"./SyntheticUIEvent":152,"./getEventCharCode":166,"./getEventKey":167,"./getEventModifierState":168}],149:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticMouseEvent
 */

'use strict';

var SyntheticUIEvent = require('./SyntheticUIEvent');
var ViewportMetrics = require('./ViewportMetrics');

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

/**
 * @interface MouseEvent
 * @see http://www.w3.org/TR/DOM-Level-3-Events/
 */
var MouseEventInterface = {
  screenX: null,
  screenY: null,
  clientX: null,
  clientY: null,
  ctrlKey: null,
  shiftKey: null,
  altKey: null,
  metaKey: null,
  getModifierState: getEventModifierState,
  button: function (event) {
    // Webkit, Firefox, IE9+
    // which:  1 2 3
    // button: 0 1 2 (standard)
    var button = event.button;
    if ('which' in event) {
      return button;
    }
    // IE<9
    // which:  undefined
    // button: 0 0 0
    // button: 1 4 2 (onmouseup)
    return button === 2 ? 2 : button === 4 ? 1 : 0;
  },
  buttons: null,
  relatedTarget: function (event) {
    return event.relatedTarget || (event.fromElement === event.srcElement ? event.toElement : event.fromElement);
  },
  // "Proprietary" Interface.
  pageX: function (event) {
    return 'pageX' in event ? event.pageX : event.clientX + ViewportMetrics.currentScrollLeft;
  },
  pageY: function (event) {
    return 'pageY' in event ? event.pageY : event.clientY + ViewportMetrics.currentScrollTop;
  }
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticUIEvent}
 */
function SyntheticMouseEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticUIEvent.augmentClass(SyntheticMouseEvent, MouseEventInterface);

module.exports = SyntheticMouseEvent;
},{"./SyntheticUIEvent":152,"./ViewportMetrics":155,"./getEventModifierState":168}],150:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticTouchEvent
 */

'use strict';

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

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

/**
 * @interface TouchEvent
 * @see http://www.w3.org/TR/touch-events/
 */
var TouchEventInterface = {
  touches: null,
  targetTouches: null,
  changedTouches: null,
  altKey: null,
  metaKey: null,
  ctrlKey: null,
  shiftKey: null,
  getModifierState: getEventModifierState
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticUIEvent}
 */
function SyntheticTouchEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticUIEvent.augmentClass(SyntheticTouchEvent, TouchEventInterface);

module.exports = SyntheticTouchEvent;
},{"./SyntheticUIEvent":152,"./getEventModifierState":168}],151:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticTransitionEvent
 */

'use strict';

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

/**
 * @interface Event
 * @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events-
 * @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent
 */
var TransitionEventInterface = {
  propertyName: null,
  elapsedTime: null,
  pseudoElement: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticEvent}
 */
function SyntheticTransitionEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticTransitionEvent, TransitionEventInterface);

module.exports = SyntheticTransitionEvent;
},{"./SyntheticEvent":145}],152:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticUIEvent
 */

'use strict';

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

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

/**
 * @interface UIEvent
 * @see http://www.w3.org/TR/DOM-Level-3-Events/
 */
var UIEventInterface = {
  view: function (event) {
    if (event.view) {
      return event.view;
    }

    var target = getEventTarget(event);
    if (target.window === target) {
      // target is a window object
      return target;
    }

    var doc = target.ownerDocument;
    // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
    if (doc) {
      return doc.defaultView || doc.parentWindow;
    } else {
      return window;
    }
  },
  detail: function (event) {
    return event.detail || 0;
  }
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticEvent}
 */
function SyntheticUIEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticUIEvent, UIEventInterface);

module.exports = SyntheticUIEvent;
},{"./SyntheticEvent":145,"./getEventTarget":169}],153:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SyntheticWheelEvent
 */

'use strict';

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

/**
 * @interface WheelEvent
 * @see http://www.w3.org/TR/DOM-Level-3-Events/
 */
var WheelEventInterface = {
  deltaX: function (event) {
    return 'deltaX' in event ? event.deltaX :
    // Fallback to `wheelDeltaX` for Webkit and normalize (right is positive).
    'wheelDeltaX' in event ? -event.wheelDeltaX : 0;
  },
  deltaY: function (event) {
    return 'deltaY' in event ? event.deltaY :
    // Fallback to `wheelDeltaY` for Webkit and normalize (down is positive).
    'wheelDeltaY' in event ? -event.wheelDeltaY :
    // Fallback to `wheelDelta` for IE<9 and normalize (down is positive).
    'wheelDelta' in event ? -event.wheelDelta : 0;
  },
  deltaZ: null,

  // Browsers without "deltaMode" is reporting in raw wheel delta where one
  // notch on the scroll is always +/- 120, roughly equivalent to pixels.
  // A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or
  // ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size.
  deltaMode: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticMouseEvent}
 */
function SyntheticWheelEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticMouseEvent.augmentClass(SyntheticWheelEvent, WheelEventInterface);

module.exports = SyntheticWheelEvent;
},{"./SyntheticMouseEvent":149}],154:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule Transaction
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var invariant = require('fbjs/lib/invariant');

/**
 * `Transaction` creates a black box that is able to wrap any method such that
 * certain invariants are maintained before and after the method is invoked
 * (Even if an exception is thrown while invoking the wrapped method). Whoever
 * instantiates a transaction can provide enforcers of the invariants at
 * creation time. The `Transaction` class itself will supply one additional
 * automatic invariant for you - the invariant that any transaction instance
 * should not be run while it is already being run. You would typically create a
 * single instance of a `Transaction` for reuse multiple times, that potentially
 * is used to wrap several different methods. Wrappers are extremely simple -
 * they only require implementing two methods.
 *
 * <pre>
 *                       wrappers (injected at creation time)
 *                                      +        +
 *                                      |        |
 *                    +-----------------|--------|--------------+
 *                    |                 v        |              |
 *                    |      +---------------+   |              |
 *                    |   +--|    wrapper1   |---|----+         |
 *                    |   |  +---------------+   v    |         |
 *                    |   |          +-------------+  |         |
 *                    |   |     +----|   wrapper2  |--------+   |
 *                    |   |     |    +-------------+  |     |   |
 *                    |   |     |                     |     |   |
 *                    |   v     v                     v     v   | wrapper
 *                    | +---+ +---+   +---------+   +---+ +---+ | invariants
 * perform(anyMethod) | |   | |   |   |         |   |   | |   | | maintained
 * +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | +---+ +---+   +---------+   +---+ +---+ |
 *                    |  initialize                    close    |
 *                    +-----------------------------------------+
 * </pre>
 *
 * Use cases:
 * - Preserving the input selection ranges before/after reconciliation.
 *   Restoring selection even in the event of an unexpected error.
 * - Deactivating events while rearranging the DOM, preventing blurs/focuses,
 *   while guaranteeing that afterwards, the event system is reactivated.
 * - Flushing a queue of collected DOM mutations to the main UI thread after a
 *   reconciliation takes place in a worker thread.
 * - Invoking any collected `componentDidUpdate` callbacks after rendering new
 *   content.
 * - (Future use case): Wrapping particular flushes of the `ReactWorker` queue
 *   to preserve the `scrollTop` (an automatic scroll aware DOM).
 * - (Future use case): Layout calculations before and after DOM updates.
 *
 * Transactional plugin API:
 * - A module that has an `initialize` method that returns any precomputation.
 * - and a `close` method that accepts the precomputation. `close` is invoked
 *   when the wrapped process is completed, or has failed.
 *
 * @param {Array<TransactionalWrapper>} transactionWrapper Wrapper modules
 * that implement `initialize` and `close`.
 * @return {Transaction} Single transaction for reuse in thread.
 *
 * @class Transaction
 */
var Mixin = {
  /**
   * Sets up this instance so that it is prepared for collecting metrics. Does
   * so such that this setup method may be used on an instance that is already
   * initialized, in a way that does not consume additional memory upon reuse.
   * That can be useful if you decide to make your subclass of this mixin a
   * "PooledClass".
   */
  reinitializeTransaction: function () {
    this.transactionWrappers = this.getTransactionWrappers();
    if (this.wrapperInitData) {
      this.wrapperInitData.length = 0;
    } else {
      this.wrapperInitData = [];
    }
    this._isInTransaction = false;
  },

  _isInTransaction: false,

  /**
   * @abstract
   * @return {Array<TransactionWrapper>} Array of transaction wrappers.
   */
  getTransactionWrappers: null,

  isInTransaction: function () {
    return !!this._isInTransaction;
  },

  /**
   * Executes the function within a safety window. Use this for the top level
   * methods that result in large amounts of computation/mutations that would
   * need to be safety checked. The optional arguments helps prevent the need
   * to bind in many cases.
   *
   * @param {function} method Member of scope to call.
   * @param {Object} scope Scope to invoke from.
   * @param {Object?=} a Argument to pass to the method.
   * @param {Object?=} b Argument to pass to the method.
   * @param {Object?=} c Argument to pass to the method.
   * @param {Object?=} d Argument to pass to the method.
   * @param {Object?=} e Argument to pass to the method.
   * @param {Object?=} f Argument to pass to the method.
   *
   * @return {*} Return value from `method`.
   */
  perform: function (method, scope, a, b, c, d, e, f) {
    !!this.isInTransaction() ? "production" !== 'production' ? invariant(false, 'Transaction.perform(...): Cannot initialize a transaction when there is already an outstanding transaction.') : _prodInvariant('27') : void 0;
    var errorThrown;
    var ret;
    try {
      this._isInTransaction = true;
      // Catching errors makes debugging more difficult, so we start with
      // errorThrown set to true before setting it to false after calling
      // close -- if it's still set to true in the finally block, it means
      // one of these calls threw.
      errorThrown = true;
      this.initializeAll(0);
      ret = method.call(scope, a, b, c, d, e, f);
      errorThrown = false;
    } finally {
      try {
        if (errorThrown) {
          // If `method` throws, prefer to show that stack trace over any thrown
          // by invoking `closeAll`.
          try {
            this.closeAll(0);
          } catch (err) {}
        } else {
          // Since `method` didn't throw, we don't want to silence the exception
          // here.
          this.closeAll(0);
        }
      } finally {
        this._isInTransaction = false;
      }
    }
    return ret;
  },

  initializeAll: function (startIndex) {
    var transactionWrappers = this.transactionWrappers;
    for (var i = startIndex; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      try {
        // Catching errors makes debugging more difficult, so we start with the
        // OBSERVED_ERROR state before overwriting it with the real return value
        // of initialize -- if it's still set to OBSERVED_ERROR in the finally
        // block, it means wrapper.initialize threw.
        this.wrapperInitData[i] = Transaction.OBSERVED_ERROR;
        this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null;
      } finally {
        if (this.wrapperInitData[i] === Transaction.OBSERVED_ERROR) {
          // The initializer for wrapper i threw an error; initialize the
          // remaining wrappers but silence any exceptions from them to ensure
          // that the first error is the one to bubble up.
          try {
            this.initializeAll(i + 1);
          } catch (err) {}
        }
      }
    }
  },

  /**
   * Invokes each of `this.transactionWrappers.close[i]` functions, passing into
   * them the respective return values of `this.transactionWrappers.init[i]`
   * (`close`rs that correspond to initializers that failed will not be
   * invoked).
   */
  closeAll: function (startIndex) {
    !this.isInTransaction() ? "production" !== 'production' ? invariant(false, 'Transaction.closeAll(): Cannot close transaction when none are open.') : _prodInvariant('28') : void 0;
    var transactionWrappers = this.transactionWrappers;
    for (var i = startIndex; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      var initData = this.wrapperInitData[i];
      var errorThrown;
      try {
        // Catching errors makes debugging more difficult, so we start with
        // errorThrown set to true before setting it to false after calling
        // close -- if it's still set to true in the finally block, it means
        // wrapper.close threw.
        errorThrown = true;
        if (initData !== Transaction.OBSERVED_ERROR && wrapper.close) {
          wrapper.close.call(this, initData);
        }
        errorThrown = false;
      } finally {
        if (errorThrown) {
          // The closer for wrapper i threw an error; close the remaining
          // wrappers but silence any exceptions from them to ensure that the
          // first error is the one to bubble up.
          try {
            this.closeAll(i + 1);
          } catch (e) {}
        }
      }
    }
    this.wrapperInitData.length = 0;
  }
};

var Transaction = {

  Mixin: Mixin,

  /**
   * Token to look for to determine if an error occurred.
   */
  OBSERVED_ERROR: {}

};

module.exports = Transaction;
},{"./reactProdInvariant":180,"fbjs/lib/invariant":204}],155:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ViewportMetrics
 */

'use strict';

var ViewportMetrics = {

  currentScrollLeft: 0,

  currentScrollTop: 0,

  refreshScrollValues: function (scrollPosition) {
    ViewportMetrics.currentScrollLeft = scrollPosition.x;
    ViewportMetrics.currentScrollTop = scrollPosition.y;
  }

};

module.exports = ViewportMetrics;
},{}],156:[function(require,module,exports){
/**
 * Copyright 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule accumulateInto
 * 
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var invariant = require('fbjs/lib/invariant');

/**
 * Accumulates items that must not be null or undefined into the first one. This
 * is used to conserve memory by avoiding array allocations, and thus sacrifices
 * API cleanness. Since `current` can be null before being passed in and not
 * null after this function, make sure to assign it back to `current`:
 *
 * `a = accumulateInto(a, b);`
 *
 * This API should be sparingly used. Try `accumulate` for something cleaner.
 *
 * @return {*|array<*>} An accumulation of items.
 */

function accumulateInto(current, next) {
  !(next != null) ? "production" !== 'production' ? invariant(false, 'accumulateInto(...): Accumulated items must not be null or undefined.') : _prodInvariant('30') : void 0;

  if (current == null) {
    return next;
  }

  // Both are not empty. Warning: Never call x.concat(y) when you are not
  // certain that x is an Array (x could be a string with concat method).
  if (Array.isArray(current)) {
    if (Array.isArray(next)) {
      current.push.apply(current, next);
      return current;
    }
    current.push(next);
    return current;
  }

  if (Array.isArray(next)) {
    // A bit too dangerous to mutate `next`.
    return [current].concat(next);
  }

  return [current, next];
}

module.exports = accumulateInto;
},{"./reactProdInvariant":180,"fbjs/lib/invariant":204}],157:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule adler32
 * 
 */

'use strict';

var MOD = 65521;

// adler32 is not cryptographically strong, and is only used to sanity check that
// markup generated on the server matches the markup generated on the client.
// This implementation (a modified version of the SheetJS version) has been optimized
// for our use case, at the expense of conforming to the adler32 specification
// for non-ascii inputs.
function adler32(data) {
  var a = 1;
  var b = 0;
  var i = 0;
  var l = data.length;
  var m = l & ~0x3;
  while (i < m) {
    var n = Math.min(i + 4096, m);
    for (; i < n; i += 4) {
      b += (a += data.charCodeAt(i)) + (a += data.charCodeAt(i + 1)) + (a += data.charCodeAt(i + 2)) + (a += data.charCodeAt(i + 3));
    }
    a %= MOD;
    b %= MOD;
  }
  for (; i < l; i++) {
    b += a += data.charCodeAt(i);
  }
  a %= MOD;
  b %= MOD;
  return a | b << 16;
}

module.exports = adler32;
},{}],158:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule canDefineProperty
 */

'use strict';

var canDefineProperty = false;
if ("production" !== 'production') {
  try {
    Object.defineProperty({}, 'x', { get: function () {} });
    canDefineProperty = true;
  } catch (x) {
    // IE will fail on defineProperty
  }
}

module.exports = canDefineProperty;
},{}],159:[function(require,module,exports){
(function (process){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule checkReactTypeSpec
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var ReactPropTypeLocationNames = require('./ReactPropTypeLocationNames');
var ReactPropTypesSecret = require('./ReactPropTypesSecret');

var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');

var ReactComponentTreeHook;

if (typeof process !== 'undefined' && process.env && "production" === 'test') {
  // Temporary hack.
  // Inline requires don't work well with Jest:
  // https://github.com/facebook/react/issues/7240
  // Remove the inline requires when we don't need them anymore:
  // https://github.com/facebook/react/pull/7178
  ReactComponentTreeHook = require('./ReactComponentTreeHook');
}

var loggedTypeFailures = {};

/**
 * Assert that the values match with the type specs.
 * Error messages are memorized and will only be shown once.
 *
 * @param {object} typeSpecs Map of name to a ReactPropType
 * @param {object} values Runtime values that need to be type-checked
 * @param {string} location e.g. "prop", "context", "child context"
 * @param {string} componentName Name of the component for error messages.
 * @param {?object} element The React element that is being type-checked
 * @param {?number} debugID The React component instance that is being type-checked
 * @private
 */
function checkReactTypeSpec(typeSpecs, values, location, componentName, element, debugID) {
  for (var typeSpecName in typeSpecs) {
    if (typeSpecs.hasOwnProperty(typeSpecName)) {
      var error;
      // Prop type validation may throw. In case they do, we don't want to
      // fail the render phase where it didn't fail before. So we log it.
      // After these have been cleaned up, we'll let them throw.
      try {
        // This is intentionally an invariant that gets caught. It's the same
        // behavior as without this statement except with a better message.
        !(typeof typeSpecs[typeSpecName] === 'function') ? "production" !== 'production' ? invariant(false, '%s: %s type `%s` is invalid; it must be a function, usually from React.PropTypes.', componentName || 'React class', ReactPropTypeLocationNames[location], typeSpecName) : _prodInvariant('84', componentName || 'React class', ReactPropTypeLocationNames[location], typeSpecName) : void 0;
        error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
      } catch (ex) {
        error = ex;
      }
      "production" !== 'production' ? warning(!error || error instanceof Error, '%s: type specification of %s `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', ReactPropTypeLocationNames[location], typeSpecName, typeof error) : void 0;
      if (error instanceof Error && !(error.message in loggedTypeFailures)) {
        // Only monitor this failure once because there tends to be a lot of the
        // same error.
        loggedTypeFailures[error.message] = true;

        var componentStackInfo = '';

        if ("production" !== 'production') {
          if (!ReactComponentTreeHook) {
            ReactComponentTreeHook = require('./ReactComponentTreeHook');
          }
          if (debugID !== null) {
            componentStackInfo = ReactComponentTreeHook.getStackAddendumByID(debugID);
          } else if (element !== null) {
            componentStackInfo = ReactComponentTreeHook.getCurrentStackAddendum(element);
          }
        }

        "production" !== 'production' ? warning(false, 'Failed %s type: %s%s', location, error.message, componentStackInfo) : void 0;
      }
    }
  }
}

module.exports = checkReactTypeSpec;
}).call(this,require('_process'))
},{"./ReactComponentTreeHook":75,"./ReactPropTypeLocationNames":122,"./ReactPropTypesSecret":125,"./reactProdInvariant":180,"_process":18,"fbjs/lib/invariant":204,"fbjs/lib/warning":213}],160:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule createMicrosoftUnsafeLocalFunction
 */

/* globals MSApp */

'use strict';

/**
 * Create a function which has 'unsafe' privileges (required by windows8 apps)
 */

var createMicrosoftUnsafeLocalFunction = function (func) {
  if (typeof MSApp !== 'undefined' && MSApp.execUnsafeLocalFunction) {
    return function (arg0, arg1, arg2, arg3) {
      MSApp.execUnsafeLocalFunction(function () {
        return func(arg0, arg1, arg2, arg3);
      });
    };
  } else {
    return func;
  }
};

module.exports = createMicrosoftUnsafeLocalFunction;
},{}],161:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule dangerousStyleValue
 */

'use strict';

var CSSProperty = require('./CSSProperty');
var warning = require('fbjs/lib/warning');

var isUnitlessNumber = CSSProperty.isUnitlessNumber;
var styleWarnings = {};

/**
 * Convert a value into the proper css writable value. The style name `name`
 * should be logical (no hyphens), as specified
 * in `CSSProperty.isUnitlessNumber`.
 *
 * @param {string} name CSS property name such as `topMargin`.
 * @param {*} value CSS property value such as `10px`.
 * @param {ReactDOMComponent} component
 * @return {string} Normalized style value with dimensions applied.
 */
function dangerousStyleValue(name, value, component) {
  // Note that we've removed escapeTextForBrowser() calls here since the
  // whole string will be escaped when the attribute is injected into
  // the markup. If you provide unsafe user data here they can inject
  // arbitrary CSS which may be problematic (I couldn't repro this):
  // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
  // http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/
  // This is not an XSS hole but instead a potential CSS injection issue
  // which has lead to a greater discussion about how we're going to
  // trust URLs moving forward. See #2115901

  var isEmpty = value == null || typeof value === 'boolean' || value === '';
  if (isEmpty) {
    return '';
  }

  var isNonNumeric = isNaN(value);
  if (isNonNumeric || value === 0 || isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name]) {
    return '' + value; // cast to string
  }

  if (typeof value === 'string') {
    if ("production" !== 'production') {
      // Allow '0' to pass through without warning. 0 is already special and
      // doesn't require units, so we don't need to warn about it.
      if (component && value !== '0') {
        var owner = component._currentElement._owner;
        var ownerName = owner ? owner.getName() : null;
        if (ownerName && !styleWarnings[ownerName]) {
          styleWarnings[ownerName] = {};
        }
        var warned = false;
        if (ownerName) {
          var warnings = styleWarnings[ownerName];
          warned = warnings[name];
          if (!warned) {
            warnings[name] = true;
          }
        }
        if (!warned) {
          "production" !== 'production' ? warning(false, 'a `%s` tag (owner: `%s`) was passed a numeric string value ' + 'for CSS property `%s` (value: `%s`) which will be treated ' + 'as a unitless number in a future version of React.', component._currentElement.type, ownerName || 'unknown', name, value) : void 0;
        }
      }
    }
    value = value.trim();
  }
  return value + 'px';
}

module.exports = dangerousStyleValue;
},{"./CSSProperty":41,"fbjs/lib/warning":213}],162:[function(require,module,exports){
/**
 * Copyright 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * Based on the escape-html library, which is used under the MIT License below:
 *
 * Copyright (c) 2012-2013 TJ Holowaychuk
 * Copyright (c) 2015 Andreas Lubbe
 * Copyright (c) 2015 Tiancheng "Timothy" Gu
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * 'Software'), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * @providesModule escapeTextContentForBrowser
 */

'use strict';

// code copied and modified from escape-html
/**
 * Module variables.
 * @private
 */

var matchHtmlRegExp = /["'&<>]/;

/**
 * Escape special characters in the given string of html.
 *
 * @param  {string} string The string to escape for inserting into HTML
 * @return {string}
 * @public
 */

function escapeHtml(string) {
  var str = '' + string;
  var match = matchHtmlRegExp.exec(str);

  if (!match) {
    return str;
  }

  var escape;
  var html = '';
  var index = 0;
  var lastIndex = 0;

  for (index = match.index; index < str.length; index++) {
    switch (str.charCodeAt(index)) {
      case 34:
        // "
        escape = '&quot;';
        break;
      case 38:
        // &
        escape = '&amp;';
        break;
      case 39:
        // '
        escape = '&#x27;'; // modified from escape-html; used to be '&#39'
        break;
      case 60:
        // <
        escape = '&lt;';
        break;
      case 62:
        // >
        escape = '&gt;';
        break;
      default:
        continue;
    }

    if (lastIndex !== index) {
      html += str.substring(lastIndex, index);
    }

    lastIndex = index + 1;
    html += escape;
  }

  return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
}
// end code copied and modified from escape-html


/**
 * Escapes text to prevent scripting attacks.
 *
 * @param {*} text Text value to escape.
 * @return {string} An escaped string.
 */
function escapeTextContentForBrowser(text) {
  if (typeof text === 'boolean' || typeof text === 'number') {
    // this shortcircuit helps perf for types that we know will never have
    // special characters, especially given that this function is used often
    // for numeric dom ids.
    return '' + text;
  }
  return escapeHtml(text);
}

module.exports = escapeTextContentForBrowser;
},{}],163:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule findDOMNode
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var ReactCurrentOwner = require('./ReactCurrentOwner');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactInstanceMap = require('./ReactInstanceMap');

var getHostComponentFromComposite = require('./getHostComponentFromComposite');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');

/**
 * Returns the DOM node rendered by this element.
 *
 * See https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode
 *
 * @param {ReactComponent|DOMElement} componentOrElement
 * @return {?DOMElement} The root node of this element.
 */
function findDOMNode(componentOrElement) {
  if ("production" !== 'production') {
    var owner = ReactCurrentOwner.current;
    if (owner !== null) {
      "production" !== 'production' ? warning(owner._warnedAboutRefsInRender, '%s is accessing findDOMNode inside its render(). ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', owner.getName() || 'A component') : void 0;
      owner._warnedAboutRefsInRender = true;
    }
  }
  if (componentOrElement == null) {
    return null;
  }
  if (componentOrElement.nodeType === 1) {
    return componentOrElement;
  }

  var inst = ReactInstanceMap.get(componentOrElement);
  if (inst) {
    inst = getHostComponentFromComposite(inst);
    return inst ? ReactDOMComponentTree.getNodeFromInstance(inst) : null;
  }

  if (typeof componentOrElement.render === 'function') {
    !false ? "production" !== 'production' ? invariant(false, 'findDOMNode was called on an unmounted component.') : _prodInvariant('44') : void 0;
  } else {
    !false ? "production" !== 'production' ? invariant(false, 'Element appears to be neither ReactComponent nor DOMNode (keys: %s)', Object.keys(componentOrElement)) : _prodInvariant('45', Object.keys(componentOrElement)) : void 0;
  }
}

module.exports = findDOMNode;
},{"./ReactCurrentOwner":78,"./ReactDOMComponentTree":83,"./ReactInstanceMap":112,"./getHostComponentFromComposite":170,"./reactProdInvariant":180,"fbjs/lib/invariant":204,"fbjs/lib/warning":213}],164:[function(require,module,exports){
(function (process){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule flattenChildren
 * 
 */

'use strict';

var KeyEscapeUtils = require('./KeyEscapeUtils');
var traverseAllChildren = require('./traverseAllChildren');
var warning = require('fbjs/lib/warning');

var ReactComponentTreeHook;

if (typeof process !== 'undefined' && process.env && "production" === 'test') {
  // Temporary hack.
  // Inline requires don't work well with Jest:
  // https://github.com/facebook/react/issues/7240
  // Remove the inline requires when we don't need them anymore:
  // https://github.com/facebook/react/pull/7178
  ReactComponentTreeHook = require('./ReactComponentTreeHook');
}

/**
 * @param {function} traverseContext Context passed through traversal.
 * @param {?ReactComponent} child React child component.
 * @param {!string} name String name of key path to child.
 * @param {number=} selfDebugID Optional debugID of the current internal instance.
 */
function flattenSingleChildIntoContext(traverseContext, child, name, selfDebugID) {
  // We found a component instance.
  if (traverseContext && typeof traverseContext === 'object') {
    var result = traverseContext;
    var keyUnique = result[name] === undefined;
    if ("production" !== 'production') {
      if (!ReactComponentTreeHook) {
        ReactComponentTreeHook = require('./ReactComponentTreeHook');
      }
      if (!keyUnique) {
        "production" !== 'production' ? warning(false, 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), ReactComponentTreeHook.getStackAddendumByID(selfDebugID)) : void 0;
      }
    }
    if (keyUnique && child != null) {
      result[name] = child;
    }
  }
}

/**
 * Flattens children that are typically specified as `props.children`. Any null
 * children will not be included in the resulting object.
 * @return {!object} flattened children keyed by name.
 */
function flattenChildren(children, selfDebugID) {
  if (children == null) {
    return children;
  }
  var result = {};

  if ("production" !== 'production') {
    traverseAllChildren(children, function (traverseContext, child, name) {
      return flattenSingleChildIntoContext(traverseContext, child, name, selfDebugID);
    }, result);
  } else {
    traverseAllChildren(children, flattenSingleChildIntoContext, result);
  }
  return result;
}

module.exports = flattenChildren;
}).call(this,require('_process'))
},{"./KeyEscapeUtils":61,"./ReactComponentTreeHook":75,"./traverseAllChildren":186,"_process":18,"fbjs/lib/warning":213}],165:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule forEachAccumulated
 * 
 */

'use strict';

/**
 * @param {array} arr an "accumulation" of items which is either an Array or
 * a single item. Useful when paired with the `accumulate` module. This is a
 * simple utility that allows us to reason about a collection of items, but
 * handling the case when there is exactly one item (and we do not need to
 * allocate an array).
 */

function forEachAccumulated(arr, cb, scope) {
  if (Array.isArray(arr)) {
    arr.forEach(cb, scope);
  } else if (arr) {
    cb.call(scope, arr);
  }
}

module.exports = forEachAccumulated;
},{}],166:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule getEventCharCode
 */

'use strict';

/**
 * `charCode` represents the actual "character code" and is safe to use with
 * `String.fromCharCode`. As such, only keys that correspond to printable
 * characters produce a valid `charCode`, the only exception to this is Enter.
 * The Tab-key is considered non-printable and does not have a `charCode`,
 * presumably because it does not produce a tab-character in browsers.
 *
 * @param {object} nativeEvent Native browser event.
 * @return {number} Normalized `charCode` property.
 */

function getEventCharCode(nativeEvent) {
  var charCode;
  var keyCode = nativeEvent.keyCode;

  if ('charCode' in nativeEvent) {
    charCode = nativeEvent.charCode;

    // FF does not set `charCode` for the Enter-key, check against `keyCode`.
    if (charCode === 0 && keyCode === 13) {
      charCode = 13;
    }
  } else {
    // IE8 does not implement `charCode`, but `keyCode` has the correct value.
    charCode = keyCode;
  }

  // Some non-printable keys are reported in `charCode`/`keyCode`, discard them.
  // Must not discard the (non-)printable Enter-key.
  if (charCode >= 32 || charCode === 13) {
    return charCode;
  }

  return 0;
}

module.exports = getEventCharCode;
},{}],167:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule getEventKey
 */

'use strict';

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

/**
 * Normalization of deprecated HTML5 `key` values
 * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
 */
var normalizeKey = {
  'Esc': 'Escape',
  'Spacebar': ' ',
  'Left': 'ArrowLeft',
  'Up': 'ArrowUp',
  'Right': 'ArrowRight',
  'Down': 'ArrowDown',
  'Del': 'Delete',
  'Win': 'OS',
  'Menu': 'ContextMenu',
  'Apps': 'ContextMenu',
  'Scroll': 'ScrollLock',
  'MozPrintableKey': 'Unidentified'
};

/**
 * Translation from legacy `keyCode` to HTML5 `key`
 * Only special keys supported, all others depend on keyboard layout or browser
 * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
 */
var translateToKey = {
  8: 'Backspace',
  9: 'Tab',
  12: 'Clear',
  13: 'Enter',
  16: 'Shift',
  17: 'Control',
  18: 'Alt',
  19: 'Pause',
  20: 'CapsLock',
  27: 'Escape',
  32: ' ',
  33: 'PageUp',
  34: 'PageDown',
  35: 'End',
  36: 'Home',
  37: 'ArrowLeft',
  38: 'ArrowUp',
  39: 'ArrowRight',
  40: 'ArrowDown',
  45: 'Insert',
  46: 'Delete',
  112: 'F1', 113: 'F2', 114: 'F3', 115: 'F4', 116: 'F5', 117: 'F6',
  118: 'F7', 119: 'F8', 120: 'F9', 121: 'F10', 122: 'F11', 123: 'F12',
  144: 'NumLock',
  145: 'ScrollLock',
  224: 'Meta'
};

/**
 * @param {object} nativeEvent Native browser event.
 * @return {string} Normalized `key` property.
 */
function getEventKey(nativeEvent) {
  if (nativeEvent.key) {
    // Normalize inconsistent values reported by browsers due to
    // implementations of a working draft specification.

    // FireFox implements `key` but returns `MozPrintableKey` for all
    // printable characters (normalized to `Unidentified`), ignore it.
    var key = normalizeKey[nativeEvent.key] || nativeEvent.key;
    if (key !== 'Unidentified') {
      return key;
    }
  }

  // Browser does not implement `key`, polyfill as much of it as we can.
  if (nativeEvent.type === 'keypress') {
    var charCode = getEventCharCode(nativeEvent);

    // The enter-key is technically both printable and non-printable and can
    // thus be captured by `keypress`, no other non-printable key should.
    return charCode === 13 ? 'Enter' : String.fromCharCode(charCode);
  }
  if (nativeEvent.type === 'keydown' || nativeEvent.type === 'keyup') {
    // While user keyboard layout determines the actual meaning of each
    // `keyCode` value, almost all function keys have a universal value.
    return translateToKey[nativeEvent.keyCode] || 'Unidentified';
  }
  return '';
}

module.exports = getEventKey;
},{"./getEventCharCode":166}],168:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule getEventModifierState
 */

'use strict';

/**
 * Translation from modifier key to the associated property in the event.
 * @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
 */

var modifierKeyToProp = {
  'Alt': 'altKey',
  'Control': 'ctrlKey',
  'Meta': 'metaKey',
  'Shift': 'shiftKey'
};

// IE8 does not implement getModifierState so we simply map it to the only
// modifier keys exposed by the event itself, does not support Lock-keys.
// Currently, all major browsers except Chrome seems to support Lock-keys.
function modifierStateGetter(keyArg) {
  var syntheticEvent = this;
  var nativeEvent = syntheticEvent.nativeEvent;
  if (nativeEvent.getModifierState) {
    return nativeEvent.getModifierState(keyArg);
  }
  var keyProp = modifierKeyToProp[keyArg];
  return keyProp ? !!nativeEvent[keyProp] : false;
}

function getEventModifierState(nativeEvent) {
  return modifierStateGetter;
}

module.exports = getEventModifierState;
},{}],169:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule getEventTarget
 */

'use strict';

/**
 * Gets the target node from a native browser event by accounting for
 * inconsistencies in browser DOM APIs.
 *
 * @param {object} nativeEvent Native browser event.
 * @return {DOMEventTarget} Target node.
 */

function getEventTarget(nativeEvent) {
  var target = nativeEvent.target || nativeEvent.srcElement || window;

  // Normalize SVG <use> element events #4963
  if (target.correspondingUseElement) {
    target = target.correspondingUseElement;
  }

  // Safari may fire events on text nodes (Node.TEXT_NODE is 3).
  // @see http://www.quirksmode.org/js/events_properties.html
  return target.nodeType === 3 ? target.parentNode : target;
}

module.exports = getEventTarget;
},{}],170:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule getHostComponentFromComposite
 */

'use strict';

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

function getHostComponentFromComposite(inst) {
  var type;

  while ((type = inst._renderedNodeType) === ReactNodeTypes.COMPOSITE) {
    inst = inst._renderedComponent;
  }

  if (type === ReactNodeTypes.HOST) {
    return inst._renderedComponent;
  } else if (type === ReactNodeTypes.EMPTY) {
    return null;
  }
}

module.exports = getHostComponentFromComposite;
},{"./ReactNodeTypes":119}],171:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule getIteratorFn
 * 
 */

'use strict';

/* global Symbol */

var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.

/**
 * Returns the iterator method function contained on the iterable object.
 *
 * Be sure to invoke the function with the iterable as context:
 *
 *     var iteratorFn = getIteratorFn(myIterable);
 *     if (iteratorFn) {
 *       var iterator = iteratorFn.call(myIterable);
 *       ...
 *     }
 *
 * @param {?object} maybeIterable
 * @return {?function}
 */
function getIteratorFn(maybeIterable) {
  var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]);
  if (typeof iteratorFn === 'function') {
    return iteratorFn;
  }
}

module.exports = getIteratorFn;
},{}],172:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule getNodeForCharacterOffset
 */

'use strict';

/**
 * Given any node return the first leaf node without children.
 *
 * @param {DOMElement|DOMTextNode} node
 * @return {DOMElement|DOMTextNode}
 */

function getLeafNode(node) {
  while (node && node.firstChild) {
    node = node.firstChild;
  }
  return node;
}

/**
 * Get the next sibling within a container. This will walk up the
 * DOM if a node's siblings have been exhausted.
 *
 * @param {DOMElement|DOMTextNode} node
 * @return {?DOMElement|DOMTextNode}
 */
function getSiblingNode(node) {
  while (node) {
    if (node.nextSibling) {
      return node.nextSibling;
    }
    node = node.parentNode;
  }
}

/**
 * Get object describing the nodes which contain characters at offset.
 *
 * @param {DOMElement|DOMTextNode} root
 * @param {number} offset
 * @return {?object}
 */
function getNodeForCharacterOffset(root, offset) {
  var node = getLeafNode(root);
  var nodeStart = 0;
  var nodeEnd = 0;

  while (node) {
    if (node.nodeType === 3) {
      nodeEnd = nodeStart + node.textContent.length;

      if (nodeStart <= offset && nodeEnd >= offset) {
        return {
          node: node,
          offset: offset - nodeStart
        };
      }

      nodeStart = nodeEnd;
    }

    node = getLeafNode(getSiblingNode(node));
  }
}

module.exports = getNodeForCharacterOffset;
},{}],173:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule getTextContentAccessor
 */

'use strict';

var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');

var contentKey = null;

/**
 * Gets the key used to access text content on a DOM node.
 *
 * @return {?string} Key used to access text content.
 * @internal
 */
function getTextContentAccessor() {
  if (!contentKey && ExecutionEnvironment.canUseDOM) {
    // Prefer textContent to innerText because many browsers support both but
    // SVG <text> elements don't support innerText even when <div> does.
    contentKey = 'textContent' in document.documentElement ? 'textContent' : 'innerText';
  }
  return contentKey;
}

module.exports = getTextContentAccessor;
},{"fbjs/lib/ExecutionEnvironment":190}],174:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule getVendorPrefixedEventName
 */

'use strict';

var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');

/**
 * Generate a mapping of standard vendor prefixes using the defined style property and event name.
 *
 * @param {string} styleProp
 * @param {string} eventName
 * @returns {object}
 */
function makePrefixMap(styleProp, eventName) {
  var prefixes = {};

  prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
  prefixes['Webkit' + styleProp] = 'webkit' + eventName;
  prefixes['Moz' + styleProp] = 'moz' + eventName;
  prefixes['ms' + styleProp] = 'MS' + eventName;
  prefixes['O' + styleProp] = 'o' + eventName.toLowerCase();

  return prefixes;
}

/**
 * A list of event names to a configurable list of vendor prefixes.
 */
var vendorPrefixes = {
  animationend: makePrefixMap('Animation', 'AnimationEnd'),
  animationiteration: makePrefixMap('Animation', 'AnimationIteration'),
  animationstart: makePrefixMap('Animation', 'AnimationStart'),
  transitionend: makePrefixMap('Transition', 'TransitionEnd')
};

/**
 * Event names that have already been detected and prefixed (if applicable).
 */
var prefixedEventNames = {};

/**
 * Element to check for prefixes on.
 */
var style = {};

/**
 * Bootstrap if a DOM exists.
 */
if (ExecutionEnvironment.canUseDOM) {
  style = document.createElement('div').style;

  // On some platforms, in particular some releases of Android 4.x,
  // the un-prefixed "animation" and "transition" properties are defined on the
  // style object but the events that fire will still be prefixed, so we need
  // to check if the un-prefixed events are usable, and if not remove them from the map.
  if (!('AnimationEvent' in window)) {
    delete vendorPrefixes.animationend.animation;
    delete vendorPrefixes.animationiteration.animation;
    delete vendorPrefixes.animationstart.animation;
  }

  // Same as above
  if (!('TransitionEvent' in window)) {
    delete vendorPrefixes.transitionend.transition;
  }
}

/**
 * Attempts to determine the correct vendor prefixed event name.
 *
 * @param {string} eventName
 * @returns {string}
 */
function getVendorPrefixedEventName(eventName) {
  if (prefixedEventNames[eventName]) {
    return prefixedEventNames[eventName];
  } else if (!vendorPrefixes[eventName]) {
    return eventName;
  }

  var prefixMap = vendorPrefixes[eventName];

  for (var styleProp in prefixMap) {
    if (prefixMap.hasOwnProperty(styleProp) && styleProp in style) {
      return prefixedEventNames[eventName] = prefixMap[styleProp];
    }
  }

  return '';
}

module.exports = getVendorPrefixedEventName;
},{"fbjs/lib/ExecutionEnvironment":190}],175:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule instantiateReactComponent
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant'),
    _assign = require('object-assign');

var ReactCompositeComponent = require('./ReactCompositeComponent');
var ReactEmptyComponent = require('./ReactEmptyComponent');
var ReactHostComponent = require('./ReactHostComponent');

var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');

// To avoid a cyclic dependency, we create the final class in this module
var ReactCompositeComponentWrapper = function (element) {
  this.construct(element);
};
_assign(ReactCompositeComponentWrapper.prototype, ReactCompositeComponent.Mixin, {
  _instantiateReactComponent: instantiateReactComponent
});

function getDeclarationErrorAddendum(owner) {
  if (owner) {
    var name = owner.getName();
    if (name) {
      return ' Check the render method of `' + name + '`.';
    }
  }
  return '';
}

/**
 * Check if the type reference is a known internal type. I.e. not a user
 * provided composite type.
 *
 * @param {function} type
 * @return {boolean} Returns true if this is a valid internal type.
 */
function isInternalComponentType(type) {
  return typeof type === 'function' && typeof type.prototype !== 'undefined' && typeof type.prototype.mountComponent === 'function' && typeof type.prototype.receiveComponent === 'function';
}

var nextDebugID = 1;

/**
 * Given a ReactNode, create an instance that will actually be mounted.
 *
 * @param {ReactNode} node
 * @param {boolean} shouldHaveDebugID
 * @return {object} A new instance of the element's constructor.
 * @protected
 */
function instantiateReactComponent(node, shouldHaveDebugID) {
  var instance;

  if (node === null || node === false) {
    instance = ReactEmptyComponent.create(instantiateReactComponent);
  } else if (typeof node === 'object') {
    var element = node;
    !(element && (typeof element.type === 'function' || typeof element.type === 'string')) ? "production" !== 'production' ? invariant(false, 'Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s', element.type == null ? element.type : typeof element.type, getDeclarationErrorAddendum(element._owner)) : _prodInvariant('130', element.type == null ? element.type : typeof element.type, getDeclarationErrorAddendum(element._owner)) : void 0;

    // Special case string values
    if (typeof element.type === 'string') {
      instance = ReactHostComponent.createInternalComponent(element);
    } else if (isInternalComponentType(element.type)) {
      // This is temporarily available for custom components that are not string
      // representations. I.e. ART. Once those are updated to use the string
      // representation, we can drop this code path.
      instance = new element.type(element);

      // We renamed this. Allow the old name for compat. :(
      if (!instance.getHostNode) {
        instance.getHostNode = instance.getNativeNode;
      }
    } else {
      instance = new ReactCompositeComponentWrapper(element);
    }
  } else if (typeof node === 'string' || typeof node === 'number') {
    instance = ReactHostComponent.createInstanceForText(node);
  } else {
    !false ? "production" !== 'production' ? invariant(false, 'Encountered invalid React node of type %s', typeof node) : _prodInvariant('131', typeof node) : void 0;
  }

  if ("production" !== 'production') {
    "production" !== 'production' ? warning(typeof instance.mountComponent === 'function' && typeof instance.receiveComponent === 'function' && typeof instance.getHostNode === 'function' && typeof instance.unmountComponent === 'function', 'Only React Components can be mounted.') : void 0;
  }

  // These two fields are used by the DOM and ART diffing algorithms
  // respectively. Instead of using expandos on components, we should be
  // storing the state needed by the diffing algorithms elsewhere.
  instance._mountIndex = 0;
  instance._mountImage = null;

  if ("production" !== 'production') {
    instance._debugID = shouldHaveDebugID ? nextDebugID++ : 0;
  }

  // Internal instances should fully constructed at this point, so they should
  // not get any new fields added to them at this point.
  if ("production" !== 'production') {
    if (Object.preventExtensions) {
      Object.preventExtensions(instance);
    }
  }

  return instance;
}

module.exports = instantiateReactComponent;
},{"./ReactCompositeComponent":77,"./ReactEmptyComponent":103,"./ReactHostComponent":108,"./reactProdInvariant":180,"fbjs/lib/invariant":204,"fbjs/lib/warning":213,"object-assign":214}],176:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule isEventSupported
 */

'use strict';

var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');

var useHasFeature;
if (ExecutionEnvironment.canUseDOM) {
  useHasFeature = document.implementation && document.implementation.hasFeature &&
  // always returns true in newer browsers as per the standard.
  // @see http://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
  document.implementation.hasFeature('', '') !== true;
}

/**
 * Checks if an event is supported in the current execution environment.
 *
 * NOTE: This will not work correctly for non-generic events such as `change`,
 * `reset`, `load`, `error`, and `select`.
 *
 * Borrows from Modernizr.
 *
 * @param {string} eventNameSuffix Event name, e.g. "click".
 * @param {?boolean} capture Check if the capture phase is supported.
 * @return {boolean} True if the event is supported.
 * @internal
 * @license Modernizr 3.0.0pre (Custom Build) | MIT
 */
function isEventSupported(eventNameSuffix, capture) {
  if (!ExecutionEnvironment.canUseDOM || capture && !('addEventListener' in document)) {
    return false;
  }

  var eventName = 'on' + eventNameSuffix;
  var isSupported = eventName in document;

  if (!isSupported) {
    var element = document.createElement('div');
    element.setAttribute(eventName, 'return;');
    isSupported = typeof element[eventName] === 'function';
  }

  if (!isSupported && useHasFeature && eventNameSuffix === 'wheel') {
    // This is the only way to test support for the `wheel` event in IE9+.
    isSupported = document.implementation.hasFeature('Events.wheel', '3.0');
  }

  return isSupported;
}

module.exports = isEventSupported;
},{"fbjs/lib/ExecutionEnvironment":190}],177:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule isTextInputElement
 * 
 */

'use strict';

/**
 * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
 */

var supportedInputTypes = {
  'color': true,
  'date': true,
  'datetime': true,
  'datetime-local': true,
  'email': true,
  'month': true,
  'number': true,
  'password': true,
  'range': true,
  'search': true,
  'tel': true,
  'text': true,
  'time': true,
  'url': true,
  'week': true
};

function isTextInputElement(elem) {
  var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();

  if (nodeName === 'input') {
    return !!supportedInputTypes[elem.type];
  }

  if (nodeName === 'textarea') {
    return true;
  }

  return false;
}

module.exports = isTextInputElement;
},{}],178:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule onlyChild
 */
'use strict';

var _prodInvariant = require('./reactProdInvariant');

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

var invariant = require('fbjs/lib/invariant');

/**
 * Returns the first child in a collection of children and verifies that there
 * is only one child in the collection.
 *
 * See https://facebook.github.io/react/docs/top-level-api.html#react.children.only
 *
 * The current implementation of this function assumes that a single child gets
 * passed without a wrapper, but the purpose of this helper function is to
 * abstract away the particular structure of children.
 *
 * @param {?object} children Child collection structure.
 * @return {ReactElement} The first and only `ReactElement` contained in the
 * structure.
 */
function onlyChild(children) {
  !ReactElement.isValidElement(children) ? "production" !== 'production' ? invariant(false, 'React.Children.only expected to receive a single React element child.') : _prodInvariant('143') : void 0;
  return children;
}

module.exports = onlyChild;
},{"./ReactElement":101,"./reactProdInvariant":180,"fbjs/lib/invariant":204}],179:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule quoteAttributeValueForBrowser
 */

'use strict';

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

/**
 * Escapes attribute value to prevent scripting attacks.
 *
 * @param {*} value Value to escape.
 * @return {string} An escaped string.
 */
function quoteAttributeValueForBrowser(value) {
  return '"' + escapeTextContentForBrowser(value) + '"';
}

module.exports = quoteAttributeValueForBrowser;
},{"./escapeTextContentForBrowser":162}],180:[function(require,module,exports){
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule reactProdInvariant
 * 
 */
'use strict';

/**
 * WARNING: DO NOT manually require this module.
 * This is a replacement for `invariant(...)` used by the error code system
 * and will _only_ be required by the corresponding babel pass.
 * It always throws.
 */

function reactProdInvariant(code) {
  var argCount = arguments.length - 1;

  var message = 'Minified React error #' + code + '; visit ' + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=' + code;

  for (var argIdx = 0; argIdx < argCount; argIdx++) {
    message += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
  }

  message += ' for the full message or use the non-minified dev environment' + ' for full errors and additional helpful warnings.';

  var error = new Error(message);
  error.name = 'Invariant Violation';
  error.framesToPop = 1; // we don't care about reactProdInvariant's own frame

  throw error;
}

module.exports = reactProdInvariant;
},{}],181:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
* @providesModule renderSubtreeIntoContainer
*/

'use strict';

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

module.exports = ReactMount.renderSubtreeIntoContainer;
},{"./ReactMount":116}],182:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule setInnerHTML
 */

'use strict';

var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
var DOMNamespaces = require('./DOMNamespaces');

var WHITESPACE_TEST = /^[ \r\n\t\f]/;
var NONVISIBLE_TEST = /<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/;

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

// SVG temp container for IE lacking innerHTML
var reusableSVGContainer;

/**
 * Set the innerHTML property of a node, ensuring that whitespace is preserved
 * even in IE8.
 *
 * @param {DOMElement} node
 * @param {string} html
 * @internal
 */
var setInnerHTML = createMicrosoftUnsafeLocalFunction(function (node, html) {
  // IE does not have innerHTML for SVG nodes, so instead we inject the
  // new markup in a temp node and then move the child nodes across into
  // the target node
  if (node.namespaceURI === DOMNamespaces.svg && !('innerHTML' in node)) {
    reusableSVGContainer = reusableSVGContainer || document.createElement('div');
    reusableSVGContainer.innerHTML = '<svg>' + html + '</svg>';
    var svgNode = reusableSVGContainer.firstChild;
    while (svgNode.firstChild) {
      node.appendChild(svgNode.firstChild);
    }
  } else {
    node.innerHTML = html;
  }
});

if (ExecutionEnvironment.canUseDOM) {
  // IE8: When updating a just created node with innerHTML only leading
  // whitespace is removed. When updating an existing node with innerHTML
  // whitespace in root TextNodes is also collapsed.
  // @see quirksmode.org/bugreports/archives/2004/11/innerhtml_and_t.html

  // Feature detection; only IE8 is known to behave improperly like this.
  var testElement = document.createElement('div');
  testElement.innerHTML = ' ';
  if (testElement.innerHTML === '') {
    setInnerHTML = function (node, html) {
      // Magic theory: IE8 supposedly differentiates between added and updated
      // nodes when processing innerHTML, innerHTML on updated nodes suffers
      // from worse whitespace behavior. Re-adding a node like this triggers
      // the initial and more favorable whitespace behavior.
      // TODO: What to do on a detached node?
      if (node.parentNode) {
        node.parentNode.replaceChild(node, node);
      }

      // We also implement a workaround for non-visible tags disappearing into
      // thin air on IE8, this only happens if there is no visible text
      // in-front of the non-visible tags. Piggyback on the whitespace fix
      // and simply check if any non-visible tags appear in the source.
      if (WHITESPACE_TEST.test(html) || html[0] === '<' && NONVISIBLE_TEST.test(html)) {
        // Recover leading whitespace by temporarily prepending any character.
        // \uFEFF has the potential advantage of being zero-width/invisible.
        // UglifyJS drops U+FEFF chars when parsing, so use String.fromCharCode
        // in hopes that this is preserved even if "\uFEFF" is transformed to
        // the actual Unicode character (by Babel, for example).
        // https://github.com/mishoo/UglifyJS2/blob/v2.4.20/lib/parse.js#L216
        node.innerHTML = String.fromCharCode(0xFEFF) + html;

        // deleteData leaves an empty `TextNode` which offsets the index of all
        // children. Definitely want to avoid this.
        var textNode = node.firstChild;
        if (textNode.data.length === 1) {
          node.removeChild(textNode);
        } else {
          textNode.deleteData(0, 1);
        }
      } else {
        node.innerHTML = html;
      }
    };
  }
  testElement = null;
}

module.exports = setInnerHTML;
},{"./DOMNamespaces":47,"./createMicrosoftUnsafeLocalFunction":160,"fbjs/lib/ExecutionEnvironment":190}],183:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule setTextContent
 */

'use strict';

var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
var escapeTextContentForBrowser = require('./escapeTextContentForBrowser');
var setInnerHTML = require('./setInnerHTML');

/**
 * Set the textContent property of a node, ensuring that whitespace is preserved
 * even in IE8. innerText is a poor substitute for textContent and, among many
 * issues, inserts <br> instead of the literal newline chars. innerHTML behaves
 * as it should.
 *
 * @param {DOMElement} node
 * @param {string} text
 * @internal
 */
var setTextContent = function (node, text) {
  if (text) {
    var firstChild = node.firstChild;

    if (firstChild && firstChild === node.lastChild && firstChild.nodeType === 3) {
      firstChild.nodeValue = text;
      return;
    }
  }
  node.textContent = text;
};

if (ExecutionEnvironment.canUseDOM) {
  if (!('textContent' in document.documentElement)) {
    setTextContent = function (node, text) {
      setInnerHTML(node, escapeTextContentForBrowser(text));
    };
  }
}

module.exports = setTextContent;
},{"./escapeTextContentForBrowser":162,"./setInnerHTML":182,"fbjs/lib/ExecutionEnvironment":190}],184:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
* @providesModule shallowCompare
*/

'use strict';

var shallowEqual = require('fbjs/lib/shallowEqual');

/**
 * Does a shallow comparison for props and state.
 * See ReactComponentWithPureRenderMixin
 * See also https://facebook.github.io/react/docs/shallow-compare.html
 */
function shallowCompare(instance, nextProps, nextState) {
  return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState);
}

module.exports = shallowCompare;
},{"fbjs/lib/shallowEqual":212}],185:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule shouldUpdateReactComponent
 */

'use strict';

/**
 * Given a `prevElement` and `nextElement`, determines if the existing
 * instance should be updated as opposed to being destroyed or replaced by a new
 * instance. Both arguments are elements. This ensures that this logic can
 * operate on stateless trees without any backing instance.
 *
 * @param {?object} prevElement
 * @param {?object} nextElement
 * @return {boolean} True if the existing instance should be updated.
 * @protected
 */

function shouldUpdateReactComponent(prevElement, nextElement) {
  var prevEmpty = prevElement === null || prevElement === false;
  var nextEmpty = nextElement === null || nextElement === false;
  if (prevEmpty || nextEmpty) {
    return prevEmpty === nextEmpty;
  }

  var prevType = typeof prevElement;
  var nextType = typeof nextElement;
  if (prevType === 'string' || prevType === 'number') {
    return nextType === 'string' || nextType === 'number';
  } else {
    return nextType === 'object' && prevElement.type === nextElement.type && prevElement.key === nextElement.key;
  }
}

module.exports = shouldUpdateReactComponent;
},{}],186:[function(require,module,exports){
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule traverseAllChildren
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var ReactCurrentOwner = require('./ReactCurrentOwner');
var ReactElement = require('./ReactElement');

var getIteratorFn = require('./getIteratorFn');
var invariant = require('fbjs/lib/invariant');
var KeyEscapeUtils = require('./KeyEscapeUtils');
var warning = require('fbjs/lib/warning');

var SEPARATOR = '.';
var SUBSEPARATOR = ':';

/**
 * TODO: Test that a single child and an array with one item have the same key
 * pattern.
 */

var didWarnAboutMaps = false;

/**
 * Generate a key string that identifies a component within a set.
 *
 * @param {*} component A component that could contain a manual key.
 * @param {number} index Index that is used if a manual key is not provided.
 * @return {string}
 */
function getComponentKey(component, index) {
  // Do some typechecking here since we call this blindly. We want to ensure
  // that we don't block potential future ES APIs.
  if (component && typeof component === 'object' && component.key != null) {
    // Explicit key
    return KeyEscapeUtils.escape(component.key);
  }
  // Implicit key determined by the index in the set
  return index.toString(36);
}

/**
 * @param {?*} children Children tree container.
 * @param {!string} nameSoFar Name of the key path so far.
 * @param {!function} callback Callback to invoke with each child found.
 * @param {?*} traverseContext Used to pass information throughout the traversal
 * process.
 * @return {!number} The number of children in this subtree.
 */
function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext) {
  var type = typeof children;

  if (type === 'undefined' || type === 'boolean') {
    // All of the above are perceived as null.
    children = null;
  }

  if (children === null || type === 'string' || type === 'number' || ReactElement.isValidElement(children)) {
    callback(traverseContext, children,
    // If it's the only child, treat the name as if it was wrapped in an array
    // so that it's consistent if the number of children grows.
    nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar);
    return 1;
  }

  var child;
  var nextName;
  var subtreeCount = 0; // Count of children found in the current subtree.
  var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;

  if (Array.isArray(children)) {
    for (var i = 0; i < children.length; i++) {
      child = children[i];
      nextName = nextNamePrefix + getComponentKey(child, i);
      subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
    }
  } else {
    var iteratorFn = getIteratorFn(children);
    if (iteratorFn) {
      var iterator = iteratorFn.call(children);
      var step;
      if (iteratorFn !== children.entries) {
        var ii = 0;
        while (!(step = iterator.next()).done) {
          child = step.value;
          nextName = nextNamePrefix + getComponentKey(child, ii++);
          subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
        }
      } else {
        if ("production" !== 'production') {
          var mapsAsChildrenAddendum = '';
          if (ReactCurrentOwner.current) {
            var mapsAsChildrenOwnerName = ReactCurrentOwner.current.getName();
            if (mapsAsChildrenOwnerName) {
              mapsAsChildrenAddendum = ' Check the render method of `' + mapsAsChildrenOwnerName + '`.';
            }
          }
          "production" !== 'production' ? warning(didWarnAboutMaps, 'Using Maps as children is not yet fully supported. It is an ' + 'experimental feature that might be removed. Convert it to a ' + 'sequence / iterable of keyed ReactElements instead.%s', mapsAsChildrenAddendum) : void 0;
          didWarnAboutMaps = true;
        }
        // Iterator will provide entry [k,v] tuples rather than values.
        while (!(step = iterator.next()).done) {
          var entry = step.value;
          if (entry) {
            child = entry[1];
            nextName = nextNamePrefix + KeyEscapeUtils.escape(entry[0]) + SUBSEPARATOR + getComponentKey(child, 0);
            subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
          }
        }
      }
    } else if (type === 'object') {
      var addendum = '';
      if ("production" !== 'production') {
        addendum = ' If you meant to render a collection of children, use an array ' + 'instead or wrap the object using createFragment(object) from the ' + 'React add-ons.';
        if (children._isReactElement) {
          addendum = ' It looks like you\'re using an element created by a different ' + 'version of React. Make sure to use only one copy of React.';
        }
        if (ReactCurrentOwner.current) {
          var name = ReactCurrentOwner.current.getName();
          if (name) {
            addendum += ' Check the render method of `' + name + '`.';
          }
        }
      }
      var childrenString = String(children);
      !false ? "production" !== 'production' ? invariant(false, 'Objects are not valid as a React child (found: %s).%s', childrenString === '[object Object]' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString, addendum) : _prodInvariant('31', childrenString === '[object Object]' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString, addendum) : void 0;
    }
  }

  return subtreeCount;
}

/**
 * Traverses children that are typically specified as `props.children`, but
 * might also be specified through attributes:
 *
 * - `traverseAllChildren(this.props.children, ...)`
 * - `traverseAllChildren(this.props.leftPanelChildren, ...)`
 *
 * The `traverseContext` is an optional argument that is passed through the
 * entire traversal. It can be used to store accumulations or anything else that
 * the callback might find relevant.
 *
 * @param {?*} children Children tree object.
 * @param {!function} callback To invoke upon traversing each child.
 * @param {?*} traverseContext Context for traversal.
 * @return {!number} The number of children in this subtree.
 */
function traverseAllChildren(children, callback, traverseContext) {
  if (children == null) {
    return 0;
  }

  return traverseAllChildrenImpl(children, '', callback, traverseContext);
}

module.exports = traverseAllChildren;
},{"./KeyEscapeUtils":61,"./ReactCurrentOwner":78,"./ReactElement":101,"./getIteratorFn":171,"./reactProdInvariant":180,"fbjs/lib/invariant":204,"fbjs/lib/warning":213}],187:[function(require,module,exports){
/**
 * Copyright 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule validateDOMNesting
 */

'use strict';

var _assign = require('object-assign');

var emptyFunction = require('fbjs/lib/emptyFunction');
var warning = require('fbjs/lib/warning');

var validateDOMNesting = emptyFunction;

if ("production" !== 'production') {
  // This validation code was written based on the HTML5 parsing spec:
  // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
  //
  // Note: this does not catch all invalid nesting, nor does it try to (as it's
  // not clear what practical benefit doing so provides); instead, we warn only
  // for cases where the parser will give a parse tree differing from what React
  // intended. For example, <b><div></div></b> is invalid but we don't warn
  // because it still parses correctly; we do warn for other cases like nested
  // <p> tags where the beginning of the second element implicitly closes the
  // first, causing a confusing mess.

  // https://html.spec.whatwg.org/multipage/syntax.html#special
  var specialTags = ['address', 'applet', 'area', 'article', 'aside', 'base', 'basefont', 'bgsound', 'blockquote', 'body', 'br', 'button', 'caption', 'center', 'col', 'colgroup', 'dd', 'details', 'dir', 'div', 'dl', 'dt', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'iframe', 'img', 'input', 'isindex', 'li', 'link', 'listing', 'main', 'marquee', 'menu', 'menuitem', 'meta', 'nav', 'noembed', 'noframes', 'noscript', 'object', 'ol', 'p', 'param', 'plaintext', 'pre', 'script', 'section', 'select', 'source', 'style', 'summary', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'title', 'tr', 'track', 'ul', 'wbr', 'xmp'];

  // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
  var inScopeTags = ['applet', 'caption', 'html', 'table', 'td', 'th', 'marquee', 'object', 'template',

  // https://html.spec.whatwg.org/multipage/syntax.html#html-integration-point
  // TODO: Distinguish by namespace here -- for <title>, including it here
  // errs on the side of fewer warnings
  'foreignObject', 'desc', 'title'];

  // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-button-scope
  var buttonScopeTags = inScopeTags.concat(['button']);

  // https://html.spec.whatwg.org/multipage/syntax.html#generate-implied-end-tags
  var impliedEndTags = ['dd', 'dt', 'li', 'option', 'optgroup', 'p', 'rp', 'rt'];

  var emptyAncestorInfo = {
    current: null,

    formTag: null,
    aTagInScope: null,
    buttonTagInScope: null,
    nobrTagInScope: null,
    pTagInButtonScope: null,

    listItemTagAutoclosing: null,
    dlItemTagAutoclosing: null
  };

  var updatedAncestorInfo = function (oldInfo, tag, instance) {
    var ancestorInfo = _assign({}, oldInfo || emptyAncestorInfo);
    var info = { tag: tag, instance: instance };

    if (inScopeTags.indexOf(tag) !== -1) {
      ancestorInfo.aTagInScope = null;
      ancestorInfo.buttonTagInScope = null;
      ancestorInfo.nobrTagInScope = null;
    }
    if (buttonScopeTags.indexOf(tag) !== -1) {
      ancestorInfo.pTagInButtonScope = null;
    }

    // See rules for 'li', 'dd', 'dt' start tags in
    // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
    if (specialTags.indexOf(tag) !== -1 && tag !== 'address' && tag !== 'div' && tag !== 'p') {
      ancestorInfo.listItemTagAutoclosing = null;
      ancestorInfo.dlItemTagAutoclosing = null;
    }

    ancestorInfo.current = info;

    if (tag === 'form') {
      ancestorInfo.formTag = info;
    }
    if (tag === 'a') {
      ancestorInfo.aTagInScope = info;
    }
    if (tag === 'button') {
      ancestorInfo.buttonTagInScope = info;
    }
    if (tag === 'nobr') {
      ancestorInfo.nobrTagInScope = info;
    }
    if (tag === 'p') {
      ancestorInfo.pTagInButtonScope = info;
    }
    if (tag === 'li') {
      ancestorInfo.listItemTagAutoclosing = info;
    }
    if (tag === 'dd' || tag === 'dt') {
      ancestorInfo.dlItemTagAutoclosing = info;
    }

    return ancestorInfo;
  };

  /**
   * Returns whether
   */
  var isTagValidWithParent = function (tag, parentTag) {
    // First, let's check if we're in an unusual parsing mode...
    switch (parentTag) {
      // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inselect
      case 'select':
        return tag === 'option' || tag === 'optgroup' || tag === '#text';
      case 'optgroup':
        return tag === 'option' || tag === '#text';
      // Strictly speaking, seeing an <option> doesn't mean we're in a <select>
      // but
      case 'option':
        return tag === '#text';

      // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intd
      // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incaption
      // No special behavior since these rules fall back to "in body" mode for
      // all except special table nodes which cause bad parsing behavior anyway.

      // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intr
      case 'tr':
        return tag === 'th' || tag === 'td' || tag === 'style' || tag === 'script' || tag === 'template';

      // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intbody
      case 'tbody':
      case 'thead':
      case 'tfoot':
        return tag === 'tr' || tag === 'style' || tag === 'script' || tag === 'template';

      // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incolgroup
      case 'colgroup':
        return tag === 'col' || tag === 'template';

      // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intable
      case 'table':
        return tag === 'caption' || tag === 'colgroup' || tag === 'tbody' || tag === 'tfoot' || tag === 'thead' || tag === 'style' || tag === 'script' || tag === 'template';

      // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inhead
      case 'head':
        return tag === 'base' || tag === 'basefont' || tag === 'bgsound' || tag === 'link' || tag === 'meta' || tag === 'title' || tag === 'noscript' || tag === 'noframes' || tag === 'style' || tag === 'script' || tag === 'template';

      // https://html.spec.whatwg.org/multipage/semantics.html#the-html-element
      case 'html':
        return tag === 'head' || tag === 'body';
      case '#document':
        return tag === 'html';
    }

    // Probably in the "in body" parsing mode, so we outlaw only tag combos
    // where the parsing rules cause implicit opens or closes to be added.
    // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
    switch (tag) {
      case 'h1':
      case 'h2':
      case 'h3':
      case 'h4':
      case 'h5':
      case 'h6':
        return parentTag !== 'h1' && parentTag !== 'h2' && parentTag !== 'h3' && parentTag !== 'h4' && parentTag !== 'h5' && parentTag !== 'h6';

      case 'rp':
      case 'rt':
        return impliedEndTags.indexOf(parentTag) === -1;

      case 'body':
      case 'caption':
      case 'col':
      case 'colgroup':
      case 'frame':
      case 'head':
      case 'html':
      case 'tbody':
      case 'td':
      case 'tfoot':
      case 'th':
      case 'thead':
      case 'tr':
        // These tags are only valid with a few parents that have special child
        // parsing rules -- if we're down here, then none of those matched and
        // so we allow it only if we don't know what the parent is, as all other
        // cases are invalid.
        return parentTag == null;
    }

    return true;
  };

  /**
   * Returns whether
   */
  var findInvalidAncestorForTag = function (tag, ancestorInfo) {
    switch (tag) {
      case 'address':
      case 'article':
      case 'aside':
      case 'blockquote':
      case 'center':
      case 'details':
      case 'dialog':
      case 'dir':
      case 'div':
      case 'dl':
      case 'fieldset':
      case 'figcaption':
      case 'figure':
      case 'footer':
      case 'header':
      case 'hgroup':
      case 'main':
      case 'menu':
      case 'nav':
      case 'ol':
      case 'p':
      case 'section':
      case 'summary':
      case 'ul':

      case 'pre':
      case 'listing':

      case 'table':

      case 'hr':

      case 'xmp':

      case 'h1':
      case 'h2':
      case 'h3':
      case 'h4':
      case 'h5':
      case 'h6':
        return ancestorInfo.pTagInButtonScope;

      case 'form':
        return ancestorInfo.formTag || ancestorInfo.pTagInButtonScope;

      case 'li':
        return ancestorInfo.listItemTagAutoclosing;

      case 'dd':
      case 'dt':
        return ancestorInfo.dlItemTagAutoclosing;

      case 'button':
        return ancestorInfo.buttonTagInScope;

      case 'a':
        // Spec says something about storing a list of markers, but it sounds
        // equivalent to this check.
        return ancestorInfo.aTagInScope;

      case 'nobr':
        return ancestorInfo.nobrTagInScope;
    }

    return null;
  };

  /**
   * Given a ReactCompositeComponent instance, return a list of its recursive
   * owners, starting at the root and ending with the instance itself.
   */
  var findOwnerStack = function (instance) {
    if (!instance) {
      return [];
    }

    var stack = [];
    do {
      stack.push(instance);
    } while (instance = instance._currentElement._owner);
    stack.reverse();
    return stack;
  };

  var didWarn = {};

  validateDOMNesting = function (childTag, childText, childInstance, ancestorInfo) {
    ancestorInfo = ancestorInfo || emptyAncestorInfo;
    var parentInfo = ancestorInfo.current;
    var parentTag = parentInfo && parentInfo.tag;

    if (childText != null) {
      "production" !== 'production' ? warning(childTag == null, 'validateDOMNesting: when childText is passed, childTag should be null') : void 0;
      childTag = '#text';
    }

    var invalidParent = isTagValidWithParent(childTag, parentTag) ? null : parentInfo;
    var invalidAncestor = invalidParent ? null : findInvalidAncestorForTag(childTag, ancestorInfo);
    var problematic = invalidParent || invalidAncestor;

    if (problematic) {
      var ancestorTag = problematic.tag;
      var ancestorInstance = problematic.instance;

      var childOwner = childInstance && childInstance._currentElement._owner;
      var ancestorOwner = ancestorInstance && ancestorInstance._currentElement._owner;

      var childOwners = findOwnerStack(childOwner);
      var ancestorOwners = findOwnerStack(ancestorOwner);

      var minStackLen = Math.min(childOwners.length, ancestorOwners.length);
      var i;

      var deepestCommon = -1;
      for (i = 0; i < minStackLen; i++) {
        if (childOwners[i] === ancestorOwners[i]) {
          deepestCommon = i;
        } else {
          break;
        }
      }

      var UNKNOWN = '(unknown)';
      var childOwnerNames = childOwners.slice(deepestCommon + 1).map(function (inst) {
        return inst.getName() || UNKNOWN;
      });
      var ancestorOwnerNames = ancestorOwners.slice(deepestCommon + 1).map(function (inst) {
        return inst.getName() || UNKNOWN;
      });
      var ownerInfo = [].concat(
      // If the parent and child instances have a common owner ancestor, start
      // with that -- otherwise we just start with the parent's owners.
      deepestCommon !== -1 ? childOwners[deepestCommon].getName() || UNKNOWN : [], ancestorOwnerNames, ancestorTag,
      // If we're warning about an invalid (non-parent) ancestry, add '...'
      invalidAncestor ? ['...'] : [], childOwnerNames, childTag).join(' > ');

      var warnKey = !!invalidParent + '|' + childTag + '|' + ancestorTag + '|' + ownerInfo;
      if (didWarn[warnKey]) {
        return;
      }
      didWarn[warnKey] = true;

      var tagDisplayName = childTag;
      var whitespaceInfo = '';
      if (childTag === '#text') {
        if (/\S/.test(childText)) {
          tagDisplayName = 'Text nodes';
        } else {
          tagDisplayName = 'Whitespace text nodes';
          whitespaceInfo = ' Make sure you don\'t have any extra whitespace between tags on ' + 'each line of your source code.';
        }
      } else {
        tagDisplayName = '<' + childTag + '>';
      }

      if (invalidParent) {
        var info = '';
        if (ancestorTag === 'table' && childTag === 'tr') {
          info += ' Add a <tbody> to your code to match the DOM tree generated by ' + 'the browser.';
        }
        "production" !== 'production' ? warning(false, 'validateDOMNesting(...): %s cannot appear as a child of <%s>.%s ' + 'See %s.%s', tagDisplayName, ancestorTag, whitespaceInfo, ownerInfo, info) : void 0;
      } else {
        "production" !== 'production' ? warning(false, 'validateDOMNesting(...): %s cannot appear as a descendant of ' + '<%s>. See %s.', tagDisplayName, ancestorTag, ownerInfo) : void 0;
      }
    }
  };

  validateDOMNesting.updatedAncestorInfo = updatedAncestorInfo;

  // For testing
  validateDOMNesting.isTagValidInContext = function (tag, ancestorInfo) {
    ancestorInfo = ancestorInfo || emptyAncestorInfo;
    var parentInfo = ancestorInfo.current;
    var parentTag = parentInfo && parentInfo.tag;
    return isTagValidWithParent(tag, parentTag) && !findInvalidAncestorForTag(tag, ancestorInfo);
  };
}

module.exports = validateDOMNesting;
},{"fbjs/lib/emptyFunction":196,"fbjs/lib/warning":213,"object-assign":214}],188:[function(require,module,exports){
'use strict';

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

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

/**
 * The CSSCore module specifies the API (and implements most of the methods)
 * that should be used when dealing with the display of elements (via their
 * CSS classes and visibility on screen. It is an API focused on mutating the
 * display and not reading it as no logical state should be encoded in the
 * display of elements.
 */

/* Slow implementation for browsers that don't natively support .matches() */
function matchesSelector_SLOW(element, selector) {
  var root = element;
  while (root.parentNode) {
    root = root.parentNode;
  }

  var all = root.querySelectorAll(selector);
  return Array.prototype.indexOf.call(all, element) !== -1;
}

var CSSCore = {

  /**
   * Adds the class passed in to the element if it doesn't already have it.
   *
   * @param {DOMElement} element the element to set the class on
   * @param {string} className the CSS className
   * @return {DOMElement} the element passed in
   */
  addClass: function addClass(element, className) {
    !!/\s/.test(className) ? "production" !== 'production' ? invariant(false, 'CSSCore.addClass takes only a single class name. "%s" contains ' + 'multiple classes.', className) : invariant(false) : void 0;

    if (className) {
      if (element.classList) {
        element.classList.add(className);
      } else if (!CSSCore.hasClass(element, className)) {
        element.className = element.className + ' ' + className;
      }
    }
    return element;
  },

  /**
   * Removes the class passed in from the element
   *
   * @param {DOMElement} element the element to set the class on
   * @param {string} className the CSS className
   * @return {DOMElement} the element passed in
   */
  removeClass: function removeClass(element, className) {
    !!/\s/.test(className) ? "production" !== 'production' ? invariant(false, 'CSSCore.removeClass takes only a single class name. "%s" contains ' + 'multiple classes.', className) : invariant(false) : void 0;

    if (className) {
      if (element.classList) {
        element.classList.remove(className);
      } else if (CSSCore.hasClass(element, className)) {
        element.className = element.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1').replace(/\s+/g, ' ') // multiple spaces to one
        .replace(/^\s*|\s*$/g, ''); // trim the ends
      }
    }
    return element;
  },

  /**
   * Helper to add or remove a class from an element based on a condition.
   *
   * @param {DOMElement} element the element to set the class on
   * @param {string} className the CSS className
   * @param {*} bool condition to whether to add or remove the class
   * @return {DOMElement} the element passed in
   */
  conditionClass: function conditionClass(element, className, bool) {
    return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className);
  },

  /**
   * Tests whether the element has the class specified.
   *
   * @param {DOMNode|DOMWindow} element the element to check the class on
   * @param {string} className the CSS className
   * @return {boolean} true if the element has the class, false if not
   */
  hasClass: function hasClass(element, className) {
    !!/\s/.test(className) ? "production" !== 'production' ? invariant(false, 'CSS.hasClass takes only a single class name.') : invariant(false) : void 0;
    if (element.classList) {
      return !!className && element.classList.contains(className);
    }
    return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1;
  },

  /**
   * Tests whether the element matches the selector specified
   *
   * @param {DOMNode|DOMWindow} element the element that we are querying
   * @param {string} selector the CSS selector
   * @return {boolean} true if the element matches the selector, false if not
   */
  matchesSelector: function matchesSelector(element, selector) {
    var matchesImpl = element.matches || element.webkitMatchesSelector || element.mozMatchesSelector || element.msMatchesSelector || function (s) {
      return matchesSelector_SLOW(element, s);
    };
    return matchesImpl.call(element, selector);
  }

};

module.exports = CSSCore;
},{"./invariant":204}],189:[function(require,module,exports){
'use strict';

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @typechecks
 */

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

/**
 * Upstream version of event listener. Does not take into account specific
 * nature of platform.
 */
var EventListener = {
  /**
   * Listen to DOM events during the bubble phase.
   *
   * @param {DOMEventTarget} target DOM element to register listener on.
   * @param {string} eventType Event type, e.g. 'click' or 'mouseover'.
   * @param {function} callback Callback function.
   * @return {object} Object with a `remove` method.
   */
  listen: function listen(target, eventType, callback) {
    if (target.addEventListener) {
      target.addEventListener(eventType, callback, false);
      return {
        remove: function remove() {
          target.removeEventListener(eventType, callback, false);
        }
      };
    } else if (target.attachEvent) {
      target.attachEvent('on' + eventType, callback);
      return {
        remove: function remove() {
          target.detachEvent('on' + eventType, callback);
        }
      };
    }
  },

  /**
   * Listen to DOM events during the capture phase.
   *
   * @param {DOMEventTarget} target DOM element to register listener on.
   * @param {string} eventType Event type, e.g. 'click' or 'mouseover'.
   * @param {function} callback Callback function.
   * @return {object} Object with a `remove` method.
   */
  capture: function capture(target, eventType, callback) {
    if (target.addEventListener) {
      target.addEventListener(eventType, callback, true);
      return {
        remove: function remove() {
          target.removeEventListener(eventType, callback, true);
        }
      };
    } else {
      if ("production" !== 'production') {
        console.error('Attempted to listen to events during the capture phase on a ' + 'browser that does not support the capture phase. Your application ' + 'will not receive some events.');
      }
      return {
        remove: emptyFunction
      };
    }
  },

  registerDefault: function registerDefault() {}
};

module.exports = EventListener;
},{"./emptyFunction":196}],190:[function(require,module,exports){
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 */

'use strict';

var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);

/**
 * Simple, lightweight module assisting with the detection and context of
 * Worker. Helps avoid circular dependencies and allows code to reason about
 * whether or not they are in a Worker, even if they never include the main
 * `ReactWorker` dependency.
 */
var ExecutionEnvironment = {

  canUseDOM: canUseDOM,

  canUseWorkers: typeof Worker !== 'undefined',

  canUseEventListeners: canUseDOM && !!(window.addEventListener || window.attachEvent),

  canUseViewport: canUseDOM && !!window.screen,

  isInWorker: !canUseDOM // For now, this is true - might change in the future.

};

module.exports = ExecutionEnvironment;
},{}],191:[function(require,module,exports){
"use strict";

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

var _hyphenPattern = /-(.)/g;

/**
 * Camelcases a hyphenated string, for example:
 *
 *   > camelize('background-color')
 *   < "backgroundColor"
 *
 * @param {string} string
 * @return {string}
 */
function camelize(string) {
  return string.replace(_hyphenPattern, function (_, character) {
    return character.toUpperCase();
  });
}

module.exports = camelize;
},{}],192:[function(require,module,exports){
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

'use strict';

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

var msPattern = /^-ms-/;

/**
 * Camelcases a hyphenated CSS property name, for example:
 *
 *   > camelizeStyleName('background-color')
 *   < "backgroundColor"
 *   > camelizeStyleName('-moz-transition')
 *   < "MozTransition"
 *   > camelizeStyleName('-ms-transition')
 *   < "msTransition"
 *
 * As Andi Smith suggests
 * (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix
 * is converted to lowercase `ms`.
 *
 * @param {string} string
 * @return {string}
 */
function camelizeStyleName(string) {
  return camelize(string.replace(msPattern, 'ms-'));
}

module.exports = camelizeStyleName;
},{"./camelize":191}],193:[function(require,module,exports){
'use strict';

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * 
 */

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

/*eslint-disable no-bitwise */

/**
 * Checks if a given DOM node contains or is another DOM node.
 */
function containsNode(outerNode, innerNode) {
  if (!outerNode || !innerNode) {
    return false;
  } else if (outerNode === innerNode) {
    return true;
  } else if (isTextNode(outerNode)) {
    return false;
  } else if (isTextNode(innerNode)) {
    return containsNode(outerNode, innerNode.parentNode);
  } else if ('contains' in outerNode) {
    return outerNode.contains(innerNode);
  } else if (outerNode.compareDocumentPosition) {
    return !!(outerNode.compareDocumentPosition(innerNode) & 16);
  } else {
    return false;
  }
}

module.exports = containsNode;
},{"./isTextNode":206}],194:[function(require,module,exports){
'use strict';

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

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

/**
 * Convert array-like objects to arrays.
 *
 * This API assumes the caller knows the contents of the data type. For less
 * well defined inputs use createArrayFromMixed.
 *
 * @param {object|function|filelist} obj
 * @return {array}
 */
function toArray(obj) {
  var length = obj.length;

  // Some browsers builtin objects can report typeof 'function' (e.g. NodeList
  // in old versions of Safari).
  !(!Array.isArray(obj) && (typeof obj === 'object' || typeof obj === 'function')) ? "production" !== 'production' ? invariant(false, 'toArray: Array-like object expected') : invariant(false) : void 0;

  !(typeof length === 'number') ? "production" !== 'production' ? invariant(false, 'toArray: Object needs a length property') : invariant(false) : void 0;

  !(length === 0 || length - 1 in obj) ? "production" !== 'production' ? invariant(false, 'toArray: Object should have keys for indices') : invariant(false) : void 0;

  !(typeof obj.callee !== 'function') ? "production" !== 'production' ? invariant(false, 'toArray: Object can\'t be `arguments`. Use rest params ' + '(function(...args) {}) or Array.from() instead.') : invariant(false) : void 0;

  // Old IE doesn't give collections access to hasOwnProperty. Assume inputs
  // without method will throw during the slice call and skip straight to the
  // fallback.
  if (obj.hasOwnProperty) {
    try {
      return Array.prototype.slice.call(obj);
    } catch (e) {
      // IE < 9 does not support Array#slice on collections objects
    }
  }

  // Fall back to copying key by key. This assumes all keys have a value,
  // so will not preserve sparsely populated inputs.
  var ret = Array(length);
  for (var ii = 0; ii < length; ii++) {
    ret[ii] = obj[ii];
  }
  return ret;
}

/**
 * Perform a heuristic test to determine if an object is "array-like".
 *
 *   A monk asked Joshu, a Zen master, "Has a dog Buddha nature?"
 *   Joshu replied: "Mu."
 *
 * This function determines if its argument has "array nature": it returns
 * true if the argument is an actual array, an `arguments' object, or an
 * HTMLCollection (e.g. node.childNodes or node.getElementsByTagName()).
 *
 * It will return false for other array-like objects like Filelist.
 *
 * @param {*} obj
 * @return {boolean}
 */
function hasArrayNature(obj) {
  return (
    // not null/false
    !!obj && (
    // arrays are objects, NodeLists are functions in Safari
    typeof obj == 'object' || typeof obj == 'function') &&
    // quacks like an array
    'length' in obj &&
    // not window
    !('setInterval' in obj) &&
    // no DOM node should be considered an array-like
    // a 'select' element has 'length' and 'item' properties on IE8
    typeof obj.nodeType != 'number' && (
    // a real array
    Array.isArray(obj) ||
    // arguments
    'callee' in obj ||
    // HTMLCollection/NodeList
    'item' in obj)
  );
}

/**
 * Ensure that the argument is an array by wrapping it in an array if it is not.
 * Creates a copy of the argument if it is already an array.
 *
 * This is mostly useful idiomatically:
 *
 *   var createArrayFromMixed = require('createArrayFromMixed');
 *
 *   function takesOneOrMoreThings(things) {
 *     things = createArrayFromMixed(things);
 *     ...
 *   }
 *
 * This allows you to treat `things' as an array, but accept scalars in the API.
 *
 * If you need to convert an array-like object, like `arguments`, into an array
 * use toArray instead.
 *
 * @param {*} obj
 * @return {array}
 */
function createArrayFromMixed(obj) {
  if (!hasArrayNature(obj)) {
    return [obj];
  } else if (Array.isArray(obj)) {
    return obj.slice();
  } else {
    return toArray(obj);
  }
}

module.exports = createArrayFromMixed;
},{"./invariant":204}],195:[function(require,module,exports){
'use strict';

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

/*eslint-disable fb-www/unsafe-html*/

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

var createArrayFromMixed = require('./createArrayFromMixed');
var getMarkupWrap = require('./getMarkupWrap');
var invariant = require('./invariant');

/**
 * Dummy container used to render all markup.
 */
var dummyNode = ExecutionEnvironment.canUseDOM ? document.createElement('div') : null;

/**
 * Pattern used by `getNodeName`.
 */
var nodeNamePattern = /^\s*<(\w+)/;

/**
 * Extracts the `nodeName` of the first element in a string of markup.
 *
 * @param {string} markup String of markup.
 * @return {?string} Node name of the supplied markup.
 */
function getNodeName(markup) {
  var nodeNameMatch = markup.match(nodeNamePattern);
  return nodeNameMatch && nodeNameMatch[1].toLowerCase();
}

/**
 * Creates an array containing the nodes rendered from the supplied markup. The
 * optionally supplied `handleScript` function will be invoked once for each
 * <script> element that is rendered. If no `handleScript` function is supplied,
 * an exception is thrown if any <script> elements are rendered.
 *
 * @param {string} markup A string of valid HTML markup.
 * @param {?function} handleScript Invoked once for each rendered <script>.
 * @return {array<DOMElement|DOMTextNode>} An array of rendered nodes.
 */
function createNodesFromMarkup(markup, handleScript) {
  var node = dummyNode;
  !!!dummyNode ? "production" !== 'production' ? invariant(false, 'createNodesFromMarkup dummy not initialized') : invariant(false) : void 0;
  var nodeName = getNodeName(markup);

  var wrap = nodeName && getMarkupWrap(nodeName);
  if (wrap) {
    node.innerHTML = wrap[1] + markup + wrap[2];

    var wrapDepth = wrap[0];
    while (wrapDepth--) {
      node = node.lastChild;
    }
  } else {
    node.innerHTML = markup;
  }

  var scripts = node.getElementsByTagName('script');
  if (scripts.length) {
    !handleScript ? "production" !== 'production' ? invariant(false, 'createNodesFromMarkup(...): Unexpected <script> element rendered.') : invariant(false) : void 0;
    createArrayFromMixed(scripts).forEach(handleScript);
  }

  var nodes = Array.from(node.childNodes);
  while (node.lastChild) {
    node.removeChild(node.lastChild);
  }
  return nodes;
}

module.exports = createNodesFromMarkup;
},{"./ExecutionEnvironment":190,"./createArrayFromMixed":194,"./getMarkupWrap":200,"./invariant":204}],196:[function(require,module,exports){
"use strict";

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * 
 */

function makeEmptyFunction(arg) {
  return function () {
    return arg;
  };
}

/**
 * This function accepts and discards inputs; it has no side effects. This is
 * primarily useful idiomatically for overridable function endpoints which
 * always need to be callable, since JS lacks a null-call idiom ala Cocoa.
 */
var emptyFunction = function emptyFunction() {};

emptyFunction.thatReturns = makeEmptyFunction;
emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
emptyFunction.thatReturnsTrue = makeEmptyFunction(true);
emptyFunction.thatReturnsNull = makeEmptyFunction(null);
emptyFunction.thatReturnsThis = function () {
  return this;
};
emptyFunction.thatReturnsArgument = function (arg) {
  return arg;
};

module.exports = emptyFunction;
},{}],197:[function(require,module,exports){
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 */

'use strict';

var emptyObject = {};

if ("production" !== 'production') {
  Object.freeze(emptyObject);
}

module.exports = emptyObject;
},{}],198:[function(require,module,exports){
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 */

'use strict';

/**
 * @param {DOMElement} node input/textarea to focus
 */

function focusNode(node) {
  // IE8 can throw "Can't move focus to the control because it is invisible,
  // not enabled, or of a type that does not accept the focus." for all kinds of
  // reasons that are too expensive and fragile to test.
  try {
    node.focus();
  } catch (e) {}
}

module.exports = focusNode;
},{}],199:[function(require,module,exports){
'use strict';

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

/* eslint-disable fb-www/typeof-undefined */

/**
 * Same as document.activeElement but wraps in a try-catch block. In IE it is
 * not safe to call document.activeElement if there is nothing focused.
 *
 * The activeElement will be null only if the document or document body is not
 * yet defined.
 */
function getActiveElement() /*?DOMElement*/{
  if (typeof document === 'undefined') {
    return null;
  }
  try {
    return document.activeElement || document.body;
  } catch (e) {
    return document.body;
  }
}

module.exports = getActiveElement;
},{}],200:[function(require,module,exports){
'use strict';

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 */

/*eslint-disable fb-www/unsafe-html */

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

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

/**
 * Dummy container used to detect which wraps are necessary.
 */
var dummyNode = ExecutionEnvironment.canUseDOM ? document.createElement('div') : null;

/**
 * Some browsers cannot use `innerHTML` to render certain elements standalone,
 * so we wrap them, render the wrapped nodes, then extract the desired node.
 *
 * In IE8, certain elements cannot render alone, so wrap all elements ('*').
 */

var shouldWrap = {};

var selectWrap = [1, '<select multiple="true">', '</select>'];
var tableWrap = [1, '<table>', '</table>'];
var trWrap = [3, '<table><tbody><tr>', '</tr></tbody></table>'];

var svgWrap = [1, '<svg xmlns="http://www.w3.org/2000/svg">', '</svg>'];

var markupWrap = {
  '*': [1, '?<div>', '</div>'],

  'area': [1, '<map>', '</map>'],
  'col': [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
  'legend': [1, '<fieldset>', '</fieldset>'],
  'param': [1, '<object>', '</object>'],
  'tr': [2, '<table><tbody>', '</tbody></table>'],

  'optgroup': selectWrap,
  'option': selectWrap,

  'caption': tableWrap,
  'colgroup': tableWrap,
  'tbody': tableWrap,
  'tfoot': tableWrap,
  'thead': tableWrap,

  'td': trWrap,
  'th': trWrap
};

// Initialize the SVG elements since we know they'll always need to be wrapped
// consistently. If they are created inside a <div> they will be initialized in
// the wrong namespace (and will not display).
var svgElements = ['circle', 'clipPath', 'defs', 'ellipse', 'g', 'image', 'line', 'linearGradient', 'mask', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'text', 'tspan'];
svgElements.forEach(function (nodeName) {
  markupWrap[nodeName] = svgWrap;
  shouldWrap[nodeName] = true;
});

/**
 * Gets the markup wrap configuration for the supplied `nodeName`.
 *
 * NOTE: This lazily detects which wraps are necessary for the current browser.
 *
 * @param {string} nodeName Lowercase `nodeName`.
 * @return {?array} Markup wrap configuration, if applicable.
 */
function getMarkupWrap(nodeName) {
  !!!dummyNode ? "production" !== 'production' ? invariant(false, 'Markup wrapping node not initialized') : invariant(false) : void 0;
  if (!markupWrap.hasOwnProperty(nodeName)) {
    nodeName = '*';
  }
  if (!shouldWrap.hasOwnProperty(nodeName)) {
    if (nodeName === '*') {
      dummyNode.innerHTML = '<link />';
    } else {
      dummyNode.innerHTML = '<' + nodeName + '></' + nodeName + '>';
    }
    shouldWrap[nodeName] = !dummyNode.firstChild;
  }
  return shouldWrap[nodeName] ? markupWrap[nodeName] : null;
}

module.exports = getMarkupWrap;
},{"./ExecutionEnvironment":190,"./invariant":204}],201:[function(require,module,exports){
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

'use strict';

/**
 * Gets the scroll position of the supplied element or window.
 *
 * The return values are unbounded, unlike `getScrollPosition`. This means they
 * may be negative or exceed the element boundaries (which is possible using
 * inertial scrolling).
 *
 * @param {DOMWindow|DOMElement} scrollable
 * @return {object} Map with `x` and `y` keys.
 */

function getUnboundedScrollPosition(scrollable) {
  if (scrollable === window) {
    return {
      x: window.pageXOffset || document.documentElement.scrollLeft,
      y: window.pageYOffset || document.documentElement.scrollTop
    };
  }
  return {
    x: scrollable.scrollLeft,
    y: scrollable.scrollTop
  };
}

module.exports = getUnboundedScrollPosition;
},{}],202:[function(require,module,exports){
'use strict';

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

var _uppercasePattern = /([A-Z])/g;

/**
 * Hyphenates a camelcased string, for example:
 *
 *   > hyphenate('backgroundColor')
 *   < "background-color"
 *
 * For CSS style names, use `hyphenateStyleName` instead which works properly
 * with all vendor prefixes, including `ms`.
 *
 * @param {string} string
 * @return {string}
 */
function hyphenate(string) {
  return string.replace(_uppercasePattern, '-$1').toLowerCase();
}

module.exports = hyphenate;
},{}],203:[function(require,module,exports){
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

'use strict';

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

var msPattern = /^ms-/;

/**
 * Hyphenates a camelcased CSS property name, for example:
 *
 *   > hyphenateStyleName('backgroundColor')
 *   < "background-color"
 *   > hyphenateStyleName('MozTransition')
 *   < "-moz-transition"
 *   > hyphenateStyleName('msTransition')
 *   < "-ms-transition"
 *
 * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix
 * is converted to `-ms-`.
 *
 * @param {string} string
 * @return {string}
 */
function hyphenateStyleName(string) {
  return hyphenate(string).replace(msPattern, '-ms-');
}

module.exports = hyphenateStyleName;
},{"./hyphenate":202}],204:[function(require,module,exports){
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 */

'use strict';

/**
 * Use invariant() to assert state which your program assumes to be true.
 *
 * Provide sprintf-style format (only %s is supported) and arguments
 * to provide information about what broke and what you were
 * expecting.
 *
 * The invariant message will be stripped in production, but the invariant
 * will remain to ensure logic does not differ in production.
 */

function invariant(condition, format, a, b, c, d, e, f) {
  if ("production" !== 'production') {
    if (format === undefined) {
      throw new Error('invariant requires an error message argument');
    }
  }

  if (!condition) {
    var error;
    if (format === undefined) {
      error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
    } else {
      var args = [a, b, c, d, e, f];
      var argIndex = 0;
      error = new Error(format.replace(/%s/g, function () {
        return args[argIndex++];
      }));
      error.name = 'Invariant Violation';
    }

    error.framesToPop = 1; // we don't care about invariant's own frame
    throw error;
  }
}

module.exports = invariant;
},{}],205:[function(require,module,exports){
'use strict';

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

/**
 * @param {*} object The object to check.
 * @return {boolean} Whether or not the object is a DOM node.
 */
function isNode(object) {
  return !!(object && (typeof Node === 'function' ? object instanceof Node : typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'));
}

module.exports = isNode;
},{}],206:[function(require,module,exports){
'use strict';

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

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

/**
 * @param {*} object The object to check.
 * @return {boolean} Whether or not the object is a DOM text node.
 */
function isTextNode(object) {
  return isNode(object) && object.nodeType == 3;
}

module.exports = isTextNode;
},{"./isNode":205}],207:[function(require,module,exports){
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks static-only
 */

'use strict';

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

/**
 * Constructs an enumeration with keys equal to their value.
 *
 * For example:
 *
 *   var COLORS = keyMirror({blue: null, red: null});
 *   var myColor = COLORS.blue;
 *   var isColorValid = !!COLORS[myColor];
 *
 * The last line could not be performed if the values of the generated enum were
 * not equal to their keys.
 *
 *   Input:  {key1: val1, key2: val2}
 *   Output: {key1: key1, key2: key2}
 *
 * @param {object} obj
 * @return {object}
 */
var keyMirror = function keyMirror(obj) {
  var ret = {};
  var key;
  !(obj instanceof Object && !Array.isArray(obj)) ? "production" !== 'production' ? invariant(false, 'keyMirror(...): Argument must be an object.') : invariant(false) : void 0;
  for (key in obj) {
    if (!obj.hasOwnProperty(key)) {
      continue;
    }
    ret[key] = key;
  }
  return ret;
};

module.exports = keyMirror;
},{"./invariant":204}],208:[function(require,module,exports){
"use strict";

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 */

/**
 * Allows extraction of a minified key. Let's the build system minify keys
 * without losing the ability to dynamically use key strings as values
 * themselves. Pass in an object with a single key/val pair and it will return
 * you the string key of that single record. Suppose you want to grab the
 * value for a key 'className' inside of an object. Key/val minification may
 * have aliased that key to be 'xa12'. keyOf({className: null}) will return
 * 'xa12' in that case. Resolve keys you want to use once at startup time, then
 * reuse those resolutions.
 */
var keyOf = function keyOf(oneKeyObj) {
  var key;
  for (key in oneKeyObj) {
    if (!oneKeyObj.hasOwnProperty(key)) {
      continue;
    }
    return key;
  }
  return null;
};

module.exports = keyOf;
},{}],209:[function(require,module,exports){
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * 
 * @typechecks static-only
 */

'use strict';

/**
 * Memoizes the return value of a function that accepts one string argument.
 */

function memoizeStringOnly(callback) {
  var cache = {};
  return function (string) {
    if (!cache.hasOwnProperty(string)) {
      cache[string] = callback.call(this, string);
    }
    return cache[string];
  };
}

module.exports = memoizeStringOnly;
},{}],210:[function(require,module,exports){
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

'use strict';

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

var performance;

if (ExecutionEnvironment.canUseDOM) {
  performance = window.performance || window.msPerformance || window.webkitPerformance;
}

module.exports = performance || {};
},{"./ExecutionEnvironment":190}],211:[function(require,module,exports){
'use strict';

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 */

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

var performanceNow;

/**
 * Detect if we can use `window.performance.now()` and gracefully fallback to
 * `Date.now()` if it doesn't exist. We need to support Firefox < 15 for now
 * because of Facebook's testing infrastructure.
 */
if (performance.now) {
  performanceNow = function performanceNow() {
    return performance.now();
  };
} else {
  performanceNow = function performanceNow() {
    return Date.now();
  };
}

module.exports = performanceNow;
},{"./performance":210}],212:[function(require,module,exports){
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @typechecks
 * 
 */

/*eslint-disable no-self-compare */

'use strict';

var hasOwnProperty = Object.prototype.hasOwnProperty;

/**
 * inlined Object.is polyfill to avoid requiring consumers ship their own
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
 */
function is(x, y) {
  // SameValue algorithm
  if (x === y) {
    // Steps 1-5, 7-10
    // Steps 6.b-6.e: +0 != -0
    // Added the nonzero y check to make Flow happy, but it is redundant
    return x !== 0 || y !== 0 || 1 / x === 1 / y;
  } else {
    // Step 6.a: NaN == NaN
    return x !== x && y !== y;
  }
}

/**
 * Performs equality by iterating through keys on an object and returning false
 * when any key has values which are not strictly equal between the arguments.
 * Returns true when the values of all keys are strictly equal.
 */
function shallowEqual(objA, objB) {
  if (is(objA, objB)) {
    return true;
  }

  if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
    return false;
  }

  var keysA = Object.keys(objA);
  var keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  // Test for A's keys different from B.
  for (var i = 0; i < keysA.length; i++) {
    if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
      return false;
    }
  }

  return true;
}

module.exports = shallowEqual;
},{}],213:[function(require,module,exports){
/**
 * Copyright 2014-2015, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 */

'use strict';

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

/**
 * Similar to invariant but only logs a warning if the condition is not met.
 * This can be used to log issues in development environments in critical
 * paths. Removing the logging code for production environments will keep the
 * same logic and follow the same code paths.
 */

var warning = emptyFunction;

if ("production" !== 'production') {
  (function () {
    var printWarning = function printWarning(format) {
      for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
        args[_key - 1] = arguments[_key];
      }

      var argIndex = 0;
      var message = 'Warning: ' + format.replace(/%s/g, function () {
        return args[argIndex++];
      });
      if (typeof console !== 'undefined') {
        console.error(message);
      }
      try {
        // --- Welcome to debugging React ---
        // This error was thrown as a convenience so that you can use this stack
        // to find the callsite that caused this warning to fire.
        throw new Error(message);
      } catch (x) {}
    };

    warning = function warning(condition, format) {
      if (format === undefined) {
        throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument');
      }

      if (format.indexOf('Failed Composite propType: ') === 0) {
        return; // Ignore CompositeComponent proptype check.
      }

      if (!condition) {
        for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
          args[_key2 - 2] = arguments[_key2];
        }

        printWarning.apply(undefined, [format].concat(args));
      }
    };
  })();
}

module.exports = warning;
},{"./emptyFunction":196}],214:[function(require,module,exports){
'use strict';
/* eslint-disable no-unused-vars */
var hasOwnProperty = Object.prototype.hasOwnProperty;
var propIsEnumerable = Object.prototype.propertyIsEnumerable;

function toObject(val) {
	if (val === null || val === undefined) {
		throw new TypeError('Object.assign cannot be called with null or undefined');
	}

	return Object(val);
}

function shouldUseNative() {
	try {
		if (!Object.assign) {
			return false;
		}

		// Detect buggy property enumeration order in older V8 versions.

		// https://bugs.chromium.org/p/v8/issues/detail?id=4118
		var test1 = new String('abc');  // eslint-disable-line
		test1[5] = 'de';
		if (Object.getOwnPropertyNames(test1)[0] === '5') {
			return false;
		}

		// https://bugs.chromium.org/p/v8/issues/detail?id=3056
		var test2 = {};
		for (var i = 0; i < 10; i++) {
			test2['_' + String.fromCharCode(i)] = i;
		}
		var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
			return test2[n];
		});
		if (order2.join('') !== '0123456789') {
			return false;
		}

		// https://bugs.chromium.org/p/v8/issues/detail?id=3056
		var test3 = {};
		'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
			test3[letter] = letter;
		});
		if (Object.keys(Object.assign({}, test3)).join('') !==
				'abcdefghijklmnopqrst') {
			return false;
		}

		return true;
	} catch (e) {
		// We don't expect any of the above to throw, but better to be safe.
		return false;
	}
}

module.exports = shouldUseNative() ? Object.assign : function (target, source) {
	var from;
	var to = toObject(target);
	var symbols;

	for (var s = 1; s < arguments.length; s++) {
		from = Object(arguments[s]);

		for (var key in from) {
			if (hasOwnProperty.call(from, key)) {
				to[key] = from[key];
			}
		}

		if (Object.getOwnPropertySymbols) {
			symbols = Object.getOwnPropertySymbols(from);
			for (var i = 0; i < symbols.length; i++) {
				if (propIsEnumerable.call(from, symbols[i])) {
					to[symbols[i]] = from[symbols[i]];
				}
			}
		}
	}

	return to;
};

},{}],215:[function(require,module,exports){
'use strict';

module.exports = require('./lib/React');

},{"./lib/React":64}],216:[function(require,module,exports){
'use strict';
module.exports = require('./lib/index');

},{"./lib/index":220}],217:[function(require,module,exports){
'use strict';

var randomFromSeed = require('./random/random-from-seed');

var ORIGINAL = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-';
var alphabet;
var previousSeed;

var shuffled;

function reset() {
    shuffled = false;
}

function setCharacters(_alphabet_) {
    if (!_alphabet_) {
        if (alphabet !== ORIGINAL) {
            alphabet = ORIGINAL;
            reset();
        }
        return;
    }

    if (_alphabet_ === alphabet) {
        return;
    }

    if (_alphabet_.length !== ORIGINAL.length) {
        throw new Error('Custom alphabet for shortid must be ' + ORIGINAL.length + ' unique characters. You submitted ' + _alphabet_.length + ' characters: ' + _alphabet_);
    }

    var unique = _alphabet_.split('').filter(function(item, ind, arr){
       return ind !== arr.lastIndexOf(item);
    });

    if (unique.length) {
        throw new Error('Custom alphabet for shortid must be ' + ORIGINAL.length + ' unique characters. These characters were not unique: ' + unique.join(', '));
    }

    alphabet = _alphabet_;
    reset();
}

function characters(_alphabet_) {
    setCharacters(_alphabet_);
    return alphabet;
}

function setSeed(seed) {
    randomFromSeed.seed(seed);
    if (previousSeed !== seed) {
        reset();
        previousSeed = seed;
    }
}

function shuffle() {
    if (!alphabet) {
        setCharacters(ORIGINAL);
    }

    var sourceArray = alphabet.split('');
    var targetArray = [];
    var r = randomFromSeed.nextValue();
    var characterIndex;

    while (sourceArray.length > 0) {
        r = randomFromSeed.nextValue();
        characterIndex = Math.floor(r * sourceArray.length);
        targetArray.push(sourceArray.splice(characterIndex, 1)[0]);
    }
    return targetArray.join('');
}

function getShuffled() {
    if (shuffled) {
        return shuffled;
    }
    shuffled = shuffle();
    return shuffled;
}

/**
 * lookup shuffled letter
 * @param index
 * @returns {string}
 */
function lookup(index) {
    var alphabetShuffled = getShuffled();
    return alphabetShuffled[index];
}

module.exports = {
    characters: characters,
    seed: setSeed,
    lookup: lookup,
    shuffled: getShuffled
};

},{"./random/random-from-seed":223}],218:[function(require,module,exports){
'use strict';
var alphabet = require('./alphabet');

/**
 * Decode the id to get the version and worker
 * Mainly for debugging and testing.
 * @param id - the shortid-generated id.
 */
function decode(id) {
    var characters = alphabet.shuffled();
    return {
        version: characters.indexOf(id.substr(0, 1)) & 0x0f,
        worker: characters.indexOf(id.substr(1, 1)) & 0x0f
    };
}

module.exports = decode;

},{"./alphabet":217}],219:[function(require,module,exports){
'use strict';

var randomByte = require('./random/random-byte');

function encode(lookup, number) {
    var loopCounter = 0;
    var done;

    var str = '';

    while (!done) {
        str = str + lookup( ( (number >> (4 * loopCounter)) & 0x0f ) | randomByte() );
        done = number < (Math.pow(16, loopCounter + 1 ) );
        loopCounter++;
    }
    return str;
}

module.exports = encode;

},{"./random/random-byte":222}],220:[function(require,module,exports){
'use strict';

var alphabet = require('./alphabet');
var encode = require('./encode');
var decode = require('./decode');
var isValid = require('./is-valid');

// Ignore all milliseconds before a certain time to reduce the size of the date entropy without sacrificing uniqueness.
// This number should be updated every year or so to keep the generated id short.
// To regenerate `new Date() - 0` and bump the version. Always bump the version!
var REDUCE_TIME = 1426452414093;

// don't change unless we change the algos or REDUCE_TIME
// must be an integer and less than 16
var version = 5;

// if you are using cluster or multiple servers use this to make each instance
// has a unique value for worker
// Note: I don't know if this is automatically set when using third
// party cluster solutions such as pm2.
var clusterWorkerId = require('./util/cluster-worker-id') || 0;

// Counter is used when shortid is called multiple times in one second.
var counter;

// Remember the last time shortid was called in case counter is needed.
var previousSeconds;

/**
 * Generate unique id
 * Returns string id
 */
function generate() {

    var str = '';

    var seconds = Math.floor((Date.now() - REDUCE_TIME) * 0.001);

    if (seconds === previousSeconds) {
        counter++;
    } else {
        counter = 0;
        previousSeconds = seconds;
    }

    str = str + encode(alphabet.lookup, version);
    str = str + encode(alphabet.lookup, clusterWorkerId);
    if (counter > 0) {
        str = str + encode(alphabet.lookup, counter);
    }
    str = str + encode(alphabet.lookup, seconds);

    return str;
}


/**
 * Set the seed.
 * Highly recommended if you don't want people to try to figure out your id schema.
 * exposed as shortid.seed(int)
 * @param seed Integer value to seed the random alphabet.  ALWAYS USE THE SAME SEED or you might get overlaps.
 */
function seed(seedValue) {
    alphabet.seed(seedValue);
    return module.exports;
}

/**
 * Set the cluster worker or machine id
 * exposed as shortid.worker(int)
 * @param workerId worker must be positive integer.  Number less than 16 is recommended.
 * returns shortid module so it can be chained.
 */
function worker(workerId) {
    clusterWorkerId = workerId;
    return module.exports;
}

/**
 *
 * sets new characters to use in the alphabet
 * returns the shuffled alphabet
 */
function characters(newCharacters) {
    if (newCharacters !== undefined) {
        alphabet.characters(newCharacters);
    }

    return alphabet.shuffled();
}


// Export all other functions as properties of the generate function
module.exports = generate;
module.exports.generate = generate;
module.exports.seed = seed;
module.exports.worker = worker;
module.exports.characters = characters;
module.exports.decode = decode;
module.exports.isValid = isValid;

},{"./alphabet":217,"./decode":218,"./encode":219,"./is-valid":221,"./util/cluster-worker-id":224}],221:[function(require,module,exports){
'use strict';
var alphabet = require('./alphabet');

function isShortId(id) {
    if (!id || typeof id !== 'string' || id.length < 6 ) {
        return false;
    }

    var characters = alphabet.characters();
    var invalidCharacters = id.split('').map(function(char){
        if (characters.indexOf(char) === -1) {
            return char;
        }
    }).join('').split('').join('');

    return invalidCharacters.length === 0;
}

module.exports = isShortId;

},{"./alphabet":217}],222:[function(require,module,exports){
'use strict';

var crypto = window.crypto || window.msCrypto; // IE 11 uses window.msCrypto

function randomByte() {
    if (!crypto || !crypto.getRandomValues) {
        return Math.floor(Math.random() * 256) & 0x30;
    }
    var dest = new Uint8Array(1);
    crypto.getRandomValues(dest);
    return dest[0] & 0x30;
}

module.exports = randomByte;

},{}],223:[function(require,module,exports){
'use strict';

// Found this seed-based random generator somewhere
// Based on The Central Randomizer 1.3 (C) 1997 by Paul Houle (houle@msc.cornell.edu)

var seed = 1;

/**
 * return a random number based on a seed
 * @param seed
 * @returns {number}
 */
function getNextValue() {
    seed = (seed * 9301 + 49297) % 233280;
    return seed/(233280.0);
}

function setSeed(_seed_) {
    seed = _seed_;
}

module.exports = {
    nextValue: getNextValue,
    seed: setSeed
};

},{}],224:[function(require,module,exports){
'use strict';

module.exports = 0;

},{}],225:[function(require,module,exports){
/**
 * Convert array of 16 byte values to UUID string format of the form:
 * XXXXXXXX-XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
 */
var byteToHex = [];
for (var i = 0; i < 256; ++i) {
  byteToHex[i] = (i + 0x100).toString(16).substr(1);
}

function bytesToUuid(buf, offset) {
  var i = offset || 0;
  var bth = byteToHex;
  return  bth[buf[i++]] + bth[buf[i++]] +
          bth[buf[i++]] + bth[buf[i++]] + '-' +
          bth[buf[i++]] + bth[buf[i++]] + '-' +
          bth[buf[i++]] + bth[buf[i++]] + '-' +
          bth[buf[i++]] + bth[buf[i++]] + '-' +
          bth[buf[i++]] + bth[buf[i++]] +
          bth[buf[i++]] + bth[buf[i++]] +
          bth[buf[i++]] + bth[buf[i++]];
}

module.exports = bytesToUuid;

},{}],226:[function(require,module,exports){
(function (global){
// Unique ID creation requires a high quality random # generator.  In the
// browser this is a little complicated due to unknown quality of Math.random()
// and inconsistent support for the `crypto` API.  We do the best we can via
// feature-detection
var rng;

var crypto = global.crypto || global.msCrypto; // for IE 11
if (crypto && crypto.getRandomValues) {
  // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
  var rnds8 = new Uint8Array(16);
  rng = function whatwgRNG() {
    crypto.getRandomValues(rnds8);
    return rnds8;
  };
}

if (!rng) {
  // Math.random()-based (RNG)
  //
  // If all else fails, use Math.random().  It's fast, but is of unspecified
  // quality.
  var  rnds = new Array(16);
  rng = function() {
    for (var i = 0, r; i < 16; i++) {
      if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
      rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
    }

    return rnds;
  };
}

module.exports = rng;

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],227:[function(require,module,exports){
var rng = require('./lib/rng');
var bytesToUuid = require('./lib/bytesToUuid');

function v4(options, buf, offset) {
  var i = buf && offset || 0;

  if (typeof(options) == 'string') {
    buf = options == 'binary' ? new Array(16) : null;
    options = null;
  }
  options = options || {};

  var rnds = options.random || (options.rng || rng)();

  // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
  rnds[6] = (rnds[6] & 0x0f) | 0x40;
  rnds[8] = (rnds[8] & 0x3f) | 0x80;

  // Copy bytes to buffer, if provided
  if (buf) {
    for (var ii = 0; ii < 16; ++ii) {
      buf[i + ii] = rnds[ii];
    }
  }

  return buf || bytesToUuid(rnds);
}

module.exports = v4;

},{"./lib/bytesToUuid":225,"./lib/rng":226}],228:[function(require,module,exports){
var IDBTransaction, Transaction, clearStore, db, dbName, dbVersion, e, error, getItem, indexedDB, noop, ready, rejectReady, removeItem, request, resolveReady, setItem, storeName, types;

resolveReady = null;

rejectReady = null;

ready = new Promise(function(resolve, reject) {
  resolveReady = resolve;
  return rejectReady = reject;
});

indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB;

IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.OIDBTransaction || window.msIDBTransaction;

db = null;

dbVersion = 1.0;

dbName = CONFIG.platform === "chrome" ? "polarr_cache" : "polarr_v3";

storeName = "keyvaluepairs";

types = {
  'readwrite': IDBTransaction.READ_WRITE != null ? IDBTransaction.READ_WRITE : "readwrite",
  'readonly': IDBTransaction.READ_ONLY != null ? IDBTransaction.READ_ONLY : "readonly"
};

noop = function() {};

clearStore = function(callback) {
  var req, store, transaction;
  transaction = db.transaction([storeName], types.readwrite);
  store = transaction.objectStore(storeName);
  req = store.clear();
  return req.onsuccess = function() {
    if (typeof callback === "function") {
      callback();
    }
    return console.log("store cleared");
  };
};

Transaction = function(method, key, item, callback) {
  var type;
  if (callback == null) {
    callback = noop;
  }
  type = method === "get" ? types.readonly : types.readwrite;
  ready.then(function() {
    var req, store, transaction;
    transaction = db.transaction([storeName], type);
    store = transaction.objectStore(storeName);
    transaction.oncomplete = function() {
      return callback({
        success: true
      }, req.result);
    };
    transaction.onabort = function() {
      var err;
      err = req.error ? req.error : req.transaction.error;
      console.error(err);
      return callback({
        success: false
      }, err);
    };
    return req = (function() {
      switch (method) {
        case "put":
          return store.put(item, key);
        case "get":
          return store.get(key);
        case "delete":
          return store["delete"](key);
      }
    })();
  });
  return key;
};

try {
  request = indexedDB.open(dbName, dbVersion);
  request.onerror = function(e) {
    rejectReady(e);
    return console.error("Error creating/accessing IndexedDB database");
  };
  request.onsuccess = function(e) {
    var setVersion;
    db = request.result;
    db.onerror = function(e) {
      rejectReady(e);
      return console.error("Error creating/accessing IndexedDB database");
    };
    if (db.setVersion && db.version !== dbVersion) {
      setVersion = db.setVersion(dbVersion);
      return setVersion.onsuccess = function() {
        db.createObjectStore(storeName);
        return resolveReady();
      };
    } else {
      return resolveReady();
    }
  };
  request.onupgradeneeded = function(event) {
    db = event.target.result;
    return db.createObjectStore(storeName);
  };
} catch (error) {
  e = error;
  console.error("Error opening IndexedDB database", e);
  rejectReady(e);
}

setItem = function(key, item, callback) {
  return Transaction("put", key, item, callback);
};

getItem = function(key, callback) {
  return Transaction("get", key, null, callback);
};

removeItem = function(key, callback) {
  return Transaction("delete", key, null, callback);
};

module.exports = {
  setItem: setItem,
  getItem: getItem,
  removeItem: removeItem,
  clearStore: clearStore,
  ready: function() {
    return ready;
  }
};


},{}],229:[function(require,module,exports){
var actions, deviceActions, editorActions, filesystem, filterActions, purchasesActions, store, uiActions, userActions, utils;

store = require("./store.coffee");

filesystem = require("./filesystem.coffee");

utils = require("./utils.coffee");

uiActions = require("./actions/ui.coffee");

filterActions = require("./actions/filters.coffee");

userActions = require("./actions/user.coffee");

editorActions = require("./actions/editor.coffee");

purchasesActions = require("./actions/purchases.coffee");

deviceActions = require("./actions/device.coffee");

actions = {
  __hardReset: function() {
    return store.storage.removeAll(function() {
      return window.location.reload();
    });
  },
  externalFilterStore: function() {
    if (utils.isChina()) {
      return actions.openURL("http://huati.weibo.com/k/%E6%B3%BC%E8%BE%A3%E6%BB%A4%E9%95%9C");
    } else {
      return actions.openURL("https://www.instagram.com/explore/tags/polarrfilter/");
    }
  },
  externalReview: function() {
    var ref, url;
    if (CONFIG.platform === "osx") {
      if (purchasesActions.isProPurchased()) {
        url = "https://itunes.apple.com/us/app/polarr-photo-editor/id1058273036?mt=12";
      } else {
        url = "https://itunes.apple.com/us/app/polarr-photo-editor-lite/id1077124956?ls=1&mt=12";
      }
      return actions.openURL(url);
    } else if (CONFIG.platform === "windows") {
      if (purchasesActions.isProPurchased()) {
        url = "https://www.microsoft.com/en-us/store/apps/polarr-photo-editor/9nblggh1npnc";
      } else {
        url = "https://www.microsoft.com/en-us/store/apps/polarr-photo-editor-lite/9nblggh6bgx8";
      }
      return actions.openURL(url);
    } else if (CONFIG.platform === "web") {
      return actions.openURL("https://www.facebook.com/polarrian/");
    } else if (CONFIG.platform === "chrome") {
      return actions.openURL("https://chrome.google.com/webstore/detail/polarr-photo-editor/djonnbgfieijldcieafgjcnhmpcfpmgg");
    } else {
      return (ref = store.manager) != null ? ref.sendMessage("ppe.external.review") : void 0;
    }
  },
  openURL: function(url) {
    return utils.openURL(url);
  }
};

actions = Filtr.utils.assign(actions, uiActions, filterActions, editorActions, purchasesActions, deviceActions, {
  user: userActions
}, {
  filesystem: filesystem
});

module.exports = actions;


},{"./actions/device.coffee":230,"./actions/editor.coffee":231,"./actions/filters.coffee":232,"./actions/purchases.coffee":233,"./actions/ui.coffee":234,"./actions/user.coffee":235,"./filesystem.coffee":311,"./store.coffee":346,"./utils.coffee":360}],230:[function(require,module,exports){
var actions, manager, ref, ref1, ref2, ref3, store, utils, uuid,
  indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

manager = require("../manager.coffee");

store = require("../store.coffee");

utils = require("../utils.coffee");

uuid = require("uuid/v4");

if ((ref = store.manager) != null) {
  ref.waitForMessage("ppe.keyboardHeightChanged", (function(_this) {
    return function(keyboard) {
      var height;
      if (keyboard.height === manager.state.keyboardHeight) {
        height = 0;
      } else {
        height = keyboard.height;
      }
      return manager.set("keyboardHeight", height);
    };
  })(this));
}

if ((ref1 = store.manager) != null) {
  ref1.sendMessage("ppe.ready", true, (function(_this) {
    return function(res) {
      return manager.set("sharingOptions", res.sharing);
    };
  })(this));
}

if ((ref2 = store.manager) != null) {
  ref2.sendMessage("ppe.device.orientation", true, (function(_this) {
    return function(res) {
      return actions.setOrientation(res.orientation);
    };
  })(this));
}

if ((ref3 = store.manager) != null) {
  ref3.waitForMessage("ppe.deviceOrientationChanged", (function(_this) {
    return function(res) {
      return actions.setOrientation(res.orientation);
    };
  })(this));
}

actions = {
  getDeviceID: function() {
    return new Promise(function(resolve) {
      var id;
      id = manager.state.deviceID || uuid();
      manager.set('deviceID', id);
      return resolve(id);
    });
  },
  setPerformance: function() {
    return store.manager.sendMessage("ppe.device.model", true, function(res) {
      var downsampleFactor, frameRate, id, model, name, number;
      model = res.model.split(",")[0];
      id = model.split(/^(iPhone|iPad|iPod)/);
      name = id[1];
      number = parseInt(id[2], 10);
      manager.set("device", {
        name: name,
        number: number
      });
      downsampleFactor = 1;
      if (name === "iPad" && number < 4) {
        downsampleFactor = 3;
      } else if (name === "iPad" && number < 5) {
        downsampleFactor = 2;
      } else if (name === "iPhone" && number < 6) {
        downsampleFactor = 3;
      } else if (name === "iPhone" && number < 7) {
        downsampleFactor = 2;
      } else if (name === "iPod" && number <= 5) {
        downsampleFactor = 3;
      } else if (name === "iPod" && number < 7) {
        downsampleFactor = 2;
      }
      frameRate = (function() {
        switch (downsampleFactor) {
          case 3:
            return 10;
          case 2:
            return 20;
          default:
            return 30;
        }
      })();
      return Filtr.setPerformance(downsampleFactor, frameRate);
    });
  },
  setOrientation: function(orientation) {
    var landscape, landscapeOrientations, portrait, portraitOrientations;
    orientation = manager.state.settings.orientation || orientation;
    landscapeOrientations = ["landscape-left", "landscape-right", "landscape"];
    portraitOrientations = ["portrait", "portrait-upsidedown"];
    portrait = indexOf.call(portraitOrientations, orientation) >= 0;
    landscape = indexOf.call(landscapeOrientations, orientation) >= 0;
    if (portrait || landscape) {
      return manager.set({
        portraitMode: portrait
      });
    }
  },
  showBatchExport: function() {
    var ref4;
    return (ref4 = store.manager) != null ? ref4.sendMessage("ppe.batch.select") : void 0;
  },
  showGallery: function() {
    var ref4;
    return (ref4 = store.manager) != null ? ref4.sendMessage("ppe.gallery.end", {
      id: store.currentPhoto,
      transition: true
    }) : void 0;
  },
  takePicture: function() {
    var ref4;
    return (ref4 = store.manager) != null ? ref4.sendMessage("ppe.gallery.camera") : void 0;
  }
};

module.exports = actions;


},{"../manager.coffee":338,"../store.coffee":346,"../utils.coffee":360,"uuid/v4":227}],231:[function(require,module,exports){
var AutoEnhance, Text, actions, clipboard, manager, store, translate, tutorials, ui, utils;

translate = require("counterpart");

manager = require("../manager.coffee");

utils = require("../utils.coffee");

store = require("../store.coffee");

tutorials = require("../tutorials.coffee");

ui = require("./ui.coffee");

Text = require("../text.coffee");

AutoEnhance = require("../lib/AutoEnhance.coffee");

clipboard = require("clipboard-js");

actions = {
  getTextPreset: function() {
    var text;
    text = "{state:" + JSON.stringify(Filtr.getState().text) + "}";
    clipboard.copy(text);
    return "Text preset copied to clipboard.";
  },
  addText: function(layers) {
    var i, layer, len, orientation, position, state, text;
    state = Filtr.getState();
    text = state.text.slice(0);
    orientation = -Filtr._.rotate90 * 90;
    for (i = 0, len = layers.length; i < len; i++) {
      layer = layers[i];
      position = layer.position || Text.defaults.position;
      position = utils.rotateVector(position, orientation, [0.5, 0.5]);
      text.push(utils.assign(Text.defaults, layer, {
        orientation: orientation,
        position: position
      }));
    }
    Filtr.setState({
      text: text
    });
    return Filtr.saveState("tooltips.add_text");
  },
  removeText: function(index) {
    var state, text;
    state = Filtr.getState();
    text = state.text.slice(0);
    text.splice(index, 1);
    Filtr.setState({
      text: text
    });
    return Filtr.saveState("tooltips.remove_text");
  },
  addFace: function() {
    var bounds, faces, h, height, ref, scale, state, w, width;
    ref = Filtr.getImageSize(), width = ref[0], height = ref[1];
    w = 1;
    h = 1.5;
    scale = height / h * 0.5;
    w *= scale;
    h *= scale;
    w /= width;
    h /= height;
    bounds = [(1 - w) / 2, (1 - h) / 2, w, h];
    state = Filtr.getState();
    faces = state.faces.slice(0);
    faces.push({
      boundaries: bounds
    });
    Filtr.setState({
      faces: faces
    });
    return Filtr.saveState("tooltips.add_face");
  },
  removeFace: function(index) {
    var faces, state;
    state = Filtr.getState();
    faces = state.faces.slice(0);
    faces.splice(index, 1);
    if (!faces.length) {
      state = {
        skin_smoothness: 0,
        skin_texture: 0,
        skin_tone: 0,
        skin_hue: 0,
        teeth_brightness: 0,
        teeth_whitening: 0,
        eyes_brightness: 0,
        eyes_clarity: 0,
        lips_brightness: 0,
        lips_saturation: 0
      };
    } else {
      state = {};
    }
    state.faces = faces;
    Filtr.setState(state);
    return Filtr.saveState("tooltips.remove_face");
  },
  createMask: function(type) {
    var adjustments, center, channel, channels, crop, height, i, index, len, m, mask, masks, minCrop, ref, state, width;
    ref = Filtr._.texture, width = ref.width, height = ref.height;
    state = Filtr.getState();
    masks = state.local_adjustments;
    crop = state.crop;
    minCrop = Math.min(crop[2], crop[3]);
    center = [(crop[0] + crop[2] / 2) - 0.5, (crop[1] + crop[3] / 2) - 0.5];
    adjustments = Filtr.getDefaultLocalState(true);
    adjustments.exposure = 0.5;
    if (type === "radial") {
      mask = {
        type: type,
        feather: 1,
        size: [0.8 * height / width * minCrop, 0.8 * minCrop],
        position: [center[0], 1.0 - (center[1] + 0.5) - 0.5],
        angle: 0,
        invert: true,
        adjustments: adjustments
      };
    }
    if (type === "gradient") {
      mask = {
        type: type,
        startPoint: utils.rotateVector([center[0], center[1] - crop[3] / 2], state.rotate90 * -90),
        endPoint: utils.rotateVector([center[0], center[1] + crop[3] / 2], state.rotate90 * -90),
        invert: false,
        reflect: false,
        adjustments: adjustments
      };
    }
    if (type === "color") {
      mask = {
        type: type,
        feather: 1,
        threshold: 0.5,
        invert: false,
        size: [0.5 * height / width * minCrop, 0.5 * minCrop],
        position: [center[0], 1.0 - (center[1] + 0.5) - 0.5],
        adjustments: adjustments
      };
    }
    if (type === "brush") {
      channels = [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]];
      for (i = 0, len = masks.length; i < len; i++) {
        m = masks[i];
        if (m.type === type) {
          index = m.channel.indexOf(1);
          channels[index] = null;
        }
      }
      channel = channels.reduce(function(prev, curr) {
        return prev || curr;
      });
      mask = {
        type: type,
        invert: false,
        channel: channel,
        brush: null,
        brush_radius: 0.3,
        brush_opacity: 0.5,
        adjustments: adjustments
      };
    }
    return mask;
  },
  addMask: function(type) {
    var mask, masks;
    masks = Filtr.utils.clone(Filtr.getState().local_adjustments);
    mask = actions.createMask(type);
    masks.push(mask);
    Filtr.setState({
      local_adjustments: masks
    });
    Filtr.saveState("buttons.add tooltips." + type + "_filter");
    manager.emit("onetime_prompt", {
      id: type,
      message: utils.translateReplace("status.show_me_how", {
        thing: translate("tooltips." + type + "_filter")
      }),
      button: translate("buttons.show_me"),
      action: function() {
        return manager.emit("tutorial_id", type + "_mask");
      }
    });
    return masks;
  },
  getMaxExportSize: function() {
    var max;
    max = Filtr.getMaxTextureSize();
    if (!actions.isExportSizeLimited()) {
      max *= 2;
    }
    return max;
  },
  isExportSizeLimited: function() {
    var device, limitExportSize, settings;
    device = manager.state.device;
    settings = manager.state.settings;
    limitExportSize = false;
    if (settings.limitExportSize != null) {
      limitExportSize = settings.limitExportSize;
    } else if (device) {
      if (device.name === "iPad" && device.number < 6) {
        limitExportSize = true;
      } else if (device.name === "iPhone" && device.number < 8) {
        limitExportSize = true;
      } else if (device.name === "iPod") {
        limitExportSize = true;
      }
    }
    return limitExportSize;
  },
  getAutoAdjustments: function(callback) {
    var filter, originalState, output, thumbnailSize;
    thumbnailSize = 300;
    originalState = Filtr.getHistory()[0].state;
    output = Filtr["export"]("data", {
      settings: originalState,
      resize: "fit",
      width: thumbnailSize,
      height: thumbnailSize
    });
    filter = AutoEnhance.auto_filter(output);
    if (typeof callback === "function") {
      callback(filter);
    }
    return filter;
  },
  autoAdjust: function() {
    return actions.getAutoAdjustments(function(adjustments) {
      var message;
      message = "tooltips.auto_adjust";
      Filtr.setState(adjustments);
      Filtr.saveState(message);
      return ui.showStatus(utils.translateMessage(message), 1000);
    });
  },
  autoAdjustFace: function() {
    return actions.getAutoAdjustments(function(auto) {
      var adjustments, average, meanValues, message, offset;
      meanValues = [auto.exposure_mean, auto.highlights_mean, auto.shadows_mean, auto.saturation_mean];
      average = meanValues.reduce(function(prev, curr) {
        return prev + curr;
      }) / meanValues.length;
      offset = function(value, mul) {
        return (value - average) * mul;
      };
      adjustments = {
        skin_smoothness: 0.35 + offset(meanValues[2], 0.3),
        skin_texture: 0.2 + offset(meanValues[1], 0.3),
        skin_tone: 0.2 + offset(meanValues[0], 0.3),
        teeth_whitening: 1.0 - Math.abs(offset(meanValues[1], 0.1)),
        teeth_brightness: 0.1 + offset(meanValues[1], 0.1),
        eyes_clarity: 0.5 + offset(meanValues[2], 0.1),
        eyes_brightness: 0.2 + offset(meanValues[2], 0.1),
        lips_saturation: 0.2 + offset(meanValues[3], 0.3),
        lips_brightness: 0.1 + offset(meanValues[3], 0.1)
      };
      message = "labels.auto labels.face";
      Filtr.setState(adjustments);
      Filtr.saveState(message);
      return ui.showStatus(utils.translateMessage(message), 1000);
    });
  },
  copyAdjustments: function() {
    ui.showStatus(translate("status.adjustment_copied"), 1000);
    return manager.set({
      copiedAdjustments: Filtr.prepareAdjustments(Filtr.getState(true))
    });
  },
  pasteAdjustments: function() {
    if (!manager.state.copiedAdjustments) {
      return;
    }
    ui.showStatus(translate("status.adjustment_pasted"), 1000);
    Filtr.setState(manager.state.copiedAdjustments);
    return Filtr.saveState("menus.paste_edit");
  },
  resetMainAdjustments: function() {
    var defaults, i, len, message, panel, panels, ref, setting, state;
    defaults = Filtr.getDefaultState();
    panels = manager.state.adjustmentPanels;
    state = {};
    for (panel in panels) {
      ref = panels[panel].controls;
      for (i = 0, len = ref.length; i < len; i++) {
        setting = ref[i];
        state[setting] = defaults[setting];
      }
    }
    message = "buttons.reset tooltips.global_adjustments";
    Filtr.setState(state);
    Filtr.saveState(message);
    return ui.showStatus(utils.translateMessage(message), 1000);
  },
  resetAdjustmentsGroup: function(group) {
    var defaults, i, len, message, panels, ref, setting, state;
    defaults = Filtr.getDefaultState();
    panels = manager.state.adjustmentPanels;
    state = {};
    ref = panels[group].controls;
    for (i = 0, len = ref.length; i < len; i++) {
      setting = ref[i];
      state[setting] = defaults[setting];
    }
    message = "buttons.reset labels." + group;
    Filtr.setState(state);
    Filtr.saveState(message);
    return ui.showStatus(utils.translateMessage(message), 1000);
  },
  undo: function() {
    var history, index, label;
    Filtr.undo();
    history = Filtr.getHistory();
    index = Filtr.getIndex();
    if (index > 0) {
      label = "tooltips.undo " + history[index + 1].message;
    } else {
      label = "labels.original";
    }
    return ui.showStatus(utils.translateMessage(label), 1000);
  },
  redo: function() {
    var history, index, label;
    index = Filtr.getIndex();
    history = Filtr.getHistory();
    if (!history[index + 1]) {
      return;
    }
    Filtr.redo();
    label = "tooltips.redo " + history[index + 1].message;
    return ui.showStatus(utils.translateMessage(label), 1000);
  },
  clearHistory: function() {
    var history, original, state;
    original = Filtr.getHistory()[0];
    state = Filtr.makeCompatible(original.state);
    history = [
      {
        message: original.message,
        state: state
      }
    ];
    return Filtr.setHistory(history);
  },
  revertToOriginal: function() {
    var original, state;
    original = Filtr.getHistory()[0];
    state = Filtr.makeCompatible(original.state);
    Filtr.setState(state);
    return Filtr.saveState("buttons.revert");
  },
  showOriginal: (function() {
    var showingOriginal;
    showingOriginal = false;
    return function(show) {
      if (show) {
        if (showingOriginal) {
          return;
        }
        Filtr.render(true);
        ui.showStatus(translate("labels.original"), 1000);
        return showingOriginal = true;
      } else {
        if (!showingOriginal) {
          return;
        }
        Filtr.render(false);
        ui.hideStatus();
        return showingOriginal = false;
      }
    };
  })(),
  _flipX: function(message) {
    var flip;
    flip = !Filtr._.flipX;
    return Filtr.set({
      rotateY: 180 * flip
    }, 0.3, (function(_this) {
      return function() {
        Filtr.setState({
          flip_x: flip
        }, false);
        return Filtr.saveState(message);
      };
    })(this));
  },
  _flipY: function(message) {
    var flip;
    flip = !Filtr._.flipY;
    return Filtr.set({
      rotateX: 180 * flip
    }, 0.3, (function(_this) {
      return function() {
        Filtr.setState({
          flip_y: flip
        }, false);
        return Filtr.saveState(message);
      };
    })(this));
  },
  flipX: function() {
    var message;
    message = "tooltips.flip_h";
    if (Filtr._.rotate90 % 2) {
      return this._flipY(message);
    } else {
      return this._flipX(message);
    }
  },
  flipY: function() {
    var message;
    message = "tooltips.flip_v";
    if (Filtr._.rotate90 % 2) {
      return this._flipX(message);
    } else {
      return this._flipY(message);
    }
  },
  rotate90: function(delta) {
    var crop, cropMode, extra, prev, size, zoom;
    cropMode = Filtr._.cropMode;
    Filtr._.cropMode = true;
    if (!cropMode) {
      Filtr.renderCacheTexture();
    }
    prev = Filtr.getState().rotate90;
    crop = Filtr.rotateCrop(delta * 90);
    size = Filtr.getImageSize();
    zoom = Filtr.getMinZoom(true, [size[1], size[0]]);
    extra = {
      crop: crop,
      zoom: zoom,
      size: [size[1], size[0]]
    };
    return Filtr.set({
      zoom: zoom,
      rotateZ: delta * 90,
      extra: extra
    }, 0.3, (function(_this) {
      return function() {
        var message;
        Filtr.setState({
          rotate90: prev + delta,
          crop: crop
        }, false);
        Filtr.set({
          rotateZ: 0,
          extra: null
        });
        message = delta < 0 ? "tooltips.rotate_ccw" : "tooltips.rotate_cw";
        Filtr.saveState(message);
        return Filtr._.cropMode = cropMode;
      };
    })(this));
  },
  resetCrop: function() {
    var original, state;
    original = Filtr.getHistory()[0];
    state = Filtr.makeCompatible(original.state);
    Filtr.setState({
      crop: [0, 0, 1, 1],
      rotation: state.rotation,
      flip_x: state.flip_x,
      flip_y: state.flip_y,
      rotate90: state.rotate90
    });
    return Filtr.saveState("buttons.reset tooltips.crop_straighten");
  }
};

module.exports = actions;


},{"../lib/AutoEnhance.coffee":314,"../manager.coffee":338,"../store.coffee":346,"../text.coffee":347,"../tutorials.coffee":348,"../utils.coffee":360,"./ui.coffee":234,"clipboard-js":2,"counterpart":3}],232:[function(require,module,exports){
var $, FILTERS_SYNCED, actions, filesystem, manager, purchases, ref, shortid, store, translate, ui, user, utils;

translate = require("counterpart");

shortid = require('shortid');

$ = require("jquery");

manager = require("../manager.coffee");

utils = require("../utils.coffee");

store = require("../store.coffee");

filesystem = require("../filesystem.coffee");

require("../lib/qrcode-reader.js");

ui = require("./ui.coffee");

user = require("./user.coffee");

purchases = require("./purchases.coffee");

if ((ref = store.manager) != null) {
  ref.waitForMessage("ppe.qr.found", function(res) {
    if (res.url) {
      log("qr code found", res.url);
      return actions.importFilterURL(res.url);
    }
  });
}

FILTERS_SYNCED = false;

manager.on("user", function() {
  if (!user.isLoggedIn()) {
    FILTERS_SYNCED = false;
    return manager.set("syncingFilters", 0);
  } else {
    return actions.syncFilters();
  }
});

actions = {
  isSyncEnabled: function() {
    return user.isLoggedIn() && purchases.isPurchased(PURCHASE_KEYS_MAP.backup_filters);
  },
  getFilter: function(id) {
    var filter, j, k, len, len1, pack, ref1, ref2;
    ref1 = manager.state.filterPacks;
    for (j = 0, len = ref1.length; j < len; j++) {
      pack = ref1[j];
      ref2 = pack.filters;
      for (k = 0, len1 = ref2.length; k < len1; k++) {
        filter = ref2[k];
        if (filter.id === id) {
          return filter;
        }
      }
    }
  },
  getFilterName: function(filter) {
    var locale, name;
    name = filter.name;
    locale = translate.getLocale();
    name = name[locale] || name["en"] || name;
    return encodeURIComponent(name);
  },
  syncFilters: function(callback) {
    var endpoint, exportQueue, processFilters, processQueue, userId;
    if (!(!FILTERS_SYNCED && actions.isSyncEnabled())) {
      return;
    }
    manager.set("syncingFilters", 1);
    userId = manager.state.user.id;
    endpoint = (utils.getUrlRoot()) + "/users/custom_filters_by_user_id/" + userId;
    exportQueue = [];
    processQueue = function() {
      var filter;
      filter = exportQueue.pop();
      if (filter) {
        return actions.exportFilter(filter, processQueue);
      }
    };
    processFilters = function(content) {
      var filter, j, k, len, len1, localFilter, localFilters, mergedFilters, newFilter, syncedFilters;
      localFilters = manager.state.customFilters;
      syncedFilters = content.data;
      mergedFilters = [];
      for (j = 0, len = localFilters.length; j < len; j++) {
        filter = localFilters[j];
        if (filter.sync_id) {
          if (syncedFilters.find(function(el) {
            return el.id === filter.sync_id;
          })) {
            mergedFilters.push(filter);
          }
        } else {
          mergedFilters.push(filter);
          exportQueue.push(filter);
        }
      }
      for (k = 0, len1 = syncedFilters.length; k < len1; k++) {
        filter = syncedFilters[k];
        localFilter = localFilters.find(function(el) {
          return el.sync_id === filter.id;
        });
        if (localFilter) {
          Object.assign(localFilter, JSON.parse(filter.filter_content), {
            sync_id: filter.id,
            created: filter.created
          });
        } else {
          newFilter = Object.assign(JSON.parse(filter.filter_content), {
            sync_id: filter.id,
            created: filter.created
          });
          mergedFilters.push(newFilter);
        }
      }
      if (typeof callback === "function") {
        callback();
      }
      FILTERS_SYNCED = true;
      manager.set("syncingFilters", 2);
      processQueue();
      return manager.set("customFilters", mergedFilters);
    };
    return $.ajax({
      type: "GET",
      dataType: "json",
      url: endpoint,
      success: processFilters,
      error: function() {
        return manager.set("syncingFilters", 3);
      }
    });
  },
  deleteFilter: function(filter, callback) {
    var data, endpoint;
    if (filter.sync_id == null) {
      return;
    }
    endpoint = (utils.getUrlRoot()) + "/users/delete_custom_filter";
    data = {
      filter_id: filter.sync_id
    };
    return $.ajax({
      type: "POST",
      dataType: "json",
      url: endpoint,
      data: data,
      success: callback
    });
  },
  updateFilter: function(filter, callback) {
    var data, endpoint;
    if (filter.sync_id == null) {
      return;
    }
    endpoint = (utils.getUrlRoot()) + "/users/modify_custom_filter";
    data = {
      filter_content: JSON.stringify(filter),
      filter_id: filter.sync_id,
      timestamp: new Date().toISOString()
    };
    return $.ajax({
      type: "POST",
      dataType: "json",
      url: endpoint,
      data: data,
      success: callback
    });
  },
  exportFilter: function(filter, callback) {
    var data, endpoint, id, shouldSync, success;
    if (filter.filter_url) {
      id = parseInt(filter.filter_url.split("/").pop(), 10);
      endpoint = (utils.getUrlRoot()) + "/users/modify_custom_filter";
    } else {
      endpoint = (utils.getUrlRoot()) + "/users/export_custom_filter";
    }
    shouldSync = actions.isSyncEnabled();
    data = {
      sync: new Number(shouldSync && !filter.sync_id),
      user_id: manager.state.user.id || 1,
      filter_content: JSON.stringify(filter),
      filter_id: id,
      timestamp: new Date().toISOString()
    };
    success = function(res) {
      var url;
      if (res.code === "ok" && res.data) {
        url = res.data.filter_url;
        id = parseInt(url.split("/").pop(), 10);
        filter.filter_url = url;
        if (shouldSync) {
          filter.sync_id = id;
        }
        manager.set("customFilters", manager.state.customFilters);
      }
      return typeof callback === "function" ? callback({
        code: res.code,
        data: {
          filter_url: filter.filter_url
        },
        message: res.message,
        status: res.status
      }) : void 0;
    };
    return $.ajax({
      type: "POST",
      dataType: "json",
      url: endpoint,
      data: data,
      success: success
    });
  },
  exportFilterQR: function(preset, anonymousUser) {
    var saveQRCode;
    if (anonymousUser == null) {
      anonymousUser = false;
    }
    saveQRCode = function() {
      return ui.setDialog("qr_code", preset);
    };
    if (user.isLoggedIn() || anonymousUser || CONFIG.demoMode) {
      return saveQRCode();
    } else {
      return ui.setDialog("user", {
        name: "signin",
        success: saveQRCode
      });
    }
  },
  importFilterQR: function() {
    if (CONFIG.platform === "mobile") {
      return store.manager.sendMessage("ppe.qr.import");
    } else {
      return filesystem.readFile({
        multiple: false,
        accept: "image/*"
      }, (function(_this) {
        return function(data) {
          var crop;
          crop = [0.665, 0.665, 0.335, 0.335];
          return store.readThumbnail(data, {
            crop: crop
          }, function(data) {
            var qr;
            qr = new QrCode();
            qr.callback = function(result) {
              return actions.importFilterURL(result);
            };
            return qr.decode(data);
          });
        };
      })(this));
    }
  },
  scanFilter: function() {
    return store.manager.sendMessage("ppe.qr.begin");
  },
  importFilterURL: function(url) {
    if (url.indexOf("https://www.polarr.co") === 0 || url.indexOf("http://www.polaxiong.com") === 0) {
      ui.showCover(translate("status.importing_filter") + "...");
      return $.ajax({
        type: "GET",
        contentType: "json",
        url: url + "/filter",
        success: function(res) {
          var filter;
          if (res.status === "success") {
            filter = JSON.parse(res.data.filter_content);
            actions.importFilter(filter);
            ui.hideCover();
            return utils.trackEventGA("filter", "import_qr", url);
          } else {
            return ui.showCover(translate("status.import_filter_error"), 3000);
          }
        },
        error: function() {
          return ui.showCover(translate("status.server_offline"), 3000);
        }
      });
    } else {
      return ui.showCover(translate("status.import_filter_error"), 3000);
    }
  },
  importFilter: function(preset) {
    var customFilters, filter, j, len;
    customFilters = manager.state.customFilters.slice(0);
    for (j = 0, len = customFilters.length; j < len; j++) {
      filter = customFilters[j];
      if (filter.id === preset.id) {
        return;
      }
    }
    customFilters.unshift(preset);
    manager.set("customFilters", customFilters);
    manager.set("filter_tab", 1);
    return utils.trackEventGA("filter", "import", preset.name);
  },
  createFilter: function(opts) {
    var customFilters, filter, id, key, propsToSave, state, val;
    state = Filtr.getState(true);
    id = shortid.generate();
    propsToSave = {
      crop: opts.saveCrop,
      rotation: opts.saveCrop,
      text: opts.saveText,
      local_adjustments: opts.saveLocalAdjustments,
      distortion_mesh: false,
      faces: false
    };
    for (key in state) {
      val = state[key];
      if (propsToSave.hasOwnProperty(key)) {
        if (!propsToSave[key]) {
          delete state[key];
        }
      } else {
        if (!opts.saveAdjustments) {
          delete state[key];
        }
      }
    }
    filter = {
      name: encodeURIComponent(opts.name || opts.placeholder),
      id: id,
      state: state
    };
    customFilters = manager.state.customFilters.slice(0);
    customFilters.unshift(filter);
    ui.showPanel("looks");
    manager.set("customFilters", customFilters);
    manager.set("filter_tab", 1);
    utils.trackEventGA("filter", "create", filter.name);
    if (user.isLoggedIn()) {
      actions.exportFilter(filter);
    }
    return filter;
  },
  renameFilter: function(key, name) {
    var custom, favorite, favorites, fi, filter, i, id, j, len;
    custom = manager.state.customFilters.slice(0);
    favorites = manager.state.favoriteFilters.slice(0);
    filter = custom[key];
    id = filter.id;
    if (name) {
      name = encodeURIComponent(name);
      filter.name = name;
      actions.updateFilter(filter);
    } else {
      actions.deleteFilter(filter);
      custom.splice(key, 1);
    }
    fi = null;
    for (i = j = 0, len = favorites.length; j < len; i = ++j) {
      favorite = favorites[i];
      if (favorite.id === id) {
        fi = i;
      }
    }
    if (fi != null) {
      if (name) {
        favorites[fi].name = name;
      } else {
        favorites.splice(fi, 1);
      }
    }
    return manager.set({
      customFilters: custom,
      favoriteFilters: favorites
    });
  },
  updateThumbnails: function() {},
  renderThumbnail: (function() {
    var thumbnailCache;
    thumbnailCache = {};
    return function(previewState, options) {
      var curState, currentState, height, key, output, pixels, resize, size, state, val, width;
      if (previewState == null) {
        previewState = Filtr.getState();
      }
      size = options.size, width = options.width, height = options.height, resize = options.resize, currentState = options.currentState;
      if (size) {
        size *= Filtr.pixelRatio;
        width = height = size;
      } else {
        width *= Filtr.pixelRatio;
        height *= Filtr.pixelRatio;
      }
      curState = currentState || Filtr.getState();
      state = {};
      for (key in previewState) {
        val = previewState[key];
        state[key] = val;
      }
      if (!state.crop) {
        state.crop = curState.crop;
      }
      state.rotate90 = curState.rotate90;
      state.blur_opacity = 0;
      if (thumbnailCache.width === width && thumbnailCache.height === height) {
        pixels = thumbnailCache.pixels;
      }
      output = Filtr["export"]("data", {
        resize: resize || "fill",
        width: width,
        height: height,
        settings: state,
        pixels: pixels
      });
      if (output) {
        thumbnailCache = output;
      }
      return thumbnailCache;
    };
  })()
};

module.exports = actions;


},{"../filesystem.coffee":311,"../lib/qrcode-reader.js":316,"../manager.coffee":338,"../store.coffee":346,"../utils.coffee":360,"./purchases.coffee":233,"./ui.coffee":234,"./user.coffee":235,"counterpart":3,"jquery":319,"shortid":216}],233:[function(require,module,exports){
var actions, device, manager, ref, store, ui, user, utils,
  indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

manager = require("../manager.coffee");

utils = require("../utils.coffee");

store = require("../store.coffee");

ui = require("./ui.coffee");

user = require("./user.coffee");

device = require("./device.coffee");

if ((ref = store.manager) != null) {
  ref.waitForMessage("ppe.external.buy", function(res) {
    var userInfo;
    log("external.buy", res);
    userInfo = res.userInfo;
    if (userInfo.purchaseId) {
      return store.manager.sendMessage("ppe.payment.purchase", {
        purchase: userInfo.purchaseId
      }, (function(_this) {
        return function(res) {
          var purchases;
          if (res.success) {
            purchases = manager.state.purchases;
            purchases[userInfo.purchaseId] = true;
            return manager.set({
              purchases: purchases
            });
          }
        };
      })(this));
    } else {
      return ui.setDialog("purchase", true);
    }
  });
}

actions = {
  restoreLogin: function(prompt) {
    var options, success;
    success = function() {
      if (prompt) {
        if (actions.isSubscriptionPurchased()) {
          return ui.setDialog("user", {
            message: "dialogs.unlock_success"
          });
        } else {
          return ui.setDialog("user", {
            name: "unlock_failed"
          });
        }
      }
    };
    if (user.isLoggedIn()) {
      options = {
        id: manager.state.user.id,
        token: manager.state.user.session_token,
        done: success
      };
      return user.signinToken(options);
    } else if (prompt) {
      return ui.setDialog("user", {
        name: "signin",
        success: success
      });
    }
  },
  checkPurchases: function(callback) {
    var i, key, len, purchases, res;
    if (CONFIG.demoMode) {
      purchases = {};
      for (i = 0, len = PURCHASE_KEYS.length; i < len; i++) {
        key = PURCHASE_KEYS[i];
        purchases[key] = true;
      }
      res = {
        "purchases": purchases,
        "purchaseAvailable": false,
        "currencies": null
      };
      callback(res);
      return;
    }
    return store.manager.sendMessage("ppe.payment.status", {
      purchases: PURCHASE_KEYS
    }, function(res) {
      log("received: ppe.payment.status");
      if (CONFIG.os === "android") {
        return store.manager.sendMessage("ppe.payment.status.onetime", {
          purchases: PURCHASE_KEYS
        }, function(res_onetime) {
          var ref1, value;
          ref1 = res_onetime.purchases;
          for (key in ref1) {
            value = ref1[key];
            if (value === true) {
              res.purchases[key] = value;
            }
          }
          log("get payment merged status", res);
          return callback(res);
        });
      } else {
        return callback(res);
      }
    });
  },
  restoreAppState: function(callback) {
    var setPurchases;
    setPurchases = function(res) {
      var purchases;
      if (CONFIG.debugPurchases) {
        purchases = {};
      } else {
        purchases = actions.setPurchases(res.purchases);
      }
      manager.set({
        "purchases": purchases,
        "purchaseAvailable": res.purchaseAvailable,
        "currencies": res.currencies
      });
      return callback();
    };
    return manager.restoreState.then(function() {
      var purchases;
      splashScreen.hide();
      device.getDeviceID().then(function() {
        return actions.restoreLogin();
      });
      if (CONFIG.platform === "mobile" && (store.manager != null)) {
        device.setPerformance();
        actions.checkPurchases(setPurchases);
        store.manager.waitForMessage("ppe.embedded.activated", function() {
          return actions.checkPurchases(setPurchases);
        });
        store.manager.waitForMessage("ppe.payment.reset", function(res) {
          console.log("has account: ", res.data.hasAccount);
          if (res.data.hasAccount) {
            return actions.checkPurchases(setPurchases);
          } else {
            return manager.set({
              "purchases": {},
              "purchaseAvailable": false
            });
          }
        });
        return store.manager.sendMessage("ppe.external.notification.badge", {
          count: 0
        });
      } else {
        purchases = CONFIG.purchases || {};
        return setPurchases({
          purchases: purchases,
          purchaseAvailable: false
        });
      }
    });
  },
  isAnyPurchased: function() {
    var any, key, purchase, purchases;
    purchases = manager.state.purchases;
    any = false;
    for (key in purchases) {
      purchase = purchases[key];
      any = purchase || any;
    }
    return any;
  },
  isSubscriptionPurchased: function() {
    var purchases, userState;
    purchases = manager.state.purchases;
    userState = manager.state.user;
    return purchases[PURCHASE_KEYS[3]] || purchases[PURCHASE_KEYS[4]] || purchases[PURCHASE_KEYS[5]] || manager.state.subscription || userState.is_unlimited;
  },
  isProPurchased: function() {
    var purchases;
    purchases = manager.state.purchases;
    return purchases[PURCHASE_KEYS[0]] || purchases[PURCHASE_KEYS[1]] || actions.isSubscriptionPurchased();
  },
  isAllFiltersPurchased: function() {
    var i, individually, key, len, purchases;
    purchases = manager.state.purchases;
    individually = true;
    for (i = 0, len = FILTER_PACK_KEYS.length; i < len; i++) {
      key = FILTER_PACK_KEYS[i];
      if (!purchases[key]) {
        individually = false;
      }
    }
    return individually || purchases[PURCHASE_KEYS[1]] || purchases[PURCHASE_KEYS[2]] || actions.isSubscriptionPurchased();
  },
  isFilterPurchased: function(key) {
    var purchases;
    purchases = manager.state.purchases;
    return purchases[key] || purchases[PURCHASE_KEYS[1]] || purchases[PURCHASE_KEYS[2]] || actions.isSubscriptionPurchased();
  },
  isPurchased: function(key) {
    var all, i, len, purchases;
    purchases = manager.state.purchases;
    if (purchases[PURCHASE_KEYS[1]] || actions.isSubscriptionPurchased()) {
      return true;
    }
    if (key === PURCHASE_KEYS[2]) {
      all = true;
      for (i = 0, len = FILTER_PACK_KEYS.length; i < len; i++) {
        key = FILTER_PACK_KEYS[i];
        if (!purchases[key]) {
          all = false;
        }
      }
      return all || purchases[PURCHASE_KEYS[2]];
    }
    if (indexOf.call(FILTER_PACK_KEYS, key) >= 0) {
      return purchases[key] || purchases[PURCHASE_KEYS[2]];
    }
    return purchases[key] || purchases[PURCHASE_KEYS[0]];
  },
  setPurchases: function(purchases) {
    var key, val;
    if (purchases == null) {
      purchases = {};
    }
    for (key in purchases) {
      val = purchases[key];
      if (val) {
        manager.state.purchases[key] = true;
      }
    }
    return manager.state.purchases;
  },
  restorePurchases: function() {
    return ui.setDialog("purchase", "restore");
  },
  unlockPurchase: function(id) {
    var purchase, purchases;
    purchase = {};
    purchase[id] = true;
    purchases = utils.assign(manager.state.purchases, purchase);
    return manager.set("purchases", purchases);
  },
  unlockAll: function() {
    var purchases;
    purchases = actions.setPurchases({
      "co.polarr.ppe.purchase.pro_all_filters": true
    });
    return manager.set("purchases", purchases);
  },
  purchaseId: function(id, callback) {
    var ref1;
    if (utils.isChina() && (CONFIG.os === "android")) {
      ui.setDialog("user", {
        name: "purchase_china"
      });
      if (typeof callback === "function") {
        callback();
      }
    } else if (CONFIG.platform === "mobile") {
      if (CONFIG.embedded) {
        store.manager.sendMessage("ppe.external.buy", {
          userInfo: {
            purchaseId: id
          }
        });
        if (typeof callback === "function") {
          callback();
        }
      } else {
        if ((ref1 = store.manager) != null) {
          ref1.sendMessage("ppe.payment.purchase", {
            purchase: id
          }, (function(_this) {
            return function(res) {
              if (res.success) {
                actions.unlockPurchase(id);
              }
              return typeof callback === "function" ? callback(res) : void 0;
            };
          })(this));
        }
      }
    } else {
      actions.openStore();
      if (typeof callback === "function") {
        callback();
      }
    }
    return utils.trackEventGA("purchase_attempt", id);
  },
  openStore: function(type) {
    switch (type || CONFIG.platform) {
      case "windows":
        return utils.openURL((utils.getUSUrlRoot()) + "/analytics/redirect?platform=polarr&label=photo_editor&redirect_url=https://www.microsoft.com/en-us/store/apps/polarr-photo-editor/9nblggh1npnc");
      case "osx":
        return utils.openURL((utils.getUSUrlRoot()) + "/analytics/redirect?platform=polarr&label=photo_editor&redirect_url=https://itunes.apple.com/us/app/polarr-photo-editor/id1058273036?mt=12");
      case "ios":
        return utils.openURL((utils.getUSUrlRoot()) + "/analytics/redirect?platform=polarr&label=photo_editor&redirect_url=https://itunes.apple.com/us/app/polarr-photo-editor-professional/id988173374?mt=8");
      case "chrome":
        return utils.openURL((utils.getUrlRoot()) + "/chrome?platform=polarr&label=photo_editor");
      default:
        return utils.openURL((utils.getUrlRoot(true)) + "/web?platform=polarr&label=photo_editor");
    }
  },
  getPrice: function(id) {
    var currency, ref1;
    currency = (ref1 = manager.state.currencies) != null ? ref1[id] : void 0;
    if (currency) {
      return currency.formattedPrice;
    } else {
      return "";
    }
  }
};

module.exports = actions;


},{"../manager.coffee":338,"../store.coffee":346,"../utils.coffee":360,"./device.coffee":230,"./ui.coffee":234,"./user.coffee":235}],234:[function(require,module,exports){
var actions, manager, store, translate, tutorials, utils;

manager = require("../manager.coffee");

store = require("../store.coffee");

utils = require("../utils.coffee");

tutorials = require("../tutorials.coffee");

translate = require("counterpart");

manager.on("photo_status", function(photo) {
  var message, show;
  show = CONFIG.platform === "electron" ? actions.showStatus : actions.showCover;
  switch (photo.status) {
    case "import_limit":
      actions.showCover(translate("status.import_limit").replace("50", CONFIG.importLimit));
      actions.showProgress(null);
      actions.showPanel("filmstrip");
      return setTimeout(function() {
        return actions.hideCover();
      }, 5000);
    case "loading":
      return show(translate("status.loading_photo"));
    case "loaded":
      actions.hideCover();
      return actions.hideStatus();
    case "importing":
      message = photo.progress != null ? "status.importing_photos" : "status.importing_photo";
      actions.showCover(translate(message));
      if (photo.progress != null) {
        return actions.showProgress(photo.progress);
      }
      break;
    case "exporting":
      actions.showCover(translate("status.saving_photo"));
      if (photo.progress != null) {
        return actions.showProgress(photo.progress);
      }
      break;
    case "error":
      actions.showCover(translate(photo.message), 2000);
      return actions.showProgress(null);
  }
});

actions = {
  hideUI: function() {
    var ui;
    ui = document.querySelector(".toolbars");
    ui.style.opacity = 0;
    return ui.style.pointerEvents = "none";
  },
  showUI: function() {
    var ui;
    ui = document.querySelector(".toolbars");
    ui.style.opacity = 1;
    return ui.style.pointerEvents = "";
  },
  switchPhoto: function(delta) {
    var index, photo;
    index = store.photos.indexOf(store.currentPhoto);
    index = utils.clamp(index + delta, 0, store.photos.length - 1);
    photo = store.photos[index];
    return store.loadPhoto(photo);
  },
  showPanel: function(panel) {
    var toolbars;
    toolbars = manager.state.toolbars;
    if (toolbars.left.indexOf(panel) >= 0) {
      toolbars.leftActive = panel;
    } else if (toolbars.right.indexOf(panel) >= 0) {
      toolbars.rightActive = panel;
    }
    return manager.set("toolbars", toolbars);
  },
  alert: function(options) {
    return manager.emit("alert", options);
  },
  setDialog: function(dialog, value) {
    var obj;
    obj = {};
    obj[dialog] = value;
    return manager.set("dialog", obj);
  },
  showStatus: function(message, timeout) {
    return manager.set("status", {
      timeout: timeout,
      message: message,
      active: true
    });
  },
  hideStatus: function() {
    return manager.set("status", {
      active: false
    });
  },
  showCover: function(message, timeout) {
    if (CONFIG.embedded && CONFIG.platform === "mobile") {
      return;
    }
    return manager.set("cover", {
      timeout: timeout,
      message: message,
      active: true
    });
  },
  hideCover: function() {
    return manager.set("cover", {
      progress: null,
      active: false
    });
  },
  showProgress: function(progress) {
    var cover;
    if (CONFIG.embedded && CONFIG.platform === "mobile") {
      return;
    }
    cover = manager.state.cover || {};
    cover.active = true;
    cover.progress = progress;
    return manager.set("cover", cover);
  },
  showSettings: function() {
    return actions.setDialog("settings", true);
  },
  showGuide: function(index) {
    return manager.set("tutorial", tutorials[index]);
  }
};

module.exports = actions;


},{"../manager.coffee":338,"../store.coffee":346,"../tutorials.coffee":348,"../utils.coffee":360,"counterpart":3}],235:[function(require,module,exports){
var $, actions, digestResponse, error, isProPurchased, manager, onSuccess, responseCodes, setUser, store, ui, unlockAllFeatures, unwatchPurchases, userState, utils;

$ = require("jquery");

manager = require("../manager.coffee");

utils = require("../utils.coffee");

store = require("../store.coffee");

ui = require("./ui.coffee");

userState = manager.state.user;

responseCodes = {
  "vdm": "field_invalid",
  "vdb": "field_invalid",
  "u0": "user_exists",
  "u2": "invalid_credentials",
  "u3": "invalid_credentials",
  "u4": "account_suspended",
  "u5": "wrong_token",
  "u8": "email_reset_failed"
};

error = function(res) {
  var dialog, message, status;
  dialog = manager.state.dialog.user;
  if (!dialog) {
    return;
  }
  status = "error";
  message = "status.server_offline";
  return ui.setDialog("user", {
    name: dialog.name,
    status: status,
    message: message
  });
};

onSuccess = null;

unwatchPurchases = null;

digestResponse = function(res, successMessage) {
  var dialog, invalid_field, message, ref, status;
  dialog = (ref = manager.state.dialog.user) != null ? ref.name : void 0;
  status = res.status;
  if (typeof unwatchPurchases === "function") {
    unwatchPurchases();
  }
  if (res.code === "ok") {
    if (res.verifyAccount && !res.data.verified_email) {
      ui.setDialog("user", {
        name: dialog,
        status: status,
        invalid_field: null,
        verification_state: 0
      });
      return;
    }
    dialog = "success";
    invalid_field = null;
    message = successMessage;
    setUser(res.data);
    if (userState.is_unlimited) {
      unlockAllFeatures();
    } else if (isProPurchased()) {
      actions.setUnlimitedUser(1);
    } else {
      unwatchPurchases = manager.on('purchases', function() {
        if (isProPurchased()) {
          return actions.setUnlimitedUser(1);
        }
      });
    }
    if (typeof onSuccess === "function") {
      onSuccess(userState);
    }
    onSuccess = null;
  } else {
    invalid_field = res.message.split(" ")[0];
    message = "status." + responseCodes[res.code];
    actions.signout();
  }
  if (manager.state.dialog.user != null) {
    if (userState.is_unlimited) {
      return ui.setDialog("user", {
        name: "success",
        status: "success",
        message: "dialogs.unlock_success"
      });
    } else if (message) {
      return ui.setDialog("user", {
        name: dialog,
        status: status,
        message: message,
        invalid_field: invalid_field,
        success: onSuccess
      });
    } else {
      return ui.setDialog(null);
    }
  }
};

unlockAllFeatures = function() {
  var purchase, purchases;
  purchase = {
    "co.polarr.ppe.purchase.pro_all_filters": true
  };
  purchases = utils.assign(manager.state.purchases, purchase);
  return manager.set("purchases", purchases);
};

isProPurchased = function() {
  var purchases;
  purchases = manager.state.purchases;
  return purchases[PURCHASE_KEYS[0]] || purchases[PURCHASE_KEYS[1]] || purchases[PURCHASE_KEYS[3]] || purchases[PURCHASE_KEYS[4]] || purchases[PURCHASE_KEYS[5]] || manager.state.subscription;
};

setUser = function(user) {
  if (user.session_token) {
    userState.session_token = user.session_token;
    userState.id = parseInt(user.user_id, 10);
    userState.picture = user.profile_picture_url;
    userState.user_name = user.user_name;
    userState.email = user.email;
    userState.verified_email = user.verified_email;
    userState.is_unlimited = !!(user.is_unlimited_user || user.ppe_unlimited_user);
  }
  return manager.set("user", userState);
};

actions = {
  isLoggedIn: function() {
    return !!manager.state.user.session_token;
  },
  setUnlimitedUser: function(unlimited) {
    return $.ajax({
      type: "POST",
      dataType: "json",
      url: (utils.getUrlRoot()) + "/users/signin_id_token",
      data: {
        user_id: userState.id,
        session_token: userState.session_token,
        ppe_unlimited_user: unlimited,
        product: "ppe_" + CONFIG.os
      },
      success: (function(_this) {
        return function(res) {
          if (res.code === "ok") {
            return setUser(res.data);
          }
        };
      })(this)
    });
  },
  signinToken: function(options) {
    var done, id, token;
    id = options.id, token = options.token, done = options.done;
    onSuccess = done;
    return $.ajax({
      type: "POST",
      dataType: "json",
      url: (utils.getUrlRoot()) + "/users/signin_id_token",
      data: {
        user_id: id,
        session_token: token,
        product: "ppe_" + CONFIG.os
      },
      success: (function(_this) {
        return function(res) {
          return digestResponse(res);
        };
      })(this),
      error: (function(_this) {
        return function(res) {
          return error(res);
        };
      })(this)
    });
  },
  signin: function(options) {
    var done, email, password;
    email = options.email, password = options.password, done = options.done;
    onSuccess = done;
    return $.ajax({
      type: "POST",
      dataType: "json",
      url: (utils.getUrlRoot()) + "/users/signin_email_pwd",
      data: {
        email: email,
        password: password,
        product: "ppe_" + CONFIG.os,
        device_id: manager.state.deviceID,
        ppe_unlimited_user: isProPurchased()
      },
      success: (function(_this) {
        return function(res) {
          res.verifyAccount = true;
          return digestResponse(res);
        };
      })(this),
      error: (function(_this) {
        return function(res) {
          return error(res);
        };
      })(this)
    });
  },
  signup: function(options) {
    var done, email, password, successMessage, user_name;
    user_name = options.user_name, email = options.email, password = options.password, done = options.done;
    onSuccess = done;
    successMessage = "status.registration_success";
    return $.ajax({
      type: "POST",
      dataType: "json",
      url: (utils.getUrlRoot()) + "/users/create",
      data: {
        user_name: user_name,
        email: email,
        password: password,
        product: "ppe_" + CONFIG.os,
        device_id: manager.state.deviceID,
        ppe_unlimited_user: isProPurchased()
      },
      success: (function(_this) {
        return function(res) {
          return digestResponse(res, successMessage);
        };
      })(this),
      error: (function(_this) {
        return function(res) {
          return error(res);
        };
      })(this)
    });
  },
  resetPassword: function(options) {
    var done, email, successMessage;
    email = options.email, done = options.done;
    onSuccess = done;
    successMessage = "status.email_reset";
    return $.ajax({
      type: "POST",
      dataType: "json",
      url: (utils.getUrlRoot()) + "/users/forget_password",
      data: {
        email: email,
        product: "ppe_" + CONFIG.os
      },
      success: (function(_this) {
        return function(res) {
          return digestResponse(res, successMessage);
        };
      })(this),
      error: (function(_this) {
        return function(res) {
          return error(res);
        };
      })(this)
    });
  },
  subscribeToUpdates: function(options) {
    var email;
    email = options.email;
    return $.ajax({
      type: "POST",
      url: (utils.getUrlRoot()) + "/users/add_to_alpha",
      crossDomain: true,
      data: {
        email: email,
        product: "ppe_" + CONFIG.os
      },
      success: (function(_this) {
        return function(res) {
          var dialog, message, status;
          dialog = manager.state.dialog.user;
          if (!dialog) {
            return;
          }
          status = res.status;
          message = "dialogs.get_updates_thankyou";
          return ui.setDialog("user", {
            name: "success",
            status: status,
            message: message
          });
        };
      })(this),
      error: (function(_this) {
        return function(res) {
          return error(res);
        };
      })(this)
    });
  },
  sendVerificationEmail: function(options) {
    var email;
    email = options.email;
    return $.ajax({
      type: "POST",
      url: (utils.getUrlRoot()) + "/users/resend_email_verification",
      crossDomain: true,
      data: {
        email: email,
        product: "ppe_" + CONFIG.os
      },
      success: (function(_this) {
        return function(res) {
          var dialog, status;
          dialog = manager.state.dialog.user;
          if (!dialog) {
            return;
          }
          status = res.status;
          return ui.setDialog("user", {
            name: dialog.name,
            status: status,
            verification_state: 1
          });
        };
      })(this),
      error: (function(_this) {
        return function(res) {
          return error(res);
        };
      })(this)
    });
  },
  signout: function() {
    userState.id = null;
    userState.session_token = null;
    userState.picture = null;
    userState.user_name = null;
    userState.email = null;
    userState.is_unlimited = false;
    userState.credits = 0;
    return manager.set("user", userState);
  },
  setUser: function(user) {
    userState = utils.extend(userState, user);
    return manager.set("user", userState);
  }
};

module.exports = actions;


},{"../manager.coffee":338,"../store.coffee":346,"../utils.coffee":360,"./ui.coffee":234,"jquery":319}],236:[function(require,module,exports){
(function (React,ReactDOM){
var $, Adjustments, AdjustmentsPanel, Button, ColorPicker, FeatureBlock, Icon, Menu, Panel, PanelHeader, PointerEvents, Scrollable, Slider, SliderPad, Swatches, actions, colorMap, keyboard, manager, mixins, store, translate, utils;

translate = require("counterpart");

$ = require('jquery');

Button = require("./button.coffee");

Icon = require("./icon.coffee");

Slider = require("./slider.coffee");

SliderPad = require("./slider-pad.coffee");

ColorPicker = require("./color-picker.coffee");

Menu = require("./popover-menu.coffee");

FeatureBlock = require("./feature-block.coffee");

Scrollable = require("./scrollable.coffee");

Panel = require("./panel.coffee");

PointerEvents = require("./pointer-events.coffee");

Swatches = require("./swatches.coffee");

store = require("../store.coffee");

utils = require("../utils.coffee");

keyboard = require("../keyboard.coffee");

mixins = require("../mixins.coffee");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

colorMap = {
  'red': 0,
  'orange': 30 / 360,
  'yellow': 50 / 360,
  'green': 90 / 360,
  'aqua': 180 / 360,
  'blue': 240 / 360,
  'purple': 270 / 360,
  'magenta': 300 / 360
};

Adjustments = React.createClass({displayName: "Adjustments",
  mixins: [require("react-addons-pure-render-mixin"), mixins.touchRipple],
  componentDidMount: function() {
    Filtr.on("history", (function(_this) {
      return function() {
        return _this.setState(Filtr.getState());
      };
    })(this));
    manager.on("adjustment_tab", (function(_this) {
      return function(val, scrollTo) {
        _this.expandPanel(val, scrollTo);
        if (val !== 5) {
          return _this.showCurve(null);
        }
      };
    })(this));
    manager.on("curves_tab", (function(_this) {
      return function(val) {
        return _this.setState({
          curvesTab: val
        });
      };
    })(this));
    manager.on("hsl_tab", (function(_this) {
      return function(val) {
        return _this.setState({
          hslTab: val
        });
      };
    })(this));
    manager.on("copiedAdjustments", (function(_this) {
      return function(copy) {
        return _this.setState({
          copiedAdjustments: copy
        });
      };
    })(this));
    return this.expandPanel(-1);
  },
  getInitialState: function() {
    var state;
    state = Filtr.getState(true);
    state.panels = manager.state.adjustmentPanels;
    state.curvesTab = manager.state.curves_tab;
    state.hslTab = manager.state.hsl_tab;
    return state;
  },
  handleScroll: function(e) {
    var menu;
    menu = this.refs["reset-menu"];
    if (menu.state.active) {
      return menu.hide();
    }
  },
  showResetMenu: function(e) {
    var el;
    el = $(e.target).closest(".panel-header")[0];
    if (!el) {
      return;
    }
    this.currentPanel = el.dataset["panel"];
    if (!this.currentPanel) {
      return;
    }
    return this.refs["reset-menu"].show({
      target: el,
      offset: [8, 0]
    });
  },
  showTab: function(panel, tab) {
    return manager.set(panel + "_tab", tab);
  },
  showCurve: function(curve) {
    if (this.state.curvesTab === curve) {
      curve = null;
    }
    return this.showTab("curves", curve);
  },
  setHighlights: function(color) {
    var hue;
    hue = colorMap[color];
    Filtr.setState({
      highlights_hue: hue,
      highlights_saturation: 0.5
    });
    return Filtr.saveState("labels.highlights labels.color", color);
  },
  setShadows: function(color) {
    var hue;
    hue = colorMap[color];
    Filtr.setState({
      shadows_hue: hue,
      shadows_saturation: 0.5
    });
    return Filtr.saveState("labels.shadows labels.color", color);
  },
  tapPanel: function(e) {
    var el, index, panel;
    el = $(e.target).closest(".panel-header")[0];
    if (!el) {
      return;
    }
    document.activeElement.blur();
    panel = el.dataset.panel;
    index = parseInt(el.dataset.index);
    if (index === manager.state["adjustment_tab"]) {
      index = -1;
    }
    this.currentPanel = panel;
    return manager.set("adjustment_tab", index);
  },
  setY: function(el, y, duration) {
    var transform;
    transform = "translate(0, " + y + "px)";
    el.style.transition = "transform " + duration + "ms ease-out";
    el.style.webkitTransform = transform;
    return el.style.transform = transform;
  },
  showPanel: function(panel, i) {
    var panelInner;
    clearTimeout(this["hidePanelTimeout" + i]);
    panelInner = panel.children[panel.children.length - 1];
    panelInner.style.opacity = 1;
    return panelInner.style.visibility = "visible";
  },
  hidePanel: function(panel, i) {
    var panelInner;
    panelInner = panel.children[panel.children.length - 1];
    panelInner.style.opacity = 0;
    return this["hidePanelTimeout" + i] = setTimeout(function() {
      return panelInner.style.visibility = "hidden";
    }, 200);
  },
  expandPanel: function(index, scrollToSelector) {
    var accordion, container, containerHeight, curHeight, duration, headerHeight, height, heights, i, j, k, len, len1, minHeight, ms, name, offsetEl, panel, ref1, ref2, scrollPos, scrollTo, totalHeight;
    if (index == null) {
      index = manager.state.adjustment_tab;
    }
    container = ReactDOM.findDOMNode(this.refs["scrollable"]);
    accordion = this.refs["accordion"];
    scrollPos = container.scrollTop;
    containerHeight = container.clientHeight;
    curHeight = accordion.clientHeight;
    headerHeight = 45;
    totalHeight = 0;
    duration = 300;
    ms = 0;
    scrollTo = 0;
    heights = [];
    minHeight = 0;
    ref1 = accordion.children;
    for (j = 0, len = ref1.length; j < len; j++) {
      panel = ref1[j];
      height = panel.clientHeight;
      heights.push(height);
      if (panel.classList.contains("fixed")) {
        minHeight += height;
      } else {
        minHeight += headerHeight;
      }
    }
    ref2 = accordion.children;
    for (i = k = 0, len1 = ref2.length; k < len1; i = ++k) {
      panel = ref2[i];
      this.setY(panel, totalHeight, duration);
      if (i === index) {
        scrollTo = totalHeight;
        name = panel.children[0].dataset.panel;
        manager.emit("show_adjustment_panel", name);
      }
      if (scrollToSelector) {
        offsetEl = panel.querySelector(scrollToSelector);
        if (offsetEl) {
          scrollTo = offsetEl.offsetTop + offsetEl.clientHeight / 2 + totalHeight - containerHeight / 2;
        }
      }
      if (i === index || panel.classList.contains("fixed")) {
        totalHeight += heights[i];
        this.showPanel(panel, i);
      } else {
        totalHeight += headerHeight;
        this.hidePanel(panel, i);
      }
    }
    if (totalHeight === minHeight) {
      ms = duration;
      scrollTo = scrollPos;
    } else if (totalHeight < curHeight) {
      ms = duration;
    }
    scrollTo = utils.clamp(scrollTo, 0, totalHeight - containerHeight);
    if (typeof this.cancelAnimation === "function") {
      this.cancelAnimation();
    }
    this.cancelAnimation = Filtr.utils.animate([scrollPos], [scrollTo], 0.3, function(val) {
      return container.scrollTop = val[0];
    });
    clearTimeout(this.expandPanelTimeout);
    return this.expandPanelTimeout = setTimeout((function(_this) {
      return function() {
        var ref, ref3;
        accordion.style.height = totalHeight + "px";
        ref = (ref3 = container.querySelector(scrollToSelector)) != null ? ref3.dataset.name : void 0;
        if (ref) {
          return _this.refs[ref + "-slider"].activate();
        }
      };
    })(this), ms);
  },
  autoAdjust: function() {
    return actions.autoAdjust();
  },
  resetAll: function() {
    return actions.resetMainAdjustments();
  },
  copyEdit: function() {
    return actions.copyAdjustments();
  },
  pasteEdit: function() {
    return actions.pasteAdjustments();
  },
  resetPanel: function(panel) {
    return actions.resetAdjustmentsGroup(panel);
  },
  resetCurrentPanel: function() {
    return this.resetPanel(this.currentPanel);
  },
  showAdjustmentsGuide: function() {
    return manager.emit("tutorial_id", "adjustment_guide");
  },
  onLongPress: function(e) {
    return this.showResetMenu(e);
  },
  onTap: function(e) {
    return this.tapPanel(e);
  },
  onPointerDown: function(e) {
    var el;
    el = $(e.target).closest(".panel-header")[0];
    if (el) {
      return this.touchRippleStart(e, el);
    }
  },
  Slider: function(opts) {
    var SliderControl;
    if (opts == null) {
      opts = {};
    }
    SliderControl = CONFIG.platform === "mobile" ? SliderPad : Slider;
    return React.createElement(SliderControl, {
      tabIndex: this.tabIndex++, 
      label: opts.label, 
      prop: opts.name, 
      tempSettings: opts.temp, 
      value: this.state[opts.name], 
      onUpdate: Filtr.setState, 
      rotated: this.props.rotated, 
      group: opts.group || "", 
      mul: (opts.mul != null) ? opts.mul : 100, 
      min: (opts.min != null) ? opts.min : -1, 
      max: (opts.max != null) ? opts.max : 1, 
      ref: opts.name + "-slider", 
      position: this.props.position}
    );
  },
  ColorPicker: function(name) {
    return React.createElement(ColorPicker, {
      tabIndex: this.tabIndex++, 
      name: "labels." + name, 
      value: [this.state[name + "_hue"], this.state[name + "_saturation"]], 
      rotated: this.props.rotated, 
      update: function(val){
        obj = {}
        obj[name+"_hue"] = val[0]
        obj[name+"_saturation"] = val[1]
        Filtr.setState(obj)
      }, 
      position: this.props.position}
    );
  },
  render: function() {
    var curvesTab, hslTab, panelIndex, topPanel;
    this.tabIndex = 1;
    hslTab = this.state.hslTab;
    curvesTab = this.state.curvesTab;
    panelIndex = 0;
    if (CONFIG.platform === "mobile") {
      topPanel = 
        React.createElement("div", {className: "accordion-panel fixed top"}, 
          React.createElement("div", {"data-index": panelIndex++, className: "panel-header"}), 
          React.createElement("div", {className: "panel-inner"}, 
            React.createElement(Button, {onTap: this.autoAdjust}, React.createElement("i", {className: "icon-wand"}), translate("labels.auto"))
          )
        )
      ;
    }
    return (
    React.createElement(PointerEvents, {
      onPointerDown: this.onPointerDown, 
      onTap: this.onTap, 
      onLongPress: this.onLongPress}, 
      React.createElement(Scrollable, {ref: "scrollable", rotated: this.props.rotated, onScroll: this.handleScroll}, 
        React.createElement(Menu, {ref: "reset-menu"}, 
          React.createElement("a", {onTouchTap: this.resetCurrentPanel}, translate("buttons.reset"))
        ), 

        React.createElement("div", {className: "accordion", ref: "accordion"}, 

          topPanel, 

          React.createElement("div", {className: "accordion-panel"}, 
            React.createElement(PanelHeader, {index: panelIndex++, name: "color"}), 
            React.createElement("div", {className: "panel-inner"}, 
               this.Slider({label: "labels.temperature", name: "temperature"}), 
               this.Slider({label: "labels.tint", name: "tint"}), 
               this.Slider({label: "labels.vibrance", name: "vibrance"}), 
               this.Slider({label: "labels.saturation", name: "saturation"}) 
            )
          ), 

          React.createElement("div", {className: "accordion-panel"}, 
            React.createElement(PanelHeader, {index: panelIndex++, name: "light"}), 
            React.createElement("div", {className: "panel-inner"}, 
               this.Slider({label: "labels.dehaze", name: "dehaze"}), 
               this.Slider({label: "labels.exposure", name: "exposure"}), 
               this.Slider({label: "labels.brightness", name: "gamma"}), 
               this.Slider({label: "labels.contrast", name: "contrast"}), 
              React.createElement("div", {className: "sep"}), 
               this.Slider({label: "labels.highlights", name: "highlights"}), 
               this.Slider({label: "labels.shadows", name: "shadows"}), 
               this.Slider({label: "labels.whites", name: "whites"}), 
               this.Slider({label: "labels.blacks", name: "blacks"}) 
            )
          ), 

          React.createElement("div", {className: "accordion-panel"}, 
            React.createElement(PanelHeader, {index: panelIndex++, name: "detail"}), 
            React.createElement("div", {className: "panel-inner"}, 
               this.Slider({label: "labels.clarity", name: "clarity"}), 
               this.Slider({label: "labels.sharpen", name: "sharpen", min: 0}), 
              React.createElement("h3", null, translate("labels.denoise")), 
              React.createElement("div", {className: "special"}, 
                 this.Slider({label: "labels.color", name: "color_denoise", group: "labels.denoise", min: 0}), 
                 this.Slider({label: "labels.luminance", name: "luminance_denoise", group: "labels.denoise", min: 0}), 
                React.createElement(FeatureBlock, {container: "adjustments", item: "denoise", bypass: true})
              )
            )
          ), 

          React.createElement("div", {className: "accordion-panel"}, 
            React.createElement(PanelHeader, {index: panelIndex++, name: "vignette"}), 
            React.createElement("div", {className: "panel-inner"}, 
               this.Slider({label: "labels.amount", name: "vignette_amount", group: "labels.vignette"}), 
               this.Slider({label: "labels.feather", name: "vignette_feather", group: "labels.vignette", min: 0}), 
               this.Slider({label: "labels.highlights", name: "vignette_highlights", group: "labels.vignette", min: 0}), 
               this.Slider({label: "labels.size", name: "vignette_size", group: "labels.vignette", min: 0}), 
               this.Slider({label: "labels.roundness", name: "vignette_roundness", group: "labels.vignette"}) 
            )
          ), 

          React.createElement("div", {className: "accordion-panel"}, 
            React.createElement(PanelHeader, {index: panelIndex++, name: "hsl"}), 
            React.createElement("div", {className: "panel-inner"}, 
              React.createElement(Swatches, {ref: "swatches", onSelect: this.showTab.bind(this, "hsl"), activeSwatch: hslTab}), 
              React.createElement("div", {className: hslTab === 'red' ? "tab-container active" : "tab-container"}, 
                 this.Slider({label: "labels.hue", name: "hue_red", group: "labels.red"}), 
                 this.Slider({label: "labels.saturation", name: "saturation_red", group: "labels.red"}), 
                 this.Slider({label: "labels.luminance", name: "luminance_red", group: "labels.red"}) 
              ), 
              React.createElement("div", {className: hslTab === 'orange' ? "tab-container active" : "tab-container"}, 
                 this.Slider({label: "labels.hue", name: "hue_orange", group: "labels.orange"}), 
                 this.Slider({label: "labels.saturation", name: "saturation_orange", group: "labels.orange"}), 
                 this.Slider({label: "labels.luminance", name: "luminance_orange", group: "labels.orange"}) 
              ), 
              React.createElement("div", {className: hslTab === 'yellow' ? "tab-container active" : "tab-container"}, 
                 this.Slider({label: "labels.hue", name: "hue_yellow", group: "labels.yellow"}), 
                 this.Slider({label: "labels.saturation", name: "saturation_yellow", group: "labels.yellow"}), 
                 this.Slider({label: "labels.luminance", name: "luminance_yellow", group: "labels.yellow"}) 
              ), 
              React.createElement("div", {className: hslTab === 'green' ? "tab-container active" : "tab-container"}, 
                 this.Slider({label: "labels.hue", name: "hue_green", group: "labels.green"}), 
                 this.Slider({label: "labels.saturation", name: "saturation_green", group: "labels.green"}), 
                 this.Slider({label: "labels.luminance", name: "luminance_green", group: "labels.green"}) 
              ), 
              React.createElement("div", {className: hslTab === 'aqua' ? "tab-container active" : "tab-container"}, 
                 this.Slider({label: "labels.hue", name: "hue_aqua", group: "labels.aqua"}), 
                 this.Slider({label: "labels.saturation", name: "saturation_aqua", group: "labels.aqua"}), 
                 this.Slider({label: "labels.luminance", name: "luminance_aqua", group: "labels.aqua"}) 
              ), 
              React.createElement("div", {className: hslTab === 'blue' ? "tab-container active" : "tab-container"}, 
                 this.Slider({label: "labels.hue", name: "hue_blue", group: "labels.blue"}), 
                 this.Slider({label: "labels.saturation", name: "saturation_blue", group: "labels.blue"}), 
                 this.Slider({label: "labels.luminance", name: "luminance_blue", group: "labels.blue"}) 
              ), 
              React.createElement("div", {className: hslTab === 'purple' ? "tab-container active" : "tab-container"}, 
                 this.Slider({label: "labels.hue", name: "hue_purple", group: "labels.purple"}), 
                 this.Slider({label: "labels.saturation", name: "saturation_purple", group: "labels.purple"}), 
                 this.Slider({label: "labels.luminance", name: "luminance_purple", group: "labels.purple"}) 
              ), 
              React.createElement("div", {className: hslTab === 'magenta' ? "tab-container active" : "tab-container"}, 
                 this.Slider({label: "labels.hue", name: "hue_magenta", group: "labels.magenta"}), 
                 this.Slider({label: "labels.saturation", name: "saturation_magenta", group: "labels.magenta"}), 
                 this.Slider({label: "labels.luminance", name: "luminance_magenta", group: "labels.magenta"}) 
              )
            )
          ), 

          React.createElement("div", {className: "accordion-panel"}, 
            React.createElement(PanelHeader, {index: panelIndex++, name: "curves"}), 
            React.createElement("div", {className: "panel-inner"}, 
              React.createElement("div", {className: "curves-swatches"}, 
                React.createElement("div", {className: curvesTab === "all" ? "active" : "", onTouchTap: this.showCurve.bind(this, "all"), onClick: this.showCurve.bind(this, "all"), style: {backgroundColor: 'hsl(0, 0%, 85%)'}}), 
                React.createElement("div", {className: curvesTab === "red" ? "active" : "", onTouchTap: this.showCurve.bind(this, "red"), onClick: this.showCurve.bind(this, "red"), style: {backgroundColor: 'hsl(0, 70%, 55%)'}}), 
                React.createElement("div", {className: curvesTab === "green" ? "active" : "", onTouchTap: this.showCurve.bind(this, "green"), onClick: this.showCurve.bind(this, "green"), style: {backgroundColor: 'hsl(90, 70%, 50%)'}}), 
                React.createElement("div", {className: curvesTab === "blue" ? "active" : "", onTouchTap: this.showCurve.bind(this, "blue"), onClick: this.showCurve.bind(this, "blue"), style: {backgroundColor: 'hsl(220, 70%, 55%)'}})
              )
            )
          ), 

          React.createElement("div", {className: "accordion-panel"}, 
            React.createElement(PanelHeader, {index: panelIndex++, name: "toning"}), 
            React.createElement("div", {className: "panel-inner"}, 
              React.createElement(Swatches, {onSelect: this.setShadows, type: "shadows"}), 
               this.ColorPicker("shadows"), 
              React.createElement("div", {className: "sep"}), 
              React.createElement(Swatches, {onSelect: this.setHighlights, type: "highlights"}), 
               this.ColorPicker("highlights"), 
               this.Slider({label: "labels.balance", name: "balance", group: "labels.split_tone"}) 
            )
          ), 

          React.createElement("div", {className: "accordion-panel"}, 
            React.createElement(PanelHeader, {index: panelIndex++, name: "distortion"}), 
            React.createElement("div", {className: "panel-inner"}, 
               this.Slider({label: "labels.lens_distortion", name: "distortion_amount", temp:{grid_size:[9,9]}}), 
               this.Slider({label: "labels.distortion_horizontal", name: "distortion_horizontal", temp:{grid_size:[9,9]}}), 
               this.Slider({label: "labels.distortion_vertical", name: "distortion_vertical", temp:{grid_size:[9,9]}}) 
            )
          ), 

          React.createElement("div", {className: "accordion-panel"}, 
            React.createElement(PanelHeader, {index: panelIndex++, name: "effects"}), 
            React.createElement("div", {className: "panel-inner"}, 
               this.Slider({label: "labels.fringing", name: "fringing"}), 
               this.Slider({label: "labels.mosaic", name: "mosaic_size", min: 0}), 
               this.Slider({label: "labels.diffuse", name: "diffuse", min: 0}), 
              React.createElement("h3", null, translate("labels.grain")), 
               this.Slider({label: "labels.amount", name: "grain_amount", group: "labels.grain", min: 0}), 
               this.Slider({label: "labels.size", name: "grain_size", group: "labels.grain", min: 0}) 
            )
          ), 

          React.createElement("div", {className: "accordion-panel fixed"}, 
            React.createElement("div", {className: "panel-header"}), 
            React.createElement("div", {className: "panel-inner"}, 
              React.createElement(Button, {onTap: this.copyEdit}, 
                React.createElement("i", {className: "icon-copy"}), 
                translate("menus.copy_edit"), 
                keyboard.renderShortcut("copy_edit")
              ), 
              React.createElement(Button, {onTap: this.pasteEdit, disabled: this.state.copiedAdjustments == null}, 
                React.createElement("i", {className: "icon-paste"}), 
                translate("menus.paste_edit"), 
                keyboard.renderShortcut("paste_edit")
              ), 
              React.createElement("div", {className: "sep"}), 
              React.createElement(Button, {onTap: this.resetAll}, 
                React.createElement("i", {className: "icon-reset"}), 
                translate("buttons.reset")
              ), 
              React.createElement(Button, {onTap: this.showAdjustmentsGuide}, 
                React.createElement("i", {className: "icon-help"}), 
                translate("menus.guide")
              )
            )
          )

        )
      )
    )
    );
  }
});

PanelHeader = React.createClass({displayName: "PanelHeader",
  render: function() {
    return (
      React.createElement("div", {
        className: "panel-header", 
        "data-index": this.props.index, 
        "data-panel": this.props.name}, 
        React.createElement("i", {className: "icon-" + this.props.name}), 
        React.createElement("span", {className: "title"}, translate("labels." + this.props.name))
      )
    );
  }
});

AdjustmentsPanel = React.createClass({displayName: "AdjustmentsPanel",
  mixins: [require("react-addons-pure-render-mixin")],
  componentDidUpdate: function() {},
  showAdjustmentsGuide: function() {
    return AdjustmentsGuide.show();
  },
  autoAdjust: function() {
    return actions.autoAdjust();
  },
  resetAll: function() {
    return actions.resetMainAdjustments();
  },
  render: function() {
    var header;
    if (CONFIG.platform !== "mobile") {
      header = 
        React.createElement("div", {className: "action-top"}, 
          React.createElement("div", {className: "title"}, translate("labels.adjustments")), 
          React.createElement(Icon, {name: "icon-wand", onTap: this.autoAdjust, tooltip: translate("tooltips.auto_adjust")})
        )
      ;
    }
    return (
      React.createElement(Panel, React.__spread({},  this.props), 
        header, 
        React.createElement(Adjustments, {ref: "adjustments", rotated: this.props.rotated, position: this.props.position})
      )
    );
  }
});

module.exports = AdjustmentsPanel;


}).call(this,require("react"),require("react-dom"))
},{"../actions.coffee":229,"../keyboard.coffee":313,"../manager.coffee":338,"../mixins.coffee":339,"../store.coffee":346,"../utils.coffee":360,"./button.coffee":241,"./color-picker.coffee":243,"./feature-block.coffee":255,"./icon.coffee":262,"./panel.coffee":272,"./pointer-events.coffee":273,"./popover-menu.coffee":276,"./scrollable.coffee":283,"./slider-pad.coffee":285,"./slider.coffee":286,"./swatches.coffee":289,"counterpart":3,"jquery":319,"react":215,"react-addons-pure-render-mixin":32,"react-dom":34}],237:[function(require,module,exports){
(function (React){
var AlertDialog, Button, Dialog, manager, translate;

Button = require("./button.coffee");

Dialog = require("./dialog.coffee");

manager = require("../manager.coffee");

translate = require("counterpart");

AlertDialog = React.createClass({displayName: "AlertDialog",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      active: false,
      button: "buttons.got_it"
    };
  },
  componentDidMount: function() {
    return manager.on("alert", (function(_this) {
      return function(options) {
        options.active = true;
        return _this.setState(options);
      };
    })(this));
  },
  hide: function() {
    return this.setState({
      active: false
    });
  },
  render: function() {
    return (
      React.createElement(Dialog, React.__spread({ref: "dialog"},  this.props, {active: this.state.active, onHide: this.hide}), 
        React.createElement("div", {className: "alert-dialog"}, 
          React.createElement("p", {dangerouslySetInnerHTML: {__html:this.state.text}}), 
          React.createElement(Button, {content: this.state.button, onTap: this.hide})
        )
      )
    );
  }
});

module.exports = AlertDialog;


}).call(this,require("react"))
},{"../manager.coffee":338,"./button.coffee":241,"./dialog.coffee":249,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],238:[function(require,module,exports){
(function (React){
var $, AnnouncementDialog, Button, Dialog, actions, storage, translate, utils;

Button = require("./button.coffee");

Dialog = require("./dialog.coffee");

$ = require("jquery");

storage = require("../storage.coffee");

utils = require("../utils.coffee");

actions = require("../actions.coffee");

translate = require("counterpart");

AnnouncementDialog = React.createClass({displayName: "AnnouncementDialog",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      imageUrl: null
    };
  },
  hide: function() {
    return this.refs.dialog.hide();
  },
  componentWillReceiveProps: function(nextProps) {
    if (nextProps.active) {
      return this.setState(nextProps.active);
    }
  },
  componentDidMount: function() {
    return storage.getItem("onboarding_completed", (function(_this) {
      return function(success, completed) {
        if (!completed) {
          return;
        }
        return storage.getItem("announcement_id", function(success, id) {
          var params, url;
          console.log("fetching announcement: " + id);
          url = (utils.getUSUrlRoot()) + "/announcements/get_latest";
          params = {
            lang: translate.getLocale(),
            platform: CONFIG.os
          };
          return $.post(url, params, function(announcement) {
            if ((announcement != null) && Number(id) !== announcement.id) {
              actions.setDialog("announcements", announcement);
              _this.loadImage(announcement.picture_url);
              return storage.setItem("announcement_id", announcement.id);
            }
          });
        });
      };
    })(this));
  },
  loadImage: function(url) {
    var xhr;
    if (!url) {
      return;
    }
    if (CONFIG.platform !== "chrome") {
      this.setState({
        imageUrl: url
      });
      return;
    }
    xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'blob';
    xhr.onload = (function(_this) {
      return function(e) {
        var src;
        src = window.URL.createObjectURL(e.target.response);
        return _this.setState({
          imageUrl: src
        });
      };
    })(this);
    return xhr.send(null);
  },
  render: function() {
    var image;
    if (this.state.imageUrl) {
      image = React.createElement("p", null, React.createElement("img", {src: this.state.imageUrl}));
    }
    return (
      React.createElement(Dialog, React.__spread({ref: "dialog"},  this.props), 
        React.createElement("div", {className: "announcement-dialog"}, 
          React.createElement("h1", null, this.state.header), 
          image, 
          React.createElement("p", {dangerouslySetInnerHTML: {__html:this.state.announcement}}), 
          React.createElement(Button, {content: "buttons.close", onTap: this.hide})
        )
      )
    );
  }
});

module.exports = AnnouncementDialog;


}).call(this,require("react"))
},{"../actions.coffee":229,"../storage.coffee":343,"../utils.coffee":360,"./button.coffee":241,"./dialog.coffee":249,"counterpart":3,"jquery":319,"react":215,"react-addons-pure-render-mixin":32}],239:[function(require,module,exports){
(function (React){
var BeforeAfterDialog, Button, Dialog, Icon, actions, classNames, manager, store, translate, utils;

translate = require("counterpart");

classNames = require("classnames");

Button = require("./button.coffee");

Icon = require("./icon.coffee");

Dialog = require("./dialog.coffee");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

utils = require("../utils.coffee");

store = require("../store.coffee");

BeforeAfterDialog = React.createClass({displayName: "BeforeAfterDialog",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      tutorial: {}
    };
  },
  componentWillReceiveProps: function(nextProps) {
    var active;
    active = nextProps.active;
    if (active) {
      return this.setState({
        tutorial: active.tutorial || {}
      });
    }
  },
  getTutorialName: function() {
    var locale, tutorial;
    tutorial = this.state.tutorial;
    if (tutorial.name) {
      locale = translate.getLocale();
      return tutorial.name[locale] || tutorial.name["en"] || tutorial.name;
    } else {
      return "Guide";
    }
  },
  getTutorialDescription: function() {
    var locale, tutorial;
    tutorial = this.state.tutorial;
    if (tutorial.description) {
      locale = translate.getLocale();
      return tutorial.description[locale] || tutorial.description["en"] || tutorial.description;
    } else {
      return "";
    }
  },
  drawImage: function(ref, output) {
    var el;
    el = this.refs[ref];
    el.width = output.width;
    el.height = output.height;
    el.style.width = output.width / Filtr.pixelRatio + "px";
    el.style.height = output.height / Filtr.pixelRatio + "px";
    return utils.drawImageData(el, output);
  },
  renderImages: function() {
    var after, before, container, height, originalState, state, width;
    state = this.state.tutorial.state || Filtr.getState();
    originalState = Filtr.getHistory()[0].state;
    container = this.refs.images;
    if (this.props.rotated) {
      width = container.clientWidth * Filtr.pixelRatio;
      height = container.clientHeight / 2 * Filtr.pixelRatio;
    } else {
      width = container.clientWidth / 2 * Filtr.pixelRatio;
      height = container.clientHeight * Filtr.pixelRatio;
    }
    after = Filtr["export"]("data", {
      width: width,
      height: height,
      resize: "fit",
      settings: state
    });
    before = Filtr["export"]("data", {
      width: width,
      height: height,
      resize: "fit",
      settings: originalState
    });
    this.drawImage("after-canvas", after);
    return this.drawImage("before-canvas", before);
  },
  start: function() {
    manager.emit("start_tutorial");
    return this.hide();
  },
  exit: function() {
    manager.emit("exit_tutorial");
    return this.hide();
  },
  hide: function() {
    return actions.setDialog("before_after", false);
  },
  render: function() {
    return (
      React.createElement(Dialog, React.__spread({fullscreen: true, nopad: true},  this.props, {onShow: this.renderImages}), 
        React.createElement("div", {className: "header"}, 
          React.createElement("div", {className: "left"}, 
            React.createElement(Icon, {name: "icon-close", onTap: this.exit})
          ), 
          React.createElement("div", {className: "center"}, 
            React.createElement("h1", null, this.getTutorialName())
          ), 
          React.createElement("div", {className: "right"}, 
            React.createElement(Button, {onTap: this.start, className: "primary", content: "buttons.next"})
          )
        ), 
        React.createElement("div", {className: "description"}, this.getTutorialDescription()), 
        React.createElement("div", {className: "before-after", ref: "images"}, 
          React.createElement("div", {className: "before", ref: "before-container"}, 
            React.createElement("header", null, translate("labels.before")), 
            React.createElement("canvas", {ref: "before-canvas"})
          ), 
          React.createElement("div", {className: "after", ref: "after-container"}, 
            React.createElement("header", null, translate("labels.after")), 
            React.createElement("canvas", {ref: "after-canvas"})
          )
        )
      )
    );
  }
});

module.exports = BeforeAfterDialog;


}).call(this,require("react"))
},{"../actions.coffee":229,"../manager.coffee":338,"../store.coffee":346,"../utils.coffee":360,"./button.coffee":241,"./dialog.coffee":249,"./icon.coffee":262,"classnames":1,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],240:[function(require,module,exports){
(function (React){
var Circle, Icon, MaskOverlay, Overlay, PointerEvents, ViewContainer, ViewportOverlay, keyboard, mousetrap, ref, translate;

Icon = require("./icon.coffee");

ViewContainer = require("./view-container.coffee");

PointerEvents = require("./pointer-events.coffee");

Overlay = require("./overlay.coffee");

ref = require("./viewport-overlay.coffee"), ViewportOverlay = ref.ViewportOverlay, Circle = ref.Circle;

keyboard = require("../keyboard.coffee");

mousetrap = require("mousetrap");

translate = require("counterpart");

MaskOverlay = React.createClass({displayName: "MaskOverlay",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    var size;
    size = this.getBrushSize();
    return {
      pan: false,
      erase: false,
      width: size[0],
      height: size[1],
      x: 0.5,
      y: 0.5,
      showBrush: false
    };
  },
  componentDidMount: function() {
    mousetrap.bind(keyboard.shortcuts.eraser, this.toggleEraser);
    return Filtr.on("history", (function(_this) {
      return function() {
        var size;
        size = _this.getBrushSize();
        return _this.setState({
          width: size[0],
          height: size[1]
        });
      };
    })(this));
  },
  componentWillReceiveProps: function(nextProps) {
    if (nextProps.active && !this.props.active) {
      return this.setState(this.getInitialState());
    }
  },
  getBrushSize: function() {
    var adjustments, height, mask, ref1, texHeight, texScale, texWidth, width;
    adjustments = Filtr.getState().local_adjustments;
    mask = adjustments[this.props.index] || {
      brush_radius: 0.3
    };
    ref1 = Filtr._.paintTexture, width = ref1.width, height = ref1.height;
    texScale = 1.0 + mask.brush_radius * 20.0;
    texWidth = width / texScale;
    texHeight = height / texScale;
    return [1 / texWidth * 7, 1 / texHeight * 7];
  },
  showBrush: function() {
    return this.setState({
      showBrush: true
    });
  },
  hideBrush: function() {
    return this.setState({
      showBrush: false
    });
  },
  setBrushPosition: function(e) {
    var point;
    point = Filtr.getMouseCoords(e.pageX, e.pageY);
    return this.setState({
      x: point[0],
      y: point[1]
    });
  },
  showBrushAt: function(x, y) {
    var size;
    size = this.getBrushSize();
    return this.setState({
      x: x,
      y: y,
      width: size[0],
      height: size[1],
      showBrush: true
    });
  },
  toggleEraser: function(e) {
    e.preventDefault();
    return this.setState({
      erase: !this.state.erase,
      pan: false
    });
  },
  togglePan: function() {
    return this.setState({
      pan: !this.state.pan,
      erase: false
    });
  },
  setPan: function(pan) {
    return this.setState({
      pan: pan
    });
  },
  deactivateViewControls: function() {
    return this.setState({
      pan: false,
      erase: false
    });
  },
  touchStart: function(e, input) {
    var blend, mask;
    if (this.state.pan) {
      return false;
    }
    e.stopPropagation();
    this.adjustments = Filtr.getState(true).local_adjustments;
    mask = this.adjustments[this.props.index];
    blend = this.state.erase ? -1 : 1;
    return Filtr.brush.setBrush({
      blend: blend,
      channel: mask.channel,
      radius: mask.brush_radius,
      opacity: mask.brush_opacity
    });
  },
  touchMove: function(e, input) {
    var point;
    if (this.state.pan) {
      return;
    }
    point = Filtr.getMouseCoords(input.x, input.y);
    Filtr.brush.paint(point);
    Filtr.setState({
      local_adjustments: this.adjustments
    });
    return this.setState({
      x: point[0],
      y: point[1]
    });
  },
  touchEnd: function(e, input) {
    var mask, stroke;
    if (this.state.pan) {
      return;
    }
    stroke = Filtr.brush.endPaint();
    if (!stroke) {
      return;
    }
    mask = this.adjustments[this.props.index];
    mask.brush = mask.brush || [];
    mask.brush.push(stroke);
    Filtr.setState({
      local_adjustments: this.adjustments
    });
    Filtr.saveState("buttons.update tooltips.brush_filter");
    if (input.type === "touch") {
      return this.hideBrush();
    }
  },
  render: function() {
    var cursor, pointerEvents;
    pointerEvents = {
      threshold: 0,
      multitouch: true,
      onPointerDown: this.touchStart,
      onPointerMove: this.touchMove,
      onPointerUp: this.touchEnd
    };
    cursor = {
      x: this.state.x,
      y: 1 - this.state.y,
      width: this.state.width,
      height: this.state.height,
      border: "1px solid white",
      outline: true
    };
    return (
      React.createElement(Overlay, {
        pointerEvents: pointerEvents, 
        onMouseMove: this.setBrushPosition, 
        onMouseEnter: this.showBrush, 
        onMouseLeave: this.hideBrush, 
        rotated: this.props.rotated, 
        active: this.props.active, 
        disabled: this.state.pan, 
        setDisabled: this.setPan, 
        hideCursor: true}, 
        React.createElement(ViewportOverlay, {active: this.state.showBrush && !this.state.pan}, 
          React.createElement(Circle, React.__spread({},  cursor)
          )
        ), 
        React.createElement(ViewContainer, {rotated: this.props.rotated, sliderActivated: this.deactivateViewControls}, 
          React.createElement(Icon, {name: "icon-eraser", active: this.state.erase, onTap: this.toggleEraser, tooltip: translate("tooltips.eraser"), shortcut: "eraser"}), 
          React.createElement(Icon, {name: "icon-move", active: this.state.pan, onTap: this.togglePan, tooltip: translate("tooltips.pan"), shortcut: "pan"})
        )
      )
    );
  }
});

module.exports = MaskOverlay;


}).call(this,require("react"))
},{"../keyboard.coffee":313,"./icon.coffee":262,"./overlay.coffee":270,"./pointer-events.coffee":273,"./view-container.coffee":296,"./viewport-overlay.coffee":298,"counterpart":3,"mousetrap":21,"react":215,"react-addons-pure-render-mixin":32}],241:[function(require,module,exports){
(function (React,ReactDOM){
var Button, PointerEvents, actions, classnames, mixins, translate;

PointerEvents = require("./pointer-events.coffee");

mixins = require("../mixins.coffee");

actions = require("../actions.coffee");

translate = require("counterpart");

classnames = require("classnames");

Button = React.createClass({displayName: "Button",
  mixins: [mixins.touchRipple],
  onPointerDown: function(e) {
    if (this.props.disabled) {
      return;
    }
    return this.touchRippleStart(e, ReactDOM.findDOMNode(this));
  },
  onTap: function(e, input) {
    var base;
    if (this.props.disabled) {
      if (this.props.alert) {
        actions.alert(this.props.alert);
      }
      return;
    }
    return typeof (base = this.props).onTap === "function" ? base.onTap(e, input) : void 0;
  },
  render: function() {
    var className, content;
    className = classnames(this.props.className, {
      active: this.props.active,
      disabled: this.props.disabled
    });
    if (this.props.content) {
      content = translate(this.props.content);
    } else {
      content = this.props.children;
    }
    return (
      React.createElement(PointerEvents, {onTap: this.onTap, onPointerDown: this.onPointerDown}, 
        React.createElement("button", {
          className: className, 
          style: this.props.style, 
          tabIndex: this.props.tabIndex, 
          type: this.props.type, 
          "data-active": this.props.active, 
          "data-id": this.props.id}, 
          content
        )
      )
    );
  }
});

module.exports = Button;


}).call(this,require("react"),require("react-dom"))
},{"../actions.coffee":229,"../mixins.coffee":339,"./pointer-events.coffee":273,"classnames":1,"counterpart":3,"react":215,"react-dom":34}],242:[function(require,module,exports){
(function (React){
var Circle, Handle, Icon, MaskOverlay, Overlay, ViewContainer, ViewportOverlay, actions, ref, translate, utils;

Icon = require("./icon.coffee");

ViewContainer = require("./view-container.coffee");

Overlay = require("./overlay.coffee");

ref = require("./viewport-overlay.coffee"), ViewportOverlay = ref.ViewportOverlay, Circle = ref.Circle, Handle = ref.Handle;

utils = require("../utils.coffee");

actions = require("../actions.coffee");

translate = require("counterpart");

MaskOverlay = React.createClass({displayName: "MaskOverlay",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      pan: false,
      x: 0,
      y: 0,
      width: 0,
      height: 0
    };
  },
  togglePan: function() {
    return this.setState({
      pan: !this.state.pan
    });
  },
  setPan: function(pan) {
    return this.setState({
      pan: pan
    });
  },
  componentDidMount: function() {
    return Filtr.on("history", (function(_this) {
      return function() {
        if (_this.props.active) {
          return _this.update();
        }
      };
    })(this));
  },
  componentDidUpdate: function(prevProps) {
    if (!prevProps.active && this.props.active) {
      return this.update();
    }
  },
  update: function() {
    var mask, state;
    state = Filtr.getState();
    mask = state.local_adjustments[this.props.index];
    if (!mask) {
      return;
    }
    return this.setState({
      x: mask.position[0],
      y: mask.position[1],
      width: mask.size[0],
      height: mask.size[1]
    });
  },
  onDoubleTap: function(e, input, component) {
    var action;
    action = component.props.action;
    if (action) {
      return this.resetMask();
    }
  },
  onDrag: function(e, input, component) {
    var action, aspect, delta, distance, height, min, point, s1, s2, size, width, x, y;
    action = component.props.action;
    if (!(action || input.multitouch)) {
      return;
    }
    if (input.multitouch) {
      action = "pinch";
    }
    point = Filtr.getMouseCoords(input.x, input.y);
    if (!input.dragging) {
      this.startPoint = point;
      this.startState = utils.assign({}, this.state);
      this.adjustments = Filtr.getState().local_adjustments;
      this.mask = this.adjustments[this.props.index];
      this.center = [this.mask.position[0] + 0.5, this.mask.position[1] + 0.5];
    }
    if (action === "pinch") {
      distance = input.distanceDelta;
      s1 = this.startState.width * distance;
      s2 = this.startState.height * distance;
      min = Math.min(s1, s2);
      width = Math.max(0.2 * (s1 / min), s1);
      height = Math.max(0.2 * (s2 / min), s2);
      this.mask.size = [width, height];
      this.setState({
        width: width,
        height: height
      });
      this.message = "tooltips.color_filter labels.size";
    } else if (action === "pan") {
      x = this.startState.x + (point[0] - this.startPoint[0]);
      y = this.startState.y + (point[1] - this.startPoint[1]);
      this.mask.position = [x, y];
      this.setState({
        x: x,
        y: y
      });
      this.message = "tooltips.color_filter labels.position";
    } else if (action === "resize") {
      x = point[0], y = point[1];
      aspect = Filtr._.texture.width / Filtr._.texture.height;
      x = (x - this.center[0]) * aspect + this.center[0];
      size = utils.distance(this.center, [x, y]) * 2.0;
      if (!input.dragging) {
        this.startSize = size;
      }
      delta = size / this.startSize;
      width = this.startState.width * delta;
      height = this.startState.height * delta;
      this.mask.size = [width, height];
      this.setState({
        width: width,
        height: height
      });
      this.message = "tooltips.color_filter labels.size";
    }
    return Filtr.setState({
      local_adjustments: this.adjustments
    });
  },
  onDragEnd: function() {
    if (this.message) {
      Filtr.saveState(this.message);
      return this.message = null;
    }
  },
  render: function() {
    var fill, props;
    props = {
      x: this.state.x + 0.5,
      y: 1 - this.state.y - 0.5,
      width: this.state.width,
      height: this.state.height,
      border: "1px solid white",
      disabled: this.state.pan,
      action: "pan"
    };
    fill = "url('../img/icons/crosshairs.png') 50% 50% / 17px no-repeat";
    return (
      React.createElement(Overlay, React.__spread({},  this.props, {disabled: this.state.pan, setDisabled: this.setPan}), 
        React.createElement(ViewportOverlay, {
          active: this.props.active, 
          onDoubleTap: this.onDoubleTap, 
          onDrag: this.onDrag, 
          onDragEnd: this.onDragEnd}, 
          React.createElement(Circle, React.__spread({},  props), 
            React.createElement(Handle, {fill: "white", position: [1, 0.5], action: "resize"})
          ), 
          React.createElement(Handle, {fill: fill, position: [props.x, props.y], disabled: this.state.pan, pointerEvents: "none"})
        ), 
        React.createElement(ViewContainer, {rotated: this.props.rotated}, 
          React.createElement(Icon, {name: "icon-move", active: this.state.pan, onTap: this.togglePan, tooltip: translate("tooltips.pan"), shortcut: "pan"})
        )
      )
    );
  }
});

module.exports = MaskOverlay;


}).call(this,require("react"))
},{"../actions.coffee":229,"../utils.coffee":360,"./icon.coffee":262,"./overlay.coffee":270,"./view-container.coffee":296,"./viewport-overlay.coffee":298,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],243:[function(require,module,exports){
(function (React,ReactDOM){
var Picker, PointerEvents, PopoutPicker, actions, translate, utils;

translate = require("counterpart");

utils = require("../utils.coffee");

translate = require("counterpart");

actions = require("../actions.coffee");

PointerEvents = require("./pointer-events.coffee");

PopoutPicker = require("./popout-colorpicker.coffee");

Picker = React.createClass({displayName: "Picker",
  getInitialState: function() {
    return {
      title: "",
      value: [0, 0]
    };
  },
  componentDidMount: function() {
    return this.setStateFromProps(this.props);
  },
  componentWillReceiveProps: function(props) {
    return this.setStateFromProps(props);
  },
  setStateFromProps: function(props) {
    var title;
    title = translate(props.label || props.name);
    return this.setState({
      title: title,
      value: props.value
    });
  },
  setValue: function(value) {
    this.setState({
      value: value
    });
    return this.props.update(value);
  },
  reset: function() {
    this.props.update([0, 0]);
    this.setState({
      value: [0, 0]
    });
    return this.saveState();
  },
  doubleTap: function() {
    return this.reset();
  },
  longPress: function() {
    this.started = true;
    return ReactDOM.findDOMNode(this).focus();
  },
  touchStart: function(e, input) {
    this.target = e.target;
    this.startVal = this.state.value.slice(0);
    this.width = this.refs.background.clientWidth;
    this.height = this.refs.background.clientHeight;
    this.started = false;
    this.dragging = false;
    if (document.activeElement === this.target) {
      this.started = true;
    }
    if (this.started) {
      return e.preventDefault();
    }
  },
  touchMove: function(e, input) {
    var curVal, deltaX, deltaY, ref;
    if (!this.started) {
      if (document.activeElement !== this.target) {
        document.activeElement.blur();
      }
    }
    if (!this.started && input.pointerType === "touch") {
      return;
    }
    this.dragging = true;
    e.preventDefault();
    deltaX = input.deltaX;
    deltaY = input.deltaY;
    if (this.props.rotated) {
      ref = [-deltaY, deltaX], deltaX = ref[0], deltaY = ref[1];
    }
    if (input.pointerType === "touch") {
      deltaX /= 2;
      deltaY /= 2;
    }
    curVal = [utils.clamp(this.startVal[0] + deltaX / this.width, 0, 1), utils.clamp(this.startVal[1] - deltaY / this.height, 0, 1)];
    return this.setValue(curVal);
  },
  touchEnd: function(e) {
    e.preventDefault();
    e.stopPropagation();
    if (!this.started && !this.dragging) {
      this.activatePad();
    } else {
      this.saveState();
      if (!this.dragging) {
        this.target.blur();
      }
    }
    if (!this.connectedSlider) {
      this.started = false;
      return this.target.blur();
    }
  },
  touchCancel: function() {
    var ref;
    return (ref = this.target) != null ? ref.blur() : void 0;
  },
  activatePad: function() {
    this.target.addEventListener("blur", this.deactivatePad);
    if (document.activeElement === this.target) {
      return this.target.blur();
    } else {
      this.target.focus();
      return this.connectedSlider = PopoutPicker.connect(this);
    }
  },
  deactivatePad: function() {
    if (!this.connectedSlider) {
      return;
    }
    PopoutPicker.disconnect();
    this.connectedSlider = null;
    return this.target.removeEventListener("blur", this.deactivatePad);
  },
  saveState: function() {
    return Filtr.saveState(this.props.name + " labels.color");
  },
  getColor: function(val) {
    if (val == null) {
      val = this.props.value;
    }
    return "hsl(" + (val[0] * 360) + "," + (val[1] * 100) + "%,50%)";
  },
  render: function() {
    var position, ref;
    position = {
      left: this.state.value[0] * 100 + "%",
      bottom: this.state.value[1] * 100 + "%"
    };
    if ((ref = this.connectedSlider) != null) {
      ref.update(this.state.value);
    }
    return (
      React.createElement(PointerEvents, {
        longPressDelay: 200, 
        multitouch: true, 
        onPointerDown: this.touchStart, 
        onPointerMove: this.touchMove, 
        onPointerUp: this.touchEnd, 
        onPointerCancel: this.touchCancel, 
        onDoubleTap: this.reset, 
        onLongPress: this.longPress}, 
        React.createElement("div", {className: "color-picker", tabIndex: "-1"}, 
          React.createElement("div", {className: "container"}, 
            React.createElement("div", {className: "background", ref: "background"}), 
            React.createElement("div", {className: "control", ref: "control", style: position}), 
            React.createElement("div", {className: "title"}, this.state.title)
          )
        )
      )
    );
  }
});

module.exports = Picker;


}).call(this,require("react"),require("react-dom"))
},{"../actions.coffee":229,"../utils.coffee":360,"./pointer-events.coffee":273,"./popout-colorpicker.coffee":274,"counterpart":3,"react":215,"react-dom":34}],244:[function(require,module,exports){
(function (React){
var Button, Cover, manager, translate;

translate = require("counterpart");

Button = require("./button.coffee");

manager = require("../manager.coffee");

Cover = React.createClass({displayName: "Cover",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      message: "",
      active: false,
      progress: null
    };
  },
  componentDidMount: function() {
    return manager.on("cover", (function(_this) {
      return function(state) {
        _this.setState(state);
        return _this.setTimeout(state.timeout);
      };
    })(this));
  },
  hide: function() {
    return this.setState({
      active: false,
      progress: null
    });
  },
  setTimeout: function(delay) {
    clearTimeout(this.hideTimeout);
    if (delay) {
      return this.hideTimeout = setTimeout((function(_this) {
        return function() {
          return _this.setState({
            active: false,
            progress: null
          });
        };
      })(this), delay);
    }
  },
  render: function() {
    var progressActive, progressStyle;
    progressActive = this.state.progress != null;
    progressStyle = {
      width: this.state.progress + "%"
    };
    return (
      React.createElement("div", {className: this.state.active ? "cover-overlay active" : "cover-overlay", onTouchTap: this.hide}, 
        React.createElement("div", {className: "cover", ref: "cover"}, 
          React.createElement("span", {dangerouslySetInnerHTML: {__html:this.state.message}})
        ), 
        React.createElement("div", {className: progressActive ? "progress-track active" : "progress-track"}, 
          React.createElement("div", {className: "progress-bar", style: progressStyle})
        )
      )
    );
  }
});

module.exports = Cover;


}).call(this,require("react"))
},{"../manager.coffee":338,"./button.coffee":241,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],245:[function(require,module,exports){
(function (React){
var Button, CropAdjustments, CropControl, Icon, Menu, Panel, PointerEvents, Portal, actions, cx, mixins, translate, utils;

Button = require("./button.coffee");

Icon = require("./icon.coffee");

Menu = require("./popover-menu.coffee");

Portal = require("./portal.coffee");

CropControl = require("./crop-overlay.coffee");

PointerEvents = require("./pointer-events.coffee");

Panel = require("./panel.coffee");

mixins = require("../mixins.coffee");

translate = require("counterpart");

utils = require("../utils.coffee");

actions = require("../actions.coffee");

cx = require("classnames");

CropAdjustments = React.createClass({displayName: "CropAdjustments",
  mixins: [require("react-addons-pure-render-mixin")],
  shouldUpdate: true,
  componentDidMount: function() {
    return Filtr.on("history", (function(_this) {
      return function() {
        var size, state;
        state = Filtr.getState();
        size = Filtr.getImageSize();
        if (_this.shouldUpdate) {
          return _this.setState({
            rotation: state.rotation,
            imageSize: size
          });
        }
      };
    })(this));
  },
  getInitialState: function() {
    return {
      rotation: 0,
      imageSize: [0, 0]
    };
  },
  resetCrop: function() {
    return actions.resetCrop();
  },
  setRatioOriginal: function() {
    Filtr.setState({
      crop: [0, 0, 1, 1]
    }, false);
    return Filtr.saveState("labels.crop labels.ratio", "Original");
  },
  setRatio: function(ratio, label) {
    var cropHeight, cropWidth, height, scale, size, width;
    size = Filtr.getInfo();
    width = size.width;
    height = width * ratio;
    scale = Math.min(size.width / width, size.height / height);
    cropWidth = width * scale / size.width;
    cropHeight = height * scale / size.height;
    Filtr.setState({
      crop: [(1 - cropWidth) / 2, (1 - cropHeight) / 2, cropWidth, cropHeight]
    }, false);
    return Filtr.saveState("labels.crop labels.ratio", label, false);
  },
  flipX: function() {
    return actions.flipX();
  },
  flipY: function() {
    return actions.flipY();
  },
  rotate90: function(delta) {
    return actions.rotate90(delta);
  },
  showMenu: function(e) {
    return this.refs["menu"].show({
      target: e.target,
      offset: [8, 0]
    });
  },
  getRatioButton: function(w, h, key) {
    var height, ratio, ref, ref1, style, text, width;
    ratio = h / w;
    width = 16;
    height = 16 * ratio;
    if (this.state.imageSize[0] < this.state.imageSize[1]) {
      ref = [height, width], width = ref[0], height = ref[1];
      ref1 = [h, w], w = ref1[0], h = ref1[1];
    }
    style = {
      width: width + "px",
      height: height + "px"
    };
    text = w + ":" + h;
    return (
      React.createElement("a", {key: key, onTouchTap: this.setRatio.bind(this, h/w, text)}, 
        React.createElement("i", null, React.createElement("span", {className: "ratio", style: style})), text
      )
    );
  },
  render: function() {
    var cropActions, ratios_h, ratios_v;
    ratios_v = [[1, 1], [16, 10], [16, 9], [5, 4], [4, 3], [3, 2]];
    ratios_h = [[1, 2], [10, 16], [9, 16], [4, 5], [3, 4], [2, 3]];
    if (CONFIG.platform === "mobile") {
      cropActions = 
        React.createElement("div", {className: "actions"}, 
          React.createElement(Icon, {name: "icon-aspect", onTap: this.showMenu}), 
          React.createElement(Icon, {name: "icon-fliph", onTap: this.flipX}), 
          React.createElement("div", {className: "spacer"}), 
          React.createElement(Icon, {name: "icon-reset", onTap: this.resetCrop}), 
          React.createElement(Icon, {name: "icon-rotate", onTap: this.rotate90.bind(this, -1)})
        )
      ;
    } else {
      cropActions = 
        React.createElement("div", {className: "actions"}, 
          React.createElement(Button, {onTap: this.showMenu}, React.createElement("i", {className: "icon-aspect"}), translate("buttons.aspect")), 
          React.createElement(Button, {onTap: this.rotate90.bind(this, -1)}, React.createElement("i", {className: "icon-rotate"}), translate("tooltips.rotate")), 
          React.createElement(Button, {onTap: this.flipX}, React.createElement("i", {className: "icon-fliph"}), translate("tooltips.flip"), " X"), 
          React.createElement(Button, {onTap: this.flipY}, React.createElement("i", {className: "icon-flipv"}), translate("tooltips.flip"), " Y"), 
          React.createElement(Button, {onTap: this.resetCrop}, React.createElement("i", {className: "icon-reset"}), translate("buttons.reset"))
        )
      ;
    }
    return (
      React.createElement(Panel, React.__spread({},  this.props), 
        React.createElement(Portal, {portalId: "crop-controls"}, 
          React.createElement(CropControl, {
            ref: "cropControls", 
            rotated: this.props.rotated, 
            active: this.props.active})
        ), 


        cropActions, 

        React.createElement(Menu, {ref: "menu"}, 
          React.createElement("a", {onTouchTap: this.setRatioOriginal}, translate("labels.original")), 
          React.createElement("div", {className: "ratios_container"}, 
            React.createElement("div", {className: "ratios"}, 
               ratios_v.map(function(ratio, i){
                return this.getRatioButton(ratio[0], ratio[1], i)
              }, this) 
            ), 
            React.createElement("div", {className: "ratios"}, 
               ratios_h.map(function(ratio, i){
                return this.getRatioButton(ratio[0], ratio[1], i)
              }, this) 
            )
          )
        )
      )
    );
  }
});

module.exports = CropAdjustments;


}).call(this,require("react"))
},{"../actions.coffee":229,"../mixins.coffee":339,"../utils.coffee":360,"./button.coffee":241,"./crop-overlay.coffee":246,"./icon.coffee":262,"./panel.coffee":272,"./pointer-events.coffee":273,"./popover-menu.coffee":276,"./portal.coffee":277,"classnames":1,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],246:[function(require,module,exports){
(function (React,ReactDOM){
var CropAngle, CropOverlay, PointerEvents, actions, classNames, manager, mixins, utils;

utils = require("../utils.coffee");

mixins = require("../mixins.coffee");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

classNames = require("classnames");

PointerEvents = require("./pointer-events.coffee");

CropOverlay = React.createClass({displayName: "CropOverlay",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      rotation: 0,
      side: this.getSide()
    };
  },
  componentDidMount: function() {
    this.overlay = this.refs['overlay'];
    this.cropEl = this.refs['crop'];
    this.pixelRatio = Filtr.pixelRatio;
    Filtr.on("history", (function(_this) {
      return function() {
        var state;
        if (Filtr._.cropMode) {
          Filtr.set({
            zoom: 0
          });
          state = Filtr.getState();
          return _this.setState({
            rotation: state.rotation,
            crop: state.crop
          });
        }
      };
    })(this));
    Filtr.on("resize", (function(_this) {
      return function() {
        if (Filtr._.cropMode) {
          return _this.change();
        }
      };
    })(this));
    Filtr.on("screen", (function(_this) {
      return function(screen) {
        var crop, ref, size, zoom;
        if (Filtr._.cropMode && screen.extra) {
          ref = screen.extra, crop = ref.crop, size = ref.size, zoom = ref.zoom;
          return _this.change(crop, size, zoom);
        }
      };
    })(this));
    manager.on("toolbars", (function(_this) {
      return function() {
        if (Filtr._.cropMode) {
          _this.setState({
            side: _this.getSide()
          });
          return _this.change();
        }
      };
    })(this));
    if (this.props.active && !Filtr._.cropMode) {
      return Filtr.cropMode(true);
    }
  },
  componentDidUpdate: function() {
    this.change();
    if (this.props.active) {
      if (!Filtr._.cropMode) {
        Filtr.cropMode(true);
        return this.setState({
          side: this.getSide()
        });
      }
    } else if (Filtr._.cropMode) {
      return Filtr.cropMode(false);
    }
  },
  getSide: function() {
    var side, toolbars;
    toolbars = manager.state.toolbars;
    return side = toolbars.left.indexOf("crop") !== -1 ? "left" : "right";
  },
  change: function(crop, size, zoom) {
    var coords;
    if (!this.props.active) {
      return;
    }
    coords = Filtr.getCropCoords(crop, size, zoom);
    this.overlay.style.width = Filtr.canvas.width / Filtr.pixelRatio + "px";
    this.overlay.style.height = Filtr.canvas.height / Filtr.pixelRatio + "px";
    this.cropEl.style.left = coords[0] + 'px';
    this.cropEl.style.top = coords[1] + 'px';
    this.cropEl.style.width = coords[2] + 'px';
    return this.cropEl.style.height = coords[3] + 'px';
  },
  updateRotation: function(angle) {
    return Filtr.set({
      rotation: angle
    });
  },
  setRotation: function(angle) {
    Filtr.setState({
      rotation: angle
    }, false);
    Filtr.saveState("labels.angle", angle.toFixed(2) + "°");
    this.setState({
      rotation: angle
    });
    return this.hideLines();
  },
  applyCrop: function() {
    var toolbars;
    toolbars = manager.state.toolbars;
    if (toolbars.leftActive === "crop") {
      toolbars.leftActive = null;
    } else if (toolbars.rightActive === "crop") {
      toolbars.rightActive = null;
    }
    Filtr.cropMode(false);
    return manager.set("toolbars", toolbars);
  },
  resetCrop: function() {
    this.setTransition(200);
    Filtr.setState({
      crop: [0, 0, 1, 1]
    });
    return Filtr.saveState("buttons.reset labels.crop");
  },
  showGrid: function() {
    clearTimeout(this.hideLinesTimeout);
    this.cropEl.classList.add("grid");
    return this.cropEl.classList.remove("thirds");
  },
  showThirds: function() {
    clearTimeout(this.hideLinesTimeout);
    this.cropEl.classList.remove("grid");
    return this.cropEl.classList.add("thirds");
  },
  hideLines: function() {
    clearTimeout(this.hideLinesTimeout);
    return this.hideLinesTimeout = setTimeout((function(_this) {
      return function() {
        _this.cropEl.classList.remove("grid");
        return _this.cropEl.classList.remove("thirds");
      };
    })(this), 300);
  },
  setTransition: function(duration) {
    return this.cropEl.style.transitionDuration = duration + "ms";
  },
  setMul: function(e) {
    var data;
    data = e.target.dataset.mul;
    if (!data) {
      this.mul = null;
      this.sum = 0;
      return;
    }
    this.mul = data.split(",").map(function(num) {
      return parseInt(num, 10);
    });
    return this.sum = this.mul.reduce(function(prev, num) {
      return prev + num;
    });
  },
  touchStart: function(e, input) {
    e.stopPropagation();
    this.setMul(e);
    if (!this.mul) {
      return false;
    }
    this.showThirds();
    this.setTransition(0);
    this.startDistance = input.distance;
    this.startCrop = this.crop = Filtr.getState().crop;
    return this.imageSize = Filtr.getImageSize();
  },
  touchMove: function(e, input) {
    var b, bottom, c1, c2, c3, c4, deltaX, deltaY, height, l, left, limitX, limitY, mx, my, r, ref, ref1, right, t, top, width, x, y, zoom;
    zoom = Filtr.getZoom();
    ref = this.imageSize, width = ref[0], height = ref[1];
    width *= zoom;
    height *= zoom;
    if (input.multitouch) {
      x = y = (input.distance - this.startDistance) / this.pixelRatio;
      this.sum = 2;
      this.mul = [-1, -1, 1, 1];
    } else {
      deltaX = input.deltaX, deltaY = input.deltaY;
      if (this.props.rotated) {
        ref1 = [-deltaY, deltaX], deltaX = ref1[0], deltaY = ref1[1];
      }
      x = deltaX;
      y = deltaY;
    }
    limitX = 150 / width;
    limitY = 150 / height;
    x = x / width * this.pixelRatio;
    y = y / height * this.pixelRatio;
    mx = this.startCrop[2] - limitX;
    my = this.startCrop[3] - limitY;
    l = left = this.startCrop[0];
    t = top = this.startCrop[1];
    r = right = 1 - (this.startCrop[0] + this.startCrop[2]);
    b = bottom = 1 - (this.startCrop[1] + this.startCrop[3]);
    if (this.sum === 4) {
      x = utils.clamp(x, -left, right);
      y = utils.clamp(y, -top, bottom);
    } else {
      if (this.sum === 2) {
        x /= this.startCrop[2];
        y /= this.startCrop[3];
        if (this.mul[2]) {
          x *= -1;
        }
        if (this.mul[3]) {
          y *= -1;
        }
        x = y = (x + y) / 2;
        if (this.mul[2]) {
          x *= -1;
        }
        if (this.mul[3]) {
          y *= -1;
        }
        x *= this.startCrop[2];
        y *= this.startCrop[3];
        l /= this.startCrop[2];
        t /= this.startCrop[3];
        r /= this.startCrop[2];
        b /= this.startCrop[3];
        mx /= this.startCrop[2];
        my /= this.startCrop[3];
        mx = my = Math.min(mx, my);
        mx *= this.startCrop[2];
        my *= this.startCrop[3];
        if (this.mul[0] && this.mul[1]) {
          t = l = Math.min(t, l);
        }
        if (this.mul[1] && this.mul[2]) {
          t = r = Math.min(t, r);
        }
        if (this.mul[2] && this.mul[3]) {
          b = r = Math.min(b, r);
        }
        if (this.mul[3] && this.mul[0]) {
          b = l = Math.min(b, l);
        }
        l *= this.startCrop[2];
        t *= this.startCrop[3];
        r *= this.startCrop[2];
        b *= this.startCrop[3];
      }
      if (input.multitouch) {
        x = utils.clamp(x, -mx / 2, r);
        y = utils.clamp(y, -my / 2, b);
      } else {
        if (this.mul[0]) {
          x = utils.clamp(x, -l, mx);
        }
        if (this.mul[2]) {
          x = utils.clamp(x, -mx, r);
        }
        if (this.mul[1]) {
          y = utils.clamp(y, -t, my);
        }
        if (this.mul[3]) {
          y = utils.clamp(y, -my, b);
        }
      }
    }
    c1 = left + x * this.mul[0];
    c2 = top + y * this.mul[1];
    c3 = right - x * this.mul[2];
    c4 = bottom - y * this.mul[3];
    this.crop = [c1, c2, 1 - c3 - c1, 1 - c4 - c2];
    this.change(this.crop);
    return Filtr.setState({
      crop: this.crop
    }, false);
  },
  touchEnd: function(e) {
    e.stopPropagation();
    this.hideLines();
    this.setTransition(200);
    if (this.crop[0] !== this.startCrop[0] || this.crop[1] !== this.startCrop[1] || this.crop[2] !== this.startCrop[2] || this.crop[3] !== this.startCrop[3]) {
      return this.saveState();
    }
  },
  doubleTap: function() {
    return this.resetCrop();
  },
  singleTap: function() {
    return this.applyCrop();
  },
  saveState: utils.debounce(function() {
    return Filtr.saveState("labels.crop");
  }, 300),
  render: function() {
    var grid, i, j, k, lines, p, style;
    lines = [];
    for (i = j = 1; j <= 2; i = ++j) {
      p = 100 / 3 * i + "%";
      lines.push(React.createElement("div", {key: "lh"+i, className: "line h", style: {top:p}}));
      lines.push(React.createElement("div", {key: "lv"+i, className: "line v", style: {left:p}}));
    }
    grid = [];
    for (i = k = 1; k <= 8; i = ++k) {
      p = 100 / 9 * i + "%";
      grid.push(React.createElement("div", {key: "gh"+i, className: "grid-line h", style: {top:p}}));
      grid.push(React.createElement("div", {key: "gv"+i, className: "grid-line v", style: {left:p}}));
    }
    if (this.props.rotated) {
      style = {
        width: window.innerWidth,
        height: window.innerHeight,
        transform: "translate(-50%, -50%) rotate(-90deg)"
      };
    } else {
      style = {
        width: window.innerWidth,
        height: window.innerHeight
      };
    }
    return (
      React.createElement(PointerEvents, {
        multitouch: true, 
        onPointerDown: this.touchStart, 
        onPointerMove: this.touchMove, 
        onPointerUp: this.touchEnd, 
        onDoubleTap: this.doubleTap, 
        onSingleTap: this.singleTap}, 
        React.createElement("div", {className: this.props.active ? 'overlay active' : 'overlay', style: style, ref: "overlay"}, 
          React.createElement("div", {className: "crop", ref: "crop"}, 
            React.createElement("div", {className: "crop-inner", "data-mul": [1, 1, 1, 1]}, 
              React.createElement("div", {className: "handle left", "data-mul": [1, 0, 0, 0]}), 
              React.createElement("div", {className: "handle right", "data-mul": [0, 0, 1, 0]}), 
              React.createElement("div", {className: "handle top", "data-mul": [0, 1, 0, 0]}), 
              React.createElement("div", {className: "handle bottom", "data-mul": [0, 0, 0, 1]}), 
              React.createElement("div", {className: "handle-corner top-left", "data-mul": [1, 1, 0, 0]}), 
              React.createElement("div", {className: "handle-corner top-right", "data-mul": [0, 1, 1, 0]}), 
              React.createElement("div", {className: "handle-corner bottom-right", "data-mul": [0, 0, 1, 1]}), 
              React.createElement("div", {className: "handle-corner bottom-left", "data-mul": [1, 0, 0, 1]}), 
              lines, 
              grid
            ), 
            React.createElement(CropAngle, {ref: "angle", side: this.state.side, value: this.state.rotation, onUpdate: this.updateRotation, onUpdateEnd: this.setRotation, onUpdateStart: this.showGrid})
          )
        )
      )
    );
  }
});

CropAngle = React.createClass({displayName: "CropAngle",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      width: 100,
      height: 800,
      angle: 0
    };
  },
  componentWillReceiveProps: function(nextProps) {
    return this.setState({
      angle: nextProps.value
    });
  },
  componentDidMount: function() {
    var canvas, context;
    canvas = document.createElement('canvas');
    canvas.width = 800;
    canvas.height = 800;
    context = canvas.getContext("2d");
    this.drawDial(context, {
      width: 800,
      height: 800,
      side: "right"
    });
    this.drawDial(context, {
      width: 800,
      height: 800,
      side: "left"
    });
    this.dialCanvas = canvas;
    return this.componentDidUpdate();
  },
  componentDidUpdate: function() {
    var canvas, context;
    canvas = ReactDOM.findDOMNode(this);
    context = canvas.getContext("2d");
    context.clearRect(0, 0, this.state.width, this.state.height);
    return this.drawCanvas(context, this.state);
  },
  drawCanvas: function(ctx, state) {
    var cx, cy, offset;
    cy = state.height / 2;
    cx = state.width - cy;
    ctx.save();
    ctx.fillStyle = "#ccc";
    ctx.beginPath();
    if (this.props.side === "right") {
      offset = -700;
      ctx.moveTo(state.width - 12, cy);
      ctx.lineTo(state.width, cy + 6);
      ctx.lineTo(state.width, cy - 6);
    } else {
      offset = 0;
      cx = state.width - cx;
      ctx.moveTo(12, cy);
      ctx.lineTo(0, cy + 6);
      ctx.lineTo(0, cy - 6);
    }
    ctx.fill();
    ctx.translate(cx, cy);
    ctx.rotate((Math.PI / 180) * state.angle);
    ctx.translate(-cx, -cy);
    ctx.drawImage(this.dialCanvas, offset, 0);
    return ctx.restore();
  },
  drawDial: function(ctx, state) {
    var angle, cx, cy, i, j, ox, ref, steps, w;
    cx = state.width / 2;
    cy = state.height - cx;
    ctx.save();
    ctx.font = "18px sans-serif";
    ctx.textAlign = state.side;
    ctx.translate(cx, cy);
    ctx.rotate((Math.PI / 180) * -90);
    steps = 36 * 5 / 2;
    angle = 180 / steps;
    for (i = j = 0, ref = steps; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
      if (i % 5) {
        ctx.fillStyle = "#aaa";
        w = 4;
      } else {
        ctx.fillStyle = "#fff";
        w = 8;
        ox = state.side === "right" ? cx - 38 : -cx + 38;
        ctx.fillText(90 - angle * i, ox, 6);
      }
      ox = state.side === "right" ? cx - w - 20 : -cx + 20;
      ctx.fillRect(ox, -1, w, 2);
      ctx.rotate((Math.PI / 180) * angle);
    }
    return ctx.restore();
  },
  pointerStart: function(e) {
    return e.stopPropagation();
  },
  pointerStartMove: function(e, pointer) {
    var base, box, node, x, y;
    node = ReactDOM.findDOMNode(this);
    box = node.getBoundingClientRect();
    if (this.props.side === "right") {
      this.nodeCenter = [box.height / 2 + box.left - (box.height - box.width), box.height / 2 + box.top];
    } else {
      this.nodeCenter = [box.left + box.height / 2, box.height / 2 + box.top];
    }
    x = pointer.x - this.nodeCenter[0];
    y = pointer.y - this.nodeCenter[1];
    this.pointerAngle = Math.atan2(x, y) * 180 / Math.PI;
    this.startAngle = this.state.angle;
    return typeof (base = this.props).onUpdateStart === "function" ? base.onUpdateStart(this.state.angle) : void 0;
  },
  pointerMove: function(e, pointer) {
    var angle, angleDelta, base, offset, x, y;
    e.preventDefault();
    e.stopPropagation();
    if (!pointer.dragging) {
      this.pointerStartMove(e, pointer);
    }
    x = pointer.x - this.nodeCenter[0];
    y = pointer.y - this.nodeCenter[1];
    if (this.props.side === "right") {
      x = Math.max(x, 0);
    } else {
      x = Math.min(x, -0);
    }
    angleDelta = (Math.atan2(x, y) * 180 / Math.PI) - this.pointerAngle;
    angle = utils.clamp(this.startAngle - angleDelta / 2, -45, 45);
    offset = utils.clamp(this.startAngle, -1, 1);
    angle -= utils.clamp(angle + offset, -1, 1) - offset;
    this.setState({
      angle: angle
    });
    return typeof (base = this.props).onUpdate === "function" ? base.onUpdate(angle) : void 0;
  },
  pointerEnd: function(e, pointer) {
    var base;
    if (pointer.dragging) {
      return typeof (base = this.props).onUpdateEnd === "function" ? base.onUpdateEnd(this.state.angle) : void 0;
    }
  },
  doubleTap: function() {
    var base;
    Filtr.set({
      rotation: 0
    });
    if (typeof (base = this.props).onUpdateEnd === "function") {
      base.onUpdateEnd(0);
    }
    return this.setState({
      angle: 0
    });
  },
  render: function() {
    var style;
    style = {
      width: this.state.width / 2 + "px",
      height: this.state.height / 2 + "px"
    };
    return (
      React.createElement(PointerEvents, {
        panY: true, 
        onPointerDown: this.pointerStart, 
        onPointerMove: this.pointerMove, 
        onPointerUp: this.pointerEnd, 
        onDoubleTap: this.doubleTap}, 
        React.createElement("canvas", {
          id: "angle", 
          className: classNames({left: this.props.side === "left"}), 
          width: this.state.width, 
          height: this.state.height, 
          style: style})
      )
    );
  }
});

module.exports = CropOverlay;


}).call(this,require("react"),require("react-dom"))
},{"../actions.coffee":229,"../manager.coffee":338,"../mixins.coffee":339,"../utils.coffee":360,"./pointer-events.coffee":273,"classnames":1,"react":215,"react-addons-pure-render-mixin":32,"react-dom":34}],247:[function(require,module,exports){
(function (React,ReactDOM){
var Curve, PointerEvents, SplineGraph, classnames;

PointerEvents = require("./pointer-events.coffee");

SplineGraph = require("../spline_graph.coffee");

classnames = require("classnames");

Curve = React.createClass({displayName: "Curve",
  componentDidMount: function() {
    return this.curve = new SplineGraph(ReactDOM.findDOMNode(this), {
      onUpdate: this.update,
      onUpdateEnd: this.saveState,
      name: this.props.name,
      color: this.props.color,
      rotated: this.props.rotated,
      width: 300,
      height: 300
    });
  },
  componentDidUpdate: function() {
    this.curve.rotated = this.props.rotated;
    return this.curve.replacePoints(this.props.points, this.props.active);
  },
  shouldComponentUpdate: function(nextProps) {
    return nextProps.points !== this.props.points || nextProps.active !== this.props.active || nextProps.rotated !== this.props.rotated;
  },
  getDefaultProps: function() {
    return {
      active: true,
      color: 'white',
      title: 'labels.curves'
    };
  },
  update: function(points) {
    var state;
    state = {};
    state[this.props.name] = points;
    return Filtr.setState(state);
  },
  saveState: function(changed) {
    if (changed == null) {
      changed = true;
    }
    clearTimeout(this.saveStateTimeout);
    if (changed) {
      return Filtr.saveState(this.props.title);
    }
  },
  reset: function(e) {
    e.stopPropagation();
    e.preventDefault();
    this.update(Filtr.getDefaultState()[this.props.name].slice(0));
    return this.saveState();
  },
  removePoint: function() {
    this.curve.deletePoint();
    return this.saveState();
  },
  render: function() {
    var cn;
    cn = classnames({
      "curves-graph": true,
      "active": this.props.active
    });
    return (
      React.createElement(PointerEvents, {onDoubleTap: this.reset}, 
        React.createElement("canvas", {className: cn, onDoubleClick: this.reset})
      )
    );
  }
});

module.exports = Curve;


}).call(this,require("react"),require("react-dom"))
},{"../spline_graph.coffee":342,"./pointer-events.coffee":273,"classnames":1,"react":215,"react-dom":34}],248:[function(require,module,exports){
(function (React,ReactDOM){
var Curve, CurvesOverlay, Icon, classnames, manager;

Curve = require("./curve.coffee");

Icon = require("./icon.coffee");

manager = require("../manager.coffee");

classnames = require("classnames");

CurvesOverlay = React.createClass({displayName: "CurvesOverlay",
  getInitialState: function() {
    var state, toolbars;
    toolbars = manager.state.toolbars;
    state = Filtr.getState(true);
    return {
      all: state.curves_all,
      red: state.curves_red,
      green: state.curves_green,
      blue: state.curves_blue,
      activeCurve: manager.state["curves_tab"],
      showCurves: toolbars.leftActive === "adjustments" || toolbars.rightActive === "adjustments"
    };
  },
  componentDidMount: function() {
    manager.on("curves_tab", (function(_this) {
      return function(val) {
        return _this.setState({
          activeCurve: val
        });
      };
    })(this));
    manager.on("toolbars", (function(_this) {
      return function(toolbars) {
        return _this.setState({
          showCurves: toolbars.leftActive === "adjustments" || toolbars.rightActive === "adjustments"
        });
      };
    })(this));
    return Filtr.on("history", (function(_this) {
      return function() {
        var state;
        state = Filtr.getState();
        return _this.setState({
          all: state.curves_all,
          red: state.curves_red,
          green: state.curves_green,
          blue: state.curves_blue
        });
      };
    })(this));
  },
  shouldComponentUpdate: function(nextProps, nextState) {
    return nextState.activeCurve || this.state.activeCurve;
  },
  componentDidUpdate: function() {
    if (this.state.activeCurve && this.state.showCurves) {
      return this.show();
    } else {
      return this.hide();
    }
  },
  show: function() {
    var el;
    el = ReactDOM.findDOMNode(this);
    el.style.display = "block";
    return setTimeout(function() {
      el.style.opacity = "1";
      return el.style.pointerEvents = "";
    }, 1);
  },
  hide: function() {
    var el;
    el = ReactDOM.findDOMNode(this);
    el.style.opacity = "0";
    return setTimeout(function() {
      el.style.display = "none";
      return el.style.pointerEvents = "none";
    }, 200);
  },
  removePoint: function(e) {
    var curve;
    curve = this.state.activeCurve;
    return this.refs[curve].removePoint();
  },
  render: function() {
    var cn;
    cn = classnames({
      "curves-overlay": true,
      "active": this.state.activeCurve && this.state.showCurves
    });
    return (
      React.createElement("div", {
        style: this.props.style, 
        className: cn}, 
        React.createElement(Icon, {name: "icon-trash", onTap: this.removePoint}), 
        React.createElement(Curve, {ref: "all", 
               title: "labels.curves labels.rgb", 
               points: this.state.all, 
               active: this.state.activeCurve === "all", 
               rotated: this.props.rotated, 
               color: "white", 
               name: "curves_all"}), 

        React.createElement(Curve, {ref: "red", 
               title: "labels.curves labels.red", 
               points: this.state.red, 
               active: this.state.activeCurve === "red", 
               rotated: this.props.rotated, 
               color: "hsl(0, 100%, 60%)", 
               name: "curves_red"}), 

        React.createElement(Curve, {ref: "green", 
               title: "labels.curves labels.green", 
               points: this.state.green, 
               active: this.state.activeCurve === "green", 
               rotated: this.props.rotated, 
               color: "hsl(120, 100%, 50%)", 
               name: "curves_green"}), 

        React.createElement(Curve, {ref: "blue", 
               title: "labels.curves labels.blue", 
               points: this.state.blue, 
               active: this.state.activeCurve === "blue", 
               rotated: this.props.rotated, 
               color: "hsl(200, 100%, 50%)", 
               name: "curves_blue"})
      )
    );
  }
});

module.exports = CurvesOverlay;


}).call(this,require("react"),require("react-dom"))
},{"../manager.coffee":338,"./curve.coffee":247,"./icon.coffee":262,"classnames":1,"react":215,"react-dom":34}],249:[function(require,module,exports){
(function (React){
var Button, Dialog, Portal, classnames, manager, mousetrap, translate, utils;

Button = require("./button.coffee");

Portal = require("./portal.coffee");

utils = require("../utils.coffee");

manager = require("../manager.coffee");

translate = require("counterpart");

classnames = require("classnames");

mousetrap = require("mousetrap");

Dialog = React.createClass({displayName: "Dialog",
  getDefaultProps: function() {
    return {
      active: false,
      margin: 32
    };
  },
  getInitialState: function() {
    return {
      active: false,
      keyboardHeight: manager.state.keyboardHeight
    };
  },
  componentWillReceiveProps: function(newProps) {
    return this.setState({
      active: newProps.active
    });
  },
  componentDidUpdate: function(prevProps, prevState) {
    var content, dialog, transform;
    if (prevState == null) {
      prevState = {};
    }
    dialog = this.refs.dialog;
    content = this.refs.content;
    transform = this.props.rotated ? "rotate(-90deg)" : "";
    if (!this.state.active) {
      transform += " scale(0.9)";
      content.style.webkitTransform = transform;
      content.style.transform = transform;
    }
    if (prevProps.rotated !== this.props.rotated) {
      content.style.webkitTransform = transform;
      content.style.transform = transform;
    }
    if (!prevState.active && this.state.active) {
      this.onShow((function(_this) {
        return function() {
          if (_this.props.width) {
            content.style.width = _this.props.width;
          }
          if (_this.props.height) {
            content.style.height = _this.props.height;
          }
          content.style.webkitTransform = transform;
          content.style.transform = transform;
          return dialog.style.opacity = 1;
        };
      })(this));
    }
    if (prevState.active && !this.state.active) {
      return this.onHide();
    }
  },
  componentDidMount: function() {
    this.setState({
      active: this.props.active
    });
    this.componentDidUpdate({
      active: false
    });
    return manager.on("keyboardHeight", (function(_this) {
      return function(height) {
        return _this.setState({
          keyboardHeight: height
        });
      };
    })(this));
  },
  onShow: function(callback) {
    var node;
    node = this.refs.dialog;
    node.style.display = "";
    utils.forceRepaint(node);
    requestAnimationFrame((function(_this) {
      return function() {
        var base;
        callback();
        return typeof (base = _this.props).onShow === "function" ? base.onShow() : void 0;
      };
    })(this));
    clearTimeout(this.hideTimeout);
    return mousetrap.bind("esc", (function(_this) {
      return function() {
        return _this.hide();
      };
    })(this));
  },
  onHide: function() {
    var base, node;
    document.activeElement.blur();
    if (typeof (base = this.props).onHide === "function") {
      base.onHide(this.props.name);
    }
    node = this.refs.dialog;
    clearTimeout(this.hideTimeout);
    this.hideTimeout = setTimeout(function() {
      return node.style.display = "none";
    }, 300);
    node.style.opacity = 0;
    return mousetrap.unbind("esc");
  },
  hide: function(e) {
    if (e != null) {
      if (e.target === this.refs.dialog || e.target.className === "close-button") {
        return this.setState({
          active: false
        });
      }
    } else {
      return this.setState({
        active: false
      });
    }
  },
  render: function() {
    var closeButton, contentStyle, dialogClass, dialogStyle, maxHeight, maxWidth, ref;
    dialogClass = classnames("dialog", {
      "fullscreen": this.props.fullscreen
    });
    dialogStyle = {
      display: "none"
    };
    if (CONFIG.platform === "mobile") {
      maxHeight = (window.outerHeight || window.screen.width) - this.props.margin;
      maxWidth = (window.outerWidth || window.screen.height) - this.props.margin;
    } else {
      maxHeight = this.props.maxHeight || window.innerHeight - this.props.margin;
      maxWidth = this.props.maxWidth || window.innerWidth - this.props.margin;
    }
    if (this.props.rotated) {
      ref = [maxWidth, maxHeight], maxHeight = ref[0], maxWidth = ref[1];
    }
    if (this.props.fullscreen) {
      maxHeight = "";
      maxWidth = "";
      if (CONFIG.platform === "mobile" && CONFIG.embedded) {
        if (this.props.rotated) {
          dialogStyle.left = "44px";
        } else {
          dialogStyle.top = "32px";
        }
      }
    } else {
      if (this.props.rotated) {
        dialogStyle.right = this.state.keyboardHeight;
      } else {
        dialogStyle.bottom = this.state.keyboardHeight;
      }
    }
    contentStyle = {
      width: this.props.width || "",
      height: this.props.height || "",
      maxWidth: maxWidth,
      maxHeight: maxHeight
    };
    if (this.props.nopad != null) {
      contentStyle.padding = 0;
    }
    if (this.props.showclose != null) {
      closeButton = React.createElement(Button, {className: "close-button", onTap: this.hide}, React.createElement("i", {className: "icon-remove"}));
    }
    return (
      React.createElement(Portal, {portalId: this.props.name+"-dialog"}, 
        React.createElement("div", {className: dialogClass, 
             style: dialogStyle, 
             ref: "dialog", 
             onTouchTap: this.hide}, 
          closeButton, 
          React.createElement("div", {className: "dialog-content", 
               tabIndex: "-1", 
               ref: "content", 
               style: contentStyle}, 
              this.props.children
          )
        )
      )
    );
  }
});

module.exports = Dialog;


}).call(this,require("react"))
},{"../manager.coffee":338,"../utils.coffee":360,"./button.coffee":241,"./portal.coffee":277,"classnames":1,"counterpart":3,"mousetrap":21,"react":215}],250:[function(require,module,exports){
(function (React){
var AlertDialog, AnnouncementDialog, BeforeAfterDialog, Dialogs, ExportDialog, FilterDialog, PurchaseDialog, QRDialog, ReviewDialog, SettingsDialog, StoreDialog, UserDialog, manager;

manager = require("../manager.coffee");

ReviewDialog = require("./review-dialog.coffee");

AnnouncementDialog = require("./announcement-dialog.coffee");

FilterDialog = require("./filter-dialog.coffee");

ExportDialog = require("./export-dialog.coffee");

StoreDialog = require("./store-dialog.coffee");

PurchaseDialog = require("./purchase-dialog.coffee");

SettingsDialog = require("./settings-dialog.coffee");

UserDialog = require("./user-dialog.coffee");

QRDialog = require("./qr-code-dialog.coffee");

BeforeAfterDialog = require("./before-after-dialog.coffee");

AlertDialog = require("./alert-dialog.coffee");

Dialogs = React.createClass({displayName: "Dialogs",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      dialog: {}
    };
  },
  componentDidMount: function() {
    return manager.on("dialog", (function(_this) {
      return function(dialog) {
        return _this.setState({
          dialog: dialog
        });
      };
    })(this));
  },
  hide: function(dialog) {
    if (this.state.dialog[dialog]) {
      return this.setState({
        dialog: {}
      });
    }
  },
  render: function() {
    return (
      React.createElement("div", {className: "hidden"}, 
        React.createElement(AlertDialog, {rotated: this.props.portraitMode, name: "alert"}), 
        React.createElement(ReviewDialog, {rotated: this.props.portraitMode, active: this.state.dialog["review"], name: "review", onHide: this.hide}), 
        React.createElement(AnnouncementDialog, {rotated: this.props.portraitMode, active: this.state.dialog["announcements"], name: "announcements", onHide: this.hide}), 
        React.createElement(FilterDialog, {rotated: this.props.portraitMode, active: this.state.dialog["create_filter"], name: "create_filter", onHide: this.hide}), 
        React.createElement(ExportDialog, {rotated: this.props.portraitMode, active: this.state.dialog["export_photo"], name: "export_photo", onHide: this.hide}), 
        React.createElement(StoreDialog, {rotated: this.props.portraitMode, active: this.state.dialog["store"], name: "store", onHide: this.hide}), 
        React.createElement(PurchaseDialog, {rotated: this.props.portraitMode, active: this.state.dialog["purchase"], name: "purchase", onHide: this.hide}), 
        React.createElement(SettingsDialog, {rotated: this.props.portraitMode, active: this.state.dialog["settings"], name: "settings", onHide: this.hide}), 
        React.createElement(UserDialog, {rotated: this.props.portraitMode, active: this.state.dialog["user"], name: "user", onHide: this.hide}), 
        React.createElement(QRDialog, {rotated: this.props.portraitMode, active: this.state.dialog["qr_code"], name: "qr_code", onHide: this.hide}), 
        React.createElement(BeforeAfterDialog, {rotated: this.props.portraitMode, active: this.state.dialog["before_after"], name: "before_after", onHide: this.hide})
      )
    );
  }
});

module.exports = Dialogs;


}).call(this,require("react"))
},{"../manager.coffee":338,"./alert-dialog.coffee":237,"./announcement-dialog.coffee":238,"./before-after-dialog.coffee":239,"./export-dialog.coffee":252,"./filter-dialog.coffee":257,"./purchase-dialog.coffee":279,"./qr-code-dialog.coffee":280,"./review-dialog.coffee":282,"./settings-dialog.coffee":284,"./store-dialog.coffee":288,"./user-dialog.coffee":295,"react":215,"react-addons-pure-render-mixin":32}],251:[function(require,module,exports){
(function (React,ReactDOM){
var DropZone, classnames, store, translate;

store = require("../store.coffee");

classnames = require("classnames");

translate = require("counterpart");

DropZone = React.createClass({displayName: "DropZone",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      active: false
    };
  },
  componentDidMount: function() {
    var el, ref, ref1;
    el = ReactDOM.findDOMNode(this);
    document.addEventListener("dragover", (function(_this) {
      return function(e) {
        e.preventDefault();
        return _this.setState({
          active: true
        });
      };
    })(this));
    el.addEventListener("dragend", (function(_this) {
      return function(e) {
        e.preventDefault();
        return _this.setState({
          active: false
        });
      };
    })(this));
    el.addEventListener("dragleave", (function(_this) {
      return function(e) {
        e.preventDefault();
        return _this.setState({
          active: false
        });
      };
    })(this));
    el.addEventListener("drop", (function(_this) {
      return function(e) {
        e.preventDefault();
        _this.setState({
          active: false
        });
        return store.importFiles(e.dataTransfer.files);
      };
    })(this));
    if ((ref = store.manager) != null) {
      ref.waitForMessage("ppe.window.draggingStarted", (function(_this) {
        return function() {
          return _this.setState({
            active: true
          });
        };
      })(this));
    }
    return (ref1 = store.manager) != null ? ref1.waitForMessage("ppe.window.draggingEnded", (function(_this) {
      return function() {
        return _this.setState({
          active: false
        });
      };
    })(this)) : void 0;
  },
  render: function() {
    var classname;
    classname = classnames("drop-zone", {
      active: this.state.active
    });
    return (
      React.createElement("div", {className: classname}, 
        React.createElement("h1", null, translate("status.drop_files")), 
        React.createElement("p", null, translate("status.supported_files"))
      )
    );
  }
});

module.exports = DropZone;


}).call(this,require("react"),require("react-dom"))
},{"../store.coffee":346,"classnames":1,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32,"react-dom":34}],252:[function(require,module,exports){
(function (React){
var Button, Dialog, ExportDialog, FeatureBlock, WatermarkDialog, WatermarkOptions, actions, classNames, io, manager, status, store, translate, utils;

Button = require("./button.coffee");

Dialog = require("./dialog.coffee");

FeatureBlock = require("./feature-block.coffee");

WatermarkOptions = require("./watermark-options.coffee");

WatermarkDialog = require("./watermark-dialog.coffee");

store = require("../store.coffee");

utils = require("../utils.coffee");

status = require("./status-overlay.coffee");

translate = require("counterpart");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

io = require("../io.coffee");

classNames = require("classnames");

ExportDialog = React.createClass({displayName: "ExportDialog",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      type: "single_export",
      name: "",
      width: 1,
      height: 1,
      cropWidth: 1,
      cropHeight: 1,
      lastWidth: 1,
      lastHeight: 1,
      quality: 90,
      useWatermark: false,
      format: 'jpg',
      estimate: '...',
      curImage: '',
      resizeMethod: "none",
      renameMethod: "none",
      renamePrefix: "polarr_export_",
      linked: true,
      unit: 'px',
      resolution: 300,
      lastResolution: 300,
      filter: null
    };
  },
  componentWillReceiveProps: function(nextProps) {
    var active;
    active = nextProps.active;
    if (active) {
      return this.setState({
        type: active.type
      });
    }
  },
  onShow: function() {
    var format, info, parts, ref, resize;
    parts = store.getParts(store.currentPhoto);
    if (this.state.type === "batch_export") {
      info = {
        cropWidth: 2048,
        cropHeight: 2048
      };
    } else {
      info = Filtr.getInfo();
    }
    format = parts.extension === "png" ? "png" : "jpg";
    resize = this.state.type === "single_export" ? "stretch" : "none";
    this.setState({
      curImage: store.currentPhoto,
      format: format,
      resizeMethod: resize,
      renameMethod: "none",
      name: parts.name,
      width: info.cropWidth,
      height: info.cropHeight,
      lastWidth: info.cropWidth,
      lastHeight: info.cropHeight,
      cropWidth: info.cropWidth,
      cropHeight: info.cropHeight,
      filter: null
    });
    if ((ref = this.refs.input) != null) {
      ref.focus();
    }
    return this.estimateSize();
  },
  estimateSize: function() {
    if (this.state.type === "single_export") {
      this.setState({
        estimate: '...'
      });
      return this._estimateSize();
    }
  },
  _estimateSize: utils.debounce(function() {
    var factor, len, output, scale, size;
    if (!this.props.active) {
      return;
    }
    if (this.state.format === "jpg") {
      factor = 10;
      scale = 4;
    } else {
      factor = 30;
      scale = 6;
    }
    output = Filtr["export"](this.state.format, {
      width: this.state.width / scale,
      height: this.state.height / scale,
      quality: this.state.quality
    });
    if (output.data instanceof Array) {
      len = output.data.reduce(function(total, buf) {
        return total + buf.byteLength;
      }, 0);
    } else {
      len = output.data.length;
    }
    size = Math.round((len * factor) / 1000);
    if (size >= 1000) {
      size /= 1000;
      return this.setState({
        estimate: Math.round(size * 10) / 10 + ' MB'
      });
    } else {
      return this.setState({
        estimate: size + ' KB'
      });
    }
  }, 300),
  editWatermark: function() {
    this.refs.watermark.show();
    return this.setState({
      useWatermark: true
    });
  },
  toggleWatermark: function() {
    return this.setState({
      useWatermark: !this.state.useWatermark
    });
  },
  toggleLinked: function() {
    return this.setState({
      linked: !this.state.linked
    });
  },
  toggleFilter: function(filter) {
    if (this.state.filter === filter) {
      filter = null;
    }
    return this.setState({
      filter: filter
    });
  },
  setUnit: function(unit) {
    return this.setState({
      unit: unit
    });
  },
  setFormat: function(format) {
    this.setState({
      format: format
    });
    return this.estimateSize();
  },
  setResize: function(method) {
    return this.setState({
      resizeMethod: method
    });
  },
  setRename: function(method) {
    return this.setState({
      renameMethod: method
    });
  },
  setName: function(e) {
    return this.setState({
      name: e.target.value
    });
  },
  setPrefix: function(e) {
    return this.setState({
      renamePrefix: e.target.value || "polarr_export_"
    });
  },
  setResolution: function(e) {
    var height, scale, val, width;
    val = parseInt(e.target.value, 10) || 1;
    if (isNaN(val)) {
      val = 1;
    }
    scale = val / (this.state.lastResolution || this.state.resolution);
    width = (this.state.lastWidth || this.state.width) * scale >> 0;
    height = (this.state.lastHeight || this.state.height) * scale >> 0;
    return this.setState({
      resolution: val,
      width: width,
      height: height
    });
  },
  resetSize: function() {
    this.setState({
      width: this.state.cropWidth,
      height: this.state.cropHeight
    });
    return this.estimateSize();
  },
  setWidth: function(e) {
    var height, rat, val, width;
    val = this.parseValue(e.target.value);
    if (this.state.unit !== "px") {
      val = Math.round(val * this.state.resolution);
    }
    width = Math.min(this.state.cropWidth * 2, val);
    this.setState({
      width: width
    });
    if (this.state.linked) {
      rat = width / this.state.cropWidth;
      height = this.parseValue(this.state.cropHeight * rat);
      this.setState({
        height: height
      });
    }
    this.setState({
      lastWidth: width,
      lastHeight: height,
      lastResolution: this.state.resolution
    });
    return this.estimateSize();
  },
  setHeight: function(e) {
    var height, rat, val, width;
    val = this.parseValue(e.target.value);
    if (this.state.unit !== "px") {
      val = Math.round(val * this.state.resolution);
    }
    height = Math.min(this.state.cropHeight * 2, val);
    this.setState({
      height: height
    });
    if (this.state.linked) {
      rat = height / this.state.cropHeight;
      width = this.parseValue(this.state.cropWidth * rat);
      this.setState({
        width: width
      });
    }
    this.setState({
      lastWidth: width,
      lastHeight: height,
      lastResolution: this.state.resolution
    });
    return this.estimateSize();
  },
  getDimensions: function() {
    var height, ref, width;
    ref = this.state, width = ref.width, height = ref.height;
    if (this.state.unit !== "px") {
      width = (width / this.state.resolution).toFixed(2);
      height = (height / this.state.resolution).toFixed(2);
    }
    return {
      width: width,
      height: height
    };
  },
  setQuality: function(val) {
    this.setState({
      quality: Math.min(100, val)
    });
    return this.estimateSize();
  },
  changeQuality: function(e) {
    var val;
    val = this.parseValue(e.target.value, 100);
    return this.setQuality(val);
  },
  parseValue: function(val, max) {
    if (this.state.unit === "px") {
      val = parseInt(val, 10);
    } else {
      val = parseFloat(val);
    }
    if (isNaN(val)) {
      return "";
    }
    if (max) {
      val = Math.min(val, max);
    }
    return Math.max(val, 1);
  },
  validateState: function() {
    return this.setState({
      width: Math.max(this.state.width, 1),
      height: Math.max(this.state.height, 1),
      name: this.state.name || "polarr_exported_image",
      renamePrefix: this.state.renamePrefix || "polarr_export_",
      quality: utils.clamp(this.state.quality, 0, 100)
    });
  },
  getOptions: function() {
    return {
      filetype: this.state.format,
      filename: this.state.name,
      quality: this.state.quality,
      width: this.state.width,
      height: this.state.height,
      watermark: this.state.useWatermark,
      resize: this.state.resizeMethod,
      renameMethod: this.state.renameMethod,
      renamePrefix: this.state.renamePrefix,
      filter: this.state.filter
    };
  },
  hide: function() {
    return actions.setDialog("export_photo", false);
  },
  "export": function() {
    if (this.state.type === "batch_export") {
      io.batchExport(this.getOptions());
    } else {
      io["export"]('copy', this.state.curImage, this.getOptions());
    }
    return this.hide();
  },
  submit: function(e) {
    e.preventDefault();
    this.validateState();
    return this["export"]();
  },
  render: function() {
    var adjustmentSettings, dimensions, featureBlock, header, linkClass, prefixSettings, q, quality, qualitySettings, renameSettings, resizeSettings, resolutionLabel, resolutionSettings, saveText, showPrefixSettings, showQualitySettings, showResizeSettings, showResolutionSettings, showSizeEstimate, showSizeSettings, sizeEstimate, sizeLabel, sizeSettings;
    linkClass = classNames("icon-link", {
      "active": this.state.linked
    });
    dimensions = this.getDimensions();
    if (this.state.type === "batch_export") {
      header = translate("menus.batch_export");
      saveText = translate("buttons.save") + (" (" + store.photos.length + ")");
    } else {
      header = translate("menus.single_export");
      saveText = translate("buttons.save");
    }
    showPrefixSettings = this.state.renameMethod !== "none" && this.state.type === "batch_export";
    showSizeSettings = this.state.resizeMethod !== "none" || this.state.type === "single_export";
    showResolutionSettings = this.state.unit !== "px" && showSizeSettings;
    showQualitySettings = this.state.format === "jpg";
    showSizeEstimate = this.state.type === "single_export";
    showResizeSettings = this.state.type === "batch_export";
    if (this.state.type === "batch_export") {
      featureBlock = React.createElement(FeatureBlock, {container: "batch_export", item: "pro_features"});
    }
    if (showQualitySettings) {
      q = this.state.quality;
      quality = (function() {
        switch (false) {
          case !(q <= 50):
            return "low";
          case !(q <= 70):
            return "med";
          case !(q <= 90):
            return "high";
          case q !== 100:
            return "best";
        }
      })();
      qualitySettings = 
        React.createElement("div", {className: "option"}, 
          React.createElement("span", {className: "label"}, translate("labels.quality")), 
          React.createElement("input", {onChange: this.changeQuality, 
                 onBlur: this.validateState, 
                 className: "narrow", 
                 type: "text", 
                 value: this.state.quality}), 
          React.createElement("div", {className: "button-group flex"}, 
            React.createElement("div", {className: "tabs"}, 
              React.createElement(Button, {type: "button", active: quality === "low", onTap: this.setQuality.bind(this, 50), content: "labels.low"}), 
              React.createElement(Button, {type: "button", active: quality === "med", onTap: this.setQuality.bind(this, 70), content: "labels.med"}), 
              React.createElement(Button, {type: "button", active: quality === "high", onTap: this.setQuality.bind(this, 90), content: "labels.high"}), 
              React.createElement(Button, {type: "button", active: quality === "best", onTap: this.setQuality.bind(this, 100), content: "labels.best"})
            )
          )
        )
      ;
    }
    if (showResolutionSettings) {
      resolutionLabel = this.state.unit === "cm" ? "Pixels/Centimeter" : "Pixels/Inch";
      resolutionSettings = 
        React.createElement("div", {className: "option"}, 
          React.createElement("span", {className: "label"}, translate("labels.resolution")), 
          React.createElement("input", {onChange: this.setResolution, 
                 onBlur: this.validateState, 
                 className: "narrow", 
                 type: "text", 
                 value: this.state.resolution}), 
          React.createElement("span", {className: "input-append"}, resolutionLabel)
        )
      ;
    }
    if (showResizeSettings) {
      resizeSettings = 
        React.createElement("div", {className: "option"}, 
          React.createElement("span", {className: "label"}, translate("labels.batch_resize")), 
          React.createElement("div", {className: "button-group"}, 
            React.createElement("div", {className: "tabs"}, 
              React.createElement(Button, {type: "button", active: this.state.resizeMethod === "none", onTap: this.setResize.bind(this, "none"), content: "labels.none"}), 
              React.createElement(Button, {type: "button", active: this.state.resizeMethod === "fit", onTap: this.setResize.bind(this, "fit"), content: "labels.fit"}), 
              React.createElement(Button, {type: "button", active: this.state.resizeMethod === "fill", onTap: this.setResize.bind(this, "fill"), content: "labels.crop"})
            )
          )
        )
      ;
    }
    if (showSizeSettings) {
      if (this.state.type === "single_export") {
        sizeLabel = translate("labels.size");
      } else if (this.state.resizeMethod === "fit") {
        sizeLabel = translate("labels.max_size");
      } else {
        sizeLabel = translate("labels.crop_size");
      }
      sizeSettings = 
        React.createElement("div", {className: "option"}, 
          React.createElement("span", {className: "label"}, sizeLabel), 
          React.createElement("input", {onChange: this.setWidth, 
                 onBlur: this.validateState, 
                 className: "narrow", 
                 type: "text", 
                 value: dimensions.width}), 
          React.createElement("span", {className: linkClass, onTouchTap: this.toggleLinked}), 
          React.createElement("input", {onChange: this.setHeight, 
                 onBlur: this.validateState, 
                 className: "narrow", 
                 type: "text", 
                 value: dimensions.height}), 
          React.createElement("div", {className: "button-group flex"}, 
            React.createElement("div", {className: "tabs"}, 
              React.createElement(Button, {type: "button", active: this.state.unit === "px", onTap: this.setUnit.bind(this, 'px')}, "PX"), 
              React.createElement(Button, {type: "button", active: this.state.unit === "cm", onTap: this.setUnit.bind(this, 'cm')}, "CM"), 
              React.createElement(Button, {type: "button", active: this.state.unit === "in", onTap: this.setUnit.bind(this, 'in')}, "INCH")
            )
          )
        );
    }
    if (this.state.type === "batch_export") {
      renameSettings = 
        React.createElement("div", {className: "option"}, 
          React.createElement("span", {className: "label"}, translate("labels.batch_rename")), 
          React.createElement("div", {className: "button-group"}, 
            React.createElement("div", {className: "tabs"}, 
              React.createElement(Button, {type: "button", active: this.state.renameMethod === "none", onTap: this.setRename.bind(this, "none"), content: "labels.none"}), 
              React.createElement(Button, {type: "button", active: this.state.renameMethod === "prefix", onTap: this.setRename.bind(this, "prefix"), content: "labels.use_prefix"})
            )
          )
        )
      ;
      adjustmentSettings = [
        React.createElement("div", {className: "sep"}), React.createElement("div", {className: "option"}, 
          React.createElement("span", {className: "label"}, translate("tooltips.apply_adjustments")), 
          React.createElement("div", {className: "button-group"}, 
            React.createElement("div", {className: "tabs default"}, 
              React.createElement(Button, {type: "button", active: this.state.filter === "auto", onTap: this.toggleFilter.bind(this, 'auto')}, React.createElement("i", {className: "icon-wand"}), translate("labels.auto")), 
              React.createElement(Button, {type: "button", active: this.state.filter === "current", onTap: this.toggleFilter.bind(this, 'current')}, translate("tooltips.current_adjustments"))
            )
          )
        )
      ];
    } else {
      renameSettings = 
        React.createElement("div", {className: "option"}, 
          React.createElement("span", {className: "label"}, translate("labels.name")), 
          React.createElement("input", {onChange: this.setName, 
                 onBlur: this.validateState, 
                 type: "text", 
                 value: this.state.name, 
                 ref: "input"}), 
          React.createElement("span", {className: "input-append"}, ".", this.state.format)
        )
      ;
    }
    if (showPrefixSettings) {
      prefixSettings = 
        React.createElement("div", {className: "option"}, 
          React.createElement("span", {className: "label"}, translate("labels.prefix")), 
          React.createElement("input", {onChange: this.setPrefix, 
                 onBlur: this.validateState, 
                 type: "text", 
                 value: this.state.renamePrefix}), 
          React.createElement("span", {className: "input-append"}, "[1..", store.photos.length, "].", this.state.format)
        );
    }
    if (showSizeEstimate) {
      sizeEstimate = 
        React.createElement("div", {className: "size-estimate-label"}, 
          React.createElement("span", {className: "label"}, translate("labels.estimated_size")), 
          React.createElement("div", {className: "size-estimate"}, this.state.estimate)
        )
      ;
    }
    return (
      React.createElement(Dialog, React.__spread({},  this.props, {onShow: this.onShow}), 
        featureBlock, 
        React.createElement(WatermarkDialog, {ref: "watermark"}), 
        React.createElement("form", {onSubmit: this.submit}, 
          React.createElement("h1", null, header), 
          React.createElement("div", {className: "export-dialog"}, 
            React.createElement("div", {className: "options"}, 
              React.createElement("div", {className: "option"}, 
                React.createElement("span", {className: "label"}, translate("labels.format")), 
                React.createElement("div", {className: "button-group"}, 
                  React.createElement("div", {className: "tabs"}, 
                    React.createElement(Button, {type: "button", active: this.state.format === "jpg", onTap: this.setFormat.bind(this, 'jpg')}, "JPEG"), 
                    React.createElement(Button, {type: "button", active: this.state.format === "png", onTap: this.setFormat.bind(this, 'png')}, "PNG"), 
                    React.createElement(Button, {type: "button", active: this.state.format === "tif", onTap: this.setFormat.bind(this, 'tif')}, "RAW")
                  )
                )
              ), 
              renameSettings, 
              prefixSettings, 
              resizeSettings, 
              sizeSettings, 
              resolutionSettings, 
              qualitySettings, 
              adjustmentSettings, 
              React.createElement("div", {className: "sep"}), 
              React.createElement("div", {className: "option"}, 
                React.createElement("span", {className: "label"}, translate("labels.use_watermark")), 
                React.createElement("input", {type: "checkbox", checked: this.state.useWatermark}), 
                React.createElement("i", {className: "checkbox", onTouchTap: this.toggleWatermark}), 
                React.createElement(Button, {type: "button", onTap: this.editWatermark}, translate("menus.edit_watermark")), 
                sizeEstimate
              )
            ), 
            React.createElement("div", {className: "footer"}, 
              React.createElement(Button, {type: "button", className: "left", onTap: this.hide, content: "buttons.cancel"}), 
              React.createElement(Button, {type: "submit", className: "primary"}, saveText)
            )
          )
        )
      )
    );
  }
});

module.exports = ExportDialog;


}).call(this,require("react"))
},{"../actions.coffee":229,"../io.coffee":312,"../manager.coffee":338,"../store.coffee":346,"../utils.coffee":360,"./button.coffee":241,"./dialog.coffee":249,"./feature-block.coffee":255,"./status-overlay.coffee":287,"./watermark-dialog.coffee":300,"./watermark-options.coffee":301,"classnames":1,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],253:[function(require,module,exports){
(function (React){
var Handle, Icon, MaskOverlay, Menu, Overlay, PointerEvents, Rect, ViewContainer, ViewportOverlay, actions, manager, ref, translate;

Icon = require("./icon.coffee");

ViewContainer = require("./view-container.coffee");

PointerEvents = require("./pointer-events.coffee");

Menu = require("./popover-menu.coffee");

Overlay = require("./overlay.coffee");

ref = require("./viewport-overlay.coffee"), ViewportOverlay = ref.ViewportOverlay, Rect = ref.Rect, Handle = ref.Handle;

actions = require("../actions.coffee");

manager = require("../manager.coffee");

translate = require("counterpart");

MaskOverlay = React.createClass({displayName: "MaskOverlay",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      distortion: 0,
      faces: []
    };
  },
  componentDidMount: function() {
    return Filtr.on("history", (function(_this) {
      return function() {
        var state;
        state = Filtr.getState();
        return _this.setState({
          faces: Filtr.utils.clone(state.faces || [])
        });
      };
    })(this));
  },
  componentWillReceiveProps: function(nextProps) {
    if (nextProps.active && !this.props.active) {
      return this.setState({
        distortion: 0
      });
    }
  },
  addFace: function() {
    return actions.addFace();
  },
  removeFace: function() {
    return actions.removeFace(this.currentFace);
  },
  setBounds: function(index, bounds) {
    var faces;
    faces = this.state.faces.slice(0);
    faces[index].boundaries = bounds;
    return this.setState({
      faces: faces
    });
  },
  setDistortion: function(radius) {
    if (this.state.distortion === radius) {
      radius = 0;
    }
    return this.setState({
      distortion: radius
    });
  },
  deactivateViewControls: function() {
    return this.setState({
      distortion: 0
    });
  },
  pressFace: function(e, input, component) {
    var data;
    data = component.props.data;
    if (!data) {
      return;
    }
    this.currentFace = data.id;
    return this.refs.menu.show({
      target: e.target,
      offset: [8, 0]
    });
  },
  pointerMoveFace: function(e, input, component) {
    var data, delta, point;
    data = component.props.data;
    if (!data) {
      return;
    }
    if (input.multitouch) {
      return;
    }
    if (!this.state.distortion) {
      point = Filtr.getMouseCoords(input.x, input.y);
      point[1] = 1 - point[1];
      if (!input.dragging) {
        this.startBounds = this.state.faces[data.id].boundaries.slice(0);
        this.startPoint = point;
      }
      delta = [point[0] - this.startPoint[0], point[1] - this.startPoint[1]];
      this.setBounds(data.id, [this.startBounds[0] + delta[0] * data.mul[0], this.startBounds[1] + delta[1] * data.mul[0], this.startBounds[2] + delta[0] * data.mul[1], this.startBounds[3] + delta[1] * data.mul[1]]);
      e.stopImmediatePropagation();
      return input.stopPropagation();
    }
  },
  pointerUpFace: function(e, input, component) {
    var data;
    data = component.props.data;
    if (!data) {
      return;
    }
    if (input.multitouch) {
      return;
    }
    if (input.dragging) {
      Filtr.setState({
        faces: this.state.faces
      });
      return Filtr.saveState("tooltips.move_face");
    }
  },
  pointerDown: function(e, input) {
    if (input.multitouch) {
      return false;
    }
  },
  pointerMove: function(e, input) {
    var aspect, delta, dist, i, j, len, point, radius, ref1, strength, vertex, vertices, weight;
    if (!this.state.distortion || input.multitouch) {
      return;
    }
    e.stopImmediatePropagation();
    radius = this.state.distortion;
    strength = 0.3;
    aspect = Filtr._.texture.width / Filtr._.texture.height;
    point = Filtr.getMouseCoords(input.x, input.y, true);
    if (!input.dragging) {
      this.prevPoint = point;
    }
    delta = [point[0] - this.prevPoint[0], point[1] - this.prevPoint[1]];
    vertices = [];
    ref1 = Filtr._.mesh.distortion;
    for (i = j = 0, len = ref1.length; j < len; i = ++j) {
      vertex = ref1[i];
      if (Math.abs(vertex[0]) === 1 || Math.abs(vertex[1]) === 1) {
        vertices[i] = vertex;
        continue;
      }
      dist = Filtr.utils.distance([point[0] * aspect, point[1]], [vertex[0] * aspect, vertex[1]]);
      if (dist <= radius) {
        dist = Math.min(1, dist / radius);
        weight = Filtr.utils.gaussian(dist) * strength;
        vertices[i] = [vertex[0] + delta[0] * weight, vertex[1] + delta[1] * weight];
      } else {
        vertices[i] = vertex;
      }
    }
    Filtr._.mesh.distortion = vertices;
    Filtr._.mesh.compile();
    Filtr.render();
    return this.prevPoint = point;
  },
  pointerUp: function(e, input) {
    var distortion_mesh, i, j, len, ref1, vertex;
    if (input.dragging && this.state.distortion && !input.multitouch) {
      distortion_mesh = [];
      ref1 = Filtr._.mesh.distortion;
      for (i = j = 0, len = ref1.length; j < len; i = ++j) {
        vertex = ref1[i];
        distortion_mesh.push(vertex[0]);
        distortion_mesh.push(vertex[1]);
      }
      Filtr.setState({
        distortion_mesh: distortion_mesh
      });
      return Filtr.saveState("labels.distortion");
    }
  },
  resetDistortion: function() {
    var message;
    message = "buttons.reset labels.distortion";
    Filtr.setState({
      distortion_mesh: null
    });
    Filtr.saveState(message);
    return actions.showStatus(utils.translateMessage(message), 1000);
  },
  render: function() {
    var face, h, i, j, len, pointerEvents, props, rects, ref1, ref2, w, x, y;
    rects = [];
    ref1 = this.state.faces;
    for (i = j = 0, len = ref1.length; j < len; i = ++j) {
      face = ref1[i];
      ref2 = face.boundaries, x = ref2[0], y = ref2[1], w = ref2[2], h = ref2[3];
      props = {
        key: i,
        x: x,
        y: y,
        width: w,
        height: h,
        borderRadius: 8,
        border: "1px solid yellow",
        data: {
          id: i,
          mul: [1, 0]
        },
        disabled: !!this.state.distortion
      };
      if (face.markers) {
        rects.push(
          React.createElement(Rect, React.__spread({},  props, {disabled: true}))
        );
      } else if (face.boundaries) {
        rects.push(
          React.createElement(Rect, React.__spread({},  props), 
            React.createElement(Handle, {fill: "yellow", position: [1, 1], data: {id: i, mul: [0, 1]}})
          )
        );
      }
    }
    pointerEvents = {
      threshold: 0,
      multitouch: true,
      ignoreTouchTarget: true,
      onPointerDown: this.pointerDown,
      onPointerMove: this.pointerMove,
      onPointerUp: this.pointerUp
    };
    return (
      React.createElement(Overlay, {pointerEvents: pointerEvents, rotated: this.props.rotated, active: this.props.active, passthrough: !this.state.distortion}, 
        React.createElement(Menu, {ref: "menu"}, 
          React.createElement("a", {onTouchTap: this.removeFace}, React.createElement("i", {className: "icon-remove"}), translate("tooltips.remove_face"))
        ), 
        React.createElement(ViewportOverlay, {
          passthrough: !this.state.distortion, 
          active: this.props.active, 
          disabled: this.state.distortion, 
          onLongPress: this.pressFace, 
          onPointerMove: this.pointerMoveFace, 
          onPointerUp: this.pointerUpFace}, 
          rects
        ), 
        React.createElement(ViewContainer, {className: "start", rotated: this.props.rotated, sliderActivated: this.deactivateViewControls}, 
          React.createElement(Icon, {name: "icon-distort-less", active: this.state.distortion === 0.2, onTap: this.setDistortion.bind(this, 0.2)}), 
          React.createElement(Icon, {name: "icon-distort-more", active: this.state.distortion === 0.5, onTap: this.setDistortion.bind(this, 0.5)}), 
          React.createElement(Icon, {name: "icon-reset", onTap: this.resetDistortion}), 
          React.createElement(Icon, {name: "icon-add-face", onTap: this.addFace})
        )
      )
    );
  }
});

module.exports = MaskOverlay;


}).call(this,require("react"))
},{"../actions.coffee":229,"../manager.coffee":338,"./icon.coffee":262,"./overlay.coffee":270,"./pointer-events.coffee":273,"./popover-menu.coffee":276,"./view-container.coffee":296,"./viewport-overlay.coffee":298,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],254:[function(require,module,exports){
(function (React){
var Button, ColorPicker, FaceMaskOverlay, FacePanel, Icon, Menu, Panel, PointerEvents, Portal, Scrollable, Slider, SliderPad, actions, classNames, manager, mixins, store, translate, utils;

Button = require("./button.coffee");

Icon = require("./icon.coffee");

Menu = require("./popover-menu.coffee");

Portal = require("./portal.coffee");

FaceMaskOverlay = require("./face-mask-overlay.coffee");

PointerEvents = require("./pointer-events.coffee");

Panel = require("./panel.coffee");

Slider = require("./slider.coffee");

SliderPad = require("./slider-pad.coffee");

ColorPicker = require("./color-picker.coffee");

Scrollable = require("./scrollable.coffee");

mixins = require("../mixins.coffee");

utils = require("../utils.coffee");

actions = require("../actions.coffee");

manager = require("../manager.coffee");

store = require("../store.coffee");

translate = require("counterpart");

classNames = require("classnames");

FacePanel = React.createClass({displayName: "FacePanel",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      faces: [],
      skin_smoothness: 0,
      skin_texture: 0,
      skin_tone: 0,
      skin_hue: 0,
      teeth_brightness: 0,
      teeth_whitening: 0,
      eyes_brightness: 0,
      eyes_clarity: 0,
      lips_brightness: 0,
      lips_saturation: 0
    };
  },
  componentDidMount: function() {
    Filtr.on("history", (function(_this) {
      return function() {
        var state;
        state = Filtr.getState();
        return _this.setState({
          faces: state.faces || [],
          skin_smoothness: state.skin_smoothness,
          skin_texture: state.skin_texture,
          skin_tone: state.skin_tone,
          skin_hue: state.skin_hue,
          teeth_whitening: state.teeth_whitening,
          teeth_brightness: state.teeth_brightness,
          eyes_brightness: state.eyes_brightness,
          eyes_clarity: state.eyes_clarity,
          lips_brightness: state.lips_brightness,
          lips_saturation: state.lips_saturation
        });
      };
    })(this));
    return store.on("loadImage", (function(_this) {
      return function() {
        return _this.scrollTo(0);
      };
    })(this));
  },
  componentDidUpdate: function() {
    if (this.props.active) {
      return manager.emit("onetime_prompt", {
        id: "face",
        message: utils.translateReplace("status.show_me_how", {
          thing: translate("tooltips.face")
        }),
        button: translate("buttons.show_me"),
        action: function() {
          return manager.emit("tutorial_id", "face_tools");
        }
      });
    }
  },
  hasMarkers: function() {
    var face, i, len, ref;
    ref = this.state.faces;
    for (i = 0, len = ref.length; i < len; i++) {
      face = ref[i];
      if (face.markers) {
        return true;
      }
    }
    return false;
  },
  scrollTo: function(scrollTop) {
    var el;
    el = this.refs.scrollable.node;
    return Filtr.utils.animate(el.scrollTop, scrollTop, 0.3, function(val) {
      return el.scrollTop = val[0];
    });
  },
  autoAdjust: function() {
    return actions.autoAdjustFace();
  },
  reset: function() {
    var state;
    state = this.getInitialState();
    Filtr.setState({
      skin_smoothness: state.skin_smoothness,
      skin_texture: state.skin_texture,
      skin_tone: state.skin_tone,
      skin_hue: state.skin_hue,
      teeth_whitening: state.teeth_whitening,
      teeth_brightness: state.teeth_brightness,
      eyes_brightness: state.eyes_brightness,
      eyes_clarity: state.eyes_clarity,
      lips_brightness: state.lips_brightness,
      lips_saturation: state.lips_saturation
    });
    return Filtr.saveState("buttons.reset labels.face");
  },
  loadSampleImage: function() {
    var url;
    url = store.getSampleDir() + "sample_faces.jpg";
    return store.loadImageURL(url);
  },
  addFace: function() {
    return actions.addFace();
  },
  Slider: function(opts) {
    var SliderControl;
    if (opts == null) {
      opts = {};
    }
    SliderControl = CONFIG.platform === "mobile" ? SliderPad : Slider;
    return React.createElement(SliderControl, {
      tabIndex: this.tabIndex++, 
      label: opts.label, 
      prop: opts.name, 
      tempSettings: opts.temp, 
      value: this.state[opts.name], 
      onUpdate: Filtr.setState, 
      rotated: this.props.rotated, 
      group: opts.group || "", 
      mul: (opts.mul != null) ? opts.mul : 100, 
      min: (opts.min != null) ? opts.min : -1, 
      max: (opts.max != null) ? opts.max : 1, 
      ref: opts.name + "-slider", 
      position: this.props.position}
    );
  },
  ColorPicker: function(opts) {
    if (opts == null) {
      opts = {};
    }
    return React.createElement(ColorPicker, {
      tabIndex: this.tabIndex++, 
      name: opts.label, 
      value: this.state[opts.name], 
      rotated: this.props.rotated, 
      update: function(val){
        obj = {}
        obj[opts.name] = val
        Filtr.setState(obj)
      }, 
      position: this.props.position}
    );
  },
  render: function() {
    var autoButton, featuresClass, hasFaces, hasMarkers, shadeClass;
    hasFaces = this.state.faces && this.state.faces.length;
    hasMarkers = this.hasMarkers();
    if (CONFIG.platform === "mobile") {
      autoButton = React.createElement(Button, {onTap: this.autoAdjust}, React.createElement("i", {className: "icon-wand"}), translate("labels.auto"));
    }
    shadeClass = classNames("shade", {
      active: !hasFaces
    });
    featuresClass = classNames("features", {
      active: hasMarkers || !hasFaces
    });
    return (
      React.createElement(Panel, React.__spread({},  this.props), 

        React.createElement(Portal, {portalId: "face-controls"}, 
          React.createElement(FaceMaskOverlay, {
            rotated: this.props.rotated, 
            active: this.props.active})
        ), 

        React.createElement("div", {className: "action-top"}, 
          React.createElement("div", {className: "title"}, "Face"), 
          React.createElement(Icon, {name: "icon-wand", disabled: !hasFaces, onTap: this.autoAdjust, tooltip: translate("labels.auto")})
        ), 

        React.createElement(Scrollable, {ref: "scrollable"}, 
          React.createElement("div", {className: shadeClass}, 
            autoButton, 
            React.createElement("h3", null, translate("labels.skin")), 
             this.Slider({label: "labels.smoothness", name: "skin_smoothness", group: "labels.skin", min: 0}), 
             this.Slider({label: "labels.texture", name: "skin_texture", group: "labels.skin", min: 0}), 
             this.Slider({label: "labels.tone", name: "skin_tone", group: "labels.skin"}), 
             this.Slider({label: "labels.hue", name: "skin_hue", group: "labels.skin"}), 
            React.createElement("div", {className: featuresClass}, 
              React.createElement("h3", null, translate("labels.teeth")), 
               this.Slider({label: "labels.whitening", name: "teeth_whitening", group: "labels.teeth", min: 0}), 
               this.Slider({label: "labels.brightness", name: "teeth_brightness", group: "labels.teeth", min: 0}), 
              React.createElement("h3", null, translate("labels.eyes")), 
               this.Slider({label: "labels.brightness", name: "eyes_brightness", group: "labels.eyes", min: 0}), 
               this.Slider({label: "labels.clarity", name: "eyes_clarity", group: "labels.eyes", min: 0}), 
              React.createElement("h3", null, translate("labels.lips")), 
               this.Slider({label: "labels.brightness", name: "lips_brightness", group: "labels.lips", min: 0}), 
               this.Slider({label: "labels.saturation", name: "lips_saturation", group: "labels.lips"}) 
            ), 
            React.createElement("div", {className: "sep"})
          ), 
          React.createElement("div", {className: "shade-text"}, 
            React.createElement("span", null, translate("status.no_faces")), 
            React.createElement("span", {onTouchTap: this.addFace, dangerouslySetInnerHTML: {__html:translate("status.add_face")}}), 
            React.createElement("br", null), 
            React.createElement(Button, {className: "alt", onTap: this.loadSampleImage}, translate("buttons.open_sample"))
          ), 
          React.createElement(Button, {onTap: this.reset}, React.createElement("i", {className: "icon-reset"}), translate("buttons.reset"))
        )

      )
    );
  }
});

module.exports = FacePanel;


}).call(this,require("react"))
},{"../actions.coffee":229,"../manager.coffee":338,"../mixins.coffee":339,"../store.coffee":346,"../utils.coffee":360,"./button.coffee":241,"./color-picker.coffee":243,"./face-mask-overlay.coffee":253,"./icon.coffee":262,"./panel.coffee":272,"./pointer-events.coffee":273,"./popover-menu.coffee":276,"./portal.coffee":277,"./scrollable.coffee":283,"./slider-pad.coffee":285,"./slider.coffee":286,"classnames":1,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],255:[function(require,module,exports){
(function (React){
var Button, FeatureBlock, PointerEvents, actions, manager, store, translate;

manager = require("../manager.coffee");

store = require("../store.coffee");

actions = require("../actions.coffee");

translate = require("counterpart");

PointerEvents = require("./pointer-events.coffee");

Button = require("./button.coffee");

FeatureBlock = React.createClass({displayName: "FeatureBlock",
  getInitialState: function() {
    return {
      locked: !this.isUnlocked(),
      bypass: store.currentPhoto === store.sampleImage
    };
  },
  componentDidMount: function() {
    this.removePurchasesListener = manager.on("purchases", this.resetState);
    this.removeUserListener = manager.on("user", this.resetState);
    return store.on("loadImage", this.resetState);
  },
  componentWillUnmount: function() {
    this.removePurchasesListener();
    this.removeUserListener();
    return store.off("loadImage", this.resetState);
  },
  resetState: function() {
    if (this.isMounted()) {
      return this.setState(this.getInitialState());
    }
  },
  isUnlocked: function() {
    return actions.isPurchased(PURCHASE_KEYS_MAP[this.props.item] || this.props.item);
  },
  preview: function(e) {
    e.stopPropagation();
    return actions.setDialog("store", {
      id: this.props.item,
      from: this.props.container
    });
  },
  showPurchaseDialog: function(e) {
    e.stopPropagation();
    if (e.target.tagName === "BUTTON") {
      return;
    }
    if (CONFIG.platform === "mobile") {
      return actions.setDialog("store", {
        id: this.props.item,
        from: this.props.container
      });
    } else {
      return actions.openStore();
    }
  },
  renderPreviewButton: function() {
    if (this.props.container === "looks") {
      return React.createElement(Button, {className: "alt", content: "buttons.preview", onTap: this.preview});
    }
  },
  renderLabel: function() {
    if (CONFIG.os === "osx") {
      return React.createElement("div", {className: "label"}, translate("status.purchase_required"));
    }
  },
  render: function() {
    if (this.state.locked && !(this.state.bypass && this.props.bypass)) {
      return (
        React.createElement(PointerEvents, {onTap: this.showPurchaseDialog}, 
          React.createElement("span", {className: "shade-lock"}, 
            React.createElement("span", {className: "shade"}, 
              React.createElement("span", {className: "lock"}
              ), 
              this.renderLabel(), 
              this.renderPreviewButton()
            )
          )
        )
      );
    } else {
      return null;
    }
  }
});

module.exports = FeatureBlock;


}).call(this,require("react"))
},{"../actions.coffee":229,"../manager.coffee":338,"../store.coffee":346,"./button.coffee":241,"./pointer-events.coffee":273,"counterpart":3,"react":215}],256:[function(require,module,exports){
(function (React,ReactDOM){
var $, Button, Filmstrip, Icon, Menu, Mousetrap, Panel, PointerEvents, Scrollable, classnames, io, keyboard, mixins, store, translate, utils;

Button = require("./button.coffee");

Icon = require("./icon.coffee");

Panel = require("./panel.coffee");

Menu = require("./popover-menu.coffee");

Scrollable = require("./scrollable.coffee");

PointerEvents = require("./pointer-events.coffee");

store = require("../store.coffee");

mixins = require("../mixins.coffee");

utils = require("../utils.coffee");

io = require("../io.coffee");

keyboard = require("../keyboard.coffee");

translate = require("counterpart");

classnames = require("classnames");

Mousetrap = require("mousetrap");

$ = require("jquery");

Filmstrip = React.createClass({displayName: "Filmstrip",
  mixins: [mixins.touchRipple, require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      photos: store.photos,
      currentPhoto: store.currentPhoto
    };
  },
  componentDidMount: function() {
    var mousetrap;
    store.on("photos", (function(_this) {
      return function() {
        return _this.setState(_this.getInitialState());
      };
    })(this));
    store.on("currentPhoto", (function(_this) {
      return function() {
        return _this.setState(_this.getInitialState());
      };
    })(this));
    store.on("thumbnail", this.updateThumb);
    store.on("extra", this.updateExtra);
    mousetrap = new Mousetrap(document);
    return mousetrap.bind(keyboard.shortcuts.remove_photo, this.removeCurrentPhoto);
  },
  componentDidUpdate: function() {
    return this.updateThumbnails();
  },
  scrollToActive: function() {
    var active, from, height, ref1, scrollable, to;
    scrollable = ReactDOM.findDOMNode(this.refs.scrollable);
    active = (ref1 = ReactDOM.findDOMNode(this.refs[this.state.currentPhoto])) != null ? ref1.parentNode : void 0;
    if (!active) {
      return;
    }
    height = scrollable.clientHeight;
    from = scrollable.scrollTop;
    to = active.offsetTop + active.clientHeight / 2 - height / 2;
    if (this.cancelAnimation != null) {
      this.cancelAnimation();
    }
    return this.cancelAnimation = Filtr.utils.animate(from, to, 0.2, function(val) {
      return scrollable.scrollTop = val[0];
    });
  },
  setExtra: utils.debounce(function() {
    return this.setState(this.extra);
  }, 300),
  updateExtra: function(id, info) {
    if (info == null) {
      info = {};
    }
    this.extra = this.extra || {};
    this.extra[id] = info;
    return this.setExtra();
  },
  updateThumbnails: function() {
    var i, update;
    i = this.state.photos.length;
    update = (function(_this) {
      return function() {
        var id, ref;
        i--;
        if (i < 0) {
          return;
        }
        id = _this.state.photos[i];
        ref = _this.refs[id];
        if (ref.className !== "updated") {
          ref.className = "updated";
          return store.get("extra", id, function(extra) {
            if (CONFIG.platform === "electron") {
              _this.updateExtra(id, extra);
              update();
              return;
            }
            return store.get("thumbnail", id, function(data) {
              _this.updateThumb(id, data);
              _this.updateExtra(id, extra);
              return update();
            });
          });
        } else {
          _this.updateLayout();
          return update();
        }
      };
    })(this);
    return update();
  },
  updateThumb: function(id, data) {
    var canvas;
    canvas = this.refs[id];
    if (canvas && data) {
      canvas.width = data.width;
      canvas.height = data.height;
      canvas.className = "updated";
      utils.drawImageData(canvas, data);
    }
    return this.updateLayout();
  },
  updateLayout: utils.debounce(function() {
    var col, container, height, j, len, offset, photo, prevOffsets, ref1, results;
    prevOffsets = [0, 0];
    ref1 = this.state.photos;
    results = [];
    for (j = 0, len = ref1.length; j < len; j++) {
      photo = ref1[j];
      container = this.refs[photo].parentElement;
      height = container.offsetHeight;
      offset = Math.min.apply(this, prevOffsets);
      col = offset === prevOffsets[0] ? 0 : 1;
      container.style.top = offset + "px";
      container.style.left = 50 * col + "%";
      container.style.opacity = 1;
      results.push(prevOffsets[col] = offset + height);
    }
    return results;
  }, 10),
  showPhotoMenu: function(e) {
    var el;
    el = $(e.target).closest(".photo")[0];
    if (!el) {
      return;
    }
    this.selectedPhoto = el.dataset.photo;
    return this.refs.photoMenu.show({
      target: el,
      offset: [8, -8]
    });
  },
  showMenu: function(e) {
    return this.refs.menu.show({
      target: e.target,
      offset: [8, -8]
    });
  },
  touchStart: function(e) {
    var el;
    el = $(e.target).closest(".photo")[0];
    if (!el) {
      return;
    }
    return this.touchRippleStart(e, el);
  },
  touchTap: function(e) {
    var el, photo;
    el = $(e.target).closest(".photo")[0];
    if (!el) {
      return;
    }
    photo = el.dataset.photo;
    if (e.target.className.indexOf("remove") > 0) {
      return this.removePhoto(el.dataset.photo);
    } else {
      return store.loadPhoto(photo);
    }
  },
  touchCancel: function(e) {
    return this.touchRippleEnd();
  },
  longPress: function(e) {
    return this.showPhotoMenu(e);
  },
  removeSelectedPhoto: function() {
    return this.removePhoto(this.selectedPhoto);
  },
  removeCurrentPhoto: function() {
    if (!this.props.active) {
      return;
    }
    return this.removePhoto(this.state.currentPhoto);
  },
  removeAllPhotos: function() {
    return store.removeAllPhotos();
  },
  removePhoto: function(id) {
    return store.removePhoto(id);
  },
  importPhotos: function() {
    return io.open();
  },
  showInFolder: function() {
    var file;
    file = store.photosMap[this.selectedPhoto];
    return Electron.shell.showItemInFolder(file.path);
  },
  render: function() {
    var classname, i, j, len, list, photo, photos, ref1, ref2, showInFolder;
    list = [];
    photos = this.state.photos || [];
    for (i = j = 0, len = photos.length; j < len; i = ++j) {
      photo = photos[i];
      classname = classnames("photo", {
        active: photo === this.state.currentPhoto,
        saved: (ref1 = this.state[photo]) != null ? ref1.saved : void 0,
        edited: (ref2 = this.state[photo]) != null ? ref2.edited : void 0
      });
      list.push(
        React.createElement("div", {
          className: classname, 
          key: photo, 
          "data-photo": photo}, 
          React.createElement("canvas", {ref: photo}), 
          React.createElement("div", {className: "remove"}, 
            React.createElement("i", {className: "icon-remove-photo"})
          ), 
          React.createElement("div", {className: "border"})
        )
      );
    }
    if (CONFIG.platform === "electron") {
      showInFolder = React.createElement("a", {onTouchTap: this.showInFolder}, React.createElement("i", {className: "icon-import"}), translate("buttons.show_in_folder"));
    }
    return (
      React.createElement(Panel, React.__spread({},  this.props), 

        React.createElement(Menu, {ref: "menu"}, 
          React.createElement("a", {onTouchTap: this.importPhotos}, React.createElement("i", {className: "icon-import"}), translate("buttons.import_photos"), keyboard.renderShortcut("open")), 
          React.createElement("div", {className: "sep"}), 
          React.createElement("a", {onTouchTap: this.removeCurrentPhoto}, React.createElement("i", {className: "icon-remove-photo"}), translate("buttons.remove_selected_photo"), keyboard.renderShortcut("remove_photo")), 
          React.createElement("a", {onTouchTap: this.removeAllPhotos}, React.createElement("i", {className: "icon-remove-photos"}), translate("buttons.remove_all_photos"))
        ), 

        React.createElement(Menu, {ref: "photoMenu"}, 
          React.createElement("a", {onTouchTap: this.removeSelectedPhoto}, React.createElement("i", {className: "icon-remove-photo"}), translate("buttons.remove_photo")), 
          showInFolder
        ), 

        React.createElement("div", {className: "action-top"}, 
          React.createElement("div", {className: "title"}, translate("dialogs.photos"), " (", photos.length, ")"), 
          React.createElement(Icon, {name: "icon-more", onTap: this.showMenu})
        ), 

        React.createElement(Scrollable, {rotated: this.props.rotated, ref: "scrollable"}, 
          React.createElement(PointerEvents, {
            onPointerDown: this.touchStart, 
            onPointerCancel: this.touchCancel, 
            onLongPress: this.longPress, 
            onTap: this.touchTap}, 
            React.createElement("ul", {className: "list"}, 
              list
            )
          )
        )
      )
    );
  }
});

module.exports = Filmstrip;


}).call(this,require("react"),require("react-dom"))
},{"../io.coffee":312,"../keyboard.coffee":313,"../mixins.coffee":339,"../store.coffee":346,"../utils.coffee":360,"./button.coffee":241,"./icon.coffee":262,"./panel.coffee":272,"./pointer-events.coffee":273,"./popover-menu.coffee":276,"./scrollable.coffee":283,"classnames":1,"counterpart":3,"jquery":319,"mousetrap":21,"react":215,"react-addons-pure-render-mixin":32,"react-dom":34}],257:[function(require,module,exports){
(function (React){
var Button, Dialog, FilterDialog, actions, status, store, translate;

Button = require("./button.coffee");

Dialog = require("./dialog.coffee");

store = require("../store.coffee");

status = require("./status-overlay.coffee");

translate = require("counterpart");

actions = require("../actions.coffee");

FilterDialog = React.createClass({displayName: "FilterDialog",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      name: "",
      placeholder: translate("dialogs.filter_name"),
      saveAdjustments: true,
      saveLocalAdjustments: true,
      saveText: true,
      saveCrop: false,
      showOptions: true
    };
  },
  componentWillReceiveProps: function(nextProps) {
    var dialog;
    if (nextProps.active) {
      dialog = nextProps.active;
      dialog.saveAdjustments = true;
      dialog.saveLocalAdjustments = true;
      dialog.saveText = true;
      dialog.saveCrop = false;
      return this.setState(dialog);
    }
  },
  hide: function() {
    return actions.setDialog("create_filter", false);
  },
  saveAndShare: function(e) {
    var bypassLogin, filter;
    e.preventDefault();
    filter = actions.createFilter(this.state);
    this.hide();
    bypassLogin = !this.state.showOptions;
    return actions.exportFilterQR(filter, bypassLogin);
  },
  submit: function(e) {
    e.preventDefault();
    actions.createFilter(this.state);
    return this.hide();
  },
  handleChange: function(e) {
    return this.setState({
      name: e.target.value
    });
  },
  toggleState: function(which) {
    var state;
    state = {};
    state[which] = !this.state[which];
    return this.setState(state);
  },
  renderOptions: function() {
    return (
      React.createElement("div", {className: "setting-group"}, 
        React.createElement(Button, {
          type: "button", 
          active: this.state.saveAdjustments, 
          onTap: this.toggleState.bind(this, 'saveAdjustments')}, 
          React.createElement("i", {className: "icon-adjustments left"}), 
          translate("tooltips.global_adjustments"), React.createElement("i", {className: "icon-check right"})
        ), 
        React.createElement(Button, {
          type: "button", 
          active: this.state.saveLocalAdjustments, 
          onTap: this.toggleState.bind(this, 'saveLocalAdjustments')}, 
          React.createElement("i", {className: "icon-local-adjustments left"}), 
          translate("tooltips.local_adjustments"), React.createElement("i", {className: "icon-check right"})
        ), 
        React.createElement(Button, {
          type: "button", 
          active: this.state.saveText, 
          onTap: this.toggleState.bind(this, 'saveText')}, 
          React.createElement("i", {className: "icon-text left"}), 
          translate("labels.text"), React.createElement("i", {className: "icon-check right"})
        ), 
        React.createElement(Button, {
          type: "button", 
          active: this.state.saveCrop, 
          onTap: this.toggleState.bind(this, 'saveCrop')}, 
          React.createElement("i", {className: "icon-crop left"}), 
          translate("tooltips.crop"), React.createElement("i", {className: "icon-check right"})
        )
      )
    );
  },
  render: function() {
    var canSubmit, dialogProps;
    canSubmit = this.state.saveAdjustments || this.state.saveLocalAdjustments || this.state.saveCrop || this.state.saveText;
    if (window.innerHeight < 375) {
      dialogProps = {
        fullscreen: true,
        showclose: true
      };
    }
    return (
      React.createElement(Dialog, React.__spread({},  this.props,  dialogProps, {onShow: this.show}), 
        React.createElement("form", {onSubmit: this.submit}, 
          React.createElement("div", {className: "filter-dialog"}, 
            React.createElement("h1", null, translate("dialogs.new_filter")), 
            React.createElement("p", {className: "subtitle"}, translate("dialogs.save_adjustments")), 
            React.createElement("div", {className: "input-control"}, 
              React.createElement("input", {type: "text", 
                     id: "filter-name", 
                     ref: "name", 
                     value: this.state.name, 
                     placeholder: this.state.placeholder, 
                     onChange: this.handleChange, 
                     onTouchTap: this.fastClick})
            ), 
            this.state.showOptions ? this.renderOptions() : null, 
            React.createElement("div", {className: "footer"}, 
              React.createElement(Button, {type: "button", onTap: this.hide, content: "buttons.cancel"}), 
              React.createElement(Button, {type: "button", onTap: this.saveAndShare, content: "buttons.save_share", className: "secondary", disabled: !canSubmit}), 
              React.createElement(Button, {type: "button", onTap: this.submit, content: "buttons.save", className: "primary", disabled: !canSubmit})
            )
          )
        )
      )
    );
  }
});

module.exports = FilterDialog;


}).call(this,require("react"))
},{"../actions.coffee":229,"../store.coffee":346,"./button.coffee":241,"./dialog.coffee":249,"./status-overlay.coffee":287,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],258:[function(require,module,exports){
(function (React){
var Handle, Icon, Line, MaskOverlay, Overlay, PointerEvents, ViewContainer, ViewportOverlay, actions, mixins, ref, translate, utils;

Icon = require("./icon.coffee");

ViewContainer = require("./view-container.coffee");

PointerEvents = require("./pointer-events.coffee");

Overlay = require("./overlay.coffee");

ref = require("./viewport-overlay.coffee"), ViewportOverlay = ref.ViewportOverlay, Handle = ref.Handle, Line = ref.Line;

utils = require("../utils.coffee");

mixins = require("../mixins.coffee");

actions = require("../actions.coffee");

translate = require("counterpart");

MaskOverlay = React.createClass({displayName: "MaskOverlay",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      pan: false,
      x0: 0,
      y0: 0,
      x1: 0,
      y1: 0,
      angle: 0
    };
  },
  togglePan: function() {
    return this.setState({
      pan: !this.state.pan
    });
  },
  setPan: function(pan) {
    return this.setState({
      pan: pan
    });
  },
  componentDidMount: function() {
    return Filtr.on("history", (function(_this) {
      return function() {
        if (_this.props.active) {
          return _this.update();
        }
      };
    })(this));
  },
  componentDidUpdate: function(prevProps) {
    if (!prevProps.active && this.props.active) {
      return this.update();
    }
  },
  update: function() {
    var angle, height, mask, ref1, ref2, ref3, state, width, x0, x1, y0, y1;
    state = Filtr.getState();
    mask = state.local_adjustments[this.props.index];
    if (!mask) {
      return;
    }
    ref1 = mask.startPoint, x0 = ref1[0], y0 = ref1[1];
    ref2 = mask.endPoint, x1 = ref2[0], y1 = ref2[1];
    ref3 = Filtr._.texture, width = ref3.width, height = ref3.height;
    angle = Math.atan2(y1 * height - y0 * height, x1 * width - x0 * width) - Math.PI / 2;
    return this.setState({
      x0: x0,
      y0: y0,
      x1: x1,
      y1: y1,
      angle: angle
    });
  },
  resetMask: function() {
    var adjustments, mask, newMask, state;
    state = Filtr.getState();
    adjustments = Filtr.utils.clone(state.local_adjustments);
    mask = adjustments[this.props.index];
    newMask = actions.createMask("gradient");
    mask.startPoint = newMask.startPoint;
    mask.endPoint = newMask.endPoint;
    Filtr.setState({
      local_adjustments: adjustments
    });
    return Filtr.saveState("buttons.reset tooltips.gradient_filter");
  },
  onDoubleTap: function() {
    return this.resetMask();
  },
  onDrag: function(e, input, component) {
    var angle, center, data, delta, distance, endPoint, height, m1, m2, mul, point, ref1, ref2, snapThreshold, startPoint, width, x0, x1, y0, y1;
    data = component.props.data;
    mul = (data != null ? data.mul : void 0) || [1, 1];
    point = Filtr.getMouseCoords(input.x, input.y);
    ref1 = Filtr._.texture, width = ref1.width, height = ref1.height;
    if (!input.dragging) {
      this.startPoint = point;
      this.startState = utils.assign({}, this.state);
      this.adjustments = Filtr.getState().local_adjustments;
      this.mask = this.adjustments[this.props.index];
      this.snapPoint = [this.rotatePoint([this.startState.x0 * width, this.startState.y0 * height], -this.startState.angle)[0], this.rotatePoint([this.startState.x1 * width, this.startState.y1 * height], -this.startState.angle)[0]];
    }
    ref2 = this.startState, x0 = ref2.x0, y0 = ref2.y0, x1 = ref2.x1, y1 = ref2.y1, angle = ref2.angle;
    if (input.multitouch) {
      distance = input.distanceDelta;
      center = [x0 * 0.5 + x1 * 0.5, y0 * 0.5 + y1 * 0.5];
      x0 = (x0 - center[0]) * distance + center[0];
      y0 = (y0 - center[1]) * distance + center[1];
      x1 = (x1 - center[0]) * distance + center[0];
      y1 = (y1 - center[1]) * distance + center[1];
    } else if (mul) {
      delta = [point[0] - this.startPoint[0], point[1] - this.startPoint[1]];
      m1 = mul[0], m2 = mul[1];
      x0 = x0 + delta[0] * m1;
      y0 = y0 - delta[1] * m1;
      x1 = x1 + delta[0] * m2;
      y1 = y1 - delta[1] * m2;
      x0 *= width;
      y0 *= height;
      x1 *= width;
      y1 *= height;
      snapThreshold = width * 0.03;
      startPoint = [x0, y0];
      endPoint = [x1, y1];
      startPoint = this.rotatePoint(startPoint, -this.startState.angle);
      startPoint[0] -= utils.clamp(startPoint[0] - this.snapPoint[0], -snapThreshold, snapThreshold);
      startPoint = this.rotatePoint(startPoint, this.startState.angle);
      endPoint = this.rotatePoint(endPoint, -this.startState.angle);
      endPoint[0] -= utils.clamp(endPoint[0] - this.snapPoint[1], -snapThreshold, snapThreshold);
      endPoint = this.rotatePoint(endPoint, this.startState.angle);
      x0 = startPoint[0], y0 = startPoint[1];
      x1 = endPoint[0], y1 = endPoint[1];
      angle = Math.atan2(y1 - y0, x1 - x0) - Math.PI / 2;
      x0 /= width;
      y0 /= height;
      x1 /= width;
      y1 /= height;
    }
    this.mask.startPoint = [x0, y0];
    this.mask.endPoint = [x1, y1];
    this.setState({
      x0: x0,
      y0: y0,
      x1: x1,
      y1: y1,
      angle: angle
    });
    return Filtr.setState({
      local_adjustments: this.adjustments
    });
  },
  onDragEnd: function() {
    return Filtr.saveState("buttons.update tooltips.gradient_filter");
  },
  rotatePoint: function(point, radians) {
    return [point[0] * Math.cos(radians) - point[1] * Math.sin(radians), point[0] * Math.sin(radians) + point[1] * Math.cos(radians)];
  },
  render: function() {
    var angle, endPoint, handleProps, lineProps, midPoint, ref1, startPoint, x0, x1, y0, y1;
    ref1 = this.state, x0 = ref1.x0, y0 = ref1.y0, x1 = ref1.x1, y1 = ref1.y1, angle = ref1.angle;
    x0 += 0.5;
    y0 += 0.5;
    x1 += 0.5;
    y1 += 0.5;
    startPoint = [x0, y0];
    endPoint = [x1, y1];
    midPoint = [x0 * 0.5 + x1 * 0.5, y0 * 0.5 + y1 * 0.5];
    lineProps = {
      fill: "linear-gradient(90deg, transparent, white, transparent)",
      angle: angle * 180 / Math.PI,
      disabled: this.state.pan
    };
    handleProps = {
      fill: "white",
      disabled: this.state.pan
    };
    return (
      React.createElement(Overlay, React.__spread({},  this.props, {disabled: this.state.pan, setDisabled: this.setPan}), 
        React.createElement(ViewportOverlay, {
          active: this.props.active, 
          onDrag: this.onDrag, 
          onDragEnd: this.onDragEnd, 
          onDoubleTap: this.onDoubleTap}, 
          React.createElement(Line, React.__spread({position: startPoint},  lineProps)), 
          React.createElement(Line, React.__spread({position: midPoint},  lineProps)), 
          React.createElement(Line, React.__spread({position: endPoint},  lineProps)), 
          React.createElement(Handle, React.__spread({position: startPoint, data: {mul: [1, 0]}},  handleProps)), 
          React.createElement(Handle, React.__spread({position: endPoint, data: {mul: [0, 1]}},  handleProps))
        ), 
        React.createElement(ViewContainer, {rotated: this.props.rotated}, 
          React.createElement(Icon, {name: "icon-move", active: this.state.pan, onTap: this.togglePan, tooltip: translate("tooltips.pan"), shortcut: "pan"})
        )
      )
    );
  }
});

module.exports = MaskOverlay;


}).call(this,require("react"))
},{"../actions.coffee":229,"../mixins.coffee":339,"../utils.coffee":360,"./icon.coffee":262,"./overlay.coffee":270,"./pointer-events.coffee":273,"./view-container.coffee":296,"./viewport-overlay.coffee":298,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],259:[function(require,module,exports){
(function (React){
var Button, Guides, Menu, Panel, PointerEvents, Scrollable, actions, classnames, manager, mixins, store, translate, tutorials, utils;

Button = require("./button.coffee");

Panel = require("./panel.coffee");

Menu = require("./popover-menu.coffee");

Scrollable = require("./scrollable.coffee");

PointerEvents = require("./pointer-events.coffee");

mixins = require("../mixins.coffee");

utils = require("../utils.coffee");

actions = require("../actions.coffee");

store = require("../store.coffee");

manager = require("../manager.coffee");

tutorials = require("../tutorials.coffee");

translate = require("counterpart");

classnames = require("classnames");

Guides = React.createClass({displayName: "Guides",
  mixins: [mixins.touchRipple, require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      tutorials: tutorials
    };
  },
  componentDidMount: function() {},
  setTutorial: function(index) {
    return manager.set("tutorial", this.state.tutorials[index]);
  },
  touchStart: function(e) {
    if (e.target.tagName === "LI") {
      return this.touchRippleStart(e, e.target);
    }
  },
  touchTap: function(e) {
    var index;
    index = e.target.dataset.index;
    if (index != null) {
      return this.setTutorial(index);
    }
  },
  touchCancel: function(e) {
    return this.touchRippleEnd();
  },
  getTutorialName: function(tutorial) {
    var locale, name;
    locale = translate.getLocale();
    return name = tutorial.name[locale] || tutorial.name["en"] || tutorial.name;
  },
  getTutorialImage: function(tutorial) {
    var url;
    if (!tutorial.image) {
      return;
    }
    if (tutorial.image.indexOf("http") === 0) {
      return url = tutorial.image;
    } else {
      return url = store.getSampleDir() + tutorial.image;
    }
  },
  render: function() {
    return (
      React.createElement(Panel, React.__spread({},  this.props), 

        React.createElement("div", {className: "action-top"}, 
          React.createElement("div", {className: "title"}, translate("labels.guides"))
        ), 

        React.createElement(Scrollable, {rotated: this.props.rotated, ref: "scrollable"}, 
          React.createElement(PointerEvents, {
            onPointerDown: this.touchStart, 
            onPointerCancel: this.touchCancel, 
            onTap: this.touchTap}, 
            React.createElement("ul", {className: "list"}, 
              this.state.tutorials.map(function(tutorial, i){
                // don't show tutorials in extension because loading
                // tutorial images break saving
                if (tutorial.image && CONFIG.embedded) return null
                var sep = tutorial.sep ? React.createElement("div", {className: "sep"}) : null
                var style = tutorial.image ? {backgroundImage: 'url("'+this.getTutorialImage(tutorial)+'")'} : {}
                var classname = classnames("button", {image: !!tutorial.image})
                var li = React.createElement("li", {key: i, "data-index": i}, 
                  React.createElement("div", {className: classname, style: style}, 
                    React.createElement("div", {className: "title"}, 
                      this.getTutorialName(tutorial)
                    ), 
                    React.createElement("i", {className: "icon-" + tutorial.icon})
                  )
                )
                return [sep, li]
              }, this)
            )
          )
        )
      )
    );
  }
});

module.exports = Guides;


}).call(this,require("react"))
},{"../actions.coffee":229,"../manager.coffee":338,"../mixins.coffee":339,"../store.coffee":346,"../tutorials.coffee":348,"../utils.coffee":360,"./button.coffee":241,"./panel.coffee":272,"./pointer-events.coffee":273,"./popover-menu.coffee":276,"./scrollable.coffee":283,"classnames":1,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],260:[function(require,module,exports){
(function (React){
var Histogram, Info, PointerEvents, manager, store, utils;

manager = require("../manager.coffee");

utils = require("../utils.coffee");

store = require("../store.coffee");

PointerEvents = require("./pointer-events.coffee");

Histogram = React.createClass({displayName: "Histogram",
  mixins: [require("react-addons-pure-render-mixin")],
  componentDidMount: function() {
    this.container = this.refs.container;
    this.histogram = this.refs.histogram;
    this.histogram.appendChild(Filtr.histogram);
    manager.on("histogram", (function(_this) {
      return function(show) {
        return _this.setState({
          active: show
        });
      };
    })(this));
    manager.on("histogramPosition", (function(_this) {
      return function(position) {
        return _this.setState({
          x: position[0],
          y: position[1]
        });
      };
    })(this));
    manager.on("metadata", (function(_this) {
      return function(metadata) {
        return _this.setState({
          metadata: metadata,
          height: 120
        });
      };
    })(this));
    manager.on("toolbars", this.setHidden);
    return manager.on("activeMask", this.setHidden);
  },
  componentDidUpdate: function(prevProps) {
    if (prevProps.rotated !== this.props.rotated) {
      return this.setState(this.clampPosition(this.state.x, this.state.y));
    }
  },
  getInitialState: function() {
    return {
      active: manager.state["histogram"],
      hidden: false,
      x: manager.state["histogramPosition"][0],
      y: manager.state["histogramPosition"][1],
      width: 200,
      height: 102
    };
  },
  setHidden: function() {
    var cropActive, maskActive, toolbars;
    toolbars = manager.state.toolbars;
    cropActive = toolbars.leftActive === "crop" || toolbars.rightActive === "crop";
    maskActive = manager.state.activeMask != null;
    return this.setState({
      hidden: cropActive || maskActive
    });
  },
  setTransition: function(delay) {
    var container;
    container = this.refs.container;
    return container.style.transitionDuration = delay + "ms";
  },
  clampPosition: function(x, y) {
    var height, left, ref, ref1, top, width;
    ref = this.state, width = ref.width, height = ref.height;
    if (this.props.rotated) {
      ref1 = [height, width], width = ref1[0], height = ref1[1];
    }
    left = utils.clamp(x, 0, window.innerWidth - this.props.right - this.props.left - width);
    top = utils.clamp(y, 0, window.innerHeight - height);
    return {
      x: left,
      y: top
    };
  },
  touchStart: function(e) {
    var ref, x, y;
    e.preventDefault();
    e.stopPropagation();
    this.setTransition(0);
    ref = this.clampPosition(this.state.x, this.state.y), x = ref.x, y = ref.y;
    return this.start = [x, y];
  },
  touchMove: function(e, input) {
    var pos;
    e.stopPropagation();
    pos = {
      x: utils.clamp(this.start[0] + input.deltaX, 0, window.innerWidth),
      y: utils.clamp(this.start[1] + input.deltaY, 0, window.innerHeight)
    };
    return this.setState(pos);
  },
  touchEnd: function() {
    this.setTransition(200);
    return manager.set("histogramPosition", [this.state.x, this.state.y]);
  },
  touchTap: function() {
    return manager.set("histogram", !this.state.active);
  },
  render: function() {
    var height, maxX, ox, oy, ref, ref1, style, transform, width, x, y;
    ref = this.state, width = ref.width, height = ref.height;
    ref1 = this.clampPosition(this.state.x, this.state.y), x = ref1.x, y = ref1.y;
    maxX = window.innerWidth - this.props.right - this.props.left - width;
    style = {};
    if (this.state.x > (window.innerWidth - width) / 2) {
      ox = "0";
      x = x - maxX;
      style.right = this.props.right;
    } else {
      ox = "0";
      style.left = this.props.left;
    }
    if (this.state.y > (window.innerHeight - height) / 2) {
      oy = "0";
      style.bottom = this.props.rotated ? width : 0;
      y = y - window.innerHeight + height + style.bottom;
    } else {
      oy = "0";
      style.top = 0;
    }
    transform = "translate(" + x + "px, " + y + "px)";
    transform += " translate(" + ox + ", " + oy + ")";
    if (this.props.rotated) {
      transform += " rotate(-90deg) translate(-100%, 0)";
    }
    style.WebkitTransform = transform;
    style.transform = transform;
    return (
      React.createElement(PointerEvents, {
        onPointerDown: this.touchStart, 
        onPointerMove: this.touchMove, 
        onPointerUp: this.touchEnd, 
        onTap: this.touchTap}, 
        React.createElement("div", {className: (this.state.active && !this.state.hidden) ? "histogram" : "histogram hidden", 
             style: style, 
             ref: "container"}, 
          React.createElement("div", {className: "background", ref: "histogram"}), 
          React.createElement(Info, {metadata: this.state.metadata})
        )
      )
    );
  }
});

Info = React.createClass({displayName: "Info",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {};
  },
  getDefaultProps: function() {
    return {
      metadata: {},
      isRAW: false
    };
  },
  componentDidMount: function() {
    return Filtr.on("screen", (function(_this) {
      return function(screen) {
        return _this.setZoom(screen.zoom);
      };
    })(this));
  },
  componentWillReceiveProps: function(nextProps) {
    return this.setEXIF(this.selectEXIF(nextProps.metadata));
  },
  setZoom: Filtr.utils.throttle(function(zoom) {
    return this.refs.zoom.innerText = (zoom * 100 >> 0) + "%";
  }, 50),
  selectEXIF: function(metadata) {
    var exif, ref;
    exif = metadata[metadata.exifKey] || metadata || {};
    return {
      ApertureValue: exif.ApertureValue || exif["Aperture Value"],
      ExposureTime: exif.ExposureTime || exif["Exposure Time"],
      ISO: exif.PhotographicSensitivity || ((ref = exif.ISOSpeedRatings) != null ? ref[0] : void 0) || exif["ISO"]
    };
  },
  extractEXIFValue: function(val) {
    var ref;
    if (typeof val === "string") {
      return ((ref = val.match(/([0-9|,]+\.[0-9]+)/g)) != null ? ref[0] : void 0) || 0;
    } else {
      return val;
    }
  },
  setEXIF: function(exif) {
    var info, ref;
    if (exif == null) {
      exif = {};
    }
    info = Filtr.getInfo();
    return this.setState({
      ApertureValue: this.extractEXIFValue(exif.ApertureValue),
      ExposureTime: this.extractEXIFValue(exif.ExposureTime),
      ISO: exif.ISO,
      width: info.width,
      height: info.height,
      megapixels: info.megapixels,
      isRAW: (ref = store.info) != null ? ref.isRAW : void 0
    });
  },
  render: function() {
    var ApertureValue, ExposureTime, ISO, RAWLabel, filename, height, isRAW, megapixels, mp, ref, resolution, width;
    ref = this.state, width = ref.width, height = ref.height, megapixels = ref.megapixels, ExposureTime = ref.ExposureTime, ApertureValue = ref.ApertureValue, ISO = ref.ISO, isRAW = ref.isRAW;
    if (width && height) {
      resolution = React.createElement("span", null, width, " x ", height);
    }
    if (megapixels) {
      mp = React.createElement("span", null, megapixels, " mp");
    }
    if (ExposureTime != null) {
      ExposureTime = React.createElement("span", null, "1/", (1000 / (this.state.ExposureTime * 1000)) >> 0, " sec");
    }
    if (ApertureValue != null) {
      ApertureValue = React.createElement("span", null, React.createElement("em", null, "ƒ"), "/", (this.state.ApertureValue * 10 >> 0) / 10);
    }
    if (ISO != null) {
      ISO = React.createElement("span", null, "ISO ", this.state.ISO);
    }
    if (store.currentPhoto && CONFIG.platform !== "mobile") {
      filename = React.createElement("span", null, store.currentPhoto.split("|")[0]);
    }
    if (isRAW) {
      RAWLabel = React.createElement("span", {className: "label"}, "RAW");
    }
    return (
      React.createElement("div", {className: "info"}, 
        React.createElement("div", null, 
          resolution, 
          mp, 
          React.createElement("span", {ref: "zoom"})
        ), 
        React.createElement("div", null, 
          RAWLabel, 
          ExposureTime, 
          ApertureValue, 
          ISO
        ), 
        React.createElement("div", {className: "filename"}, 
          filename
        )
      )
    );
  }
});

module.exports = Histogram;


}).call(this,require("react"))
},{"../manager.coffee":338,"../store.coffee":346,"../utils.coffee":360,"./pointer-events.coffee":273,"react":215,"react-addons-pure-render-mixin":32}],261:[function(require,module,exports){
(function (React){
var Button, History, HistoryItem, Icon, List, Menu, Panel, actions, classnames, keyboard, mixins, translate, utils;

Button = require("./button.coffee");

Panel = require("./panel.coffee");

Menu = require("./popover-menu.coffee");

List = require("./list.coffee");

Icon = require("./icon.coffee");

mixins = require("../mixins.coffee");

utils = require("../utils.coffee");

actions = require("../actions.coffee");

keyboard = require("../keyboard.coffee");

translate = require("counterpart");

classnames = require("classnames");

History = React.createClass({displayName: "History",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      history: [
        {
          message: "labels.original"
        }
      ],
      index: 0
    };
  },
  componentDidMount: function() {
    return Filtr.on("history", (function(_this) {
      return function(history, index) {
        return _this.setState({
          history: history,
          index: index
        });
      };
    })(this));
  },
  showMenu: function(e) {
    return this.refs.menu.show({
      target: e.target,
      offset: [8, -8]
    });
  },
  setHistoryAt: function(index) {
    return Filtr.setHistoryAt(index);
  },
  clearHistory: function() {
    return actions.clearHistory();
  },
  revert: function() {
    return actions.revertToOriginal();
  },
  render: function() {
    var history, i, j, len, list, state;
    list = [];
    history = this.state.history;
    for (i = j = 0, len = history.length; j < len; i = ++j) {
      state = history[i];
      list.unshift(
        React.createElement(HistoryItem, {
          state: state, 
          key: i, 
          index: i, 
          disabled: this.state.index < i, 
          active: this.state.index === i})
      );
    }
    return (
      React.createElement(Panel, React.__spread({},  this.props), 

        React.createElement(Menu, {ref: "menu"}, 
          React.createElement("a", {onTouchTap: this.revert}, React.createElement("i", {className: "icon-reset"}), translate("buttons.revert"), keyboard.renderShortcut("revert")), 
          React.createElement("a", {onTouchTap: this.clearHistory}, React.createElement("i", {className: "icon-remove"}), translate("buttons.clear_history"))
        ), 

        React.createElement("div", {className: "action-top"}, 
          React.createElement("div", {className: "title"}, translate("labels.history")), 
          React.createElement(Icon, {name: "icon-more", onTap: this.showMenu})
        ), 

        React.createElement(List, {rotated: this.props.rotated, onSelect: this.setHistoryAt}, 
          list
        )

      )
    );
  }
});

HistoryItem = function(props) {
  var changed, classname, message;
  classname = classnames({
    "disabled": props.disabled,
    "active": props.active,
    "revert": props.state.message === "buttons.revert"
  });
  changed = typeof props.state.changed === 'object' ? '' : props.state.changed;
  message = utils.translateMessage(props.state.message);
  return (
    React.createElement("li", {"data-id": props.index, className: classname}, 
      React.createElement("div", null, 
        React.createElement("span", {className: "changed"}, changed), 
        message
      )
    )
  );
};

module.exports = History;


}).call(this,require("react"))
},{"../actions.coffee":229,"../keyboard.coffee":313,"../mixins.coffee":339,"../utils.coffee":360,"./button.coffee":241,"./icon.coffee":262,"./list.coffee":264,"./panel.coffee":272,"./popover-menu.coffee":276,"classnames":1,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],262:[function(require,module,exports){
(function (React,ReactDOM){
var Icon, PointerEvents, Tooltip, classnames, mixins;

mixins = require("../mixins.coffee");

PointerEvents = require("./pointer-events.coffee");

Tooltip = require("./tooltip.coffee");

classnames = require("classnames");

Icon = React.createClass({displayName: "Icon",
  mixins: [mixins.touchRipple],
  componentDidMount: function() {},
  getDefaultProps: function() {
    return {
      x: 0,
      y: 0
    };
  },
  stopPropagation: function(e) {
    var ref;
    e.stopPropagation();
    return (ref = e.nativeEvent) != null ? ref.stopPropagation() : void 0;
  },
  onPointerDown: function(e) {
    var base;
    this.touchRippleStart(e, ReactDOM.findDOMNode(this));
    return typeof (base = this.props).onPointerDown === "function" ? base.onPointerDown(e) : void 0;
  },
  onPointerUp: function(e) {
    var base;
    return typeof (base = this.props).onPointerUp === "function" ? base.onPointerUp(e) : void 0;
  },
  onTap: function(e) {
    if (this.props.stopPropagation) {
      this.stopPropagation(e);
    }
    if (this.props.onTap != null) {
      return this.props.onTap(e);
    }
  },
  triggerTooltip: function(e) {
    var ref;
    return (ref = this.refs.tooltip) != null ? ref.trigger(e) : void 0;
  },
  render: function() {
    var badge, badgeStyle, classname, tooltip;
    classname = classnames(this.props.name, {
      "active": this.props.active,
      "hidden": this.props.hidden,
      "disabled": this.props.disabled,
      "highlighted": this.props.highlighted,
      "rotated": this.props.rotated
    });
    if (this.props.badge) {
      badgeStyle = {
        top: CONFIG.os === "android" ? "1px" : "0px"
      };
      badge = React.createElement("span", {className: "badge", style: badgeStyle}, this.props.badge);
    }
    if (this.props.tooltip && CONFIG.platform !== "mobile") {
      tooltip = React.createElement(Tooltip, {ref: "tooltip", text: this.props.tooltip, shortcut: this.props.shortcut});
    }
    return (
      React.createElement(PointerEvents, {
        onPointerDown: this.onPointerDown, 
        onPointerUp: this.onPointerUp, 
        onTap: this.onTap}, 
        React.createElement("i", {
          className: classname, 
          onMouseEnter: this.triggerTooltip}, 
          badge, 
          tooltip
        )
      )
    );
  }
});

module.exports = Icon;


}).call(this,require("react"),require("react-dom"))
},{"../mixins.coffee":339,"./pointer-events.coffee":273,"./tooltip.coffee":293,"classnames":1,"react":215,"react-dom":34}],263:[function(require,module,exports){
(function (React){
var Button, Languages, Menu, actions, langMap, localeMap, storage, translate;

Button = require("./button.coffee");

Menu = require("./popover-menu.coffee");

translate = require("counterpart");

actions = require("../actions.coffee");

storage = require("../storage.coffee");

localeMap = {
  "en": "English",
  "zh": "Chinese (simplified)",
  "zh-tw": "Chinese (traditional)",
  "ja": "Japanese",
  "ko": "Korean",
  "ru": "Russian",
  "es": "Spanish",
  "pt": "Portuguese",
  "fr": "French",
  "tr": "Turkish",
  "cy": "Welsh",
  "ar": "Arabic",
  "de": "German",
  "it": "Italian",
  "nl": "Dutch",
  "id": "Indonesian",
  "th": "Thai"
};

langMap = {
  "en": "English",
  "es": "Español",
  "fr": "Français",
  "de": "Deutsch",
  "zh": "汉语",
  "zh-tw": "漢語",
  "ja": "日本語",
  "nl": "Nederlands",
  "it": "Italiano",
  "ko": "한국어",
  "pt": "Português",
  "ru": "Русский",
  "tr": "Türkçe",
  "ar": "العربية",
  "cy": "Cymraeg",
  "th": "ภาษาไทย",
  "id": "Bahasa Indonesia"
};

Languages = React.createClass({displayName: "Languages",
  componentDidMount: function() {},
  getInitialState: function() {
    return {
      locale: translate.getLocale(),
      locales: translate.getAvailableLocales()
    };
  },
  setLocale: function(locale) {
    this.setState({
      locale: locale
    });
    translate.setLocale(locale);
    return storage.setItem("locale", locale);
  },
  showMenu: function(e) {
    return this.refs.menu.show({
      target: e.target,
      offset: [16, 0]
    });
  },
  render: function() {
    var i, locale, locales;
    locales = (function() {
      var j, len, ref, results;
      ref = this.state.locales;
      results = [];
      for (i = j = 0, len = ref.length; j < len; i = ++j) {
        locale = ref[i];
        results.push(React.createElement("a", {
        key: i, 
        className: this.state.locale === locale ? "locale active" : "locale", 
        onTouchTap: this.setLocale.bind(this, locale)}, 
        langMap[locale], 
        React.createElement("em", null, localeMap[locale])
      ));
      }
      return results;
    }).call(this);
    return (
      React.createElement(Button, {className: "language-select", onTap: this.showMenu}, 
        React.createElement(Menu, {rotated: this.props.rotated, ref: "menu"}, 
          locales
        ), 

        langMap[this.state.locale], 
        React.createElement("i", {className: "icon-down right"})
      )
    );
  }
});

module.exports = Languages;


}).call(this,require("react"))
},{"../actions.coffee":229,"../storage.coffee":343,"./button.coffee":241,"./popover-menu.coffee":276,"counterpart":3,"react":215}],264:[function(require,module,exports){
(function (React,ReactDOM){
var List, PointerEvents, Scrollable, classnames, mixins;

PointerEvents = require("./pointer-events.coffee");

Scrollable = require("./scrollable.coffee");

mixins = require("../mixins.coffee");

classnames = require("classnames");

List = React.createClass({displayName: "List",
  mixins: [mixins.touchRipple],
  componentDidUpdate: function(prevProps, prevState) {
    var active, box, container, el, from, newCount, oldCount, to;
    oldCount = React.Children.count(prevProps.children);
    newCount = React.Children.count(this.props.children);
    el = ReactDOM.findDOMNode(this);
    if (newCount > oldCount) {
      return el.scrollTop = 0;
    } else {
      active = el.querySelector('.active');
      if (!active) {
        return;
      }
      from = to = el.scrollTop;
      container = el.getBoundingClientRect();
      box = active.getBoundingClientRect();
      if (box.top < container.top + box.height) {
        to = active.offsetTop - box.height;
      } else if (box.bottom > container.bottom - box.height) {
        to = active.offsetTop - container.height + box.height * 2;
      }
      if (from === to) {
        return;
      }
      if (this.cancelAnimation != null) {
        this.cancelAnimation();
      }
      return this.cancelAnimation = Filtr.utils.animate(from, to, 0.2, function(val) {
        return el.scrollTop = val[0];
      });
    }
  },
  onTap: function(e, input) {
    var id;
    id = e.target.dataset.id;
    if (id != null) {
      return this.props.onSelect(id);
    }
  },
  touchStart: function(e) {
    if (e.target.tagName === "LI") {
      return this.touchRippleStart(e, e.target);
    }
  },
  touchCancel: function(e) {
    return this.touchRippleEnd();
  },
  render: function() {
    var classname;
    classname = classnames("list", this.props.className);
    return (
      React.createElement(Scrollable, {style: this.props.style, rotated: this.props.rotated}, 
        React.createElement(PointerEvents, {
          onPointerDown: this.touchStart, 
          onPointerCancel: this.touchCancel, 
          onTap: this.onTap}, 
          React.createElement("ul", {className: classname}, 
            this.props.children
          )
        )
      )
    );
  }
});

module.exports = List;


}).call(this,require("react"),require("react-dom"))
},{"../mixins.coffee":339,"./pointer-events.coffee":273,"./scrollable.coffee":283,"classnames":1,"react":215,"react-dom":34}],265:[function(require,module,exports){
(function (React){
var Button, ColorPicker, FeatureBlock, Icon, LocalAdjustments, LocalAdjustmentsList, LocalAdjustmentsSettings, Menu, Panel, PointerEvents, Portal, Scrollable, Slider, SliderPad, actions, classnames, controls, cx, labels, manager, mixins, store, translate, utils;

Button = require("./button.coffee");

Icon = require("./icon.coffee");

Slider = require("./slider.coffee");

SliderPad = require("./slider-pad.coffee");

ColorPicker = require("./color-picker.coffee");

Panel = require("./panel.coffee");

Menu = require("./popover-menu.coffee");

Scrollable = require("./scrollable.coffee");

Portal = require("./portal.coffee");

FeatureBlock = require("./feature-block.coffee");

PointerEvents = require("./pointer-events.coffee");

utils = require("../utils.coffee");

store = require("../store.coffee");

translate = require("counterpart");

actions = require("../actions.coffee");

mixins = require("../mixins.coffee");

manager = require("../manager.coffee");

classnames = require("classnames");

controls = {
  "brush": require("./brush-mask-overlay.coffee"),
  "color": require("./color-mask-overlay.coffee"),
  "radial": require("./radial-mask-overlay.coffee"),
  "gradient": require("./gradient-mask-overlay.coffee")
};

labels = {
  "brush": "tooltips.brush_filter",
  "color": "tooltips.color_filter",
  "radial": "tooltips.radial_filter",
  "gradient": "tooltips.gradient_filter"
};

cx = require("classnames");

LocalAdjustments = React.createClass({displayName: "LocalAdjustments",
  mixins: [require("react-addons-pure-render-mixin")],
  componentDidMount: function() {
    return Filtr.on("history", this.update);
  },
  componentDidUpdate: function() {
    var mask;
    mask = this.props.active && this.state.index >= 0 ? this.state.index : null;
    return manager.set("activeMask", mask);
  },
  update: function() {
    var index, masks;
    masks = Filtr.getState().local_adjustments.slice(0);
    if (this.state.index > -1 && this.state.index < masks.length) {
      if (masks[this.state.index].type === this.state.masks[this.state.index].type) {
        index = this.state.index;
      } else {
        index = -1;
      }
    } else {
      index = -1;
    }
    return this.setState({
      masks: masks,
      index: index
    });
  },
  getInitialState: function() {
    return {
      masks: [],
      index: -1,
      showMenu: false
    };
  },
  addMask: function(type) {
    var masks;
    masks = actions.addMask(type);
    return setTimeout((function(_this) {
      return function() {
        return _this.setState({
          masks: masks,
          index: masks.length - 1,
          showMenu: false
        });
      };
    })(this), 100);
  },
  addRadialMask: function() {
    return this.addMask("radial");
  },
  addGradientMask: function() {
    return this.addMask("gradient");
  },
  addColorMask: function() {
    return this.addMask("color");
  },
  addBrushMask: function() {
    return this.addMask("brush");
  },
  updateEnd: function() {
    return this.refs.brushOverlay.hideBrush();
  },
  setMask: function(state) {
    var key, mask, masks, val;
    masks = this.state.masks;
    mask = masks[this.state.index];
    for (key in state) {
      val = state[key];
      mask[key] = val;
      if (key === "brush_radius") {
        this.refs.brushOverlay.showBrushAt(0.5, 0.5);
      }
    }
    return Filtr.setState({
      local_adjustments: masks
    });
  },
  deleteMask: function(index) {
    var masks, type;
    if (index == null) {
      index = this.state.index;
    }
    masks = this.state.masks.slice(0);
    type = masks[index].type;
    masks.splice(index, 1);
    this.setState({
      index: -1
    });
    return setTimeout(function() {
      Filtr.setState({
        local_adjustments: masks
      });
      return Filtr.saveState("buttons.delete tooltips." + type + "_filter");
    }, 1);
  },
  setIndex: function(index) {
    document.activeElement.blur();
    return this.setState({
      index: index
    });
  },
  showMenu: function() {
    return this.setState({
      showMenu: !this.state.showMenu
    });
  },
  render: function() {
    var BrushControl, ColorControl, GradientControl, RadialControl, actionClass, addButton, brushCount, brushMaskAlert, i, len, m, mask, masks, menuClass;
    masks = this.state.masks;
    mask = this.state.masks[this.state.index];
    brushCount = 0;
    for (i = 0, len = masks.length; i < len; i++) {
      m = masks[i];
      if (m.type === "brush") {
        brushCount++;
      }
    }
    GradientControl = controls.gradient;
    RadialControl = controls.radial;
    ColorControl = controls.color;
    BrushControl = controls.brush;
    actionClass = classnames("action-top", {
      "hidden": this.state.index >= 0 || !masks.length && CONFIG.platform === "mobile"
    });
    menuClass = classnames("shade-menu", {
      "active": this.state.showMenu || !masks.length
    });
    brushMaskAlert = {
      text: utils.translateReplace("dialogs.brush_limit", {
        limit: 4
      })
    };
    if (CONFIG.platform === "mobile") {
      addButton = React.createElement(Icon, {name: this.state.showMenu ? "icon-back" : "icon-add", onTap: this.showMenu, rotated: this.state.showMenu});
    }
    return (
      React.createElement(Panel, React.__spread({},  this.props), 

        React.createElement(Portal, {portalId: this.props.name}, 
          React.createElement(GradientControl, {
            rotated: this.props.rotated, 
            active: this.props.active && mask && mask.type === "gradient", 
            index: this.state.index}), 
          React.createElement(RadialControl, {
            rotated: this.props.rotated, 
            active: this.props.active && mask && mask.type === "radial", 
            index: this.state.index}), 
          React.createElement(ColorControl, {
            rotated: this.props.rotated, 
            active: this.props.active && mask && mask.type === "color", 
            index: this.state.index}), 
          React.createElement(BrushControl, {
            ref: "brushOverlay", 
            rotated: this.props.rotated, 
            active: this.props.active && mask && mask.type === "brush", 
            index: this.state.index})
        ), 

        React.createElement("div", {className: actionClass}, 
          React.createElement("div", {className: "title"}, translate("tooltips.masks"), " (", masks.length, ")"), 
          addButton
        ), 

        React.createElement("div", {className: menuClass}, 
          React.createElement(Button, {onTap: this.addRadialMask}, 
            React.createElement("i", {className: "icon-radial"}), 
            React.createElement("span", {className: "text"}, translate(labels.radial))
          ), 
          React.createElement(Button, {onTap: this.addGradientMask}, 
            React.createElement("i", {className: "icon-gradient"}), 
            React.createElement("span", {className: "text"}, translate(labels.gradient))
          ), 
          React.createElement(Button, {onTap: this.addColorMask}, 
            React.createElement("i", {className: "icon-color"}), 
            React.createElement("span", {className: "text"}, translate(labels.color))
          ), 
          React.createElement(Button, {onTap: this.addBrushMask, disabled: brushCount >= 4, alert: brushMaskAlert}, 
            React.createElement("i", {className: "icon-brush"}), 
            React.createElement("span", {className: "text"}, translate(labels.brush))
          )
        ), 

        React.createElement(LocalAdjustmentsSettings, {
          mask: mask, 
          index: this.state.index, 
          type: mask && mask.type, 
          setMask: this.setMask, 
          deleteMask: this.deleteMask, 
          rotated: this.props.rotated, 
          position: this.props.position, 
          updateEnd: this.updateEnd, 
          done: this.setIndex.bind(this, -1)}), 

        React.createElement(LocalAdjustmentsList, {
          masks: this.state.masks, 
          deleteMask: this.deleteMask, 
          active: !(this.state.showMenu || !masks.length || this.state.index >= 0), 
          setIndex: this.setIndex})
      )
    );
  }
});

LocalAdjustmentsList = React.createClass({displayName: "LocalAdjustmentsList",
  mixins: [mixins.touchRipple],
  deleteMask: function() {
    return this.props.deleteMask(this.currentIndex);
  },
  touchStart: function(e) {
    return this.touchRippleStart(e, e.target);
  },
  touchTap: function(e) {
    var target;
    e.preventDefault();
    target = e.target;
    this.currentIndex = parseInt(target.dataset.index, 10);
    return this.props.setIndex(this.currentIndex);
  },
  longPress: function(e) {
    var target;
    target = e.target;
    this.currentIndex = parseInt(target.dataset.index, 10);
    return this.refs.menu.show({
      target: target,
      offset: [8, 0]
    });
  },
  render: function() {
    return (
      React.createElement(Scrollable, {rotated: this.props.rotated, className: this.props.active ? "adjustment-list active" : "adjustment-list"}, 
        React.createElement(Menu, {ref: "menu"}, 
          React.createElement("a", {onTouchTap: this.deleteMask}, React.createElement("i", {className: "icon-remove"}), translate("buttons.delete"))
        ), 
        React.createElement(PointerEvents, {
          onPointerDown: this.touchStart, 
          onLongPress: this.longPress, 
          onTap: this.touchTap}, 
          React.createElement("ul", {className: "list"}, 
            this.props.masks.map(function(mask, i){
              return (
                React.createElement("li", {key: i, "data-index": i}, 
                  React.createElement("div", null, 
                    React.createElement("span", {className: "index"}, i+1), 
                    React.createElement("i", {className: "icon-"+mask.type}), 
                    translate(labels[mask.type])
                  )
                )
                )
            }, this)
          )
        )
      )
    );
  }
});

LocalAdjustmentsSettings = React.createClass({displayName: "LocalAdjustmentsSettings",
  deleteMask: function() {
    return this.props.deleteMask();
  },
  resetMask: function() {
    var mask, message, title;
    mask = actions.createMask(this.props.type);
    if (this.props.mask.channel != null) {
      mask.channel = this.props.mask.channel;
    }
    this.props.setMask(mask);
    this.props.updateEnd();
    title = labels[this.props.type];
    message = "buttons.reset " + title;
    Filtr.saveState(message);
    return actions.showStatus(utils.translateMessage(message), 1000);
  },
  invertMask: function() {
    var title;
    this.props.setMask({
      invert: !this.props.mask.invert
    });
    title = labels[this.props.type];
    return Filtr.saveState(title + " labels.invert");
  },
  reflectMask: function() {
    var title;
    this.props.setMask({
      reflect: !this.props.mask.reflect
    });
    title = labels[this.props.type];
    return Filtr.saveState(title + " labels.reflect");
  },
  setMask: function(props) {
    return this.props.setMask(props);
  },
  setMaskAdjustments: function(state) {
    var adjustments, key, val;
    adjustments = this.props.mask.adjustments;
    for (key in state) {
      val = state[key];
      adjustments[key] = val;
    }
    return this.props.setMask({
      adjustments: adjustments
    });
  },
  updateEnd: function() {
    return this.props.updateEnd();
  },
  Slider: function(opts) {
    var SliderControl, value;
    if (opts == null) {
      opts = {};
    }
    value = opts.update ? this.props.mask[opts.name] : this.props.mask.adjustments[opts.name];
    SliderControl = CONFIG.platform === "mobile" ? SliderPad : Slider;
    return React.createElement(SliderControl, {
      label: opts.label, 
      prop: opts.name, 
      value: value, 
      onUpdate: opts.update || this.setMaskAdjustments, 
      onUpdateEnd: this.updateEnd, 
      rotated: this.props.rotated, 
      group: opts.group || "", 
      hideGroup: true, 
      mul: (opts.mul != null) ? opts.mul : 100, 
      min: (opts.min != null) ? opts.min : -1, 
      max: (opts.max != null) ? opts.max : 1, 
      position: this.props.position}
    );
  },
  ColorPicker: function(name) {
    var set, state;
    state = this.props.mask.adjustments;
    set = this.setMaskAdjustments;
    return React.createElement(ColorPicker, {
      label: "labels.color", 
      name: name, 
      value: [state["shadows_hue"], state["shadows_saturation"]], 
      rotated: this.props.rotated, 
      update: function(val){
        set({
          "shadows_hue": val[0],
          "shadows_saturation": val[1]
        })
      }, 
      position: this.props.position}
    );
  },
  render: function() {
    var active, featherSlider, group, mask, opacitySlider, options, radiusSlider, reflectButton, thresholdSlider;
    active = !!this.props.mask;
    mask = this.props.mask;
    if (!mask || !mask.adjustments) {
      return null;
    }
    group = labels[mask.type];
    if (mask.type === "brush") {
      radiusSlider = this.Slider({
        label: "labels.brush_size",
        name: "brush_radius",
        group: group,
        min: 0,
        update: this.setMask
      });
      opacitySlider = this.Slider({
        label: "labels.brush_opacity",
        name: "brush_opacity",
        group: group,
        min: 0,
        update: this.setMask
      });
    }
    if (mask.type === "color") {
      thresholdSlider = this.Slider({
        label: "labels.threshold",
        name: "threshold",
        group: group,
        min: 0,
        update: this.setMask
      });
    }
    if (mask.type === "radial") {
      featherSlider = this.Slider({
        label: "labels.feather",
        name: "feather",
        group: group,
        min: 0,
        update: this.setMask
      });
    }
    if (mask.type === "gradient") {
      reflectButton = React.createElement(Button, {"data-name": "reflect", onTap: this.reflectMask, active: mask.reflect}, React.createElement("i", {className: "icon-reflect"}), translate("labels.reflect"));
    }
    options = React.createElement("div", {className: "adjustment-options"}, 
       radiusSlider, 
       thresholdSlider, 
       featherSlider, 
       reflectButton, 
      React.createElement(Button, {"data-name": "invert", onTap: this.invertMask, active: mask.invert}, React.createElement("i", {className: "icon-invert"}), translate("labels.invert")), 
      React.createElement(Button, {onTap: this.resetMask}, React.createElement("i", {className: "icon-reset"}), translate("buttons.reset")), 
      React.createElement(Button, {onTap: this.deleteMask}, React.createElement("i", {className: "icon-remove"}), translate("buttons.delete"))
    );
    return (
      React.createElement("div", {className: active ? "adjustment-settings active" : "adjustment-settings"}, 

        React.createElement("div", {className: "action-top"}, 
          React.createElement("div", {className: "title"}, translate(group)), 
          React.createElement(Icon, {name: "icon-check", onTap: this.props.done, rotated: this.props.rotated}, 
            React.createElement("span", {className: "index"}, this.props.index+1)
          )
        ), 

        React.createElement(Scrollable, {rotated: this.props.rotated, className: "mask-controls"}, 
          React.createElement("div", {className: "feature-block-wrapper"}, 
            React.createElement(FeatureBlock, {container: "local_adjustments", item: "local_adjustments", bypass: true}), 
             this.Slider({label: "labels.blur", name: "blur", group: group, min: 0}), 
             this.Slider({label: "labels.exposure", name: "exposure", group: group}), 
             this.Slider({label: "labels.temperature", name: "temperature", group: group}), 
             this.Slider({label: "labels.tint", name: "tint", group: group}), 
             this.Slider({label: "labels.saturation", name: "saturation", group: group}), 
             this.Slider({label: "labels.vibrance", name: "vibrance", group: group}), 
             this.Slider({label: "labels.contrast", name: "contrast", group: group}), 
             this.Slider({label: "labels.highlights", name: "highlights", group: group}), 
             this.Slider({label: "labels.shadows", name: "shadows", group: group}), 
             this.Slider({label: "labels.clarity", name: "clarity", group: group}), 
             this.Slider({label: "labels.mosaic", name: "mosaic_size", group: group, min:0}), 
             this.ColorPicker(group), 
            React.createElement("div", {className: "sep"}), 
             options 
          )
        )
      )
    );
  }
});

module.exports = LocalAdjustments;


}).call(this,require("react"))
},{"../actions.coffee":229,"../manager.coffee":338,"../mixins.coffee":339,"../store.coffee":346,"../utils.coffee":360,"./brush-mask-overlay.coffee":240,"./button.coffee":241,"./color-mask-overlay.coffee":242,"./color-picker.coffee":243,"./feature-block.coffee":255,"./gradient-mask-overlay.coffee":258,"./icon.coffee":262,"./panel.coffee":272,"./pointer-events.coffee":273,"./popover-menu.coffee":276,"./portal.coffee":277,"./radial-mask-overlay.coffee":281,"./scrollable.coffee":283,"./slider-pad.coffee":285,"./slider.coffee":286,"classnames":1,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],266:[function(require,module,exports){
(function (React,ReactDOM){
var $, Button, Dialog, EditDialog, FeatureBlock, Icon, InfoDialog, Look, Looks, LooksPreviews, Menu, Panel, PointerEvents, PopoutSlider, Scrollable, Tooltip, actions, classnames, connectedSlider, manager, mixins, presets, shortid, store, translate, utils;

translate = require("counterpart");

$ = require('jquery');

shortid = require('shortid');

classnames = require("classnames");

Menu = require("./popover-menu.coffee");

Button = require("./button.coffee");

Icon = require("./icon.coffee");

Scrollable = require("./scrollable.coffee");

FeatureBlock = require("./feature-block.coffee");

Panel = require("./panel.coffee");

PopoutSlider = require("./popout-slider.coffee");

Dialog = require("./dialog.coffee");

PointerEvents = require("./pointer-events.coffee");

Tooltip = require("./tooltip.coffee");

store = require("../store.coffee");

mixins = require("../mixins.coffee");

utils = require("../utils.coffee");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

presets = require("../presets.js");

Looks = React.createClass({displayName: "Looks",
  mixins: [require("react-addons-pure-render-mixin")],
  componentDidMount: function() {
    manager.on("customFilters", (function(_this) {
      return function() {
        return _this.setState(_this.getInitialState());
      };
    })(this));
    manager.on("favoriteFilters", (function(_this) {
      return function() {
        return _this.setState(_this.getInitialState());
      };
    })(this));
    manager.on("purchases", (function(_this) {
      return function() {
        return _this.setState(_this.getInitialState());
      };
    })(this));
    manager.on("user", (function(_this) {
      return function() {
        return _this.setState(_this.getInitialState());
      };
    })(this));
    manager.on("filter_tab", (function(_this) {
      return function(tab) {
        return _this.setState({
          tab: tab,
          title: _this.state.packs[tab].name
        });
      };
    })(this));
    return manager.on("syncingFilters", (function(_this) {
      return function(syncState) {
        return _this.setState({
          syncing: syncState
        });
      };
    })(this));
  },
  getInitialState: function() {
    var batchFilters, filters, j, k, len, len1, pack, packCount, packs, ref, tab;
    packCount = 0;
    packs = [
      {
        enabled: true,
        decode: true,
        removable: true,
        pack_id: "favorites",
        name: translate("labels.favorites"),
        filters: manager.state.favoriteFilters
      }, {
        editable: true,
        enabled: true,
        decode: true,
        pack_id: "custom",
        name: translate("labels.custom"),
        filters: manager.state.customFilters
      }
    ];
    for (j = 0, len = presets.length; j < len; j++) {
      pack = presets[j];
      pack.name = this.translateName(pack.pack_name);
      if (pack.is_free != null) {
        pack.purchasable = true;
        pack.enabled = actions.isFilterPurchased(pack.pack_id);
      } else {
        pack.enabled = true;
      }
      packCount += 1;
      packs.push(pack);
    }
    batchFilters = [];
    for (k = 0, len1 = packs.length; k < len1; k++) {
      pack = packs[k];
      if (pack.enabled) {
        filters = pack.filters.map((function(_this) {
          return function(filter) {
            var name;
            name = decodeURIComponent(_this.translateName(filter.name));
            return {
              id: filter.id,
              name: name
            };
          };
        })(this));
        batchFilters.push({
          name: pack.name,
          id: pack.pack_id,
          filters: filters
        });
      }
    }
    if ((ref = store.manager) != null) {
      ref.sendMessage("ppe.batch.filters", batchFilters);
    }
    manager.set("filterPacks", packs);
    tab = manager.state.filter_tab;
    return {
      packs: packs,
      packCount: packCount,
      tab: tab,
      title: packs[tab].name
    };
  },
  translateName: function(name) {
    var locale;
    locale = translate.getLocale();
    return name[locale] || name["en"] || name;
  },
  getPresetByID: function(packs, id) {
    var filter, j, k, len, len1, pack, ref;
    for (j = 0, len = packs.length; j < len; j++) {
      pack = packs[j];
      ref = pack.filters;
      for (k = 0, len1 = ref.length; k < len1; k++) {
        filter = ref[k];
        if (filter.id === id) {
          return filter;
        }
      }
    }
  },
  showTabMenu: function(e) {
    return this.refs["menu"].show({
      target: e.target,
      offset: [8, -8]
    });
  },
  showTitle: function(title) {
    this.setState({
      title: title,
      showTitle: true
    });
    clearTimeout(this.showTitleTimeout);
    return this.showTitleTimeout = setTimeout((function(_this) {
      return function() {
        return _this.setState({
          showTitle: false
        });
      };
    })(this), 800);
  },
  setActiveTab: function(index) {
    return manager.set("filter_tab", index);
  },
  createFilter: function() {
    return actions.setDialog("create_filter", {
      showOptions: true
    });
  },
  moreFilters: function() {
    return actions.setDialog("store", true);
  },
  importFilter: function(e) {
    if (CONFIG.platform === "mobile") {
      return this.refs["import-menu"].show({
        target: e.target,
        offset: [8, 0]
      });
    } else {
      return this.importFilterPhotos();
    }
  },
  scanFilter: function() {
    return actions.scanFilter();
  },
  importFilterPhotos: function() {
    return actions.importFilterQR();
  },
  triggerTooltip: function(e) {
    var ref;
    return (ref = this.refs.tooltip) != null ? ref.trigger(e) : void 0;
  },
  render: function() {
    var currentPack, dotsClass, i, icon, importFilterButton, importFilterMenu, itemClass, j, len, list, pack, purchaseText, ref, syncTooltip, syncingLabel, titleClass, tooltipText;
    purchaseText = translate("status.purchase_required");
    currentPack = this.state.packs[this.state.tab];
    list = [];
    ref = this.state.packs;
    for (i = j = 0, len = ref.length; j < len; i = ++j) {
      pack = ref[i];
      itemClass = classnames({
        active: i === this.state.tab,
        "purchase-required": !pack.enabled && !actions.isFilterPurchased(pack.pack_id)
      });
      icon = (function() {
        switch (i) {
          case 0:
            return React.createElement("i", {className: "icon-heart"});
          case 1:
            return React.createElement("i", {className: "icon-adjustments"});
          default:
            return null;
        }
      })();
      list.push(
        React.createElement("a", {
          key: i, 
          onTouchTap: this.setActiveTab.bind(this, i), 
          "data-purchase": purchaseText, 
          className: itemClass}, 
          icon, 
          pack.name, " (", pack.filters.length, ")")
      );
    }
    titleClass = classnames("title", {
      small: this.state.title.length > 14,
      active: this.state.showTitle
    });
    dotsClass = classnames("dots", {
      small: this.state.packCount > 10,
      "show-title": this.state.showTitle
    });
    if (!CONFIG.embedded) {
      importFilterButton = 
        React.createElement("a", {onTouchTap: this.importFilter}, React.createElement("i", {className: "icon-qr-code"}), translate("buttons.import_filter"))
      ;
      importFilterMenu = 
        React.createElement(Menu, {ref: "import-menu"}, 
          React.createElement("a", {onTouchTap: this.scanFilter}, translate("buttons.scan_filter")), 
          React.createElement("a", {onTouchTap: this.importFilterPhotos}, translate("buttons.import_filter_photos"))
        )
      ;
    }
    if (CONFIG.platform !== "mobile") {
      switch (this.state.syncing) {
        case 1:
          syncingLabel = React.createElement("span", {onMouseEnter: this.triggerTooltip, className: "indicator grey"});
          tooltipText = translate("status.filters_syncing");
          break;
        case 2:
          syncingLabel = React.createElement("span", {onMouseEnter: this.triggerTooltip, className: "indicator green"});
          tooltipText = translate("status.filters_synced");
          break;
        case 3:
          syncingLabel = React.createElement("span", {onMouseEnter: this.triggerTooltip, className: "indicator red"});
          tooltipText = translate("status.filters_sync_error");
      }
      syncTooltip = React.createElement(Tooltip, {ref: "tooltip", text: tooltipText});
    }
    return (
      React.createElement(Panel, React.__spread({},  this.props), 
        importFilterMenu, 

        React.createElement(Menu, {rotated: this.props.rotated, ref: "menu"}, 
          React.createElement("a", {onTouchTap: actions.externalFilterStore}, React.createElement("i", {className: "icon-instagram"}), translate("menus.filters_users")), 
          React.createElement("a", {onTouchTap: this.moreFilters}, React.createElement("i", {className: "icon-shop"}), translate("menus.more_filters")), 
          React.createElement("a", {onTouchTap: this.createFilter}, React.createElement("i", {className: "icon-add-filter"}), translate("buttons.new_filter")), 
          importFilterButton, 
          React.createElement("div", {className: "sep"}), 
          list
        ), 

        React.createElement("div", {className: "action-top"}, 
          React.createElement("div", {className: titleClass}, this.state.title, " ", currentPack.pack_id === "custom" ? syncingLabel : null), 
          React.createElement(Icon, {name: "icon-more", onTap: this.showTabMenu}), 
          syncTooltip
        ), 
        
        React.createElement("div", {className: dotsClass}, 
          this.state.packs.map(function(pack, i){
            return React.createElement("span", {key: i, className: i === this.state.tab ? "active": ""}, "•")
            }, this)
        ), 

        React.createElement(LooksPreviews, {
          ref: "previews", 
          tab: this.state.tab, 
          packs: this.state.packs, 
          rotated: this.props.rotated, 
          position: this.props.position, 
          showTitle: this.showTitle, 
          syncing: this.state.syncing})

      )
    );
  }
});

module.exports = Looks;

LooksPreviews = React.createClass({displayName: "LooksPreviews",
  mixins: [mixins.touchRipple, require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    var filter;
    filter = Filtr.getDefaultState(true);
    delete filter.crop;
    delete filter.rotate90;
    delete filter.rotation;
    return {
      defaultFilter: {
        id: "none",
        name: translate("labels.none"),
        state: filter
      }
    };
  },
  componentDidMount: function() {
    var ref;
    actions.updateThumbnails = this.updatePreviews;
    this.showActiveTab(this.props.tab);
    return (ref = store.manager) != null ? ref.waitForMessage("ppe.batch.previews", (function(_this) {
      return function(res, callback) {
        var renderThumbnails, url;
        renderThumbnails = function() {
          var thumbnail;
          if (res.collectionId === "current") {
            thumbnail = actions.renderThumbnail(Filtr.getState(), {
              size: 80
            });
            return callback({
              "current": thumbnail
            });
          } else {
            return _this.updatePreviews(res.collectionId, callback);
          }
        };
        if (Filtr._.photoLoaded) {
          return renderThumbnails();
        } else {
          url = store.getSampleDir() + "default_small.jpg";
          return Filtr["import"](url, {
            done: renderThumbnails
          });
        }
      };
    })(this)) : void 0;
  },
  componentDidUpdate: function() {
    this.showActiveTab(this.props.tab);
    return this.updatePreviews();
  },
  importFilter: function(e) {
    return this.refs["import-menu"].show({
      target: e.target,
      offset: [8, 0]
    });
  },
  scanFilter: function() {
    return actions.scanFilter();
  },
  importFilterPhotos: function() {
    return actions.importFilterQR();
  },
  createFilter: function() {
    return actions.setDialog("create_filter", {
      showOptions: true
    });
  },
  moreFilters: function() {
    return actions.setDialog("store", true);
  },
  updatePreviews: utils.debounce(function(packId, callback) {
    var component, els, i, previews, update;
    if (!Filtr._.photoLoaded) {
      return;
    }
    previews = [];
    if (packId) {
      els = $(ReactDOM.findDOMNode(this.refs["pack_" + packId])).find(".preview");
    } else {
      els = $(ReactDOM.findDOMNode(this)).find(".tab-panel").eq(this.panelIndex).find(".preview");
    }
    els.each(function() {
      return previews.push(this.getAttribute("data-pointer"));
    });
    i = 0;
    component = null;
    update = (function(_this) {
      return function(canvas, filterId) {
        var ctx, imageData, thumbnails;
        if (callback && canvas) {
          ctx = canvas.getContext("2d");
          imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
          thumbnails = {};
          thumbnails[filterId] = {
            data: imageData.data,
            width: imageData.width,
            height: imageData.height
          };
          if (typeof callback === "function") {
            callback(thumbnails, true);
          }
        }
        if (i >= previews.length) {
          return;
        }
        component = _this.refs[previews[i]];
        if (component) {
          component.updatePreview(update);
          return i++;
        } else {
          i++;
          return update();
        }
      };
    })(this);
    return update();
  }, 300),
  applyLook: function(component) {
    var active, from, height, scrollable, to;
    component.activate();
    active = ReactDOM.findDOMNode(component);
    scrollable = $(active).closest(".scrollable")[0];
    height = scrollable.clientHeight;
    from = scrollable.scrollTop;
    to = active.offsetTop + active.clientHeight / 2 - height / 2;
    if (this.cancelAnimation != null) {
      this.cancelAnimation();
    }
    return this.cancelAnimation = Filtr.utils.animate(from, to, 0.15, function(val) {
      return scrollable.scrollTop = val[0];
    });
  },
  editLook: function() {
    return this.refs["edit-dialog"].show(this.state.currentLook);
  },
  removeLook: function() {
    return this.refs["edit-dialog"].removeFilter(this.state.currentLook);
  },
  exportLook: function() {
    var preset;
    preset = this.state.currentLook.props.preset;
    return actions.exportFilterQR(preset);
  },
  favoriteLook: function() {
    var favorites, filter, j, len, preset;
    favorites = manager.state.favoriteFilters.slice(0);
    preset = this.state.currentLook.props.preset;
    for (j = 0, len = favorites.length; j < len; j++) {
      filter = favorites[j];
      if (filter.id === preset.id) {
        return;
      }
    }
    favorites.push(preset);
    return manager.set("favoriteFilters", favorites);
  },
  removeFavorite: function() {
    var favorites, index;
    index = this.state.currentLook.index;
    favorites = manager.state.favoriteFilters.slice(0);
    favorites.splice(index, 1);
    return manager.set("favoriteFilters", favorites);
  },

  /* DOM manipulation */
  transform: function(el, transform, visibility, trans) {
    el.style.visibility = visibility;
    el.style.webkitTransform = transform;
    el.style.transform = transform;
    return el.style.transition = trans ? trans : "none";
  },
  panEls: function(coll, index, delta) {
    var cur, next, prev;
    cur = coll[index];
    prev = coll[index - 1] || coll[coll.length - 1];
    next = coll[index + 1] || coll[0];
    this.transform(cur, "translate(" + delta + "px,0)", "");
    if (prev) {
      this.transform(prev, "translate(" + (-this.container.clientWidth + delta) + "px,0)", "");
    }
    if (next) {
      return this.transform(next, "translate(" + (this.container.clientWidth + delta) + "px,0)", "");
    }
  },
  showActiveTab: function(tabIndex) {
    var display, duration, i, index, j, lastIndex, lastPanel, len, nextIndex, prevIndex, ref, tab, transform;
    index = Math.min(tabIndex, this.props.packs.length);
    this.prevTab = this.prevTab != null ? this.prevTab : index;
    this.container = ReactDOM.findDOMNode(this);
    this.panels = $(this.container).find('.tab-panel');
    this.panel = this.container.querySelector('.tab-panel[data-index="' + index + '"]');
    this.panelIndex = this.panels.indexOf(this.panel);
    lastPanel = this.container.querySelector('.tab-panel[data-index="' + this.prevTab + '"]');
    lastIndex = this.panels.indexOf(lastPanel);
    prevIndex = (this.panelIndex ? this.panelIndex : this.panels.length) - 1;
    nextIndex = (this.panelIndex + 1) % this.panels.length;
    ref = this.panels;
    for (i = j = 0, len = ref.length; j < len; i = ++j) {
      tab = ref[i];
      if (i === prevIndex) {
        transform = "translate(-100%,0)";
      } else if (i === this.panelIndex) {
        transform = "";
      } else if (i === nextIndex) {
        transform = "translate(100%,0)";
      } else if (i < this.panelIndex) {
        transform = "translate(-100%,0)";
      } else if (i > this.panelIndex) {
        transform = "translate(100%,0)";
      }
      duration = i === this.panelIndex || i === lastIndex ? 200 : 0;
      display = duration === 200 ? "" : "hidden";
      this.transform(this.panels[i], transform, display, duration + "ms ease-out");
    }
    return this.prevTab = index;
  },
  showMenu: function(el, look) {
    this.setState({
      currentLook: look
    });
    return this.refs["menu"].show({
      target: el,
      offset: [16, 0]
    });
  },
  showSyncInfo: function(e) {
    return this.refs["syncInfo"].show({
      target: e.target,
      offset: [16, 0]
    });
  },

  /* Handlers */
  touchStart: function(e) {
    var el;
    if (e.type !== "touchstart") {
      e.preventDefault();
    }
    el = $(e.target).closest(".preview")[0];
    if (el) {
      this.touchRippleStart(e, el.querySelector(".thumb"));
    }
    this.swipeStarted = false;
    return true;
  },
  touchMove: function(e, input) {
    if (input.dragging) {
      e.preventDefault();
    }
    this.swipeStarted = true;
    this.swipeX = input.deltaX;
    if (this.props.rotated) {
      this.swipeX = -this.swipeX;
    }
    return this.panEls(this.panels, this.panelIndex, this.swipeX);
  },
  touchEnd: function(e) {
    var delta, index, movement, tab;
    if (!this.swipeStarted) {
      return;
    }
    movement = this.swipeX / this.container.clientWidth;
    delta = 0;
    if (movement > 0.25) {
      delta = -1;
    }
    if (movement < -0.25) {
      delta = 1;
    }
    index = (this.panelIndex + delta) % this.panels.length;
    if (index < 0) {
      index = this.panels.length + index;
    }
    tab = parseInt(this.panels[index].dataset.index);
    if (this.panelIndex !== index) {
      this.props.showTitle(this.props.packs[tab].name);
    }
    this.showActiveTab(tab);
    return this.setFilterTab(tab);
  },
  touchCancel: function(e) {
    this.touchEnd();
    return this.touchRippleEnd();
  },
  longPress: function(e) {
    var look;
    e.preventDefault();
    look = this.getComponent(e.target);
    if (look != null) {
      return this.showMenu(e.target, look);
    }
  },
  touchTap: function(e) {
    var look;
    look = this.getComponent(e.target);
    if (look != null) {
      e.preventDefault();
      return this.applyLook(look);
    }
  },
  getComponent: function(target) {
    var el;
    el = $(target).closest(".preview")[0];
    if (el) {
      return this.refs[el.dataset.pointer];
    }
  },
  setFilterTab: Filtr.utils.debounce(function(tab) {
    return manager.set("filter_tab", tab);
  }, 200),
  hidePackInfo: function() {
    return this.setState({
      showInfo: false
    });
  },
  showPackInfo: function(pack) {
    return this.setState({
      showInfo: true,
      showInfoPack: pack
    });
  },
  renderButton: function(icon, text, action) {
    if (CONFIG.platform === "mobile") {
      return (
        React.createElement("li", {className: "preview", onTouchTap: action}, 
          React.createElement("div", {className: "preview-header"}, text), 
          React.createElement("div", {className: "thumb"}, React.createElement("i", {className: icon}))
        )
      );
    } else {
      return (
        React.createElement(Button, {onTap: action}, 
          React.createElement("i", {className: icon}), 
          text
        )
      );
    }
  },
  renderInfoButton: function(pack) {
    return this.renderButton("icon-info", translate("buttons.info"), this.showPackInfo.bind(this, pack));
  },
  render: function() {
    var importFilterButton, importFilterMenu, item, menu, moreFiltersButton, newFilterButton, ref, scanFilterButton, syncingInfo, syncingLabel;
    newFilterButton = this.renderButton("icon-add-filter", translate("buttons.new_filter"), this.createFilter);
    moreFiltersButton = this.renderButton("icon-shop", translate("menus.more_filters"), this.moreFilters);
    if (CONFIG.platform === "mobile") {
      scanFilterButton = React.createElement("a", {onTouchTap: this.scanFilter}, translate("buttons.scan_filter"));
    }
    if (!CONFIG.embedded) {
      importFilterButton = this.renderButton("icon-qr-code", translate("buttons.import_filter"), this.importFilter);
      importFilterMenu = 
        React.createElement(Menu, {ref: "import-menu"}, 
          React.createElement("a", {onTouchTap: actions.externalFilterStore}, translate("menus.filters_users")), 
          scanFilterButton, 
          React.createElement("a", {onTouchTap: this.importFilterPhotos}, translate("buttons.import_filter_photos"))
        )
      ;
    }
    if ((ref = this.state.currentLook) != null ? ref.props.locked : void 0) {
      if (this.state.currentLook.props.removable) {
        item = React.createElement("a", {onTouchTap: this.removeFavorite}, React.createElement("i", {className: "icon-remove"}), translate("menus.remove"));
      } else {
        item = React.createElement("a", {onTouchTap: this.favoriteLook}, React.createElement("i", {className: "icon-heart"}), translate("buttons.add_to_favorites"));
      }
      menu = 
        React.createElement(Menu, {ref: "menu"}, 
          item
        )
      ;
    } else {
      menu = 
        React.createElement(Menu, {ref: "menu"}, 
          React.createElement("a", {onTouchTap: this.favoriteLook}, React.createElement("i", {className: "icon-heart"}), translate("buttons.add_to_favorites")), 
          React.createElement("a", {onTouchTap: this.exportLook}, React.createElement("i", {className: "icon-export"}), translate("menus.share_filter")), 
          React.createElement("div", {className: "sep"}), 
          React.createElement("a", {onTouchTap: this.editLook}, React.createElement("i", {className: "icon-edit-text"}), translate("menus.rename")), 
          React.createElement("a", {onTouchTap: this.removeLook}, React.createElement("i", {className: "icon-remove"}), translate("menus.remove"))
        )
      ;
    }
    syncingLabel = (function() {
      switch (this.props.syncing) {
        case 1:
          return React.createElement("span", null, React.createElement("span", {className: "indicator grey"}), " ", translate("status.filters_syncing"));
        case 2:
          return React.createElement("span", null, React.createElement("span", {className: "indicator green"}), " ", translate("status.filters_synced"));
        case 3:
          return React.createElement("span", null, React.createElement("span", {className: "indicator red"}), " ", translate("status.filters_sync_error"));
      }
    }).call(this);
    if (syncingLabel) {
      syncingInfo = 
        React.createElement(Menu, {ref: "syncInfo"}, 
          React.createElement("a", null, syncingLabel)
        )
      ;
    }
    return (
      React.createElement("div", {className: "previews-container"}, 
        React.createElement(EditDialog, {ref: "edit-dialog", rotated: this.props.rotated}), 
        React.createElement(InfoDialog, {pack: this.state.showInfoPack, active: this.state.showInfo, onHide: this.hidePackInfo, rotated: this.props.rotated}), 
        menu, 
        syncingInfo, 
        importFilterMenu, 
        this.props.packs.map(function(pack, i){
          var featureBlock = pack.enabled || pack.is_free ? null : React.createElement(FeatureBlock, {container: "looks", item: pack.pack_id});
          var packInfoButton = pack.info ? this.renderInfoButton(pack) : null;
          var appendLabel = syncingLabel && pack.pack_id === "custom" ? React.createElement(Button, {className: "info", onTap: this.showSyncInfo}, syncingLabel) : null
          return (
          React.createElement(Scrollable, {
            rotated: this.props.rotated, 
            disabled: !(pack.enabled || pack.is_free), 
            key: i, 
            props: {"data-index": i}, 
            ref: "pack_"+pack.pack_id, 
            className: "tab-panel"}, 
            React.createElement(PointerEvents, {
              panX: true, 
              onPointerDown: this.touchStart, 
              onPointerMove: this.touchMove, 
              onPointerUp: this.touchEnd, 
              onPointerCancel: this.touchCancel, 
              onLongPress: this.longPress, 
              onTap: this.touchTap}, 
              React.createElement("ul", {className: "previews"}, 
                pack.filters.map(function(filter, j){
                  return React.createElement(Look, {
                    index: j, 
                    key: filter.id, 
                    ref: i+filter.id, 
                    pointer: i+filter.id, 
                    preset: filter, 
                    decode: pack.decode, 
                    removable: pack.removable, 
                    locked: !pack.editable, 
                    position: this.props.position, 
                    change: this.renameLook})
                  }, this), 
                React.createElement("div", {className: "sep"}), 
                i === 1 ? importFilterButton : null, 
                i === 1 ? newFilterButton : null, 
                packInfoButton, 
                moreFiltersButton, 
                appendLabel, 
                featureBlock
              )
            )
          )
            )
          }, this)
      )
    );
  }
});

InfoDialog = React.createClass({displayName: "InfoDialog",
  mixins: [require("react-addons-pure-render-mixin")],
  getName: function() {
    var locale, pack, presetName;
    pack = this.props.pack || {};
    presetName = pack.name || "";
    locale = translate.getLocale();
    return presetName = presetName[locale] || presetName["en"] || presetName;
  },
  hide: function() {
    return this.refs.dialog.hide();
  },
  render: function() {
    var pack, packInfo;
    pack = this.props.pack;
    if (pack) {
      packInfo = 
        React.createElement("div", {className: "text"}, 
          React.createElement("div", {dangerouslySetInnerHTML: {__html:translate(pack.info)}}), 
          React.createElement("div", {className: "credits"}, 
            pack.filters.map(function(filter){
              return React.createElement("div", {key: filter.name}, 
                React.createElement("strong", null, filter.name), " by ", React.createElement("a", {href: "https://"+filter.web}, filter.author)
              )
              }, this)
          )
        )
      ;
    }
    return (
      React.createElement(Dialog, React.__spread({ref: "dialog", name: "pack-info"},  this.props, {showclose: true}), 
        React.createElement("h1", null, this.getName()), 
        packInfo
      )
    );
  }
});

EditDialog = React.createClass({displayName: "EditDialog",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      active: false
    };
  },
  rename: function(key, name) {
    return actions.renameFilter(key, name);
  },
  show: function(filter) {
    return this.setState({
      active: true,
      index: filter.props.index,
      name: filter.getName()
    });
  },
  onShow: function() {
    var input;
    input = this.refs.input;
    input.focus();
    return input.select();
  },
  hide: function() {
    return this.setState({
      active: false
    });
  },
  onChange: function(e) {
    return this.setState({
      name: e.target.value
    });
  },
  save: function(e) {
    e.preventDefault();
    this.rename(this.state.index, this.state.name);
    return this.hide();
  },
  removeFilter: function(filter) {
    return this.rename(filter.props.index, null);
  },
  render: function() {
    return (
      React.createElement(Dialog, React.__spread({ref: "dialog", name: "edit-filter"},  this.props, {onShow: this.onShow, active: this.state.active}), 
        React.createElement("form", {onSubmit: this.save}, 
          React.createElement("h1", null, translate("dialogs.rename_filter")), 
          React.createElement("div", null, 
            React.createElement("input", {ref: "input", type: "text", value: this.state.name, onChange: this.onChange})
          ), 
          React.createElement("div", {className: "footer"}, 
            React.createElement(Button, {content: "buttons.cancel", onTap: this.hide}), 
            React.createElement(Button, {content: "buttons.save", onTap: this.save, className: "primary"})
          )
        )
      )
    );
  }
});

connectedSlider = null;

Look = React.createClass({displayName: "Look",
  getDefaultProps: function() {
    return {
      min: 0,
      max: 1,
      position: "right"
    };
  },
  getInitialState: function() {
    return {
      value: 1,
      modal: true
    };
  },
  deactivate: function() {
    var error;
    try {
      connectedSlider.disconnect();
      connectedSlider = null;
      ReactDOM.findDOMNode(this).removeEventListener("blur", this.deactivate);
    } catch (error) {
      console.log("filter is removed already from DOM");
    }
    return setTimeout((function(_this) {
      return function() {
        var name;
        if (connectedSlider) {
          return;
        }
        if (_this.state.value === 0) {
          Filtr.setState(_this.interpolateAdjustments(_this.state.value));
        }
        name = _this.state.value === 0 ? translate("labels.none") : encodeURIComponent(_this.getName());
        Filtr.saveState("labels.look %10:%20" + name);
        utils.trackEventGA("filter", "apply", name);
        return manager.emit("apply_filter");
      };
    })(this), 1);
  },
  activate: function() {
    var node;
    node = ReactDOM.findDOMNode(this);
    if (node) {
      node.addEventListener("blur", this.deactivate);
      if (document.activeElement === node) {
        this.state.value = 0;
        return node.blur();
      } else {
        node.focus();
        this.state.value = 1;
        connectedSlider = PopoutSlider.connect(this);
        this.initialPresetState = this.getPreset();
        Filtr.setState(this.initialPresetState);
        clearTimeout(this.updateTimeout);
        return this.updateTimeout = setTimeout(function() {
          Filtr.update();
          return manager.emit("select_filter");
        }, 100);
      }
    }
  },
  setValue: function(value) {
    this.state.value = utils.clamp(value, 0, 1);
    Filtr.setState(this.interpolateAdjustments(this.state.value));
    return connectedSlider != null ? connectedSlider.update(this.state.value, this.getMulValue()) : void 0;
  },
  saveState: function() {
    return Filtr.update();
  },
  apply: function() {
    var error;
    try {
      return ReactDOM.findDOMNode(this).blur();
    } catch (error) {
      return console.log("filter is removed already from DOM");
    }
  },
  cancel: function() {
    this.state.value = 0;
    return ReactDOM.findDOMNode(this).blur();
  },
  reset: function() {
    this.state.value = 1;
    connectedSlider.update(this.state.value, this.getMulValue());
    return Filtr.setState(this.initialPresetState);
  },
  getMulValue: function() {
    return this.state.value * 100 >> 0;
  },
  getPreset: function() {
    return Filtr.prepareAdjustments(this.props.preset.state);
  },
  interpolateAdjustments: function(percent) {
    var output;
    output = Filtr.utils.clone(this.initialPresetState);
    return Filtr.utils.interpolateAdjustments(this.initialPresetState, percent, output);
  },
  updatePreview: function(callback) {
    return requestAnimationFrame((function(_this) {
      return function() {
        var canvas, output;
        canvas = _this.refs["canvas"];
        if (_this.renderedPhoto === store.currentPhoto) {
          if (typeof callback === "function") {
            callback(canvas, _this.props.preset.id);
          }
          return;
        }
        output = actions.renderThumbnail(_this.props.preset.state, {
          size: 68
        });
        canvas.width = output.width;
        canvas.height = output.height;
        utils.drawImageData(canvas, output);
        setTimeout(function() {
          return canvas.style.opacity = 1;
        }, 200);
        _this.renderedPhoto = store.currentPhoto;
        return typeof callback === "function" ? callback(canvas, _this.props.preset.id) : void 0;
      };
    })(this));
  },
  getName: function() {
    var locale, presetName;
    presetName = this.props.preset.name;
    locale = translate.getLocale();
    presetName = presetName[locale] || presetName["en"] || presetName;
    if (this.props.decode) {
      return decodeURIComponent(presetName);
    } else {
      return presetName;
    }
  },
  render: function() {
    return (
      React.createElement("li", {className: "preview", "data-pointer": this.props.pointer, tabIndex: "-1"}, 
        React.createElement("div", {className: "preview-header"}, 
          React.createElement("span", {ref: "name", className: "name"}, this.getName())
        ), 
        React.createElement("div", {className: "thumb"}, 
          React.createElement("canvas", {ref: "canvas"})
        )
      )
    );
  }
});


}).call(this,require("react"),require("react-dom"))
},{"../actions.coffee":229,"../manager.coffee":338,"../mixins.coffee":339,"../presets.js":340,"../store.coffee":346,"../utils.coffee":360,"./button.coffee":241,"./dialog.coffee":249,"./feature-block.coffee":255,"./icon.coffee":262,"./panel.coffee":272,"./pointer-events.coffee":273,"./popout-slider.coffee":275,"./popover-menu.coffee":276,"./scrollable.coffee":283,"./tooltip.coffee":293,"classnames":1,"counterpart":3,"jquery":319,"react":215,"react-addons-pure-render-mixin":32,"react-dom":34,"shortid":216}],267:[function(require,module,exports){
(function (React){
var MainMenu, Menu, actions, classnames, io, keyboard, manager, store, translate, utils;

Menu = require("./popover-menu.coffee");

translate = require("counterpart");

classnames = require("classnames");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

store = require("../store.coffee");

io = require("../io.coffee");

utils = require("../utils.coffee");

keyboard = require("../keyboard.coffee");

MainMenu = React.createClass({displayName: "MainMenu",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      saveable: store.saveable,
      sharing: manager.state.sharingOptions
    };
  },
  componentDidMount: function() {
    store.on("saveable", (function(_this) {
      return function(saveable) {
        return _this.setState({
          saveable: saveable
        });
      };
    })(this));
    return manager.on("sharingOptions", (function(_this) {
      return function(options) {
        return _this.setState({
          sharing: options
        });
      };
    })(this));
  },
  show: function(e) {
    return this.refs["menu"].show({
      target: e.target,
      offset: [8, -8]
    });
  },
  "import": function() {
    return io["import"]();
  },
  shareFilter: function() {
    return actions.setDialog("create_filter", {
      showOptions: false
    });
  },
  batchExport: function() {
    if (CONFIG.platform === "mobile") {
      return actions.showBatchExport();
    } else {
      return actions.setDialog("export_photo", {
        type: "batch_export"
      });
    }
  },
  open: function() {
    return io.open();
  },
  "export": function() {
    return actions.setDialog("export_photo", {
      type: "single_export"
    });
  },
  openYoutu: function() {
    return actions.openURL("http://www.polayoutu.com");
  },
  save: function() {
    return io.save("save");
  },
  copy: function() {
    return io.save("copy");
  },
  share: function(service) {
    return io.save("share", {
      service: service
    });
  },
  render: function() {
    var batchExport, exportClass, exportCopy, saveButton, saveCopy, share, shareFilter, shareSep, youtu, youtuSep;
    if (CONFIG.enableSave) {
      saveButton = React.createElement("a", {className: this.state.saveable === 0 ? "export" : "disabled", onTouchTap: this.save}, translate("buttons.save"));
    }
    if (CONFIG.enableSaveCopy) {
      saveCopy = React.createElement("a", {className: "export", onTouchTap: this.copy}, translate("menus.save_copy"), keyboard.renderShortcut("save"));
      shareFilter = React.createElement("a", {className: "export", onTouchTap: this.shareFilter}, translate("menus.share_edits"), "...");
      shareSep = React.createElement("div", {className: "sep"});
    }
    if (CONFIG.enableSaveCopy && CONFIG.platform !== "mobile") {
      exportCopy = React.createElement("a", {className: "export", onTouchTap: this.export}, translate("menus.export"), keyboard.renderShortcut("save_as"));
    }
    if (CONFIG.enableShare) {
      shareFilter = React.createElement("a", {className: "export", onTouchTap: this.shareFilter}, translate("menus.share_edits"), "...");
      shareSep = React.createElement("div", {className: "sep"});
      if (CONFIG.platform === "osx") {
        share = this.state.sharing.map(function(option, i){
          return React.createElement("a", {className: "export", key: i, onTouchTap: this.share.bind(this, option.title)}, React.createElement("img", {src: option.icon}), option.title)
        }, this);
        share.unshift(React.createElement("div", {key: "sep", className: "sep"}));
      } else {
        share = React.createElement("a", {className: "export", onTouchTap: this.share}, translate("menus.share"), "...");
      }
    }
    if (CONFIG.enableBatchExport) {
      exportClass = classnames("export", {
        "purchase-required": !actions.isProPurchased()
      });
      batchExport = 
        React.createElement("a", {className: exportClass, "data-purchase": translate("status.purchase_required"), onTouchTap: this.batchExport}, 
          translate("menus.batch_export"), "...", keyboard.renderShortcut("batch_export")
        );
    }
    if (utils.isChina()) {
      youtuSep = React.createElement("div", {className: "sep"});
      youtu = React.createElement("a", {className: "export", onTouchTap: this.openYoutu}, "泼辣有图");
    }
    return (
      React.createElement("div", {className: "hidden"}, 

        React.createElement(Menu, {ref: "menu"}, 
          saveCopy, 
          saveButton, 
          exportCopy, 
          batchExport, 
          share, 
          shareSep, 
          shareFilter, 
          youtuSep, 
          youtu
        )
      )
    );
  }
});

module.exports = MainMenu;


}).call(this,require("react"))
},{"../actions.coffee":229,"../io.coffee":312,"../keyboard.coffee":313,"../manager.coffee":338,"../store.coffee":346,"../utils.coffee":360,"./popover-menu.coffee":276,"classnames":1,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],268:[function(require,module,exports){
(function (React){
var CoverOverlay, Dialogs, DropZone, Main, PopoutPicker, PopoutSlider, Toolbars, Tutorial, Viewport, actions, classnames, keyboard, manager, mixins, store;

Viewport = require("./viewport.coffee");

Toolbars = require("./toolbars.coffee");

CoverOverlay = require("./cover-overlay.coffee");

Dialogs = require("./dialogs.coffee");

PopoutSlider = require("./popout-slider.coffee");

PopoutPicker = require("./popout-colorpicker.coffee");

Tutorial = require("./tutorial.coffee");

DropZone = require("./drop-zone.coffee");

store = require("../store.coffee");

mixins = require("../mixins.coffee");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

keyboard = require("../keyboard.coffee");

require("react-tap-event-plugin")();

classnames = require("classnames");

if (CONFIG.debug) {
  window.Actions = actions;
  window.Manager = manager;
  window.Store = store;
}

Main = React.createClass({displayName: "Main",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      portraitMode: manager.state.portraitMode,
      disabled: !store.currentPhoto
    };
  },
  componentDidMount: function() {
    store.on("loadImage", (function(_this) {
      return function() {
        return _this.setState({
          disabled: !store.currentPhoto
        });
      };
    })(this));
    manager.on("portraitMode", (function(_this) {
      return function(mode) {
        return _this.setState({
          portraitMode: mode
        });
      };
    })(this));
    actions.restoreAppState(function() {
      if (CONFIG.embedded) {
        manager.set("photo_status", {
          status: "importing"
        });
      }
      if (CONFIG.platform !== "mobile" || CONFIG.debug && !CONFIG.embedded) {
        store.loadPhotos();
      }
      if (CONFIG.os === "android") {
        return actions.setOrientation();
      }
    });
    return this.componentDidUpdate();
  },
  componentDidUpdate: function() {
    var className;
    className = classnames(CONFIG.platform, {
      "portrait-mode": this.state.portraitMode,
      "disabled": this.state.disabled
    });
    return document.body.className = className;
  },
  render: function() {
    var dropZone;
    if (!CONFIG.embedded) {
      dropZone = React.createElement(DropZone, null);
    }
    return (
      React.createElement("div", {id: "main"}, 
        React.createElement(Viewport, {portraitMode: this.state.portraitMode, disabled: this.state.disabled}), 
        React.createElement(Toolbars, {portraitMode: this.state.portraitMode, disabled: this.state.disabled}), 
        React.createElement(CoverOverlay, {portraitMode: this.state.portraitMode}), 
        React.createElement(Dialogs, {portraitMode: this.state.portraitMode}), 
        React.createElement(Tutorial, {portraitMode: this.state.portraitMode}), 
        React.createElement(PopoutSlider, null), 
        React.createElement(PopoutPicker, null), 
        dropZone
      )
    );
  }
});

module.exports = Main;


}).call(this,require("react"))
},{"../actions.coffee":229,"../keyboard.coffee":313,"../manager.coffee":338,"../mixins.coffee":339,"../store.coffee":346,"./cover-overlay.coffee":244,"./dialogs.coffee":250,"./drop-zone.coffee":251,"./popout-colorpicker.coffee":274,"./popout-slider.coffee":275,"./toolbars.coffee":292,"./tutorial.coffee":294,"./viewport.coffee":299,"classnames":1,"react":215,"react-addons-pure-render-mixin":32,"react-tap-event-plugin":38}],269:[function(require,module,exports){
(function (React){
var Button, Prompt, manager, storage, translate;

translate = require("counterpart");

Button = require("./button.coffee");

manager = require("../manager.coffee");

storage = require("../storage.coffee");

Prompt = React.createClass({displayName: "Prompt",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      active: false,
      prompts: {},
      message: null,
      button: null,
      action: null
    };
  },
  componentDidMount: function() {
    return manager.on("onetime_prompt", this.showPrompt);
  },
  showPrompt: function(prompt) {
    var key;
    key = prompt.id;
    return storage.getItem("onetime_prompt", (function(_this) {
      return function(res, prompts) {
        if (prompts == null) {
          prompts = {};
        }
        if (!prompts[key]) {
          prompts[key] = true;
          return _this.setState({
            prompts: prompts,
            message: prompt.message,
            button: prompt.button,
            action: prompt.action,
            active: true
          });
        }
      };
    })(this));
  },
  confirm: function() {
    var base;
    if (typeof (base = this.state).action === "function") {
      base.action();
    }
    return this.dismiss();
  },
  dismiss: function() {
    storage.setItem("onetime_prompt", this.state.prompts);
    return this.setState({
      active: false
    });
  },
  render: function() {
    var style;
    style = {
      left: this.props.left + "px",
      right: this.props.right + "px",
      top: this.props.top + "px"
    };
    return (
      React.createElement("div", {style: style, className: "prompt-overlay"}, 
        React.createElement("div", {className: this.state.active ? "prompt active" : "prompt", ref: "prompt"}, 
          React.createElement(Button, {content: "buttons.dismiss", onTap: this.dismiss}), 
          React.createElement(Button, {className: "primary", onTap: this.confirm}, 
            React.createElement("span", {dangerouslySetInnerHTML: {__html:this.state.message}})
          )
        )
      )
    );
  }
});

module.exports = Prompt;


}).call(this,require("react"))
},{"../manager.coffee":338,"../storage.coffee":343,"./button.coffee":241,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],270:[function(require,module,exports){
(function (React){
var Overlay, PointerEvents, keyboard, mousetrap, shortcut;

PointerEvents = require("./pointer-events.coffee");

keyboard = require("../keyboard.coffee");

mousetrap = require("mousetrap");

shortcut = keyboard.shortcuts.pan;

Overlay = React.createClass({displayName: "Overlay",
  componentDidUpdate: function(prevProps) {
    if (!prevProps.active && this.props.active) {
      return this.show();
    } else if (prevProps.active && !this.props.active) {
      return this.hide();
    }
  },
  show: function() {
    mousetrap.bind(shortcut, this.disable, "keydown");
    return mousetrap.bind(shortcut, this.enable, "keyup");
  },
  hide: function() {
    mousetrap.unbind(shortcut, "keydown");
    return mousetrap.unbind(shortcut, "keyup");
  },
  enable: function(e) {
    e.preventDefault();
    return this.setDisabled(false);
  },
  disable: function(e) {
    e.preventDefault();
    return this.setDisabled(true);
  },
  setDisabled: function(disabled) {
    var base;
    if (!this.props.active || this.props.disabled === disabled) {
      return;
    }
    return typeof (base = this.props).setDisabled === "function" ? base.setDisabled(disabled) : void 0;
  },
  render: function() {
    var extra, overlay, style;
    extra = {
      onMouseMove: this.props.onMouseMove,
      onMouseEnter: this.props.onMouseEnter,
      onMouseLeave: this.props.onMouseLeave
    };
    if (this.props.rotated) {
      style = {
        width: window.innerHeight,
        height: window.innerWidth,
        transform: "translate(-50%, -50%) rotate(-90deg)",
        pointerEvents: this.props.disabled ? "none" : ""
      };
    } else {
      style = {
        width: window.innerWidth,
        height: window.innerHeight,
        pointerEvents: this.props.disabled ? "none" : ""
      };
    }
    if (this.props.passthrough) {
      style.pointerEvents = "";
      extra["data-passthrough"] = true;
    }
    if (this.props.hideCursor) {
      style.cursor = 'none';
    }
    overlay = (
      React.createElement("div", React.__spread({
        className: this.props.active ? 'overlay active' : 'overlay', 
        style: style}, 
        extra), 
        this.props.children
      )
    );
    if (this.props.pointerEvents) {
      return React.cloneElement(React.createElement(PointerEvents, null), this.props.pointerEvents, overlay);
    } else {
      return overlay;
    }
  }
});

module.exports = Overlay;


}).call(this,require("react"))
},{"../keyboard.coffee":313,"./pointer-events.coffee":273,"mousetrap":21,"react":215}],271:[function(require,module,exports){
(function (React){
exports.ContainerGroup = function(props) {
  var direction;
  direction = "left";
  return (
    React.createElement("div", {className: "panel-container"}, 
      React.Children.map(props.children, function(child, index){
        if (child.props.name === props.active) {
          direction = "right"
          return React.cloneElement(child, {direction: direction, active: true})
        } else {
          return React.cloneElement(child, {direction: direction})
        }
      }, this)
    )
  );
};

exports.PanelContainer = function(props) {
  var activeStyle, inactiveStyle, transform;
  transform = props.direction === "left" ? "translateX(-100%)" : "translateX(100%)";
  inactiveStyle = {
    transform: transform,
    WebkitTransform: transform,
    opacity: 0,
    transition: "visibility 0s linear 0.3s, all 0.3s",
    visibility: "hidden"
  };
  activeStyle = {
    transform: "",
    WebkitTransform: "",
    transition: "visibility 0s linear 0s, all 0.3s",
    visibility: "visible"
  };
  return (
    React.createElement("div", {
      className: "panel-container", 
      "data-name": props.name, 
      style: props.active ? activeStyle : inactiveStyle}, 
      props.children
    )
  );
};


}).call(this,require("react"))
},{"react":215}],272:[function(require,module,exports){
(function (React,ReactDOM){
var Panel, manager;

manager = require("../manager.coffee");

Panel = React.createClass({displayName: "Panel",
  getDefaultProps: function() {
    return {
      active: false
    };
  },
  componentDidMount: function() {
    this.node = ReactDOM.findDOMNode(this);
    return this.componentDidUpdate();
  },
  componentDidUpdate: function(prevProps) {
    if (prevProps == null) {
      prevProps = {};
    }
    if (!prevProps.active && this.props.active) {
      return this.show();
    } else if (!this.props.active && prevProps.active) {
      return this.hide();
    }
  },
  fade: function(to) {
    var startOpacity;
    startOpacity = Number(this.node.style.opacity);
    if (typeof this.cancelAnimation === "function") {
      this.cancelAnimation();
    }
    return this.cancelAnimation = Filtr.utils.animate([startOpacity], [to], 0.3, (function(_this) {
      return function(val) {
        return _this.node.style.opacity = val[0];
      };
    })(this));
  },
  show: function() {
    this.node.style.pointerEvents = "all";
    this.node.style.visibility = "visible";
    this.node.style.zIndex = 2;
    this.fade(1);
    return manager.emit("show_panel", this.props.name);
  },
  hide: function() {
    this.node.style.pointerEvents = "none";
    this.fade(0);
    return setTimeout((function(_this) {
      return function() {
        _this.node.style.visibility = "hidden";
        return _this.node.style.zIndex = 0;
      };
    })(this), 300);
  },
  render: function() {
    return (
      React.createElement("div", {className: "panel", 
           "data-position": this.props.position, 
           "data-name": this.props.name}, 
        this.props.children
      )
    );
  }
});

module.exports = Panel;


}).call(this,require("react"),require("react-dom"))
},{"../manager.coffee":338,"react":215,"react-dom":34}],273:[function(require,module,exports){
(function (React,ReactDOM){
var $, PointerEvents, eventsMap, getDistance, getPointerCenter, getPointerDistance, propagationStopped, updateWhere, utils;

utils = require("../utils.coffee");

$ = require("jquery");

getDistance = function(p1, p2) {
  var dx, dy;
  dx = p2[0] - p1[0];
  dy = p2[1] - p1[1];
  dx *= dx;
  dy *= dy;
  return Math.sqrt(dx + dy);
};

getPointerDistance = function(pointers) {
  var p1, p2;
  p1 = [pointers[0].pageX, pointers[0].pageY];
  p2 = [pointers[1].pageX, pointers[1].pageY];
  return getDistance(p1, p2);
};

getPointerCenter = function(pointers) {
  return [pointers[0].pageX * 0.5 + pointers[1].pageX * 0.5, pointers[0].pageY * 0.5 + pointers[1].pageY * 0.5];
};

updateWhere = function(array, item, prop) {
  var i, inArray, j, len, v;
  inArray = false;
  for (i = j = 0, len = array.length; j < len; i = ++j) {
    v = array[i];
    if (v[prop] === item[prop]) {
      array[i] = item;
      inArray = true;
    }
  }
  if (!inArray) {
    array.push(item);
  }
  return array;
};

eventsMap = {
  pointer: {
    "pointerdown": "_pointerDown",
    "pointermove": "_pointerMove",
    "pointerup": "_pointerUp",
    "pointercancel": "_pointerCancel",
    "contextmenu": "_longPress"
  },
  touch: {
    "touchstart": "_pointerDown",
    "touchmove": "_pointerMove",
    "touchend": "_pointerUp",
    "touchcancel": "_pointerCancel",
    "contextmenu": "_longPress"
  },
  mouse: {
    "mousedown": "_pointerDown",
    "mousemove": "_pointerMove",
    "mouseup": "_pointerUp",
    "contextmenu": "_longPress"
  }
};

propagationStopped = false;

PointerEvents = React.createClass({displayName: "PointerEvents",
  componentWillUnmount: function() {
    var node;
    node = ReactDOM.findDOMNode(this);
    return node.removeEventListener("pointerdown", this.pointerHelperStart);
  },
  componentDidMount: function() {
    var node;
    node = ReactDOM.findDOMNode(this);
    node.addEventListener("pointerdown", this.pointerHelperStart);
    this.scrollView = $(node).closest(".scrollable")[0];
    if (this.props.panX) {
      node.style.touchAction = "pan-y";
    } else if (this.props.panY) {
      node.style.touchAction = "pan-x";
    } else if (this.props.multitouch) {
      node.style.touchAction = "none";
    } else {
      node.style.touchAction = "auto";
    }
    this.pointers = [];
    this.listenersAttached = false;
    this._pressing = false;
    this._dragging = false;
    this._initialInput = null;
    if (CONFIG.platform === "windows") {
      return this._pointerMove = Filtr.utils.throttleAnimation(this._pointerMove);
    }
  },
  onScroll: function() {
    this.scrolling = true;
    clearTimeout(this.scrollTimeout);
    this.scrollTimeout = setTimeout((function(_this) {
      return function() {
        return _this.scrolling = false;
      };
    })(this), 100);
    return this._pointerCancel();
  },
  getDefaultProps: function() {
    return {
      threshold: 10,
      longPressDelay: 300,
      panX: false,
      panY: false,
      multitouch: false
    };
  },
  updatePointers: function(event) {
    var touches;
    if (event.touches != null) {
      touches = this.props.ignoreTouchTarget ? event.touches : event.targetTouches;
      return this.pointers = touches;
    } else if (event.pointerId != null) {
      return this.pointers = updateWhere(this.pointers, event, "pointerId");
    } else {
      return this.pointers = [event];
    }
  },
  getEventType: function(event) {
    if (eventsMap.pointer[event.type]) {
      return "pointer";
    } else if (eventsMap.touch[event.type]) {
      return "touch";
    } else {
      return "mouse";
    }
  },
  stopPropagation: function() {
    propagationStopped = true;
    return setTimeout(function() {
      return propagationStopped = false;
    }, 0);
  },
  getInput: function(event) {
    var center, distance, force, multitouch, pageX, pageY, ref;
    this.updatePointers(event);
    ref = this.pointers[0] || event.changedTouches[0], pageX = ref.pageX, pageY = ref.pageY, force = ref.force;
    multitouch = this.pointers.length > 1;
    if (multitouch) {
      distance = getPointerDistance(this.pointers);
      center = getPointerCenter(this.pointers);
    }
    return {
      force: force || 0,
      distance: distance || 0,
      center: center || [pageX, pageY],
      multitouch: multitouch,
      pointers: this.pointers,
      x: pageX,
      y: pageY,
      type: this._inputType,
      pointerType: this._pointerType,
      dragging: this._dragging,
      pressing: this._pressing,
      taps: this._taps,
      distanceDelta: 1,
      centerDelta: [0, 0],
      deltaX: 0,
      deltaY: 0,
      stopPropagation: this.stopPropagation
    };
  },
  pointerHelperStart: function(e) {
    var event, func, pointerType, ref, ref1, type;
    if (propagationStopped) {
      return;
    }
    if (this.scrolling) {
      return;
    }
    event = e.nativeEvent || e;
    type = this.getEventType(event);
    pointerType = e.pointerType || type;
    if (this._inputType && this._inputType !== type) {
      event.stopImmediatePropagation();
      event.preventDefault();
      return;
    }
    this._taps = this._taps || 0;
    this._inputType = type;
    this._pointerType = pointerType;
    this._currentEvent = event;
    this._initialInput = this.getInput(event);
    this._initialForceValue = this._initialInput.force || 0;
    if (this.props.onPointerDown != null) {
      if (this.props.onPointerDown(e, this._initialInput) === false) {
        this._removeListeners();
        return;
      }
    }
    if (!this.listenersAttached) {
      this._listenerTarget = this._inputType === "touch" ? e.target : window;
      ref = eventsMap[this._inputType];
      for (event in ref) {
        func = ref[event];
        this._listenerTarget.addEventListener(event, this[func]);
      }
      if ((ref1 = this.scrollView) != null) {
        ref1.addEventListener("scroll", this.onScroll);
      }
      this.listenersAttached = true;
    }
    clearTimeout(this._longPressTimeout);
    clearTimeout(this._tapCountTimeout);
    clearTimeout(this._singleTapTimeout);
    clearTimeout(this._clearInputTimeout);
    if (this.pointers.length > 1) {
      return;
    }
    this._tapCountTimeout = setTimeout((function(_this) {
      return function() {
        return _this._taps = 0;
      };
    })(this), this.props.longPressDelay);
    this._taps++;
    if (this._pointerType === "touch") {
      this._longPressTimeout = setTimeout(this._longPress.bind(this, this._currentEvent), this.props.longPressDelay);
    }
    return this._testForceValue();
  },
  _touchContext: function(e) {
    return e.preventDefault();
  },
  _pointerDown: function(e) {
    if (navigator.userAgent.indexOf('Mac OS X') === -1 && e.which === 3) {
      return this._longPress(e);
    }
  },
  _pointerMove: function(e) {
    var base, distance, input, threshold;
    if (propagationStopped) {
      return;
    }
    if ((this.props.onLongPress != null) && this._pressing) {
      e.preventDefault();
    }
    input = this.getInput(e);
    if (!this._dragging) {
      if (this.pointers.length === 1 && this._initialInput) {
        if (this.props.panX) {
          distance = Math.abs(input.x - this._initialInput.x);
        } else if (this.props.panY) {
          distance = Math.abs(input.y - this._initialInput.y);
        } else {
          distance = utils.distance([input.x, input.y], [this._initialInput.x, this._initialInput.y]);
        }
        threshold = input.pointerType === "touch" ? 10 : this.props.threshold;
        if (distance >= threshold) {
          this._initialInput = input;
          this._dragging = true;
        }
      } else {
        this._initialInput = input;
        this._dragging = true;
      }
    }
    if (!this._dragging) {
      return;
    }
    input.deltaX = input.x - this._initialInput.x;
    input.deltaY = input.y - this._initialInput.y;
    if (input.multitouch) {
      input.distanceDelta = input.distance / this._initialInput.distance;
      input.centerDelta[0] = input.center[0] - this._initialInput.center[0];
      input.centerDelta[1] = input.center[1] - this._initialInput.center[1];
    }
    this._taps = 0;
    this._dragging = true;
    clearTimeout(this._longPressTimeout);
    return typeof (base = this.props).onPointerMove === "function" ? base.onPointerMove(e, input) : void 0;
  },
  _pointerUp: function(e) {
    var base, base1;
    if (this._inputType !== "mouse") {
      e.preventDefault();
    }
    if (!this._dragging && !this._pressing) {
      if (typeof (base = this.props).onTap === "function") {
        base.onTap(e, this.getInput(e));
      }
      clearTimeout(this._singleTapTimeout);
      this._singleTapTimeout = setTimeout((function(_this) {
        return function() {
          return _this._singleTap(e);
        };
      })(this), 300);
    }
    if (this._taps > 1) {
      this._doubleTap(e);
    }
    if (typeof (base1 = this.props).onPointerUp === "function") {
      base1.onPointerUp(e, this.getInput(e));
    }
    return this._removeListeners(e);
  },
  _pointerCancel: function(e) {
    var base;
    this._removeListeners();
    return typeof (base = this.props).onPointerCancel === "function" ? base.onPointerCancel(e) : void 0;
  },
  _removeListeners: function(e) {
    var event, func, ref, ref1, ref2;
    this.pointers = [];
    ref = eventsMap[this._inputType];
    for (event in ref) {
      func = ref[event];
      if ((ref1 = this._listenerTarget) != null) {
        ref1.removeEventListener(event, this[func]);
      }
    }
    if ((ref2 = this.scrollView) != null) {
      ref2.removeEventListener("scroll", this.onScroll);
    }
    this.listenersAttached = false;
    this._dragging = false;
    this._initialInput = null;
    clearTimeout(this._clearInputTimeout);
    this._clearInputTimeout = setTimeout((function(_this) {
      return function() {
        return _this._inputType = null;
      };
    })(this), this.props.longPressDelay);
    clearTimeout(this._longPressTimeout);
    return this._pressing = false;
  },
  _longPress: function(e) {
    var base;
    e.preventDefault();
    clearTimeout(this._longPressTimeout);
    clearTimeout(this._singleTapTimeout);
    this._initialInput.pressing = true;
    this._pressing = true;
    return typeof (base = this.props).onLongPress === "function" ? base.onLongPress(e, this._initialInput) : void 0;
  },
  _doubleTap: function(e) {
    var base;
    this._taps = 0;
    clearTimeout(this._tapCountTimeout);
    clearTimeout(this._singleTapTimeout);
    return typeof (base = this.props).onDoubleTap === "function" ? base.onDoubleTap(e, this.getInput(e)) : void 0;
  },
  _singleTap: function(e) {
    var base;
    return typeof (base = this.props).onSingleTap === "function" ? base.onSingleTap(e, this._initialInput) : void 0;
  },
  _testForceValue: function() {
    var value;
    if (CONFIG.os === "android") {
      return;
    }
    if (CONFIG.platform !== "mobile") {
      return;
    }
    value = 0;
    if (this.pointers[0]) {
      value = this.pointers[0].force || 0;
      setTimeout(this._testForceValue, 10);
    } else {
      value = 0;
    }
    if (value !== this._initialForceValue && value >= 0.5) {
      return this._longPress(this._currentEvent);
    }
  },
  render: function() {
    var props;
    props = {
      onTouchStart: this.pointerHelperStart,
      onMouseDown: this.pointerHelperStart
    };
    return React.cloneElement(React.Children.only(this.props.children), props);
  }
});

module.exports = PointerEvents;


}).call(this,require("react"),require("react-dom"))
},{"../utils.coffee":360,"jquery":319,"react":215,"react-dom":34}],274:[function(require,module,exports){
(function (React){
var PointerEvents, PopoutPicker, classnames, mixins, status, store, translate, utils;

mixins = require("../mixins.coffee");

utils = require("../utils.coffee");

status = require("./status-overlay.coffee");

store = require("../store.coffee");

translate = require("counterpart");

classnames = require("classnames");

PointerEvents = require("./pointer-events.coffee");

PopoutPicker = React.createClass({displayName: "PopoutPicker",
  mixins: [mixins.pointer],
  getInitialState: function() {
    return {
      active: false,
      value: [0, 0],
      heights: [0, 0]
    };
  },
  componentDidMount: function() {
    module.exports.connect = this.connect;
    return module.exports.disconnect = this.disconnect;
  },
  shouldComponentUpdate: function(nextProps, nextState) {
    return nextState.active || this.state.active;
  },
  connect: function(component) {
    var timeout;
    this.sliders = [this.refs["slider-hue"], this.refs["slider-sat"]];
    this.handles = [this.refs["handle-hue"], this.refs["handle-sat"]];
    this.displays = [this.refs["value-hue"], this.refs["value-sat"]];
    timeout = this.state.active ? 0 : 100;

    /* component example
    component = {
      saveState: () ->
      setValue: () ->
      reset: () ->
      state: {
        value: [0, 0]
      }
      props: {
        position: "left"
      }
    }
     */
    clearTimeout(this.connectTimeout);
    this.connectTimeout = setTimeout((function(_this) {
      return function() {
        _this.setState({
          active: true,
          value: component.state.value,
          position: component.props.position,
          parent: component,
          heights: [_this.sliders[0].clientHeight, _this.sliders[1].clientHeight]
        });
        return _this.update(component.state.value);
      };
    })(this), timeout);
    return this;
  },
  disconnect: function() {
    clearTimeout(this.connectTimeout);
    return this.connectTimeout = setTimeout((function(_this) {
      return function() {
        return _this.setState({
          active: false
        });
      };
    })(this), 100);
  },
  saveState: function() {
    clearTimeout(this.saveStateTimeout);
    return this.saveStateTimeout = setTimeout((function(_this) {
      return function() {
        return _this.state.parent.saveState();
      };
    })(this), 300);
  },
  getPosition: function() {
    return [this.state.value[0] * (this.state.heights[0] - 1), this.state.value[1] * (this.state.heights[1] - 1)];
  },
  getDisplay: function() {
    return [(this.state.value[0] * 360 >> 0) + "°", (this.state.value[1] * 100 >> 0) + "%"];
  },
  getBackground: function() {
    var hue;
    hue = this.state.value[0] * 360;
    return "linear-gradient(0deg, hsl(" + hue + ", 0%, 50%), hsl(" + hue + ", 100%, 50%))";
  },
  update: function(value) {
    var background, display, pos, transformH, transformS;
    if (!this.state.active) {
      return;
    }
    this.state.value = value;
    display = this.getDisplay();
    pos = this.getPosition();
    background = this.getBackground();
    transformH = "translate(0, " + (-pos[0]) + "px)";
    transformS = "translate(0, " + (-pos[1]) + "px)";
    this.displays[0].innerText = display[0];
    this.displays[1].innerText = display[1];
    this.handles[0].style.webkitTransform = transformH;
    this.handles[0].style.transform = transformH;
    this.handles[1].style.webkitTransform = transformS;
    this.handles[1].style.transform = transformS;
    return this.sliders[1].style.backgroundImage = background;
  },
  doubleTap: function() {
    return this.state.parent.reset();
  },
  touchStart: function(e) {
    var index, ref, ref1;
    index = e.target.dataset.component;
    if (index != null) {
      this.index = parseInt(index, 10);
    }
    if ((ref = this.displays[this.index]) != null) {
      if ((ref1 = ref.style) != null) {
        ref1.opacity = 1;
      }
    }
    return this.startVal = this.state.value;
  },
  touchMove: function(e, input) {
    var deltaY, value;
    e.preventDefault();
    deltaY = input.deltaY / this.state.heights[this.index];
    value = this.startVal.slice(0);
    value[this.index] = utils.clamp(this.startVal[this.index] - deltaY, 0, 1);
    return this.state.parent.setValue(value);
  },
  touchEnd: function(e) {
    var ref, ref1, ref2;
    e.stopPropagation();
    e.preventDefault();
    if ((ref = this.displays) != null) {
      if ((ref1 = ref[this.index]) != null) {
        if ((ref2 = ref1.style) != null) {
          ref2.opacity = 0;
        }
      }
    }
    if (this.startVal[0] !== this.state.value[0] || this.startVal[1] !== this.state.value[1]) {
      return this.state.parent.saveState();
    }
  },
  render: function() {
    var background, cn, display, handleHue, handleSat, pos, satBackground, transformH, transformS;
    display = this.getDisplay();
    pos = this.getPosition();
    background = this.getBackground();
    transformH = "translate(0, " + (-pos[0]) + "px)";
    transformS = "translate(0, " + (-pos[1]) + "px)";
    handleHue = {
      transform: transformH,
      WebkitTransform: transformH
    };
    handleSat = {
      transform: transformS,
      WebkitTransform: transformS
    };
    satBackground = {
      backgroundImage: background
    };
    cn = classnames({
      "popout-picker": true,
      "active": this.state.active,
      "left": this.state.position === "left",
      "right": this.state.position === "right"
    });
    return (
      React.createElement(PointerEvents, {
        onPointerDown: this.touchStart, 
        onPointerMove: this.touchMove, 
        onPointerUp: this.touchEnd}, 
        React.createElement("div", {className: cn}, 
          React.createElement("div", {className: "track-hue", ref: "slider-hue", "data-component": 0}, 
            React.createElement("div", {className: "handle", ref: "handle-hue", style: handleHue}, 
              React.createElement("div", {className: "value", ref: "value-hue"}, display[0])
            )
          ), 
          React.createElement("div", {className: "track-sat", ref: "slider-sat", style: satBackground, "data-component": 1}, 
            React.createElement("div", {className: "handle", ref: "handle-sat", style: handleSat}, 
              React.createElement("div", {className: "value", ref: "value-sat"}, display[1])
            )
          )
        )
      )
    );
  }
});

module.exports = PopoutPicker;


}).call(this,require("react"))
},{"../mixins.coffee":339,"../store.coffee":346,"../utils.coffee":360,"./pointer-events.coffee":273,"./status-overlay.coffee":287,"classnames":1,"counterpart":3,"react":215}],275:[function(require,module,exports){
(function (React){
var Icon, PointerEvents, PopoutSlider, classnames, manager, translate;

Icon = require("./icon.coffee");

PointerEvents = require("./pointer-events.coffee");

manager = require("../manager.coffee");

translate = require("counterpart");

classnames = require("classnames");

PopoutSlider = React.createClass({displayName: "PopoutSlider",
  componentDidMount: function() {
    module.exports.connect = this.connect;
    return module.exports.disconnect = this.disconnect;
  },
  componentDidUpdate: function() {
    var sliderState;
    sliderState = manager.state.popoutSliders;
    sliderState[this.state.position] = this.state.active;
    return manager.set("popoutSliders", sliderState);
  },
  shouldComponentUpdate: function(nextProps, nextState) {
    return nextState.active || this.state.active;
  },
  connect: function(component) {
    var displayValue, timeout;
    this.slider = this.refs["slider"];
    this.handle = this.refs["handle"];
    this.background = this.refs["background"];
    this.display = this.refs["value"];
    displayValue = component.getMulValue();
    timeout = this.state.active ? 0 : 200;

    /* component example
    component = {
      saveState: () ->
      setValue: () ->
      reset: () ->
      getMulValue: ->
      state: {
        value: 1
      }
      props: {
        min: 0
        position: "left"
      }
    }
     */
    clearTimeout(this.connectTimeout);
    this.connectTimeout = setTimeout((function(_this) {
      return function() {
        _this.setState({
          active: true,
          displayValue: displayValue,
          value: component.state.value,
          modal: component.state.modal,
          min: component.props.min,
          max: component.props.max,
          position: component.props.position,
          parent: component,
          sliderHeight: _this.slider.clientHeight
        });
        return _this.update(component.state.value, displayValue);
      };
    })(this), timeout);
    return this;
  },
  disconnect: function() {
    clearTimeout(this.connectTimeout);
    return this.connectTimeout = setTimeout((function(_this) {
      return function() {
        return _this.setState({
          active: false
        });
      };
    })(this), 100);
  },
  saveState: function() {
    clearTimeout(this.saveStateTimeout);
    return this.saveStateTimeout = setTimeout((function(_this) {
      return function() {
        return _this.state.parent.saveState();
      };
    })(this), 500);
  },
  increment: function() {
    var value;
    value = (this.state.value * 100 + 1) / 100;
    this.state.parent.setValue(value);
    return this.saveState();
  },
  decrement: function() {
    var value;
    value = (this.state.value * 100 - 1) / 100;
    this.state.parent.setValue(value);
    return this.saveState();
  },
  apply: function() {
    var base;
    return typeof (base = this.state.parent).apply === "function" ? base.apply() : void 0;
  },
  cancel: function() {
    var base;
    return typeof (base = this.state.parent).cancel === "function" ? base.cancel() : void 0;
  },
  getPos: function(val) {
    if (val == null) {
      val = this.state.value;
    }
    return (val - this.state.min) / (this.state.max - this.state.min);
  },
  lerp: function(x, a, b) {
    if (a == null) {
      a = this.state.min;
    }
    if (b == null) {
      b = this.state.max;
    }
    return a * (1 - x) + b * x;
  },
  getStyle: function(value) {
    var offset, pos, transform;
    if (value == null) {
      value = this.state.value;
    }
    offset = this.getPos(0);
    pos = this.getPos(value);
    transform = "scaleY(" + (pos - offset) + ")";
    return {
      background: {
        WebkitTransform: transform,
        transform: transform,
        bottom: offset * 100 + "%",
        top: -offset * 100 + "%"
      },
      handle: {
        bottom: pos * 100 + "%"
      }
    };
  },
  update: function(value, displayValue) {
    var style;
    if (!this.state.active) {
      return;
    }
    style = this.getStyle(value);
    this.state.value = value;
    this.state.displayValue = displayValue;
    this.display.innerText = this.state.displayValue;
    this.handle.style.bottom = style.handle.bottom;
    this.background.style.webkitTransform = style.background.transform;
    this.background.style.transform = style.background.transform;
    this.background.style.bottom = style.background.bottom;
    return this.background.style.top = style.background.top;
  },
  getInitialState: function() {
    return {
      active: false,
      value: 0,
      min: -1
    };
  },
  doubleTap: function() {
    this.state.parent.reset();
    return clearTimeout(this.tapCountTimeout);
  },
  dragStart: function(e) {
    e.preventDefault();
    if (!this.state.modal) {
      clearTimeout(this.hideDisplay);
      this.display.style.opacity = 1;
      this.hideDisplay = setTimeout((function(_this) {
        return function() {
          return _this.display.style.opacity = 0;
        };
      })(this), 1000);
    }
    if (e.target.tagName === "I") {
      return false;
    }
    this.startPos = this.getPos();
    this.startVal = this.state.value;
    return this.sliderHeight = this.slider.clientHeight;
  },
  drag: function(e, input) {
    var offset, ref, snapPoint, value;
    e.preventDefault();
    clearTimeout(this.hideDisplay);
    this.display.style.opacity = 1;
    value = this.lerp(this.startPos - input.deltaY / this.sliderHeight);
    snapPoint = ((ref = manager.state.sliderSnap) != null ? ref[this.state.parent.props.prop] : void 0) || 0;
    offset = utils.clamp(this.startVal - snapPoint, -0.1, 0.1);
    value -= utils.clamp(value - snapPoint + offset, -0.1, 0.1) - offset;
    return this.state.parent.setValue(value);
  },
  dragEnd: function(e) {
    e.stopPropagation();
    e.preventDefault();
    this.state.parent.saveState();
    return this.display.style.opacity = 0;
  },
  render: function() {
    var bottomControl, cn, style, topControl;
    style = this.getStyle();
    cn = classnames({
      "popout-slider": true,
      "active": this.state.active,
      "left": this.state.position === "left",
      "right": this.state.position === "right",
      "extension": CONFIG.embedded
    });
    if (this.state.modal) {
      topControl = React.createElement(Icon, {name: "icon-check", onTap: this.apply});
      bottomControl = React.createElement(Icon, {name: "icon-remove", onTap: this.cancel});
    } else {
      topControl = React.createElement(Icon, {name: "icon-up", onTap: this.increment});
      bottomControl = React.createElement(Icon, {name: "icon-down", onTap: this.decrement});
    }
    return (
      React.createElement(PointerEvents, {
        onPointerDown: this.dragStart, 
        onPointerMove: this.drag, 
        onPointerUp: this.dragEnd, 
        onDoubleTap: this.doubleTap}, 
        React.createElement("div", {className: cn}, 
          topControl, 
          bottomControl, 
          React.createElement("div", {className: "track", ref: "slider"}, 
            React.createElement("div", {className: "background", ref: "background", style: style.background}), 
            React.createElement("div", {className: "handle", ref: "handle", style: style.handle}, 
              React.createElement("div", {className: "value", ref: "value"}, this.state.displayValue)
            )
          )
        )
      )
    );
  }
});

module.exports = PopoutSlider;


}).call(this,require("react"))
},{"../manager.coffee":338,"./icon.coffee":262,"./pointer-events.coffee":273,"classnames":1,"counterpart":3,"react":215}],276:[function(require,module,exports){
(function (React){
var $, Menu, PointerEvents, Portal, classnames, manager, mixins, utils;

Portal = require("./portal.coffee");

PointerEvents = require("./pointer-events.coffee");

mixins = require("../mixins.coffee");

utils = require("../utils.coffee");

manager = require("../manager.coffee");

$ = require("jquery");

classnames = require("classnames");

Menu = React.createClass({displayName: "Menu",
  mixins: [mixins.touchRipple],
  getInitialState: function() {
    return {
      position: 'left',
      left: null,
      top: null,
      right: null,
      bottom: null,
      active: false
    };
  },
  componentDidUpdate: function() {
    if (this.state.active) {
      return this.didShow();
    } else {
      return this.didHide();
    }
  },
  didShow: function() {
    var node;
    node = this.refs.menu;
    node.style.display = "block";
    this.hidden = false;
    requestAnimationFrame(function() {
      return node.style.opacity = 1;
    });
    return clearTimeout(this.hideTimeout);
  },
  didHide: function() {
    var menu, node;
    menu = this.refs.menu;
    if (!menu) {
      return;
    }
    node = menu;
    clearTimeout(this.hideTimeout);
    this.hideTimeout = setTimeout((function(_this) {
      return function() {
        node.style.display = "none";
        return _this.hidden = true;
      };
    })(this), 300);
    return node.style.opacity = 0;
  },
  show: function(options) {
    var box, isLeft, isTop, maxX, maxY, menu, offset, parentName, ref, state, target;
    target = options.target, offset = (ref = options.offset) != null ? ref : [0, 0];
    if (!target) {
      return;
    }
    if (!this.hidden && target === this.target) {
      return;
    }
    this.target = target;
    menu = this.refs.menu;
    box = target.getBoundingClientRect();
    state = {
      left: null,
      top: null,
      right: null,
      bottom: null,
      height: null
    };
    isTop = box.bottom < window.innerHeight / 2;
    isLeft = box.right < window.innerWidth / 2;
    maxY = window.innerHeight - menu.clientHeight - 8;
    maxX = window.innerWidth - menu.clientWidth - 8;
    if (isTop && isLeft) {
      state.position = "top left";
      state.top = box.top - offset[1];
      state.left = box.right + offset[0];
    } else if (isTop && !isLeft) {
      state.position = "top right";
      state.top = box.top - offset[1];
      state.right = window.innerWidth - box.left + offset[0];
    } else if (!isTop && isLeft) {
      state.position = "bottom left";
      state.bottom = window.innerHeight - box.bottom - offset[1];
      state.left = box.right + offset[0];
    } else if (!isTop && !isLeft) {
      state.position = "bottom right";
      state.bottom = window.innerHeight - box.bottom - offset[1];
      state.right = window.innerWidth - box.left + offset[0];
    }
    if (state.left != null) {
      state.left = utils.clamp(state.left, 8, maxX);
    }
    if (state.right != null) {
      state.right = utils.clamp(state.right, 8, maxX);
    }
    if (state.top != null) {
      state.top = utils.clamp(state.top, 8, maxY);
    }
    if (state.bottom != null) {
      state.bottom = utils.clamp(state.bottom, 8, maxY);
    }
    state.height = menu.scrollHeight - 1;
    this.setActiveTimeout = setTimeout((function(_this) {
      return function() {
        state.active = true;
        return _this.setState(state);
      };
    })(this), 1);
    clearTimeout(this.addListenersTimeout);
    this.addListenersTimeout = setTimeout(this.addListeners, 300);
    parentName = this._reactInternalInstance._currentElement._owner.getName();
    return manager.emit("show_menu", parentName);
  },
  hide: function(e) {
    var base;
    clearTimeout(this.setActiveTimeout);
    clearTimeout(this.addListenersTimeout);
    clearTimeout(this.hideTimeout);
    this.setState({
      active: false
    });
    this.refs.menu.style.height = "auto";
    this.removeListeners();
    this.touchRippleEnd();
    return typeof (base = this.props).onHide === "function" ? base.onHide() : void 0;
  },
  triggerMouseEvent: function(node, eventType) {
    var clickEvent;
    clickEvent = document.createEvent('MouseEvents');
    clickEvent.initEvent(eventType, true, true);
    return node.dispatchEvent(clickEvent);
  },
  cancelTouch: function() {
    document.removeEventListener("mouseup", this.touchEnd);
    document.removeEventListener("pointerup", this.touchEnd);
    return document.removeEventListener("touchend", this.touchEnd);
  },
  touchStart: function(e) {
    var inMenu;
    inMenu = $(e.target).closest(".popover-menu")[0];
    if (inMenu) {
      if (!this.props.persist) {
        document.addEventListener("mouseup", this.touchEnd);
        document.addEventListener("pointerup", this.touchEnd);
        return document.addEventListener("touchend", this.touchEnd);
      }
    } else {
      return this.hide(e);
    }
  },
  touchEnd: function(e) {
    e.preventDefault();
    this.hide(e);
    this.cancelTouch();
    if (e.type === "pointerup") {
      this.triggerMouseEvent(e.target, "mousedown");
      return this.triggerMouseEvent(e.target, "mouseup");
    }
  },
  addListeners: function() {
    document.addEventListener("mousedown", this.touchStart);
    document.addEventListener("pointerdown", this.touchStart);
    document.addEventListener("touchstart", this.touchStart);
    this.refs.menu.addEventListener("scroll", this.cancelTouch);
    return this.listenersBound = true;
  },
  removeListeners: function() {
    if (!this.listenersBound) {
      return;
    }
    document.removeEventListener("mousedown", this.touchStart);
    document.removeEventListener("pointerdown", this.touchStart);
    document.removeEventListener("touchstart", this.touchStart);
    this.refs.menu.removeEventListener("scroll", this.cancelTouch);
    return this.listenersBound = false;
  },
  touchHighlight: function(e) {
    this.startScroll = this.refs.menu.scrollTop;
    if (e.target.tagName === "A") {
      return this.touchRippleStart(e, e.target);
    }
  },
  touchMove: function(e, input) {
    if (this.props.rotated && (CONFIG.os === "android")) {
      e.preventDefault();
      return this.refs.menu.scrollTop = this.startScroll - input.deltaX;
    }
  },
  render: function() {
    var ID, className, classes, parentName, style;
    classes = {
      "popover-menu": true,
      "active": this.state.active,
      "transparent": CONFIG.enableBackgroundBlur
    };
    classes[this.state.position] = true;
    className = classnames(classes);
    style = {};
    style.maxHeight = window.innerHeight - 16 - this.state.top - this.state.bottom + "px";
    if (this.state.height != null) {
      style.height = this.state.height + 'px';
    }
    if (this.state.left != null) {
      style.left = this.state.left + 'px';
    }
    if (this.state.top != null) {
      style.top = this.state.top + 'px';
    }
    if (this.state.right != null) {
      style.right = this.state.right + 'px';
    }
    if (this.state.bottom != null) {
      style.bottom = this.state.bottom + 'px';
    }
    ID = this._reactInternalInstance._mountOrder;
    parentName = this._reactInternalInstance._currentElement._owner.getName();
    return (
      React.createElement(Portal, {className: "popover", portalId: parentName + "_menu_" + ID}, 
        React.createElement(PointerEvents, {onPointerDown: this.touchHighlight, onPointerMove: this.touchMove}, 
          React.createElement("div", {ref: "menu", className: className, style: style}, 
            this.props.children
          )
        )
      )
    );
  }
});

module.exports = Menu;


}).call(this,require("react"))
},{"../manager.coffee":338,"../mixins.coffee":339,"../utils.coffee":360,"./pointer-events.coffee":273,"./portal.coffee":277,"classnames":1,"jquery":319,"react":215}],277:[function(require,module,exports){
(function (React,ReactDOM){
var Portal;

Portal = React.createClass({displayName: "Portal",
  render: function() {
    return null;
  },
  portalElement: null,
  componentDidMount: function() {
    var p;
    p = this.props.portalId && document.getElementById(this.props.portalId);
    if (!p) {
      p = document.createElement('div');
      if (this.props.portalId != null) {
        p.id = this.props.portalId;
      }
      if (this.props.className != null) {
        p.className = this.props.className;
      }
      document.body.appendChild(p);
    }
    this.portalElement = p;
    return this.componentDidUpdate();
  },
  componentWillUnmount: function() {
    return document.body.removeChild(this.portalElement);
  },
  componentDidUpdate: function() {
    return ReactDOM.render(React.createElement("div", {className: this.props.className}, this.props.children), this.portalElement);
  }
});

module.exports = Portal;


}).call(this,require("react"),require("react-dom"))
},{"react":215,"react-dom":34}],278:[function(require,module,exports){
(function (React){
var Icon, Menu, PSColorPicker, PointerEvents, translate;

Menu = require("./popover-menu.coffee");

Icon = require("./icon.coffee");

PointerEvents = require("./pointer-events.coffee");

translate = require("counterpart");

module.exports = PSColorPicker = React.createClass({displayName: "PSColorPicker",
  mixins: [require("react-addons-pure-render-mixin")],
  getDefaultProps: function() {
    return {
      tabIndex: 1
    };
  },
  getInitialState: function() {
    return {
      hue: 0,
      sat: 0,
      val: 0,
      alpha: 1
    };
  },
  rgbToHsv: function(r, g, b) {
    var hsv;
    hsv = null;
    
      var max = Math.max(r, g, b), min = Math.min(r, g, b);
      var h, s, v = max;

      var d = max - min;
      s = max == 0 ? 0 : d / max;

      if (max == min) {
        h = 0; // achromatic
      } else {
        switch (max) {
          case r: h = (g - b) / d + (g < b ? 6 : 0); break;
          case g: h = (b - r) / d + 2; break;
          case b: h = (r - g) / d + 4; break;
        }

        h /= 6;
      }

      hsv = [ h, s, v ];
    ;
    return hsv;
  },
  hsvToRgb: function(h, s, v) {
    var rgb;
    rgb = null;
    
      var r, g, b;

      var i = Math.floor(h * 6);
      var f = h * 6 - i;
      var p = v * (1 - s);
      var q = v * (1 - f * s);
      var t = v * (1 - (1 - f) * s);

      switch (i % 6) {
        case 0: r = v, g = t, b = p; break;
        case 1: r = q, g = v, b = p; break;
        case 2: r = p, g = v, b = t; break;
        case 3: r = p, g = q, b = v; break;
        case 4: r = t, g = p, b = v; break;
        case 5: r = v, g = p, b = q; break;
      }

      rgb = [r, g, b];
    ;
    return rgb;
  },
  setColor: function() {
    var a, b, g, hsv, r, ref;
    ref = this.props.value, r = ref[0], g = ref[1], b = ref[2], a = ref[3];
    hsv = this.rgbToHsv(r, g, b);
    this.changed = false;
    return this.setState({
      hue: hsv[0],
      sat: hsv[1],
      val: hsv[2],
      alpha: a
    });
  },
  updateColor: function() {
    var hue, ref, rgb, sat, val;
    ref = this.state, hue = ref.hue, sat = ref.sat, val = ref.val;
    rgb = this.hsvToRgb(hue, sat, val);
    rgb.push(this.state.alpha);
    return this.props.onUpdate(rgb);
  },
  update: function(obj) {
    this.setState(obj);
    this.updateColor();
    return this.changed = true;
  },
  dragPickerStart: function(e) {
    this.width = e.target.clientWidth;
    return this.height = e.target.clientHeight;
  },
  dragPicker: function(e, input) {
    var deltaX, deltaY, ref, sat, val;
    deltaX = input.deltaX, deltaY = input.deltaY;
    if (this.props.rotated) {
      ref = [-deltaY, deltaX], deltaX = ref[0], deltaY = ref[1];
    }
    if (!input.dragging) {
      this.startState = utils.assign({}, this.state);
    }
    sat = this.startState.sat + deltaX / this.width;
    val = this.startState.val - deltaY / this.height;
    return this.update({
      sat: utils.clamp(sat, 0, 1),
      val: utils.clamp(val, 0, 1)
    });
  },
  dragHue: function(e, input) {
    var deltaX, deltaY, hue, ref;
    deltaY = input.deltaY, deltaX = input.deltaX;
    if (this.props.rotated) {
      ref = [-deltaY, deltaX], deltaX = ref[0], deltaY = ref[1];
    }
    if (!input.dragging) {
      this.startState = utils.assign({}, this.state);
    }
    hue = this.startState.hue - deltaY / this.height;
    return this.update({
      hue: utils.clamp(hue, 0, 1)
    });
  },
  dragEnd: function(e, input) {
    var base;
    if (input.dragging && this.changed) {
      return typeof (base = this.props).onUpdateEnd === "function" ? base.onUpdateEnd() : void 0;
    }
  },
  done: function() {
    return this.refs.menu.hide();
  },
  show: function(options) {
    this.setColor();
    return this.refs.menu.show(options);
  },
  render: function() {
    var controlPosition, huePosition, pickerBackground, tabIndex;
    tabIndex = this.props.tabIndex;
    pickerBackground = {
      backgroundColor: "hsl(" + (this.state.hue * 360) + ", 100%, 50%)"
    };
    controlPosition = {
      bottom: this.state.val * 100 + "%",
      left: this.state.sat * 100 + "%"
    };
    huePosition = {
      bottom: this.state.hue * 100 + "%"
    };
    return (
      React.createElement(Menu, {ref: "menu", persist: true}, 
        React.createElement("div", {className: "ps-colorpicker"}, 
          React.createElement("div", {className: "header"}, 
            translate("labels.color"), 
            React.createElement(Icon, {name: "icon-check", onTap: this.done})
          ), 
          React.createElement("div", {className: "controls"}, 
            React.createElement(PointerEvents, {
              multitouch: true, 
              onPointerDown: this.dragPickerStart, 
              onPointerMove: this.dragPicker, 
              onPointerUp: this.dragEnd}, 
              React.createElement("div", {className: "picker", style: pickerBackground}, 
                React.createElement("div", {className: "control", style: controlPosition})
              )
            ), 
            React.createElement(PointerEvents, {
              onPointerDown: this.dragPickerStart, 
              onPointerMove: this.dragHue, 
              onPointerUp: this.dragEnd}, 
              React.createElement("div", {className: "hue-slider"}, 
                React.createElement("div", {className: "control", style: huePosition})
              )
            )
          )
        )
      )
    );
  }
});


}).call(this,require("react"))
},{"./icon.coffee":262,"./pointer-events.coffee":273,"./popover-menu.coffee":276,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],279:[function(require,module,exports){
(function (React){
var Button, Dialog, PurchaseDialog, actions, manager, store, translate;

translate = require("counterpart");

Dialog = require("./dialog.coffee");

Button = require("./button.coffee");

store = require("../store.coffee");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

PurchaseDialog = React.createClass({displayName: "PurchaseDialog",
  mixins: [require("react-addons-pure-render-mixin")],
  componentWillReceiveProps: function(nextProps) {
    var active, dialog;
    active = nextProps.active;
    dialog = active ? "purchase" : this.state.dialog;
    if (typeof active === "string") {
      if (active === "restore") {
        this.restore(true);
      } else {
        dialog = active;
      }
    } else {
      this.setState({
        inProgress: false
      });
    }
    return this.setState({
      dialog: dialog
    });
  },
  getInitialState: function() {
    return {
      dialog: "purchase",
      inProgress: false
    };
  },
  hide: function() {
    return actions.setDialog("purchase", false);
  },
  openStore: function() {
    return actions.setDialog("store", true);
  },
  restore: function(restore) {
    var ref;
    this.setState({
      inProgress: true
    });
    return (ref = store.manager) != null ? ref.sendMessage("ppe.payment.purchase", {
      restore: true
    }, (function(_this) {
      return function(res) {
        var anyPurchased, dialog, purchase, purchases;
        _this.setState({
          inProgress: false
        });
        dialog = _this.state.dialog;
        if (res.success && !res.restore) {
          dialog = "purchase_successful";
        } else if (res.success && res.restore) {
          anyPurchased = false;
          for (purchase in res.purchases) {
            if (purchase) {
              anyPurchased = true;
            }
          }
          if (anyPurchased) {
            dialog = "restore_successful";
          } else {
            dialog = "no_purchases";
          }
        } else if (!res.success && res.restore) {
          dialog = "restore_failed";
        } else if (!res.success && res.error === "Not available") {
          dialog = "purchase_unavailable";
        } else if (!res.success && res.error === "Transaction failed") {
          _this.hide();
        }
        _this.setState({
          dialog: dialog
        });
        log("purchase attempt...");
        log(JSON.stringify(res));
        if (res.success) {
          purchases = actions.setPurchases(res.purchases);
          log("purchase successful!");
          log(JSON.stringify({
            purchases: purchases
          }));
          return manager.set({
            "purchases": purchases
          });
        }
      };
    })(this)) : void 0;
  },
  render: function() {
    var content, restoreButton, storeButton;
    storeButton = React.createElement(Button, {onTap: this.openStore, className: "primary"}, translate("dialogs.store"));
    restoreButton = React.createElement(Button, {onTap: this.restore}, translate("buttons.restore"));
    switch (this.state.dialog) {
      case "purchase":
        content = React.createElement("div", null, 
          React.createElement("h2", {dangerouslySetInnerHTML: {__html:translate("menus.restore_purchases")}}), 
          React.createElement("div", {className: "footer"}, 
            React.createElement(Button, {onTap: this.hide, content: "buttons.cancel"}), 
            restoreButton, 
            storeButton
          )
        );
        break;
      case "restore_failed":
        content = React.createElement("div", null, 
          React.createElement("h2", {dangerouslySetInnerHTML: {__html:translate("dialogs.restore_failed")}}), 
          React.createElement("div", {className: "footer"}, 
            React.createElement(Button, {onTap: this.hide, content: "buttons.close"}), 
            restoreButton, 
            storeButton
          )
        );
        break;
      case "offline":
        content = React.createElement("div", null, 
          React.createElement("h2", {dangerouslySetInnerHTML: {__html:translate("dialogs.offline")}}), 
          React.createElement("div", {className: "footer"}, 
            React.createElement(Button, {onTap: this.hide, content: "buttons.close"}), 
            restoreButton
          )
        );
        break;
      case "purchase_unavailable":
        content = React.createElement("div", null, 
          React.createElement("h2", {dangerouslySetInnerHTML: {__html:translate("dialogs.offline")}}), 
          React.createElement("div", {className: "footer"}, 
            React.createElement(Button, {onTap: this.hide, content: "buttons.close"}), 
            restoreButton
          )
        );
        break;
      case "no_purchases":
        content = React.createElement("div", null, 
          React.createElement("h2", {dangerouslySetInnerHTML: {__html:translate("dialogs." + this.state.dialog)}}), 
          React.createElement("div", {className: "footer"}, 
            React.createElement(Button, {onTap: this.hide, content: "buttons.close"}), 
            storeButton
          )
        );
        break;
      default:
        content = React.createElement("div", null, 
          React.createElement("h2", {dangerouslySetInnerHTML: {__html:translate("dialogs." + this.state.dialog)}}), 
          React.createElement("div", {className: "footer"}, 
            React.createElement(Button, {onTap: this.hide, content: "buttons.close"})
          )
        );
    }
    return (
      React.createElement(Dialog, React.__spread({},  this.props), 
        React.createElement("div", {className: this.state.inProgress ? "progress-overlay active" : "progress-overlay"}, 
          React.createElement("div", {className: "spinner"})
        ), 
        content
      )
    );
  }
});

module.exports = PurchaseDialog;


}).call(this,require("react"))
},{"../actions.coffee":229,"../manager.coffee":338,"../store.coffee":346,"./button.coffee":241,"./dialog.coffee":249,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],280:[function(require,module,exports){
(function (React){
var Button, Dialog, JPEG, QR, QRDialog, actions, manager, store, translate, utils;

translate = require("counterpart");

Dialog = require("./dialog.coffee");

Button = require("./button.coffee");

store = require("../store.coffee");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

utils = require("../utils.coffee");

QR = require("../qrcode.js");

JPEG = require("../../filtr/lib/jpeg_encoder");

QRDialog = React.createClass({displayName: "QRDialog",
  mixins: [require("react-addons-pure-render-mixin")],
  componentWillReceiveProps: function(nextProps) {
    var active, preset;
    active = nextProps.active;
    preset = active ? active : this.state.preset;
    return this.setState({
      preset: preset
    });
  },
  componentDidUpdate: function(prevProps) {
    if (this.props.active && this.props.active !== prevProps.active) {
      return this.generateQRCode();
    }
  },
  getInitialState: function() {
    return {
      progress: false,
      preset: {},
      imageData: null
    };
  },
  generateQRCode: function() {
    var canvas;
    this.setState({
      progress: true
    });
    canvas = this.refs.canvas;
    return actions.exportFilter(this.state.preset, (function(_this) {
      return function(res) {
        return QR.create(res.data.filter_url, _this.state.preset, function(imageData) {
          var ctx;
          canvas.width = imageData.width;
          canvas.height = imageData.height;
          ctx = canvas.getContext("2d");
          ctx.putImageData(imageData, 0, 0);
          return _this.setState({
            progress: false,
            imageData: imageData
          });
        });
      };
    })(this));
  },
  saveQRCode: function() {
    return this.exportQRCode("export");
  },
  shareQRCode: function() {
    return this.exportQRCode("share");
  },
  exportQRCode: function(type) {
    var blob, error, filename, imageData, load, output, success;
    this.setState({
      progress: true
    });
    imageData = this.state.imageData;
    output = JPEG(imageData, 95);
    blob = new Blob([output.data], {
      type: "image/jpeg"
    });
    success = (function(_this) {
      return function() {
        _this.hide();
        utils.trackEventGA("filter", "export_qr", type);
        if (type === "export") {
          if (CONFIG.platform === "mobile") {
            return actions.showCover(translate("status.filter_exported"), 2000);
          } else {
            return actions.showCover(translate("status.filter_saved"), 2000);
          }
        }
      };
    })(this);
    error = (function(_this) {
      return function() {
        actions.hideCover();
        return _this.setState({
          progress: false
        });
      };
    })(this);
    if (CONFIG.platform !== "mobile") {
      filename = decodeURIComponent(this.state.preset.name) + ".jpg";
      return actions.filesystem.choosePath({
        title: translate("buttons.save"),
        filename: filename
      }, function(entry) {
        if (entry.canceled) {
          return error();
        } else {
          return actions.filesystem.save(blob, {
            filename: filename,
            entry: entry,
            success: success,
            error: error
          });
        }
      });
    } else {
      load = function() {
        var message;
        message = {
          type: type,
          width: imageData.width,
          height: imageData.height
        };
        return store.manager.sendMessage("ppe.export", message, function(res) {
          log(res);
          if (res.success) {
            return success();
          } else {
            return error();
          }
        });
      };
      return actions.filesystem.sendBlob(blob, {
        success: load,
        error: error
      });
    }
  },
  hide: function() {
    this.setState({
      progress: false
    });
    return actions.setDialog("qr_code", false);
  },
  render: function() {
    var shareButton;
    if (CONFIG.platform === "mobile" && CONFIG.enableShare) {
      shareButton = 
        React.createElement("div", null, 
          React.createElement(Button, {content: "menus.share", onTap: this.shareQRCode, className: "secondary"}), 
          React.createElement("div", {className: "subtext", dangerouslySetInnerHTML: {__html:translate("dialogs.use_polarrfilter")}})
        )
      ;
    }
    return (
      React.createElement(Dialog, React.__spread({},  this.props, {showclose: true}), 
        React.createElement("div", {className: this.state.progress ? "progress-overlay active" : "progress-overlay"}, 
          React.createElement("div", {className: "spinner"})
        ), 
        React.createElement("div", {className: "qr-code"}, 
          React.createElement("canvas", {ref: "canvas"})
        ), 
        React.createElement("div", {className: "footer"}, 
          React.createElement(Button, {content: "buttons.save_qr", onTap: this.saveQRCode, className: "primary"}), 
          shareButton
        )
      )
    );
  }
});

module.exports = QRDialog;


}).call(this,require("react"))
},{"../../filtr/lib/jpeg_encoder":370,"../actions.coffee":229,"../manager.coffee":338,"../qrcode.js":341,"../store.coffee":346,"../utils.coffee":360,"./button.coffee":241,"./dialog.coffee":249,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],281:[function(require,module,exports){
(function (React){
var Circle, Handle, Icon, MaskOverlay, Overlay, ViewContainer, ViewportOverlay, actions, ref, translate, utils;

Icon = require("./icon.coffee");

ViewContainer = require("./view-container.coffee");

Overlay = require("./overlay.coffee");

ref = require("./viewport-overlay.coffee"), ViewportOverlay = ref.ViewportOverlay, Circle = ref.Circle, Handle = ref.Handle;

utils = require("../utils.coffee");

actions = require("../actions.coffee");

translate = require("counterpart");

MaskOverlay = React.createClass({displayName: "MaskOverlay",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      pan: false,
      x: 0,
      y: 0,
      width: 0,
      height: 0,
      angle: 0
    };
  },
  togglePan: function() {
    return this.setState({
      pan: !this.state.pan
    });
  },
  setPan: function(pan) {
    return this.setState({
      pan: pan
    });
  },
  componentDidMount: function() {
    return Filtr.on("history", (function(_this) {
      return function() {
        if (_this.props.active) {
          return _this.update();
        }
      };
    })(this));
  },
  componentDidUpdate: function(prevProps) {
    if (!prevProps.active && this.props.active) {
      return this.update();
    }
  },
  update: function() {
    var mask, state;
    state = Filtr.getState();
    mask = state.local_adjustments[this.props.index];
    if (!mask) {
      return;
    }
    return this.setState({
      x: mask.position[0],
      y: mask.position[1],
      width: mask.size[0],
      height: mask.size[1],
      angle: mask.angle || 0
    });
  },
  resetMask: function() {
    var adjustments, mask, newMask, state;
    state = Filtr.getState();
    adjustments = Filtr.utils.clone(state.local_adjustments);
    mask = adjustments[this.props.index];
    newMask = actions.createMask("radial");
    mask.angle = newMask.angle;
    mask.size = newMask.size;
    mask.position = newMask.position;
    Filtr.setState({
      local_adjustments: adjustments
    });
    return Filtr.saveState("buttons.reset tooltips.radial_filter");
  },
  onDoubleTap: function(e, input, component) {
    var mul, ref1;
    mul = (ref1 = component.props.data) != null ? ref1.mul : void 0;
    if (mul) {
      return this.resetMask();
    }
  },
  onDrag: function(e, input, component) {
    var action, angle, angleDelta, aspect, distance, height, min, mul, point, ref1, s1, s2, size, sizeDelta, width, x, y;
    mul = (ref1 = component.props.data) != null ? ref1.mul : void 0;
    if (!(mul || input.multitouch)) {
      return;
    }
    if (input.multitouch) {
      action = "pinch";
    } else if (mul[0] + mul[1] === 2) {
      action = "pan";
    } else {
      action = "resize";
    }
    point = Filtr.getMouseCoords(input.x, input.y);
    if (!input.dragging) {
      this.startPoint = point;
      this.startState = utils.assign({}, this.state);
      this.startAdjustments = Filtr.getState().local_adjustments;
      this.mask = this.startAdjustments[this.props.index];
      this.center = [this.mask.position[0] + 0.5, this.mask.position[1] + 0.5];
    }
    if (action === "pinch") {
      distance = input.distanceDelta;
      s1 = this.startState.width * distance;
      s2 = this.startState.height * distance;
      min = Math.min(s1, s2);
      width = Math.max(0.2 * (s1 / min), s1);
      height = Math.max(0.2 * (s2 / min), s2);
      this.mask.size = [width, height];
      this.setState({
        width: width,
        height: height
      });
      this.message = "tooltips.radial_filter labels.size";
    } else if (action === "pan") {
      x = this.startState.x + (point[0] - this.startPoint[0]);
      y = this.startState.y + (point[1] - this.startPoint[1]);
      this.mask.position = [x, y];
      this.setState({
        x: x,
        y: y
      });
      this.message = "tooltips.radial_filter labels.position";
    } else if (action === "resize") {
      x = point[0], y = point[1];
      if (mul[0]) {
        aspect = Filtr._.texture.width / Filtr._.texture.height;
        x = (x - this.center[0]) * aspect + this.center[0];
      } else {
        aspect = Filtr._.texture.height / Filtr._.texture.width;
        y = (y - this.center[1]) * aspect + this.center[1];
      }
      size = utils.distance(this.center, [x, y]) * 2.0;
      angle = Math.atan2(x - this.center[0], y - this.center[1]) * 180 / Math.PI;
      if (!input.dragging) {
        this.startSize = size;
        this.startAngle = angle;
      }
      angleDelta = angle - this.startAngle;
      sizeDelta = size - this.startSize;
      if (e.shiftKey) {
        distance = size / this.startSize;
        width = this.startState.width * distance;
        height = this.startState.height * distance;
      } else {
        width = this.startState.width + sizeDelta * mul[1];
        height = this.startState.height + sizeDelta * mul[0];
      }
      angle = this.startState.angle + angleDelta;
      this.mask.size = [width, height];
      this.mask.angle = angle;
      this.setState({
        width: width,
        height: height,
        angle: angle
      });
      this.message = "tooltips.radial_filter labels.size";
    }
    return Filtr.setState({
      local_adjustments: this.startAdjustments
    });
  },
  onDragEnd: function(e, input, component) {
    var mul, ref1;
    mul = (ref1 = component.props.data) != null ? ref1.mul : void 0;
    if (mul || input.multitouch) {
      return Filtr.saveState(this.message);
    }
  },
  render: function() {
    var props;
    props = {
      x: this.state.x + 0.5,
      y: 1 - this.state.y - 0.5,
      angle: this.state.angle,
      width: this.state.width,
      height: this.state.height,
      border: "1px solid white",
      data: {
        mul: [1, 1]
      },
      disabled: this.state.pan
    };
    return (
      React.createElement(Overlay, React.__spread({},  this.props, {disabled: this.state.pan, setDisabled: this.setPan}), 
        React.createElement(ViewportOverlay, {
          active: this.props.active, 
          onDoubleTap: this.onDoubleTap, 
          onDrag: this.onDrag, 
          onDragEnd: this.onDragEnd}, 
          React.createElement(Circle, React.__spread({},  props), 
            React.createElement(Handle, {fill: "white", position: [0.5, 0], data: {mul: [1, 0]}}), 
            React.createElement(Handle, {fill: "white", position: [1, 0.5], data: {mul: [0, 1]}})
          ), 
          React.createElement(Handle, {outline: "white", position: [props.x, props.y], disabled: this.state.pan, pointerEvents: "none"})
        ), 
        React.createElement(ViewContainer, {rotated: this.props.rotated}, 
          React.createElement(Icon, {name: "icon-move", active: this.state.pan, onTap: this.togglePan, tooltip: translate("tooltips.pan"), shortcut: "pan"})
        )
      )
    );
  }
});

module.exports = MaskOverlay;


}).call(this,require("react"))
},{"../actions.coffee":229,"../utils.coffee":360,"./icon.coffee":262,"./overlay.coffee":270,"./view-container.coffee":296,"./viewport-overlay.coffee":298,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],282:[function(require,module,exports){
(function (React){
var Button, Dialog, Review, actions, status, storage, store, translate, utils;

Dialog = require("./dialog.coffee");

Button = require("./button.coffee");

status = require("./status-overlay.coffee");

translate = require("counterpart");

store = require("../store.coffee");

storage = require("../storage.coffee");

utils = require("../utils.coffee");

actions = require("../actions.coffee");

Review = React.createClass({displayName: "Review",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      step: 0
    };
  },
  step: function(step) {
    this.setState({
      step: step
    });
    return storage.setItem('reviewed', true);
  },
  show: function() {
    this.setState({
      step: 0
    });
    return this.refs["dialog"].show();
  },
  hide: function() {
    return this.refs["dialog"].hide();
  },
  mailtoFeedback: function() {
    var os, subject;
    this.hide();
    os = utils.getPlatformOS();
    subject = "Polarr " + os + " Feedback";
    return window.location = "mailto:support@polarr.co?subject=" + subject;
  },
  render: function() {
    var content;
    switch (this.state.step) {
      case 0:
        content = React.createElement("div", null, 
          React.createElement("div", {className: "text"}, translate("dialogs.review")), 
          React.createElement("div", {className: "footer"}, 
              React.createElement(Button, {onTap: this.step.bind(this, 2), content: "buttons.not_really"}), 
              React.createElement(Button, {onTap: this.hide, content: "buttons.close"}), 
              React.createElement(Button, {onTap: this.step.bind(this, 1), className: "primary", content: "buttons.yes"})
          )
        );
        break;
      case 1:
        content = React.createElement("div", null, 
          React.createElement("div", {className: "text"}, translate("dialogs.review_yes")), 
          React.createElement("div", {className: "footer"}, 
            React.createElement(Button, {onTap: this.hide, content: "buttons.no_thanks"}), 
            React.createElement(Button, {onTap: actions.externalReview, className: "primary", content: "buttons.sure"})
          )
        );
        break;
      case 2:
        content = React.createElement("div", null, 
          React.createElement("div", {className: "text"}, translate("dialogs.review_no")), 
          React.createElement("div", {className: "footer"}, 
            React.createElement(Button, {onTap: this.hide, content: "buttons.no_thanks"}), 
            React.createElement(Button, {onTap: this.mailtoFeedback, className: "primary", content: "buttons.sure"})
          )
        );
    }
    return (
      React.createElement(Dialog, React.__spread({ref: "dialog"},  this.props), 
        content
      )
    );
  }
});

module.exports = Review;


}).call(this,require("react"))
},{"../actions.coffee":229,"../storage.coffee":343,"../store.coffee":346,"../utils.coffee":360,"./button.coffee":241,"./dialog.coffee":249,"./status-overlay.coffee":287,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],283:[function(require,module,exports){
(function (React,ReactDOM){
var $, Scrollable, classNames, manager;

manager = require("../manager.coffee");

$ = require('jquery');

classNames = require("classnames");

Scrollable = React.createClass({displayName: "Scrollable",
  componentDidMount: function() {
    this.node = ReactDOM.findDOMNode(this);
    return this.inPanel = !!$(this.node).closest(".panel")[0];
  },
  touchStart: function(e) {
    var base;
    if (typeof (base = this.props).onTouchStart === "function") {
      base.onTouchStart(e);
    }
    if (e.isPropagationStopped() || e.isDefaultPrevented()) {
      return;
    }
    this.startY = e.targetTouches[0].pageY;
    this.startX = e.targetTouches[0].pageX;
    this.startScroll = this.node.scrollTop;
    this.node.addEventListener("touchmove", this.touchMove);
    this.node.addEventListener("touchend", this.touchEnd);
    return this.node.addEventListener("touchcancel", this.touchEnd);
  },
  touchMove: function(e) {
    var delta;
    if (e.defaultPrevented) {
      return;
    }
    if (manager.state.portraitMode && (CONFIG.os === "android")) {
      e.preventDefault();
      if (this.inPanel) {
        delta = e.targetTouches[0].pageY - this.startY;
      } else {
        delta = this.startX - e.targetTouches[0].pageX;
      }
      return this.node.scrollTop = this.startScroll + delta;
    }
  },
  touchEnd: function() {
    this.node.removeEventListener("touchmove", this.touchMove);
    this.node.removeEventListener("touchend", this.touchEnd);
    return this.node.removeEventListener("touchcancel", this.touchEnd);
  },
  render: function() {
    var className;
    className = classNames("scrollable", this.props.className, {
      disabled: this.props.disabled
    });
    return (
      React.createElement("div", React.__spread({}, 
        this.props.props, 
        {style: this.props.style, 
        className: className, 
        onTouchStart: this.touchStart, 
        onMouseDown: this.props.onMouseDown, 
        onScroll: this.props.onScroll}), 
        this.props.children
      )
    );
  }
});

module.exports = Scrollable;


}).call(this,require("react"),require("react-dom"))
},{"../manager.coffee":338,"classnames":1,"jquery":319,"react":215,"react-dom":34}],284:[function(require,module,exports){
(function (React){
var Button, Dialog, LanguageMenu, Scrollable, Settings, actions, manager, store, translate, utils;

translate = require("counterpart");

Button = require("./button.coffee");

Dialog = require("./dialog.coffee");

LanguageMenu = require("./language-menu.coffee");

Scrollable = require("./scrollable.coffee");

manager = require("../manager.coffee");

utils = require("../utils.coffee");

actions = require("../actions.coffee");

store = require("../store.coffee");

Settings = React.createClass({displayName: "Settings",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return manager.state.settings;
  },
  componentDidMount: function() {
    manager.on("settings", (function(_this) {
      return function(settings) {
        return _this.setState(settings);
      };
    })(this));
    return manager.on("user", (function(_this) {
      return function(user) {
        return _this.setState({
          email: user.email
        });
      };
    })(this));
  },
  sendFeedback: function() {
    var os, subject;
    os = utils.getPlatformOS();
    subject = "Polarr " + os + " Feedback";
    return window.location = "mailto:support@polarr.co?subject=" + subject;
  },
  resetOnboarding: function() {
    return manager.set("onboarding_completed", 0);
  },
  resetPhotos: function() {
    if (store.manager != null) {
      return store.manager.sendMessage("ppe.settings.resetimages");
    } else {
      return store.removeAllExtra();
    }
  },
  showIntro: function() {
    manager.emit("show_intro");
    return this.hide();
  },
  openStore: function() {
    return actions.openStore();
  },
  openiOS: function() {
    return actions.openStore("ios");
  },
  openWindows: function() {
    return actions.openStore("windows");
  },
  openMac: function() {
    return actions.openStore("osx");
  },
  showLogin: function() {
    return actions.setDialog("user", {
      name: "signin"
    });
  },
  showSignup: function() {
    return actions.setDialog("user", {
      name: "signup"
    });
  },
  signout: function() {
    return actions.user.signout();
  },
  restorePurchases: function() {
    return actions.restorePurchases();
  },
  purchaseFull: function() {
    return actions.purchaseId(PURCHASE_KEYS[1]);
  },
  getUpdates: function() {
    return actions.setDialog("user", {
      name: "get_updates"
    });
  },
  limitExportResolution: function(limit) {
    var settings;
    settings = manager.state.settings;
    settings.limitExportSize = limit;
    return manager.set("settings", settings);
  },
  lockRotation: function(orientation) {
    var settings;
    settings = manager.state.settings;
    if (settings.orientation === orientation) {
      orientation = null;
    }
    settings.orientation = orientation;
    actions.setOrientation(orientation);
    return manager.set("settings", settings);
  },
  loadLastPhoto: function() {
    var settings;
    settings = manager.state.settings;
    settings.loadLastPhoto = !settings.loadLastPhoto;
    return manager.set("settings", settings);
  },
  showGuide: function() {
    manager.emit("tutorial_id", "adjustment_guide");
    return this.hide();
  },
  openYoutu: function() {
    return actions.openURL("http://www.polayoutu.com");
  },
  getPicky: function() {
    return utils.openURL("https://itunes.apple.com/us/app/picky-album-automatic-photo/id1156687266?ls=1&mt=8#");
  },
  goTo: function(link) {
    return actions.openURL(link);
  },
  hide: function() {
    return actions.setDialog("settings", false);
  },
  render: function() {
    var WechatPrompt, accountButton, loadLastButton, maxSize, maxSizes, moreApps, newAccountButton, proLabel, restorePurchases, rotationLock, rotationSettings, unlockButton, unlockNote, unlockText;
    maxSize = Filtr.getMaxTextureSize();
    maxSizes = [maxSize + " px", (maxSize * 2) + " px"];
    if (actions.user.isLoggedIn()) {
      accountButton = 
        React.createElement(Button, {onTap: this.signout}, translate("buttons.signout"), " ", React.createElement("span", {className: "label-right"}, this.state.email))
      ;
    } else {
      newAccountButton = 
        React.createElement(Button, {content: "buttons.signup", onTap: this.showSignup})
      ;
      accountButton = 
        React.createElement(Button, {content: "buttons.signin", onTap: this.showLogin})
      ;
    }
    if (utils.isChina()) {
      WechatPrompt = 
         React.createElement("div", null, 
          React.createElement("h3", null, "泼辣修图微信互动"), 
          React.createElement("p", null, " 扫描二维码，关注泼辣修图(polaxiutu)微信公众号，获取最新修图教程和软件更新和动态。"), 
          React.createElement("p", null, 
            React.createElement("div", {className: "wechat_qr"})
          ), 
          React.createElement("p", null, " 想要更多的人欣赏到你的照片？访问", React.createElement("a", {onTouchTap: this.openYoutu}, "泼辣有图"), "社区提交您的作品。")
         )
      ;
    }
    if (CONFIG.os === "ios") {
      restorePurchases = 
        React.createElement(Button, {content: "menus.restore_purchases", onTap: this.restorePurchases})
      ;
    }
    if (CONFIG.os === "android") {
      rotationLock = React.createElement("h3", null, translate("dialogs.lock_rotation"));
      rotationSettings = 
        React.createElement("div", {className: "setting-group"}, 
          React.createElement(Button, {active: this.state.orientation === "landscape", onTap: this.lockRotation.bind(this, 'landscape')}, "Landscape"), 
          React.createElement(Button, {active: this.state.orientation === "portrait", onTap: this.lockRotation.bind(this, 'portrait')}, "Portrait")
        )
      ;
    }
    if (actions.isProPurchased()) {
      proLabel = React.createElement("span", {className: "label"}, "Pro");
      unlockText = translate("dialogs.user_in_paid");
    } else {
      unlockButton = React.createElement(Button, {onTap: this.purchaseFull, content: "dialogs.unlock_all"});
      unlockText = translate("dialogs.user_in_unpaid");
    }
    unlockNote = React.createElement("p", null, unlockText);
    if (CONFIG.platform !== "mobile") {
      moreApps = 
        React.createElement("div", {className: "setting-group"}, 
          React.createElement(Button, {onTap: this.openiOS}, "Polarr for iOS", React.createElement("i", {className: "icon-forward right"}))
        )
      ;
      loadLastButton = React.createElement(Button, {active: this.state.loadLastPhoto, content: "buttons.load_last_photo", onTap: this.loadLastPhoto});
    } else {
      moreApps = 
        React.createElement("div", {className: "setting-group"}, 
          React.createElement(Button, {onTap: this.openMac}, "Polarr for Mac", React.createElement("i", {className: "icon-forward right"})), 
          React.createElement(Button, {onTap: this.openWindows}, "Polarr for Windows", React.createElement("i", {className: "icon-forward right"}))
        )
      ;
    }
    return (
      React.createElement(Dialog, React.__spread({ref: "dialog", fullscreen: true, nopad: true, showclose: true},  this.props), 
        React.createElement("div", {className: "header"}, 
          React.createElement("div", {className: "center"}, 
            React.createElement("h1", null, translate("tooltips.settings"))
          )
        ), 
        React.createElement(Scrollable, {rotated: this.props.rotated, className: "settings-dialog"}, 
          React.createElement("div", {className: "content"}, 
            React.createElement("p", null, translate("dialogs.get_picky")), 
            React.createElement("div", {className: "setting-group"}, 
              React.createElement(Button, {onTap: this.getPicky}, "Picky for iOS", React.createElement("i", {className: "icon-forward right"}))
            ), 

            React.createElement("h3", null, translate("dialogs.account"), " ", proLabel), 
            React.createElement("div", {className: "setting-group"}, 
              newAccountButton, 
              accountButton, 
              unlockButton
            ), 
            unlockNote, 

            React.createElement("h3", null, translate("menus.language")), 
            React.createElement("div", {className: "setting-group"}, 
              React.createElement(LanguageMenu, {rotated: this.props.rotated})
            ), 

            React.createElement("h3", null, "Get Polarr"), 
            moreApps, 

            WechatPrompt, 

            React.createElement("h3", null, translate("dialogs.max_export_size")), 
            React.createElement("div", {className: "setting-group"}, 
              React.createElement(Button, {active: actions.isExportSizeLimited(), onTap: this.limitExportResolution.bind(this, true)}, maxSizes[0]), 
              React.createElement(Button, {active: !actions.isExportSizeLimited(), onTap: this.limitExportResolution.bind(this, false)}, maxSizes[1])
            ), 

            React.createElement("h3", null, translate("dialogs.photos")), 
            React.createElement("div", {className: "setting-group"}, 
              React.createElement(Button, {content: "buttons.reset_photos", onTap: this.resetPhotos}), 
              loadLastButton
            ), 

            rotationLock, 
            rotationSettings, 

            React.createElement("h3", null, translate("dialogs.help")), 
            React.createElement("div", {className: "setting-group"}, 
              React.createElement(Button, {onTap: this.showIntro}, translate("menus.show_intro")), 
              React.createElement(Button, {onTap: this.showGuide}, translate("menus.show_instructions"))
            ), 

            React.createElement("div", {className: "setting-group"}, 
              React.createElement(Button, {content: "dialogs.send_feedback", onTap: this.sendFeedback}), 
              React.createElement(Button, {content: "dialogs.rate_review", onTap: actions.externalReview}), 
              React.createElement(Button, {content: "dialogs.get_updates", onTap: this.getUpdates})
            ), 

            React.createElement("div", {className: "setting-group"}, 
              restorePurchases
            ), 

            React.createElement("div", null, 
              React.createElement("p", {className: "center"}, 
                translate("dialogs.about_text")
              )
            ), 

            React.createElement("p", {className: "center"}, 
              React.createElement("i", {className: "icon-facebook", onTouchTap: this.goTo.bind(this, "https://www.facebook.com/polarrian/")}), 
              React.createElement("i", {className: "icon-twitter", onTouchTap: this.goTo.bind(this, "https://twitter.com/polarrist")}), 
              React.createElement("i", {className: "icon-instagram", onTouchTap: this.goTo.bind(this, "https://www.instagram.com/polarrist/")})
            )
          )
        )
      )
    );
  }
});

module.exports = Settings;


}).call(this,require("react"))
},{"../actions.coffee":229,"../manager.coffee":338,"../store.coffee":346,"../utils.coffee":360,"./button.coffee":241,"./dialog.coffee":249,"./language-menu.coffee":263,"./scrollable.coffee":283,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],285:[function(require,module,exports){
(function (React,ReactDOM){
var Filtr, PointerEvents, PopoutSlider, Slider, classnames, manager, mixins, store, translate, utils;

Filtr = require("../../filtr/api.coffee");

translate = require("counterpart");

classnames = require("classnames");

PopoutSlider = require("./popout-slider.coffee");

PointerEvents = require("./pointer-events.coffee");

mixins = require("../mixins.coffee");

utils = require("../utils.coffee");

store = require("../store.coffee");

utils = require("../utils.coffee");

manager = require("../manager.coffee");

Slider = React.createClass({displayName: "Slider",
  mixins: [mixins.pointer],
  componentDidMount: function() {
    return this.setStateFromProps(this.props);
  },
  componentWillReceiveProps: function(props) {
    return this.setStateFromProps(props);
  },
  componentWillUnmount: function() {
    return this.deactivatePad();
  },
  shouldComponentUpdate: function(nextProps, nextState) {
    return nextState.value !== this.state.value || nextState.active !== this.state.active || nextState.title !== this.state.title || nextProps.disabled !== this.props.disabled;
  },
  setStateFromProps: function(props) {
    var group, title;
    title = translate(props.label);
    group = props.group && !props.hideGroup ? translate(props.group) : "";
    return this.setState({
      title: title,
      group: group,
      value: props.value
    });
  },
  getInitialState: function() {
    return {
      active: false,
      value: 0
    };
  },
  getDefaultProps: function() {
    return {
      min: -1,
      max: 1,
      mul: 100,
      displayGroup: true
    };
  },
  getInitialState: function() {
    return {
      transition: true
    };
  },
  getDefaultValue: function() {
    if (this.props["default"] != null) {
      return this.props["default"];
    } else {
      return Filtr.getDefaultState()[this.props.prop];
    }
  },
  getMulValue: function(value) {
    var val;
    if (value == null) {
      value = this.state.value;
    }
    if (this.props.mul < 100) {
      val = Math.round(value * this.props.mul * 100) / 100;
    } else {
      val = Math.round(value * this.props.mul);
    }
    return val;
  },
  getPos: function(val) {
    if (val == null) {
      val = this.state.value;
    }
    return (val - this.props.min) / (this.props.max - this.props.min);
  },
  getLabel: function() {
    var label;
    label = this.props.label;
    if (this.props.group) {
      label = this.props.group + " " + label;
    }
    return label;
  },
  lerp: function(x, a, b) {
    if (a == null) {
      a = this.props.min;
    }
    if (b == null) {
      b = this.props.max;
    }
    return a * (1 - x) + b * x;
  },
  reset: function() {
    this.setValue(this.getDefaultValue());
    return this.saveState();
  },
  update: function(state) {
    return this.props.onUpdate(state);
  },
  setValue: function(val) {
    var key, obj, ref, setting;
    if (this.props.disabled) {
      return;
    }
    val = Math.max(this.props.min, Math.min(this.props.max, val || 0));
    obj = {};
    obj[this.props.prop] = val;
    if (this.props.tempSettings) {
      ref = this.props.tempSettings;
      for (key in ref) {
        setting = ref[key];
        obj[key] = setting;
      }
    }
    this.update(obj);
    return this.setState({
      value: val
    });
  },
  saveState: function() {
    var base, defaults, key, settings;
    if (this.props.tempSettings) {
      settings = {};
      defaults = Filtr.getDefaultState();
      for (key in this.props.tempSettings) {
        settings[key] = defaults[key];
      }
      this.update(settings);
    }
    if (typeof (base = this.props).onUpdateEnd === "function") {
      base.onUpdateEnd();
    }
    if (this.props.saveState === false) {
      return;
    }
    if (this.prevValue !== this.state.value) {
      Filtr.saveState(this.getLabel(), this.getMulValue());
      return this.prevValue = this.state.value;
    }
  },
  doubleTap: function() {
    return this.reset();
  },
  longPress: function() {
    this.started = true;
    return ReactDOM.findDOMNode(this).focus();
  },
  touchStart: function(e) {
    var input, pageX, pageY, ref;
    input = this.getInput(e);
    pageX = input.pageX, pageY = input.pageY;
    if (this.props.rotated) {
      pageX = -pageX;
    }
    this.target = ReactDOM.findDOMNode(this);
    this.startX = pageX;
    this.startY = pageY;
    this.trackWidth = this.refs.area.clientWidth;
    this.prevValue = this.state.value;
    this.startPos = this.getPos();
    this.started = false;
    this.dragging = false;
    if (document.activeElement === this.target) {
      this.started = true;
    }
    if (this.started) {
      e.preventDefault();
    }
    return utils.snap.start({
      startValue: this.prevValue,
      snapPoint: ((ref = manager.state.sliderSnap) != null ? ref[this.props.prop] : void 0) || 0,
      threshold: 0.1
    });
  },
  touchMove: function(e, touch) {
    var delta, value;
    if (!this.started) {
      if (document.activeElement !== this.target) {
        document.activeElement.blur();
      }
    }
    if (!this.started) {
      return;
    }
    this.dragging = true;
    e.preventDefault();
    if (this.props.rotated) {
      delta = (-touch.deltaX - touch.deltaY) / 4;
    } else {
      delta = (touch.deltaX - touch.deltaY) / 4;
    }
    value = this.lerp(this.startPos + delta / this.trackWidth);
    value = utils.snap.value(value);
    return this.setValue(value);
  },
  touchEnd: function(e) {
    e.preventDefault();
    e.stopPropagation();
    if (!this.started) {
      this.activatePad();
    } else {
      this.saveState();
      if (!this.dragging) {
        this.target.blur();
      }
    }
    if (!this.connectedSlider) {
      this.started = false;
      return this.target.blur();
    }
  },
  touchCancel: function() {
    var ref;
    return (ref = this.target) != null ? ref.blur() : void 0;
  },
  activate: function() {
    var target;
    target = ReactDOM.findDOMNode(this);
    target.focus();
    target.addEventListener("blur", this.deactivatePad);
    this.connectedSlider = PopoutSlider.connect(this);
    return manager.emit("activate_control", this.props.prop);
  },
  activatePad: function() {
    var target;
    target = ReactDOM.findDOMNode(this);
    if (document.activeElement === target) {
      return this.deactivatePad();
    } else if (this.props.position) {
      return this.activate();
    }
  },
  deactivatePad: function() {
    var target;
    target = ReactDOM.findDOMNode(this);
    target.removeEventListener("blur", this.deactivatePad);
    PopoutSlider.disconnect();
    return this.connectedSlider = null;
  },
  render: function() {
    var displayValue, ghostPos, group, offset, pos, ref, sliderClass, transformGhost, transformTrack, transition, value, wrapperClass;
    value = this.state.value;
    displayValue = this.getMulValue();
    offset = this.getPos(0);
    pos = this.getPos();
    ghostPos = (pos - offset) * 2;
    if ((ref = this.connectedSlider) != null) {
      ref.update(value, displayValue);
    }
    transformTrack = "translateX(" + (pos * -100) + "%) scaleX(1.34)";
    transformGhost = "skew(45deg) translateX(" + ((ghostPos - (0.5 - offset)) * 100) + "%) scaleX(" + (-ghostPos) + ")";
    transition = "none";
    sliderClass = classnames({
      "slider": true
    });
    wrapperClass = classnames({
      "slider-wrapper": true,
      "disabled": this.props.disabled
    });
    if (this.props.displayGroup) {
      group = [this.state.group, React.createElement("br", null)];
    }
    return (
      React.createElement(PointerEvents, {
        longPressDelay: 200, 
        onPointerDown: this.touchStart, 
        onPointerMove: this.touchMove, 
        onPointerUp: this.touchEnd, 
        onPointerCancel: this.touchCancel, 
        onLongPress: this.longPress, 
        onDoubleTap: this.doubleTap}, 
        React.createElement("div", {className: wrapperClass, tabIndex: "-1"}, 
          React.createElement("div", {className: sliderClass, "data-name": this.props.prop}, 
            React.createElement("span", {className: "title"}, 
              group, 
              this.state.title
            ), 
            React.createElement("span", {className: "area", ref: "area"}, 
              React.createElement("div", {className: "track", 
                   ref: "track", 
                   style: {
                     left: "50%",
                     WebkitTransform: transformTrack,
                     transform: transformTrack,
                     transition: transition
                   }}
              ), 
              React.createElement("div", {className: "ghost", 
                   ref: "ghost", 
                   style: {
                     left: offset * 100 + "%",
                     WebkitTransform: transformGhost,
                     transform: transformGhost,
                     transition: transition
                   }}
              )
            ), 
            React.createElement("span", {className: "input"}, 
                displayValue
            )
          )
        )
      )
    );
  }
});

module.exports = Slider;


}).call(this,require("react"),require("react-dom"))
},{"../../filtr/api.coffee":362,"../manager.coffee":338,"../mixins.coffee":339,"../store.coffee":346,"../utils.coffee":360,"./pointer-events.coffee":273,"./popout-slider.coffee":275,"classnames":1,"counterpart":3,"react":215,"react-dom":34}],286:[function(require,module,exports){
(function (React,ReactDOM){
var PointerEvents, PopoutSlider, Slider, classnames, manager, mixins, store, translate, utils;

translate = require("counterpart");

classnames = require("classnames");

PopoutSlider = require("./popout-slider.coffee");

PointerEvents = require("./pointer-events.coffee");

mixins = require("../mixins.coffee");

utils = require("../utils.coffee");

store = require("../store.coffee");

utils = require("../utils.coffee");

manager = require("../manager.coffee");

Slider = React.createClass({displayName: "Slider",
  mixins: [mixins.pointer],
  componentDidMount: function() {
    var el;
    this.setStateFromProps(this.props);
    el = ReactDOM.findDOMNode(this);
    return el.addEventListener("keydown", this.onKeyDown);
  },
  componentWillUnmount: function() {
    var el;
    el = ReactDOM.findDOMNode(this);
    return el.removeEventListener("keydown", this.onKeyDown);
  },
  componentWillReceiveProps: function(props) {
    return this.setStateFromProps(props);
  },
  shouldComponentUpdate: function(nextProps, nextState) {
    return nextState.value !== this.state.value || nextState.active !== this.state.active || nextState.title !== this.state.title || nextProps.disabled !== this.props.disabled;
  },
  setStateFromProps: function(props) {
    var group, title;
    title = props.label ? translate(props.label) : "";
    group = props.group && !props.hideGroup ? translate(props.group) : "";
    return this.setState({
      title: title,
      group: group,
      value: props.value
    });
  },
  getInitialState: function() {
    return {
      active: false,
      value: 0
    };
  },
  getDefaultProps: function() {
    return {
      min: -1,
      max: 1,
      mul: 100
    };
  },
  getInitialState: function() {
    return {
      transition: true
    };
  },
  getDefaultValue: function() {
    if (this.props["default"] != null) {
      return this.props["default"];
    } else {
      return Filtr.getDefaultState()[this.props.prop];
    }
  },
  isChanged: function() {
    return this.state.value !== this.getDefaultValue();
  },
  getMulValue: function(value) {
    var val;
    if (value == null) {
      value = this.state.value;
    }
    if (this.props.mul < 100) {
      val = Math.round(value * this.props.mul * 100) / 100;
    } else {
      val = Math.round(value * this.props.mul);
    }
    return val;
  },
  getPos: function(val) {
    if (val == null) {
      val = this.state.value;
    }
    return (val - this.props.min) / (this.props.max - this.props.min);
  },
  getLabel: function() {
    var label;
    label = this.props.label;
    if (this.props.group) {
      label = this.props.group + " " + label;
    }
    return label;
  },
  lerp: function(x, a, b) {
    if (a == null) {
      a = this.props.min;
    }
    if (b == null) {
      b = this.props.max;
    }
    return a * (1 - x) + b * x;
  },
  reset: function() {
    this.setValue(this.getDefaultValue());
    return this.saveState();
  },
  update: function(state) {
    return this.props.onUpdate(state);
  },
  stepValue: function(step) {
    var val;
    step = step / 100;
    val = this.state.value + step;
    this.setValue(val);
    clearTimeout(this.stepValueTimeout);
    return this.stepValueTimeout = setTimeout(this.saveState, 300);
  },
  setValue: function(val) {
    var key, obj, ref, setting;
    if (this.props.disabled) {
      return;
    }
    val = Math.max(this.props.min, Math.min(this.props.max, val || 0));
    obj = {};
    obj[this.props.prop] = val;
    if (this.props.tempSettings) {
      ref = this.props.tempSettings;
      for (key in ref) {
        setting = ref[key];
        obj[key] = setting;
      }
    }
    this.update(obj);
    return this.setState({
      value: val
    });
  },
  saveState: function() {
    var base, defaults, key, settings;
    if (this.props.tempSettings) {
      settings = {};
      defaults = Filtr.getDefaultState();
      for (key in this.props.tempSettings) {
        settings[key] = defaults[key];
      }
      this.update(settings);
    }
    if (typeof (base = this.props).onUpdateEnd === "function") {
      base.onUpdateEnd();
    }
    if (this.props.saveState === false) {
      return;
    }
    if (this.prevValue !== this.state.value) {
      Filtr.saveState(this.getLabel(), this.getMulValue());
      return this.prevValue = this.state.value;
    }
  },
  onKeyDown: function(e) {
    var step;
    step = e.shiftKey ? 10 : 1;
    switch (e.keyCode) {
      case 37:
        return this.stepValue(-step);
      case 39:
        return this.stepValue(step);
    }
  },
  doubleTap: function() {
    return this.reset();
  },
  touchStart: function(e, input) {
    var ref, startVal;
    e.stopPropagation();
    this.target = ReactDOM.findDOMNode(this);
    this.sliderRect = this.target.getBoundingClientRect();
    this.prevValue = this.state.value;
    if (input.pointerType !== "touch") {
      this.startPos = (input.x - this.sliderRect.left) / this.sliderRect.width;
      startVal = this.lerp(this.startPos);
      this.setValue(startVal);
      this.activate();
    } else {
      this.startPos = this.getPos();
      startVal = this.prevValue;
    }
    utils.snap.start({
      startValue: startVal,
      snapPoint: ((ref = manager.state.sliderSnap) != null ? ref[this.props.prop] : void 0) || this.props.snapPoint || 0,
      threshold: (this.props.max - this.props.min) * 0.05
    });
  },
  touchMove: function(e, input) {
    var delta, value;
    e.preventDefault();
    if (!input.dragging) {
      this.activate();
    }
    if (this.props.rotated) {
      delta = -input.deltaX;
    } else {
      delta = input.deltaX;
    }
    value = this.lerp(this.startPos + delta / this.sliderRect.width);
    value = utils.snap.value(value);
    return this.setValue(value);
  },
  touchEnd: function(e, input) {
    if (!input.dragging) {
      this.activate();
    }
    e.preventDefault();
    e.stopPropagation();
    return this.saveState();
  },
  activate: function() {
    ReactDOM.findDOMNode(this).focus();
    return manager.emit("activate_control", this.props.prop);
  },
  render: function() {
    var control, displayValue, offset, pos, sliderClass, transformGhost, transformTrack, transition, wrapperClass;
    displayValue = this.getMulValue();
    offset = this.getPos(0);
    pos = this.getPos();
    transformTrack = "translateX(" + (pos * -100) + "%) scaleX(2)";
    transformGhost = "scaleX(" + (pos - offset) + ")";
    control = {
      position: "absolute",
      left: pos * 100 + "%"
    };
    transition = "none";
    sliderClass = classnames({
      "slider": true,
      "changed": this.isChanged()
    });
    wrapperClass = classnames({
      "slider-wrapper": true,
      "disabled": this.props.disabled
    });
    return (
      React.createElement(PointerEvents, {
        panX: true, 
        threshold: 0, 
        onPointerDown: this.touchStart, 
        onPointerMove: this.touchMove, 
        onPointerUp: this.touchEnd, 
        onDoubleTap: this.doubleTap}, 
        React.createElement("div", {className: wrapperClass, tabIndex: this.props.tabIndex || -1}, 
          React.createElement("div", {className: sliderClass, "data-name": this.props.prop}, 
            React.createElement("span", {className: "title"}, 
              this.state.title
            ), 
            React.createElement("span", {className: "area", ref: "area"}, 
              React.createElement("div", {className: "track", 
                   ref: "track", 
                   style: {
                     left: "50%",
                     WebkitTransform: transformTrack,
                     transform: transformTrack,
                     transition: transition
                   }}
              ), 
              React.createElement("div", {className: "ghost", 
                   ref: "ghost", 
                   style: {
                     left: offset * 100 + "%",
                     WebkitTransform: transformGhost,
                     transform: transformGhost,
                     transition: transition
                   }}
              ), 
              React.createElement("div", {className: "control-container"}, 
                React.createElement("div", {className: "control", style: control})
              )
            ), 
            React.createElement("span", {className: "input"}, 
                displayValue
            )
          )
        )
      )
    );
  }
});

module.exports = Slider;


}).call(this,require("react"),require("react-dom"))
},{"../manager.coffee":338,"../mixins.coffee":339,"../store.coffee":346,"../utils.coffee":360,"./pointer-events.coffee":273,"./popout-slider.coffee":275,"classnames":1,"counterpart":3,"react":215,"react-dom":34}],287:[function(require,module,exports){
(function (React){
var Button, Status, manager, translate;

translate = require("counterpart");

Button = require("./button.coffee");

manager = require("../manager.coffee");

Status = React.createClass({displayName: "Status",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      message: "",
      active: false
    };
  },
  componentDidMount: function() {
    return manager.on("status", (function(_this) {
      return function(state) {
        _this.setState(state);
        return _this.setTimeout(state.timeout);
      };
    })(this));
  },
  setTimeout: function(delay) {
    clearTimeout(this.hideTimeout);
    if (delay) {
      return this.hideTimeout = setTimeout((function(_this) {
        return function() {
          return _this.setState({
            active: false
          });
        };
      })(this), delay);
    }
  },
  render: function() {
    var style;
    style = {
      left: this.props.left + "px",
      right: this.props.right + "px"
    };
    return (
      React.createElement("div", {style: style, className: this.state.active ? "status-overlay active" : "status-overlay"}, 
        React.createElement("div", {className: "status", ref: "status"}, 
          React.createElement("span", {dangerouslySetInnerHTML: {__html:this.state.message}})
        )
      )
    );
  }
});

module.exports = Status;


}).call(this,require("react"))
},{"../manager.coffee":338,"./button.coffee":241,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],288:[function(require,module,exports){
(function (React){
var $, BeforeAfter, Button, CollectionDetail, Detail, DetailHeader, DetailSection, Dialog, FilterPreview, Icon, ItemBlock, PointerEvents, PurchaseDialog, ReactCSSTransitionGroup, Scrollable, Store, actions, classnames, helpers, manager, presets, proFeatures, proTools, store, translate, utils;

Button = require("./button.coffee");

Icon = require("./icon.coffee");

Dialog = require("./dialog.coffee");

PurchaseDialog = require("./purchase-dialog.coffee");

PointerEvents = require("./pointer-events.coffee");

Scrollable = require("./scrollable.coffee");

ReactCSSTransitionGroup = require('react-addons-css-transition-group');

store = require("../store.coffee");

utils = require("../utils.coffee");

translate = require("counterpart");

classnames = require("classnames");

$ = require("jquery");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

presets = require("../presets.js");

proTools = [
  {
    title: translate("tooltips.local_adjustments"),
    image: "local_adjustments.jpg",
    description: translate("dialogs.local_adjustments"),
    id: PURCHASE_KEYS_MAP.local_adjustments,
    sections: [
      {
        title: translate("tooltips.radial_filter"),
        figures: [
          {
            caption: "Demo",
            video: "radial_0.mp4",
            poster: "radial_1b.jpg"
          }, {
            caption: "Add mask",
            image: "radial_3a.jpg"
          }, {
            caption: "Blur",
            image: "radial_3b.jpg"
          }, {
            caption: "Set exposure",
            image: "radial_3c.jpg"
          }, {
            caption: "Feather",
            image: "radial_3d.jpg"
          }
        ]
      }, {
        title: translate("tooltips.gradient_filter"),
        figures: [
          {
            caption: "Demo",
            video: "gradient_0.mp4",
            poster: "gradient_1b.jpg"
          }, {
            caption: "Add mask",
            image: "gradient_3a.jpg"
          }, {
            caption: "Blur",
            image: "gradient_3b.jpg"
          }, {
            caption: "Colorize",
            image: "gradient_3c.jpg"
          }
        ]
      }, {
        title: translate("tooltips.color_filter"),
        figures: [
          {
            caption: "Demo",
            video: "color_0.mp4",
            poster: "color_1b.jpg"
          }, {
            caption: "Target color",
            image: "color_3a.jpg"
          }, {
            caption: "Change color",
            image: "color_3b.jpg"
          }, {
            caption: "Blur",
            image: "color_3c.jpg"
          }
        ]
      }, {
        title: translate("tooltips.brush_filter"),
        figures: [
          {
            caption: "Demo",
            video: "brush_0.mp4",
            poster: "brush_1a.jpg"
          }, {
            caption: "Brush area",
            image: "brush_2a.jpg"
          }, {
            caption: "Set exposure",
            image: "brush_2b.jpg"
          }, {
            caption: "Change color",
            image: "brush_2c.jpg"
          }
        ]
      }
    ]
  }, {
    title: translate("tooltips.text"),
    image: "text_tool.jpg",
    description: translate("dialogs.text_tool"),
    id: PURCHASE_KEYS_MAP.text_tool,
    sections: [
      {
        title: translate("labels.text"),
        description: translate("dialogs.text_tool_description"),
        figures: [
          {
            caption: "Add Text",
            image: "text_1a.jpg"
          }, {
            caption: "Text wrap",
            image: "text_1d.jpg"
          }, {
            caption: "Letter spacing",
            image: "text_1e.jpg"
          }, {
            caption: "Blending",
            image: "text_1f.jpg"
          }, {
            caption: "Font & color",
            image: "text_1g.jpg"
          }, {
            caption: "Invert",
            image: "text_1h.jpg"
          }
        ]
      }, {
        title: translate("labels.shapes_presets"),
        figures: [
          {
            caption: "Shapes",
            image: "text_1b.jpg"
          }, {
            caption: "Dividers",
            image: "text_1m.jpg"
          }, {
            caption: "Presets",
            image: "text_1c.jpg"
          }, {
            caption: "Invert",
            image: "text_1i.jpg"
          }, {
            caption: "Outline",
            image: "text_1j.jpg"
          }, {
            caption: "Blending",
            image: "text_1k.jpg"
          }, {
            caption: "Resize",
            image: "text_1l.jpg"
          }
        ]
      }
    ]
  }, {
    title: translate("labels.denoise"),
    image: "denoise.jpg",
    description: translate("dialogs.denoise"),
    id: PURCHASE_KEYS_MAP.denoise,
    viewers: [
      {
        before: "denoise_before_3.jpg",
        after: "denoise_after_3.jpg"
      }, {
        before: "denoise_before_0.jpg",
        after: "denoise_after_0.jpg"
      }, {
        before: "denoise_before_1.jpg",
        after: "denoise_after_1.jpg"
      }, {
        before: "denoise_before_2.jpg",
        after: "denoise_after_2.jpg"
      }
    ]
  }, {
    title: translate("dialogs.backup_filters_title"),
    image: "backup_filters.jpg",
    description: translate("dialogs.backup_filters"),
    id: PURCHASE_KEYS_MAP.backup_filters,
    directPurchase: true
  }
];

proFeatures = [
  {
    title: translate("dialogs.unlock_all"),
    gradient: "linear-gradient(45deg, rgb(156, 0, 195), rgb(0, 160, 208))",
    description: translate("dialogs.pro_all_filters_text"),
    id: PURCHASE_KEYS_MAP.everything,
    group: "all"
  }, {
    title: translate("dialogs.pro_features_all"),
    gradient: "linear-gradient(45deg, rgb(0, 255, 173), rgb(255, 59, 0))",
    description: translate("dialogs.purchase"),
    id: PURCHASE_KEYS_MAP.pro_features,
    group: "tools"
  }, {
    title: translate("dialogs.all_filters"),
    gradient: "linear-gradient(45deg, #008dc3, #00ff8a)",
    description: translate("dialogs.all_filters_text"),
    id: PURCHASE_KEYS_MAP.all_filters,
    group: "filters"
  }, {
    title: translate("buttons.whats_new"),
    id: "whats-new",
    info: true,
    render: function() {
      return (
        React.createElement("div", {className: "timeline"}, 
          React.createElement("div", {className: "heading"}, 
            "Every month, Polarr Team develops and brings you new tools, feature improvement, and design tweaks. Pro Tools / Panels will be sold separately. However, once you bought unlock all tools, you won't need to purchase new tools in the future."
          ), 
          React.createElement("div", {className: "note"}, 
            React.createElement("div", {className: "date"}, "Wed Nov 25 2016"), 
            React.createElement("div", {className: "update"}, React.createElement("div", {className: "version"}, "v3.2"), 
              "Introducing text editing panel and advanced text controls."
            )
          ), 
          React.createElement("div", {className: "note"}, 
            React.createElement("div", {className: "date"}, "Fri Sep 30 2016"), 
            React.createElement("div", {className: "update"}, React.createElement("div", {className: "version"}, "v3.1"), 
              "Improved denoise and dehaze tools."
            )
          ), 
          React.createElement("div", {className: "note"}, 
            React.createElement("div", {className: "date"}, "Thu Sep 15 2016"), 
            React.createElement("div", {className: "update"}, React.createElement("div", {className: "version"}, "v3.0"), 
              "Introducing face editing tools with automatic face recognition.")
          ), 
          React.createElement("div", {className: "note"}, 
            React.createElement("div", {className: "date"}, "Tue Aug 30 2016"), 
            React.createElement("div", {className: "update"}, React.createElement("div", {className: "version"}, "v2.9"), 
              "Step by step interactive guides to address common photo issues with more content added" + ' ' +
              "every month. New onboarding design."
            )
          ), 
          React.createElement("div", {className: "note"}, 
            React.createElement("div", {className: "date"}, "Sat Jun 25 2016"), 
            React.createElement("div", {className: "update"}, React.createElement("div", {className: "version"}, "v2.7"), 
              "Batch exporting support.")
          ), 
          React.createElement("div", {className: "note"}, 
            React.createElement("div", {className: "date"}, "Fri Jun 10 2016"), 
            React.createElement("div", {className: "update"}, React.createElement("div", {className: "version"}, "v2.6"), 
              "Brushing tool as a local adjustment and Apple Pencil support.")
          ), 
          React.createElement("div", {className: "note"}, 
            React.createElement("div", {className: "date"}, "Mon May 16 2016"), 
            React.createElement("div", {className: "update"}, React.createElement("div", {className: "version"}, "v2.5"), 
              "Full resolution export is now supported up to 40MP.")
          ), 
          React.createElement("div", {className: "note"}, 
            React.createElement("div", {className: "date"}, "Thu May 05 2016"), 
            React.createElement("div", {className: "update"}, React.createElement("div", {className: "version"}, "v2.4"), 
              "Added Unsplash filter collection, in collaboration with the Unsplash community." + ' ' +
              "Reduced app size to under 30MB."
            )
          ), 
          React.createElement("div", {className: "note"}, 
            React.createElement("div", {className: "date"}, "Fri Apr 22 2016"), 
            React.createElement("div", {className: "update"}, React.createElement("div", {className: "version"}, "v2.3"), 
              "Polarr now works with iOS Photos Extension.")
          ), 
          React.createElement("div", {className: "note"}, 
            React.createElement("div", {className: "date"}, "Sat Apr 16 2016"), 
            React.createElement("div", {className: "update"}, React.createElement("div", {className: "version"}, "v2.2"), 
              "Lens blur tools for local adjustments. Color swatches for color toning. Coordinates" + ' ' +
              "for curve tools. Reflectable gradient adjustments for tilt-shift effect."
            )
          ), 
          React.createElement("div", {className: "note"}, 
            React.createElement("div", {className: "date"}, "Mon Apr 04 2016"), 
            React.createElement("div", {className: "update"}, React.createElement("div", {className: "version"}, "v2.1"), 
              "New color toning tool. Color masking tool. Pixelate filter. Added ability to export" + ' ' +
              "and import filters as QR codes."
            )
          ), 
          React.createElement("div", {className: "note"}, 
            React.createElement("div", {className: "date"}, "Tue Mar 29 2016"), 
            React.createElement("div", {className: "update"}, React.createElement("div", {className: "version"}, "v2.0"), 
              "Polarr iOS 2.0 released with new portrait mode, rearrangeable UI tools, new vignette," + ' ' +
              "curves, cropping, and perspective tools. Users can now add multiple local adjustments and adjust the" + ' ' +
              "strength of each individual filters."
            )
          )
        )
      );
    }
  }
].concat(proTools);

helpers = {
  getBuyText: function(id, isFree) {
    var price, purchased;
    if (isFree) {
      return translate("labels.free");
    }
    purchased = actions.isPurchased(id);
    price = actions.getPrice(id);
    switch (false) {
      case !purchased:
        return translate("buttons.installed");
      case !(CONFIG.os === "android"):
        return translate("buttons.get");
      default:
        if (price) {
          return price;
        } else {
          return translate("buttons.buy");
        }
    }
  },
  translate: function(item) {
    var locale;
    locale = translate.getLocale();
    return item[locale] || item["en"] || item;
  }
};

Store = React.createClass({displayName: "Store",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      packs: presets,
      selectedPack: null,
      currentPack: null,
      purchases: {}
    };
  },
  backState: [],
  componentDidMount: function() {
    return manager.on("purchases", (function(_this) {
      return function(purchases) {
        return _this.setState({
          purchases: purchases
        });
      };
    })(this));
  },
  componentWillReceiveProps: function(nextProps) {
    var dialog;
    if (nextProps.active && !this.props.active) {
      dialog = nextProps.active;
      if (dialog.id != null) {
        this.selectItem(dialog.id);
      }
      this.setState({
        inProgress: false
      });
      return utils.trackEventGA("store", "open", dialog.from, dialog.id);
    }
  },
  componentDidUpdate: function(prevProps, prevState) {
    var list, opacity;
    opacity = this.props.active ? 1 : 0.9;
    list = this.refs["collection-list"].node;
    list.style.opacity = opacity * !this.state.selectedPack;
    if (!this.props.active) {
      return;
    }
    if (this.scrollTop != null) {
      clearTimeout(this.scrollTimeout);
      return this.scrollTimeout = setTimeout((function(_this) {
        return function() {
          _this.scrollTo(_this.scrollTop);
          return _this.scrollTop = null;
        };
      })(this), 300);
    }
  },
  scrollTo: function(top) {
    var ref, scrollable;
    scrollable = (ref = this.refs["collection-list"]) != null ? ref.node : void 0;
    if (!scrollable) {
      return;
    }
    if (typeof top === "object") {
      top = top.offsetTop - 50;
    }
    if (typeof this.cancelScroll === "function") {
      this.cancelScroll();
    }
    return this.cancelScroll = Filtr.utils.animate(scrollable.scrollTop, top, 0.3, function(val) {
      return scrollable.scrollTop = val[0];
    });
  },
  selectItem: function(e) {
    var id, item, scrollable;
    id = this.getId(e);
    if (id == null) {
      return;
    }
    item = this.state.packs.find(function(pack) {
      return pack.pack_id === id;
    });
    item = item || proFeatures.find(function(feature) {
      return feature.id === id;
    });
    if (id && !item) {
      this.scrollTop = 0;
      return;
    }
    if (item) {
      scrollable = this.refs["collection-list"];
      this.scrollTop = scrollable != null ? scrollable.node.querySelector("[data-id='" + id + "']") : void 0;
    }
    if (item.directPurchase) {
      this.purchaseItem(id);
      return;
    }
    this.backState.push({
      selectedPack: this.state.selectedPack,
      currentPack: this.state.currentPack
    });
    this.setState({
      selectedPack: id,
      currentPack: item || this.state.currentPack
    });
    if (item) {
      return utils.trackEventGA("store", "open_pack", id);
    }
  },
  purchaseItem: function(e) {
    var done, id, isPurchased, pack;
    id = this.getId(e);
    if (id == null) {
      return;
    }
    pack = this.state.packs.find(function(item) {
      return item.pack_id === id;
    });
    isPurchased = actions.isPurchased(id);
    if (pack) {
      done = (function(_this) {
        return function() {
          var index;
          index = _this.state.packs.indexOf(pack);
          index += 2;
          manager.set("filter_tab", index);
          return actions.showPanel("looks");
        };
      })(this);
      if (isPurchased) {
        done();
        this.hide();
        return;
      }
      if (pack.is_free) {
        actions.unlockPurchase(pack.pack_id);
        done();
        return;
      }
    } else if (isPurchased) {
      this.showFeature(id);
      this.hide();
      return;
    }
    this.setState({
      inProgress: true
    });
    return actions.purchaseId(id, (function(_this) {
      return function(res) {
        _this.setState({
          inProgress: false
        });
        if (res != null ? res.success : void 0) {
          return typeof done === "function" ? done() : void 0;
        }
      };
    })(this));
  },
  getId: function(e) {
    if (typeof e === "string") {
      return e;
    } else if (e && e.target) {
      e.stopPropagation();
      return $(e.target).closest("[data-id]").data("id");
    } else {

    }
  },
  showFeature: function(id) {
    switch (id) {
      case PURCHASE_KEYS_MAP.local_adjustments:
        actions.showPanel("local-adjustments");
        break;
      case PURCHASE_KEYS_MAP.text_tool:
        actions.showPanel("text");
        break;
      case PURCHASE_KEYS_MAP.denoise:
        actions.showPanel("adjustments");
        manager.set("adjustment_tab", 3);
    }
    return this.hide();
  },
  showNew: function() {
    return this.selectItem("whats-new");
  },
  goBack: function() {
    return this.setState(this.backState.pop());
  },
  flushBackState: function() {
    this.backState = [];
    return this.setState({
      selectedPack: null,
      currentPack: null
    });
  },
  onHide: function(name) {
    var base;
    this.flushBackState();
    return typeof (base = this.props).onHide === "function" ? base.onHide(name) : void 0;
  },
  hide: function() {
    return actions.setDialog("store", false);
  },
  renderItem: function(item) {
    if (actions.isPurchased(item.id)) {
      return null;
    }
    return (
      React.createElement(ItemBlock, {
        id: item.id, 
        gradient: item.gradient, 
        title: helpers.translate(item.title), 
        description: helpers.translate(item.description), 
        button: helpers.getBuyText(item.id), 
        select: this.selectItem, 
        purchase: this.purchaseItem, 
        center: true, 
        short: true})
    );
  },
  render: function() {
    var allFiltersBlock, allToolsBlock, children, closeButtonClass, collectionDetail, collectionList, everythingBlock, featuredHeader, filterBlocks, listClass, newButtonClass, pack, proToolBlocks, progressClass;
    closeButtonClass = classnames("close-button", {
      hidden: this.state.selectedPack
    });
    newButtonClass = classnames("new-button", {
      hidden: this.state.selectedPack
    });
    progressClass = classnames("progress-overlay", {
      active: this.state.inProgress
    });
    listClass = classnames("collection-list", {
      active: !this.state.selectedPack
    });
    everythingBlock = this.renderItem(proFeatures[0]);
    allToolsBlock = this.renderItem(proFeatures[1]);
    allFiltersBlock = this.renderItem(proFeatures[2]);
    if (everythingBlock) {
      featuredHeader = 
        React.createElement("div", {className: "sticky-header"}, translate("dialogs.featured"))
      ;
    }
    filterBlocks = this.state.packs.map(function(pack, i) {
      if (pack.is_free == null) {
        return null;
      }
      return React.createElement(ItemBlock, {
        key: i, 
        id: pack.pack_id, 
        image: pack.preview_images[0], 
        isFree: pack.is_free, 
        title: helpers.translate(pack.pack_name), 
        description: helpers.translate(pack.pack_description), 
        button: helpers.getBuyText(pack.pack_id, pack.is_free), 
        select: this.selectItem, 
        purchase: this.purchaseItem});
    }, this);
    proToolBlocks = proTools.map(function(tool, i) {
      return React.createElement(ItemBlock, {
        key: i, 
        id: tool.id, 
        image: tool.image, 
        title: helpers.translate(tool.title), 
        description: helpers.translate(tool.description), 
        button: helpers.getBuyText(tool.id), 
        select: this.selectItem, 
        purchase: this.purchaseItem});
    }, this);
    if (this.state.selectedPack) {
      pack = this.state.currentPack;
      if (pack.filters) {
        collectionDetail = 
          React.createElement("div", {className: "store-page", key: "filters"}, 
            React.createElement(CollectionDetail, {
              ref: "collection-detail", 
              collection: pack, 
              active: this.state.selectedPack, 
              back: this.goBack, 
              purchase: this.purchaseItem, 
              rotated: this.props.rotated})
          )
        ;
      } else if (pack.group) {
        children = (function() {
          switch (pack.group) {
            case "all":
              return [allToolsBlock, allFiltersBlock];
            case "tools":
              return proToolBlocks;
            case "filters":
              return filterBlocks;
            default:
              return null;
          }
        })();
        collectionDetail = 
          React.createElement("div", {className: "store-page", key: pack.id}, 
            React.createElement(Detail, React.__spread({}, 
              pack, 
              {ref: "collection-detail", 
              active: this.state.selectedPack, 
              back: this.goBack, 
              purchase: this.purchaseItem, 
              rotated: this.props.rotated}), 
              children
            )
          )
        ;
      } else {
        collectionDetail = 
          React.createElement("div", {className: "store-page", key: pack.id}, 
            React.createElement(Detail, React.__spread({}, 
              pack, 
              {ref: "collection-detail", 
              active: this.state.selectedPack, 
              back: this.goBack, 
              purchase: this.purchaseItem, 
              showFeature: this.showFeature, 
              rotated: this.props.rotated})
            )
          )
        ;
      }
    }
    collectionList = 
      React.createElement(Scrollable, {className: listClass, ref: "collection-list", rotated: this.props.rotated}, 
        featuredHeader, 
        everythingBlock, 

        React.createElement("div", {className: "sticky-header"}, translate("dialogs.pro_features")), 
        allToolsBlock, 
        proToolBlocks, 

        React.createElement("div", {className: "sticky-header"}, translate("dialogs.filter_collections")), 
        allFiltersBlock, 
        filterBlocks
      )
    ;
    return (
      React.createElement(Dialog, React.__spread({ref: "dialog", fullscreen: true, nopad: true},  this.props, {onShow: this.show, onHide: this.onHide}), 
        React.createElement("div", {className: progressClass}, 
          React.createElement("div", {className: "spinner"})
        ), 
        React.createElement(Button, {className: closeButtonClass, onTap: this.hide}, React.createElement("i", {className: "icon-remove"})), 
        React.createElement(Button, {className: newButtonClass, onTap: this.showNew, content: "buttons.whats_new"}), 
        collectionList, 
        React.createElement(ReactCSSTransitionGroup, {
          transitionEnterTimeout: 300, 
          transitionLeaveTimeout: 300, 
          transitionName: "page"}, 
          collectionDetail
        )
      )
    );
  }
});

CollectionDetail = React.createClass({displayName: "CollectionDetail",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      selectedImage: null,
      loading: true
    };
  },
  componentWillUnmount: function() {
    this.cancelPreviews();
    return this.popContext();
  },
  componentDidMount: function() {
    this.imageLoading = Promise.resolve();
    return this.setInitialImage();
  },
  componentDidUpdate: function(prevProps, prevState) {
    if (prevProps.collection !== this.props.collection) {
      return this.setInitialImage();
    } else if (this.state.selectedImage !== prevState.selectedImage) {
      return this.loadImage(this.state.selectedImage);
    }
  },
  setInitialImage: function() {
    var collection, image;
    collection = this.props.collection;
    if (!collection) {
      return;
    }
    if (collection.preview_images) {
      image = store.currentPhoto && "current" || collection.preview_images[0];
    }
    this.setState({
      selectedImage: image
    });
    return this.loadImage(image);
  },
  pushContext: function() {
    if (!this.tempContext) {
      return this.tempContext = Filtr._.pushContext();
    }
  },
  popContext: function() {
    if (this.tempContext) {
      Filtr._.popContext();
      return this.tempContext = null;
    }
  },
  loadImage: function(image) {
    if (!image) {
      return;
    }
    this.cancelPreviews();
    this.setState({
      loading: true
    });
    this.hidePreviews();
    return this.imageLoading.then((function(_this) {
      return function() {
        return _this.imageLoading = new Promise(function(resolve) {
          var sample;
          if (image === "current") {
            _this.popContext();
            _this.renderPreviews();
            log("loaded: current");
            return resolve();
          } else {
            sample = store.getSampleDir() + image;
            _this.pushContext();
            return Filtr["import"](sample, {
              done: function() {
                log("loaded: " + sample);
                resolve();
                return _this.renderPreviews();
              }
            });
          }
        });
      };
    })(this));
  },
  getPreview: function(id) {
    var ref;
    return (ref = this.refs.previews) != null ? ref.node.querySelector("[data-id='" + id + "']") : void 0;
  },
  hidePreviews: function() {
    var j, len, node, preview, previews, ref, results;
    node = (ref = this.refs.previews) != null ? ref.node : void 0;
    if (!node) {
      return;
    }
    previews = node.querySelectorAll(".preview");
    results = [];
    for (j = 0, len = previews.length; j < len; j++) {
      preview = previews[j];
      results.push(preview.style.opacity = 0);
    }
    return results;
  },
  cancelPreviews: function() {
    return this.previewsCanceled = true;
  },
  renderPreviews: utils.debounce(function(done) {
    var container, currentState, filters, i, maxHeight, maxWidth, pack, render;
    this.previewsCanceled = false;
    pack = this.props.collection;
    if (!pack) {
      return;
    }
    this.setState({
      loading: false
    });
    filters = pack.filters;
    container = this.refs.previews.node;
    maxWidth = container.clientWidth / 2;
    maxHeight = container.clientHeight;
    currentState = this.state.selectedImage === "current" ? Filtr.getState() : Filtr.getDefaultState();
    i = 0;
    render = (function(_this) {
      return function(filter) {
        var canvas, output;
        if (!filter || _this.previewsCanceled) {
          if (typeof done === "function") {
            done();
          }
          return;
        }
        output = actions.renderThumbnail(filter.state, {
          resize: "fit",
          width: maxWidth,
          height: maxHeight,
          currentState: currentState
        });
        canvas = _this.getPreview(filter.id);
        canvas.width = output.width;
        canvas.height = output.height;
        utils.drawImageData(canvas, output);
        canvas.parentNode.style.opacity = 1;
        return requestAnimationFrame(function() {
          return render(filters[i++]);
        });
      };
    })(this);
    return render({
      id: "original",
      state: Filtr.getDefaultState()
    });
  }, 300),
  selectImage: function(image) {
    return this.setState({
      selectedImage: image
    });
  },
  renderTabButton: function(image, text, key) {
    return React.createElement(Button, {
      key: key, 
      disabled: image === "current" && !store.currentPhoto, 
      active: this.state.selectedImage === image, 
      onTap: this.selectImage.bind(this, image)}, 
      text
    );
  },
  purchase: function(id) {
    return this.props.purchase(id);
  },
  render: function() {
    var allPurchased, back, buttonClass, buttonText, collection, filters, loadingClass, purchaseAll, purchaseAllOption, purchaseThis, purchased, ref, rotated, tabButtons;
    ref = this.props, collection = ref.collection, back = ref.back, rotated = ref.rotated;
    purchased = actions.isPurchased(collection.pack_id);
    allPurchased = actions.isAllFiltersPurchased();
    purchaseThis = this.purchase.bind(this, collection.pack_id);
    purchaseAll = this.purchase.bind(this, proFeatures[0].id);
    buttonText = helpers.getBuyText(collection.pack_id, collection.is_free);
    buttonClass = classnames("buy-button", {
      purchased: purchased
    });
    loadingClass = classnames("previews-overlay", {
      active: this.state.loading
    });
    filters = collection.filters.map(function(filter, i) {
      return React.createElement(FilterPreview, {id: filter.id, name: filter.name, index: i, key: i});
    });
    tabButtons = collection.preview_images.map((function(_this) {
      return function(image, i) {
        return _this.renderTabButton(image, (translate("buttons.sample")) + " " + (1 + i), i);
      };
    })(this));
    if (!allPurchased) {
      purchaseAllOption = 
        React.createElement(Button, {className: "buy-button primary", onTap: purchaseAll}, 
          translate("dialogs.all_filters"), " ", 
          helpers.getBuyText("co.polarr.ppe.purchase.filters.all")
        )
      ;
    }
    return (
      React.createElement("div", {className: "collection-detail"}, 
        React.createElement(DetailHeader, {title: collection.pack_name, back: back}, 
          React.createElement(Button, {className: buttonClass, onTap: purchaseThis}, 
            buttonText
          ), 
          purchaseAllOption
        ), 

        React.createElement("div", {className: "collection-description"}, 
          React.createElement("p", null, helpers.translate(collection.pack_description)), 
          React.createElement("div", {className: "button-tabs"}, 
            this.renderTabButton("current", translate("buttons.current_image")), 
            tabButtons
          )
        ), 

        React.createElement("div", {className: loadingClass}, 
          React.createElement("div", {className: "spinner"})
        ), 

        React.createElement(Scrollable, {className: "collection-preview", ref: "previews", rotated: rotated}, 
          React.createElement(FilterPreview, {id: "original", name: translate("labels.none")}), 
          filters
        )
      )
    );
  }
});

Detail = React.createClass({displayName: "Detail",
  mixins: [require("react-addons-pure-render-mixin")],
  getDefaultProps: function() {
    return {
      sections: []
    };
  },
  render: function() {
    var buttonClass, buttonText, buyButton, description, props, purchase, purchased, sections, showFeature, tryButton, viewers;
    props = this.props;
    sections = props.sections || [];
    viewers = props.viewers || [];
    if (!props.info) {
      purchase = function() {
        return props.purchase(props.id);
      };
      purchased = actions.isPurchased(props.id);
      buttonText = helpers.getBuyText(props.id);
      buttonClass = classnames("buy-button", "primary", {
        purchased: purchased
      });
      buyButton = 
        React.createElement(Button, {className: buttonClass, onTap: purchase}, 
          buttonText
        )
      ;
      if (props.showFeature && !purchased) {
        showFeature = function() {
          return props.showFeature(props.id);
        };
        tryButton = 
          React.createElement(Button, {onTap: showFeature}, 
            translate("buttons.try")
          )
        ;
      }
    }
    if (props.description) {
      description = React.createElement("p", {dangerouslySetInnerHTML: {__html:props.description}});
    }
    return (
      React.createElement("div", {className: "collection-detail"}, 
        React.createElement(DetailHeader, {title: props.title, back: props.back}, 
          tryButton, 
          buyButton
        ), 

        React.createElement(Scrollable, {rotated: props.rotated, className: "detail-view"}, 
          description, 
          sections.map(function(section, i){
            return React.createElement(DetailSection, React.__spread({key: i, purchase: props.purchase},  section))
          }), 
          viewers.map(function(viewer, i){
            return React.createElement(BeforeAfter, React.__spread({key: i},  viewer, {rotated: props.rotated}))
          }), 
          props.render ? props.render() : props.children
        )
      )
    );
  }
});

FilterPreview = function(props) {
  var count;
  if (props.index != null) {
    count = React.createElement("span", {className: "count"}, props.index+1, ". ");
  }
  return (
    React.createElement("div", {className: "preview", key: props.id}, 
      React.createElement("div", {className: "filter-title"}, 
        count, 
        helpers.translate(props.name)
      ), 
      React.createElement("canvas", {"data-id": props.id, width: "0"})
    )
  );
};

ItemBlock = function(props) {
  var background, backgroundImage, className, purchased;
  purchased = actions.isPurchased(props.id);
  if (props.gradient) {
    backgroundImage = props.gradient;
  } else {
    backgroundImage = "url(./img/sample_images/" + props.image + ")";
  }
  background = {
    backgroundImage: backgroundImage
  };
  className = classnames("collection", {
    center: props.center,
    short: props.short
  });
  return (
    React.createElement("div", {
      className: className, 
      style: background, 
      "data-id": props.id, 
      onTouchTap: props.select}, 
      React.createElement("div", {className: "collection-title"}, 
        React.createElement("h1", null, props.title), 
        React.createElement("p", {dangerouslySetInnerHTML: {__html:props.description}}), 
        React.createElement(Button, {
          onTap: props.purchase, 
          className: purchased ? "price purchased" : "price"}, 
          props.button
        )
      )
    )
  );
};

DetailHeader = function(props) {
  return (
    React.createElement("div", {className: "header"}, 
      React.createElement("div", {className: "left"}, 
        React.createElement(Icon, {name: "icon-back", onTap: props.back})
      ), 
      React.createElement("div", {className: "center"}, 
        React.createElement("h1", null, helpers.translate(props.title))
      ), 
      React.createElement("div", {className: "right"}, 
        props.children
      )
    )
  );
};

DetailSection = function(props) {
  var button, description, playVideo, purchase, showGuide;
  purchase = function() {
    return props.purchase(props.id);
  };
  playVideo = function(e) {
    var ref, video;
    video = e.currentTarget;
    video.play();
    return (ref = video.parentElement.querySelector(".play-button")) != null ? ref.remove() : void 0;
  };
  if (props.description) {
    description = React.createElement("p", {dangerouslySetInnerHTML: {__html:props.description}});
  }
  if (props.guide) {
    showGuide = function() {
      return actions.showGuide(props.guide);
    };
    button = React.createElement(Button, {onTap: showGuide}, 
      translate("buttons.show_me"), 
      React.createElement("i", {className: "icon-play"})
    );
  }
  return (
    React.createElement("div", {className: "detail-section"}, 
      React.createElement("div", {className: "detail-header"}, 
        React.createElement("h1", null, props.title), 
        description, 
        React.createElement("div", {className: "right"}, 
          button
        )
      ), 
      React.createElement(Scrollable, {className: "horizontal"}, 
        props.figures.map(function(figure, i){
          var caption, image, play;
          if (figure.caption) {
            caption = React.createElement("div", {className: "caption"}, figure.caption)
          }
          if (figure.image) {
            image = React.createElement("img", {src: "img/sample_images/guides/" + figure.image})
          }
          else if (figure.video) {
            image = React.createElement("video", {
              muted: true, loop: true, playsInline: true, 
              onTouchTap: playVideo, 
              width: 150, 
              height: 150, 
              poster: "img/sample_images/guides/" + figure.poster}, 
              React.createElement("source", {src: "img/sample_images/guides/" + figure.video, type: "video/mp4"})
            )
            play = React.createElement("div", {className: "play-button"}, React.createElement("i", {className: "icon-play"}))
          }
          return React.createElement("div", {key: i, className: "figure"}, 
            caption, 
            image, 
            play
          )
        })
      )
    )
  );
};

BeforeAfter = React.createClass({displayName: "BeforeAfter",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      left: 0.5,
      right: 0.5
    };
  },
  onDrag: function(e, input) {
    var delta, left, right;
    if (!input.dragging) {
      this.width = this.refs.container.clientWidth;
      this.startLeft = this.state.left;
    }
    e.preventDefault();
    delta = this.props.rotated ? -input.deltaY : input.deltaX;
    left = this.startLeft + delta / this.width;
    left = Filtr.utils.clamp(left, 0, 1 - 1 / this.width);
    right = 1 - left;
    return this.setState({
      left: left,
      right: right
    });
  },
  render: function() {
    var after, before, pan;
    before = {
      backgroundImage: "url('img/sample_images/" + this.props.before + "')",
      backgroundPosition: "left",
      right: this.state.right * 100 + "%"
    };
    after = {
      backgroundImage: "url('img/sample_images/" + this.props.after + "')",
      backgroundPosition: "right",
      left: this.state.left * 100 + "%"
    };
    if (this.props.rotated) {
      pan = {
        panY: true
      };
    } else {
      pan = {
        panX: true
      };
    }
    return (
      React.createElement(PointerEvents, React.__spread({}, 
        pan, 
        {onPointerMove: this.onDrag}), 
        React.createElement("div", {className: "before-after", ref: "container"}, 
          React.createElement("div", {className: "before", style: before}, 
            React.createElement("div", {className: "label"}, translate("labels.before"))
          ), 
          React.createElement("div", {className: "after", style: after}, 
            React.createElement("div", {className: "label"}, translate("labels.after"))
          )
        )
      )
    );
  }
});

module.exports = Store;


}).call(this,require("react"))
},{"../actions.coffee":229,"../manager.coffee":338,"../presets.js":340,"../store.coffee":346,"../utils.coffee":360,"./button.coffee":241,"./dialog.coffee":249,"./icon.coffee":262,"./pointer-events.coffee":273,"./purchase-dialog.coffee":279,"./scrollable.coffee":283,"classnames":1,"counterpart":3,"jquery":319,"react":215,"react-addons-css-transition-group":31,"react-addons-pure-render-mixin":32}],289:[function(require,module,exports){
(function (React){
var PointerEvents, Swatches, manager, mixins;

PointerEvents = require("./pointer-events.coffee");

mixins = require("../mixins.coffee");

manager = require("../manager.coffee");

Swatches = React.createClass({displayName: "Swatches",
  mixins: [mixins.touchRipple],
  onPointerDown: function(e) {
    return this.touchRippleStart(e, e.target);
  },
  onTap: function(e) {
    var color;
    color = e.target.dataset.color;
    if (color != null) {
      return this.selectSwatch(color);
    }
  },
  selectSwatch: function(color) {
    var ref;
    if ((ref = document.activeElement) != null) {
      ref.blur();
    }
    return this.props.onSelect(color);
  },
  colorMap: {
    'red': 'hsl(0, 70%, 55%)',
    'orange': 'hsl(30, 70%, 55%)',
    'yellow': 'hsl(60, 70%, 55%)',
    'green': 'hsl(90, 70%, 50%)',
    'aqua': 'hsl(180, 70%, 45%)',
    'blue': 'hsl(240, 70%, 65%)',
    'purple': 'hsl(270, 70%, 60%)',
    'magenta': 'hsl(300, 70%, 55%)',
    'black': 'hsl(0, 0%, 0%)',
    'darkgray': 'hsl(0, 0%, 33%)',
    'lightgray': 'hsl(0, 0%, 66%)',
    'white': 'hsl(0, 0%, 100%)'
  },
  render: function() {
    var swatchOrder;
    if (manager.state.panelWidth < 100) {
      swatchOrder = ['red', 'magenta', 'orange', 'purple', 'yellow', 'blue', 'green', 'aqua'];
    } else {
      swatchOrder = ['red', 'orange', 'yellow', 'green', 'magenta', 'purple', 'blue', 'aqua'];
    }
    if (this.props.showShades) {
      swatchOrder = ['black', 'darkgray', 'lightgray', 'white'].concat(swatchOrder);
    }
    return (
      React.createElement(PointerEvents, {
        onPointerDown: this.onPointerDown, 
        onTap: this.onTap}, 
        React.createElement("div", {className: "swatches "+this.props.type, ref: "swatches"}, 
          swatchOrder.map(function(swatch){
            return React.createElement("div", {
              key: swatch, 
              className: this.props.activeSwatch === swatch ? "active" : "", 
              style: {backgroundColor: this.colorMap[swatch]}, 
              "data-color": swatch})
          }, this)
        )
      )
    );
  }
});

module.exports = Swatches;


}).call(this,require("react"))
},{"../manager.coffee":338,"../mixins.coffee":339,"./pointer-events.coffee":273,"react":215}],290:[function(require,module,exports){
(function (React){
var Edge, Handle, Icon, MaskOverlay, Menu, Mousetrap, Overlay, PointerEvents, TextBox, ViewContainer, ViewportOverlay, actions, keyboard, manager, ref, store, translate;

Icon = require("./icon.coffee");

ViewContainer = require("./view-container.coffee");

PointerEvents = require("./pointer-events.coffee");

Menu = require("./popover-menu.coffee");

Overlay = require("./overlay.coffee");

ref = require("./viewport-overlay.coffee"), ViewportOverlay = ref.ViewportOverlay, TextBox = ref.TextBox, Edge = ref.Edge, Handle = ref.Handle;

actions = require("../actions.coffee");

manager = require("../manager.coffee");

keyboard = require("../keyboard.coffee");

store = require("../store.coffee");

translate = require("counterpart");

Mousetrap = require("mousetrap");

MaskOverlay = React.createClass({displayName: "MaskOverlay",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      text: []
    };
  },
  componentDidMount: function() {
    var mousetrap;
    mousetrap = new Mousetrap(document);
    mousetrap.bind(keyboard.shortcuts["delete"], this.removeText);
    mousetrap.bind(keyboard.shortcuts.duplicate, this.duplicateText);
    return manager.on("keyboardHeight", (function(_this) {
      return function(height) {
        if (height && (_this.focused != null)) {
          return _this.hideUI(height);
        }
      };
    })(this));
  },
  componentWillReceiveProps: function(nextProps) {
    var text;
    text = nextProps.text;
    if (this.focused != null) {
      text[this.focused] = utils.assign(text[this.focused], {
        disabled: true
      });
    }
    return this.setState({
      text: text
    });
  },
  duplicateText: function(e) {
    if (!this.props.active) {
      return;
    }
    if (e != null) {
      e.preventDefault();
    }
    return this.props.duplicate();
  },
  removeText: function(e) {
    if (!this.props.active) {
      return;
    }
    if (e != null) {
      e.preventDefault();
    }
    return this.props.remove();
  },
  editText: function() {
    var component;
    component = this.refs[this.props.index];
    if (!component.props.data.editable) {
      return;
    }
    return component.focus();
  },
  saveState: function(text) {
    return this.props.update(text);
  },
  save: function(index, props) {
    var text;
    text = this.state.text.slice(0);
    text[index] = utils.assign(text[index], props);
    return this.saveState(text);
  },
  set: function(index, props) {
    var text;
    text = this.state.text.slice(0);
    text[index] = utils.assign(text[index], props);
    Filtr.setState({
      text: text
    });
    return this.setState({
      text: text
    });
  },
  hideUI: function(offset) {
    var bottom, left, ref1, right, top;
    if (CONFIG.platform !== "mobile") {
      return;
    }
    ref1 = Filtr._.margins, left = ref1[0], top = ref1[1], right = ref1[2], bottom = ref1[3];
    this.margins = Filtr._.margins.slice(0);
    Filtr.set({
      margins: [left, 0, right, offset],
      zoom: 0
    });
    return actions.hideUI();
  },
  showUI: function() {
    if (CONFIG.platform !== "mobile") {
      return;
    }
    if (this.margins != null) {
      Filtr.set({
        margins: this.margins,
        zoom: 0
      });
    }
    return actions.showUI();
  },
  changeText: function(e, component) {
    var id;
    id = component.props.data.id;
    return this.set(id, {
      text: e.target.value
    });
  },
  onFocus: function(e, component) {
    var id;
    id = component.props.data.id;
    this.set(id, {
      disabled: true
    });
    this.props.setIndex(id);
    return this.focused = id;
  },
  onBlur: function() {
    var index;
    index = this.focused;
    this.focused = null;
    this.save(index, {
      disabled: false
    });
    return setTimeout(this.showUI, 300);
  },
  getLayerId: function(input, component) {
    var data, i, id, j, layer, len, matrix, pixel, point, ref1, textLayer, x, y;
    data = component.props.data;
    if (data == null) {
      return;
    }
    id = data.id;
    if (data.mul) {
      return id;
    }
    ref1 = this.state.text;
    for (i = j = 0, len = ref1.length; j < len; i = ++j) {
      textLayer = ref1[i];
      layer = Filtr._.textLayers[i];
      matrix = Filtr._.matrix.multiply(layer.matrix).inverse();
      point = Filtr.getMouseCoords(input.x, input.y, false, matrix);
      point[1] = 1 - point[1];
      if (i === this.focused) {
        if (point[0] >= 0 && point[0] <= 1 && point[1] >= 0 && point[1] <= 1) {
          id = i;
          break;
        }
      }
      x = point[0] * layer.width >> 0;
      y = point[1] * layer.height >> 0;
      pixel = Filtr.readPixels(x, y, 1, 1, layer);
      if (pixel[3] > 0) {
        id = i;
      }
    }
    return id;
  },
  doubleTap: function(e, input, component) {
    var data;
    data = component.props.data;
    if (!data) {
      return;
    }
    return this.editText();
  },
  longPress: function(e, input, component) {
    var id;
    if ((this.focused != null) && CONFIG.platform === "mobile") {
      return;
    }
    id = this.getLayerId(input, component);
    if (id == null) {
      return;
    }
    this.props.setIndex(id);
    return this.refs.menu.show({
      target: e.target,
      offset: [8, 0]
    });
  },
  pointerDown: function(e, input, component) {
    var data, id;
    data = component.props.data;
    this.started = false;
    if (this.props.index != null) {
      input.stopPropagation();
    }
    id = this.getLayerId(input, component);
    if (id == null) {
      return;
    }
    if (this.focused != null) {
      if (data.mul != null) {
        e.preventDefault();
      }
      if (id !== this.focused) {
        document.activeElement.blur();
      }
    }
    this.props.setIndex(id);
    return input.stopPropagation();
  },
  pointerMove: function(e, input, component) {
    var data, id;
    data = component.props.data;
    if (data && this.focused === data.id && !data.mul) {
      return;
    }
    id = this.props.index;
    if (id == null) {
      return;
    }
    e.stopImmediatePropagation();
    return this.pointerDrag(id, input, data);
  },
  pointerDrag: function(id, input, data) {
    var delta, distance, height, point, position, ratio, ref1, ref2, scale, text, width, wrap, x, y;
    point = Filtr.getMouseCoords(input.x, input.y);
    point[1] = 1 - point[1];
    text = this.state.text[id];
    if (!input.dragging) {
      this.startPosition = text.position.slice(0);
      this.startScale = ((ref1 = text.scale) != null ? ref1.slice(0) : void 0) || [1, 1];
      this.startSize = Filtr.text.getSize(text);
      this.startWrap = text.wrap || this.startSize.offsetWidth;
      this.startPoint = point;
      this.started = true;
      utils.snap.start({
        startValue: this.startPosition[0],
        snapPoint: 0.5,
        threshold: 0.02
      }, {
        startValue: this.startPosition[1],
        snapPoint: 0.5,
        threshold: 0.02
      });
    }
    ref2 = Filtr._.texture, width = ref2.width, height = ref2.height;
    ratio = width / height;
    delta = [point[0] - this.startPoint[0], (point[1] - this.startPoint[1]) / ratio];
    if (input.multitouch) {
      distance = input.distanceDelta;
      scale = [Math.max(0, this.startScale[0] * distance), Math.max(0, this.startScale[1] * distance)];
      this.set(id, {
        scale: scale
      });
      return;
    }
    if (data && data.mul) {
      delta = Filtr.utils.rotateVector(delta, -(text.angle + text.orientation));
      if (text.type === "artwork") {
        scale = [Math.max(0, this.startScale[0] + delta[0] * (data.mul[0] * 2) / this.startSize.width), Math.max(0, this.startScale[1] + delta[1] * (data.mul[1] * 2) / this.startSize.height)];
        return this.set(id, {
          scale: scale
        });
      } else {
        wrap = this.startWrap + delta[0] * (data.mul[1] * 2);
        return this.set(id, {
          wrap: wrap
        });
      }
    } else {
      x = this.startPosition[0] + delta[0];
      y = this.startPosition[1] + delta[1];
      position = utils.snap.value(x, y);
      return this.set(id, {
        position: position
      });
    }
  },
  pointerUp: function(e, input, component) {
    var data;
    data = component.props.data;
    if (input.dragging && this.started) {
      return this.save(this.props.index, {
        disabled: this.focused != null
      });
    } else if ((data == null) && !input.dragging) {
      document.activeElement.blur();
      return this.props.setIndex(null);
    }
  },
  render: function() {
    var b, color, g, lum, r, ref1;
    if (this.props.invertedColor) {
      ref1 = this.props.invertedColor, r = ref1[0], g = ref1[1], b = ref1[2];
      lum = 0.298839 * r + 0.586811 * g + 0.11435 * b;
      color = lum < 0.5 ? "white" : "black";
    }
    return (
      React.createElement(Overlay, {rotated: this.props.rotated, active: this.props.active}, 
        React.createElement(Menu, {ref: "menu"}, 
          React.createElement("a", {onTouchTap: this.editText}, React.createElement("i", {className: "icon-edit-text"}), translate("labels.edit")), 
          React.createElement("a", {onTouchTap: this.removeText}, React.createElement("i", {className: "icon-remove"}), translate("buttons.delete"), keyboard.renderShortcut("delete")), 
          React.createElement("a", {onTouchTap: this.duplicateText}, React.createElement("i", {className: "icon-paste"}), translate("buttons.duplicate"), keyboard.renderShortcut("duplicate"))
        ), 
        React.createElement(ViewportOverlay, {
          passthrough: this.props.index == null, 
          active: this.props.active, 
          onLongPress: this.longPress, 
          onDoubleTap: this.doubleTap, 
          onPointerDown: this.pointerDown, 
          onPointerMove: this.pointerMove, 
          onPointerUp: this.pointerUp}, 
          this.state.text.map(function(text, i){
            var handles;
            if (text.type === "artwork") {
              handles = [
                React.createElement(Handle, {fill: "white", position: [0.5, 0], data: {id: i, mul: [0, -1]}}),
                React.createElement(Handle, {fill: "white", position: [1, 0.5], data: {id: i, mul: [1, 0]}}),
                React.createElement(Handle, {fill: "white", position: [0.5, 1], data: {id: i, mul: [0, 1]}}),
                React.createElement(Handle, {fill: "white", position: [0, 0.5], data: {id: i, mul: [-1, 0]}})
              ]
            } else {
              handles = [
                React.createElement(Edge, {fill: "white", side: "left", data: {id: i, mul: [0.5, -1]}}),
                React.createElement(Edge, {fill: "white", side: "right", data: {id: i, mul: [0.5, 1]}})
              ]
            }
            return React.createElement(TextBox, React.__spread({}, 
              text, 
              {ref: i, 
              key: i, 
              onChange: this.changeText, 
              onBlur: this.onBlur, 
              onFocus: this.onFocus, 
              color: text.disabled ? (color || text.color) : "transparent", 
              active: this.props.index === i, 
              border: "1px solid white", 
              data: {id: i, editable: text.type !== "artwork"}}), 
              handles
            )
          }, this)
        )
      )
    );
  }
});

module.exports = MaskOverlay;


}).call(this,require("react"))
},{"../actions.coffee":229,"../keyboard.coffee":313,"../manager.coffee":338,"../store.coffee":346,"./icon.coffee":262,"./overlay.coffee":270,"./pointer-events.coffee":273,"./popover-menu.coffee":276,"./view-container.coffee":296,"./viewport-overlay.coffee":298,"counterpart":3,"mousetrap":21,"react":215,"react-addons-pure-render-mixin":32}],291:[function(require,module,exports){
(function (React){
var AddList, BlendModeList, Button, ContainerGroup, FeatureBlock, FontList, Icon, List, Menu, PSColorPicker, Panel, PanelContainer, PointerEvents, Portal, PresetList, Scrollable, Slider, SliderPad, Text, TextOverlay, TextPanel, WebFont, actions, classNames, manager, mixins, ref, store, translate, utils,
  indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

Button = require("./button.coffee");

Icon = require("./icon.coffee");

Menu = require("./popover-menu.coffee");

Portal = require("./portal.coffee");

TextOverlay = require("./text-overlay.coffee");

FeatureBlock = require("./feature-block.coffee");

PointerEvents = require("./pointer-events.coffee");

Panel = require("./panel.coffee");

Slider = require("./slider.coffee");

SliderPad = require("./slider-pad.coffee");

Scrollable = require("./scrollable.coffee");

List = require("./list.coffee");

PSColorPicker = require("./ps-colorpicker.coffee");

ref = require("./panel-container.coffee"), ContainerGroup = ref.ContainerGroup, PanelContainer = ref.PanelContainer;

mixins = require("../mixins.coffee");

utils = require("../utils.coffee");

actions = require("../actions.coffee");

manager = require("../manager.coffee");

store = require("../store.coffee");

translate = require("counterpart");

classNames = require("classnames");

WebFont = require("../lib/webfont");

Text = require("../text.coffee");

TextPanel = React.createClass({displayName: "TextPanel",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      text: [],
      presets: [],
      artwork: [],
      category: {},
      customFonts: [],
      downloadedFonts: [],
      downloadableFonts: Text.downloadableFonts,
      systemFonts: Text.systemFonts,
      blendModes: Text.blendModes,
      defaults: Text.defaults,
      index: null,
      active: "add",
      inverted: false
    };
  },
  componentDidMount: function() {
    var families;
    Filtr.on("history", (function(_this) {
      return function() {
        var active, index, state, text;
        state = Filtr.getState();
        text = Filtr.utils.clone(state.text || []);
        active = _this.state.active;
        index = _this.state.index;
        if (!text.length) {
          index = null;
          active = "add";
        } else if (text.length < _this.state.text.length) {
          index = utils.clamp(index + 1, 0, text.length - 1);
          active = "options";
        } else if (text.length > _this.state.text.length) {
          index = text.length - 1;
          active = "options";
        }
        return _this.setState({
          text: text,
          index: index,
          active: active,
          inverted: state.textInverted,
          invertedColor: state.textInvertedColor
        });
      };
    })(this));
    families = Text.customFonts.concat(Text.artworkFonts);
    Text.restoreFonts((function(_this) {
      return function(downloaded) {
        var j, len, ref1, ref2, shouldUpdate, text;
        _this.setState({
          downloadedFonts: downloaded
        });
        shouldUpdate = false;
        ref1 = Filtr.getState().text;
        for (j = 0, len = ref1.length; j < len; j++) {
          text = ref1[j];
          if (ref2 = text.font_name, indexOf.call(downloaded, ref2) >= 0) {
            shouldUpdate = true;
          }
        }
        if (shouldUpdate) {
          return Filtr.text.forceUpdate();
        }
      };
    })(this));
    return WebFont.load({
      classes: false,
      custom: {
        families: families
      },
      active: (function(_this) {
        return function() {
          return _this.setState({
            customFonts: Text.customFonts,
            presets: Text.presets,
            artwork: Text.artwork
          });
        };
      })(this),
      fontactive: (function(_this) {
        return function(font) {
          var j, len, ref1, results, text;
          ref1 = Filtr.getState().text;
          results = [];
          for (j = 0, len = ref1.length; j < len; j++) {
            text = ref1[j];
            if (text.font_name === font) {
              results.push(Filtr.text.forceUpdate());
            } else {
              results.push(void 0);
            }
          }
          return results;
        };
      })(this)
    });
  },
  invertText: function() {
    var color, inverted, label;
    inverted = !this.state.inverted;
    label = inverted ? "yes" : "no";
    color = inverted ? this.state.text[this.state.index].color : this.state.invertedColor;
    Filtr.setState({
      textInverted: inverted,
      textInvertedColor: color
    });
    return Filtr.saveState("labels.invert labels.text", label);
  },
  outlineText: function() {
    var mode, outlined, text;
    text = this.state.text[this.state.index];
    outlined = text.mode === "outline";
    mode = outlined ? "" : "outline";
    this.update({
      mode: mode
    });
    return Filtr.saveState("labels.text labels.outline");
  },
  duplicateText: function() {
    var copy;
    if (this.state.index == null) {
      return;
    }
    copy = Filtr.utils.clone(this.state.text[this.state.index]);
    copy.position[1] += 0.01;
    return actions.addText([copy]);
  },
  addText: function() {
    var newText, text;
    if (this.state.index != null) {
      text = this.state.text[this.state.index];
      if (text.type === "artwork") {
        newText = utils.assign(this.state.defaults, {
          color: text.color
        });
      } else {
        newText = utils.assign(this.state.defaults, {
          align: text.align,
          font_name: text.font_name,
          font_family: text.font_family,
          font_weight: text.font_weight,
          font_style: text.font_style,
          font_size: text.font_size,
          color: text.color
        });
      }
    } else {
      newText = this.state.defaults;
    }
    return actions.addText([newText]);
  },
  removeText: function() {
    if (this.state.index == null) {
      return;
    }
    return actions.removeText(this.state.index);
  },
  setAlignment: function(alignment) {
    this.update({
      align: alignment
    });
    return Filtr.saveState("labels.text labels.alignment", utils.ucFirst(alignment));
  },
  alignLeft: function() {
    return this.setAlignment("left");
  },
  alignCenter: function() {
    return this.setAlignment("center");
  },
  alignRight: function() {
    return this.setAlignment("right");
  },
  alignJustify: function() {
    return this.setAlignment("justify");
  },
  showOptions: function() {
    return this.setState({
      active: "options"
    });
  },
  showFonts: function() {
    return this.setState({
      active: "fonts"
    });
  },
  showBlend: function() {
    return this.setState({
      active: "blendModes"
    });
  },
  showAdd: function() {
    return this.setState({
      active: "add"
    });
  },
  choosePresets: function(index) {
    var category;
    category = this.state.presets[index];
    return this.setState({
      active: "presets",
      category: category
    });
  },
  chooseArtwork: function(index) {
    var category;
    category = this.state.artwork[index];
    return this.setState({
      active: "presets",
      category: category
    });
  },
  setFont: function(type, key) {
    var defaults, error, font, success;
    if (type === 'downloadable') {
      if (!Text.fonts.downloadable[key].downloaded) {
        actions.showStatus((translate('status.downloading_font')) + " " + key + "...");
        success = (function(_this) {
          return function() {
            actions.hideStatus();
            return Text.loadFont(key, function() {
              return _this.setFont(type, key);
            });
          };
        })(this);
        error = (function(_this) {
          return function() {
            return actions.showStatus(translate("status.offline"), 2000);
          };
        })(this);
        Text.downloadFont(key, success, error);
        return;
      }
    }
    font = Text.fonts[type][key];
    defaults = this.state.defaults;
    this.update({
      font_name: key,
      font_family: font.fontFamily,
      font_weight: font.fontWeight || defaults.font_weight,
      font_style: font.fontStyle || defaults.font_style
    });
    return Filtr.saveState("labels.text labels.font");
  },
  setBlend: function(mode) {
    var index, name;
    index = Number(mode);
    name = this.state.blendModes[index];
    this.update({
      blend_mode: index
    });
    return Filtr.saveState("labels.text labels.blend_mode", name);
  },
  setPreset: function(index) {
    var presets;
    presets = this.state.active === "presets" ? this.state.category.presets : this.state.presets[0].presets;
    return actions.addText(presets[index].state);
  },
  update: function(state, setState) {
    var index, text;
    if (setState == null) {
      setState = true;
    }
    index = this.state.index;
    text = this.state.text.slice(0);
    text[index] = utils.assign(text[index], state);
    Filtr.setState({
      text: text
    });
    if (setState) {
      return this.setState({
        text: text
      });
    }
  },
  updateText: function(text) {
    Filtr.setState({
      text: text
    });
    return Filtr.saveState("buttons.update labels.text");
  },
  setIndex: function(index) {
    var active;
    active = index != null ? "options" : "add";
    return this.setState({
      index: index,
      active: active
    });
  },
  setColor: function(color) {
    if (this.state.inverted) {
      return Filtr.setState({
        textInvertedColor: color
      });
    } else {
      return this.update({
        color: color
      }, false);
    }
  },
  setColorOpacity: function(state) {
    var color, opacity;
    opacity = state.opacity;
    color = this.state.invertedColor;
    color[3] = opacity;
    return Filtr.setState({
      textInvertedColor: color
    });
  },
  saveColor: function() {
    return Filtr.saveState("labels.text labels.color");
  },
  pickColor: function(e) {
    return this.refs.colorpicker.show({
      target: e.target,
      offset: [8, 118 - 34 / 2]
    });
  },
  Slider: function(opts) {
    var SliderControl, text, update, value;
    if (opts == null) {
      opts = {};
    }
    SliderControl = CONFIG.platform === "mobile" ? SliderPad : Slider;
    text = this.state.text[this.state.index] || this.state.defaults;
    if (opts.name === "opacity" && this.state.inverted) {
      update = this.setColorOpacity;
      value = this.state.invertedColor[3];
    } else {
      update = this.update;
      value = text[opts.name];
    }
    return React.createElement(SliderControl, {
      tabIndex: this.tabIndex++, 
      label: opts.label, 
      prop: opts.name, 
      value: value, 
      default: this.state.defaults[opts.name], 
      onUpdate: update, 
      rotated: this.props.rotated, 
      group: opts.group || "", 
      displayGroup: false, 
      mul: (opts.mul != null) ? opts.mul : 100, 
      min: (opts.min != null) ? opts.min : -1, 
      max: (opts.max != null) ? opts.max : 1, 
      ref: opts.name + "-slider", 
      position: this.props.position, 
      disabled: opts.disabled, 
      snapPoint: opts.snapPoint}
    );
  },
  render: function() {
    var alignOptions, color, fontOptions, optionsActive, spacingOptions, swatchColor, text;
    this.tabIndex = 1;
    text = this.state.text[this.state.index] || this.state.defaults;
    color = this.state.inverted ? this.state.invertedColor : text.color;
    swatchColor = "rgb(" + (color[0] * 255 >> 0) + ", " + (color[1] * 255 >> 0) + ", " + (color[2] * 255 >> 0) + ")";
    optionsActive = !(this.state.showBlendList || this.state.showFontList);
    if (text.type !== "artwork") {
      alignOptions = 
        React.createElement("div", {className: "icon-group"}, 
          React.createElement(Icon, {name: "icon-text-left", onTap: this.alignLeft, active: text.align === "left"}), 
          React.createElement(Icon, {name: "icon-text-center", onTap: this.alignCenter, active: text.align === "center"}), 
          React.createElement(Icon, {name: "icon-text-right", onTap: this.alignRight, active: text.align === "right"}), 
          React.createElement(Icon, {name: "icon-text-justify", onTap: this.alignJustify, active: text.align === "justify"})
        )
      ;
      spacingOptions = [
        this.Slider({
          label: "labels.line_spacing",
          name: "line_height",
          group: "labels.text",
          min: 0,
          max: 10,
          mul: 1
        }), this.Slider({
          label: "labels.letter_spacing",
          name: "letter_spacing",
          group: "labels.text",
          min: 0,
          max: 1,
          mul: 100
        })
      ];
      fontOptions = [
        React.createElement("h3", null, translate("labels.font")), React.createElement(Button, {onTap: this.showFonts}, 
          React.createElement("i", {className: CONFIG.platform === "mobile" ? "icon-text" : "icon-forward right"}), 
          text.font_name)
      ];
    }
    return (
      React.createElement(Panel, React.__spread({},  this.props), 

        React.createElement(Portal, {portalId: "text-controls"}, 
          React.createElement(TextOverlay, {
            setIndex: this.setIndex, 
            index: this.state.index, 
            update: this.updateText, 
            remove: this.removeText, 
            duplicate: this.duplicateText, 
            text: this.state.text, 
            invertedColor: this.state.inverted ? color : null, 
            rotated: this.props.rotated, 
            active: this.props.active})
        ), 

        React.createElement(PSColorPicker, {
          ref: "colorpicker", 
          value: color, 
          onUpdate: this.setColor, 
          onUpdateEnd: this.saveColor, 
          rotated: this.props.rotated, 
          position: this.props.position}), 

        React.createElement(ContainerGroup, {active: this.state.active}, 

          React.createElement(PanelContainer, {name: "add"}, 
            React.createElement("div", {className: "action-top"}, 
              React.createElement("div", {className: "title"}, translate("labels.text"))
            ), 

            React.createElement(AddList, {
              presets: this.state.presets, 
              artwork: this.state.artwork, 
              choosePresets: this.choosePresets, 
              chooseArtwork: this.chooseArtwork, 
              addText: this.addText, 
              setPreset: this.setPreset})
          ), 

          React.createElement(PanelContainer, {name: "options"}, 
            React.createElement("div", {className: "action-top center"}, 
              React.createElement(Icon, {name: CONFIG.platform === "mobile" ? "icon-add" : "icon-back", onTap: this.showAdd}), 
              React.createElement("div", {className: "title"}, translate("labels.edit"))
            ), 

            React.createElement(Scrollable, {rotated: this.props.rotated, ref: "scrollable", disabled: this.state.index == null}, 
              fontOptions, 
              alignOptions, 
               this.Slider({label: "labels.size", name: "font_size", group: "labels.text", min: 0, max: 3}), 
              React.createElement("div", {className: "feature-block-wrapper"}, 
                React.createElement(FeatureBlock, {container: "text_tool", item: "text_tool", bypass: true}), 
                spacingOptions, 
                 this.Slider({label: "labels.angle", name: "angle", group: "labels.text", min: -180, max: 180, mul: 1}), 
                React.createElement("div", {className: "sep"}), 
                React.createElement(Button, {className: "color", onTap: this.pickColor}, React.createElement("i", {className: "icon-swatch right", style: {backgroundColor: swatchColor}}), translate("labels.color")), 
                 this.Slider({label: "labels.opacity", name: "opacity", group: "labels.text", min: 0}), 
                 this.Slider({label: "labels.blend", name: "blend_mix", group: "labels.text", disabled: this.state.inverted}), 
                React.createElement("div", {className: "sep"})
              ), 
              React.createElement(Button, {onTap: this.outlineText, active: text.mode === "outline"}, React.createElement("i", {className: "icon-outline"}), translate("labels.outline")), 
              React.createElement(Button, {onTap: this.invertText, active: this.state.inverted}, React.createElement("i", {className: "icon-invert"}), translate("labels.invert")), 
              React.createElement("div", {className: "sep"}), 
              React.createElement(Button, {onTap: this.removeText}, React.createElement("i", {className: "icon-remove"}), translate("buttons.delete")), 
              React.createElement(Button, {onTap: this.duplicateText}, React.createElement("i", {className: "icon-paste"}), translate("buttons.duplicate"))
            )
          ), 

          React.createElement(PanelContainer, {name: "presets"}, 
            React.createElement("div", {className: "action-top center"}, 
              React.createElement(Icon, {name: "icon-back", onTap: this.showAdd}), 
              React.createElement("div", {className: "title"}, this.state.category.name)
            ), 

            React.createElement(PresetList, {
              presets: this.state.category.presets, 
              update: this.setPreset})
          ), 

          React.createElement(PanelContainer, {name: "fonts"}, 
            React.createElement("div", {className: "action-top center"}, 
              React.createElement(Icon, {name: "icon-back", onTap: this.showOptions}), 
              React.createElement("div", {className: "title"}, translate("labels.font"))
            ), 

            React.createElement(FontList, {
              systemFonts: this.state.systemFonts, 
              downloadableFonts: this.state.downloadableFonts, 
              fonts: this.state.customFonts, 
              update: this.setFont, 
              selected: text.font_name})
          ), 

          React.createElement(PanelContainer, {name: "blendModes"}, 
            React.createElement("div", {className: "action-top center"}, 
              React.createElement(Icon, {name: "icon-back", onTap: this.showOptions}), 
              React.createElement("div", {className: "title"}, translate("labels.blend_mode"))
            ), 

            React.createElement(BlendModeList, {
              update: this.setBlend, 
              selected: text.blend_mode})
          )

        )

      )
    );
  }
});

module.exports = TextPanel;

AddList = React.createClass({displayName: "AddList",
  mixins: [require("react-addons-pure-render-mixin")],
  onSelect: function(id) {
    var index, parts, type;
    parts = id.split("_");
    type = parts[0];
    index = Number(parts[1]);
    if (type === "artwork") {
      return this.props.chooseArtwork(index);
    } else if (type === "presets") {
      return this.props.choosePresets(index);
    }
  },
  render: function() {
    var lines, presets, shapes;
    shapes = this.props.artwork[0];
    lines = this.props.artwork[1];
    presets = this.props.presets[0];
    return (
      React.createElement(List, {onSelect: this.onSelect}, 
        React.createElement(Button, {onTap: this.props.addText}, React.createElement("i", {className: "icon-add"}), translate("tooltips.add_text")), 

        React.createElement("div", {className: "sep"}), 

        React.createElement(Button, {id: "artwork_0"}, 
          React.createElement("i", {className: "icon-triangle"}), 
          translate("labels.shapes"), 
          React.createElement("span", {className: "index"}, shapes && shapes.presets.length)
        ), 

        React.createElement(Button, {id: "artwork_1"}, 
          React.createElement("i", {className: "icon-lines"}), 
          translate("labels.dividers"), 
          React.createElement("span", {className: "index"}, lines && lines.presets.length)
        ), 

        React.createElement(Button, {id: "presets_0"}, 
          React.createElement("i", {className: "icon-text"}), 
          translate("labels.presets"), 
          React.createElement("span", {className: "index"}, presets && presets.presets.length)
        )
      )
    );
  }
});

PresetList = React.createClass({displayName: "PresetList",
  mixins: [require("react-addons-pure-render-mixin")],
  getDefaultProps: function() {
    return {
      presets: []
    };
  },
  componentDidUpdate: function(prevProps) {
    var canvas, ctx, i, j, layers, len, preset, ref1, results;
    if (prevProps.presets === this.props.presets) {
      return;
    }
    ref1 = this.props.presets;
    results = [];
    for (i = j = 0, len = ref1.length; j < len; i = ++j) {
      preset = ref1[i];
      canvas = this.refs[i];
      ctx = canvas.getContext("2d");
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      layers = preset.state.map(function(layer) {
        return utils.assign(Text.defaults, layer);
      });
      results.push(Filtr.text.generateLayers(layers, {
        width: canvas.width
      }, function(options) {
        var context, ratio, text, y;
        context = options.context, text = options.text;
        ctx.globalAlpha = text.opacity;
        ratio = canvas.width / canvas.height;
        y = ((text.position[1] - 0.5) * 2 * ratio) / 2 + 0.5;
        ctx.translate(canvas.width * text.position[0], canvas.height * y);
        ctx.rotate((text.orientation + text.angle) / 180 * Math.PI);
        ctx.drawImage(context.canvas, -context.canvas.width / 2, -context.canvas.height / 2);
        return ctx.setTransform(1, 0, 0, 1, 0, 0);
      }, true));
    }
    return results;
  },
  onSelect: function(id) {
    var index;
    index = Number(id);
    return this.props.update(index);
  },
  render: function() {
    var canvasHeight, canvasWidth;
    canvasWidth = manager.state.panelWidth * Filtr.pixelRatio;
    canvasHeight = Math.min(112 * Filtr.pixelRatio, canvasWidth);
    return (
      React.createElement(List, {onSelect: this.onSelect, className: "presets"}, 
        this.props.presets.map(function(preset, i){
          return React.createElement("li", {key: i, "data-id": i}, 
            React.createElement("div", null, 
              React.createElement("canvas", {width: canvasWidth, height: canvasHeight, ref: i})
            )
          )
        }, this)
      )
    );
  }
});

BlendModeList = React.createClass({displayName: "BlendModeList",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      blendModes: Text.blendModes,
      common: Text.commonBlendModes,
      uncommon: Text.uncommonBlendModes,
      indexMap: Text.blendModesMap
    };
  },
  onSelect: function(id) {
    var index;
    index = this.state.indexMap[id];
    return this.props.update(index);
  },
  render: function() {
    var selected;
    if (this.props.selected != null) {
      selected = this.state.blendModes[this.props.selected];
    }
    return (
      React.createElement(List, {onSelect: this.onSelect}, 
        this.state.common.map(function(name, i){
          return React.createElement("li", {
            className: name === selected ? "active" : "", 
            key: i, 
            "data-id": name}, 
            React.createElement("div", null, name)
          )
        }, this), 
        React.createElement("li", {className: "sep"}), 
        this.state.uncommon.map(function(name, i){
          return React.createElement("li", {
            className: name === selected ? "active" : "", 
            key: i, 
            "data-id": name}, 
            React.createElement("div", null, name)
          )
        }, this)
      )
    );
  }
});

FontList = React.createClass({displayName: "FontList",
  mixins: [require("react-addons-pure-render-mixin")],
  onSelect: function(id) {
    var key, parts, type;
    parts = id.split("_");
    type = parts[0];
    key = parts[1];
    return this.props.update(type, key);
  },
  render: function() {
    var showDownloadable, style;
    style = {
      fontSize: "15px"
    };
    showDownloadable = utils.isChina();
    return (
      React.createElement(List, {onSelect: this.onSelect, style: style}, 
        this.props.downloadableFonts.map(function(font, i){
          var opts = Text.fonts.downloadable[font]
          var icon = opts.downloaded ? null : React.createElement("i", {className: "icon-download"})
          if (!showDownloadable && !opts.downloaded) return null
          return React.createElement("li", {
            className: font === this.props.selected ? "active" : "", 
            style: opts, 
            key: i, 
            "data-id": "downloadable_"+font}, 
             opts.thumbnail && CONFIG.platform === "mobile" ?
              React.createElement("div", {style: {backgroundImage: "url('"+opts.thumbnail+"')"}}, icon) :
              React.createElement("div", null, icon, CONFIG.platform === "mobile" ? "abc" : font)
          )
        }, this), 
        showDownloadable ? React.createElement("li", {className: "sep"}) : null, 
        this.props.systemFonts.map(function(font, i){
          return React.createElement("li", {
            className: font === this.props.selected ? "active" : "", 
            style: Text.fonts.system[font], 
            key: i, 
            "data-id": "system_"+font}, 
            React.createElement("div", null, CONFIG.platform === "mobile" ? "abc" : font)
          )
        }, this), 
        React.createElement("li", {className: "sep"}), 
        this.props.fonts.map(function(font, i){
          return React.createElement("li", {
            className: font === this.props.selected ? "active" : "", 
            style: Text.fonts.custom[font], 
            key: i, 
            "data-id": "custom_"+font}, 
            React.createElement("div", null, CONFIG.platform === "mobile" ? "abc" : font)
          )
        }, this)
      )
    );
  }
});


}).call(this,require("react"))
},{"../actions.coffee":229,"../lib/webfont":318,"../manager.coffee":338,"../mixins.coffee":339,"../store.coffee":346,"../text.coffee":347,"../utils.coffee":360,"./button.coffee":241,"./feature-block.coffee":255,"./icon.coffee":262,"./list.coffee":264,"./panel-container.coffee":271,"./panel.coffee":272,"./pointer-events.coffee":273,"./popover-menu.coffee":276,"./portal.coffee":277,"./ps-colorpicker.coffee":278,"./scrollable.coffee":283,"./slider-pad.coffee":285,"./slider.coffee":286,"./text-overlay.coffee":290,"classnames":1,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],292:[function(require,module,exports){
(function (React,ReactDOM){
var Button, Icon, MainMenu, Menu, Panels, Toolbars, actions, classnames, io, manager, mixins, status, store, translate, utils,
  indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

Menu = require("./popover-menu.coffee");

Button = require("./button.coffee");

Icon = require("./icon.coffee");

MainMenu = require("./main-menu.coffee");

translate = require("counterpart");

status = require("./status-overlay.coffee");

store = require("../store.coffee");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

mixins = require("../mixins.coffee");

utils = require("../utils.coffee");

io = require("../io.coffee");

Panels = {
  "looks": require("./looks.coffee"),
  "history": require("./history.coffee"),
  "crop": require("./crop-adjustments.coffee"),
  "adjustments": require("./adjustments.coffee"),
  "local-adjustments": require("./local-adjustments.coffee"),
  "filmstrip": require("./filmstrip.coffee"),
  "guides": require("./guides.coffee"),
  "face": require("./face-panel.coffee"),
  "text": require("./text-panel.coffee")
};

classnames = require("classnames");

Toolbars = React.createClass({displayName: "Toolbars",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      iconHeight: 50,
      dragIcon: null,
      states: 0,
      masks: 0,
      photos: 0,
      faces: 0
    };
  },
  componentWillMount: function() {
    return this.setState(manager.state.toolbars);
  },
  componentDidMount: function() {
    this.node = ReactDOM.findDOMNode(this);
    this.updateIcons();
    manager.on("toolbars", (function(_this) {
      return function(state) {
        return _this.setState(state);
      };
    })(this));
    store.on("photos", (function(_this) {
      return function(photos) {
        return _this.setState({
          photos: photos.length
        });
      };
    })(this));
    Filtr.on("history", (function(_this) {
      return function(history) {
        var faces, masks, state, states;
        state = Filtr.getState();
        masks = state.local_adjustments.length;
        faces = state.faces || [];
        states = history.length - 1;
        return _this.setState({
          masks: masks,
          states: states,
          faces: faces.length
        });
      };
    })(this));
    return window.addEventListener("resize", this.updateIcons);
  },
  componentDidUpdate: function() {
    return this.updateIcons();
  },
  updateIcons: function() {
    this.updateIconPositions("left");
    return this.updateIconPositions("right");
  },
  iconAction: function(toolbar, index, e) {
    var icon, ref;
    icon = this.state[toolbar][index];
    if ((ref = document.activeElement) != null) {
      ref.blur();
    }
    switch (icon) {
      case "export":
        return this.showExportMenu(e);
      case "undo":
        return actions.undo();
      case "camera":
        return actions.takePicture();
      default:
        return this.setActiveIcon(icon, toolbar);
    }
  },
  showExportMenu: function(e) {
    return this.refs.mainMenu.show(e);
  },
  setActiveIcon: function(icon, toolbar) {
    var activeIcon, newState;
    activeIcon = this.state[toolbar + "Active"];
    newState = {};
    newState[toolbar + "Active"] = icon === activeIcon ? null : icon;
    return this.setToolbar(newState);
  },
  setToolbar: function(state) {
    var cropMode, icon, j, k, l, leftMoved, len, len1, oppSide, ref, ref1, ref2, rightMoved, rule, rules, testSide, toolbars, v;
    toolbars = manager.state.toolbars;
    for (k in state) {
      v = state[k];
      toolbars[k] = v;
    }
    leftMoved = !!toolbars.leftActive;
    ref = toolbars.left;
    for (j = 0, len = ref.length; j < len; j++) {
      icon = ref[j];
      if (icon === toolbars.leftActive) {
        leftMoved = false;
      }
    }
    rightMoved = !!toolbars.rightActive;
    ref1 = toolbars.right;
    for (l = 0, len1 = ref1.length; l < len1; l++) {
      icon = ref1[l];
      if (icon === toolbars.rightActive) {
        rightMoved = false;
      }
    }
    if (leftMoved) {
      toolbars.rightActive = toolbars.leftActive;
      toolbars.leftActive = null;
    }
    if (rightMoved) {
      toolbars.leftActive = toolbars.rightActive;
      toolbars.rightActive = null;
    }
    rules = {
      "crop": ["local-adjustments"],
      "local-adjustments": ["crop"]
    };
    if (state.leftActive) {
      testSide = "leftActive";
    } else if (state.rightActive) {
      testSide = "rightActive";
    } else if (rightMoved) {
      testSide = "leftActive";
    } else {
      testSide = "rightActive";
    }
    oppSide = testSide === "leftActive" ? "rightActive" : "leftActive";
    rule = rules[toolbars[testSide]];
    if (rule && (ref2 = toolbars[oppSide], indexOf.call(rule, ref2) >= 0)) {
      toolbars[oppSide] = null;
    }
    cropMode = toolbars.leftActive === "crop" || toolbars.rightActive === "crop";
    if (cropMode !== Filtr._.cropMode) {
      Filtr.cropMode(cropMode);
    }
    return manager.set({
      toolbars: toolbars
    });
  },
  startDrag: function(toolbar, i, e) {
    var ref, touch;
    e = e.nativeEvent || e;
    e.preventDefault();
    touch = ((ref = e.targetTouches) != null ? ref[0] : void 0) || e;
    this.target = e.target;
    this.currentIndex = i;
    this.currentToolbar = toolbar;
    this.started = false;
    this.pointerStart = [touch.pageX, touch.pageY];
    window.addEventListener("touchmove", this.drag);
    window.addEventListener("touchend", this.dragEnd);
    window.addEventListener("touchcancel", this.dragEnd);
    window.addEventListener("mousemove", this.drag);
    window.addEventListener("mouseup", this.dragEnd);
    window.addEventListener("pointermove", this.drag);
    window.addEventListener("pointerup", this.dragEnd);
    return false;
  },
  initDrag: function() {
    var ref;
    if (!store.currentPhoto) {
      return;
    }
    if ((ref = document.activeElement) != null) {
      ref.blur();
    }
    this.dragging = true;
    this.toolbars = {
      left: this.state.left.slice(0),
      right: this.state.right.slice(0)
    };
    this.proxyState = {
      left: this.toolbars.left.slice(0),
      right: this.toolbars.right.slice(0)
    };
    this.currentIcon = this.toolbars[this.currentToolbar].splice(this.currentIndex, 1)[0];
    this.offset = this.target.getBoundingClientRect();
    this.currentTarget = document.createElement("i");
    this.currentTarget.className = this.target.className;
    this.currentTarget.style.backgroundColor = "#333";
    this.currentTarget.style.transition = "none";
    this.currentTarget.style.position = "fixed";
    this.currentTarget.style.zIndex = "99999";
    this.setIconPosition.apply(this, this.pointerStart);
    this.compileIcons();
    this.setState({
      dragIcon: this.currentIcon
    });
    return document.body.appendChild(this.currentTarget);
  },
  drag: function(e) {
    var i, icon, index, j, l, len, len1, ref, ref1, ref2, side, ti, toolbar, touch, x, y;
    touch = ((ref = e.touches) != null ? ref[0] : void 0) || e;
    x = touch.pageX;
    y = touch.pageY;
    if (!this.started) {
      if (utils.distance([x, y], this.pointerStart) > 20) {
        this.pointerStart = [x, y];
        this.initDrag();
        this.started = true;
      }
      return;
    }
    this.setIconPosition(x, y);
    index = this.currentIndex;
    side = x < window.innerWidth / 2 ? "left" : "right";
    ref1 = this.bounds;
    for (ti = j = 0, len = ref1.length; j < len; ti = ++j) {
      toolbar = ref1[ti];
      if (x >= toolbar.left && x <= toolbar.right) {
        index = y < toolbar.height / 2 ? 0 : toolbar.icons.length;
        if (side !== this.currentToolbar) {
          y -= this.state.iconHeight / 2;
        }
        ref2 = toolbar.icons;
        for (i = l = 0, len1 = ref2.length; l < len1; i = ++l) {
          icon = ref2[i];
          if (y >= icon.top && y <= icon.bottom) {
            index = i;
          }
        }
      }
    }
    if (side !== this.currentToolbar) {
      this.spliceIcon(this.currentToolbar, -1);
      this.spliceIcon(side, index);
    } else if (index !== this.currentIndex) {
      this.spliceIcon(side, index);
    }
    this.currentToolbar = side;
    return this.currentIndex = index;
  },
  dragEnd: function(e) {
    e.preventDefault();
    if (this.dragging) {
      this.setToolbar(this.proxyState);
      setTimeout((function(_this) {
        return function() {
          var bounds;
          bounds = ReactDOM.findDOMNode(_this.refs[_this.currentIcon]).getBoundingClientRect();
          return _this.animateIcon(_this.currentTarget, [bounds.left, bounds.top]);
        };
      })(this), 1);
    } else {
      this.iconAction(this.currentToolbar, this.currentIndex, e);
    }
    this.dragging = false;
    window.removeEventListener("touchmove", this.drag);
    window.removeEventListener("touchend", this.dragEnd);
    window.removeEventListener("touchcancel", this.dragEnd);
    window.removeEventListener("mousemove", this.drag);
    window.removeEventListener("mouseup", this.dragEnd);
    window.removeEventListener("pointermove", this.drag);
    return window.removeEventListener("pointerup", this.dragEnd);
  },
  spliceIcon: function(toolbar, i) {
    var icons;
    icons = this.toolbars[toolbar].slice(0);
    if (i >= 0) {
      icons.splice(i, 0, this.currentIcon);
    }
    this.proxyState[toolbar] = icons;
    return this.updateIconPositions(toolbar, icons);
  },
  transform: function(x, y, scale, rotation) {
    if (scale == null) {
      scale = 1;
    }
    rotation = rotation || (this.props.portraitMode ? "-90deg" : "0deg");
    return "translate(" + x + "px, " + y + "px) scale(" + scale + ") rotate(" + rotation + ")";
  },
  animateIcon: function(icon, to) {
    var target, transform;
    icon.style.transition = "all 0.2s ease-out";
    transform = this.transform(to[0], to[1]);
    icon.style.webkitTransform = transform;
    icon.style.transform = transform;
    target = this.currentTarget;
    return setTimeout((function(_this) {
      return function() {
        _this.setState({
          dragIcon: null
        });
        return setTimeout(function() {
          return target.parentNode.removeChild(target);
        }, 100);
      };
    })(this), 200);
  },
  setIconPosition: function(x, y) {
    var transform;
    x = this.offset.left + x - this.pointerStart[0];
    y = this.offset.top + y - this.pointerStart[1];
    transform = this.transform(x, y, 1);
    this.currentTarget.style.webkitTransform = transform;
    return this.currentTarget.style.transform = transform;
  },
  updateIconPositions: function(toolbar, icons) {
    var el, height, i, icon, j, len, offset, transform;
    if (icons == null) {
      icons = this.state[toolbar];
    }
    height = this.refs[toolbar].clientHeight;
    offset = (height - icons.length * this.state.iconHeight) / 2;
    for (i = j = 0, len = icons.length; j < len; i = ++j) {
      icon = icons[i];
      el = ReactDOM.findDOMNode(this.refs[icon]);
      transform = this.transform(0, offset + i * this.state.iconHeight);
      el.style.webkitTransform = transform;
      el.style.transform = transform;
    }
  },
  compileIcons: function() {
    var bounds, container, currentToolbar, icon, iconBounds, icons, j, l, len, len1, offset, toolbar, toolbarBounds, toolbars;
    currentToolbar = this.refs[this.currentToolbar];
    container = ReactDOM.findDOMNode(this);
    toolbarBounds = [];
    toolbars = container.querySelectorAll(".toolbar");
    for (j = 0, len = toolbars.length; j < len; j++) {
      toolbar = toolbars[j];
      offset = toolbar !== currentToolbar ? this.state.iconHeight / 2 : 0;
      iconBounds = [];
      icons = toolbar.querySelectorAll("i");
      for (l = 0, len1 = icons.length; l < len1; l++) {
        icon = icons[l];
        bounds = icon.getBoundingClientRect();
        bounds = {
          top: bounds.top - offset,
          bottom: bounds.bottom - offset,
          left: bounds.left,
          right: bounds.right,
          width: bounds.width,
          height: bounds.height
        };
        bounds.el = icon;
        iconBounds.push(bounds);
      }
      bounds = toolbar.getBoundingClientRect();
      bounds.el = toolbar;
      bounds.icons = iconBounds;
      toolbarBounds.push(bounds);
    }
    return this.bounds = toolbarBounds;
  },
  open: function(e) {
    return this.refs.mainMenu.open(e);
  },
  openStore: function() {
    return actions.setDialog("store", true);
  },
  showBefore: function(e) {
    e.preventDefault();
    return actions.showOriginal(true);
  },
  showAfter: function() {
    return actions.showOriginal(false);
  },
  renderIcon: function(icon, i, position) {
    var badge, shortcut, tooltip;
    badge = (function() {
      switch (icon) {
        case "history":
          return this.state.states;
        case "local-adjustments":
          return this.state.masks;
        case "filmstrip":
          return this.state.photos;
        case "face":
          return this.state.faces;
        default:
          return 0;
      }
    }).call(this);
    shortcut = icon;
    tooltip = (function() {
      switch (icon) {
        case "local-adjustments":
          if (CONFIG.platform === "osx" && !actions.isProPurchased()) {
            shortcut = null;
            return translate("status.purchase_required");
          } else {
            return translate("tooltips." + "local_adjustments");
          }
          break;
        default:
          return translate("tooltips." + icon);
      }
    })();
    return (React.createElement(Icon, {
      ref: icon, 
      key: icon, 
      name: "icon-"+icon, 
      disabled: icon === "export" && CONFIG.disableExport, 
      hidden: this.state.dragIcon === icon, 
      active: this.state[position+"Active"] === icon && !this.props.disabled, 
      badge: badge, 
      tooltip: tooltip, 
      shortcut: shortcut, 
      onPointerDown: this.startDrag.bind(this, position, i)})
    );
  },
  render: function() {
    var Panel, disabled, exportButton, galleryButton, j, l, leftStyle, len, len1, name, panels, positions, ref, ref1, rightStyle, showOriginal, storeButton, styles, toolbarsStyle;
    positions = {};
    ref = this.state.left;
    for (j = 0, len = ref.length; j < len; j++) {
      name = ref[j];
      positions[name] = "left";
    }
    ref1 = this.state.right;
    for (l = 0, len1 = ref1.length; l < len1; l++) {
      name = ref1[l];
      positions[name] = "right";
    }
    panels = [];
    for (name in Panels) {
      Panel = Panels[name];
      disabled = this.props.disabled && name !== 'filmstrip';
      panels.push(
        React.createElement(Panel, {active: (this.state.leftActive === name || this.state.rightActive === name) && !disabled, 
               rotated: this.props.portraitMode, 
               position: positions[name], 
               ref: "panel-"+name, 
               key: name, 
               name: name})
      );
    }
    styles = {
      active: {
        pointerEvents: "all",
        opacity: 1
      },
      inactive: {
        pointerEvents: "none",
        opacity: 0
      },
      dragging: {
        pointerEvents: "all",
        opacity: 1
      }
    };
    if (CONFIG.embedded && CONFIG.platform === "mobile") {
      toolbarsStyle = {};
      if (this.props.portraitMode) {
        toolbarsStyle.left = "44px";
      } else {
        toolbarsStyle.top = "32px";
      }
    }
    if (CONFIG.platform === "mobile") {
      if (CONFIG.enableGallery) {
        if (CONFIG.os === "android") {
          galleryButton = React.createElement(Icon, {name: "icon-import", onTap: this.open});
        } else {
          galleryButton = React.createElement(Icon, {name: "icon-back", onTap: this.open});
        }
      }
      storeButton = React.createElement(Icon, {name: "icon-shop", onTap: this.openStore});
    } else {
      if (!CONFIG.embedded) {
        galleryButton = React.createElement(Icon, {name: "icon-import", onTap: this.open, tooltip: translate("tooltips.open"), shortcut: "open"});
      }
      showOriginal = React.createElement(Icon, {name: "icon-eye", onPointerDown: this.showBefore, onPointerUp: this.showAfter, tooltip: translate("tooltips.show_original"), shortcut: "show_original"});
    }
    if (!CONFIG.embedded) {
      exportButton = React.createElement(Icon, {name: "icon-export", onTap: this.showExportMenu, tooltip: translate("tooltips.save")});
    }
    leftStyle = this.state.left.length ? styles.active : styles.inactive;
    rightStyle = this.state.right.length ? styles.active : styles.inactive;
    return (
      React.createElement("div", {className: this.props.disabled ? "toolbars disabled" : "toolbars", style: toolbarsStyle}, 

        React.createElement(MainMenu, {ref: "mainMenu", rotated: this.props.portraitMode}), 

        React.createElement(Icon, {name: "icon-settings", onTap: actions.showSettings, tooltip: translate("tooltips.settings")}), 

        storeButton, 
        exportButton, 
        galleryButton, 
        showOriginal, 

        panels, 

        React.createElement("div", {className: "toolbar left", ref: "left", style: leftStyle}, 
          this.state.left.map(function(icon, i){
            return this.renderIcon(icon, i, "left")
          }, this)
        ), 

        React.createElement("div", {className: "toolbar right", ref: "right", style: rightStyle}, 
          this.state.right.map(function(icon, i){
            return this.renderIcon(icon, i, "right")
            }, this)
        )

      )
    );
  }
});

module.exports = Toolbars;


}).call(this,require("react"),require("react-dom"))
},{"../actions.coffee":229,"../io.coffee":312,"../manager.coffee":338,"../mixins.coffee":339,"../store.coffee":346,"../utils.coffee":360,"./adjustments.coffee":236,"./button.coffee":241,"./crop-adjustments.coffee":245,"./face-panel.coffee":254,"./filmstrip.coffee":256,"./guides.coffee":259,"./history.coffee":261,"./icon.coffee":262,"./local-adjustments.coffee":265,"./looks.coffee":266,"./main-menu.coffee":267,"./popover-menu.coffee":276,"./status-overlay.coffee":287,"./text-panel.coffee":291,"classnames":1,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32,"react-dom":34}],293:[function(require,module,exports){
var Portal, React, Tooltip, keyboard, utils;

React = require("react");

Portal = require("./portal.coffee");

utils = require("../utils.coffee");

keyboard = require("../keyboard.coffee");

Tooltip = React.createClass({displayName: "Tooltip",
  render: function() {
    var key, keyLabels;
    key = keyboard.getKey(this.props.shortcut);
    if (key && key.length) {
      keyLabels = key.map(function(label, i) {
        return React.createElement("span", {key: i, className: "key"}, label);
      });
    }
    return (
      React.createElement(Portal, {className: "tooltip-container"}, 
        React.createElement("div", {ref: "tooltip", className: "tooltip"}, 
          React.createElement("div", {className: "caret"}), 
          this.props.text, 
          keyLabels
        )
      )
    );
  },
  trigger: function(e) {
    var target;
    if (e.nativeEvent.which !== 0) {
      return;
    }
    target = e.target;
    target.addEventListener("mouseleave", this.hide);
    target.addEventListener("mousedown", this.hide);
    clearTimeout(this.timeout);
    return this.timeout = setTimeout((function(_this) {
      return function() {
        return _this.show(target);
      };
    })(this), 300);
  },
  hide: function(e) {
    this.refs.tooltip.style.opacity = 0;
    clearTimeout(this.timeout);
    e.target.removeEventListener("mouseleave", this.hide);
    return e.target.removeEventListener("mousedown", this.hide);
  },
  show: function(target) {
    var rect, tip;
    tip = this.refs.tooltip;
    rect = target.getBoundingClientRect();
    if (rect.left > window.innerWidth / 2) {
      tip.className = "tooltip right";
      tip.style.right = window.innerWidth - rect.left + 8 + "px";
    } else {
      tip.className = "tooltip left";
      tip.style.left = rect.right + 8 + "px";
    }
    tip.style.top = rect.top + rect.height / 2 + "px";
    return tip.style.opacity = 1;
  }
});

module.exports = Tooltip;


},{"../keyboard.coffee":313,"../utils.coffee":360,"./portal.coffee":277,"react":215}],294:[function(require,module,exports){
(function (React,ReactDOM){
var $, Button, ColorPicker, Icon, Menu, Portal, Slider, SliderPad, Tutorial, actions, classNames, manager, storage, store, translate, tutorials, utils;

Button = require("./button.coffee");

Icon = require("./icon.coffee");

Menu = require("./popover-menu.coffee");

Slider = require("./slider.coffee");

SliderPad = require("./slider-pad.coffee");

ColorPicker = require("./color-picker.coffee");

Portal = require("./portal.coffee");

translate = require("counterpart");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

store = require("../store.coffee");

storage = require("../storage.coffee");

utils = require("../utils.coffee");

tutorials = require("../tutorials.coffee");

classNames = require("classnames");

$ = require("jquery");

Tutorial = React.createClass({displayName: "Tutorial",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      pointerLeft: 0,
      pointerTop: 0,
      showPointer: false,
      showNext: false,
      showPrev: false,
      showTip: false,
      tip: "",
      tipAnchor: null,
      tipStyle: {},
      tutorial: [],
      index: 0,
      active: false
    };
  },
  componentDidMount: function() {
    manager.on("tutorial", this.setTutorial);
    manager.on("tutorial_id", this.setTutorialByID);
    manager.on("show_intro", this.loadStartingGuide);
    manager.on("exit_tutorial", this.done);
    manager.on("popoutSliders", (function(_this) {
      return function() {
        var ref;
        if ((ref = _this.refs.tip) != null) {
          ref.style.transitionProperty = "all";
        }
        return _this.updateTipPosition();
      };
    })(this));
    if (CONFIG.enableStartScreen) {
      return storage.getItem("onboarding_completed", (function(_this) {
        return function(success, completed) {
          if (!completed) {
            return _this.showStartScreen();
          }
        };
      })(this));
    }
  },
  componentDidUpdate: function() {
    if (typeof this.cancelAnimation === "function") {
      this.cancelAnimation();
    }
    if (this.state.active) {
      this.didShow();
      this.updateTipPosition();
      this.playVideo();
      return this.playAnimation();
    } else {
      return this.didHide();
    }
  },
  getVideoURL: function(source, callback) {
    var res;
    source = store.getSampleDir() + source;
    if (CONFIG.os === "osx" || CONFIG.os === "ios") {
      res = utils.getArraybuffer(source);
      return res.then(function(buffer) {
        var blob;
        blob = new Blob([buffer], {
          type: "video/mp4"
        });
        return callback(URL.createObjectURL(blob));
      });
    } else {
      return callback(source);
    }
  },
  playVideo: function() {
    var video;
    video = this.refs.video;
    if (!video) {
      return;
    }
    video.autoplay = true;
    video.muted = true;
    video.loop = true;
    video.setAttribute('playsinline', 'true');
    return video.setAttribute('webkit-playsinline', 'true');
  },
  playAnimation: function() {
    var animate, control, frames, i, image, images, index, j, len, next, play, prevIndex;
    images = this.state.images;
    if (!images) {
      if (typeof this.cancelAnimation === "function") {
        this.cancelAnimation();
      }
      return;
    }
    for (i = j = 0, len = images.length; j < len; i = ++j) {
      image = images[i];
      this.refs["image_" + i].style.opacity = 0;
    }
    if (this.refs.control) {
      control = ReactDOM.findDOMNode(this.refs.control);
      control.focus();
    }
    frames = this.state.images.length;
    index = 0;
    prevIndex = null;
    image = this.state.images[index];
    next = function() {
      clearTimeout(this.animationTimeout);
      return this.animationTimeout = setTimeout(function() {
        prevIndex = index;
        index = (index + 1) % frames;
        return play();
      }, 800);
    };
    animate = (function(_this) {
      return function(val) {
        var a, b, prevValue, value;
        val = val[0];
        if (!_this.state.images) {
          return;
        }
        image = _this.state.images[index];
        if (!image) {
          return;
        }
        a = _this.refs["image_" + prevIndex];
        b = _this.refs["image_" + index];
        if (a != null) {
          a.style.opacity = 1.0 - val;
        }
        if (b != null) {
          b.style.opacity = val;
        }
        if ((image.value != null) && _this.refs.control) {
          value = image.value;
          if ((prevIndex != null) && _this.state.images[prevIndex]) {
            prevValue = _this.state.images[prevIndex].value;
            if (typeof value === "number") {
              value = Filtr.utils.lerp(val, prevValue, value);
            }
            if (value instanceof Array) {
              value = [Filtr.utils.lerp(val, prevValue[0], value[0]), Filtr.utils.lerp(val, prevValue[1], value[1])];
            }
          }
          switch (_this.state.control.type) {
            case "slider":
              return _this.refs.control.setState({
                value: value
              });
            case "picker":
              return _this.refs.control.setState({
                value: value
              });
            case "button":
              return control.classList[value ? "add" : "remove"]("active");
          }
        }
      };
    })(this);
    play = (function(_this) {
      return function() {
        var cancelAnimation;
        cancelAnimation = Filtr.utils.animate(0, 1, 1.0, animate, next);
        return _this.cancelAnimation = function() {
          cancelAnimation();
          animate = next = function() {};
          return clearTimeout(_this.animationTimeout);
        };
      };
    })(this);
    return next();
  },
  updateTipPosition: function() {
    var actualBottom, actualTop, anchor, anchorBox, box, diff, offset, offsetY, side, tip, top, transform, transformOrigin;
    if (!this.state.active) {
      return;
    }
    tip = this.refs.tip;
    if (!this.state.tipAnchor) {
      tip.style.top = "50%";
      tip.style.left = "50%";
      tip.style.right = "auto";
      transformOrigin = "50% 50%";
      transform = "translate(-50%, -50%)";
      if (this.props.portraitMode) {
        transform += " rotate(-90deg)";
      }
    } else {
      anchor = $(this.state.tipAnchor)[0];
      if (!anchor) {
        return;
      }
      anchorBox = anchor.getBoundingClientRect();
      side = anchorBox.left < window.innerWidth / 2 ? "left" : "right";
      top = this.props.portraitMode ? "50%" : anchorBox.top + anchorBox.height / 2 + "px";
      offset = manager.state.toolbarWidth + 16;
      if (side === "right") {
        offset += !!manager.state.toolbars.rightActive * manager.state.panelWidth;
        offset += !!manager.state.popoutSliders.right * 32;
        tip.style.top = top;
        tip.style.right = offset + "px";
        tip.style.left = "auto";
      } else {
        offset += !!manager.state.toolbars.leftActive * manager.state.panelWidth;
        offset += !!manager.state.popoutSliders.left * 32;
        tip.style.top = top;
        tip.style.left = offset + "px";
        tip.style.right = "auto";
      }
      box = tip.getBoundingClientRect();
      top = box.top;
      if (this.props.portraitMode) {
        if (side === "right") {
          transformOrigin = "100% 0";
          transform = "rotate(-90deg) translate(50%, -100%)";
        } else {
          transformOrigin = "0 0";
          transform = "rotate(-90deg) translate(-50%, 0)";
        }
      } else {
        offsetY = box.height / 2;
        actualTop = top - offsetY;
        diff = Math.max(actualTop, 8) - actualTop;
        offsetY = -(offsetY - diff);
        actualTop = top + offsetY;
        actualBottom = actualTop + box.height;
        diff = Math.min(actualBottom, window.innerHeight - 8) - actualBottom;
        offsetY += diff;
        transform = "translate(0, " + offsetY + "px)";
      }
    }
    tip.style.webkitTransformOrigin = transformOrigin;
    tip.style.transformOrigin = transformOrigin;
    tip.style.webkitTransform = transform;
    return tip.style.transform = transform;
  },
  didShow: function() {
    var node;
    node = ReactDOM.findDOMNode(this);
    node.style.display = "block";
    requestAnimationFrame(function() {
      return node.style.opacity = 1;
    });
    return clearTimeout(this.didHideTimeout);
  },
  didHide: function() {
    var node;
    node = ReactDOM.findDOMNode(this);
    clearTimeout(this.didHideTimeout);
    this.didHideTimeout = setTimeout(function() {
      return node.style.display = "none";
    }, 300);
    return node.style.opacity = 0;
  },
  showStartScreen: function() {
    this.setState({
      active: true,
      showStart: true
    });
    return setTimeout((function(_this) {
      return function() {
        return _this.loadStartingGuide();
      };
    })(this), 300);
  },
  hideStartScreen: function() {
    this.refs.startScreen.style.opacity = 0;
    return setTimeout((function(_this) {
      return function() {
        return _this.setState({
          showStart: false
        });
      };
    })(this), 300);
  },
  loadStartingGuide: function() {
    store.loadSampleImage();
    return this.setTutorialByID("getting_started");
  },
  setTutorialByID: function(id) {
    var j, len, results, tutorial;
    results = [];
    for (j = 0, len = tutorials.length; j < len; j++) {
      tutorial = tutorials[j];
      if (tutorial.id === id) {
        this.setTutorial(tutorial);
        break;
      } else {
        results.push(void 0);
      }
    }
    return results;
  },
  setTutorial: function(tutorial) {
    this.setState({
      tutorial: tutorial,
      index: 0,
      active: true
    });
    this.showStep(0);
    manager.set("histogram", false);
    return utils.trackEventGA("tutorial", "start", tutorial.id);
  },
  triggerStep: function(trigger) {
    var i, j, len, ref, results, step;
    ref = this.state.tutorial.steps;
    results = [];
    for (i = j = 0, len = ref.length; j < len; i = ++j) {
      step = ref[i];
      if ((step.triggerOn != null) && step.triggerOn === trigger) {
        if (i !== this.state.index) {
          this.showStep(i);
        }
        break;
      } else {
        results.push(void 0);
      }
    }
    return results;
  },
  done: function() {
    var action, step, steps, value;
    $(".toolbars > i, .toolbar > i").each((function(_this) {
      return function(i, icon) {
        return _this.showIcon(icon);
      };
    })(this));
    this.hideTip();
    clearTimeout(this.timeout);
    if (typeof this.removeListeners === "function") {
      this.removeListeners();
    }
    setTimeout((function(_this) {
      return function() {
        _this.setState(_this.getInitialState());
        return storage.setItem("onboarding_completed", true);
      };
    })(this), 300);
    step = this.state.index + 1;
    steps = this.state.tutorial.steps.length;
    if (step === steps) {
      action = "complete";
    } else {
      action = "skip";
    }
    value = step + "|" + steps;
    return utils.trackEventGA("tutorial", action, this.state.tutorial.id, value);
  },
  next: function() {
    return this.showStep(this.state.index + 1);
  },
  prev: function() {
    return this.showStep(this.state.index - 1);
  },
  hideIcon: function(icon) {
    if (icon) {
      icon.style.opacity = 0;
      return icon.style.pointerEvents = "none";
    }
  },
  showIcon: function(icon) {
    if (icon) {
      icon.style.opacity = 1;
      return icon.style.pointerEvents = "";
    }
  },
  hideTip: function() {
    var ref, ref1;
    if ((ref = this.refs.tip) != null) {
      ref.style.opacity = 0;
    }
    return (ref1 = this.refs.pointer) != null ? ref1.style.opacity = 0 : void 0;
  },
  showTip: function() {
    var ref, ref1, ref2;
    if ((ref = this.refs.tip) != null) {
      ref.style.transitionProperty = "opacity";
    }
    if ((ref1 = this.refs.tip) != null) {
      ref1.style.opacity = 1;
    }
    return (ref2 = this.refs.pointer) != null ? ref2.style.opacity = 1 : void 0;
  },
  showStep: function(index) {
    var condition, curStep, delay, event, i, icon, icons, j, k, key, len, len1, listener, loadVideo, ref, ref1, removeListener, s, state, step, steps, tabIndex, toolbars, url;
    if (!this.state.tutorial) {
      return;
    }
    if (!this.state.tutorial.steps) {
      return;
    }
    if (index >= this.state.tutorial.steps.length) {
      this.done();
      return;
    }
    if (typeof this.removeListeners === "function") {
      this.removeListeners();
    }
    step = this.state.tutorial.steps[index];
    state = {
      pointerLeft: 0,
      pointerTop: 0,
      showPointer: false,
      showNext: false,
      showPrev: false,
      showTip: false,
      tip: "",
      tipAnchor: null,
      tipLeft: null,
      tipRight: null,
      active: true,
      index: index,
      videoWidth: 0,
      videoHeight: 0,
      images: null,
      imageStyle: "default",
      control: null,
      fullscreen: false,
      step: null
    };
    toolbars = manager.state.toolbars;
    icons = {};
    $(".toolbars > i, .toolbar > i").each(function(i, icon) {
      return icons[icon.classList[0]] = icon;
    });
    if (CONFIG.os === "android") {
      this.hideIcon(icons["icon-import"]);
    }
    this.hideIcon(icons["icon-back"]);
    this.hideIcon(icons["icon-settings"]);
    if (step.showIcons) {
      for (key in icons) {
        icon = icons[key];
        this.hideIcon(icon);
      }
      ref = step.showIcons;
      for (j = 0, len = ref.length; j < len; j++) {
        icon = ref[j];
        this.showIcon(icons["icon-" + icon]);
      }
    }
    this.hideTip();
    if (step.hidePanels) {
      toolbars.leftActive = null;
      toolbars.rightActive = null;
      manager.set("toolbars", toolbars);
    }
    if (step.showPanel) {
      actions.showPanel(step.showPanel);
    }
    if (step.showBeforeAfter) {
      actions.setDialog("before_after", {
        tutorial: this.state.tutorial
      });
    }
    if (step.loadImage) {
      if (step.loadImage.indexOf("http") === 0) {
        url = step.loadImage;
      } else {
        url = store.getSampleDir() + step.loadImage;
      }
      manager.set("photo_status", {
        status: "loading"
      });
      store.loadImageURL(url, {
        resetState: true
      });
    }
    loadVideo = new Promise((function(_this) {
      return function(resolve) {
        if (step.video) {
          state.videoWidth = step.video.width;
          state.videoHeight = step.video.height;
          if (step.video.src.indexOf("http") === 0) {
            return resolve(step.video.src);
          } else {
            return _this.getVideoURL(step.video.src, function(url) {
              return resolve(url);
            });
          }
        } else {
          return resolve(null);
        }
      };
    })(this));
    if (step.showAdjustmentPanel != null) {
      tabIndex = $(".panel-header[data-panel='" + step.showAdjustmentPanel + "']").data("index");
      tabIndex = parseInt(tabIndex, 10);
      manager.state.adjustment_tab = tabIndex;
      manager.emit("adjustment_tab", tabIndex, step.tipAnchor);
    }
    if (step.set) {
      manager.set(step.set);
    }
    if (step.showStep) {
      steps = 0;
      curStep = 0;
      ref1 = this.state.tutorial.steps;
      for (i = k = 0, len1 = ref1.length; k < len1; i = ++k) {
        s = ref1[i];
        if (s.showStep) {
          steps++;
          if (i === index) {
            curStep = steps;
          }
        }
      }
      state.step = [curStep, steps];
    }
    utils.extend(state, {
      showFeedback: step.showFeedback,
      showNext: step.showNext,
      showPrev: step.showPrev && state.index > 0,
      tipAnchor: step.tipAnchor || null,
      images: step.images || null,
      control: step.control || null,
      imageStyle: step.imageStyle,
      fullscreen: step.fullscreen
    });
    if (typeof step.nextOn === "string") {
      removeListener = manager.once(step.nextOn, this.next);
    } else if (typeof step.nextOn === "function") {
      step.nextOn(this.next);
    } else if (typeof step.nextOn === "object") {
      event = step.nextOn.event;
      condition = step.nextOn.when;
      listener = (function(_this) {
        return function() {
          var history, idx;
          if (event === "show_panel") {
            if (arguments[0] === condition) {
              return _this.next();
            }
          } else if (event === "history") {
            history = arguments[0];
            idx = arguments[1];
            state = history[idx];
            if (condition.value != null) {
              if (state.message === condition.message && utils.approx(state.changed, condition.value)) {
                return _this.next();
              }
            } else if (state.message === condition.message) {
              return _this.next();
            }
          }
        };
      })(this);
      manager.emitter.addListener(event, listener);
      removeListener = function() {
        return manager.emitter.removeListener(event, listener);
      };
    }
    delay = 300;
    clearTimeout(this.timeout);
    return this.timeout = setTimeout((function(_this) {
      return function() {
        var l, len2, pointer, ref2, stepListeners, transform;
        if (_this.state.tutorial.triggerStepOn) {
          stepListeners = [];
          ref2 = _this.state.tutorial.triggerStepOn;
          for (l = 0, len2 = ref2.length; l < len2; l++) {
            event = ref2[l];
            stepListeners.push(manager.on(event, _this.triggerStep));
          }
        }
        _this.removeListeners = function() {
          var len3, m;
          if (typeof removeListener === "function") {
            removeListener();
          }
          if (stepListeners) {
            for (m = 0, len3 = stepListeners.length; m < len3; m++) {
              removeListener = stepListeners[m];
              removeListener();
            }
          }
          manager.set("sliderSnap", null);
          return this.removeListeners = null;
        };
        transform = "translate(-50%, -50%)";
        if (step.tipAnchor) {
          transform = "translate(0, 0)";
        }
        if (_this.props.portraitMode) {
          transform += " rotate(-90deg)";
        }
        state.tipStyle = {
          WebkitTransform: transform,
          transform: transform
        };
        if (step.pointer) {
          pointer = _this.getPointerPosition(step.pointer);
          utils.extend(state, pointer);
        }
        if (step.tip) {
          utils.extend(state, {
            tip: step.tip,
            showTip: true
          });
        }
        return loadVideo.then(function(url) {
          state.videoUrl = url;
          _this.setState(state);
          return _this.showTip();
        });
      };
    })(this), delay);
  },
  getPointerPosition: function(selector) {
    var box, left, target, top;
    target = $(selector)[0];
    if (!target) {
      return {};
    }
    box = target.getBoundingClientRect();
    top = box.top + box.height / 2;
    left = box.left + box.width / 2;
    return {
      pointerLeft: left,
      pointerTop: top,
      showPointer: true
    };
  },
  sendFeedback: function() {
    var subject;
    subject = "Polarr Tutorial Feedback";
    return window.location = "mailto:support@polarr.co?subject=" + subject;
  },
  renderButton: function(opts) {
    if (opts == null) {
      opts = {};
    }
    return React.createElement(Button, {
      active: opts.active, 
      ref: "control"}, 
      React.createElement("i", {className: "icon-" + opts.name}), 
      translate("labels." + opts.name)
    );
  },
  renderSlider: function(opts) {
    var SliderControl;
    if (opts == null) {
      opts = {};
    }
    SliderControl = CONFIG.platform === "mobile" ? SliderPad : Slider;
    return React.createElement(SliderControl, {
      tabIndex: -1, 
      label: "labels." + opts.name, 
      prop: opts.name, 
      value: opts.value || 0, 
      onUpdate: function(){}, 
      rotated: false, 
      group: "", 
      mul: (opts.mul != null) ? opts.mul : 100, 
      min: (opts.min != null) ? opts.min : -1, 
      max: (opts.max != null) ? opts.max : 1, 
      ref: "control"}
    );
  },
  renderPicker: function(opts) {
    if (opts == null) {
      opts = {};
    }
    return React.createElement(ColorPicker, {
      label: "labels." + opts.name, 
      name: opts.name, 
      value: [0, 0], 
      rotated: false, 
      update: function(){}, 
      ref: "control"}
    );
  },
  renderControl: function(control) {
    switch (control.type) {
      case "slider":
        return this.renderSlider(control);
      case "button":
        return this.renderButton(control);
      case "picker":
        return this.renderPicker(control);
    }
  },
  render: function() {
    var buttonText, control, dismissButton, feedbackButton, images, nextButton, pointer, pointerStyle, prevButton, startScreen, tipClass, tipHTML, tipStyle, tutorialClass, video;
    tipStyle = this.state.tipStyle || {};
    tipStyle.display = this.state.showTip ? "" : "none";
    tutorialClass = classNames({
      "active": this.state.active
    });
    tipClass = classNames("tip", {
      "fullscreen": this.state.fullscreen
    });
    tipHTML = this.state.tip;
    if (this.state.step) {
      tipHTML = ("<span class='step'>" + this.state.step[0] + "/" + this.state.step[1] + "</span>") + tipHTML;
    }
    if (this.state.showPointer && !this.state.showStart) {
      pointerStyle = {
        left: this.state.pointerLeft + 'px',
        top: this.state.pointerTop + 'px'
      };
      pointer = React.createElement("div", {ref: "pointer", className: "pointer", style: pointerStyle});
    }
    if (this.state.showStart) {
      startScreen = 
        React.createElement("div", {ref: "startScreen", className: "start-screen"}, 
          React.createElement("div", {className: "background"}), 
          React.createElement("div", {className: "logo"}), 
          React.createElement("div", {className: "title"}, translate("onboarding.become_master")), 
          React.createElement("div", {className: "action"}, 
            React.createElement(Button, {onTap: this.hideStartScreen, content: "buttons.get_started", className: "primary"})
          )
        )
      ;
    }
    if (this.state.showFeedback) {
      feedbackButton = React.createElement(Button, {content: "dialogs.send_feedback", onTap: this.sendFeedback});
    }
    if (this.state.dismissable) {
      dismissButton = React.createElement(Button, {content: "buttons.dismiss", onTap: this.done});
    }
    if (this.state.showNext) {
      if (this.state.index === this.state.tutorial.steps.length - 1) {
        buttonText = "buttons.done";
      } else {
        buttonText = "buttons.next";
      }
      nextButton = React.createElement(Button, {content: buttonText, onTap: this.next, className: "primary"});
    }
    if (this.state.showPrev) {
      prevButton = React.createElement(Button, {content: "buttons.prev", onTap: this.prev});
    }
    if (this.state.videoUrl) {
      video = 
        React.createElement("video", {ref: "video", width: this.state.videoWidth, height: this.state.videoHeight, src: this.state.videoUrl})
      ;
    }
    if (this.state.images) {
      images = 
        React.createElement("div", {ref: "images", className: "images " + this.state.imageStyle}, 
          this.state.images.map(function(image, i){
            var src = store.getSampleDir() + image.src
            return React.createElement("div", {className: "image", key: i, ref: "image_" + i}, 
              React.createElement("img", {src: src}), 
              React.createElement("div", {className: "label"}, image.label)
            )
            }, this)
        )
      ;
    }
    if (this.state.control) {
      control = this.renderControl(this.state.control);
    }
    return (
      React.createElement("div", {id: "tutorial", className: tutorialClass}, 
        React.createElement(Portal, {portalId: "pointer"}, 
          pointer
        ), 
        React.createElement("div", {className: "skip-tutorial"}, 
          React.createElement(Button, {content: "buttons.skip", onTap: this.done})
        ), 
        startScreen, 
        React.createElement("div", {className: tipClass, style: tipStyle, ref: "tip"}, 
          React.createElement("div", null, 
            images, 
            video
          ), 
          React.createElement("div", null, 
            React.createElement("div", {className: "text", dangerouslySetInnerHTML: {__html: tipHTML}}), 
            React.createElement("div", {className: "footer"}, 
              feedbackButton, 
              React.createElement("div", {className: "panel"}, 
                control
              ), 
              prevButton, 
              nextButton, 
              dismissButton
            )
          )
        )
      )
    );
  }
});

module.exports = Tutorial;


}).call(this,require("react"),require("react-dom"))
},{"../actions.coffee":229,"../manager.coffee":338,"../storage.coffee":343,"../store.coffee":346,"../tutorials.coffee":348,"../utils.coffee":360,"./button.coffee":241,"./color-picker.coffee":243,"./icon.coffee":262,"./popover-menu.coffee":276,"./portal.coffee":277,"./slider-pad.coffee":285,"./slider.coffee":286,"classnames":1,"counterpart":3,"jquery":319,"react":215,"react-addons-pure-render-mixin":32,"react-dom":34}],295:[function(require,module,exports){
var $, Button, Dialog, React, Scrollable, UserDialog, actions, classnames, manager, translate, utils;

React = require("react");

translate = require("counterpart");

$ = require("jquery");

Dialog = require("./dialog.coffee");

Button = require("./button.coffee");

Scrollable = require("./scrollable.coffee");

actions = require("../actions.coffee");

utils = require("../utils.coffee");

manager = require("../manager.coffee");

classnames = require("classnames");

UserDialog = React.createClass({displayName: "UserDialog",
  mixins: [require("react-addons-pure-render-mixin")],
  componentDidMount: function() {
    return manager.on("user", (function(_this) {
      return function(user) {
        return _this.setState({
          user: user
        });
      };
    })(this));
  },
  componentWillReceiveProps: function(nextProps) {
    var active, dialog;
    active = nextProps.active;
    dialog = active ? active : this.state.dialog;
    return this.setState({
      dialog: dialog,
      progress: false
    });
  },
  getInitialState: function() {
    return {
      _user_name: "",
      _email: "",
      _password: "",
      dialog: {},
      user: manager.state.user
    };
  },
  show: function(dialog, e) {
    if (e != null) {
      e.preventDefault();
    }
    return actions.setDialog("user", {
      name: dialog,
      success: this.state.dialog.success
    });
  },
  hide: function() {
    return actions.setDialog("user", null);
  },
  payWeChat: function() {
    var ref;
    return typeof POIPCManager !== "undefined" && POIPCManager !== null ? (ref = POIPCManager.sharedManager) != null ? ref.sendMessage("ppe.payment.wechat") : void 0 : void 0;
  },
  checkLogin: function(e) {
    e.preventDefault();
    this.setState({
      progress: true
    });
    return actions.restoreLogin(true);
  },
  signup: function(e) {
    var options;
    e.preventDefault();
    this.setState({
      progress: true
    });
    options = {
      user_name: this.state._user_name,
      email: this.state._email,
      password: this.state._password,
      done: this.state.dialog.success
    };
    return actions.user.signup(options);
  },
  signin: function(e) {
    var options;
    e.preventDefault();
    this.setState({
      progress: true
    });
    options = {
      email: this.state._email,
      password: this.state._password,
      done: this.state.dialog.success
    };
    return actions.user.signin(options);
  },
  signout: function(e) {
    return actions.user.signout();
  },
  reset: function(e) {
    e.preventDefault();
    this.setState({
      progress: true
    });
    return actions.user.resetPassword({
      email: this.state._email
    });
  },
  subscribeToUpdates: function(e) {
    e.preventDefault();
    this.setState({
      progress: true
    });
    return actions.user.subscribeToUpdates({
      email: this.state._email
    });
  },
  sendVerification: function(e) {
    this.setState({
      progress: true
    });
    return actions.user.sendVerificationEmail({
      email: this.state._email
    });
  },
  openStore: function(e) {
    e.preventDefault();
    return actions.openStore();
  },
  isValidEmail: function(email) {
    return utils.testEmail(email);
  },
  showSettings: function() {
    return actions.setDialog("settings", true);
  },
  setField: function(field, e) {
    var obj;
    obj = {};
    obj[field] = e.target.value;
    return this.setState(obj);
  },
  getLabel: function(field) {
    return translate("labels." + field);
  },
  submitForm: function(e, input) {
    document.activeElement.blur();
    if (input.pointerType === "touch") {
      e.preventDefault();
      return e.target.click();
    }
  },
  Input: function(field, className) {
    return React.createElement("input", {
      ref: field, 
      name: field, 
      type: field === "password" ? "password" : field === "email" ? "email" : "text", 
      placeholder: this.getLabel(field), 
      value: this.state["_"+field], 
      className: (field === this.state.dialog.invalid_field ? "invalid" : "") + " " + className, 
      onChange: this.setField.bind(this, "_"+field)});
  },
  render: function() {
    var content, dialog, dialogSettings, errorMessage, errorText, fullscreen, invalid_field, maxWidth, message, messageText, progressClass, ref, status, subtext, verificationMessage, verificationText, verification_state;
    ref = this.state.dialog, message = ref.message, subtext = ref.subtext, status = ref.status, invalid_field = ref.invalid_field, verification_state = ref.verification_state;
    progressClass = classnames({
      "progress-overlay": true,
      active: this.state.progress
    });
    fullscreen = true;
    if (verification_state != null) {
      verificationText = (function() {
        switch (verification_state) {
          case 1:
            return translate("dialogs.verification_sent");
          case 0:
            return translate("dialogs.verify_account");
        }
      })();
      verificationMessage = 
        React.createElement("div", {className: "warning"}, 
          React.createElement("div", {dangerouslySetInnerHTML: {__html: verificationText}}), 
          React.createElement("p", null, React.createElement("a", {onTouchTap: this.sendVerification}, translate("buttons.send_verification")))
        )
        ;
    }
    if (message) {
      messageText = translate(message);
    }
    if (subtext) {
      subtext = React.createElement("p", {className: "subtext"}, subtext);
    }
    if (status === "error") {
      if (message === "status.field_invalid") {
        errorText = this.getLabel(invalid_field) + " " + messageText;
      } else {
        errorText = messageText;
      }
      errorMessage = 
        React.createElement("div", {className: "error", dangerouslySetInnerHTML: {__html: errorText}})
      ;
    }
    switch (this.state.dialog.name) {
      case "get_updates":
        dialog = (
          React.createElement("form", {ref: "form", onSubmit: this.subscribeToUpdates}, 
            React.createElement("h1", null, translate("dialogs.get_updates")), 
            React.createElement("h2", {dangerouslySetInnerHTML: {__html: translate("dialogs.get_updates_about")}}), 
            errorMessage, 
            React.createElement("div", {className: "options"}, 
              React.createElement("label", null, 
                this.Input("email")
              )
            ), 
            React.createElement("div", {className: "footer"}, 
              React.createElement(Button, {onTap: this.submitForm, content: "buttons.subscribe", className: "primary wide", type: "submit"})
            )
          )
        );
        break;
      case "purchase_china":
        dialog = (
          React.createElement("form", {ref: "form", className: "subscribe-form"}, 
            React.createElement("h1", {dangerouslySetInnerHTML: {__html: translate("dialogs.upgrade")}}), 
            React.createElement("ul", {dangerouslySetInnerHTML: {__html: translate("dialogs.upgrade_features")}}), 
            React.createElement("div", {className: "sep"}), 
            React.createElement("p", null, React.createElement("div", {className: "qr"})), 
            React.createElement("p", null, translate("dialogs.subscribe_china")), 
            React.createElement("div", {className: "footer"}, 
              CONFIG.platform === "mobile" ?
              React.createElement(Button, {content: "buttons.pay_wechat", onTap: this.payWeChat, className: "primary wide"}) :
              null, 
              React.createElement(Button, {content: "buttons.signin", onTap: this.checkLogin, className: "wide"})
            )
          )
        );
        break;
      case "unlock_failed":
        fullscreen = false;
        dialog = (
          React.createElement("div", null, 
            React.createElement("h2", {dangerouslySetInnerHTML: {__html: translate("dialogs.unlock_failed")}}), 
            React.createElement("div", {className: "footer"}, 
              React.createElement(Button, {content: "buttons.close", onTap: this.hide}), 
              React.createElement(Button, {content: "buttons.check_again", onTap: this.checkLogin}), 
              React.createElement(Button, {content: "buttons.buy_full_version", onTap: this.openStore, className: "primary"})
            )
          )
        );
        break;
      case "signup":
        dialog = (
          React.createElement("form", {ref: "form", onSubmit: this.signup}, 
            React.createElement("h1", null, translate("buttons.signup")), 
            errorMessage, 
            React.createElement("div", {className: "options"}, 
              React.createElement("label", null, 
                this.Input("user_name")
              )
            ), 
            React.createElement("div", {className: "options"}, 
              React.createElement("label", null, 
                this.Input("email")
              )
            ), 
            React.createElement("div", {className: "options"}, 
              React.createElement("label", null, 
                this.Input("password")
              )
            ), 
            React.createElement("div", {className: "footer"}, 
              React.createElement(Button, {onTap: this.submitForm, content: "buttons.signup", className: "primary wide", type: "submit"}), 
              React.createElement("p", {className: "center actions"}, 
                translate("buttons.have_account"), " ", 
                React.createElement("a", {onTouchTap: this.show.bind(this, "signin")}, translate("buttons.signin"))
              )
            )
          )
        );
        break;
      case "signin":
        dialog = (
          React.createElement("form", {ref: "form", onSubmit: this.signin}, 
            React.createElement("h1", null, translate("dialogs.signin_polarr_account")), 
            verificationMessage, 
            errorMessage, 
            React.createElement("div", {className: "options"}, 
              React.createElement("label", null, 
                this.Input("email")
              )
            ), 
            React.createElement("div", {className: "options"}, 
              React.createElement("label", null, 
                this.Input("password")
              )
            ), 
            React.createElement("div", {className: "footer"}, 
              React.createElement(Button, {onTap: this.submitForm, content: "buttons.signin", className: "primary wide", type: "submit"}), 
              React.createElement("p", {className: "actions justify"}, 
                React.createElement("a", {onTouchTap: this.show.bind(this, "reset_password")}, translate("buttons.forgot_password")), 
                React.createElement("a", {onTouchTap: this.show.bind(this, "signup")}, translate("buttons.signup"))
              )
            )
          )
        );
        break;
      case "reset_password":
        dialog = (
          React.createElement("form", {ref: "form", onSubmit: this.reset}, 
            React.createElement("h1", null, translate("buttons.reset_password")), 
            errorMessage, 
            React.createElement("div", {className: "options"}, 
              React.createElement("label", null, 
                this.Input("email")
              )
            ), 
            React.createElement("div", {className: "footer"}, 
              React.createElement(Button, {onTap: this.submitForm, content: "buttons.reset_password", className: "primary wide", type: "submit"}), 
              React.createElement("p", {className: "center actions"}, 
                React.createElement("a", {onTouchTap: this.show.bind(this, "signin")}, translate("buttons.signin"))
              )
            )
          )
        );
        break;
      default:
        fullscreen = false;
        maxWidth = "50%";
        dialog = (
          React.createElement("div", null, 
            React.createElement("h2", {dangerouslySetInnerHTML: {__html: messageText}}), 
            subtext, 
            React.createElement("div", {className: "footer"}, 
              React.createElement(Button, {content: "buttons.close", onTap: this.hide})
            )
          )
        );
    }
    if (fullscreen) {
      dialogSettings = {
        fullscreen: true,
        margin: 0,
        showclose: true,
        nopad: true
      };
      content = 
        React.createElement(Scrollable, {rotated: this.props.rotated, className: "opaque-dialog"}, 
          React.createElement("div", {className: "user-dialog", ref: "dialog"}, 
            dialog
          )
        )
      ;
    } else {
      content = dialog;
    }
    return (
      React.createElement(Dialog, React.__spread({},  dialogSettings, {maxWidth: maxWidth},  this.props), 
        React.createElement("div", {className: progressClass}, 
          React.createElement("div", {className: "spinner"})
        ), 
        content
      )
    );
  }
});

module.exports = UserDialog;


},{"../actions.coffee":229,"../manager.coffee":338,"../utils.coffee":360,"./button.coffee":241,"./dialog.coffee":249,"./scrollable.coffee":283,"classnames":1,"counterpart":3,"jquery":319,"react":215,"react-addons-pure-render-mixin":32}],296:[function(require,module,exports){
(function (React){
var ViewContainer, manager;

manager = require("../manager.coffee");

ViewContainer = React.createClass({displayName: "ViewContainer",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    var left, right, sliders, toolbars;
    left = 0;
    right = 0;
    toolbars = manager.state.toolbars;
    sliders = manager.state.popoutSliders;
    if (sliders.left) {
      left += 42;
    }
    if (sliders.right) {
      right += 42;
    }
    if (toolbars.right.length) {
      right += manager.state.toolbarWidth;
    }
    if (toolbars.rightActive) {
      right += manager.state.panelWidth;
    }
    if (toolbars.left.length) {
      left += manager.state.toolbarWidth;
    }
    if (toolbars.leftActive) {
      left += manager.state.panelWidth;
    }
    return {
      activeSliders: sliders.right + sliders.left,
      right: right,
      left: left
    };
  },
  componentDidMount: function() {
    manager.on("toolbars", (function(_this) {
      return function() {
        return _this.setState(_this.getInitialState());
      };
    })(this));
    return manager.on("popoutSliders", (function(_this) {
      return function() {
        return _this.setState(_this.getInitialState());
      };
    })(this));
  },
  componentDidUpdate: function(prevProps, prevState) {
    var base;
    if (prevState.activeSliders < this.state.activeSliders) {
      return typeof (base = this.props).sliderActivated === "function" ? base.sliderActivated(this.state.activeSliders) : void 0;
    }
  },
  render: function() {
    var className, style;
    if (this.props.rotated) {
      style = {
        top: this.state.left + 'px',
        bottom: this.state.right + 'px'
      };
    } else {
      style = {
        left: this.state.left + 'px',
        right: this.state.right + 'px'
      };
    }
    className = "view-container " + this.props.className;
    return (
      React.createElement("div", {className: className, style: style}, 
        this.props.children
      )
    );
  }
});

module.exports = ViewContainer;


}).call(this,require("react"))
},{"../manager.coffee":338,"react":215,"react-addons-pure-render-mixin":32}],297:[function(require,module,exports){
(function (React){
var Controls, CurvesOverlay, Histogram, Icon, Prompt, StatusOverlay;

Histogram = require("./histogram.coffee");

Icon = require("./icon.coffee");

CurvesOverlay = require("./curves-overlay.coffee");

StatusOverlay = require("./status-overlay.coffee");

Prompt = require("./onetime-prompt.coffee");

Controls = React.createClass({displayName: "Controls",
  mixins: [require("react-addons-pure-render-mixin")],
  stopPropagation: function(e) {
    return e.stopPropagation();
  },
  render: function() {
    return (
      React.createElement("div", {className: "controls"}, 
        React.createElement("div", {id: "face-controls"}), 
        React.createElement("div", {id: "text-controls"}), 
        React.createElement("div", {id: "local-adjustments", onTouchStart: this.stopPropagation, onMouseDown: this.stopPropagation}), 
        React.createElement("div", {id: "crop-controls", onTouchStart: this.stopPropagation, onMouseDown: this.stopPropagation}), 
        React.createElement(CurvesOverlay, {rotated: this.props.portraitMode, style: {right: this.props.right + "px"}}), 
        React.createElement(Histogram, {left: this.props.left, right: this.props.right, top: this.props.top, rotated: this.props.portraitMode}), 
        React.createElement(StatusOverlay, {left: this.props.left, right: this.props.right, top: this.props.top}), 
        React.createElement(Prompt, {left: this.props.left, right: this.props.right, top: this.props.top})
      )
    );
  }
});

module.exports = Controls;


}).call(this,require("react"))
},{"./curves-overlay.coffee":248,"./histogram.coffee":260,"./icon.coffee":262,"./onetime-prompt.coffee":269,"./status-overlay.coffee":287,"react":215,"react-addons-pure-render-mixin":32}],298:[function(require,module,exports){
(function (React){
var PointerEvents, shapeMixin;

PointerEvents = require("./pointer-events.coffee");

shapeMixin = {
  contextTypes: {
    width: React.PropTypes.number,
    height: React.PropTypes.number,
    active: React.PropTypes.bool
  },
  renderChildren: function(props) {
    return React.Children.map(props.children, function(child, index) {
      return React.cloneElement(child, {
        index: props.index + "." + index,
        disabled: props.disabled
      });
    }, this);
  },
  renderShape: function(style) {
    if (this.props.disabled) {
      style.opacity = 0.5;
      style.pointerEvents = "none";
    }
    return (
      React.createElement("div", {style: style, "data-index": this.props.index}, 
        this.renderChildren(this.props)
      )
    );
  }
};

exports.Edge = React.createClass({displayName: "Edge",
  render: function() {
    var left, marginLeft, style, width;
    switch (this.props.side) {
      case "left":
        left = 0;
        width = 7;
        marginLeft = -4;
        break;
      case "right":
        left = "100%";
        width = 7;
        marginLeft = -3;
    }
    style = {
      position: "absolute",
      left: left,
      top: '8px',
      bottom: '8px',
      width: width,
      marginLeft: marginLeft,
      border: "1px solid rgba(0,0,0,0.3)",
      boxSizing: "border-box",
      borderRadius: width + "px",
      background: this.props.fill,
      backgroundClip: "content-box",
      pointerEvents: this.props.pointerEvents,
      cursor: "ew-resize"
    };
    return React.createElement("div", {className: "handle", style: style, "data-index": this.props.index});
  }
});

exports.Handle = React.createClass({displayName: "Handle",
  render: function() {
    var border, position, size, style;
    position = this.props.position || [0, 0];
    if (this.props.outline) {
      border = "1px solid " + this.props.outline;
      size = 20;
    } else {
      border = "1px solid rgba(0,0,0,0.1)";
      size = 20 + 1;
    }
    style = {
      position: "absolute",
      left: position[0] * 100 + "%",
      top: position[1] * 100 + "%",
      width: size,
      height: size,
      marginTop: -size / 2,
      marginLeft: -size / 2,
      border: border,
      borderRadius: "50%",
      boxSizing: "border-box",
      background: this.props.fill,
      backgroundClip: "content-box",
      pointerEvents: this.props.pointerEvents
    };
    if (this.props.disabled) {
      style.opacity = 0;
      style.pointerEvents = "none";
    }
    return React.createElement("div", {className: "handle", style: style, "data-index": this.props.index});
  }
});

exports.Line = React.createClass({displayName: "Line",
  mixins: [shapeMixin],
  render: function() {
    var position, style, transform;
    position = this.props.position || [0, 0];
    transform = "rotate(" + this.props.angle + "deg)";
    style = {
      position: "absolute",
      left: position[0] * 100 + "%",
      top: position[1] * 100 + "%",
      marginLeft: -this.context.width / 2,
      width: this.context.width,
      height: 1,
      background: this.props.fill || "white",
      pointerEvents: "none",
      WebkitTransform: transform,
      transform: transform
    };
    return this.renderShape(style);
  }
});

exports.Circle = React.createClass({displayName: "Circle",
  mixins: [shapeMixin],
  render: function() {
    var angle, height, outline, ref1, style, transform, width, x, y;
    ref1 = this.props, x = ref1.x, y = ref1.y, width = ref1.width, height = ref1.height, angle = ref1.angle, outline = ref1.outline;
    if (angle == null) {
      angle = 0;
    }
    x -= width / 2;
    y -= height / 2;
    x *= this.context.width;
    y *= this.context.height;
    width *= this.context.width;
    height *= this.context.height;
    transform = "translate(" + x + "px, " + y + "px) rotate(" + angle + "deg)";
    style = {
      position: "absolute",
      left: 0,
      top: 0,
      WebkitTransform: transform,
      transform: transform,
      width: width,
      height: height,
      border: this.props.border,
      borderRadius: "100%",
      pointerEvents: this.context.active ? "all" : "none",
      boxSizing: "border-box"
    };
    if (outline) {
      style.boxShadow = "0 0 0 1px rgba(0,0,0,0.3)";
    }
    return this.renderShape(style);
  }
});

exports.Rect = React.createClass({displayName: "Rect",
  mixins: [shapeMixin],
  render: function() {
    var height, ref1, style, transform, width, x, y;
    ref1 = this.props, x = ref1.x, y = ref1.y, width = ref1.width, height = ref1.height;
    x *= this.context.width;
    y *= this.context.height;
    width *= this.context.width;
    height *= this.context.height;
    transform = "translate(" + x + "px, " + y + "px)";
    style = {
      position: "absolute",
      left: 0,
      top: 0,
      WebkitTransform: transform,
      transform: transform,
      width: width,
      height: height,
      border: this.props.border,
      borderRadius: this.props.borderRadius + "px",
      pointerEvents: this.context.active ? "all" : "none",
      boxSizing: "border-box"
    };
    return this.renderShape(style);
  }
});

exports.TextBox = React.createClass({displayName: "TextBox",
  mixins: [shapeMixin],
  getDefaultProps: function() {
    return {
      text: ""
    };
  },
  getInitialState: function() {
    return {
      focused: false,
      text: ""
    };
  },
  componentDidMount: function() {
    return this.setText(this.props);
  },
  componentDidUpdate: function(prevProps, prevState) {
    var box, height, hidden, padding, ref1, scale, textbox, width;
    ref1 = this.refs, textbox = ref1.textbox, box = ref1.box, hidden = ref1.hidden;
    if (textbox != null) {
      padding = (CONFIG.os = "ios") ? 7 : 0;
      textbox.style.width = hidden.scrollWidth + padding + "px";
    }
    if (this.props.type === "artwork") {
      scale = this.props.scale || [1, 1];
      width = Math.abs(hidden.clientWidth * scale[0]) + "px";
      height = Math.abs(hidden.clientHeight * scale[1]) + "px";
    } else {
      width = hidden.clientWidth + "px";
      height = hidden.clientHeight + "px";
    }
    if (this.props.wrap) {
      box.style.minWidth = width;
    } else {
      box.style.width = width;
    }
    box.style.height = height;
    if ((textbox != null ? textbox.scrollTop : void 0) > 0) {
      textbox.scrollTop = 0;
    }
    if (this.state.focused && !prevState.focused) {
      textbox.focus();
      return textbox.setSelectionRange(0, 9999);
    }
  },
  componentWillReceiveProps: function(nextProps) {
    var propsToTest;
    if (nextProps.mode === "artwork") {
      return this.setState({
        text: nextProps.text
      });
    } else {
      propsToTest = {
        font_name: true,
        font_size: true,
        letter_spacing: true,
        text: true,
        wrap: true
      };
      if (!Filtr.utils.deepEqual(this.props, nextProps, true, propsToTest)) {
        return this.setText(nextProps);
      }
    }
  },
  setText: function(props) {
    var lines, style;
    style = Filtr.text.getStyle(props);
    lines = Filtr.text.getLines(props.text, style);
    return this.setState({
      text: lines.join("\n")
    });
  },
  focus: function() {
    return this.setState({
      focused: true
    });
  },
  onBlur: function(e) {
    var base;
    this.setState({
      focused: false
    });
    return typeof (base = this.props).onBlur === "function" ? base.onBlur(e, this) : void 0;
  },
  onFocus: function(e) {
    var base;
    return typeof (base = this.props).onFocus === "function" ? base.onFocus(e, this) : void 0;
  },
  onChange: function(e) {
    var base;
    return typeof (base = this.props).onChange === "function" ? base.onChange(e, this) : void 0;
  },
  preventDrag: function(e) {
    e.preventDefault();
    return false;
  },
  render: function() {
    var b, bottomExtra, color, fontSize, g, height, hidden, letterSpacing, lineHeight, maxWidth, opacity, padding, r, ratio, ref1, ref2, ref3, scale, shadowBlur, shadowColor, shadowX, shadowY, style, textShadow, textarea, textbox, transform, whiteSpace, width, x, y, zIndex;
    ref1 = this.props.position || [0.5, 0.5], x = ref1[0], y = ref1[1];
    ref2 = Filtr._.texture, width = ref2.width, height = ref2.height;
    ratio = width / height;
    y = ((y - 0.5) * 2 * ratio) / 2 + 0.5;
    x *= this.context.width;
    y *= this.context.height;
    opacity = this.props.active ? 1 : 0;
    zIndex = this.props.active ? 2 : 1;
    if (this.props.type === "artwork") {
      fontSize = this.props.font_size * this.context.width * 0.3;
      lineHeight = this.props.line_height;
      letterSpacing = "0em";
      padding = 0;
      bottomExtra = 0;
    } else {
      scale = this.props.scale || [1, 1];
      fontSize = this.props.font_size * this.context.width * 0.1 * scale[0];
      lineHeight = this.props.line_height;
      letterSpacing = this.props.letter_spacing + "em";
      padding = fontSize * 0.2;
      bottomExtra = Math.max(0, 1.2 - lineHeight) * fontSize;
      if (this.props.wrap) {
        maxWidth = (this.props.wrap - (padding / this.context.width) * 2) * 100 + "%";
      }
    }
    whiteSpace = this.props.wrap ? "pre-line" : "pre";
    ref3 = this.props.color, r = ref3[0], g = ref3[1], b = ref3[2];
    color = "rgba(" + (r * 255 >> 0) + ", " + (g * 255 >> 0) + ", " + (b * 255 >> 0) + ", " + this.props.opacity + ")";
    if (this.props.shadow_opacity) {
      shadowX = this.props.shadow_offset[0] * fontSize;
      shadowY = this.props.shadow_offset[1] * fontSize;
      shadowBlur = this.props.shadow_blur * fontSize;
      shadowColor = "rgba(" + (this.props.shadow_color.join(",")) + ", " + this.props.shadow_opacity + ")";
      textShadow = shadowX + "px " + shadowY + "px " + shadowBlur + "px " + shadowColor;
    }
    transform = "translate(-50%, -50%) rotate(" + (this.props.angle + this.props.orientation) + "deg)";
    style = {
      opacity: opacity,
      position: "absolute",
      left: x + "px",
      top: y + "px",
      fontSize: fontSize + "px",
      fontFamily: this.props.font_family,
      fontWeight: this.props.font_weight,
      fontStyle: this.props.font_style,
      lineHeight: lineHeight,
      letterSpacing: letterSpacing,
      WebkitTransform: transform,
      transform: transform,
      pointerEvents: this.context.active ? "all" : "none",
      border: this.props.border,
      outline: "1px solid rgba(0,0,0,0.3)",
      whiteSpace: "nowrap",
      textAlign: "center",
      width: maxWidth,
      paddingLeft: padding + "px",
      paddingRight: padding + "px",
      zIndex: zIndex
    };
    textbox = {
      background: "transparent",
      display: "block",
      position: "absolute",
      top: 0,
      left: "50%",
      width: "100%",
      height: "100%",
      border: "none",
      padding: 0,
      resize: "none",
      fontFamily: "inherit",
      fontSize: "inherit",
      fontWeight: "inherit",
      fontStyle: "inherit",
      lineHeight: "inherit",
      letterSpacing: "inherit",
      textAlign: this.props.align,
      textAlignLast: this.props.align,
      overflow: "hidden",
      color: color,
      textShadow: textShadow,
      whiteSpace: whiteSpace,
      transform: "translateX(-50%)",
      WebkitTransform: "translateX(-50%)"
    };
    hidden = {
      visibility: "hidden",
      display: "inline-block",
      lineHeight: "inherit",
      letterSpacing: "inherit",
      textAlign: this.props.align,
      textAlignLast: this.props.align,
      paddingBottom: bottomExtra + "px",
      pointerEvents: "none",
      whiteSpace: "pre",
      float: "left"
    };
    if (this.state.focused) {
      textarea = React.createElement("textarea", {
        ref: "textbox", 
        style: textbox, 
        "data-index": this.props.index, 
        onChange: this.onChange, 
        onBlur: this.onBlur, 
        onFocus: this.onFocus, 
        onDragStart: this.preventDrag, 
        value: this.props.text}
        );
    }
    return (
      React.createElement("div", {
        ref: "box", 
        className: "text-box", 
        "data-index": this.props.index, 
        style: style}, 
        textarea, 
        React.createElement("div", {ref: "hidden", style: hidden}, this.state.text, React.createElement("br", null)), 
        this.renderChildren(this.props)
      )
    );
  }
});

exports.ViewportOverlay = React.createClass({displayName: "ViewportOverlay",
  mixins: [require("react-addons-pure-render-mixin")],
  getChildContext: function() {
    return {
      width: this.width,
      height: this.height,
      active: this.props.active
    };
  },
  childContextTypes: {
    width: React.PropTypes.number,
    height: React.PropTypes.number,
    active: React.PropTypes.bool
  },
  getInitialState: function() {
    var center, crop, cropScale, flipX, flipY, height, position, ref1, ref2, rotate90, rotation, screen, width, zoom;
    ref1 = Filtr._, rotate90 = ref1.rotate90, rotation = ref1.rotation, cropScale = ref1.cropScale, screen = ref1.screen, flipX = ref1.flipX, flipY = ref1.flipY;
    ref2 = Filtr._.texture, width = ref2.width, height = ref2.height;
    crop = Filtr.getState().crop;
    position = screen.offset;
    zoom = screen.zoom;
    center = screen.center;
    return {
      zoom: zoom,
      cropScale: cropScale,
      rotation: rotation,
      rotate90: rotate90,
      width: width,
      height: height,
      x: position[0],
      y: position[1],
      ox: center[0],
      oy: center[1],
      cx: crop[0],
      cy: crop[1],
      cw: crop[2],
      ch: crop[3],
      flipX: flipX,
      flipY: flipY
    };
  },
  componentDidMount: function() {
    return Filtr.on("render", this.update);
  },
  componentWillUnmount: function() {
    return Filtr.off("render", this.update);
  },
  update: function() {
    return this.setState(this.getInitialState());
  },
  getComponent: function(ref) {
    var address;
    if (ref == null) {
      return this;
    }
    address = ref.split(".");
    return address.reduce(function(prev, curr) {
      var index;
      index = Number(curr);
      return React.Children.toArray(prev.props.children)[index];
    }, this);
  },
  onPointerDown: function(e, input) {
    var base;
    if (!this.props.disabled) {
      e.stopPropagation();
    }
    this.component = this.getComponent(e.target.dataset.index);
    return typeof (base = this.props).onPointerDown === "function" ? base.onPointerDown(e, input, this.component) : void 0;
  },
  onPointerMove: function(e, input) {
    var base, base1, base2;
    if (typeof (base = this.props).onPointerMove === "function") {
      base.onPointerMove(e, input, this.component);
    }
    if (!input.dragging) {
      if (typeof (base1 = this.props).onDragStart === "function") {
        base1.onDragStart(e, input, this.component);
      }
    }
    return typeof (base2 = this.props).onDrag === "function" ? base2.onDrag(e, input, this.component) : void 0;
  },
  onPointerUp: function(e, input) {
    var base, base1;
    if (typeof (base = this.props).onPointerUp === "function") {
      base.onPointerUp(e, input, this.component);
    }
    if (input.dragging) {
      return typeof (base1 = this.props).onDragEnd === "function" ? base1.onDragEnd(e, input, this.component) : void 0;
    }
  },
  onLongPress: function(e, input) {
    var base;
    return typeof (base = this.props).onLongPress === "function" ? base.onLongPress(e, input, this.component) : void 0;
  },
  onDoubleTap: function(e, input) {
    var base;
    return typeof (base = this.props).onDoubleTap === "function" ? base.onDoubleTap(e, input, this.component) : void 0;
  },
  render: function() {
    var flipX, flipY, ox, oy, props, rotation, size, style, transform, tx, ty;
    if (!Filtr._.photoLoaded || !this.props.active) {
      return null;
    }
    size = Filtr.getImageSize();
    this.width = this.state.width / Filtr.pixelRatio * this.state.zoom / this.state.cropScale;
    this.height = this.state.height / Filtr.pixelRatio * this.state.zoom / this.state.cropScale;
    rotation = this.state.rotation + this.state.rotate90 * 90;
    flipX = this.state.flipX ? -1 : 1;
    flipY = this.state.flipY ? -1 : 1;
    if (Filtr._.cropMode) {
      ox = oy = 0;
    } else {
      ox = ((1 - this.state.cw) / 2 - this.state.cx) * size[0] * this.state.cropScale * this.state.zoom;
      oy = ((1 - this.state.ch) / 2 - this.state.cy) * size[1] * this.state.cropScale * this.state.zoom;
    }
    tx = -0.5 + (-this.state.x + ox + this.state.ox) / this.width / Filtr.pixelRatio;
    ty = -0.5 + (this.state.y + oy + this.state.oy) / this.height / Filtr.pixelRatio;
    transform = ("translate(" + (tx * 100) + "%, " + (ty * 100) + "%) rotate(") + rotation + ("deg) scale(" + flipX + ", " + flipY + ")");
    style = {
      width: this.width,
      height: this.height,
      position: 'absolute',
      left: '50%',
      top: '50%',
      WebkitTransform: transform,
      transform: transform
    };
    props = {};
    if (this.props.passthrough) {
      props["data-passthrough"] = true;
    }
    return (
      React.createElement(PointerEvents, {
        threshold: 5, 
        multitouch: true, 
        ignoreTouchTarget: true, 
        onLongPress: this.onLongPress, 
        onDoubleTap: this.onDoubleTap, 
        onPointerDown: this.onPointerDown, 
        onPointerMove: this.onPointerMove, 
        onPointerUp: this.onPointerUp}, 
        React.createElement("div", React.__spread({className: "overlay-container"},  props, {style: {width: "100%", height: "100%"}}), 
          React.createElement("div", React.__spread({className: "viewport-overlay"},  props, {style: style}), 
            React.Children.map(this.props.children, function(child, index){
              return React.cloneElement(child, {index: index})
            }, this)
          )
        )
      )
    );
  }
});


}).call(this,require("react"))
},{"./pointer-events.coffee":273,"react":215,"react-addons-pure-render-mixin":32}],299:[function(require,module,exports){
(function (React){
var Button, Controls, PointerEvents, Viewport, actions, io, manager, store, translate, utils,
  slice = [].slice;

Controls = require("./viewport-controls.coffee");

Button = require("./button.coffee");

PointerEvents = require("./pointer-events.coffee");

translate = require("counterpart");

manager = require("../manager.coffee");

actions = require("../actions.coffee");

utils = require("../utils.coffee");

store = require("../store.coffee");

io = require("../io.coffee");

Viewport = React.createClass({displayName: "Viewport",
  mixins: [require("react-addons-pure-render-mixin")],
  destination: [0, 0, 0, 0],
  getInitialState: function() {
    var cropMode, left, margin, right, toolbars;
    left = 0;
    right = 0;
    margin = 2;
    toolbars = manager.state.toolbars;
    cropMode = toolbars.leftActive === "crop" || toolbars.rightActive === "crop";
    if (toolbars.right.length) {
      right += manager.state.toolbarWidth;
    }
    if (toolbars.rightActive) {
      right += manager.state.panelWidth;
    }
    if (toolbars.left.length) {
      left += manager.state.toolbarWidth;
    }
    if (toolbars.leftActive) {
      left += manager.state.panelWidth;
    }
    return {
      right: right,
      left: left,
      margin: margin,
      cropMode: cropMode
    };
  },
  componentDidMount: function() {
    var ref, ref1, viewport;
    viewport = this.refs.viewport;
    viewport.appendChild(Filtr.canvas);
    this.resize();
    manager.on("toolbars", (function(_this) {
      return function() {
        return _this.setState(_this.getInitialState());
      };
    })(this));
    viewport.addEventListener("wheel", this.handleWheel);
    window.addEventListener("resize", this.resize);
    if ((ref = store.manager) != null) {
      ref.waitForMessage("ppe.window.endLiveResize", (function(_this) {
        return function(res) {
          return _this.resize();
        };
      })(this));
    }
    return (ref1 = store.manager) != null ? ref1.waitForMessage("ppe.window.magnificationChanged", (function(_this) {
      return function(res) {
        return _this.zoom(res.delta * -250);
      };
    })(this)) : void 0;
  },
  componentDidUpdate: function() {
    return this.resize();
  },
  resize: function() {
    var bottom, duration, height, left, m, margins, marginsChanged, minZoom, modeChanged, orientation, orientationChanged, ref, right, sizeChanged, top, width, zoom;
    if (this.touching) {
      return;
    }
    top = 0 + this.state.margin;
    bottom = 0 + this.state.margin;
    left = this.state.left + this.state.margin;
    right = this.state.right + this.state.margin;
    if (CONFIG.embedded && CONFIG.platform === "mobile") {
      if (this.props.portraitMode) {
        left += 44;
      } else {
        top += 32;
      }
    }
    this.cropMode = Filtr._.cropMode;
    if (this.cropMode) {
      left = Math.max(50, left);
    }
    orientation = 0;
    if (this.props.portraitMode) {
      ref = [left, top, right, bottom], top = ref[0], right = ref[1], bottom = ref[2], left = ref[3];
      orientation = -90;
    }
    m = Filtr._.margins;
    Filtr.canvas.style.width = window.innerWidth + 'px';
    Filtr.canvas.style.height = window.innerHeight + 'px';
    width = window.innerWidth * Filtr.pixelRatio;
    height = window.innerHeight * Filtr.pixelRatio;
    marginsChanged = !(m[0] === left && m[1] === top && m[2] === right && m[3] === bottom);
    sizeChanged = !(width === Filtr.canvas.width && height === Filtr.canvas.height);
    modeChanged = Filtr._cropMode !== this.cropMode;
    orientationChanged = orientation !== Filtr._.orientation;
    if (!marginsChanged && !sizeChanged && !modeChanged) {
      return;
    }
    zoom = Filtr.getZoom();
    minZoom = Filtr.getMinZoom();
    margins = [left, top, right, bottom];
    if (sizeChanged) {
      Filtr.resize(width, height);
      Filtr.set({
        margins: margins,
        orientation: orientation
      });
    }
    duration = orientationChanged ? 0 : 0.3;
    if (marginsChanged || modeChanged) {
      return Filtr.set({
        margins: margins,
        zoom: zoom <= minZoom || this.cropMode ? 0 : zoom,
        orientation: orientation
      }, duration);
    }
  },
  zoom: function(delta) {
    var curPos, curZoom, origin, position, scale, y, zoom;
    curZoom = Filtr.getZoom();
    delta = utils.clamp(delta, -10, 10);
    y = delta * curZoom;
    zoom = utils.clamp(curZoom - y / 100, Filtr.getMinZoom(), 3);
    curPos = Filtr.getPosition();
    scale = curZoom / zoom;
    origin = this.origin || [0, 0];
    position = [(origin[0] + curPos[0]) / scale - origin[0], (origin[1] + curPos[1]) / scale - origin[1]];
    return Filtr.set({
      position: this.limitPosition.apply(this, slice.call(position).concat([zoom])),
      zoom: zoom
    });
  },
  toggleZoom: function() {
    var curPos, minZoom, origin, position, zoom;
    zoom = Filtr.getZoom();
    minZoom = Filtr.getMinZoom();
    if (zoom === 1) {
      return Filtr.set({
        zoom: minZoom,
        position: [0, 0]
      }, 0.3);
    } else {
      curPos = Filtr.getPosition();
      origin = this.origin || [0, 0];
      position = [(origin[0] + curPos[0]) / zoom - origin[0], (origin[1] + curPos[1]) / zoom - origin[1]];
      return Filtr.set({
        zoom: 1,
        position: this.limitPosition.apply(this, slice.call(position).concat([1]))
      }, 0.3);
    }
  },
  showBefore: function() {
    this.originalActive = true;
    Filtr.render(true);
    return actions.showStatus(translate("labels.original"), 1000);
  },
  showAfter: function() {
    if (!this.originalActive) {
      return;
    }
    Filtr.render(false);
    actions.hideStatus();
    return this.originalActive = false;
  },
  galleryBegin: function() {
    var beginCoords, ref;
    this.galleryStarted = true;
    beginCoords = Filtr.getCoordinates(this.props.portraitMode);
    return (ref = store.manager) != null ? ref.sendMessage("ppe.gallery.begin", {
      identifier: store.currentPhoto,
      coordinates: beginCoords
    }, (function(_this) {
      return function(res) {
        return _this.destination = res.destination;
      };
    })(this)) : void 0;
  },
  galleryUpdate: function(coords, opacity) {
    var ref;
    if ((ref = store.manager) != null) {
      ref.sendMessage("ppe.gallery.update", {
        coordinates: coords,
        opacity: opacity
      });
    }
    if (opacity < 1) {
      return actions.hideUI();
    }
  },
  galleryEnd: function(cancel) {
    var beginCoords, beginOpacity, beginZoom, canvas, center, cx, cy, dest, done, props, size, update, zoom;
    this.galleryStarted = false;
    done = function() {
      var ref;
      actions.showUI();
      if ((ref = store.manager) != null) {
        ref.sendMessage("ppe.gallery.end", {
          cancelled: cancel
        });
      }
      if (!cancel) {
        return setTimeout(function() {
          return Filtr.set({
            zoom: 0,
            position: [0, 0]
          });
        }, 100);
      }
    };
    update = (function(_this) {
      return function() {
        var coords, opacity, t;
        t = (Filtr._.screen.zoom - beginZoom) / (props.zoom - beginZoom);
        coords = [beginCoords[0] * (1 - t) + _this.destination[0] * t, beginCoords[1] * (1 - t) + _this.destination[1] * t, beginCoords[2] * (1 - t) + _this.destination[2] * t, beginCoords[3] * (1 - t) + _this.destination[3] * t];
        opacity = beginOpacity * (1 - t);
        return _this.galleryUpdate(coords, opacity);
      };
    })(this);
    if (cancel) {
      return done();
    } else {
      beginZoom = Filtr.getZoom();
      beginOpacity = beginZoom / this.minZoom;
      beginCoords = Filtr.getCoordinates(this.props.portraitMode);
      canvas = Filtr.getCanvasSize();
      center = Filtr.getCenter();
      size = Filtr._.crop;
      if (this.cancelGallery) {
        cancel = true;
        Filtr.set(this.endProps, 0.2, done, (function(_this) {
          return function(val) {
            var coords, opacity;
            coords = Filtr.getCoordinates(_this.props.portraitMode);
            opacity = Math.min(val[0] / _this.minZoom, 1);
            return _this.galleryUpdate(coords, opacity);
          };
        })(this));
        return;
      } else if (this.destination[2] === 0 && this.destination[3] === 0) {
        this.destination = [canvas[0] / 4, canvas[1] / 4, 1, 1];
        cx = center[0];
        cy = -center[1];
        props = {
          zoom: 0.0001,
          position: [cx, cy]
        };
      } else {
        dest = [this.destination[0] * 2 - canvas[0] / 2, this.destination[1] * 2 - canvas[1] / 2, this.destination[2] * 2, this.destination[3] * 2];
        zoom = Math.max(dest[2] / size[2], dest[3] / size[3]);
        cx = (-dest[0] - dest[2] / 2) + center[0];
        cy = (dest[1] + dest[3] / 2) - center[1];
        props = {
          zoom: zoom,
          position: [cx, cy]
        };
      }
      return Filtr.set(props, 0.2, done, update);
    }
  },
  limitPosition: function(x, y, z) {
    var canvas, crop, cx, cy, height, margin, margins, mx, my, ref, viewHeight, viewWidth, width, zoom;
    ref = Filtr._, margins = ref.margins, margin = ref.margin, zoom = ref.zoom, crop = ref.crop;
    zoom = z || zoom;
    canvas = this.canvasSize || Filtr.getCanvasSize();
    viewWidth = canvas[0] - (margins[0] + margins[2] + margin) * Filtr.pixelRatio;
    viewHeight = canvas[1] - (margins[1] + margins[3] + margin) * Filtr.pixelRatio;
    width = Math.max(crop[2] * zoom, viewWidth);
    height = Math.max(crop[3] * zoom, viewHeight);
    mx = Math.abs(width - viewWidth) / 2;
    my = Math.abs(height - viewHeight) / 2;
    cx = utils.clamp(x, -mx, mx);
    cy = utils.clamp(y, -my, my);
    return [cx, cy];
  },
  setOrigin: function(x, y) {
    var ref;
    if (this.props.portraitMode) {
      ref = [-y, x], x = ref[0], y = ref[1];
    }
    if (!this.viewCenter) {
      this.setStart();
    }
    return this.origin = [(x - this.viewWidth / 2) * Filtr.pixelRatio - this.viewCenter[0], (this.viewHeight / 2 - y) * Filtr.pixelRatio - this.viewCenter[1]];
  },

  /* handlers */
  handleWheel: function(e) {
    e.preventDefault();
    this.setOrigin(e.pageX, e.pageY);
    return this.zoom(e.deltaY);
  },
  setStart: function() {
    this.touching = true;
    this.minZoom = Filtr.getMinZoom();
    this.startZoom = Filtr.getZoom();
    this.canvasSize = Filtr.getCanvasSize();
    this.startPos = Filtr.getPosition();
    this.viewCenter = Filtr.getCenter();
    this.viewWidth = window.innerWidth;
    this.viewHeight = window.innerHeight;
    if (this.props.portraitMode) {
      this.viewWidth = -window.innerHeight;
      this.viewHeight = window.innerWidth;
      return this.viewCenter[1] = -this.viewCenter[1];
    }
  },
  touchStart: function(e, input) {
    e.preventDefault();
    if (Filtr._.cropMode || !store.currentPhoto) {
      return false;
    }
    this.setStart();
    if (CONFIG.enableGalleryTransitions && input.multitouch) {
      return this.galleryBegin();
    }
  },
  touchMove: function(e, input) {
    var coords, curZoom, cx, cy, deltaX, deltaY, dx, dy, h, multitouch, opacity, origin, px, py, ref, ref1, ref2, ref3, scale, x, y, z, zoom;
    this.zoomOut = false;
    if (!input.dragging) {
      this.setStart();
    }
    deltaX = input.deltaX, deltaY = input.deltaY;
    if (this.props.portraitMode) {
      ref = [-deltaY, deltaX], deltaX = ref[0], deltaY = ref[1];
    }
    multitouch = input.multitouch;
    this.showAfter();
    curZoom = zoom = Filtr.getZoom();
    if (multitouch) {
      dx = input.centerDelta[0] * Filtr.pixelRatio;
      dy = -input.centerDelta[1] * Filtr.pixelRatio;
      ref1 = input.center, cx = ref1[0], cy = ref1[1];
      if (this.props.portraitMode) {
        ref2 = [dy, -dx], dx = ref2[0], dy = ref2[1];
      }
      origin = this.setOrigin(cx, cy);
      zoom = this.startZoom * input.distanceDelta;
      zoom = Math.min(zoom, 5);
      this.toGallery = false;
      if (zoom < this.minZoom) {
        if (this.startZoom <= this.minZoom && CONFIG.enableGalleryTransitions) {
          this.toGallery = true;
          this.cancelGallery = zoom > curZoom;
        } else {
          z = this.minZoom - zoom;
          zoom = this.minZoom - z / 4;
          dx = dx * (1 - z);
          dy = dy * (1 - z);
        }
      }
    } else {
      dx = deltaX * Filtr.pixelRatio;
      dy = -deltaY * Filtr.pixelRatio;
    }
    x = this.startPos[0] - dx;
    y = this.startPos[1] - dy;
    if (origin) {
      scale = this.startZoom / zoom;
      x = (origin[0] + x) / scale - origin[0];
      y = (origin[1] + y) / scale - origin[1];
    }
    ref3 = this.limitPosition(x, y, zoom), cx = ref3[0], cy = ref3[1];
    px = cx + (x - cx) / 2;
    py = cy + (y - cy) / 2;
    if (CONFIG.enableGalleryTransitions) {
      if (!multitouch && this.startZoom === this.minZoom && y > 40) {
        if (!this.galleryStarted) {
          this.galleryBegin();
        }
        py = y - 20;
        h = this.viewHeight * 2;
        zoom = this.startZoom * ((h - (y - 40)) / h);
        this.cancelGallery = zoom > curZoom;
        this.toGallery = true;
      }
    }
    Filtr.set({
      zoom: zoom,
      position: [px, py]
    });
    if (this.toGallery && CONFIG.enableGalleryTransitions) {
      coords = Filtr.getCoordinates(this.props.portraitMode);
      opacity = Math.min(zoom / this.minZoom, 1);
      return this.galleryUpdate(coords, opacity);
    }
  },
  touchEnd: function(e) {
    var pos, zoom;
    pos = Filtr.getPosition();
    zoom = Math.max(Filtr.getZoom(), this.minZoom);
    this.endProps = {
      zoom: zoom,
      position: this.limitPosition(pos[0], pos[1], zoom)
    };
    if (this.toGallery) {
      this.galleryEnd();
    } else {
      Filtr.set(this.endProps, 0.3);
      this.galleryEnd(true);
    }
    this.touching = false;
    this.toGallery = false;
    return this.showAfter();
  },
  doubleTap: function(e, input) {
    var x, y;
    x = input.x, y = input.y;
    this.setOrigin(x, y);
    return this.toggleZoom();
  },
  singleTap: function(e) {
    if (this.props.disabled) {
      return;
    }
    if (e.target === Filtr.canvas || e.target.dataset.passthrough) {
      return manager.set("histogram", !manager.state.histogram);
    }
  },
  longPress: function(e) {
    if (e.target === Filtr.canvas || e.target.dataset.passthrough) {
      return this.showBefore();
    }
  },
  openSampleImage: function() {
    return store.loadSampleImage();
  },
  openImage: function() {
    return io.open();
  },
  tap: function(e, input) {
    var a, b, color, g, h, r, ref, s, x, y;
    x = input.x, y = input.y;
    x *= Filtr.pixelRatio;
    y *= Filtr.pixelRatio;
    color = Filtr.readPixels(x, y, 1, 1);
    r = color[0], g = color[1], b = color[2], a = color[3];
    function rgbToHsv(r, g, b){
      r = r/255, g = g/255, b = b/255;
      var max = Math.max(r, g, b), min = Math.min(r, g, b);
      var h, s, v = max;

      var d = max - min;
      s = max == 0 ? 0 : d / max;

      if(max == min){
        h = 0; // achromatic
      }else{
        switch(max){
          case r: h = (g - b) / d + (g < b ? 6 : 0); break;
          case g: h = (b - r) / d + 2; break;
          case b: h = (r - g) / d + 4; break;
        }
        h /= 6;
      }

      return [h, s, v];
    };
    ref = rgbToHsv(r, g, b), h = ref[0], s = ref[1];
    console.log(h, s);
    this.refs.pixel.style.backgroundColor = "hsl(" + (h * 360) + ", " + (s * 100) + "%, 50%)";
    Filtr.setState({
      white_balance: [h, s]
    });
    return Filtr.saveState();
  },
  render: function() {
    var left, openPhotos, pixelStyle, right, top;
    pixelStyle = {
      width: 34,
      height: 34,
      position: 'absolute',
      top: 0,
      left: "50%"
    };
    left = this.state.left;
    right = this.state.right;
    top = 0;
    if (CONFIG.embedded && CONFIG.platform === "mobile") {
      if (this.props.portraitMode) {
        left += 44;
      } else {
        top += 32;
      }
    }
    if (!CONFIG.embedded) {
      openPhotos = 
        React.createElement("div", {className: this.props.disabled ? "no-photos-screen" : "no-photos-screen hidden"}, 
          React.createElement(Button, {tabIndex: "-1", className: "alt", onTap: this.openImage}, translate("buttons.import_photos")), 
          React.createElement("div", {className: "sep"}), 
          React.createElement(Button, {tabIndex: "-1", className: "alt", onTap: this.openSampleImage}, translate("buttons.open_sample"))
        )
      ;
    }
    return (
      React.createElement(PointerEvents, {
        threshold: 5, 
        multitouch: true, 
        onPointerDown: this.touchStart, 
        onPointerMove: this.touchMove, 
        onPointerUp: this.touchEnd, 
        onDoubleTap: this.doubleTap, 
        onSingleTap: this.singleTap, 
        onLongPress: this.longPress}, 
        React.createElement("div", {className: "viewport", ref: "viewport"}, 
          openPhotos, 
          React.createElement(Controls, {left: left, right: right, top: top, portraitMode: this.props.portraitMode})
        )
      )
    );
  }
});

module.exports = Viewport;


}).call(this,require("react"))
},{"../actions.coffee":229,"../io.coffee":312,"../manager.coffee":338,"../store.coffee":346,"../utils.coffee":360,"./button.coffee":241,"./pointer-events.coffee":273,"./viewport-controls.coffee":297,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],300:[function(require,module,exports){
(function (React){
var Button, Dialog, WatermarkDialog, WatermarkOptions, translate;

Button = require("./button.coffee");

Dialog = require("./dialog.coffee");

WatermarkOptions = require("./watermark-options.coffee");

translate = require("counterpart");

WatermarkDialog = React.createClass({displayName: "WatermarkDialog",
  mixins: [require("react-addons-pure-render-mixin")],
  getInitialState: function() {
    return {
      active: false
    };
  },
  componentDidUpdate: function() {
    return this.refs.watermark.updatePreview();
  },
  show: function() {
    return this.setState({
      active: true
    });
  },
  hide: function() {
    return this.setState({
      active: false
    });
  },
  render: function() {
    return (
      React.createElement(Dialog, React.__spread({ref: "dialog", name: "edit-watermark"},  this.props, {onShow: this.onShow, active: this.state.active}), 
        React.createElement("h1", null, translate("menus.edit_watermark")), 
        React.createElement(WatermarkOptions, {active: this.state.active, ref: "watermark"}), 
        React.createElement("div", {className: "footer"}, 
          React.createElement(Button, {className: "primary", onTap: this.hide}, translate("buttons.done"))
        )
      )
    );
  }
});

module.exports = WatermarkDialog;


}).call(this,require("react"))
},{"./button.coffee":241,"./dialog.coffee":249,"./watermark-options.coffee":301,"counterpart":3,"react":215,"react-addons-pure-render-mixin":32}],301:[function(require,module,exports){
(function (React){
var Button, Dialog, PointerEvents, Slider, WatermarkOptions, classnames, filesystem, storage, store, translate, utils;

classnames = require("classnames");

translate = require("counterpart");

Dialog = require("./dialog.coffee");

Button = require("./button.coffee");

Slider = require("./slider.coffee");

PointerEvents = require("./pointer-events.coffee");

utils = require("../utils.coffee");

store = require("../store.coffee");

storage = require("../storage.coffee");

filesystem = require("../filesystem.coffee");

WatermarkOptions = React.createClass({displayName: "WatermarkOptions",
  getInitialState: function() {
    return Filtr.getWatermark();
  },
  componentDidMount: function() {
    storage.getItem("watermark", (function(_this) {
      return function(success, buffer) {
        var load;
        load = function(buf) {
          var blob;
          blob = new Blob([buf]);
          return _this.loadWatermark(URL.createObjectURL(blob));
        };
        if (buffer) {
          return load(buffer);
        } else {
          return store.readURL((store.getSampleDir()) + "logo-large.png", load);
        }
      };
    })(this));
    return storage.getItem("watermark_options", (function(_this) {
      return function(success, options) {
        if (options) {
          return _this.updateWatermark(options);
        }
      };
    })(this));
  },
  componentDidUpdate: function() {
    this.updatePreview();
    return this.saveWatermark();
  },
  startDrag: function(e) {
    return this.position = this.state.position.slice(0);
  },
  drag: function(e, input) {
    var canvas, pos;
    canvas = this.refs["preview-canvas"];
    pos = {
      x: this.position[0] + input.deltaX * 2 / canvas.clientWidth,
      y: this.position[1] + input.deltaY * 2 / canvas.clientHeight
    };
    if (!input.dragging) {
      utils.snap.start({
        startValue: pos.x
      }, {
        startValue: pos.y
      });
    }
    return this.setPosition(pos);
  },
  updateWatermark: function(options) {
    Filtr.setWatermark(options);
    return this.setState(options);
  },
  saveWatermark: utils.debounce(function() {
    return storage.setItem("watermark_options", Filtr.getWatermark());
  }, 300),
  setPosition: function(pos) {
    var position, x, y;
    x = utils.clamp(pos.x, -1, 1);
    y = utils.clamp(pos.y, -1, 1);
    position = utils.snap.value(x, y);
    return this.updateWatermark({
      position: position
    });
  },
  updatePreview: Filtr.utils.throttle(function() {
    var canvas, max, output, watermark;
    if (this.props.active) {
      max = 250 * Filtr.pixelRatio;
      output = Filtr["export"]("data", {
        width: max,
        height: max,
        resize: "fit",
        watermark: true
      });
      canvas = this.refs["preview-canvas"];
      canvas.width = output.width;
      canvas.height = output.height;
      watermark = this.refs["watermark-image"];
      watermark.style.height = Math.max(48, output.height / Filtr.pixelRatio - 86) + "px";
      return utils.drawImageData(canvas, output);
    }
  }, 1000 / 30),
  loadWatermark: function(url) {
    var image;
    image = this.refs.image;
    image.onload = (function(_this) {
      return function() {
        Filtr.setWatermark({
          image: image
        });
        return _this.updatePreview();
      };
    })(this);
    return image.src = url;
  },
  addImage: function() {
    return filesystem.readFile({
      multiple: false,
      accept: "image/*"
    }, (function(_this) {
      return function(data) {
        var file;
        file = new Blob([data]);
        _this.loadWatermark(URL.createObjectURL(file));
        return storage.setItem("watermark", new Uint8Array(data));
      };
    })(this));
  },
  render: function() {
    var optionsClass;
    optionsClass = classnames("watermark", "options", {
      active: this.props.active
    });
    return (
      React.createElement("div", {className: optionsClass}, 
        React.createElement("div", null, 
          React.createElement("div", {className: "option"}, 
            React.createElement("span", null, translate("labels.watermark_image")), 
            React.createElement("div", {className: "watermark-image", ref: "watermark-image"}, 
              React.createElement("img", {ref: "image"}), 
              React.createElement("button", {type: "button", onTouchTap: this.addImage}, translate("buttons.add_image")), 
              React.createElement("div", {className: "background"})
            )
          ), 
          React.createElement("div", {className: "option"}, 
            React.createElement(Slider, {
              label: "labels.opacity", 
              prop: "opacity", 
              value: this.state.opacity, 
              onUpdate: this.updateWatermark, 
              saveState: false, 
              min: 0}), 
            React.createElement(Slider, {
              label: "labels.size", 
              prop: "scale", 
              value: this.state.scale, 
              onUpdate: this.updateWatermark, 
              saveState: false, 
              min: 0})
          )
        ), 
        React.createElement("div", null, 
          React.createElement("span", null, translate("tooltips.drag_watermark")), 
          React.createElement(PointerEvents, {
            multitouch: true, 
            onPointerDown: this.startDrag, 
            onPointerMove: this.drag}, 
            React.createElement("canvas", {ref: "preview-canvas"})
          ), 
          React.createElement("div", {className: "position"}, this.state.position)
        )
      )
    );
  }
});

module.exports = WatermarkOptions;


}).call(this,require("react"))
},{"../filesystem.coffee":311,"../storage.coffee":343,"../store.coffee":346,"../utils.coffee":360,"./button.coffee":241,"./dialog.coffee":249,"./pointer-events.coffee":273,"./slider.coffee":286,"classnames":1,"counterpart":3,"react":215}],302:[function(require,module,exports){
/* FileSaver.js
 * A saveAs() FileSaver implementation.
 * 1.3.0
 *
 * By Eli Grey, http://eligrey.com
 * License: MIT
 *   See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
 */

/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */

/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */

var saveAs = saveAs || (function(view) {
		"use strict";
		// IE <10 is explicitly unsupported
		if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
			return;
		}
		var
			doc = view.document
		// only get URL when necessary in case Blob.js hasn't overridden it yet
			, get_URL = function() {
				return view.URL || view.webkitURL || view;
			}
			, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
			, can_use_save_link = "download" in save_link
			, click = function(node) {
				var event = new MouseEvent("click");
				node.dispatchEvent(event);
			}
			, is_safari = /constructor/i.test(view.HTMLElement)
			, throw_outside = function(ex) {
				(view.setImmediate || view.setTimeout)(function() {
					throw ex;
				}, 0);
			}
			, force_saveable_type = "application/octet-stream"
		// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
			, arbitrary_revoke_timeout = 1000 * 40 // in ms
			, revoke = function(file) {
				var revoker = function() {
					if (typeof file === "string") { // file is an object URL
						get_URL().revokeObjectURL(file);
					} else { // file is a File
						file.remove();
					}
				};
				setTimeout(revoker, arbitrary_revoke_timeout);
			}
			, dispatch = function(filesaver, event_types, event) {
				event_types = [].concat(event_types);
				var i = event_types.length;
				while (i--) {
					var listener = filesaver["on" + event_types[i]];
					if (typeof listener === "function") {
						try {
							listener.call(filesaver, event || filesaver);
						} catch (ex) {
							throw_outside(ex);
						}
					}
				}
			}
			, auto_bom = function(blob) {
				// prepend BOM for UTF-8 XML and text/* types (including HTML)
				// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
				if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
					return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
				}
				return blob;
			}
			, FileSaver = function(blob, name, no_auto_bom) {
				if (!no_auto_bom) {
					blob = auto_bom(blob);
				}
				// First try a.download, then web filesystem, then object URLs
				var
					filesaver = this
					, type = blob.type
					, force = type === force_saveable_type
					, object_url
					, dispatch_all = function() {
						dispatch(filesaver, "writestart progress write writeend".split(" "));
					}
				// on any filesys errors revert to saving with object URLs
					, fs_error = function() {
						if (force && is_safari && view.FileReader) {
							// Safari doesn't allow downloading of blob urls
							var reader = new FileReader();
							reader.onloadend = function() {
								var base64Data = reader.result;
								view.location.href = "data:attachment/file" + base64Data.slice(base64Data.search(/[,;]/));
								filesaver.readyState = filesaver.DONE;
								dispatch_all();
							};
							reader.readAsDataURL(blob);
							filesaver.readyState = filesaver.INIT;
							return;
						}
						// don't create more object URLs than needed
						if (!object_url) {
							object_url = get_URL().createObjectURL(blob);
						}
						if (force) {
							view.location.href = object_url;
						} else {
							var opened = view.open(object_url, "_blank");
							if (!opened) {
								// Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
								view.location.href = object_url;
							}
						}
						filesaver.readyState = filesaver.DONE;
						dispatch_all();
						revoke(object_url);
					}
					;
				filesaver.readyState = filesaver.INIT;

				if (can_use_save_link) {
					object_url = get_URL().createObjectURL(blob);
					setTimeout(function() {
						save_link.href = object_url;
						save_link.download = name;
						click(save_link);
						dispatch_all();
						revoke(object_url);
						filesaver.readyState = filesaver.DONE;
					});
					return;
				}

				fs_error();
			}
			, FS_proto = FileSaver.prototype
			, saveAs = function(blob, name, no_auto_bom) {
				return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
			}
			;
		// IE 10+ (native saveAs)
		if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
			return function(blob, name, no_auto_bom) {
				name = name || blob.name || "download";

				if (!no_auto_bom) {
					blob = auto_bom(blob);
				}
				return navigator.msSaveOrOpenBlob(blob, name);
			};
		}

		FS_proto.abort = function(){};
		FS_proto.readyState = FS_proto.INIT = 0;
		FS_proto.WRITING = 1;
		FS_proto.DONE = 2;

		FS_proto.error =
			FS_proto.onwritestart =
				FS_proto.onprogress =
					FS_proto.onwrite =
						FS_proto.onabort =
							FS_proto.onerror =
								FS_proto.onwriteend =
									null;

		return saveAs;
	}(
		typeof self !== "undefined" && self
		|| typeof window !== "undefined" && window
		|| this.content
	));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window

if (typeof module !== "undefined" && module.exports) {
	module.exports.saveAs = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
	define([], function() {
		return saveAs;
	});
}
},{}],303:[function(require,module,exports){
var store;

store = require("../store.coffee");

module.exports.choosePath = function(options, callback) {
  var type;
  type = options.directory ? "openDirectory" : "saveFile";
  return chrome.fileSystem.chooseEntry({
    type: type,
    suggestedName: options.filename
  }, function(entry) {
    if (!entry) {
      callback({
        canceled: true
      });
      return;
    }
    return chrome.fileSystem.getWritableEntry(entry, function(entry) {
      if (entry) {
        return callback({
          path: entry,
          directory: options.directory
        });
      } else {
        return callback({
          canceled: true
        });
      }
    });
  });
};

module.exports.save = function(blob, options) {
  var cancel, entry, error, filename, success, writeFile;
  if (options == null) {
    options = {};
  }
  success = options.success, error = options.error, cancel = options.cancel, filename = options.filename, entry = options.entry;
  writeFile = function(entry) {
    if (!entry) {
      if (typeof cancel === "function") {
        cancel();
      }
      return;
    }
    return entry.createWriter(function(writer) {
      writer.onerror = function(e) {
        console.error(e);
        return typeof error === "function" ? error() : void 0;
      };
      writer.onwriteend = function(e) {
        return typeof success === "function" ? success() : void 0;
      };
      return writer.write(blob, {
        type: blob.type
      });
    });
  };
  if (entry.directory) {
    return entry.path.getFile(filename, {
      create: true
    }, writeFile, error);
  } else {
    console.log(entry.path);
    console.log(filename);
    return writeFile(entry.path);
  }
};


},{"../store.coffee":346}],304:[function(require,module,exports){
var filetypes;

filetypes = require("./filetypes.coffee");

if (typeof Filesystem !== "undefined" && Filesystem !== null) {
  module.exports = new Filesystem(filetypes);
}


},{"./filetypes.coffee":305}],305:[function(require,module,exports){
var inExtensions;

exports.images = [".jpg", ".jpeg", ".png", ".bmp", ".gif"];

exports.raw = [".tiff", ".tif", ".3fr", ".ari", ".arw", ".bay", ".crw", ".cr2", ".cap", ".dcs", ".dcr", ".dng", ".drf", ".eip", ".erf", ".fff", ".iiq", ".k25", ".kdc", ".mef", ".mos", ".mrw", ".nef", ".nrw", ".obm", ".orf", ".pef", ".ptx", ".pxn", ".r3d", ".raf", ".raw", ".rwl", ".rw2", ".rwz", ".sr2", ".srf", ".srw", ".x3f"];

exports.preset = [".polarr"];

inExtensions = function(name, extensions) {
  var extension, i, len;
  name = name.toLowerCase();
  for (i = 0, len = extensions.length; i < len; i++) {
    extension = extensions[i];
    if (name.indexOf(extension) !== -1) {
      return true;
    }
  }
  return false;
};

exports.isRAW = function(name) {
  return inExtensions(name, exports.raw);
};

exports.isSupported = function(name) {
  return inExtensions(name, exports.raw.concat(exports.images).concat(exports.preset));
};


},{}],306:[function(require,module,exports){
var store, utils;

store = require("../store.coffee");

utils = require("./utils.coffee");

module.exports.open = function() {
  return store.manager.sendMessage("ppe.gallery.end", {
    id: store.currentPhoto,
    transition: true
  });
};

module.exports.save = function(blob, options) {
  var adjustments, cancel, complete, error, filename, progress, success, type;
  if (options == null) {
    options = {};
  }
  success = options.success, error = options.error, cancel = options.cancel, progress = options.progress, type = options.type, filename = options.filename, adjustments = options.adjustments;
  complete = function() {
    var identifier, message;
    identifier = filename === store.sampleImage ? "SAMPLE" : filename;
    message = {
      type: store.saveable === store.READ_ONLY ? "export" : type,
      identifier: identifier
    };
    if (type === "save" || type === "copy") {
      message.adjustments = adjustments;
    }
    log("sending ppe.export: " + identifier, "filesize: " + blob.size);
    return store.manager.sendMessage("ppe.export", message, function(res) {
      log("photo exported", res);
      if (res.success) {
        if (res.error === false) {
          return cancel();
        } else {
          return typeof success === "function" ? success(res) : void 0;
        }
      } else {
        return typeof error === "function" ? error() : void 0;
      }
    });
  };
  return utils.sendBlob(blob, {
    success: complete,
    error: error,
    progress: progress,
    cancel: cancel
  });
};


},{"../store.coffee":346,"./utils.coffee":308}],307:[function(require,module,exports){
var store, utils;

store = require("../store.coffee");

utils = require("./utils.coffee");

module.exports.readFile = function(options, callback) {
  return store.manager.sendMessage("ppe.window.showOpenFileDialog", {}, (function(_this) {
    return function(res) {
      if (res.canceled) {
        callback();
      }
      return store.manager.sendMessage("ppe.window.readDataFromFile", {
        path: res.paths[0]
      }, function(file) {
        return callback(file.data);
      });
    };
  })(this));
};

module.exports.choosePath = function(options, callback) {
  if (options.directory) {
    return store.manager.sendMessage("ppe.window.showOpenFileDialog", {
      canChooseDirectories: true,
      title: options.title,
      secureBookmark: true
    }, function(res) {
      if (res.canceled) {
        return callback({
          canceled: true
        });
      } else {
        return callback({
          path: res.paths[0],
          directory: true
        });
      }
    });
  } else {
    return store.manager.sendMessage("ppe.window.showSaveFileDialog", {
      filename: options.filename,
      title: options.title,
      allowedExtensions: [options.filename.split(".").pop()]
    }, (function(_this) {
      return function(res) {
        if (res.canceled) {
          return callback({
            canceled: true
          });
        } else {
          return callback({
            path: res.url
          });
        }
      };
    })(this));
  }
};

module.exports.open = function(options) {
  return store.manager.sendMessage("ppe.import", {
    type: "filesystem"
  });
};

module.exports.save = function(blob, options) {
  var adjustments, cancel, complete, entry, error, exportFile, filename, progress, success, type;
  if (options == null) {
    options = {};
  }
  success = options.success, error = options.error, cancel = options.cancel, progress = options.progress, type = options.type, filename = options.filename, entry = options.entry, adjustments = options.adjustments;
  exportFile = function(me) {
    var method;
    log("sending ppe.export...");
    if (type === "share") {
      cancel();
      method = "ppe.window.shareImageUsingSystem";
    } else {
      method = "ppe.export";
    }
    return store.manager.sendMessage(method, me, function(res) {
      log("photo exported", res);
      if (res.success) {
        if (res.error === false) {
          return cancel();
        } else {
          return typeof success === "function" ? success(res) : void 0;
        }
      } else {
        return typeof error === "function" ? error() : void 0;
      }
    });
  };
  complete = function() {
    var filepath, identifier, message;
    identifier = filename;
    if (entry) {
      if (entry.directory) {
        filepath = entry.path + "/" + filename;
      } else {
        filepath = entry.path;
      }
    }
    message = {
      type: store.saveable === store.READ_ONLY ? "export" : type,
      identifier: identifier,
      filepath: filepath,
      service: options.service
    };
    if (type === "save" || type === "copy") {
      message.adjustments = adjustments;
    }
    log(message, blob.size);
    return exportFile(message);
  };
  return utils.sendBlob(blob, {
    success: complete,
    error: error,
    progress: progress,
    cancel: cancel
  });
};


},{"../store.coffee":346,"./utils.coffee":308}],308:[function(require,module,exports){
var store;

store = require("../store.coffee");

module.exports.sendBlob = function(blob, options) {
  var cancel, error, onabort, onerror, onload, onprogress, progress, request, success, url;
  if (options == null) {
    options = {};
  }
  url = options.url, success = options.success, error = options.error, progress = options.progress, cancel = options.cancel;
  url = url || store.getRootUrl() + "export/";
  log("sending blob: " + url);
  onprogress = function(e) {
    var percent;
    if (e.lengthComputable) {
      percent = e.loaded / e.total * 100;
      return typeof progress === "function" ? progress(percent) : void 0;
    }
  };
  onload = function() {
    return typeof success === "function" ? success() : void 0;
  };
  onerror = function(e) {
    log("onerror", e);
    return typeof error === "function" ? error() : void 0;
  };
  onabort = function(e) {
    log("onabort", e);
    return typeof cancel === "function" ? cancel() : void 0;
  };
  request = new XMLHttpRequest();
  request.upload.addEventListener("progress", onprogress);
  request.upload.addEventListener("error", onerror);
  request.upload.addEventListener("abort", onabort);
  request.onreadystatechange = function(e) {
    if (request.readyState === XMLHttpRequest.DONE) {
      return onload(e);
    }
  };
  request.open("POST", url, true);
  return request.send(blob);
};


},{"../store.coffee":346}],309:[function(require,module,exports){
var fileInput, filesaver, open, openFiles, store;

store = require("../store-universal.coffee");

filesaver = require("./FileSaver.js");

openFiles = function() {};

fileInput = document.createElement("input");

fileInput.type = "file";

fileInput.multiple = true;

fileInput.accept = "image/*";

fileInput.onchange = function(e) {
  return openFiles(e.target.files);
};

module.exports.readFile = function(options, callback) {
  return open({}, function(files) {
    var file;
    file = files[0];
    if (!file) {
      callback();
    }
    return store.readFile(file, callback);
  });
};

module.exports.open = open = function(options, callback) {
  var accept, multiple;
  multiple = options.multiple, accept = options.accept;
  fileInput.multiple = multiple;
  fileInput.accept = accept || "image/*";
  openFiles = callback;
  return fileInput.click();
};

module.exports.save = function(blob, options) {
  var filename, success;
  if (options == null) {
    options = {};
  }
  success = options.success, filename = options.filename;
  filesaver.saveAs(blob, filename);
  return typeof success === "function" ? success() : void 0;
};


},{"../store-universal.coffee":345,"./FileSaver.js":302}],310:[function(require,module,exports){
var store;

store = require("../store.coffee");

module.exports.choosePath = function(options, callback) {
  var error1, folderPicker;
  if (options.directory) {
    folderPicker = new Windows.Storage.Pickers.FolderPicker();
    folderPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.documentsLibrary;
    folderPicker.fileTypeFilter.replaceAll(["*"]);
    try {
      return folderPicker.pickSingleFolderAsync().done(function(entry) {
        if (entry) {
          return callback(entry);
        } else {
          return callback({
            canceled: true
          });
        }
      });
    } catch (error1) {
      console.error("error picking folder");
      return callback({
        canceled: true
      });
    }
  } else {
    return callback({
      path: null,
      directory: options.directory
    });
  }
};

module.exports.save = function(blob, options) {
  var cancel, entry, error, error1, error2, filename, savePicker, success;
  if (options == null) {
    options = {};
  }
  success = options.success, error = options.error, cancel = options.cancel, filename = options.filename, entry = options.entry;
  if (entry && typeof entry.createFileAsync === 'function') {
    try {
      return entry.createFileAsync(filename, Windows.Storage.CreationCollisionOption.generateUniqueName).done(function(file) {
        return Filtr.readAsArrayBuffer(blob, function(buffer) {
          var e, error1;
          try {
            return Windows.Storage.FileIO.writeBytesAsync(file, new Uint8Array(buffer)).done(function() {
              return typeof success === "function" ? success() : void 0;
            });
          } catch (error1) {
            e = error1;
            console.error("failed to write to windows storage");
            return typeof error === "function" ? error() : void 0;
          }
        });
      }, function(err) {
        console.error("error creating file");
        return typeof error === "function" ? error(err) : void 0;
      });
    } catch (error1) {
      console.error("error creating files from batch export");
      return typeof error === "function" ? error() : void 0;
    }
  } else {
    savePicker = new Windows.Storage.Pickers.FileSavePicker();
    savePicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.documentsLibrary;
    savePicker.fileTypeChoices.insert("Images", [".jpg"]);
    savePicker.fileTypeChoices.insert("Images", [".png"]);
    savePicker.fileTypeChoices.insert("Images", [".tif"]);
    savePicker.suggestedFileName = filename;
    try {
      return savePicker.pickSaveFileAsync().then(function(file) {
        if (file) {
          return Filtr.readAsArrayBuffer(blob, function(buffer) {
            var e, error2;
            try {
              return Windows.Storage.FileIO.writeBytesAsync(file, new Uint8Array(buffer)).done(function() {
                return typeof success === "function" ? success() : void 0;
              });
            } catch (error2) {
              e = error2;
              console.error("failed to write to windows storage");
              return typeof error === "function" ? error() : void 0;
            }
          });
        } else {
          console.error("user did not pick file");
          return typeof error === "function" ? error() : void 0;
        }
      });
    } catch (error2) {
      console.error("error save file with single export");
      return typeof error === "function" ? error() : void 0;
    }
  }
};


},{"../store.coffee":346}],311:[function(require,module,exports){
var chrome, electron, mobile, osx, utils, web, windows;

web = require("./filesystem-adapters/web.coffee");

mobile = require("./filesystem-adapters/mobile.coffee");

windows = require("./filesystem-adapters/windows.coffee");

osx = require("./filesystem-adapters/osx.coffee");

chrome = require("./filesystem-adapters/chrome.coffee");

electron = require("./filesystem-adapters/electron.coffee");

utils = require("./filesystem-adapters/utils.coffee");

module.exports.web = web;

module.exports.mobile = mobile;

module.exports.windows = windows;

module.exports.osx = osx;

module.exports.chrome = chrome;

module.exports.electron = electron;

module.exports.readFile = function(options, callback) {
  switch (CONFIG.platform) {
    case "osx":
      return osx.readFile(options, callback);
    default:
      return web.readFile(options, callback);
  }
};

module.exports.choosePath = function(options, callback) {
  switch (CONFIG.platform) {
    case "osx":
      return osx.choosePath(options, callback);
    case "chrome":
      return chrome.choosePath(options, callback);
    case "windows":
      return windows.choosePath(options, callback);
    case "electron":
      return electron.choosePath(options, callback);
    default:
      return callback({
        path: null
      });
  }
};

module.exports.open = function(options, callback) {
  switch (CONFIG.platform) {
    case "mobile":
      return mobile.open(options, callback);
    case "osx":
      return osx.open(options, callback);
    case "electron":
      return electron.open(options, callback);
    default:
      return web.open(options, callback);
  }
};

module.exports.save = function(blob, options) {
  return module.exports[CONFIG.platform].save(blob, options);
};

module.exports.sendBlob = function(blob, options) {
  return utils.sendBlob(blob, options);
};

module.exports.read = function() {
  return electron.read.apply(electron, arguments);
};

module.exports.createThumbnail = function() {
  return electron.createThumbnail.apply(electron, arguments);
};


},{"./filesystem-adapters/chrome.coffee":303,"./filesystem-adapters/electron.coffee":304,"./filesystem-adapters/mobile.coffee":306,"./filesystem-adapters/osx.coffee":307,"./filesystem-adapters/utils.coffee":308,"./filesystem-adapters/web.coffee":309,"./filesystem-adapters/windows.coffee":310}],312:[function(require,module,exports){
var FACE_DATA, actions, batchExport, exportPhoto, filetypes, getExportOptions, importPhoto, importURL, manager, open, save, storage, store, storeHistory, storeThumbnail, trackExports, translate, utils;

translate = require("counterpart");

store = require("./store.coffee");

storage = require("./storage.coffee");

actions = require("./actions.coffee");

utils = require("./utils.coffee");

manager = require("./manager.coffee");

filetypes = require("./filesystem-adapters/filetypes.coffee");

FACE_DATA = {
  "sample_faces.jpg": [
    {
      markers: [[0.43973214285714285, 0.4898336414048059], [0.4451530612244898, 0.5485212569316081], [0.453125, 0.6048983364140481], [0.46269132653061223, 0.6621996303142329], [0.4789540816326531, 0.7153419593345656], [0.5041454081632653, 0.761090573012939], [0.5334821428571429, 0.8012939001848429], [0.5660076530612245, 0.8368761552680222], [0.6029974489795918, 0.8461182994454713], [0.6399872448979592, 0.8359519408502772], [0.6744260204081632, 0.7999075785582255], [0.704719387755102, 0.756007393715342], [0.7279974489795918, 0.7051756007393716], [0.7413903061224489, 0.6478743068391867], [0.7471301020408163, 0.5864140480591498], [0.7512755102040817, 0.5254158964879853], [0.7519132653061225, 0.46395563770794823], [0.46205357142857145, 0.43715341959334564], [0.48086734693877553, 0.41913123844731975], [0.5051020408163265, 0.41451016635859517], [0.5306122448979592, 0.4214417744916821], [0.5545280612244898, 0.43530499075785584], [0.6176658163265306, 0.42929759704251386], [0.6428571428571429, 0.4108133086876155], [0.6686862244897959, 0.40064695009242146], [0.6954719387755102, 0.4029574861367837], [0.7181122448979592, 0.42051756007393715], [0.5883290816326531, 0.4801293900184843], [0.5889668367346939, 0.5207948243992606], [0.5899234693877551, 0.5623844731977818], [0.5905612244897959, 0.6044362292051756], [0.5682397959183674, 0.6256931608133087], [0.5800382653061225, 0.633086876155268], [0.5924744897959183, 0.63909426987061], [0.6049107142857143, 0.6321626617375231], [0.6173469387755102, 0.6252310536044362], [0.49330357142857145, 0.4856746765249538], [0.5098852040816326, 0.4690388170055453], [0.5315688775510204, 0.4690388170055453], [0.5475127551020408, 0.4912199630314233], [0.529655612244898, 0.49815157116451014], [0.5089285714285714, 0.49815157116451014], [0.6329719387755102, 0.48613678373382624], [0.6485969387755102, 0.46349353049907577], [0.6709183673469388, 0.4602587800369686], [0.6881377551020408, 0.4755083179297597], [0.6731505102040817, 0.4898336414048059], [0.6524234693877551, 0.4921441774491682], [0.5385841836734694, 0.6991682070240296], [0.5567602040816326, 0.6820702402957486], [0.5778061224489796, 0.6746765249537893], [0.5947066326530612, 0.6802218114602587], [0.6106505102040817, 0.6742144177449169], [0.6345663265306123, 0.6811460258780037], [0.6572066326530612, 0.6968576709796673], [0.6377551020408163, 0.7393715341959335], [0.6138392857142857, 0.7569316081330869], [0.5959821428571429, 0.7592421441774492], [0.5784438775510204, 0.7569316081330869], [0.5567602040816326, 0.7398336414048059], [0.548469387755102, 0.7019408502772643], [0.578125, 0.6940850277264325], [0.5950255102040817, 0.6963955637707948], [0.6116071428571429, 0.694547134935305], [0.6470025510204082, 0.7000924214417745], [0.6122448979591837, 0.7264325323475046], [0.5950255102040817, 0.7292051756007394], [0.5784438775510204, 0.7264325323475046]],
      boundaries: [0.4139030612244898, 0.3133086876155268, 0.35586734693877553, 0.5157116451016636]
    }, {
      markers: [[0.1600765306122449, 0.6012014787430684], [0.16677295918367346, 0.660351201478743], [0.17506377551020408, 0.7162661737523105], [0.18622448979591838, 0.7703327171903882], [0.20535714285714285, 0.816543438077634], [0.23405612244897958, 0.8521256931608133], [0.2688137755102041, 0.8793900184842883], [0.3067602040816326, 0.8983364140480592], [0.34215561224489793, 0.9011090573012939], [0.3721301020408163, 0.8886321626617375], [0.3944515306122449, 0.8539741219963032], [0.4135841836734694, 0.8137707948243993], [0.4276147959183674, 0.7675600739371534], [0.43686224489795916, 0.7185767097966729], [0.44228316326530615, 0.6677449168207024], [0.4457908163265306, 0.6173752310536045], [0.44387755102040816, 0.566543438077634], [0.2037627551020408, 0.527264325323475], [0.22448979591836735, 0.5064695009242144], [0.24840561224489796, 0.4944547134935305], [0.2755102040816326, 0.49306839186691315], [0.30038265306122447, 0.5046210720887245], [0.36033163265306123, 0.5041589648798521], [0.3791454081632653, 0.4898336414048059], [0.40019132653061223, 0.4879852125693161], [0.41996173469387754, 0.49491682070240295], [0.4343112244897959, 0.5124768946395564], [0.3354591836734694, 0.5522181146025879], [0.34024234693877553, 0.5868761552680222], [0.34598214285714285, 0.6229205175600739], [0.3514030612244898, 0.6589648798521257], [0.31855867346938777, 0.6866913123844732], [0.3322704081632653, 0.6922365988909427], [0.34598214285714285, 0.6977818853974121], [0.3565051020408163, 0.6917744916820703], [0.36575255102040816, 0.6829944547134935], [0.23660714285714285, 0.5646950092421442], [0.2528698979591837, 0.5457486136783734], [0.2729591836734694, 0.5457486136783734], [0.2892219387755102, 0.5656192236598891], [0.27168367346938777, 0.571626617375231], [0.2528698979591837, 0.5720887245841035], [0.3622448979591837, 0.5614602587800369], [0.37850765306122447, 0.5397412199630314], [0.3979591836734694, 0.538354898336414], [0.4116709183673469, 0.555452865064695], [0.399234693877551, 0.5656192236598891], [0.3804209183673469, 0.5660813308687616], [0.27519132653061223, 0.7592421441774492], [0.30165816326530615, 0.738909426987061], [0.32748724489795916, 0.7292051756007394], [0.3440688775510204, 0.7324399260628466], [0.35841836734693877, 0.7268946395563771], [0.37436224489795916, 0.7319778188539742], [0.3867984693877551, 0.7504621072088724], [0.3753188775510204, 0.782347504621072], [0.3596938775510204, 0.7966728280961183], [0.34375, 0.7999075785582255], [0.32716836734693877, 0.7994454713493531], [0.30165816326530615, 0.7902033271719039], [0.2844387755102041, 0.7597042513863216], [0.3278061224489796, 0.7467652495378928], [0.3440688775510204, 0.7472273567467652], [0.35841836734693877, 0.7453789279112755], [0.3778698979591837, 0.7527726432532348], [0.35778061224489793, 0.7712569316081331], [0.34311224489795916, 0.7754158964879853], [0.3268494897959184, 0.7763401109057301]],
      boundaries: [0.16581632653061223, 0.42791127541589646, 0.31887755102040816, 0.46210720887245843]
    }
  ]
};

if (typeof POIPCManager !== "undefined" && POIPCManager !== null) {
  store.manager.waitForMessage("ppe.embedded.save", function() {
    log("received 'ppe.embedded.save'");
    return exportPhoto("save", store.currentPhoto);
  });
  store.manager.waitForMessage("ppe.import.error", function(error) {
    log(error);
    switch (error.code) {
      case "I1":
        return store.loadUrl(null);
      case "I2":
        return store.setSaveable(store.READ_ONLY);
    }
  });
  store.manager.waitForMessage("ppe.import.pending", function() {
    return manager.set("photo_status", {
      status: "importing"
    });
  });
  store.manager.waitForMessage("ppe.import.incoming", function(res, callback) {
    var adjustments, exifKey, extension, faces, filename, filesize, id, isRAW, metadata, saveable, url;
    url = res.url, id = res.id, filename = res.filename, filesize = res.filesize, extension = res.extension, adjustments = res.adjustments, metadata = res.metadata, exifKey = res.exifKey, saveable = res.saveable, faces = res.faces;
    log("import incoming", id, url);
    if (faces == null) {
      faces = [];
    }
    isRAW = filetypes.isRAW("." + extension);
    if (CONFIG.os === "android") {
      callback = function(payload) {
        return store.manager.sendMessage("ppe.import.done", payload);
      };
    }
    if (CONFIG.os === "osx") {
      if (CONFIG.embedded) {
        return store.readURL(url, function(data, info) {
          store.info = info;
          store.info.faces = faces;
          store.info.isRAW = isRAW;
          store.currentPhoto = filename + "|" + info.id;
          store.setSaveable(store.READ_ONLY);
          return store.loadData(data, info.metadata, adjustments, callback);
        });
      } else {
        return store.importFiles([
          {
            id: res.identifier,
            url: url,
            filename: filename,
            filesize: filesize,
            faces: faces
          }
        ], callback);
      }
    } else {
      metadata.exifKey = exifKey;
      store.info = {};
      store.info.faces = faces;
      store.info.isRAW = isRAW;
      store.currentPhoto = id;
      store.metadata = metadata;
      store.loadUrl(url, metadata, adjustments, callback);
      return store.setSaveable(saveable);
    }
  });
  store.manager.waitForMessage("ppe.batch.export", function(res, callback) {
    var adjustmentsFrom, adjustmentsId, batchOptions, filter, id, importError, importSuccess, metadata, orientation, resize, resizingHeight, resizingWidth, shouldCrop, shouldResize, url;
    url = res.url, id = res.id, orientation = res.orientation, batchOptions = res.batchOptions;
    log("batch exporting: " + id);
    shouldResize = batchOptions.shouldResize, shouldCrop = batchOptions.shouldCrop, resizingWidth = batchOptions.resizingWidth, resizingHeight = batchOptions.resizingHeight, adjustmentsId = batchOptions.adjustmentsId, adjustmentsFrom = batchOptions.adjustmentsFrom;
    resize = "none";
    if (shouldResize) {
      resize = "fit";
      if (shouldCrop) {
        resize = "fill";
      }
    }
    log(batchOptions, resize);
    metadata = {
      Orientation: orientation
    };
    filter = null;
    importError = function() {
      log("error exporting image: " + id);
      return callback({
        error: true,
        identifier: id
      });
    };
    importSuccess = function() {
      var adjustments, blob, complete, message, output;
      output = Filtr["export"]("jpeg", {
        width: resizingWidth,
        height: resizingHeight,
        resize: resize,
        quality: manager.state.settings.quality
      });
      blob = new Blob([output.data], {
        type: "image/jpeg"
      });
      adjustments = {
        version: VERSION,
        history: Filtr.getHistory()
      };
      message = {
        type: "copy",
        identifier: id,
        adjustments: adjustments
      };
      complete = function() {
        return callback(message);
      };
      return actions.filesystem.sendBlob(blob, {
        success: complete,
        error: importError,
        cancel: importError
      });
    };
    return store.get("state", id, (function(_this) {
      return function(history) {
        var apply;
        if (adjustmentsFrom === "filter") {
          filter = actions.getFilter(adjustmentsId);
          apply = {
            state: filter.state,
            message: "labels.look %10:%20" + actions.getFilterName(filter)
          };
        }
        return importURL(url, {
          metadata: metadata,
          history: history,
          applyAdjustments: apply,
          success: importSuccess,
          error: importError
        });
      };
    })(this));
  });
}

getExportOptions = function(options) {
  var adjustments, filetype, height, image, max, quality, ref, scale, settings, width;
  if (options == null) {
    options = {};
  }
  image = Filtr.getInfo();
  filetype = (options.filetype || "jpg").toLowerCase();
  settings = options.settings || Filtr.getState(true);
  quality = options.quality || manager.state.settings.quality;
  width = options.width || image.cropWidth;
  height = options.height || image.cropHeight;
  adjustments = options.adjustments || {
    version: VERSION,
    history: Filtr.getHistory().slice(-1)
  };
  if (filetype === "jpeg") {
    filetype = "jpg";
  }
  max = actions.getMaxExportSize();
  scale = Math.min(max / width, max / height, 1);
  width = width * scale >> 0;
  height = height * scale >> 0;
  options = Filtr.utils.assign(options, {
    adjustments: adjustments,
    settings: settings,
    filetype: filetype,
    quality: quality,
    width: width,
    height: height
  });
  if (!options.head && ((ref = store.info) != null ? ref.metadata : void 0)) {
    options.head = store.info.metadata.head;
  }
  return options;
};

trackExports = function() {
  return storage.getItem('exports', (function(_this) {
    return function(err, exports) {
      if (exports == null) {
        exports = 0;
      }
      exports += 1;
      storage.getItem('reviewed', function(err, hasReviewed) {
        var times;
        if (hasReviewed) {
          return;
        }
        if (exports <= 5) {
          times = 5;
        } else if (exports <= 10) {
          times = 10;
        } else if (exports <= 50) {
          times = 50;
        } else {
          times = 100;
        }
        if (exports % times === 0 && !(CONFIG.os === "android")) {
          return actions.setDialog("review", true);
        }
      });
      return storage.setItem('exports', exports);
    };
  })(this));
};

storeThumbnail = function(thumbnail) {
  var blob, height, success, width;
  if (CONFIG.embedded) {
    return;
  }
  if (CONFIG.platform === "mobile") {
    blob = new Blob([thumbnail.data]);
    width = thumbnail.width;
    height = thumbnail.height;
    success = function() {
      var ref;
      return (ref = store.manager) != null ? ref.sendMessage("ppe.export.thumbnail", {
        identifier: store.currentPhoto,
        width: width,
        height: height
      }) : void 0;
    };
    return actions.filesystem.sendBlob(blob, {
      url: store.getRootUrl() + "thumbnail/",
      success: success
    });
  } else {
    return store.set("thumbnail", store.currentPhoto, thumbnail);
  }
};

storeHistory = function(history) {
  if (CONFIG.embedded) {
    store.manager.sendMessage("ppe.embedded.changed", {
      changed: true
    });
  } else {
    store.set("state", store.currentPhoto, history);
  }
  return store.setExtra(store.currentPhoto, {
    edited: true,
    saved: false
  });
};

importURL = function(url, options) {
  var adjustments, alpha, angle, applyAdjustments, error, faces, flipX, flipY, height, history, item, j, len, metadata, orientation, originalState, ref, ref1, ref2, state, success, width;
  metadata = options.metadata, history = options.history, success = options.success, error = options.error, applyAdjustments = options.applyAdjustments, alpha = options.alpha;
  if (!history) {
    state = Filtr.getDefaultState(true);
    if (metadata.Orientation) {
      orientation = Filtr.getOrientationSettings(metadata.Orientation);
      state = Filtr.utils.assign(state, orientation);
    }
    history = [
      {
        message: "labels.original",
        state: state
      }
    ];
  }
  originalState = history[0].state;
  if (((ref = store.info.faces) != null ? ref.length : void 0) && !((ref1 = originalState.faces) != null ? ref1.length : void 0)) {
    angle = originalState.rotate90 * -90;
    flipX = originalState.flip_x;
    flipY = originalState.flip_y;
    if (angle) {
      faces = store.info.faces.map(function(face) {
        return {
          markers: face.markers.map(function(point) {
            return Filtr.utils.rotatePoint(point, angle, flipX, flipY);
          }),
          boundaries: Filtr.utils.rotateBounds(face.boundaries, angle, flipX, flipY)
        };
      });
    } else {
      faces = store.info.faces;
    }
    for (j = 0, len = history.length; j < len; j++) {
      item = history[j];
      item.state.faces = faces;
    }
  }
  if (applyAdjustments && applyAdjustments.state) {
    adjustments = Filtr.prepareAdjustments(applyAdjustments.state);
    state = history[history.length - 1].state;
    state = Filtr.utils.assign(state, adjustments);
    history.push({
      message: applyAdjustments.message || "buttons.paste_adjustments",
      state: state
    });
  }
  if (typeof url === "string") {
    return Filtr["import"](url, {
      settings: history,
      alpha: alpha,
      orientation: metadata.Orientation,
      done: success,
      error: error
    });
  } else {
    ref2 = store.info, width = ref2.width, height = ref2.height;
    return Filtr["import"](url, {
      width: width,
      height: height,
      settings: history,
      alpha: alpha,
      orientation: metadata.Orientation,
      done: success,
      error: error
    });
  }
};

importPhoto = function(url, metadata, adjustments, callback) {
  var alpha, filetype, history, importError, importSuccess, ref, ref1, ref2;
  if (metadata == null) {
    metadata = {};
  }
  logTime("photo loaded");
  Filtr.off("history", storeHistory);
  Filtr.off("thumbnail", storeThumbnail);
  if (!store.currentPhoto) {
    return;
  }
  if (!url) {
    actions.showCover(translate("status.photo_load_error"), 1000);
    return;
  }
  importSuccess = (function(_this) {
    return function() {
      var height, ref, width;
      Filtr.on("thumbnail", storeThumbnail);
      Filtr.update();
      logTimeEnd("photo loaded");
      setTimeout(function() {
        actions.updateThumbnails();
        return Filtr.on("history", storeHistory);
      }, 320);
      manager.set("metadata", metadata);
      manager.emit("load_image");
      log("photo loaded", store.currentPhoto);
      if (typeof callback === "function") {
        callback({
          id: store.currentPhoto,
          coordinates: Filtr.getCoordinates()
        });
      }
      ref = Filtr._.texture, width = ref.width, height = ref.height;
      return utils.trackEventGA("photo", "load", width + ", " + height);
    };
  })(this);
  importError = function(err) {
    callback();
    log("error loading photo", url, err);
    return setTimeout(actions.showGallery, 300);
  };
  if (!((ref = store.info.faces) != null ? ref.length : void 0)) {
    store.info.faces = FACE_DATA[store.info.filename] || [];
  }
  if (CONFIG.platform === "mobile") {
    return store.get("state", store.currentPhoto, (function(_this) {
      return function(history) {
        adjustments = adjustments || {};
        if (CONFIG.embedded) {
          history = adjustments.history;
        } else {
          if (!history) {
            history = adjustments.history;
          }
        }
        return importURL(url, {
          history: history,
          metadata: metadata,
          alpha: false,
          success: importSuccess,
          error: importError
        });
      };
    })(this));
  } else {
    filetype = (ref1 = store.info) != null ? (ref2 = ref1.filename) != null ? ref2.split(".").pop() : void 0 : void 0;
    alpha = (filetype != null ? filetype.toLowerCase() : void 0) === "png";
    if (CONFIG.embedded) {
      history = adjustments.history;
    } else {
      history = adjustments;
    }
    return importURL(url, {
      history: history,
      metadata: metadata,
      alpha: alpha,
      success: importSuccess,
      error: importError
    });
  }
};

batchExport = function(options) {
  var cancel, currentSettings, currentState, entry, error, exportComplete, i, ref, save, success;
  if (options == null) {
    options = {};
  }
  if (!store.photos.length) {
    return;
  }
  entry = null;
  actions.showCover(translate("status.saving_photos"));
  actions.showProgress(0);
  exportComplete = function() {
    actions.showCover(translate("status.photos_saved"), 1000);
    return actions.showProgress(100);
  };
  error = function() {
    var text;
    text = translate("status.saving_photo_error");
    actions.showCover(utils.ucFirst(text), 2000);
    return actions.showProgress(null);
  };
  cancel = function() {
    return actions.hideCover();
  };
  success = function() {
    store.setExtra(store.photos[i], {
      saved: true
    });
    i++;
    return save();
  };
  i = 0;
  save = function() {
    var photo;
    if (i < store.photos.length) {
      photo = store.photos[i];
      console.log("exporting photo: " + photo);
      return store.getPhotoData(photo, function(data, info, adjustments) {
        var alpha, done, extension, filename, name, parts;
        parts = store.getParts(photo);
        extension = parts.extension;
        name = parts.name;
        alpha = extension === "png";
        name = options.renameMethod === "prefix" ? "" + options.renamePrefix + (i + 1) : name;
        filename = name + "." + options.filetype;
        actions.showCover(translate("status.saving_photo") + " " + filename);
        actions.showProgress((i / (store.photos.length - 1)) * 100);
        done = function() {
          return setTimeout(function() {
            var blob, exportOptions, filter, output, preservedSettings, ref;
            exportOptions = getExportOptions(options);
            exportOptions.head = (ref = info.metadata) != null ? ref.head : void 0;
            if (options.filter === "auto") {
              filter = actions.getAutoAdjustments();
              exportOptions.settings = Filtr.utils.assign(exportOptions.settings, filter);
            } else if (options.filter === "current") {
              preservedSettings = {
                rotation: exportOptions.settings.rotation,
                rotate90: exportOptions.settings.rotate90,
                flip_x: exportOptions.settings.flip_x,
                flip_y: exportOptions.settings.flip_y,
                crop: exportOptions.settings.crop
              };
              exportOptions.settings = Filtr.utils.assign(currentState, preservedSettings);
            }
            output = Filtr["export"](exportOptions.filetype, exportOptions);
            blob = Filtr.getBlob(output.data, exportOptions.filetype);
            return actions.filesystem.save(blob, {
              type: "copy",
              filename: filename,
              entry: entry,
              success: success,
              error: error,
              cancel: cancel
            });
          }, CONFIG.os === "android" ? 300 : 100);
        };
        return importURL(data, {
          history: adjustments,
          metadata: info.metadata,
          alpha: alpha,
          success: done,
          error: error
        });
      });
    } else {
      return store.get("photo", store.currentPhoto, function(data) {
        return Filtr["import"](data, currentSettings);
      });
    }
  };
  currentSettings = {
    settings: Filtr.getHistory().slice(0),
    alpha: Filtr.hasAlpha(),
    error: error,
    done: exportComplete
  };
  currentState = (ref = currentSettings.settings.slice(0).pop()) != null ? ref.state : void 0;
  return actions.filesystem.choosePath({
    directory: true,
    title: translate("buttons.choose_folder")
  }, function(res) {
    if (res.canceled) {
      return cancel();
    } else {
      entry = res;
      utils.trackEventGA("photo", "batch_export", store.photos.length);
      return save();
    }
  });
};

exportPhoto = function(type, id, options) {
  var _timestamps, cancel, error, filename, name, now, parts, progress, save, success, time, timeEnd;
  if (type === "share") {
    actions.showCover(translate("status.sharing_photo"));
  } else {
    actions.showCover(translate("status.saving_photo"));
  }
  actions.showProgress(0);
  progress = (function(_this) {
    return function(progress) {
      return actions.showProgress(progress);
    };
  })(this);
  error = (function(_this) {
    return function() {
      var text;
      if (type === "share") {
        text = translate("status.sharing_photo_error");
      } else {
        text = translate("status.saving_photo_error");
      }
      actions.showCover(utils.ucFirst(text), 2000);
      return actions.showProgress(null);
    };
  })(this);
  cancel = (function(_this) {
    return function() {
      return actions.hideCover();
    };
  })(this);
  success = (function(_this) {
    return function(res) {
      if (res == null) {
        res = {};
      }
      timeEnd("export");
      log("transferred photo: " + id);
      store.setExtra(store.currentPhoto, {
        saved: true
      });
      if (res.newIdentifier != null) {
        id = res.newIdentifier;
        store.currentPhoto = id;
        store.setSaveable(store.READ_WRITE);
        store.set("state", id, Filtr.getHistory());
      }
      if (type === "share") {
        actions.showCover(translate("status.photo_shared"), 1000);
      } else {
        actions.showCover(translate("status.photo_saved"), 1000);
      }
      actions.showProgress(null);
      utils.trackEventGA("photo", "export", type);
      return trackExports();
    };
  })(this);
  now = function() {
    return performance.now();
  };
  _timestamps = {};
  time = function(key) {
    _timestamps[key] = now();
    return logTime(key);
  };
  timeEnd = function(key) {
    log(key + ":", now() - _timestamps[key]);
    return logTimeEnd(key);
  };
  save = function(entry) {
    var delay;
    delay = (entry != null ? entry.path : void 0) ? 0 : 300;
    return setTimeout(function() {
      var blob, output;
      time("export");
      time("render output");
      output = Filtr["export"](options.filetype, options);
      blob = Filtr.getBlob(output.data, options.filetype);
      timeEnd("render output");
      return actions.filesystem.save(blob, {
        type: type,
        filename: filename,
        entry: entry,
        service: options.service,
        adjustments: options.adjustments,
        success: success,
        error: error,
        cancel: cancel,
        progress: progress
      });
    }, delay);
  };
  options = getExportOptions(options);
  if (CONFIG.platform === "mobile") {
    filename = id;
  } else {
    parts = store.getParts(id);
    name = options.filename || parts.name;
    filename = name + "." + options.filetype;
  }
  if (type === "share" || CONFIG.embedded) {
    return save();
  } else {
    return actions.filesystem.choosePath({
      title: translate("buttons.save"),
      filename: filename
    }, function(res) {
      if (res.canceled) {
        return cancel();
      } else {
        return save(res);
      }
    });
  }
};

open = function() {
  return actions.filesystem.open({
    multiple: true,
    accept: "image/*"
  }, function(files) {
    return store.importFiles(files);
  });
};

save = function(type, options) {
  var id, ref;
  if (((ref = store.info) != null ? ref.filename : void 0) != null) {
    id = store.info.filename;
  } else {
    id = store.currentPhoto.split(",")[0];
  }
  return exportPhoto(type, id, options);
};

store.on("loadImage", importPhoto);

module.exports = {
  importURL: importURL,
  "import": importPhoto,
  "export": exportPhoto,
  batchExport: batchExport,
  open: open,
  save: save
};


},{"./actions.coffee":229,"./filesystem-adapters/filetypes.coffee":305,"./manager.coffee":338,"./storage.coffee":343,"./store.coffee":346,"./utils.coffee":360,"counterpart":3}],313:[function(require,module,exports){
(function (React){
var actions, fn, getKey, io, isMac, key, keys, methods, mousetrap, name, renderShortcut, shortcuts, utils;

utils = require("./utils.coffee");

actions = require("./actions.coffee");

io = require("./io.coffee");

mousetrap = require("mousetrap");

methods = {
  "noop": function() {},
  "cancel_export": function() {},
  "open": function() {
    if (CONFIG.embedded) {
      return;
    }
    return io.open();
  },
  "save": function() {
    if (CONFIG.embedded) {
      return;
    }
    return io.save("save");
  },
  "save_as": function() {
    if (CONFIG.embedded) {
      return;
    }
    return actions.setDialog("export_photo", {
      type: "single_export"
    });
  },
  "batch_export": function() {
    if (CONFIG.embedded) {
      return;
    }
    return actions.setDialog("export_photo", {
      type: "batch_export"
    });
  },
  "copy_edit": function() {
    if (CONFIG.embedded) {
      return;
    }
    return actions.copyAdjustments();
  },
  "paste_edit": function() {
    if (CONFIG.embedded) {
      return;
    }
    return actions.pasteAdjustments();
  },
  "undo": function() {
    return actions.undo();
  },
  "redo": function() {
    return actions.redo();
  },
  "revert": function() {
    return actions.revertToOriginal();
  },
  "import": function() {
    if (CONFIG.embedded) {

    }
  },
  "export": function() {
    if (CONFIG.embedded) {

    }
  },
  "next_photo": function() {
    if (CONFIG.embedded) {
      return;
    }
    return actions.switchPhoto(+1);
  },
  "prev_photo": function() {
    if (CONFIG.embedded) {
      return;
    }
    return actions.switchPhoto(-1);
  },
  "guides": function() {
    return actions.showPanel("guides");
  },
  "looks": function() {
    return actions.showPanel("looks");
  },
  "history": function() {
    return actions.showPanel("history");
  },
  "adjustments": function() {
    return actions.showPanel("adjustments");
  },
  "local-adjustments": function() {
    return actions.showPanel("local-adjustments");
  },
  "crop": function() {
    return actions.showPanel("crop");
  },
  "filmstrip": function() {
    return actions.showPanel("filmstrip");
  },
  "text": function() {
    return actions.showPanel("text");
  },
  "rotate_ccw": function() {
    return actions.rotate90(-1);
  },
  "rotate_cw": function() {
    return actions.rotate90(1);
  },
  "flip_x": function() {
    return actions.flipX();
  },
  "flip_y": function() {
    return actions.flipY();
  },
  "reset_adjustments": function() {
    return actions.resetMainAdjustments();
  }
};

keys = {
  "backspace": "noop",
  "esc": "cancel_export",
  "mod+s or s": "save",
  "mod+o or o": "open",
  "shift+mod+s": "save_as",
  "mod+b": "batch_export",
  "mod+c": "copy_edit",
  "mod+v": "paste_edit",
  "mod+z or z": "undo",
  "shift+mod+z or mod+y or y": "redo",
  "mod+r": "revert",
  "j": "prev_photo",
  "k": "next_photo",
  "g": "guides",
  "f": "looks",
  "p": "filmstrip",
  "h": "history",
  "a": "adjustments",
  "s": "local-adjustments",
  "t": "text",
  "c": "crop",
  "[": "rotate_ccw",
  "]": "rotate_cw",
  ";": "flip_x",
  "'": "flip_y"
};

shortcuts = {
  "show_original": "b",
  "eraser": "e",
  "pan": "space",
  "remove_photo": "backspace",
  "delete": ["backspace", "del"],
  "duplicate": "mod+d"
};

for (key in keys) {
  name = keys[key];
  shortcuts[name] = key;
  fn = function(e) {
    if (typeof e.preventDefault === "function") {
      e.preventDefault();
    }
    return this(e);
  };
  mousetrap.bind(key.split(" or "), fn.bind(methods[name]));
}

mousetrap.bind(shortcuts.show_original, function(e) {
  e.preventDefault();
  return actions.showOriginal(true);
}, "keydown");

mousetrap.bind(shortcuts.show_original, function(e) {
  e.preventDefault();
  return actions.showOriginal(false);
}, "keyup");

mousetrap.prototype.stopCallback = function(e, element, combo) {
  var isInput;
  if (["esc", "mod+s", "mod+o", "mod+r", "mod+d"].indexOf(combo) !== -1) {
    return false;
  }
  isInput = element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA' || (element.contentEditable && element.contentEditable === 'true');
  return isInput;
};

isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);

getKey = function(shortcut) {
  var backspace, join, left, mod, right, shift;
  if (!shortcuts[shortcut]) {
    return;
  }
  mod = isMac ? "⌘" : "ctrl";
  shift = isMac ? "⇧" : "shift";
  backspace = isMac ? "⌫" : "backspace";
  join = isMac ? " " : "+";
  left = "←";
  right = "→";
  key = shortcuts[shortcut];
  if (typeof key === "string") {
    key = key.split(" or ");
  }
  key = key[0].replace("mod", mod).replace("shift", shift).replace("left", left).replace("right", right).replace("backspace", backspace).split(/\+/g).map(utils.ucFirst).join(join);
  return [key];
};

renderShortcut = function(shortcut) {
  var label;
  if (CONFIG.platform === "mobile") {
    return null;
  }
  key = getKey(shortcut);
  if (key && key.length) {
    label = 
      React.createElement("span", {className: "shortcut-key"}, 
        key.map(function(label, i){
          return React.createElement("span", {key: i, className: "key"}, label)
        }, this)
      )
      ;
  }
  return label;
};

if (typeof POIPCManager !== "undefined" && POIPCManager !== null) {
  POIPCManager.sharedManager.waitForMessage("ppe.window.shortcut", function(res) {
    var shortcut;
    shortcut = res.shortcut.replace("cmd", "mod");
    return mousetrap.trigger(shortcut);
  });
}

module.exports = {
  getKey: getKey,
  renderShortcut: renderShortcut,
  keys: keys,
  shortcuts: shortcuts,
  actions: methods
};


}).call(this,require("react"))
},{"./actions.coffee":229,"./io.coffee":312,"./utils.coffee":360,"mousetrap":21,"react":215}],314:[function(require,module,exports){

/*
 * AutoEnhance.js
 * Filter optimization
 *
 * Enhao @ Polarr.co
 */
var AutoEnhance, mergeObj;

mergeObj = function(obj1, obj2) {
  var attrname, obj3;
  obj3 = {};
  for (attrname in obj1) {
    obj3[attrname] = obj1[attrname];
  }
  for (attrname in obj2) {
    obj3[attrname] = obj2[attrname];
  }
  return obj3;
};

AutoEnhance = {
  weights_highlights: [],
  weights_shadows: []
};

AutoEnhance.option_search_default = {
  method: 'rgb',
  to_log: 1,
  size: 50,
  sample_step: 5,
  histogram_grid: 10,
  num_trial: 50,
  num_gen: 6,
  num_spec: 20,
  tolerance: 100,
  change_max: 0.3
};

AutoEnhance.getIndex_Load = function(x, y, imWidth, imHeight, source) {
  source = source || 'load';
  if (source === 'load') {
    return (y * imWidth + x) * 4;
  } else {
    return ((imHeight - 1 - y) * imWidth + x) * 4;
  }
};

AutoEnhance.histogram = function(data, histogram_max, histogram_grid) {
  var histogram_output, i;
  histogram_max = histogram_max || 1;
  histogram_output = Array.apply(null, new Array(histogram_grid + 1)).map(Number.prototype.valueOf, 0);
  i = 0;
  while (i < data.length) {
    histogram_output[Math.floor(data[i] * histogram_grid / histogram_max)] += 1.0 / data.length;
    i++;
  }
  return histogram_output;
};

AutoEnhance.hue = function(img_data, sample_step) {
  var b, diff, g, h, imHeight, imWidth, index_process, max, min, r, result, x, y;
  imHeight = img_data.height;
  imWidth = img_data.width;
  y = 0;
  result = [];
  while (y + sample_step < imHeight) {
    x = 0;
    while (x + sample_step < imWidth) {
      index_process = AutoEnhance.getIndex_Load(x, y, imWidth, imHeight);
      r = img_data.data[index_process] / 255.0;
      g = img_data.data[index_process + 1] / 255.0;
      b = img_data.data[index_process + 2] / 255.0;
      min = Math.min(r, g, b);
      max = Math.max(r, g, b);
      diff = max - min;
      h = 0;
      if (Math.abs(diff) > 1e-6) {
        if (r === max) {
          h = (g - b) / diff / 6 + (g < b);
        } else if (g === max) {
          h = (2 + (b - r) / diff) / 6;
        } else {
          h = (4 + (r - g) / diff) / 6;
        }
      }
      result.push(h);
      x += sample_step;
    }
    y += sample_step;
  }
  return result;
};

AutoEnhance.lum = function(img_data, sample_step) {
  var b, g, imHeight, imWidth, index_process, lum, max, min, r, result, x, y;
  imHeight = img_data.height;
  imWidth = img_data.width;
  y = 0;
  result = [];
  while (y + sample_step < imHeight) {
    x = 0;
    while (x + sample_step < imWidth) {
      index_process = AutoEnhance.getIndex_Load(x, y, imWidth, imHeight);
      r = img_data.data[index_process] / 255.0;
      g = img_data.data[index_process + 1] / 255.0;
      b = img_data.data[index_process + 2] / 255.0;
      min = Math.min(r, g, b);
      max = Math.max(r, g, b);
      lum = (max + min) / 2;
      result.push(lum);
      x += sample_step;
    }
    y += sample_step;
  }
  return result;
};

AutoEnhance.exposure = function(img_data, sample_step) {
  var b, exp, g, imHeight, imWidth, index_process, r, result, x, y;
  imHeight = img_data.height;
  imWidth = img_data.width;
  y = 0;
  result = [];
  while (y + sample_step < imHeight) {
    x = 0;
    while (x + sample_step < imWidth) {
      index_process = AutoEnhance.getIndex_Load(x, y, imWidth, imHeight);
      r = img_data.data[index_process] / 255.0;
      g = img_data.data[index_process + 1] / 255.0;
      b = img_data.data[index_process + 2] / 255.0;
      exp = r * 0.298839 + g * 0.586811 + b * 0.11435;
      result.push(exp);
      x += sample_step;
    }
    y += sample_step;
  }
  return result;
};

AutoEnhance.sat = function(img_data, sample_step) {
  var b, diff, g, imHeight, imWidth, index_process, lum, max, min, r, result, s, x, y;
  imHeight = img_data.height;
  imWidth = img_data.width;
  y = 0;
  result = [];
  while (y + sample_step < imHeight) {
    x = 0;
    while (x + sample_step < imWidth) {
      index_process = AutoEnhance.getIndex_Load(x, y, imWidth, imHeight);
      r = img_data.data[index_process] / 255.0;
      g = img_data.data[index_process + 1] / 255.0;
      b = img_data.data[index_process + 2] / 255.0;
      min = Math.min(r, g, b);
      max = Math.max(r, g, b);
      diff = max - min;
      lum = (max + min) / 2;
      s = 0;
      if (Math.abs(diff) > 1e-6) {
        if (lum < 0.5) {
          s = diff / (max + min + 1e-6);
        } else {
          s = diff / (2 - max - min + 1e-6);
        }
      }
      result.push(s);
      x += sample_step;
    }
    y += sample_step;
  }
  return result;
};

AutoEnhance.compute_feature = function(obj, option) {
  var histogram_grid, method, sample_step;
  method = option.method || AutoEnhance.option_search_default.method;
  sample_step = option.sample_step || AutoEnhance.option_search_default.sample_step;
  histogram_grid = option.histogram_grid || AutoEnhance.option_search_default.histogram_grid;
  if (method === 'sat_hist') {
    return AutoEnhance.histogram(AutoEnhance.sat(obj, sample_step), 1.0, histogram_grid);
  }
  if (method === 'lum_hist') {
    return AutoEnhance.histogram(AutoEnhance.lum(obj, sample_step), 1.0, histogram_grid);
  }
};

AutoEnhance.compute_exposure_auto = function(array_histogram, exposure_target, highlights_target, shadows_target) {
  var exposure_auto, exposure_mean, exposure_tmp, grid_hist, highlights_auto, highlights_mean, i_index, shadows_auto, shadows_mean, weight_highlights, weight_shadows, weightsum_highlights, weightsum_shadows;
  exposure_target = exposure_target || 0.6;
  highlights_target = highlights_target || 0.8;
  shadows_target = shadows_target || 0.3;
  exposure_auto = 0.0;
  highlights_auto = 0.0;
  shadows_auto = 0.0;
  weight_highlights = 0;
  weight_shadows = 0;
  grid_hist = array_histogram.length;
  exposure_mean = 0.0;
  highlights_mean = 0.0;
  shadows_mean = 0.0;
  weight_highlights = 0.0;
  weight_shadows = 0.0;
  weightsum_highlights = 0.0;
  weightsum_shadows = 0.0;
  i_index = 0;
  while (i_index < grid_hist) {
    exposure_mean += (i_index + 0.5) / (grid_hist - 1.0) * array_histogram[i_index];
    i_index += 1;
  }
  i_index = 0;
  while (i_index < grid_hist) {
    exposure_tmp = (i_index + 0.5) / (grid_hist - 1.0);
    weight_highlights = Math.exp(-4.0 * (exposure_tmp - 1) * (exposure_tmp - 1));
    AutoEnhance.weights_highlights[i_index] = weight_highlights;
    highlights_mean += weight_highlights * array_histogram[i_index] * exposure_tmp;
    weightsum_highlights += weight_highlights * array_histogram[i_index];
    weight_shadows = Math.exp(-4.0 * exposure_tmp * exposure_tmp);
    AutoEnhance.weights_shadows[i_index] = weight_shadows;
    shadows_mean += weight_shadows * array_histogram[i_index] * exposure_tmp;
    weightsum_shadows += weight_shadows * array_histogram[i_index];
    i_index += 1;
  }
  shadows_mean /= weightsum_shadows;
  highlights_mean /= weightsum_highlights;
  if (exposure_mean > exposure_target) {
    exposure_auto = (exposure_target - exposure_mean) / (exposure_mean - 0.0);
  } else {
    exposure_auto = (exposure_target - exposure_mean) / (1.0 - exposure_mean);
  }
  if (highlights_mean > highlights_target) {
    highlights_auto = (highlights_target - highlights_mean) / (highlights_mean - 0.0);
  } else {
    highlights_auto = (highlights_target - highlights_mean) / (1.0 - highlights_mean);
  }
  if (shadows_mean > shadows_target) {
    shadows_auto = (shadows_target - shadows_mean) / (shadows_mean - 0.0);
  } else {
    shadows_auto = (shadows_target - shadows_mean) / (1.0 - shadows_mean);
  }
  return {
    exposure: exposure_auto,
    exposure_mean: exposure_mean,
    highlights: highlights_auto,
    highlights_mean: highlights_mean,
    shadows: shadows_auto,
    shadows_mean: shadows_mean
  };
};

AutoEnhance.compute_saturation_auto = function(array_histogram) {
  var saturation_auto;
  var grid_hist, i_index, saturation_auto, saturation_mean, saturation_target;
  grid_hist = array_histogram.length;
  saturation_mean = 0.0;
  i_index = 0;
  while (i_index < grid_hist) {
    saturation_mean += (i_index + 0.5) / (grid_hist - 1.0) * array_histogram[i_index];
    i_index += 1;
  }
  saturation_target = 0.7;
  if (saturation_mean > saturation_target) {
    saturation_auto = (saturation_mean - saturation_target) / (saturation_mean - 0.0);
  } else {
    saturation_auto = (saturation_target - saturation_mean) / (1.0 - saturation_mean);
  }
  return {
    vibrance: saturation_auto,
    saturation_mean: saturation_mean
  };
};

AutoEnhance.create_filter = function(dict_exposure_auto, dict_saturation_auto, dict_clarity_auto, dict_dehaze_auto) {
  var filter_merge;
  filter_merge = {};
  filter_merge = mergeObj(filter_merge, dict_exposure_auto);
  filter_merge = mergeObj(filter_merge, dict_saturation_auto);
  filter_merge = mergeObj(filter_merge, dict_clarity_auto);
  filter_merge = mergeObj(filter_merge, dict_dehaze_auto);
  return filter_merge;
};

AutoEnhance.auto_filter = function(data_before) {
  var option_hist;
  var auto_result, data_lum_histogram, data_sat_histogram, dict_clarity_auto, dict_dehaze_auto, dict_exposure_auto, dict_saturation_auto, option_hist;
  option_hist = {
    method: 'lum_hist',
    to_log: 1,
    size: 50,
    sample_step: 5,
    histogram_grid: 20
  };
  data_lum_histogram = AutoEnhance.compute_feature(data_before, option_hist);
  dict_exposure_auto = AutoEnhance.compute_exposure_auto(data_lum_histogram);
  option_hist = {
    method: 'sat_hist',
    to_log: 1,
    size: 50,
    sample_step: 5,
    histogram_grid: 20
  };
  data_sat_histogram = AutoEnhance.compute_feature(data_before, option_hist);
  dict_saturation_auto = AutoEnhance.compute_saturation_auto(data_sat_histogram);
  dict_clarity_auto = {
    clarity: Math.min(0.3, dict_exposure_auto.highlights)
  };
  dict_dehaze_auto = {
    dehaze: Math.max(0, Math.min(dict_exposure_auto.highlights * 0.9 + dict_exposure_auto.exposure * 0.4, 1.0))
  };
  auto_result = AutoEnhance.create_filter(dict_exposure_auto, dict_saturation_auto, dict_clarity_auto, dict_dehaze_auto);
  if (auto_result.exposure + auto_result.highlights >= 0.9) {
    auto_result.gamma = -auto_result.exposure * 0.35;
  }
  return auto_result;
};

module.exports = AutoEnhance;


},{}],315:[function(require,module,exports){
/*
 * JavaScript Load Image
 * https://github.com/blueimp/JavaScript-Load-Image
 *
 * Copyright 2011, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

var exifMap = {
  // =================
  // TIFF tags (IFD0):
  // =================
  0x0100: 'ImageWidth',
  0x0101: 'ImageHeight',
  //0x8769: 'ExifIFDPointer',
  //0x8825: 'GPSInfoIFDPointer',
  0xA005: 'InteroperabilityIFDPointer',
  0x0102: 'BitsPerSample',
  0x0103: 'Compression',
  0x0106: 'PhotometricInterpretation',
  0x0112: 'Orientation',
  0x0115: 'SamplesPerPixel',
  0x011C: 'PlanarConfiguration',
  0x0212: 'YCbCrSubSampling',
  0x0213: 'YCbCrPositioning',
  0x011A: 'XResolution',
  0x011B: 'YResolution',
  0x0128: 'ResolutionUnit',
  0x0111: 'StripOffsets',
  0x0116: 'RowsPerStrip',
  0x0117: 'StripByteCounts',
  0x0201: 'JPEGInterchangeFormat', // Offset to JPEG thumbnail SOI
  0x0202: 'JPEGInterchangeFormatLength',
  0x012D: 'TransferFunction',
  0x013E: 'WhitePoint',
  0x013F: 'PrimaryChromaticities',
  0x0211: 'YCbCrCoefficients',
  0x0214: 'ReferenceBlackWhite',
  0x0132: 'DateTime',
  0x010E: 'ImageDescription',
  0x010F: 'Make',
  0x0110: 'Model',
  0x0131: 'Software',
  0x013B: 'Artist',
  0x8298: 'Copyright',

  0xC6f8: 'ProfileName',
  0x8773: 'ICCProfile',
  0xc68f: 'AsShotICCProfile',
  0xc691: 'CurrentICCProfile',

  // ==================
  // Exif Sub IFD tags:
  // ==================
  0x9000: 'ExifVersion', // EXIF version
  0xA000: 'FlashpixVersion', // Flashpix format version
  0xA001: 'ColorSpace', // Color space information tag
  0xA002: 'PixelXDimension', // Valid width of meaningful image
  0xA003: 'PixelYDimension', // Valid height of meaningful image
  0xA500: 'Gamma',
  0x9101: 'ComponentsConfiguration', // Information about channels
  0x9102: 'CompressedBitsPerPixel', // Compressed bits per pixel
  0x927C: 'MakerNote', // Any desired information written by the manufacturer
  0x9286: 'UserComment', // Comments by user
  0xA004: 'RelatedSoundFile', // Name of related sound file
  0x9003: 'DateTimeOriginal', // Date and time when the original image was generated
  0x9004: 'DateTimeDigitized', // Date and time when the image was stored digitally
  0x9290: 'SubSecTime', // Fractions of seconds for DateTime
  0x9291: 'SubSecTimeOriginal', // Fractions of seconds for DateTimeOriginal
  0x9292: 'SubSecTimeDigitized', // Fractions of seconds for DateTimeDigitized
  0x829A: 'ExposureTime', // Exposure time (in seconds)
  0x829D: 'FNumber',
  0x8822: 'ExposureProgram', // Exposure program
  0x8824: 'SpectralSensitivity', // Spectral sensitivity
  0x8827: 'PhotographicSensitivity', // EXIF 2.3, ISOSpeedRatings in EXIF 2.2
  0x8828: 'OECF', // Optoelectric conversion factor
  0x8830: 'SensitivityType',
  0x8831: 'StandardOutputSensitivity',
  0x8832: 'RecommendedExposureIndex',
  0x8833: 'ISOSpeed',
  0x8834: 'ISOSpeedLatitudeyyy',
  0x8835: 'ISOSpeedLatitudezzz',
  0x9201: 'ShutterSpeedValue', // Shutter speed
  0x9202: 'ApertureValue', // Lens aperture
  0x9203: 'BrightnessValue', // Value of brightness
  0x9204: 'ExposureBias', // Exposure bias
  0x9205: 'MaxApertureValue', // Smallest F number of lens
  0x9206: 'SubjectDistance', // Distance to subject in meters
  0x9207: 'MeteringMode', // Metering mode
  0x9208: 'LightSource', // Kind of light source
  0x9209: 'Flash', // Flash status
  0x9214: 'SubjectArea', // Location and area of main subject
  0x920A: 'FocalLength', // Focal length of the lens in mm
  0xA20B: 'FlashEnergy', // Strobe energy in BCPS
  0xA20C: 'SpatialFrequencyResponse',
  0xA20E: 'FocalPlaneXResolution', // Number of pixels in width direction per FPRUnit
  0xA20F: 'FocalPlaneYResolution', // Number of pixels in height direction per FPRUnit
  0xA210: 'FocalPlaneResolutionUnit', // Unit for measuring the focal plane resolution
  0xA214: 'SubjectLocation', // Location of subject in image
  0xA215: 'ExposureIndex', // Exposure index selected on camera
  0xA217: 'SensingMethod', // Image sensor type
  0xA300: 'FileSource', // Image source (3 == DSC)
  0xA301: 'SceneType', // Scene type (1 == directly photographed)
  0xA302: 'CFAPattern', // Color filter array geometric pattern
  0xA401: 'CustomRendered', // Special processing
  0xA402: 'ExposureMode', // Exposure mode
  0xA403: 'WhiteBalance', // 1 = auto white balance, 2 = manual
  0xA404: 'DigitalZoomRatio', // Digital zoom ratio
  0xA405: 'FocalLengthIn35mmFilm',
  0xA406: 'SceneCaptureType', // Type of scene
  0xA407: 'GainControl', // Degree of overall image gain adjustment
  0xA408: 'Contrast', // Direction of contrast processing applied by camera
  0xA409: 'Saturation', // Direction of saturation processing applied by camera
  0xA40A: 'Sharpness', // Direction of sharpness processing applied by camera
  0xA40B: 'DeviceSettingDescription',
  0xA40C: 'SubjectDistanceRange', // Distance to subject
  0xA420: 'ImageUniqueID', // Identifier assigned uniquely to each image
  0xA430: 'CameraOwnerName',
  0xA431: 'BodySerialNumber',
  0xA432: 'LensSpecification',
  0xA433: 'LensMake',
  0xA434: 'LensModel',
  0xA435: 'LensSerialNumber',
  // ==============
  // GPS Info tags:
  // ==============
  0x0000: 'GPSVersionID',
  0x0001: 'GPSLatitudeRef',
  0x0002: 'GPSLatitude',
  0x0003: 'GPSLongitudeRef',
  0x0004: 'GPSLongitude',
  0x0005: 'GPSAltitudeRef',
  0x0006: 'GPSAltitude',
  0x0007: 'GPSTimeStamp',
  0x0008: 'GPSSatellites',
  0x0009: 'GPSStatus',
  0x000A: 'GPSMeasureMode',
  0x000B: 'GPSDOP',
  0x000C: 'GPSSpeedRef',
  0x000D: 'GPSSpeed',
  0x000E: 'GPSTrackRef',
  0x000F: 'GPSTrack',
  0x0010: 'GPSImgDirectionRef',
  0x0011: 'GPSImgDirection',
  0x0012: 'GPSMapDatum',
  0x0013: 'GPSDestLatitudeRef',
  0x0014: 'GPSDestLatitude',
  0x0015: 'GPSDestLongitudeRef',
  0x0016: 'GPSDestLongitude',
  0x0017: 'GPSDestBearingRef',
  0x0018: 'GPSDestBearing',
  0x0019: 'GPSDestDistanceRef',
  0x001A: 'GPSDestDistance',
  0x001B: 'GPSProcessingMethod',
  0x001C: 'GPSAreaInformation',
  0x001D: 'GPSDateStamp',
  0x001E: 'GPSDifferential',
  0x001F: 'GPSHPositioningError'
};

var exifTagTypes = {
  // byte, 8-bit unsigned int:
  1: {
    getValue: function (dataView, dataOffset) {
      return dataView.getUint8(dataOffset)
    },
    setValue: function(dataView, dataOffset, value) {
      dataView.setUint8(dataOffset, value)
    },
    size: 1
  },
  // ascii, 8-bit byte:
  2: {
    getValue: function (dataView, dataOffset) {
      return String.fromCharCode(dataView.getUint8(dataOffset))
    },
    setValue: function(dataView, dataOffset, value) {
      dataView.setUint8(dataOffset, value.charCodeAt(0))
    },
    size: 1,
    ascii: true
  },
  // short, 16 bit int:
  3: {
    getValue: function (dataView, dataOffset, littleEndian) {
      return dataView.getUint16(dataOffset, littleEndian)
    },
    setValue: function(dataView, dataOffset, value) {
      dataView.setUint16(dataOffset, value)
    },
    size: 2
  },
  // long, 32 bit int:
  4: {
    getValue: function (dataView, dataOffset, littleEndian) {
      return dataView.getUint32(dataOffset, littleEndian)
    },
    setValue: function(dataView, dataOffset, value) {
      dataView.setUint32(dataOffset, value)
    },
    size: 4
  },
  // rational = two long values, first is numerator, second is denominator:
  5: {
    getValue: function (dataView, dataOffset, littleEndian) {
      return dataView.getUint32(dataOffset, littleEndian) /
        dataView.getUint32(dataOffset + 4, littleEndian)
    },
    setValue: function(dataView, dataOffset, value) {
      var hi = Math.floor(value / (4294967296)),
        lo = value - hi * (4294967296);
      dataView.setUint32(dataOffset, lo);
      dataView.setUint32(dataOffset + 4, hi);
    },
    size: 8
  },
  // slong, 32 bit signed int:
  9: {
    getValue: function (dataView, dataOffset, littleEndian) {
      return dataView.getInt32(dataOffset, littleEndian)
    },
    setValue: function(dataView, dataOffset, value) {
      dataView.setInt32(dataOffset, value);
    },
    size: 4
  },
  // srational, two slongs, first is numerator, second is denominator:
  10: {
    getValue: function (dataView, dataOffset, littleEndian) {
      return dataView.getInt32(dataOffset, littleEndian) /
        dataView.getInt32(dataOffset + 4, littleEndian)
    },
    setValue: function(dataView, dataOffset, value) {
      var lo, hi;
      if (value >= 0) {
        hi = Math.floor(value / (4294967296));
        lo = value - hi * (4294967296);
      } else {
        hi = Math.floor(value / 4294967296);
        lo = value - hi * 4294967296;
        hi += 4294967296;
      }
      dataView.setInt32(dataOffset, lo);
      dataView.setInt32(dataOffset + 4, hi);
    },
    size: 8
  }
};
// undefined, 8-bit byte, value depending on field:
exifTagTypes[7] = exifTagTypes[1];

var getExifValue = function (dataView, tiffOffset, offset, type, length, littleEndian, setValue) {
  var tagType = exifTagTypes[type];
  var tagSize;
  var dataOffset;
  var value;
  var i;
  var str;
  var c;
  if (!tagType) {
    console.log('Invalid Exif data: Invalid tag type.');
    return
  }
  tagSize = tagType.size * length;
  // Determine if the value is contained in the dataOffset bytes,
  // or if the value at the dataOffset is a pointer to the actual data:
  dataOffset = tagSize > 4
    ? tiffOffset + dataView.getUint32(offset + 8, littleEndian)
    : (offset + 8);
  if (dataOffset + tagSize > dataView.byteLength) {
    console.log('Invalid Exif data: Invalid data offset.');
    return
  }
  if (length === 1) {
    value = tagType.getValue(dataView, dataOffset, littleEndian);
    if (setValue != null) {
      tagType.setValue(dataView, dataOffset, setValue);
    }
  } else {
    value = [];
    for (i = 0; i < length; i += 1) {
      value[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
      if (setValue && setValue[i] != null) tagType.setValue(dataView, dataOffset, setValue[i]);
    }
    if (tagType.ascii) {
      str = '';
      // Concatenate the chars:
      for (i = 0; i < value.length; i += 1) {
        c = value[i];
        // Ignore the terminating NULL byte(s):
        if (c === '\u0000') {
          break
        }
        str += c
      }
      return str
    }
  }
  return value
};

var parseExifTag = function (dataView, tiffOffset, offset, littleEndian, data, overwrite) {
  var tag = dataView.getUint16(offset, littleEndian);
  var tagName = exifMap[tag] || tag;
  var setValue;
  if (overwrite) {
    setValue = overwrite[tagName];
  }
  data.exif[tagName] = getExifValue(
    dataView,
    tiffOffset,
    offset,
    dataView.getUint16(offset + 2, littleEndian), // tag type
    dataView.getUint32(offset + 4, littleEndian), // tag length
    littleEndian,
    setValue
  )
};

var parseExifTags = function (dataView, tiffOffset, dirOffset, littleEndian, data, overwrite) {
  var tagsNumber,
    dirEndOffset,
    i;
  if (dirOffset + 6 > dataView.byteLength) {
    console.log('Invalid Exif data: Invalid directory offset.');
    return
  }
  tagsNumber = dataView.getUint16(dirOffset, littleEndian);
  dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
  if (dirEndOffset + 4 > dataView.byteLength) {
    console.log('Invalid Exif data: Invalid directory size.');
    return
  }
  for (i = 0; i < tagsNumber; i += 1) {
    parseExifTag(
      dataView,
      tiffOffset,
      dirOffset + 2 + 12 * i, // tag offset
      littleEndian,
      data,
      overwrite
    )
  }
  // Return the offset to the next directory:
  return dataView.getUint32(dirEndOffset, littleEndian)
};

var parseExifData = function(dataView, offset, length, data, overwrite) {
  var tiffOffset = offset + 10;
  var littleEndian;
  var dirOffset;
  var thumbnailData;

  if (tiffOffset + 8 > dataView.byteLength) {
    console.log('Invalid Exif data: Invalid segment size.');
    return
  }
  // Check for the two null bytes:
  if (dataView.getUint16(offset + 8) !== 0x0000) {
    console.log('Invalid Exif data: Missing byte alignment offset.');
    return
  }
  switch (dataView.getUint16(tiffOffset)) {
    case 0x4949:
      littleEndian = true;
      break;
    case 0x4D4D:
      littleEndian = false;
      break;
    default:
      console.log('Invalid Exif data: Invalid byte alignment marker.');
      return
  }
  // Check for the TIFF tag marker (0x002A):
  if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
    console.log('Invalid Exif data: Missing TIFF marker.');
    return
  }

  data.exif = {};

  // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
  dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);

  dirOffset = parseExifTags(
    dataView,
    tiffOffset,
    tiffOffset + dirOffset,
    littleEndian,
    data,
    overwrite
  );

  //if (dirOffset) {
  //  thumbnailData = {exif: {}};
  //  dirOffset = parseExifTags(
  //    dataView,
  //    tiffOffset,
  //    tiffOffset + dirOffset,
  //    littleEndian,
  //    thumbnailData
  //  );
  //  // Check for JPEG Thumbnail offset:
  //  if (thumbnailData.exif[0x0201]) {
  //    data.exif.Thumbnail = getExifThumbnail(
  //      dataView,
  //      tiffOffset + thumbnailData.exif[0x0201],
  //      thumbnailData.exif[0x0202] // Thumbnail data length
  //    )
  //  }
  //}

  // Check for Exif Sub IFD Pointer:
  if (data.exif[0x8769]) {
    parseExifTags(
      dataView,
      tiffOffset,
      tiffOffset + data.exif[0x8769], // directory offset
      littleEndian,
      data,
      overwrite
    )
  }
  // Check for GPS Info IFD Pointer:
  if (data.exif[0x8825]) {
    parseExifTags(
      dataView,
      tiffOffset,
      tiffOffset + data.exif[0x8825], // directory offset
      littleEndian,
      data,
      overwrite
    )
  }
};

var readMetadata = function(buffer, overwrite) {
  var data = {};
  var dataView = new DataView(buffer);
  var offset = 2;
  var maxOffset = dataView.byteLength - 4;
  var headLength = offset;
  var markerBytes;
  var markerLength;
  var header;
  var profileOffset;
  // Check for the JPEG marker (0xffd8):
  try{
    if (dataView.getUint16(0) === 0xffd8) {
      while (offset < maxOffset) {
        markerBytes = dataView.getUint16(offset);
        // Search for APPn (0xffeN) and COM (0xfffe) markers,
        // which contain application-specific meta-data like
        // Exif, ICC and IPTC data and text comments:
        if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) ||
          markerBytes === 0xfffe) {
          // The marker bytes (2) are always followed by
          // the length bytes (2), indicating the length of the
          // marker segment, which includes the length bytes,
          // but not the marker bytes, so we add 2:
          markerLength = dataView.getUint16(offset + 2) + 2;
          if (offset + markerLength > dataView.byteLength) {
            console.log('Invalid meta data: Invalid segment size.');
            break
          }

          if (markerBytes === 0xffe1 || markerBytes === 0xffe2 || markerBytes === 0xffe3) {
            header = dataView.getUint32(offset + 4)
            // check for "Exif"
            if (header === 0x45786966) {
              parseExifData(dataView, offset, markerLength, data, overwrite);
            }
            // check for "ICC_PROFILE"
            // 49 43 43 5F 50 52 4F 46 49 4C 45
            else if (header === 0x4943435f) {
              console.log("ICC_PROFILE")
              // beginning of icc header (header is always 128 bytes)
              profileOffset = offset + 4 + 12

              var profileFields = [
                ["Size", 4],
                ["CMM", 4],
                ["Version", 4],
                ["Class", 4],
                ["Color space", 4],
                ["PCS", 4],
                ["Date time", 12],
                ["acsp", 4],
                ["Platform Signature", 4],
                ["Flags", 4],
                ["Device manufacturer", 4],
                ["Device model", 4],
                ["Device attributes", 8],
                ["Rendering intent", 4],
                ["Illuminant", 12],
                ["Creator Signature", 4],
                ["Profile ID", 16],
              ]

              profileFields.forEach(function(field) {
                // console.log(field[0], dataView.getUint32(profileOffset))
                profileOffset += field[1]
              })

              // console.log(dataView.getUint32(profileOffset))
            }
            // check for "META"
            else if (header === 0x4d455441 || header === 0x4d657461) {
              console.log("META")
              // parseExifData(dataView, offset, markerLength, data, overwrite);
            }
          }

          offset += markerLength;
          headLength = offset;
        } else {
          // Not an APPn or COM marker, probably safe to
          // assume that this is the end of the meta data
          break;
        }
      }
      // Meta length must be longer than JPEG marker (2)
      // plus APPn marker (2), followed by length bytes (2):
      if (headLength > 6) {
        data.head = new Uint8Array(buffer).subarray(0, headLength);

        // override ICC profile to sRGB
        // if (profileOffset) {
        //   data.head = data.head.slice(0)
        //   dataView = new DataView(data.head.buffer)
        //   dataView.setUint32(profileOffset, 128) // profile size
        //   dataView.setUint32(profileOffset + 4, 0x00000000) // CMM type
        // }
      }

      if (typeof data.exif !== "undefined" && typeof data.exif.Orientation !== "undefined") {
        data.Orientation = data.exif.Orientation
      }

      data.exifKey = "exif";
      
    } else {
      console.log('Invalid JPEG file: Missing JPEG marker.');
    }
    
  }catch(e){
    console.error('Failed to read metadata.');
  }
  return data;
};

module.exports = readMetadata;
},{}],316:[function(require,module,exports){
!function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={exports:{},id:r,loaded:!1};return e[r].call(i.exports,i,i.exports,t),i.loaded=!0,i.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t){function n(e,t){this.count=e,this.dataCodewords=t,Object.defineProperty(this,"Count",{get:function(){return this.count}}),Object.defineProperty(this,"DataCodewords",{get:function(){return this.dataCodewords}})}function r(e,t,n){this.ecCodewordsPerBlock=e,n?this.ecBlocks=new Array(t,n):this.ecBlocks=new Array(t),Object.defineProperty(this,"ECCodewordsPerBlock",{get:function(){return this.ecCodewordsPerBlock}}),Object.defineProperty(this,"TotalECCodewords",{get:function(){return this.ecCodewordsPerBlock*this.NumBlocks}}),Object.defineProperty(this,"NumBlocks",{get:function(){for(var e=0,t=0;t<this.ecBlocks.length;t++)e+=this.ecBlocks[t].length;return e}}),this.getECBlocks=function(){return this.ecBlocks}}function i(e,t,n,r,i,s){this.versionNumber=e,this.alignmentPatternCenters=t,this.ecBlocks=new Array(n,r,i,s);for(var o=0,a=n.ECCodewordsPerBlock,h=n.getECBlocks(),w=0;w<h.length;w++){var f=h[w];o+=f.Count*(f.DataCodewords+a)}this.totalCodewords=o,Object.defineProperty(this,"VersionNumber",{get:function(){return this.versionNumber}}),Object.defineProperty(this,"AlignmentPatternCenters",{get:function(){return this.alignmentPatternCenters}}),Object.defineProperty(this,"TotalCodewords",{get:function(){return this.totalCodewords}}),Object.defineProperty(this,"DimensionForVersion",{get:function(){return 17+4*this.versionNumber}}),this.buildFunctionPattern=function(){var e=this.DimensionForVersion,t=new u(e);t.setRegion(0,0,9,9),t.setRegion(e-8,0,8,9),t.setRegion(0,e-8,9,8);for(var n=this.alignmentPatternCenters.length,r=0;n>r;r++)for(var i=this.alignmentPatternCenters[r]-2,s=0;n>s;s++)0==r&&(0==s||s==n-1)||r==n-1&&0==s||t.setRegion(this.alignmentPatternCenters[s]-2,i,5,5);return t.setRegion(6,9,1,e-17),t.setRegion(9,6,e-17,1),this.versionNumber>6&&(t.setRegion(e-11,0,3,6),t.setRegion(0,e-11,6,3)),t},this.getECBlocksForLevel=function(e){return this.ecBlocks[e.ordinal()]}}function s(){return new Array(new i(1,new Array,new r(7,new n(1,19)),new r(10,new n(1,16)),new r(13,new n(1,13)),new r(17,new n(1,9))),new i(2,new Array(6,18),new r(10,new n(1,34)),new r(16,new n(1,28)),new r(22,new n(1,22)),new r(28,new n(1,16))),new i(3,new Array(6,22),new r(15,new n(1,55)),new r(26,new n(1,44)),new r(18,new n(2,17)),new r(22,new n(2,13))),new i(4,new Array(6,26),new r(20,new n(1,80)),new r(18,new n(2,32)),new r(26,new n(2,24)),new r(16,new n(4,9))),new i(5,new Array(6,30),new r(26,new n(1,108)),new r(24,new n(2,43)),new r(18,new n(2,15),new n(2,16)),new r(22,new n(2,11),new n(2,12))),new i(6,new Array(6,34),new r(18,new n(2,68)),new r(16,new n(4,27)),new r(24,new n(4,19)),new r(28,new n(4,15))),new i(7,new Array(6,22,38),new r(20,new n(2,78)),new r(18,new n(4,31)),new r(18,new n(2,14),new n(4,15)),new r(26,new n(4,13),new n(1,14))),new i(8,new Array(6,24,42),new r(24,new n(2,97)),new r(22,new n(2,38),new n(2,39)),new r(22,new n(4,18),new n(2,19)),new r(26,new n(4,14),new n(2,15))),new i(9,new Array(6,26,46),new r(30,new n(2,116)),new r(22,new n(3,36),new n(2,37)),new r(20,new n(4,16),new n(4,17)),new r(24,new n(4,12),new n(4,13))),new i(10,new Array(6,28,50),new r(18,new n(2,68),new n(2,69)),new r(26,new n(4,43),new n(1,44)),new r(24,new n(6,19),new n(2,20)),new r(28,new n(6,15),new n(2,16))),new i(11,new Array(6,30,54),new r(20,new n(4,81)),new r(30,new n(1,50),new n(4,51)),new r(28,new n(4,22),new n(4,23)),new r(24,new n(3,12),new n(8,13))),new i(12,new Array(6,32,58),new r(24,new n(2,92),new n(2,93)),new r(22,new n(6,36),new n(2,37)),new r(26,new n(4,20),new n(6,21)),new r(28,new n(7,14),new n(4,15))),new i(13,new Array(6,34,62),new r(26,new n(4,107)),new r(22,new n(8,37),new n(1,38)),new r(24,new n(8,20),new n(4,21)),new r(22,new n(12,11),new n(4,12))),new i(14,new Array(6,26,46,66),new r(30,new n(3,115),new n(1,116)),new r(24,new n(4,40),new n(5,41)),new r(20,new n(11,16),new n(5,17)),new r(24,new n(11,12),new n(5,13))),new i(15,new Array(6,26,48,70),new r(22,new n(5,87),new n(1,88)),new r(24,new n(5,41),new n(5,42)),new r(30,new n(5,24),new n(7,25)),new r(24,new n(11,12),new n(7,13))),new i(16,new Array(6,26,50,74),new r(24,new n(5,98),new n(1,99)),new r(28,new n(7,45),new n(3,46)),new r(24,new n(15,19),new n(2,20)),new r(30,new n(3,15),new n(13,16))),new i(17,new Array(6,30,54,78),new r(28,new n(1,107),new n(5,108)),new r(28,new n(10,46),new n(1,47)),new r(28,new n(1,22),new n(15,23)),new r(28,new n(2,14),new n(17,15))),new i(18,new Array(6,30,56,82),new r(30,new n(5,120),new n(1,121)),new r(26,new n(9,43),new n(4,44)),new r(28,new n(17,22),new n(1,23)),new r(28,new n(2,14),new n(19,15))),new i(19,new Array(6,30,58,86),new r(28,new n(3,113),new n(4,114)),new r(26,new n(3,44),new n(11,45)),new r(26,new n(17,21),new n(4,22)),new r(26,new n(9,13),new n(16,14))),new i(20,new Array(6,34,62,90),new r(28,new n(3,107),new n(5,108)),new r(26,new n(3,41),new n(13,42)),new r(30,new n(15,24),new n(5,25)),new r(28,new n(15,15),new n(10,16))),new i(21,new Array(6,28,50,72,94),new r(28,new n(4,116),new n(4,117)),new r(26,new n(17,42)),new r(28,new n(17,22),new n(6,23)),new r(30,new n(19,16),new n(6,17))),new i(22,new Array(6,26,50,74,98),new r(28,new n(2,111),new n(7,112)),new r(28,new n(17,46)),new r(30,new n(7,24),new n(16,25)),new r(24,new n(34,13))),new i(23,new Array(6,30,54,74,102),new r(30,new n(4,121),new n(5,122)),new r(28,new n(4,47),new n(14,48)),new r(30,new n(11,24),new n(14,25)),new r(30,new n(16,15),new n(14,16))),new i(24,new Array(6,28,54,80,106),new r(30,new n(6,117),new n(4,118)),new r(28,new n(6,45),new n(14,46)),new r(30,new n(11,24),new n(16,25)),new r(30,new n(30,16),new n(2,17))),new i(25,new Array(6,32,58,84,110),new r(26,new n(8,106),new n(4,107)),new r(28,new n(8,47),new n(13,48)),new r(30,new n(7,24),new n(22,25)),new r(30,new n(22,15),new n(13,16))),new i(26,new Array(6,30,58,86,114),new r(28,new n(10,114),new n(2,115)),new r(28,new n(19,46),new n(4,47)),new r(28,new n(28,22),new n(6,23)),new r(30,new n(33,16),new n(4,17))),new i(27,new Array(6,34,62,90,118),new r(30,new n(8,122),new n(4,123)),new r(28,new n(22,45),new n(3,46)),new r(30,new n(8,23),new n(26,24)),new r(30,new n(12,15),new n(28,16))),new i(28,new Array(6,26,50,74,98,122),new r(30,new n(3,117),new n(10,118)),new r(28,new n(3,45),new n(23,46)),new r(30,new n(4,24),new n(31,25)),new r(30,new n(11,15),new n(31,16))),new i(29,new Array(6,30,54,78,102,126),new r(30,new n(7,116),new n(7,117)),new r(28,new n(21,45),new n(7,46)),new r(30,new n(1,23),new n(37,24)),new r(30,new n(19,15),new n(26,16))),new i(30,new Array(6,26,52,78,104,130),new r(30,new n(5,115),new n(10,116)),new r(28,new n(19,47),new n(10,48)),new r(30,new n(15,24),new n(25,25)),new r(30,new n(23,15),new n(25,16))),new i(31,new Array(6,30,56,82,108,134),new r(30,new n(13,115),new n(3,116)),new r(28,new n(2,46),new n(29,47)),new r(30,new n(42,24),new n(1,25)),new r(30,new n(23,15),new n(28,16))),new i(32,new Array(6,34,60,86,112,138),new r(30,new n(17,115)),new r(28,new n(10,46),new n(23,47)),new r(30,new n(10,24),new n(35,25)),new r(30,new n(19,15),new n(35,16))),new i(33,new Array(6,30,58,86,114,142),new r(30,new n(17,115),new n(1,116)),new r(28,new n(14,46),new n(21,47)),new r(30,new n(29,24),new n(19,25)),new r(30,new n(11,15),new n(46,16))),new i(34,new Array(6,34,62,90,118,146),new r(30,new n(13,115),new n(6,116)),new r(28,new n(14,46),new n(23,47)),new r(30,new n(44,24),new n(7,25)),new r(30,new n(59,16),new n(1,17))),new i(35,new Array(6,30,54,78,102,126,150),new r(30,new n(12,121),new n(7,122)),new r(28,new n(12,47),new n(26,48)),new r(30,new n(39,24),new n(14,25)),new r(30,new n(22,15),new n(41,16))),new i(36,new Array(6,24,50,76,102,128,154),new r(30,new n(6,121),new n(14,122)),new r(28,new n(6,47),new n(34,48)),new r(30,new n(46,24),new n(10,25)),new r(30,new n(2,15),new n(64,16))),new i(37,new Array(6,28,54,80,106,132,158),new r(30,new n(17,122),new n(4,123)),new r(28,new n(29,46),new n(14,47)),new r(30,new n(49,24),new n(10,25)),new r(30,new n(24,15),new n(46,16))),new i(38,new Array(6,32,58,84,110,136,162),new r(30,new n(4,122),new n(18,123)),new r(28,new n(13,46),new n(32,47)),new r(30,new n(48,24),new n(14,25)),new r(30,new n(42,15),new n(32,16))),new i(39,new Array(6,26,54,82,110,138,166),new r(30,new n(20,117),new n(4,118)),new r(28,new n(40,47),new n(7,48)),new r(30,new n(43,24),new n(22,25)),new r(30,new n(10,15),new n(67,16))),new i(40,new Array(6,30,58,86,114,142,170),new r(30,new n(19,118),new n(6,119)),new r(28,new n(18,47),new n(31,48)),new r(30,new n(34,24),new n(34,25)),new r(30,new n(20,15),new n(61,16))))}function o(e,t,n,r,i,s,a,h,w){this.a11=e,this.a12=r,this.a13=a,this.a21=t,this.a22=i,this.a23=h,this.a31=n,this.a32=s,this.a33=w,this.transformPoints1=function(e){for(var t=e.length,n=this.a11,r=this.a12,i=this.a13,s=this.a21,o=this.a22,a=this.a23,h=this.a31,w=this.a32,f=this.a33,u=0;t>u;u+=2){var d=e[u],l=e[u+1],c=i*d+a*l+f;e[u]=(n*d+s*l+h)/c,e[u+1]=(r*d+o*l+w)/c}},this.transformPoints2=function(e,t){for(var n=e.length,r=0;n>r;r++){var i=e[r],s=t[r],o=this.a13*i+this.a23*s+this.a33;e[r]=(this.a11*i+this.a21*s+this.a31)/o,t[r]=(this.a12*i+this.a22*s+this.a32)/o}},this.buildAdjoint=function(){return new o(this.a22*this.a33-this.a23*this.a32,this.a23*this.a31-this.a21*this.a33,this.a21*this.a32-this.a22*this.a31,this.a13*this.a32-this.a12*this.a33,this.a11*this.a33-this.a13*this.a31,this.a12*this.a31-this.a11*this.a32,this.a12*this.a23-this.a13*this.a22,this.a13*this.a21-this.a11*this.a23,this.a11*this.a22-this.a12*this.a21)},this.times=function(e){return new o(this.a11*e.a11+this.a21*e.a12+this.a31*e.a13,this.a11*e.a21+this.a21*e.a22+this.a31*e.a23,this.a11*e.a31+this.a21*e.a32+this.a31*e.a33,this.a12*e.a11+this.a22*e.a12+this.a32*e.a13,this.a12*e.a21+this.a22*e.a22+this.a32*e.a23,this.a12*e.a31+this.a22*e.a32+this.a32*e.a33,this.a13*e.a11+this.a23*e.a12+this.a33*e.a13,this.a13*e.a21+this.a23*e.a22+this.a33*e.a23,this.a13*e.a31+this.a23*e.a32+this.a33*e.a33)}}function a(e,t){this.bits=e,this.points=t}function h(e){this.image=e,this.resultPointCallback=null,this.sizeOfBlackWhiteBlackRun=function(t,n,r,i){var s=Math.abs(i-n)>Math.abs(r-t);if(s){var o=t;t=n,n=o,o=r,r=i,i=o}for(var a=Math.abs(r-t),h=Math.abs(i-n),w=-a>>1,f=i>n?1:-1,u=r>t?1:-1,d=0,l=t,c=n;l!=r;l+=u){var g=s?c:l,v=s?l:c;if(1==d?this.image.data[g+v*e.width]&&d++:this.image.data[g+v*e.width]||d++,3==d){var m=l-t,y=c-n;return Math.sqrt(m*m+y*y)}if(w+=h,w>0){if(c==i)break;c+=f,w-=a}}var b=r-t,p=i-n;return Math.sqrt(b*b+p*p)},this.sizeOfBlackWhiteBlackRunBothWays=function(t,n,r,i){var s=this.sizeOfBlackWhiteBlackRun(t,n,r,i),o=1,a=t-(r-t);0>a?(o=t/(t-a),a=0):a>=e.width&&(o=(e.width-1-t)/(a-t),a=e.width-1);var h=Math.floor(n-(i-n)*o);return o=1,0>h?(o=n/(n-h),h=0):h>=e.height&&(o=(e.height-1-n)/(h-n),h=e.height-1),a=Math.floor(t+(a-t)*o),s+=this.sizeOfBlackWhiteBlackRun(t,n,a,h),s-1},this.calculateModuleSizeOneWay=function(e,t){var n=this.sizeOfBlackWhiteBlackRunBothWays(Math.floor(e.X),Math.floor(e.Y),Math.floor(t.X),Math.floor(t.Y)),r=this.sizeOfBlackWhiteBlackRunBothWays(Math.floor(t.X),Math.floor(t.Y),Math.floor(e.X),Math.floor(e.Y));return isNaN(n)?r/7:isNaN(r)?n/7:(n+r)/14},this.calculateModuleSize=function(e,t,n){return(this.calculateModuleSizeOneWay(e,t)+this.calculateModuleSizeOneWay(e,n))/2},this.distance=function(e,t){return xDiff=e.X-t.X,yDiff=e.Y-t.Y,Math.sqrt(xDiff*xDiff+yDiff*yDiff)},this.computeDimension=function(e,t,n,r){var i=Math.round(this.distance(e,t)/r),s=Math.round(this.distance(e,n)/r),o=(i+s>>1)+7;switch(3&o){case 0:o++;break;case 2:o--;break;case 3:throw"Error"}return o},this.findAlignmentInRegion=function(t,n,r,i){var s=Math.floor(i*t),o=Math.max(0,n-s),a=Math.min(e.width-1,n+s);if(3*t>a-o)throw"Error";var h=Math.max(0,r-s),w=Math.min(e.height-1,r+s),f=new E(this.image,o,h,a-o,w-h,t,this.resultPointCallback);return f.find()},this.createTransform=function(e,t,n,r,i){var s,a,h,w,f=i-3.5;null!=r?(s=r.X,a=r.Y,h=w=f-3):(s=t.X-e.X+n.X,a=t.Y-e.Y+n.Y,h=w=f);var u=o.quadrilateralToQuadrilateral(3.5,3.5,f,3.5,h,w,3.5,f,e.X,e.Y,t.X,t.Y,s,a,n.X,n.Y);return u},this.sampleGrid=function(e,t,n){var r=F;return r.sampleGrid3(e,n,t)},this.processFinderPatternInfo=function(e){var t=e.TopLeft,n=e.TopRight,r=e.BottomLeft,s=this.calculateModuleSize(t,n,r);if(1>s)throw"Error";var o=this.computeDimension(t,n,r,s),h=i.getProvisionalVersionForDimension(o),w=h.DimensionForVersion-7,f=null;if(h.AlignmentPatternCenters.length>0)for(var u=n.X-t.X+r.X,d=n.Y-t.Y+r.Y,l=1-3/w,c=Math.floor(t.X+l*(u-t.X)),g=Math.floor(t.Y+l*(d-t.Y)),v=4;16>=v;v<<=1){f=this.findAlignmentInRegion(s,c,g,v);break}var m,y=this.createTransform(t,n,r,f,o),b=this.sampleGrid(this.image,y,o);return m=null==f?new Array(r,t,n):new Array(r,t,n,f),new a(b,m)},this.detect=function(){var e=(new D).findFinderPattern(this.image);return this.processFinderPatternInfo(e)}}function w(e){this.errorCorrectionLevel=f.forBits(e>>3&3),this.dataMask=7&e,Object.defineProperty(this,"ErrorCorrectionLevel",{get:function(){return this.errorCorrectionLevel}}),Object.defineProperty(this,"DataMask",{get:function(){return this.dataMask}}),this.GetHashCode=function(){return this.errorCorrectionLevel.ordinal()<<3|dataMask},this.Equals=function(e){var t=e;return this.errorCorrectionLevel==t.errorCorrectionLevel&&this.dataMask==t.dataMask}}function f(e,t,n){this.ordinal_Renamed_Field=e,this.bits=t,this.name=n,Object.defineProperty(this,"Bits",{get:function(){return this.bits}}),Object.defineProperty(this,"Name",{get:function(){return this.name}}),this.ordinal=function(){return this.ordinal_Renamed_Field}}function u(e,t){if(t||(t=e),1>e||1>t)throw"Both dimensions must be greater than 0";this.width=e,this.height=t;var n=e>>5;0!=(31&e)&&n++,this.rowSize=n,this.bits=new Array(n*t);for(var r=0;r<this.bits.length;r++)this.bits[r]=0;Object.defineProperty(this,"Width",{get:function(){return this.width}}),Object.defineProperty(this,"Height",{get:function(){return this.height}}),Object.defineProperty(this,"Dimension",{get:function(){if(this.width!=this.height)throw"Can't call getDimension() on a non-square matrix";return this.width}}),this.get_Renamed=function(e,t){var n=t*this.rowSize+(e>>5);return 0!=(1&N(this.bits[n],31&e))},this.set_Renamed=function(e,t){var n=t*this.rowSize+(e>>5);this.bits[n]|=1<<(31&e)},this.flip=function(e,t){var n=t*this.rowSize+(e>>5);this.bits[n]^=1<<(31&e)},this.clear=function(){for(var e=this.bits.length,t=0;e>t;t++)this.bits[t]=0},this.setRegion=function(e,t,n,r){if(0>t||0>e)throw"Left and top must be nonnegative";if(1>r||1>n)throw"Height and width must be at least 1";var i=e+n,s=t+r;if(s>this.height||i>this.width)throw"The region must fit inside the matrix";for(var o=t;s>o;o++)for(var a=o*this.rowSize,h=e;i>h;h++)this.bits[a+(h>>5)]|=1<<(31&h)}}function d(e,t){this.numDataCodewords=e,this.codewords=t,Object.defineProperty(this,"NumDataCodewords",{get:function(){return this.numDataCodewords}}),Object.defineProperty(this,"Codewords",{get:function(){return this.codewords}})}function l(e){var t=e.Dimension;if(21>t||1!=(3&t))throw"Error BitMatrixParser";this.bitMatrix=e,this.parsedVersion=null,this.parsedFormatInfo=null,this.copyBit=function(e,t,n){return this.bitMatrix.get_Renamed(e,t)?n<<1|1:n<<1},this.readFormatInformation=function(){if(null!=this.parsedFormatInfo)return this.parsedFormatInfo;for(var e=0,t=0;6>t;t++)e=this.copyBit(t,8,e);e=this.copyBit(7,8,e),e=this.copyBit(8,8,e),e=this.copyBit(8,7,e);for(var n=5;n>=0;n--)e=this.copyBit(8,n,e);if(this.parsedFormatInfo=w.decodeFormatInformation(e),null!=this.parsedFormatInfo)return this.parsedFormatInfo;var r=this.bitMatrix.Dimension;e=0;for(var i=r-8,t=r-1;t>=i;t--)e=this.copyBit(t,8,e);for(var n=r-7;r>n;n++)e=this.copyBit(8,n,e);if(this.parsedFormatInfo=w.decodeFormatInformation(e),null!=this.parsedFormatInfo)return this.parsedFormatInfo;throw"Error readFormatInformation"},this.readVersion=function(){if(null!=this.parsedVersion)return this.parsedVersion;var e=this.bitMatrix.Dimension,t=e-17>>2;if(6>=t)return i.getVersionForNumber(t);for(var n=0,r=e-11,s=5;s>=0;s--)for(var o=e-9;o>=r;o--)n=this.copyBit(o,s,n);if(this.parsedVersion=i.decodeVersionInformation(n),null!=this.parsedVersion&&this.parsedVersion.DimensionForVersion==e)return this.parsedVersion;n=0;for(var o=5;o>=0;o--)for(var s=e-9;s>=r;s--)n=this.copyBit(o,s,n);if(this.parsedVersion=i.decodeVersionInformation(n),null!=this.parsedVersion&&this.parsedVersion.DimensionForVersion==e)return this.parsedVersion;throw"Error readVersion"},this.readCodewords=function(){var e=this.readFormatInformation(),t=this.readVersion(),n=L.forReference(e.DataMask),r=this.bitMatrix.Dimension;n.unmaskBitMatrix(this.bitMatrix,r);for(var i=t.buildFunctionPattern(),s=!0,o=new Array(t.TotalCodewords),a=0,h=0,w=0,f=r-1;f>0;f-=2){6==f&&f--;for(var u=0;r>u;u++)for(var d=s?r-1-u:u,l=0;2>l;l++)i.get_Renamed(f-l,d)||(w++,h<<=1,this.bitMatrix.get_Renamed(f-l,d)&&(h|=1),8==w&&(o[a++]=h,w=0,h=0));s^=!0}if(a!=t.TotalCodewords)throw"Error readCodewords";return o}}function c(){this.unmaskBitMatrix=function(e,t){for(var n=0;t>n;n++)for(var r=0;t>r;r++)this.isMasked(n,r)&&e.flip(r,n)},this.isMasked=function(e,t){return 0==(e+t&1)}}function g(){this.unmaskBitMatrix=function(e,t){for(var n=0;t>n;n++)for(var r=0;t>r;r++)this.isMasked(n,r)&&e.flip(r,n)},this.isMasked=function(e,t){return 0==(1&e)}}function v(){this.unmaskBitMatrix=function(e,t){for(var n=0;t>n;n++)for(var r=0;t>r;r++)this.isMasked(n,r)&&e.flip(r,n)},this.isMasked=function(e,t){return t%3==0}}function m(){this.unmaskBitMatrix=function(e,t){for(var n=0;t>n;n++)for(var r=0;t>r;r++)this.isMasked(n,r)&&e.flip(r,n)},this.isMasked=function(e,t){return(e+t)%3==0}}function y(){this.unmaskBitMatrix=function(e,t){for(var n=0;t>n;n++)for(var r=0;t>r;r++)this.isMasked(n,r)&&e.flip(r,n)},this.isMasked=function(e,t){return 0==(N(e,1)+t/3&1)}}function b(){this.unmaskBitMatrix=function(e,t){for(var n=0;t>n;n++)for(var r=0;t>r;r++)this.isMasked(n,r)&&e.flip(r,n)},this.isMasked=function(e,t){var n=e*t;return(1&n)+n%3==0}}function C(){this.unmaskBitMatrix=function(e,t){for(var n=0;t>n;n++)for(var r=0;t>r;r++)this.isMasked(n,r)&&e.flip(r,n)},this.isMasked=function(e,t){var n=e*t;return 0==((1&n)+n%3&1)}}function A(){this.unmaskBitMatrix=function(e,t){for(var n=0;t>n;n++)for(var r=0;t>r;r++)this.isMasked(n,r)&&e.flip(r,n)},this.isMasked=function(e,t){return 0==((e+t&1)+e*t%3&1)}}function M(e){this.field=e,this.decode=function(e,t){for(var n=new P(this.field,e),r=new Array(t),i=0;i<r.length;i++)r[i]=0;for(var s=!1,o=!0,i=0;t>i;i++){var a=n.evaluateAt(this.field.exp(s?i+1:i));r[r.length-1-i]=a,0!=a&&(o=!1)}if(!o)for(var h=new P(this.field,r),w=this.runEuclideanAlgorithm(this.field.buildMonomial(t,1),h,t),f=w[0],u=w[1],d=this.findErrorLocations(f),l=this.findErrorMagnitudes(u,d,s),i=0;i<d.length;i++){var c=e.length-1-this.field.log(d[i]);if(0>c)throw"ReedSolomonException Bad error location";e[c]=k.addOrSubtract(e[c],l[i])}},this.runEuclideanAlgorithm=function(e,t,n){if(e.Degree<t.Degree){var r=e;e=t,t=r}for(var i=e,s=t,o=this.field.One,a=this.field.Zero,h=this.field.Zero,w=this.field.One;s.Degree>=Math.floor(n/2);){var f=i,u=o,d=h;if(i=s,o=a,h=w,i.Zero)throw"r_{i-1} was zero";s=f;for(var l=this.field.Zero,c=i.getCoefficient(i.Degree),g=this.field.inverse(c);s.Degree>=i.Degree&&!s.Zero;){var v=s.Degree-i.Degree,m=this.field.multiply(s.getCoefficient(s.Degree),g);l=l.addOrSubtract(this.field.buildMonomial(v,m)),s=s.addOrSubtract(i.multiplyByMonomial(v,m))}a=l.multiply1(o).addOrSubtract(u),w=l.multiply1(h).addOrSubtract(d)}var y=w.getCoefficient(0);if(0==y)throw"ReedSolomonException sigmaTilde(0) was zero";var b=this.field.inverse(y),p=w.multiply2(b),C=s.multiply2(b);return new Array(p,C)},this.findErrorLocations=function(e){var t=e.Degree;if(1==t)return new Array(e.getCoefficient(1));for(var n=new Array(t),r=0,i=1;256>i&&t>r;i++)0==e.evaluateAt(i)&&(n[r]=this.field.inverse(i),r++);if(r!=t)throw"Error locator degree does not match number of roots";return n},this.findErrorMagnitudes=function(e,t,n){for(var r=t.length,i=new Array(r),s=0;r>s;s++){for(var o=this.field.inverse(t[s]),a=1,h=0;r>h;h++)s!=h&&(a=this.field.multiply(a,k.addOrSubtract(1,this.field.multiply(t[h],o))));i[s]=this.field.multiply(e.evaluateAt(o),this.field.inverse(a)),n&&(i[s]=this.field.multiply(i[s],o))}return i}}function P(e,t){if(null==t||0==t.length)throw"System.ArgumentException";this.field=e;var n=t.length;if(n>1&&0==t[0]){for(var r=1;n>r&&0==t[r];)r++;if(r==n)this.coefficients=e.Zero.coefficients;else{this.coefficients=new Array(n-r);for(var i=0;i<this.coefficients.length;i++)this.coefficients[i]=0;for(var s=0;s<this.coefficients.length;s++)this.coefficients[s]=t[r+s]}}else this.coefficients=t;Object.defineProperty(this,"Zero",{get:function(){return 0==this.coefficients[0]}}),Object.defineProperty(this,"Degree",{get:function(){return this.coefficients.length-1}}),Object.defineProperty(this,"Coefficients",{get:function(){return this.coefficients}}),this.getCoefficient=function(e){return this.coefficients[this.coefficients.length-1-e]},this.evaluateAt=function(e){if(0==e)return this.getCoefficient(0);var t=this.coefficients.length;if(1==e){for(var n=0,r=0;t>r;r++)n=k.addOrSubtract(n,this.coefficients[r]);return n}for(var i=this.coefficients[0],r=1;t>r;r++)i=k.addOrSubtract(this.field.multiply(e,i),this.coefficients[r]);return i},this.addOrSubtract=function(t){if(this.field!=t.field)throw"GF256Polys do not have same GF256 field";if(this.Zero)return t;if(t.Zero)return this;var n=this.coefficients,r=t.coefficients;if(n.length>r.length){var i=n;n=r,r=i}for(var s=new Array(r.length),o=r.length-n.length,a=0;o>a;a++)s[a]=r[a];for(var h=o;h<r.length;h++)s[h]=k.addOrSubtract(n[h-o],r[h]);return new P(e,s)},this.multiply1=function(e){if(this.field!=e.field)throw"GF256Polys do not have same GF256 field";if(this.Zero||e.Zero)return this.field.Zero;for(var t=this.coefficients,n=t.length,r=e.coefficients,i=r.length,s=new Array(n+i-1),o=0;n>o;o++)for(var a=t[o],h=0;i>h;h++)s[o+h]=k.addOrSubtract(s[o+h],this.field.multiply(a,r[h]));return new P(this.field,s)},this.multiply2=function(e){if(0==e)return this.field.Zero;if(1==e)return this;for(var t=this.coefficients.length,n=new Array(t),r=0;t>r;r++)n[r]=this.field.multiply(this.coefficients[r],e);return new P(this.field,n)},this.multiplyByMonomial=function(e,t){if(0>e)throw"System.ArgumentException";if(0==t)return this.field.Zero;for(var n=this.coefficients.length,r=new Array(n+e),i=0;i<r.length;i++)r[i]=0;for(var i=0;n>i;i++)r[i]=this.field.multiply(this.coefficients[i],t);return new P(this.field,r)},this.divide=function(e){if(this.field!=e.field)throw"GF256Polys do not have same GF256 field";if(e.Zero)throw"Divide by 0";for(var t=this.field.Zero,n=this,r=e.getCoefficient(e.Degree),i=this.field.inverse(r);n.Degree>=e.Degree&&!n.Zero;){var s=n.Degree-e.Degree,o=this.field.multiply(n.getCoefficient(n.Degree),i),a=e.multiplyByMonomial(s,o),h=this.field.buildMonomial(s,o);t=t.addOrSubtract(h),n=n.addOrSubtract(a)}return new Array(t,n)}}function k(e){this.expTable=new Array(256),this.logTable=new Array(256);for(var t=1,n=0;256>n;n++)this.expTable[n]=t,t<<=1,t>=256&&(t^=e);for(var n=0;255>n;n++)this.logTable[this.expTable[n]]=n;var r=new Array(1);r[0]=0,this.zero=new P(this,new Array(r));var i=new Array(1);i[0]=1,this.one=new P(this,new Array(i)),Object.defineProperty(this,"Zero",{get:function(){return this.zero}}),Object.defineProperty(this,"One",{get:function(){return this.one}}),this.buildMonomial=function(e,t){if(0>e)throw"System.ArgumentException";if(0==t)return zero;for(var n=new Array(e+1),r=0;r<n.length;r++)n[r]=0;return n[0]=t,new P(this,n)},this.exp=function(e){return this.expTable[e]},this.log=function(e){if(0==e)throw"System.ArgumentException";return this.logTable[e]},this.inverse=function(e){if(0==e)throw"System.ArithmeticException";return this.expTable[255-this.logTable[e]]},this.multiply=function(e,t){return 0==e||0==t?0:1==e?t:1==t?e:this.expTable[(this.logTable[e]+this.logTable[t])%255]}}function N(e,t){return e>=0?e>>t:(e>>t)+(2<<~t)}function B(e,t,n){this.x=e,this.y=t,this.count=1,this.estimatedModuleSize=n,Object.defineProperty(this,"EstimatedModuleSize",{get:function(){return this.estimatedModuleSize}}),Object.defineProperty(this,"Count",{get:function(){return this.count}}),Object.defineProperty(this,"X",{get:function(){return this.x}}),Object.defineProperty(this,"Y",{get:function(){return this.y}}),this.incrementCount=function(){this.count++},this.aboutEquals=function(e,t,n){if(Math.abs(t-this.y)<=e&&Math.abs(n-this.x)<=e){var r=Math.abs(e-this.estimatedModuleSize);return 1>=r||r/this.estimatedModuleSize<=1}return!1}}function S(e){this.bottomLeft=e[0],this.topLeft=e[1],this.topRight=e[2],Object.defineProperty(this,"BottomLeft",{get:function(){return this.bottomLeft}}),Object.defineProperty(this,"TopLeft",{get:function(){return this.topLeft}}),Object.defineProperty(this,"TopRight",{get:function(){return this.topRight}})}function D(){this.image=null,this.possibleCenters=[],this.hasSkipped=!1,this.crossCheckStateCount=new Array(0,0,0,0,0),this.resultPointCallback=null,Object.defineProperty(this,"CrossCheckStateCount",{get:function(){return this.crossCheckStateCount[0]=0,this.crossCheckStateCount[1]=0,this.crossCheckStateCount[2]=0,this.crossCheckStateCount[3]=0,this.crossCheckStateCount[4]=0,this.crossCheckStateCount}}),this.foundPatternCross=function(e){for(var t=0,n=0;5>n;n++){var r=e[n];if(0==r)return!1;t+=r}if(7>t)return!1;var i=Math.floor((t<<Y)/7),s=Math.floor(i/2);return Math.abs(i-(e[0]<<Y))<s&&Math.abs(i-(e[1]<<Y))<s&&Math.abs(3*i-(e[2]<<Y))<3*s&&Math.abs(i-(e[3]<<Y))<s&&Math.abs(i-(e[4]<<Y))<s},this.centerFromEnd=function(e,t){return t-e[4]-e[3]-e[2]/2},this.crossCheckVertical=function(e,t,n,r){for(var i=this.image,s=i.height,o=this.CrossCheckStateCount,a=e;a>=0&&i.data[t+a*i.width];)o[2]++,a--;if(0>a)return NaN;for(;a>=0&&!i.data[t+a*i.width]&&o[1]<=n;)o[1]++,a--;if(0>a||o[1]>n)return NaN;for(;a>=0&&i.data[t+a*i.width]&&o[0]<=n;)o[0]++,a--;if(o[0]>n)return NaN;for(a=e+1;s>a&&i.data[t+a*i.width];)o[2]++,a++;if(a==s)return NaN;for(;s>a&&!i.data[t+a*i.width]&&o[3]<n;)o[3]++,a++;if(a==s||o[3]>=n)return NaN;for(;s>a&&i.data[t+a*i.width]&&o[4]<n;)o[4]++,a++;if(o[4]>=n)return NaN;var h=o[0]+o[1]+o[2]+o[3]+o[4];return 5*Math.abs(h-r)>=2*r?NaN:this.foundPatternCross(o)?this.centerFromEnd(o,a):NaN},this.crossCheckHorizontal=function(e,t,n,r){for(var i=this.image,s=i.width,o=this.CrossCheckStateCount,a=e;a>=0&&i.data[a+t*i.width];)o[2]++,a--;if(0>a)return NaN;for(;a>=0&&!i.data[a+t*i.width]&&o[1]<=n;)o[1]++,a--;if(0>a||o[1]>n)return NaN;for(;a>=0&&i.data[a+t*i.width]&&o[0]<=n;)o[0]++,a--;if(o[0]>n)return NaN;for(a=e+1;s>a&&i.data[a+t*i.width];)o[2]++,a++;if(a==s)return NaN;for(;s>a&&!i.data[a+t*i.width]&&o[3]<n;)o[3]++,a++;if(a==s||o[3]>=n)return NaN;for(;s>a&&i.data[a+t*i.width]&&o[4]<n;)o[4]++,a++;if(o[4]>=n)return NaN;var h=o[0]+o[1]+o[2]+o[3]+o[4];return 5*Math.abs(h-r)>=r?NaN:this.foundPatternCross(o)?this.centerFromEnd(o,a):NaN},this.handlePossibleCenter=function(e,t,n){var r=e[0]+e[1]+e[2]+e[3]+e[4],i=this.centerFromEnd(e,n),s=this.crossCheckVertical(t,Math.floor(i),e[2],r);if(!isNaN(s)&&(i=this.crossCheckHorizontal(Math.floor(i),Math.floor(s),e[2],r),!isNaN(i))){for(var o=r/7,a=!1,h=this.possibleCenters.length,w=0;h>w;w++){var f=this.possibleCenters[w];if(f.aboutEquals(o,s,i)){f.incrementCount(),a=!0;break}}if(!a){var u=new B(i,s,o);this.possibleCenters.push(u),null!=this.resultPointCallback&&this.resultPointCallback.foundPossibleResultPoint(u)}return!0}return!1},this.selectBestPatterns=function(){var e=this.possibleCenters.length;if(3>e)throw"Couldn't find enough finder patterns:"+e+" patterns found";if(e>3){for(var t=0,n=0,r=0;e>r;r++){var i=this.possibleCenters[r].EstimatedModuleSize;t+=i,n+=i*i}var s=t/e;this.possibleCenters.sort(function(e,t){var n=Math.abs(t.EstimatedModuleSize-s),r=Math.abs(e.EstimatedModuleSize-s);return r>n?-1:n==r?0:1});for(var o=Math.sqrt(n/e-s*s),a=Math.max(.2*s,o),r=0;r<this.possibleCenters.length&&this.possibleCenters.length>3;r++){var h=this.possibleCenters[r];Math.abs(h.EstimatedModuleSize-s)>a&&(this.possibleCenters.splice(r,1),r--)}}return this.possibleCenters.length>3&&this.possibleCenters.sort(function(e,t){return e.count>t.count?-1:e.count<t.count?1:0}),new Array(this.possibleCenters[0],this.possibleCenters[1],this.possibleCenters[2])},this.findRowSkip=function(){var e=this.possibleCenters.length;if(1>=e)return 0;for(var t=null,n=0;e>n;n++){var r=this.possibleCenters[n];if(r.Count>=q){if(null!=t)return this.hasSkipped=!0,Math.floor((Math.abs(t.X-r.X)-Math.abs(t.Y-r.Y))/2);t=r}}return 0},this.haveMultiplyConfirmedCenters=function(){for(var e=0,t=0,n=this.possibleCenters.length,r=0;n>r;r++){var i=this.possibleCenters[r];i.Count>=q&&(e++,t+=i.EstimatedModuleSize)}if(3>e)return!1;for(var s=t/n,o=0,r=0;n>r;r++)i=this.possibleCenters[r],o+=Math.abs(i.EstimatedModuleSize-s);return.05*t>=o},this.findFinderPattern=function(e){var t=!1;this.image=e;var n=e.height,r=e.width,i=Math.floor(3*n/(4*_));(X>i||t)&&(i=X);for(var s=!1,o=new Array(5),a=i-1;n>a&&!s;a+=i){o[0]=0,o[1]=0,o[2]=0,o[3]=0,o[4]=0;for(var h=0,w=0;r>w;w++)if(e.data[w+a*e.width])1==(1&h)&&h++,o[h]++;else if(0==(1&h))if(4==h)if(this.foundPatternCross(o)){var f=this.handlePossibleCenter(o,a,w);if(f)if(i=2,this.hasSkipped)s=this.haveMultiplyConfirmedCenters();else{var u=this.findRowSkip();u>o[2]&&(a+=u-o[2]-i,w=r-1)}else{do w++;while(r>w&&!e.data[w+a*e.width]);w--}h=0,o[0]=0,o[1]=0,o[2]=0,o[3]=0,o[4]=0}else o[0]=o[2],o[1]=o[3],o[2]=o[4],o[3]=1,o[4]=0,h=3;else o[++h]++;else o[h]++;if(this.foundPatternCross(o)){var f=this.handlePossibleCenter(o,a,r);f&&(i=o[0],this.hasSkipped&&(s=haveMultiplyConfirmedCenters()))}}var d=this.selectBestPatterns();return j.orderBestPatterns(d),new S(d)}}function x(e,t,n){this.x=e,this.y=t,this.count=1,this.estimatedModuleSize=n,Object.defineProperty(this,"EstimatedModuleSize",{get:function(){return this.estimatedModuleSize}}),Object.defineProperty(this,"Count",{get:function(){return this.count}}),Object.defineProperty(this,"X",{get:function(){return Math.floor(this.x)}}),Object.defineProperty(this,"Y",{get:function(){return Math.floor(this.y)}}),this.incrementCount=function(){this.count++},this.aboutEquals=function(e,t,n){if(Math.abs(t-this.y)<=e&&Math.abs(n-this.x)<=e){var r=Math.abs(e-this.estimatedModuleSize);return 1>=r||r/this.estimatedModuleSize<=1}return!1}}function E(e,t,n,r,i,s,o){this.image=e,this.possibleCenters=new Array,this.startX=t,this.startY=n,this.width=r,this.height=i,this.moduleSize=s,this.crossCheckStateCount=new Array(0,0,0),this.resultPointCallback=o,this.centerFromEnd=function(e,t){return t-e[2]-e[1]/2},this.foundPatternCross=function(e){for(var t=this.moduleSize,n=t/2,r=0;3>r;r++)if(Math.abs(t-e[r])>=n)return!1;return!0},this.crossCheckVertical=function(e,t,n,r){var i=this.image,s=i.height,o=this.crossCheckStateCount;o[0]=0,o[1]=0,o[2]=0;for(var a=e;a>=0&&i.data[t+a*i.width]&&o[1]<=n;)o[1]++,a--;if(0>a||o[1]>n)return NaN;for(;a>=0&&!i.data[t+a*i.width]&&o[0]<=n;)o[0]++,a--;if(o[0]>n)return NaN;for(a=e+1;s>a&&i.data[t+a*i.width]&&o[1]<=n;)o[1]++,a++;if(a==s||o[1]>n)return NaN;for(;s>a&&!i.data[t+a*i.width]&&o[2]<=n;)o[2]++,a++;if(o[2]>n)return NaN;var h=o[0]+o[1]+o[2];return 5*Math.abs(h-r)>=2*r?NaN:this.foundPatternCross(o)?this.centerFromEnd(o,a):NaN},this.handlePossibleCenter=function(e,t,n){var r=e[0]+e[1]+e[2],i=this.centerFromEnd(e,n),s=this.crossCheckVertical(t,Math.floor(i),2*e[1],r);if(!isNaN(s)){for(var o=(e[0]+e[1]+e[2])/3,a=this.possibleCenters.length,h=0;a>h;h++){var w=this.possibleCenters[h];if(w.aboutEquals(o,s,i))return new x(i,s,o)}var f=new x(i,s,o);this.possibleCenters.push(f),null!=this.resultPointCallback&&this.resultPointCallback.foundPossibleResultPoint(f)}return null},this.find=function(){for(var t=this.startX,i=this.height,s=t+r,o=n+(i>>1),a=new Array(0,0,0),h=0;i>h;h++){
  var w=o+(0==(1&h)?h+1>>1:-(h+1>>1));a[0]=0,a[1]=0,a[2]=0;for(var f=t;s>f&&!e.data[f+e.width*w];)f++;for(var u=0;s>f;){if(e.data[f+w*e.width])if(1==u)a[u]++;else if(2==u){if(this.foundPatternCross(a)){var d=this.handlePossibleCenter(a,w,f);if(null!=d)return d}a[0]=a[2],a[1]=1,a[2]=0,u=1}else a[++u]++;else 1==u&&u++,a[u]++;f++}if(this.foundPatternCross(a)){var d=this.handlePossibleCenter(a,w,s);if(null!=d)return d}}if(0!=this.possibleCenters.length)return this.possibleCenters[0];throw"Couldn't find enough alignment patterns"}}function O(e,t,n){this.blockPointer=0,this.bitPointer=7,this.dataLength=0,this.blocks=e,this.numErrorCorrectionCode=n,9>=t?this.dataLengthMode=0:t>=10&&26>=t?this.dataLengthMode=1:t>=27&&40>=t&&(this.dataLengthMode=2),this.getNextBits=function(e){var t=0;if(e<this.bitPointer+1){for(var n=0,r=0;e>r;r++)n+=1<<r;return n<<=this.bitPointer-e+1,t=(this.blocks[this.blockPointer]&n)>>this.bitPointer-e+1,this.bitPointer-=e,t}if(e<this.bitPointer+1+8){for(var i=0,r=0;r<this.bitPointer+1;r++)i+=1<<r;return t=(this.blocks[this.blockPointer]&i)<<e-(this.bitPointer+1),this.blockPointer++,t+=this.blocks[this.blockPointer]>>8-(e-(this.bitPointer+1)),this.bitPointer=this.bitPointer-e%8,this.bitPointer<0&&(this.bitPointer=8+this.bitPointer),t}if(e<this.bitPointer+1+16){for(var i=0,s=0,r=0;r<this.bitPointer+1;r++)i+=1<<r;var o=(this.blocks[this.blockPointer]&i)<<e-(this.bitPointer+1);this.blockPointer++;var a=this.blocks[this.blockPointer]<<e-(this.bitPointer+1+8);this.blockPointer++;for(var r=0;r<e-(this.bitPointer+1+8);r++)s+=1<<r;s<<=8-(e-(this.bitPointer+1+8));var h=(this.blocks[this.blockPointer]&s)>>8-(e-(this.bitPointer+1+8));return t=o+a+h,this.bitPointer=this.bitPointer-(e-8)%8,this.bitPointer<0&&(this.bitPointer=8+this.bitPointer),t}return 0},this.NextMode=function(){return this.blockPointer>this.blocks.length-this.numErrorCorrectionCode-2?0:this.getNextBits(4)},this.getDataLength=function(e){for(var t=0;;){if(e>>t==1)break;t++}return this.getNextBits(j.sizeOfDataLengthInfo[this.dataLengthMode][t])},this.getRomanAndFigureString=function(e){var t=e,n=0,r="",i=new Array("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"," ","$","%","*","+","-",".","/",":");do if(t>1){n=this.getNextBits(11);var s=Math.floor(n/45),o=n%45;r+=i[s],r+=i[o],t-=2}else 1==t&&(n=this.getNextBits(6),r+=i[n],t-=1);while(t>0);return r},this.getFigureString=function(e){var t=e,n=0,r="";do t>=3?(n=this.getNextBits(10),100>n&&(r+="0"),10>n&&(r+="0"),t-=3):2==t?(n=this.getNextBits(7),10>n&&(r+="0"),t-=2):1==t&&(n=this.getNextBits(4),t-=1),r+=n;while(t>0);return r},this.get8bitByteArray=function(e){var t=e,n=0,r=new Array;do n=this.getNextBits(8),r.push(n),t--;while(t>0);return r},this.getKanjiString=function(e){var t=e,n=0,r="";do{n=getNextBits(13);var i=n%192,s=n/192,o=(s<<8)+i,a=0;a=40956>=o+33088?o+33088:o+49472,r+=String.fromCharCode(a),t--}while(t>0);return r},Object.defineProperty(this,"DataByte",{get:function(){for(var e=new Array,t=1,n=2,r=4,i=8;;){var s=this.NextMode();if(0==s){if(e.length>0)break;throw"Empty data block"}if(s!=t&&s!=n&&s!=r&&s!=i)throw"Invalid mode: "+s+" in (block:"+this.blockPointer+" bit:"+this.bitPointer+")";if(dataLength=this.getDataLength(s),dataLength<1)throw"Invalid data length: "+dataLength;switch(s){case t:for(var o=this.getFigureString(dataLength),a=new Array(o.length),h=0;h<o.length;h++)a[h]=o.charCodeAt(h);e.push(a);break;case n:for(var o=this.getRomanAndFigureString(dataLength),a=new Array(o.length),h=0;h<o.length;h++)a[h]=o.charCodeAt(h);e.push(a);break;case r:var w=this.get8bitByteArray(dataLength);e.push(w);break;case i:var o=this.getKanjiString(dataLength);e.push(o)}}return e}})}var F={};F.checkAndNudgePoints=function(e,t){for(var n=e.width,r=e.height,i=!0,s=0;s<t.length&&i;s+=2){var o=Math.floor(t[s]),a=Math.floor(t[s+1]);if(-1>o||o>n||-1>a||a>r)throw"Error.checkAndNudgePoints ";i=!1,-1==o?(t[s]=0,i=!0):o==n&&(t[s]=n-1,i=!0),-1==a?(t[s+1]=0,i=!0):a==r&&(t[s+1]=r-1,i=!0)}i=!0;for(var s=t.length-2;s>=0&&i;s-=2){var o=Math.floor(t[s]),a=Math.floor(t[s+1]);if(-1>o||o>n||-1>a||a>r)throw"Error.checkAndNudgePoints ";i=!1,-1==o?(t[s]=0,i=!0):o==n&&(t[s]=n-1,i=!0),-1==a?(t[s+1]=0,i=!0):a==r&&(t[s+1]=r-1,i=!0)}},F.sampleGrid3=function(e,t,n){for(var r=new u(t),i=new Array(t<<1),s=0;t>s;s++){for(var o=i.length,a=s+.5,h=0;o>h;h+=2)i[h]=(h>>1)+.5,i[h+1]=a;n.transformPoints1(i),F.checkAndNudgePoints(e,i);try{for(var h=0;o>h;h+=2){var w=(4*Math.floor(i[h])+Math.floor(i[h+1])*e.width*4,e.data[Math.floor(i[h])+e.width*Math.floor(i[h+1])]);w&&r.set_Renamed(h>>1,s)}}catch(f){throw"Error.checkAndNudgePoints"}}return r},i.VERSION_DECODE_INFO=new Array(31892,34236,39577,42195,48118,51042,55367,58893,63784,68472,70749,76311,79154,84390,87683,92361,96236,102084,102881,110507,110734,117786,119615,126325,127568,133589,136944,141498,145311,150283,152622,158308,161089,167017),i.VERSIONS=s(),i.getVersionForNumber=function(e){if(1>e||e>40)throw"ArgumentException";return i.VERSIONS[e-1]},i.getProvisionalVersionForDimension=function(e){if(e%4!=1)throw"Error getProvisionalVersionForDimension";try{return i.getVersionForNumber(e-17>>2)}catch(t){throw"Error getVersionForNumber"}},i.decodeVersionInformation=function(e){for(var t=4294967295,n=0,r=0;r<i.VERSION_DECODE_INFO.length;r++){var s=i.VERSION_DECODE_INFO[r];if(s==e)return this.getVersionForNumber(r+7);var o=w.numBitsDiffering(e,s);t>o&&(n=r+7,t=o)}return 3>=t?this.getVersionForNumber(n):null},o.quadrilateralToQuadrilateral=function(e,t,n,r,i,s,o,a,h,w,f,u,d,l,c,g){var v=this.quadrilateralToSquare(e,t,n,r,i,s,o,a),m=this.squareToQuadrilateral(h,w,f,u,d,l,c,g);return m.times(v)},o.squareToQuadrilateral=function(e,t,n,r,i,s,a,h){return dy2=h-s,dy3=t-r+s-h,0==dy2&&0==dy3?new o(n-e,i-n,e,r-t,s-r,t,0,0,1):(dx1=n-i,dx2=a-i,dx3=e-n+i-a,dy1=r-s,denominator=dx1*dy2-dx2*dy1,a13=(dx3*dy2-dx2*dy3)/denominator,a23=(dx1*dy3-dx3*dy1)/denominator,new o(n-e+a13*n,a-e+a23*a,e,r-t+a13*r,h-t+a23*h,t,a13,a23,1))},o.quadrilateralToSquare=function(e,t,n,r,i,s,o,a){return this.squareToQuadrilateral(e,t,n,r,i,s,o,a).buildAdjoint()};var R=21522,I=new Array(new Array(21522,0),new Array(20773,1),new Array(24188,2),new Array(23371,3),new Array(17913,4),new Array(16590,5),new Array(20375,6),new Array(19104,7),new Array(30660,8),new Array(29427,9),new Array(32170,10),new Array(30877,11),new Array(26159,12),new Array(25368,13),new Array(27713,14),new Array(26998,15),new Array(5769,16),new Array(5054,17),new Array(7399,18),new Array(6608,19),new Array(1890,20),new Array(597,21),new Array(3340,22),new Array(2107,23),new Array(13663,24),new Array(12392,25),new Array(16177,26),new Array(14854,27),new Array(9396,28),new Array(8579,29),new Array(11994,30),new Array(11245,31)),z=new Array(0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4);w.numBitsDiffering=function(e,t){return e^=t,z[15&e]+z[15&N(e,4)]+z[15&N(e,8)]+z[15&N(e,12)]+z[15&N(e,16)]+z[15&N(e,20)]+z[15&N(e,24)]+z[15&N(e,28)]},w.decodeFormatInformation=function(e){var t=w.doDecodeFormatInformation(e);return null!=t?t:w.doDecodeFormatInformation(e^R)},w.doDecodeFormatInformation=function(e){for(var t=4294967295,n=0,r=0;r<I.length;r++){var i=I[r],s=i[0];if(s==e)return new w(i[1]);var o=this.numBitsDiffering(e,s);t>o&&(n=i[1],t=o)}return 3>=t?new w(n):null},f.forBits=function(e){if(0>e||e>=V.length)throw"ArgumentException";return V[e]};var V=new Array(new f(1,0,"M"),new f(0,1,"L"),new f(3,2,"H"),new f(2,3,"Q"));d.getDataBlocks=function(e,t,n){if(e.length!=t.TotalCodewords)throw"ArgumentException";for(var r=t.getECBlocksForLevel(n),i=0,s=r.getECBlocks(),o=0;o<s.length;o++)i+=s[o].Count;for(var a=new Array(i),h=0,w=0;w<s.length;w++)for(var f=s[w],o=0;o<f.Count;o++){var u=f.DataCodewords,l=r.ECCodewordsPerBlock+u;a[h++]=new d(u,new Array(l))}for(var c=a[0].codewords.length,g=a.length-1;g>=0;){var v=a[g].codewords.length;if(v==c)break;g--}g++;for(var m=c-r.ECCodewordsPerBlock,y=0,o=0;m>o;o++)for(var w=0;h>w;w++)a[w].codewords[o]=e[y++];for(var w=g;h>w;w++)a[w].codewords[m]=e[y++];for(var b=a[0].codewords.length,o=m;b>o;o++)for(var w=0;h>w;w++){var p=g>w?o:o+1;a[w].codewords[p]=e[y++]}return a};var L={};L.forReference=function(e){if(0>e||e>7)throw"System.ArgumentException";return L.DATA_MASKS[e]},L.DATA_MASKS=new Array(new c,new g,new v,new m,new y,new b,new C,new A),k.QR_CODE_FIELD=new k(285),k.DATA_MATRIX_FIELD=new k(301),k.addOrSubtract=function(e,t){return e^t};var T={};T.rsDecoder=new M(k.QR_CODE_FIELD),T.correctErrors=function(e,t){for(var n=e.length,r=new Array(n),i=0;n>i;i++)r[i]=255&e[i];var s=e.length-t;try{T.rsDecoder.decode(r,s)}catch(o){throw o}for(var i=0;t>i;i++)e[i]=r[i]},T.decode=function(e){for(var t=new l(e),n=t.readVersion(),r=t.readFormatInformation().ErrorCorrectionLevel,i=t.readCodewords(),s=d.getDataBlocks(i,n,r),o=0,a=0;a<s.length;a++)o+=s[a].NumDataCodewords;for(var h=new Array(o),w=0,f=0;f<s.length;f++){var u=s[f],c=u.Codewords,g=u.NumDataCodewords;T.correctErrors(c,g);for(var a=0;g>a;a++)h[w++]=c[a]}var v=new O(h,n.VersionNumber,r.Bits);return v};var j={};j.sizeOfDataLengthInfo=[[10,9,8,8],[12,11,16,10],[14,13,16,12]],QrCode=function(){this.imagedata=null,this.width=0,this.height=0,this.qrCodeSymbol=null,this.debug=!1,this.callback=null,this.decode=function(e,t){var n=function(){try{this.result=this.process(this.imagedata)}catch(e){this.result="error decoding QR Code: "+e}return null!=this.callback&&this.callback(this.result),this.result}.bind(this);if(void 0==e){var r=document.getElementById("qr-canvas"),i=r.getContext("2d");this.width=r.width,this.height=r.height,this.imagedata=i.getImageData(0,0,this.width,this.height),n()}else if(void 0!=e.width)this.width=e.width,this.height=e.height,this.imagedata={data:t||e.data},this.imagedata.width=e.width,this.imagedata.height=e.height,n();else{var s=new Image;s.onload=function(){var e=document.createElement("canvas"),t=e.getContext("2d"),r=document.getElementById("out-canvas");if(null!=r){var i=r.getContext("2d");i.clearRect(0,0,320,240),i.drawImage(s,0,0,320,240)}e.width=s.width,e.height=s.height,t.drawImage(s,0,0),this.width=s.width,this.height=s.height;try{this.imagedata=t.getImageData(0,0,s.width,s.height)}catch(o){if(this.result="Cross domain image reading not supported in your browser! Save it to your computer then drag and drop the file!",null!=this.callback)return this.callback(this.result)}n()}.bind(this),s.src=e}},this.decode_utf8=function(e){return decodeURIComponent(escape(e))},this.process=function(e){for(var t=(new Date).getTime(),n=this.grayScaleToBitmap(this.grayscale(e)),r=new h(n),i=r.detect(),s=T.decode(i.bits),o=s.DataByte,a="",w=0;w<o.length;w++)for(var f=0;f<o[w].length;f++)a+=String.fromCharCode(o[w][f]);var u=(new Date).getTime(),d=u-t;return this.debug&&console.log("QR Code processing time (ms): "+d),this.decode_utf8(a)},this.getPixel=function(e,t,n){if(e.width<t)throw"point error";if(e.height<n)throw"point error";return point=4*t+n*e.width*4,p=(33*e.data[point]+34*e.data[point+1]+33*e.data[point+2])/100,p},this.binarize=function(e){for(var t=new Array(this.width*this.height),n=0;n<this.height;n++)for(var r=0;r<this.width;r++){var i=this.getPixel(r,n);t[r+n*this.width]=e>=i}return t},this.getMiddleBrightnessPerArea=function(e){for(var t=4,n=Math.floor(e.width/t),r=Math.floor(e.height/t),i=new Array(t),s=0;t>s;s++){i[s]=new Array(t);for(var o=0;t>o;o++)i[s][o]=new Array(0,0)}for(var a=0;t>a;a++)for(var h=0;t>h;h++){i[h][a][0]=255;for(var w=0;r>w;w++)for(var f=0;n>f;f++){var u=e.data[n*h+f+(r*a+w)*e.width];u<i[h][a][0]&&(i[h][a][0]=u),u>i[h][a][1]&&(i[h][a][1]=u)}}for(var d=new Array(t),l=0;t>l;l++)d[l]=new Array(t);for(var a=0;t>a;a++)for(var h=0;t>h;h++)d[h][a]=Math.floor((i[h][a][0]+i[h][a][1])/2);return d},this.grayScaleToBitmap=function(e){for(var t=this.getMiddleBrightnessPerArea(e),n=t.length,r=Math.floor(e.width/n),i=Math.floor(e.height/n),s=0;n>s;s++)for(var o=0;n>o;o++)for(var a=0;i>a;a++)for(var h=0;r>h;h++)e.data[r*o+h+(i*s+a)*e.width]=e.data[r*o+h+(i*s+a)*e.width]<t[o][s];return e},this.grayscale=function(e){for(var t=new Array(e.width*e.height),n=0;n<e.height;n++)for(var r=0;r<e.width;r++){var i=this.getPixel(e,r,n);t[r+n*e.width]=i}return{height:e.height,width:e.width,data:t}}};var X=3,_=57,Y=8,q=2;j.orderBestPatterns=function(e){function t(e,t){return xDiff=e.X-t.X,yDiff=e.Y-t.Y,Math.sqrt(xDiff*xDiff+yDiff*yDiff)}function n(e,t,n){var r=t.x,i=t.y;return(n.x-r)*(e.y-i)-(n.y-i)*(e.x-r)}var r,i,s,o=t(e[0],e[1]),a=t(e[1],e[2]),h=t(e[0],e[2]);if(a>=o&&a>=h?(i=e[0],r=e[1],s=e[2]):h>=a&&h>=o?(i=e[1],r=e[0],s=e[2]):(i=e[2],r=e[0],s=e[1]),n(r,i,s)<0){var w=r;r=s,s=w}e[0]=r,e[1]=i,e[2]=s},e.exports=QrCode}]);
},{}],317:[function(require,module,exports){
var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this.parsedData=[];for(var b=[],d=0,e=this.data.length;e>d;d++){var f=this.data.charCodeAt(d);f>65536?(b[0]=240|(1835008&f)>>>18,b[1]=128|(258048&f)>>>12,b[2]=128|(4032&f)>>>6,b[3]=128|63&f):f>2048?(b[0]=224|(61440&f)>>>12,b[1]=128|(4032&f)>>>6,b[2]=128|63&f):f>128?(b[0]=192|(1984&f)>>>6,b[1]=128|63&f):b[0]=f,this.parsedData=this.parsedData.concat(b)}this.parsedData.length!=this.data.length&&(this.parsedData.unshift(191),this.parsedData.unshift(187),this.parsedData.unshift(239))}function b(a,b){this.typeNumber=a,this.errorCorrectLevel=b,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}function i(a,b){if(void 0==a.length)throw new Error(a.length+"/"+b);for(var c=0;c<a.length&&0==a[c];)c++;this.num=new Array(a.length-c+b);for(var d=0;d<a.length-c;d++)this.num[d]=a[d+c]}function j(a,b){this.totalCount=a,this.dataCount=b}function k(){this.buffer=[],this.length=0}function m(){return"undefined"!=typeof CanvasRenderingContext2D}function n(){var a=!1,b=navigator.userAgent;return/android/i.test(b)&&(a=!0,aMat=b.toString().match(/android ([0-9]\.[0-9])/i),aMat&&aMat[1]&&(a=parseFloat(aMat[1]))),a}function r(a,b){for(var c=1,e=s(a),f=0,g=l.length;g>=f;f++){var h=0;switch(b){case d.L:h=l[f][0];break;case d.M:h=l[f][1];break;case d.Q:h=l[f][2];break;case d.H:h=l[f][3]}if(h>=e)break;c++}if(c>l.length)throw new Error("Too long data");return c}function s(a){var b=encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g,"a");return b.length+(b.length!=a?3:0)}a.prototype={getLength:function(){return this.parsedData.length},write:function(a){for(var b=0,c=this.parsedData.length;c>b;b++)a.put(this.parsedData[b],8)}},b.prototype={addData:function(b){var c=new a(b);this.dataList.push(c),this.dataCache=null},isDark:function(a,b){if(0>a||this.moduleCount<=a||0>b||this.moduleCount<=b)throw new Error(a+","+b);return this.modules[a][b]},getModuleCount:function(){return this.moduleCount},make:function(){this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17,this.modules=new Array(this.moduleCount);for(var d=0;d<this.moduleCount;d++){this.modules[d]=new Array(this.moduleCount);for(var e=0;e<this.moduleCount;e++)this.modules[d][e]=null}this.setupPositionProbePattern(0,0),this.setupPositionProbePattern(this.moduleCount-7,0),this.setupPositionProbePattern(0,this.moduleCount-7),this.setupPositionAdjustPattern(),this.setupTimingPattern(),this.setupTypeInfo(a,c),this.typeNumber>=7&&this.setupTypeNumber(a),null==this.dataCache&&(this.dataCache=b.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,b){for(var c=-1;7>=c;c++)if(!(-1>=a+c||this.moduleCount<=a+c))for(var d=-1;7>=d;d++)-1>=b+d||this.moduleCount<=b+d||(this.modules[a+c][b+d]=c>=0&&6>=c&&(0==d||6==d)||d>=0&&6>=d&&(0==c||6==c)||c>=2&&4>=c&&d>=2&&4>=d?!0:!1)},getBestMaskPattern:function(){for(var a=0,b=0,c=0;8>c;c++){this.makeImpl(!0,c);var d=f.getLostPoint(this);(0==c||a>d)&&(a=d,b=c)}return b},createMovieClip:function(a,b,c){var d=a.createEmptyMovieClip(b,c),e=1;this.make();for(var f=0;f<this.modules.length;f++)for(var g=f*e,h=0;h<this.modules[f].length;h++){var i=h*e,j=this.modules[f][h];j&&(d.beginFill(0,100),d.moveTo(i,g),d.lineTo(i+e,g),d.lineTo(i+e,g+e),d.lineTo(i,g+e),d.endFill())}return d},setupTimingPattern:function(){for(var a=8;a<this.moduleCount-8;a++)null==this.modules[a][6]&&(this.modules[a][6]=0==a%2);for(var b=8;b<this.moduleCount-8;b++)null==this.modules[6][b]&&(this.modules[6][b]=0==b%2)},setupPositionAdjustPattern:function(){for(var a=f.getPatternPosition(this.typeNumber),b=0;b<a.length;b++)for(var c=0;c<a.length;c++){var d=a[b],e=a[c];if(null==this.modules[d][e])for(var g=-2;2>=g;g++)for(var h=-2;2>=h;h++)this.modules[d+g][e+h]=-2==g||2==g||-2==h||2==h||0==g&&0==h?!0:!1}},setupTypeNumber:function(a){for(var b=f.getBCHTypeNumber(this.typeNumber),c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[Math.floor(c/3)][c%3+this.moduleCount-8-3]=d}for(var c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[c%3+this.moduleCount-8-3][Math.floor(c/3)]=d}},setupTypeInfo:function(a,b){for(var c=this.errorCorrectLevel<<3|b,d=f.getBCHTypeInfo(c),e=0;15>e;e++){var g=!a&&1==(1&d>>e);6>e?this.modules[e][8]=g:8>e?this.modules[e+1][8]=g:this.modules[this.moduleCount-15+e][8]=g}for(var e=0;15>e;e++){var g=!a&&1==(1&d>>e);8>e?this.modules[8][this.moduleCount-e-1]=g:9>e?this.modules[8][15-e-1+1]=g:this.modules[8][15-e-1]=g}this.modules[this.moduleCount-8][8]=!a},mapData:function(a,b){for(var c=-1,d=this.moduleCount-1,e=7,g=0,h=this.moduleCount-1;h>0;h-=2)for(6==h&&h--;;){for(var i=0;2>i;i++)if(null==this.modules[d][h-i]){var j=!1;g<a.length&&(j=1==(1&a[g]>>>e));var k=f.getMask(b,d,h-i);k&&(j=!j),this.modules[d][h-i]=j,e--,-1==e&&(g++,e=7)}if(d+=c,0>d||this.moduleCount<=d){d-=c,c=-c;break}}}},b.PAD0=236,b.PAD1=17,b.createData=function(a,c,d){for(var e=j.getRSBlocks(a,c),g=new k,h=0;h<d.length;h++){var i=d[h];g.put(i.mode,4),g.put(i.getLength(),f.getLengthInBits(i.mode,a)),i.write(g)}for(var l=0,h=0;h<e.length;h++)l+=e[h].dataCount;if(g.getLengthInBits()>8*l)throw new Error("code length overflow. ("+g.getLengthInBits()+">"+8*l+")");for(g.getLengthInBits()+4<=8*l&&g.put(0,4);0!=g.getLengthInBits()%8;)g.putBit(!1);for(;;){if(g.getLengthInBits()>=8*l)break;if(g.put(b.PAD0,8),g.getLengthInBits()>=8*l)break;g.put(b.PAD1,8)}return b.createBytes(g,e)},b.createBytes=function(a,b){for(var c=0,d=0,e=0,g=new Array(b.length),h=new Array(b.length),j=0;j<b.length;j++){var k=b[j].dataCount,l=b[j].totalCount-k;d=Math.max(d,k),e=Math.max(e,l),g[j]=new Array(k);for(var m=0;m<g[j].length;m++)g[j][m]=255&a.buffer[m+c];c+=k;var n=f.getErrorCorrectPolynomial(l),o=new i(g[j],n.getLength()-1),p=o.mod(n);h[j]=new Array(n.getLength()-1);for(var m=0;m<h[j].length;m++){var q=m+p.getLength()-h[j].length;h[j][m]=q>=0?p.get(q):0}}for(var r=0,m=0;m<b.length;m++)r+=b[m].totalCount;for(var s=new Array(r),t=0,m=0;d>m;m++)for(var j=0;j<b.length;j++)m<g[j].length&&(s[t++]=g[j][m]);for(var m=0;e>m;m++)for(var j=0;j<b.length;j++)m<h[j].length&&(s[t++]=h[j][m]);return s};for(var c={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},d={L:1,M:0,Q:3,H:2},e={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},f={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:1335,G18:7973,G15_MASK:21522,getBCHTypeInfo:function(a){for(var b=a<<10;f.getBCHDigit(b)-f.getBCHDigit(f.G15)>=0;)b^=f.G15<<f.getBCHDigit(b)-f.getBCHDigit(f.G15);return(a<<10|b)^f.G15_MASK},getBCHTypeNumber:function(a){for(var b=a<<12;f.getBCHDigit(b)-f.getBCHDigit(f.G18)>=0;)b^=f.G18<<f.getBCHDigit(b)-f.getBCHDigit(f.G18);return a<<12|b},getBCHDigit:function(a){for(var b=0;0!=a;)b++,a>>>=1;return b},getPatternPosition:function(a){return f.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,b,c){switch(a){case e.PATTERN000:return 0==(b+c)%2;case e.PATTERN001:return 0==b%2;case e.PATTERN010:return 0==c%3;case e.PATTERN011:return 0==(b+c)%3;case e.PATTERN100:return 0==(Math.floor(b/2)+Math.floor(c/3))%2;case e.PATTERN101:return 0==b*c%2+b*c%3;case e.PATTERN110:return 0==(b*c%2+b*c%3)%2;case e.PATTERN111:return 0==(b*c%3+(b+c)%2)%2;default:throw new Error("bad maskPattern:"+a)}},getErrorCorrectPolynomial:function(a){for(var b=new i([1],0),c=0;a>c;c++)b=b.multiply(new i([1,g.gexp(c)],0));return b},getLengthInBits:function(a,b){if(b>=1&&10>b)switch(a){case c.MODE_NUMBER:return 10;case c.MODE_ALPHA_NUM:return 9;case c.MODE_8BIT_BYTE:return 8;case c.MODE_KANJI:return 8;default:throw new Error("mode:"+a)}else if(27>b)switch(a){case c.MODE_NUMBER:return 12;case c.MODE_ALPHA_NUM:return 11;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 10;default:throw new Error("mode:"+a)}else{if(!(41>b))throw new Error("type:"+b);switch(a){case c.MODE_NUMBER:return 14;case c.MODE_ALPHA_NUM:return 13;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 12;default:throw new Error("mode:"+a)}}},getLostPoint:function(a){for(var b=a.getModuleCount(),c=0,d=0;b>d;d++)for(var e=0;b>e;e++){for(var f=0,g=a.isDark(d,e),h=-1;1>=h;h++)if(!(0>d+h||d+h>=b))for(var i=-1;1>=i;i++)0>e+i||e+i>=b||(0!=h||0!=i)&&g==a.isDark(d+h,e+i)&&f++;f>5&&(c+=3+f-5)}for(var d=0;b-1>d;d++)for(var e=0;b-1>e;e++){var j=0;a.isDark(d,e)&&j++,a.isDark(d+1,e)&&j++,a.isDark(d,e+1)&&j++,a.isDark(d+1,e+1)&&j++,(0==j||4==j)&&(c+=3)}for(var d=0;b>d;d++)for(var e=0;b-6>e;e++)a.isDark(d,e)&&!a.isDark(d,e+1)&&a.isDark(d,e+2)&&a.isDark(d,e+3)&&a.isDark(d,e+4)&&!a.isDark(d,e+5)&&a.isDark(d,e+6)&&(c+=40);for(var e=0;b>e;e++)for(var d=0;b-6>d;d++)a.isDark(d,e)&&!a.isDark(d+1,e)&&a.isDark(d+2,e)&&a.isDark(d+3,e)&&a.isDark(d+4,e)&&!a.isDark(d+5,e)&&a.isDark(d+6,e)&&(c+=40);for(var k=0,e=0;b>e;e++)for(var d=0;b>d;d++)a.isDark(d,e)&&k++;var l=Math.abs(100*k/b/b-50)/5;return c+=10*l}},g={glog:function(a){if(1>a)throw new Error("glog("+a+")");return g.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;a>=256;)a-=255;return g.EXP_TABLE[a]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},h=0;8>h;h++)g.EXP_TABLE[h]=1<<h;for(var h=8;256>h;h++)g.EXP_TABLE[h]=g.EXP_TABLE[h-4]^g.EXP_TABLE[h-5]^g.EXP_TABLE[h-6]^g.EXP_TABLE[h-8];for(var h=0;255>h;h++)g.LOG_TABLE[g.EXP_TABLE[h]]=h;i.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var b=new Array(this.getLength()+a.getLength()-1),c=0;c<this.getLength();c++)for(var d=0;d<a.getLength();d++)b[c+d]^=g.gexp(g.glog(this.get(c))+g.glog(a.get(d)));return new i(b,0)},mod:function(a){if(this.getLength()-a.getLength()<0)return this;for(var b=g.glog(this.get(0))-g.glog(a.get(0)),c=new Array(this.getLength()),d=0;d<this.getLength();d++)c[d]=this.get(d);for(var d=0;d<a.getLength();d++)c[d]^=g.gexp(g.glog(a.get(d))+b);return new i(c,0).mod(a)}},j.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],j.getRSBlocks=function(a,b){var c=j.getRsBlockTable(a,b);if(void 0==c)throw new Error("bad rs block @ typeNumber:"+a+"/errorCorrectLevel:"+b);for(var d=c.length/3,e=[],f=0;d>f;f++)for(var g=c[3*f+0],h=c[3*f+1],i=c[3*f+2],k=0;g>k;k++)e.push(new j(h,i));return e},j.getRsBlockTable=function(a,b){switch(b){case d.L:return j.RS_BLOCK_TABLE[4*(a-1)+0];case d.M:return j.RS_BLOCK_TABLE[4*(a-1)+1];case d.Q:return j.RS_BLOCK_TABLE[4*(a-1)+2];case d.H:return j.RS_BLOCK_TABLE[4*(a-1)+3];default:return void 0}},k.prototype={get:function(a){var b=Math.floor(a/8);return 1==(1&this.buffer[b]>>>7-a%8)},put:function(a,b){for(var c=0;b>c;c++)this.putBit(1==(1&a>>>b-c-1))},getLengthInBits:function(){return this.length},putBit:function(a){var b=Math.floor(this.length/8);this.buffer.length<=b&&this.buffer.push(0),a&&(this.buffer[b]|=128>>>this.length%8),this.length++}};var l=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]],o=function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){function g(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg",a);for(var d in b)b.hasOwnProperty(d)&&c.setAttribute(d,b[d]);return c}var b=this._htOption,c=this._el,d=a.getModuleCount();Math.floor(b.width/d),Math.floor(b.height/d),this.clear();var h=g("svg",{viewBox:"0 0 "+String(d)+" "+String(d),width:"100%",height:"100%",fill:b.colorLight});h.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink"),c.appendChild(h),h.appendChild(g("rect",{fill:b.colorDark,width:"1",height:"1",id:"template"}));for(var i=0;d>i;i++)for(var j=0;d>j;j++)if(a.isDark(i,j)){var k=g("use",{x:String(i),y:String(j)});k.setAttributeNS("http://www.w3.org/1999/xlink","href","#template"),h.appendChild(k)}},a.prototype.clear=function(){for(;this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild)},a}(),p="svg"===document.documentElement.tagName.toLowerCase(),q=p?o:m()?function(){function a(){this._elImage.src=this._elCanvas.toDataURL("image/png"),this._elImage.style.display="block",this._elCanvas.style.display="none"}function d(a,b){var c=this;if(c._fFail=b,c._fSuccess=a,null===c._bSupportDataURI){var d=document.createElement("img"),e=function(){c._bSupportDataURI=!1,c._fFail&&_fFail.call(c)},f=function(){c._bSupportDataURI=!0,c._fSuccess&&c._fSuccess.call(c)};return d.onabort=e,d.onerror=e,d.onload=f,d.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",void 0}c._bSupportDataURI===!0&&c._fSuccess?c._fSuccess.call(c):c._bSupportDataURI===!1&&c._fFail&&c._fFail.call(c)}if(this._android&&this._android<=2.1){var b=1/window.devicePixelRatio,c=CanvasRenderingContext2D.prototype.drawImage;CanvasRenderingContext2D.prototype.drawImage=function(a,d,e,f,g,h,i,j){if("nodeName"in a&&/img/i.test(a.nodeName))for(var l=arguments.length-1;l>=1;l--)arguments[l]=arguments[l]*b;else"undefined"==typeof j&&(arguments[1]*=b,arguments[2]*=b,arguments[3]*=b,arguments[4]*=b);c.apply(this,arguments)}}var e=function(a,b){this._bIsPainted=!1,this._android=n(),this._htOption=b,this._elCanvas=document.createElement("canvas"),this._elCanvas.width=b.width,this._elCanvas.height=b.height,a.appendChild(this._elCanvas),this._el=a,this._oContext=this._elCanvas.getContext("2d"),this._bIsPainted=!1,this._elImage=document.createElement("img"),this._elImage.style.display="none",this._el.appendChild(this._elImage),this._bSupportDataURI=null};return e.prototype.draw=function(a){var b=this._elImage,c=this._oContext,d=this._htOption,e=a.getModuleCount(),f=d.width/e,g=d.height/e,h=Math.round(f),i=Math.round(g);b.style.display="none",this.clear();for(var j=0;e>j;j++)for(var k=0;e>k;k++){var l=a.isDark(j,k),m=k*f,n=j*g;c.strokeStyle=l?d.colorDark:d.colorLight,c.lineWidth=1,c.fillStyle=l?d.colorDark:d.colorLight,c.fillRect(m,n,f,g),c.strokeRect(Math.floor(m)+.5,Math.floor(n)+.5,h,i),c.strokeRect(Math.ceil(m)-.5,Math.ceil(n)-.5,h,i)}this._bIsPainted=!0},e.prototype.makeImage=function(){this._bIsPainted&&d.call(this,a)},e.prototype.isPainted=function(){return this._bIsPainted},e.prototype.clear=function(){this._oContext.clearRect(0,0,this._elCanvas.width,this._elCanvas.height),this._bIsPainted=!1},e.prototype.round=function(a){return a?Math.floor(1e3*a)/1e3:a},e}():function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){for(var b=this._htOption,c=this._el,d=a.getModuleCount(),e=Math.floor(b.width/d),f=Math.floor(b.height/d),g=['<table style="border:0;border-collapse:collapse;">'],h=0;d>h;h++){g.push("<tr>");for(var i=0;d>i;i++)g.push('<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:'+e+"px;height:"+f+"px;background-color:"+(a.isDark(h,i)?b.colorDark:b.colorLight)+';"></td>');g.push("</tr>")}g.push("</table>"),c.innerHTML=g.join("");var j=c.childNodes[0],k=(b.width-j.offsetWidth)/2,l=(b.height-j.offsetHeight)/2;k>0&&l>0&&(j.style.margin=l+"px "+k+"px")},a.prototype.clear=function(){this._el.innerHTML=""},a}();QRCode=function(a,b){if(this._htOption={width:256,height:256,typeNumber:4,colorDark:"#000000",colorLight:"#ffffff",correctLevel:d.H},"string"==typeof b&&(b={text:b}),b)for(var c in b)this._htOption[c]=b[c];"string"==typeof a&&(a=document.getElementById(a)),this._android=n(),this._el=a,this._oQRCode=null,this._oDrawing=new q(this._el,this._htOption),this._htOption.text&&this.makeCode(this._htOption.text)},QRCode.prototype.makeCode=function(a){this._oQRCode=new b(r(a,this._htOption.correctLevel),this._htOption.correctLevel),this._oQRCode.addData(a),this._oQRCode.make(),this._el.title=a,this._oDrawing.draw(this._oQRCode),this.makeImage()},QRCode.prototype.makeImage=function(){"function"==typeof this._oDrawing.makeImage&&(!this._android||this._android>=3)&&this._oDrawing.makeImage()},QRCode.prototype.clear=function(){this._oDrawing.clear()},QRCode.CorrectLevel=d}();
module.exports = QRCode;
},{}],318:[function(require,module,exports){
/*
 * Copyright 2016 Small Batch, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
/* Web Font Loader v1.6.16 - (c) Adobe Systems, Google. License: Apache 2.0 */
(function(){function aa(a,b,c){return a.call.apply(a.bind,arguments)}function ba(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}}function n(a,b,c){n=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?aa:ba;return n.apply(null,arguments)}var p=Date.now||function(){return+new Date};function r(a,b){this.F=a;this.k=b||a;this.H=this.k.document}var ca=!!window.FontFace;r.prototype.createElement=function(a,b,c){a=this.H.createElement(a);if(b)for(var d in b)b.hasOwnProperty(d)&&("style"==d?a.style.cssText=b[d]:a.setAttribute(d,b[d]));c&&a.appendChild(this.H.createTextNode(c));return a};function s(a,b,c){a=a.H.getElementsByTagName(b)[0];a||(a=document.documentElement);a.insertBefore(c,a.lastChild)}
function t(a,b,c){b=b||[];c=c||[];for(var d=a.className.split(/\s+/),e=0;e<b.length;e+=1){for(var f=!1,g=0;g<d.length;g+=1)if(b[e]===d[g]){f=!0;break}f||d.push(b[e])}b=[];for(e=0;e<d.length;e+=1){f=!1;for(g=0;g<c.length;g+=1)if(d[e]===c[g]){f=!0;break}f||b.push(d[e])}a.className=b.join(" ").replace(/\s+/g," ").replace(/^\s+|\s+$/,"")}function u(a,b){for(var c=a.className.split(/\s+/),d=0,e=c.length;d<e;d++)if(c[d]==b)return!0;return!1}
function v(a){if("string"===typeof a.fa)return a.fa;var b=a.k.location.protocol;"about:"==b&&(b=a.F.location.protocol);return"https:"==b?"https:":"http:"}function x(a,b,c){function d(){l&&e&&f&&(l(g),l=null)}b=a.createElement("link",{rel:"stylesheet",href:b,media:"all"});var e=!1,f=!0,g=null,l=c||null;ca?(b.onload=function(){e=!0;d()},b.onerror=function(){e=!0;g=Error("Stylesheet failed to load");d()}):setTimeout(function(){e=!0;d()},0);s(a,"head",b)}
function y(a,b,c,d){var e=a.H.getElementsByTagName("head")[0];if(e){var f=a.createElement("script",{src:b}),g=!1;f.onload=f.onreadystatechange=function(){g||this.readyState&&"loaded"!=this.readyState&&"complete"!=this.readyState||(g=!0,c&&c(null),f.onload=f.onreadystatechange=null,"HEAD"==f.parentNode.tagName&&e.removeChild(f))};e.appendChild(f);setTimeout(function(){g||(g=!0,c&&c(Error("Script load timeout")))},d||5E3);return f}return null};function z(){this.S=0;this.K=null}function A(a){a.S++;return function(){a.S--;B(a)}}function C(a,b){a.K=b;B(a)}function B(a){0==a.S&&a.K&&(a.K(),a.K=null)};function D(a){this.ea=a||"-"}D.prototype.d=function(a){for(var b=[],c=0;c<arguments.length;c++)b.push(arguments[c].replace(/[\W_]+/g,"").toLowerCase());return b.join(this.ea)};function E(a,b){this.Q=a;this.M=4;this.L="n";var c=(b||"n4").match(/^([nio])([1-9])$/i);c&&(this.L=c[1],this.M=parseInt(c[2],10))}E.prototype.getName=function(){return this.Q};function da(a){return F(a)+" "+(a.M+"00")+" 300px "+G(a.Q)}function G(a){var b=[];a=a.split(/,\s*/);for(var c=0;c<a.length;c++){var d=a[c].replace(/['"]/g,"");-1!=d.indexOf(" ")||/^\d/.test(d)?b.push("'"+d+"'"):b.push(d)}return b.join(",")}function I(a){return a.L+a.M}
function F(a){var b="normal";"o"===a.L?b="oblique":"i"===a.L&&(b="italic");return b}function ea(a){var b=4,c="n",d=null;a&&((d=a.match(/(normal|oblique|italic)/i))&&d[1]&&(c=d[1].substr(0,1).toLowerCase()),(d=a.match(/([1-9]00|normal|bold)/i))&&d[1]&&(/bold/i.test(d[1])?b=7:/[1-9]00/.test(d[1])&&(b=parseInt(d[1].substr(0,1),10))));return c+b};function fa(a,b){this.a=a;this.j=a.k.document.documentElement;this.O=b;this.f="wf";this.e=new D("-");this.da=!1!==b.events;this.u=!1!==b.classes}function ga(a){a.u&&t(a.j,[a.e.d(a.f,"loading")]);J(a,"loading")}function K(a){if(a.u){var b=u(a.j,a.e.d(a.f,"active")),c=[],d=[a.e.d(a.f,"loading")];b||c.push(a.e.d(a.f,"inactive"));t(a.j,c,d)}J(a,"inactive")}function J(a,b,c){if(a.da&&a.O[b])if(c)a.O[b](c.getName(),I(c));else a.O[b]()};function ha(){this.t={}}function ia(a,b,c){var d=[],e;for(e in b)if(b.hasOwnProperty(e)){var f=a.t[e];f&&d.push(f(b[e],c))}return d};function L(a,b){this.a=a;this.h=b;this.m=this.a.createElement("span",{"aria-hidden":"true"},this.h)}function M(a,b){var c=a.m,d;d="display:block;position:absolute;top:-9999px;left:-9999px;font-size:300px;width:auto;height:auto;line-height:normal;margin:0;padding:0;font-variant:normal;white-space:nowrap;font-family:"+G(b.Q)+";"+("font-style:"+F(b)+";font-weight:"+(b.M+"00")+";");c.style.cssText=d}function N(a){s(a.a,"body",a.m)}L.prototype.remove=function(){var a=this.m;a.parentNode&&a.parentNode.removeChild(a)};function O(a,b,c,d,e,f){this.G=a;this.J=b;this.g=d;this.a=c;this.v=e||3E3;this.h=f||void 0}O.prototype.start=function(){function a(){p()-d>=c.v?c.J(c.g):b.fonts.load(da(c.g),c.h).then(function(b){1<=b.length?c.G(c.g):setTimeout(a,25)},function(){c.J(c.g)})}var b=this.a.k.document,c=this,d=p();a()};function P(a,b,c,d,e,f,g){this.G=a;this.J=b;this.a=c;this.g=d;this.h=g||"BESbswy";this.s={};this.v=e||3E3;this.Z=f||null;this.D=this.C=this.A=this.w=null;this.w=new L(this.a,this.h);this.A=new L(this.a,this.h);this.C=new L(this.a,this.h);this.D=new L(this.a,this.h);M(this.w,new E(this.g.getName()+",serif",I(this.g)));M(this.A,new E(this.g.getName()+",sans-serif",I(this.g)));M(this.C,new E("serif",I(this.g)));M(this.D,new E("sans-serif",I(this.g)));N(this.w);N(this.A);N(this.C);N(this.D)}
var Q={ia:"serif",ha:"sans-serif"},R=null;function S(){if(null===R){var a=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent);R=!!a&&(536>parseInt(a[1],10)||536===parseInt(a[1],10)&&11>=parseInt(a[2],10))}return R}P.prototype.start=function(){this.s.serif=this.C.m.offsetWidth;this.s["sans-serif"]=this.D.m.offsetWidth;this.ga=p();ja(this)};function ka(a,b,c){for(var d in Q)if(Q.hasOwnProperty(d)&&b===a.s[Q[d]]&&c===a.s[Q[d]])return!0;return!1}
function ja(a){var b=a.w.m.offsetWidth,c=a.A.m.offsetWidth,d;(d=b===a.s.serif&&c===a.s["sans-serif"])||(d=S()&&ka(a,b,c));d?p()-a.ga>=a.v?S()&&ka(a,b,c)&&(null===a.Z||a.Z.hasOwnProperty(a.g.getName()))?T(a,a.G):T(a,a.J):la(a):T(a,a.G)}function la(a){setTimeout(n(function(){ja(this)},a),50)}function T(a,b){setTimeout(n(function(){this.w.remove();this.A.remove();this.C.remove();this.D.remove();b(this.g)},a),0)};function U(a,b,c){this.a=a;this.p=b;this.P=0;this.ba=this.Y=!1;this.v=c}var V=null;U.prototype.V=function(a){var b=this.p;b.u&&t(b.j,[b.e.d(b.f,a.getName(),I(a).toString(),"active")],[b.e.d(b.f,a.getName(),I(a).toString(),"loading"),b.e.d(b.f,a.getName(),I(a).toString(),"inactive")]);J(b,"fontactive",a);this.ba=!0;ma(this)};
U.prototype.W=function(a){var b=this.p;if(b.u){var c=u(b.j,b.e.d(b.f,a.getName(),I(a).toString(),"active")),d=[],e=[b.e.d(b.f,a.getName(),I(a).toString(),"loading")];c||d.push(b.e.d(b.f,a.getName(),I(a).toString(),"inactive"));t(b.j,d,e)}J(b,"fontinactive",a);ma(this)};function ma(a){0==--a.P&&a.Y&&(a.ba?(a=a.p,a.u&&t(a.j,[a.e.d(a.f,"active")],[a.e.d(a.f,"loading"),a.e.d(a.f,"inactive")]),J(a,"active")):K(a.p))};function na(a){this.F=a;this.q=new ha;this.$=0;this.T=this.U=!0}na.prototype.load=function(a){this.a=new r(this.F,a.context||this.F);this.U=!1!==a.events;this.T=!1!==a.classes;oa(this,new fa(this.a,a),a)};
function pa(a,b,c,d,e){var f=0==--a.$;(a.T||a.U)&&setTimeout(function(){var a=e||null,l=d||null||{};if(0===c.length&&f)K(b.p);else{b.P+=c.length;f&&(b.Y=f);var h,k=[];for(h=0;h<c.length;h++){var m=c[h],w=l[m.getName()],q=b.p,H=m;q.u&&t(q.j,[q.e.d(q.f,H.getName(),I(H).toString(),"loading")]);J(q,"fontloading",H);q=null;null===V&&(V=window.FontFace?(q=/Gecko.*Firefox\/(\d+)/.exec(window.navigator.userAgent))?42<parseInt(q[1],10):!0:!1);q=V?new O(n(b.V,b),n(b.W,b),b.a,m,b.v,w):new P(n(b.V,b),n(b.W,b),
b.a,m,b.v,a,w);k.push(q)}for(h=0;h<k.length;h++)k[h].start()}},0)}function oa(a,b,c){var d=[],e=c.timeout;ga(b);var d=ia(a.q,c,a.a),f=new U(a.a,b,e);a.$=d.length;b=0;for(c=d.length;b<c;b++)d[b].load(function(b,c,d){pa(a,f,b,c,d)})};function qa(a,b,c){this.N=a?a:b+ra;this.o=[];this.R=[];this.ca=c||""}var ra="//fonts.googleapis.com/css";function sa(a,b){for(var c=b.length,d=0;d<c;d++){var e=b[d].split(":");3==e.length&&a.R.push(e.pop());var f="";2==e.length&&""!=e[1]&&(f=":");a.o.push(e.join(f))}}
qa.prototype.d=function(){if(0==this.o.length)throw Error("No fonts to load!");if(-1!=this.N.indexOf("kit="))return this.N;for(var a=this.o.length,b=[],c=0;c<a;c++)b.push(this.o[c].replace(/ /g,"+"));a=this.N+"?family="+b.join("%7C");0<this.R.length&&(a+="&subset="+this.R.join(","));0<this.ca.length&&(a+="&text="+encodeURIComponent(this.ca));return a};function ta(a){this.o=a;this.aa=[];this.I={}}
var ua={latin:"BESbswy",cyrillic:"&#1081;&#1103;&#1046;",greek:"&#945;&#946;&#931;",khmer:"&#x1780;&#x1781;&#x1782;",Hanuman:"&#x1780;&#x1781;&#x1782;"},va={thin:"1",extralight:"2","extra-light":"2",ultralight:"2","ultra-light":"2",light:"3",regular:"4",book:"4",medium:"5","semi-bold":"6",semibold:"6","demi-bold":"6",demibold:"6",bold:"7","extra-bold":"8",extrabold:"8","ultra-bold":"8",ultrabold:"8",black:"9",heavy:"9",l:"3",r:"4",b:"7"},wa={i:"i",italic:"i",n:"n",normal:"n"},xa=/^(thin|(?:(?:extra|ultra)-?)?light|regular|book|medium|(?:(?:semi|demi|extra|ultra)-?)?bold|black|heavy|l|r|b|[1-9]00)?(n|i|normal|italic)?$/;
ta.prototype.parse=function(){for(var a=this.o.length,b=0;b<a;b++){var c=this.o[b].split(":"),d=c[0].replace(/\+/g," "),e=["n4"];if(2<=c.length){var f;var g=c[1];f=[];if(g)for(var g=g.split(","),l=g.length,h=0;h<l;h++){var k;k=g[h];if(k.match(/^[\w-]+$/))if(k=xa.exec(k.toLowerCase()),null==k)k="";else{var m;m=k[1];if(null==m||""==m)m="4";else{var w=va[m];m=w?w:isNaN(m)?"4":m.substr(0,1)}k=k[2];k=[null==k||""==k?"n":wa[k],m].join("")}else k="";k&&f.push(k)}0<f.length&&(e=f);3==c.length&&(c=c[2],f=
[],c=c?c.split(","):f,0<c.length&&(c=ua[c[0]])&&(this.I[d]=c))}this.I[d]||(c=ua[d])&&(this.I[d]=c);for(c=0;c<e.length;c+=1)this.aa.push(new E(d,e[c]))}};function ya(a,b){this.a=a;this.c=b}var za={Arimo:!0,Cousine:!0,Tinos:!0};ya.prototype.load=function(a){var b=new z,c=this.a,d=new qa(this.c.api,v(c),this.c.text),e=this.c.families;sa(d,e);var f=new ta(e);f.parse();x(c,d.d(),A(b));C(b,function(){a(f.aa,f.I,za)})};function W(a,b){this.a=a;this.c=b;this.X=[]}W.prototype.B=function(a){var b=this.a;return v(this.a)+(this.c.api||"//f.fontdeck.com/s/css/js/")+(b.k.location.hostname||b.F.location.hostname)+"/"+a+".js"};
W.prototype.load=function(a){var b=this.c.id,c=this.a.k,d=this;b?(c.__webfontfontdeckmodule__||(c.__webfontfontdeckmodule__={}),c.__webfontfontdeckmodule__[b]=function(b,c){for(var g=0,l=c.fonts.length;g<l;++g){var h=c.fonts[g];d.X.push(new E(h.name,ea("font-weight:"+h.weight+";font-style:"+h.style)))}a(d.X)},y(this.a,this.B(b),function(b){b&&a([])})):a([])};function X(a,b){this.a=a;this.c=b}X.prototype.B=function(a){return(this.c.api||"https://use.typekit.net")+"/"+a+".js"};X.prototype.load=function(a){var b=this.c.id,c=this.a.k;b?y(this.a,this.B(b),function(b){if(b)a([]);else if(c.Typekit&&c.Typekit.config&&c.Typekit.config.fn){b=c.Typekit.config.fn;for(var e=[],f=0;f<b.length;f+=2)for(var g=b[f],l=b[f+1],h=0;h<l.length;h++)e.push(new E(g,l[h]));try{c.Typekit.load({events:!1,classes:!1,async:!0})}catch(k){}a(e)}},2E3):a([])};function Y(a,b){this.a=a;this.c=b}Y.prototype.B=function(a,b){var c=v(this.a),d=(this.c.api||"fast.fonts.net/jsapi").replace(/^.*http(s?):(\/\/)?/,"");return c+"//"+d+"/"+a+".js"+(b?"?v="+b:"")};
Y.prototype.load=function(a){function b(){if(e["__mti_fntLst"+c]){var d=e["__mti_fntLst"+c](),g=[],l;if(d)for(var h=0;h<d.length;h++){var k=d[h].fontfamily;void 0!=d[h].fontStyle&&void 0!=d[h].fontWeight?(l=d[h].fontStyle+d[h].fontWeight,g.push(new E(k,l))):g.push(new E(k))}a(g)}else setTimeout(function(){b()},50)}var c=this.c.projectId,d=this.c.version;if(c){var e=this.a.k;y(this.a,this.B(c,d),function(c){c?a([]):b()}).id="__MonotypeAPIScript__"+c}else a([])};function Aa(a,b){this.a=a;this.c=b}Aa.prototype.load=function(a){var b,c,d=this.c.urls||[],e=this.c.families||[],f=this.c.testStrings||{},g=new z;b=0;for(c=d.length;b<c;b++)x(this.a,d[b],A(g));var l=[];b=0;for(c=e.length;b<c;b++)if(d=e[b].split(":"),d[1])for(var h=d[1].split(","),k=0;k<h.length;k+=1)l.push(new E(d[0],h[k]));else l.push(new E(d[0]));C(g,function(){a(l,f)})};var Z=new na(window);Z.q.t.custom=function(a,b){return new Aa(b,a)};Z.q.t.fontdeck=function(a,b){return new W(b,a)};Z.q.t.monotype=function(a,b){return new Y(b,a)};Z.q.t.typekit=function(a,b){return new X(b,a)};Z.q.t.google=function(a,b){return new ya(b,a)};var $={load:n(Z.load,Z)};"function"===typeof define&&define.amd?define(function(){return $}):"undefined"!==typeof module&&module.exports?module.exports=$:(window.WebFont=$,window.WebFontConfig&&Z.load(window.WebFontConfig));}());


},{}],319:[function(require,module,exports){
/* Zepto v1.1.6 - zepto event ajax form ie - zeptojs.com/license */
var Zepto=function(){function L(t){return null==t?String(t):j[S.call(t)]||"object"}function Z(t){return"function"==L(t)}function _(t){return null!=t&&t==t.window}function $(t){return null!=t&&t.nodeType==t.DOCUMENT_NODE}function D(t){return"object"==L(t)}function M(t){return D(t)&&!_(t)&&Object.getPrototypeOf(t)==Object.prototype}function R(t){return"number"==typeof t.length}function k(t){return s.call(t,function(t){return null!=t})}function z(t){return t.length>0?n.fn.concat.apply([],t):t}function F(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function q(t){return t in f?f[t]:f[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function H(t,e){return"number"!=typeof e||c[F(t)]?e:e+"px"}function I(t){var e,n;return u[t]||(e=a.createElement(t),a.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),u[t]=n),u[t]}function V(t){return"children"in t?o.call(t.children):n.map(t.childNodes,function(t){return 1==t.nodeType?t:void 0})}function B(n,i,r){for(e in i)r&&(M(i[e])||A(i[e]))?(M(i[e])&&!M(n[e])&&(n[e]={}),A(i[e])&&!A(n[e])&&(n[e]=[]),B(n[e],i[e],r)):i[e]!==t&&(n[e]=i[e])}function U(t,e){return null==e?n(t):n(t).filter(e)}function J(t,e,n,i){return Z(e)?e.call(t,n,i):e}function X(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}function W(e,n){var i=e.className||"",r=i&&i.baseVal!==t;return n===t?r?i.baseVal:i:void(r?i.baseVal=n:e.className=n)}function Y(t){try{return t?"true"==t||("false"==t?!1:"null"==t?null:+t+""==t?+t:/^[\[\{]/.test(t)?n.parseJSON(t):t):t}catch(e){return t}}function G(t,e){e(t);for(var n=0,i=t.childNodes.length;i>n;n++)G(t.childNodes[n],e)}var t,e,n,i,C,N,r=[],o=r.slice,s=r.filter,a=window.document,u={},f={},c={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},l=/^\s*<(\w+|!)[^>]*>/,h=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,p=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,d=/^(?:body|html)$/i,m=/([A-Z])/g,g=["val","css","html","text","data","width","height","offset"],v=["after","prepend","before","append"],y=a.createElement("table"),x=a.createElement("tr"),b={tr:a.createElement("tbody"),tbody:y,thead:y,tfoot:y,td:x,th:x,"*":a.createElement("div")},w=/complete|loaded|interactive/,E=/^[\w-]*$/,j={},S=j.toString,T={},O=a.createElement("div"),P={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},A=Array.isArray||function(t){return t instanceof Array};return T.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var i,r=t.parentNode,o=!r;return o&&(r=O).appendChild(t),i=~T.qsa(r,e).indexOf(t),o&&O.removeChild(t),i},C=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},N=function(t){return s.call(t,function(e,n){return t.indexOf(e)==n})},T.fragment=function(e,i,r){var s,u,f;return h.test(e)&&(s=n(a.createElement(RegExp.$1))),s||(e.replace&&(e=e.replace(p,"<$1></$2>")),i===t&&(i=l.test(e)&&RegExp.$1),i in b||(i="*"),f=b[i],f.innerHTML=""+e,s=n.each(o.call(f.childNodes),function(){f.removeChild(this)})),M(r)&&(u=n(s),n.each(r,function(t,e){g.indexOf(t)>-1?u[t](e):u.attr(t,e)})),s},T.Z=function(t,e){return t=t||[],t.__proto__=n.fn,t.selector=e||"",t},T.isZ=function(t){return t instanceof T.Z},T.init=function(e,i){var r;if(!e)return T.Z();if("string"==typeof e)if(e=e.trim(),"<"==e[0]&&l.test(e))r=T.fragment(e,RegExp.$1,i),e=null;else{if(i!==t)return n(i).find(e);r=T.qsa(a,e)}else{if(Z(e))return n(a).ready(e);if(T.isZ(e))return e;if(A(e))r=k(e);else if(D(e))r=[e],e=null;else if(l.test(e))r=T.fragment(e.trim(),RegExp.$1,i),e=null;else{if(i!==t)return n(i).find(e);r=T.qsa(a,e)}}return T.Z(r,e)},n=function(t,e){return T.init(t,e)},n.extend=function(t){var e,n=o.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){B(t,n,e)}),t},T.qsa=function(t,e){var n,i="#"==e[0],r=!i&&"."==e[0],s=i||r?e.slice(1):e,a=E.test(s);return $(t)&&a&&i?(n=t.getElementById(s))?[n]:[]:1!==t.nodeType&&9!==t.nodeType?[]:o.call(a&&!i?r?t.getElementsByClassName(s):t.getElementsByTagName(e):t.querySelectorAll(e))},n.contains=a.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},n.type=L,n.isFunction=Z,n.isWindow=_,n.isArray=A,n.isPlainObject=M,n.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},n.inArray=function(t,e,n){return r.indexOf.call(e,t,n)},n.camelCase=C,n.trim=function(t){return null==t?"":String.prototype.trim.call(t)},n.uuid=0,n.support={},n.expr={},n.map=function(t,e){var n,r,o,i=[];if(R(t))for(r=0;r<t.length;r++)n=e(t[r],r),null!=n&&i.push(n);else for(o in t)n=e(t[o],o),null!=n&&i.push(n);return z(i)},n.each=function(t,e){var n,i;if(R(t)){for(n=0;n<t.length;n++)if(e.call(t[n],n,t[n])===!1)return t}else for(i in t)if(e.call(t[i],i,t[i])===!1)return t;return t},n.grep=function(t,e){return s.call(t,e)},window.JSON&&(n.parseJSON=JSON.parse),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(t,e){j["[object "+e+"]"]=e.toLowerCase()}),n.fn={forEach:r.forEach,reduce:r.reduce,push:r.push,sort:r.sort,indexOf:r.indexOf,concat:r.concat,map:function(t){return n(n.map(this,function(e,n){return t.call(e,n,e)}))},slice:function(){return n(o.apply(this,arguments))},ready:function(t){return w.test(a.readyState)&&a.body?t(n):a.addEventListener("DOMContentLoaded",function(){t(n)},!1),this},get:function(e){return e===t?o.call(this):this[e>=0?e:e+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return r.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return Z(t)?this.not(this.not(t)):n(s.call(this,function(e){return T.matches(e,t)}))},add:function(t,e){return n(N(this.concat(n(t,e))))},is:function(t){return this.length>0&&T.matches(this[0],t)},not:function(e){var i=[];if(Z(e)&&e.call!==t)this.each(function(t){e.call(this,t)||i.push(this)});else{var r="string"==typeof e?this.filter(e):R(e)&&Z(e.item)?o.call(e):n(e);this.forEach(function(t){r.indexOf(t)<0&&i.push(t)})}return n(i)},has:function(t){return this.filter(function(){return D(t)?n.contains(this,t):n(this).find(t).size()})},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!D(t)?t:n(t)},last:function(){var t=this[this.length-1];return t&&!D(t)?t:n(t)},find:function(t){var e,i=this;return e=t?"object"==typeof t?n(t).filter(function(){var t=this;return r.some.call(i,function(e){return n.contains(e,t)})}):1==this.length?n(T.qsa(this[0],t)):this.map(function(){return T.qsa(this,t)}):n()},closest:function(t,e){var i=this[0],r=!1;for("object"==typeof t&&(r=n(t));i&&!(r?r.indexOf(i)>=0:T.matches(i,t));)i=i!==e&&!$(i)&&i.parentNode;return n(i)},parents:function(t){for(var e=[],i=this;i.length>0;)i=n.map(i,function(t){return(t=t.parentNode)&&!$(t)&&e.indexOf(t)<0?(e.push(t),t):void 0});return U(e,t)},parent:function(t){return U(N(this.pluck("parentNode")),t)},children:function(t){return U(this.map(function(){return V(this)}),t)},contents:function(){return this.map(function(){return o.call(this.childNodes)})},siblings:function(t){return U(this.map(function(t,e){return s.call(V(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return n.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=I(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var e=Z(t);if(this[0]&&!e)var i=n(t).get(0),r=i.parentNode||this.length>1;return this.each(function(o){n(this).wrapAll(e?t.call(this,o):r?i.cloneNode(!0):i)})},wrapAll:function(t){if(this[0]){n(this[0]).before(t=n(t));for(var e;(e=t.children()).length;)t=e.first();n(t).append(this)}return this},wrapInner:function(t){var e=Z(t);return this.each(function(i){var r=n(this),o=r.contents(),s=e?t.call(this,i):t;o.length?o.wrapAll(s):r.append(s)})},unwrap:function(){return this.parent().each(function(){n(this).replaceWith(n(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(e){return this.each(function(){var i=n(this);(e===t?"none"==i.css("display"):e)?i.show():i.hide()})},prev:function(t){return n(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return n(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var i=this.innerHTML;n(this).empty().append(J(this,t,e,i))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=J(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this[0].textContent:null},attr:function(n,i){var r;return"string"!=typeof n||1 in arguments?this.each(function(t){if(1===this.nodeType)if(D(n))for(e in n)X(this,e,n[e]);else X(this,n,J(this,i,t,this.getAttribute(n)))}):this.length&&1===this[0].nodeType?!(r=this[0].getAttribute(n))&&n in this[0]?this[0][n]:r:t},removeAttr:function(t){return this.each(function(){1===this.nodeType&&t.split(" ").forEach(function(t){X(this,t)},this)})},prop:function(t,e){return t=P[t]||t,1 in arguments?this.each(function(n){this[t]=J(this,e,n,this[t])}):this[0]&&this[0][t]},data:function(e,n){var i="data-"+e.replace(m,"-$1").toLowerCase(),r=1 in arguments?this.attr(i,n):this.attr(i);return null!==r?Y(r):t},val:function(t){return 0 in arguments?this.each(function(e){this.value=J(this,t,e,this.value)}):this[0]&&(this[0].multiple?n(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(t){if(t)return this.each(function(e){var i=n(this),r=J(this,t,e,i.offset()),o=i.offsetParent().offset(),s={top:r.top-o.top,left:r.left-o.left};"static"==i.css("position")&&(s.position="relative"),i.css(s)});if(!this.length)return null;var e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(t,i){if(arguments.length<2){var r,o=this[0];if(!o)return;if(r=getComputedStyle(o,""),"string"==typeof t)return o.style[C(t)]||r.getPropertyValue(t);if(A(t)){var s={};return n.each(t,function(t,e){s[e]=o.style[C(e)]||r.getPropertyValue(e)}),s}}var a="";if("string"==L(t))i||0===i?a=F(t)+":"+H(t,i):this.each(function(){this.style.removeProperty(F(t))});else for(e in t)t[e]||0===t[e]?a+=F(e)+":"+H(e,t[e])+";":this.each(function(){this.style.removeProperty(F(e))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf(n(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return t?r.some.call(this,function(t){return this.test(W(t))},q(t)):!1},addClass:function(t){return t?this.each(function(e){if("className"in this){i=[];var r=W(this),o=J(this,t,e,r);o.split(/\s+/g).forEach(function(t){n(this).hasClass(t)||i.push(t)},this),i.length&&W(this,r+(r?" ":"")+i.join(" "))}}):this},removeClass:function(e){return this.each(function(n){if("className"in this){if(e===t)return W(this,"");i=W(this),J(this,e,n,i).split(/\s+/g).forEach(function(t){i=i.replace(q(t)," ")}),W(this,i.trim())}})},toggleClass:function(e,i){return e?this.each(function(r){var o=n(this),s=J(this,e,r,W(this));s.split(/\s+/g).forEach(function(e){(i===t?!o.hasClass(e):i)?o.addClass(e):o.removeClass(e)})}):this},scrollTop:function(e){if(this.length){var n="scrollTop"in this[0];return e===t?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=e}:function(){this.scrollTo(this.scrollX,e)})}},scrollLeft:function(e){if(this.length){var n="scrollLeft"in this[0];return e===t?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=e}:function(){this.scrollTo(e,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),i=this.offset(),r=d.test(e[0].nodeName)?{top:0,left:0}:e.offset();return i.top-=parseFloat(n(t).css("margin-top"))||0,i.left-=parseFloat(n(t).css("margin-left"))||0,r.top+=parseFloat(n(e[0]).css("border-top-width"))||0,r.left+=parseFloat(n(e[0]).css("border-left-width"))||0,{top:i.top-r.top,left:i.left-r.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||a.body;t&&!d.test(t.nodeName)&&"static"==n(t).css("position");)t=t.offsetParent;return t})}},n.fn.detach=n.fn.remove,["width","height"].forEach(function(e){var i=e.replace(/./,function(t){return t[0].toUpperCase()});n.fn[e]=function(r){var o,s=this[0];return r===t?_(s)?s["inner"+i]:$(s)?s.documentElement["scroll"+i]:(o=this.offset())&&o[e]:this.each(function(t){s=n(this),s.css(e,J(this,r,t,s[e]()))})}}),v.forEach(function(t,e){var i=e%2;n.fn[t]=function(){var t,o,r=n.map(arguments,function(e){return t=L(e),"object"==t||"array"==t||null==e?e:T.fragment(e)}),s=this.length>1;return r.length<1?this:this.each(function(t,u){o=i?u:u.parentNode,u=0==e?u.nextSibling:1==e?u.firstChild:2==e?u:null;var f=n.contains(a.documentElement,o);r.forEach(function(t){if(s)t=t.cloneNode(!0);else if(!o)return n(t).remove();o.insertBefore(t,u),f&&G(t,function(t){null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src||window.eval.call(window,t.innerHTML)})})})},n.fn[i?t+"To":"insert"+(e?"Before":"After")]=function(e){return n(e)[t](this),this}}),T.Z.prototype=n.fn,T.uniq=N,T.deserializeValue=Y,n.zepto=T,n}();window.Zepto=Zepto,void 0===window.$&&(window.$=Zepto),function(t){function l(t){return t._zid||(t._zid=e++)}function h(t,e,n,i){if(e=p(e),e.ns)var r=d(e.ns);return(s[l(t)]||[]).filter(function(t){return!(!t||e.e&&t.e!=e.e||e.ns&&!r.test(t.ns)||n&&l(t.fn)!==l(n)||i&&t.sel!=i)})}function p(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function d(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function m(t,e){return t.del&&!u&&t.e in f||!!e}function g(t){return c[t]||u&&f[t]||t}function v(e,i,r,o,a,u,f){var h=l(e),d=s[h]||(s[h]=[]);i.split(/\s/).forEach(function(i){if("ready"==i)return t(document).ready(r);var s=p(i);s.fn=r,s.sel=a,s.e in c&&(r=function(e){var n=e.relatedTarget;return!n||n!==this&&!t.contains(this,n)?s.fn.apply(this,arguments):void 0}),s.del=u;var l=u||r;s.proxy=function(t){if(t=j(t),!t.isImmediatePropagationStopped()){t.data=o;var i=l.apply(e,t._args==n?[t]:[t].concat(t._args));return i===!1&&(t.preventDefault(),t.stopPropagation()),i}},s.i=d.length,d.push(s),"addEventListener"in e&&e.addEventListener(g(s.e),s.proxy,m(s,f))})}function y(t,e,n,i,r){var o=l(t);(e||"").split(/\s/).forEach(function(e){h(t,e,n,i).forEach(function(e){delete s[o][e.i],"removeEventListener"in t&&t.removeEventListener(g(e.e),e.proxy,m(e,r))})})}function j(e,i){return(i||!e.isDefaultPrevented)&&(i||(i=e),t.each(E,function(t,n){var r=i[t];e[t]=function(){return this[n]=x,r&&r.apply(i,arguments)},e[n]=b}),(i.defaultPrevented!==n?i.defaultPrevented:"returnValue"in i?i.returnValue===!1:i.getPreventDefault&&i.getPreventDefault())&&(e.isDefaultPrevented=x)),e}function S(t){var e,i={originalEvent:t};for(e in t)w.test(e)||t[e]===n||(i[e]=t[e]);return j(i,t)}var n,e=1,i=Array.prototype.slice,r=t.isFunction,o=function(t){return"string"==typeof t},s={},a={},u="onfocusin"in window,f={focus:"focusin",blur:"focusout"},c={mouseenter:"mouseover",mouseleave:"mouseout"};a.click=a.mousedown=a.mouseup=a.mousemove="MouseEvents",t.event={add:v,remove:y},t.proxy=function(e,n){var s=2 in arguments&&i.call(arguments,2);if(r(e)){var a=function(){return e.apply(n,s?s.concat(i.call(arguments)):arguments)};return a._zid=l(e),a}if(o(n))return s?(s.unshift(e[n],e),t.proxy.apply(null,s)):t.proxy(e[n],e);throw new TypeError("expected function")},t.fn.bind=function(t,e,n){return this.on(t,e,n)},t.fn.unbind=function(t,e){return this.off(t,e)},t.fn.one=function(t,e,n,i){return this.on(t,e,n,i,1)};var x=function(){return!0},b=function(){return!1},w=/^([A-Z]|returnValue$|layer[XY]$)/,E={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return this.off(e,t,n)},t.fn.live=function(e,n){return t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,s,a,u,f){var c,l,h=this;return e&&!o(e)?(t.each(e,function(t,e){h.on(t,s,a,e,f)}),h):(o(s)||r(u)||u===!1||(u=a,a=s,s=n),(r(a)||a===!1)&&(u=a,a=n),u===!1&&(u=b),h.each(function(n,r){f&&(c=function(t){return y(r,t.type,u),u.apply(this,arguments)}),s&&(l=function(e){var n,o=t(e.target).closest(s,r).get(0);return o&&o!==r?(n=t.extend(S(e),{currentTarget:o,liveFired:r}),(c||u).apply(o,[n].concat(i.call(arguments,1)))):void 0}),v(r,e,u,a,s,l||c)}))},t.fn.off=function(e,i,s){var a=this;return e&&!o(e)?(t.each(e,function(t,e){a.off(t,i,e)}),a):(o(i)||r(s)||s===!1||(s=i,i=n),s===!1&&(s=b),a.each(function(){y(this,e,s,i)}))},t.fn.trigger=function(e,n){return e=o(e)||t.isPlainObject(e)?t.Event(e):j(e),e._args=n,this.each(function(){e.type in f&&"function"==typeof this[e.type]?this[e.type]():"dispatchEvent"in this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,n){var i,r;return this.each(function(s,a){i=S(o(e)?t.Event(e):e),i._args=n,i.target=a,t.each(h(a,e.type||e),function(t,e){return r=e.proxy(i),i.isImmediatePropagationStopped()?!1:void 0})}),r},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(e){t.fn[e]=function(t){return 0 in arguments?this.bind(e,t):this.trigger(e)}}),t.Event=function(t,e){o(t)||(e=t,t=e.type);var n=document.createEvent(a[t]||"Events"),i=!0;if(e)for(var r in e)"bubbles"==r?i=!!e[r]:n[r]=e[r];return n.initEvent(t,i,!0),j(n)}}(Zepto),function(t){function h(e,n,i){var r=t.Event(n);return t(e).trigger(r,i),!r.isDefaultPrevented()}function p(t,e,i,r){return t.global?h(e||n,i,r):void 0}function d(e){e.global&&0===t.active++&&p(e,null,"ajaxStart")}function m(e){e.global&&!--t.active&&p(e,null,"ajaxStop")}function g(t,e){var n=e.context;return e.beforeSend.call(n,t,e)===!1||p(e,n,"ajaxBeforeSend",[t,e])===!1?!1:void p(e,n,"ajaxSend",[t,e])}function v(t,e,n,i){var r=n.context,o="success";n.success.call(r,t,o,e),i&&i.resolveWith(r,[t,o,e]),p(n,r,"ajaxSuccess",[e,n,t]),x(o,e,n)}function y(t,e,n,i,r){var o=i.context;i.error.call(o,n,e,t),r&&r.rejectWith(o,[n,e,t]),p(i,o,"ajaxError",[n,i,t||e]),x(e,n,i)}function x(t,e,n){var i=n.context;n.complete.call(i,e,t),p(n,i,"ajaxComplete",[e,n]),m(n)}function b(){}function w(t){return t&&(t=t.split(";",2)[0]),t&&(t==f?"html":t==u?"json":s.test(t)?"script":a.test(t)&&"xml")||"text"}function E(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function j(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()||(e.url=E(e.url,e.data),e.data=void 0)}function S(e,n,i,r){return t.isFunction(n)&&(r=i,i=n,n=void 0),t.isFunction(i)||(r=i,i=void 0),{url:e,data:n,success:i,dataType:r}}function C(e,n,i,r){var o,s=t.isArray(n),a=t.isPlainObject(n);t.each(n,function(n,u){o=t.type(u),r&&(n=i?r:r+"["+(a||"object"==o||"array"==o?n:"")+"]"),!r&&s?e.add(u.name,u.value):"array"==o||!i&&"object"==o?C(e,u,i,n):e.add(n,u)})}var i,r,e=0,n=window.document,o=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,s=/^(?:text|application)\/javascript/i,a=/^(?:text|application)\/xml/i,u="application/json",f="text/html",c=/^\s*$/,l=n.createElement("a");l.href=window.location.href,t.active=0,t.ajaxJSONP=function(i,r){if(!("type"in i))return t.ajax(i);var f,h,o=i.jsonpCallback,s=(t.isFunction(o)?o():o)||"jsonp"+ ++e,a=n.createElement("script"),u=window[s],c=function(e){t(a).triggerHandler("error",e||"abort")},l={abort:c};return r&&r.promise(l),t(a).on("load error",function(e,n){clearTimeout(h),t(a).off().remove(),"error"!=e.type&&f?v(f[0],l,i,r):y(null,n||"error",l,i,r),window[s]=u,f&&t.isFunction(u)&&u(f[0]),u=f=void 0}),g(l,i)===!1?(c("abort"),l):(window[s]=function(){f=arguments},a.src=i.url.replace(/\?(.+)=\?/,"?$1="+s),n.head.appendChild(a),i.timeout>0&&(h=setTimeout(function(){c("timeout")},i.timeout)),l)},t.ajaxSettings={type:"GET",beforeSend:b,success:b,error:b,complete:b,context:null,global:!0,xhr:function(){return new window.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript, application/x-javascript",json:u,xml:"application/xml, text/xml",html:f,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0},t.ajax=function(e){var a,o=t.extend({},e||{}),s=t.Deferred&&t.Deferred();for(i in t.ajaxSettings)void 0===o[i]&&(o[i]=t.ajaxSettings[i]);d(o),o.crossDomain||(a=n.createElement("a"),a.href=o.url,a.href=a.href,o.crossDomain=l.protocol+"//"+l.host!=a.protocol+"//"+a.host),o.url||(o.url=window.location.toString()),j(o);var u=o.dataType,f=/\?.+=\?/.test(o.url);if(f&&(u="jsonp"),o.cache!==!1&&(e&&e.cache===!0||"script"!=u&&"jsonp"!=u)||(o.url=E(o.url,"_="+Date.now())),"jsonp"==u)return f||(o.url=E(o.url,o.jsonp?o.jsonp+"=?":o.jsonp===!1?"":"callback=?")),t.ajaxJSONP(o,s);var C,h=o.accepts[u],p={},m=function(t,e){p[t.toLowerCase()]=[t,e]},x=/^([\w-]+:)\/\//.test(o.url)?RegExp.$1:window.location.protocol,S=o.xhr(),T=S.setRequestHeader;if(s&&s.promise(S),o.crossDomain||m("X-Requested-With","XMLHttpRequest"),m("Accept",h||"*/*"),(h=o.mimeType||h)&&(h.indexOf(",")>-1&&(h=h.split(",",2)[0]),S.overrideMimeType&&S.overrideMimeType(h)),(o.contentType||o.contentType!==!1&&o.data&&"GET"!=o.type.toUpperCase())&&m("Content-Type",o.contentType||"application/x-www-form-urlencoded"),o.headers)for(r in o.headers)m(r,o.headers[r]);if(S.setRequestHeader=m,S.onreadystatechange=function(){if(4==S.readyState){S.onreadystatechange=b,clearTimeout(C);var e,n=!1;if(S.status>=200&&S.status<300||304==S.status||0==S.status&&"file:"==x){u=u||w(o.mimeType||S.getResponseHeader("content-type")),e=S.responseText;try{"script"==u?(1,eval)(e):"xml"==u?e=S.responseXML:"json"==u&&(e=c.test(e)?null:t.parseJSON(e))}catch(i){n=i}n?y(n,"parsererror",S,o,s):v(e,S,o,s)}else y(S.statusText||null,S.status?"error":"abort",S,o,s)}},g(S,o)===!1)return S.abort(),y(null,"abort",S,o,s),S;if(o.xhrFields)for(r in o.xhrFields)S[r]=o.xhrFields[r];var N="async"in o?o.async:!0;S.open(o.type,o.url,N,o.username,o.password);for(r in p)T.apply(S,p[r]);return o.timeout>0&&(C=setTimeout(function(){S.onreadystatechange=b,S.abort(),y(null,"timeout",S,o,s)},o.timeout)),S.send(o.data?o.data:null),S},t.get=function(){return t.ajax(S.apply(null,arguments))},t.post=function(){var e=S.apply(null,arguments);return e.type="POST",t.ajax(e)},t.getJSON=function(){var e=S.apply(null,arguments);return e.dataType="json",t.ajax(e)},t.fn.load=function(e,n,i){if(!this.length)return this;var a,r=this,s=e.split(/\s/),u=S(e,n,i),f=u.success;return s.length>1&&(u.url=s[0],a=s[1]),u.success=function(e){r.html(a?t("<div>").html(e.replace(o,"")).find(a):e),f&&f.apply(r,arguments)},t.ajax(u),this};var T=encodeURIComponent;t.param=function(e,n){var i=[];return i.add=function(e,n){t.isFunction(n)&&(n=n()),null==n&&(n=""),this.push(T(e)+"="+T(n))},C(i,e,n),i.join("&").replace(/%20/g,"+")}}(Zepto),function(t){t.fn.serializeArray=function(){var e,n,i=[],r=function(t){return t.forEach?t.forEach(r):void i.push({name:e,value:t})};return this[0]&&t.each(this[0].elements,function(i,o){n=o.type,e=o.name,e&&"fieldset"!=o.nodeName.toLowerCase()&&!o.disabled&&"submit"!=n&&"reset"!=n&&"button"!=n&&"file"!=n&&("radio"!=n&&"checkbox"!=n||o.checked)&&r(t(o).val())}),i},t.fn.serialize=function(){var t=[];return this.serializeArray().forEach(function(e){t.push(encodeURIComponent(e.name)+"="+encodeURIComponent(e.value))}),t.join("&")},t.fn.submit=function(e){if(0 in arguments)this.bind("submit",e);else if(this.length){var n=t.Event("submit");this.eq(0).trigger(n),n.isDefaultPrevented()||this.get(0).submit()}return this}}(Zepto),function(t){"__proto__"in{}||t.extend(t.zepto,{Z:function(e,n){return e=e||[],t.extend(e,t.fn),e.selector=n||"",e.__Z=!0,e},isZ:function(e){return"array"===t.type(e)&&"__Z"in e}});try{getComputedStyle(void 0)}catch(e){var n=getComputedStyle;window.getComputedStyle=function(t){try{return n(t)}catch(e){return null}}}}(Zepto);

  module.exports = Zepto || $;

},{}],320:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "إضافة",
    "add_image": "اختيار صورة",
    "add_to_favorites": "أضف الى المفضلة",
    "aspect": "مظهر",
    "back": "الى الخلف",
    "buy": "يشترى",
    "buy_full_version": "شراء النسخة الكاملة",
    "cancel": "إلغاء",
    "check_again": "تحقق مرة اخرى",
    "choose_folder": "اختيار مجلد",
    "clear": "مسح",
    "clear_history": "مسح المحفوظات",
    "close": "أغلاق",
    "cloud": "سحابي",
    "continue_anyway": "المتابعة على أية حال",
    "current_image": "تيار",
    "delete": "حذف",
    "device": "جهاز",
    "dismiss": "رفض",
    "done": "تم",
    "dont_ask": "لا تسأل ثانية",
    "duplicate": "مكرر",
    "enter_license": "إدخال مفتاح الترخيص",
    "enter_license_key": "إدخال مفتاح الترخيص",
    "exit": "خروج",
    "export": "تصدير",
    "export_filter": "حفظ ك رمز الاستجابة السريعة",
    "forgot_password": "هل نسيت كلمة المرور؟",
    "free_membership": "اشتراك مجاني",
    "get": "يحصل على",
    "get_license": "الحصول على مفتاح الترخيص",
    "get_started": "بدء",
    "got_it": "فهمتك",
    "have_account": "هل لديك حساب؟",
    "import": "استيراد",
    "import_filter": "تصفية استيراد",
    "import_filter_photos": "الاستيراد من صور",
    "import_photos": "صور مفتوحة",
    "info": "معلومات",
    "installed": "تثبيت",
    "invite": "إرسال الرابط",
    "learn_more": "أعرف أكثر",
    "load_last_photo": "تحميل الصورة الأخيرة على إطلاق",
    "new": "جديد",
    "new_filter": "إنشاء تصفية",
    "next": "التالي",
    "no": "لا",
    "no_thanks": "لا شكرا",
    "not_really": "ليس حقا",
    "not_this_time": "ليس هذه المرة",
    "open_sample": "فتح عينة الصورة",
    "paste_adjustments": "التعديلات معجون",
    "pay_wechat": "فتح Wechat",
    "prev": "السابق",
    "preview": "معاينة",
    "pro": "محترف",
    "purchased": "تم شراؤها",
    "remove": "إزالة",
    "remove_all_photos": "إزالة جميع الصور",
    "remove_photo": "إزالة الصورة",
    "remove_selected_photo": "إزالة الصورة المحددة",
    "reset": "إعادة الضبط",
    "reset_all": "إعادة التعيين كاملاً",
    "reset_password": "اعادة تعيين كلمة السر",
    "reset_photos": "إعادة تعيين تسميات حفظها وتحريرها",
    "reset_tips": "نصائح إعادة تعيين",
    "restore": "استعادة",
    "revert": "العودة إلى الأصل",
    "sample": "عينة",
    "save": "حفظ",
    "save_qr": "حفظ رمز الاستجابة السريعة",
    "save_share": "حفظ + حصة",
    "scan_filter": "فلتر مسح",
    "select_all": "اختر الكل",
    "select_changed": "حدد حرره",
    "send": "إرسال",
    "send_verification": "إعادة ارسال بريد التحقق",
    "show_in_folder": "أظهر ذلك في الملف",
    "show_me": "أرني",
    "signin": "تسجيل الدخول",
    "signout": "خروج",
    "signup": "إنشاء حساب Polarr",
    "skip": "تخطى",
    "submit_license_key": "إرسال مفتاح الترخيص",
    "subscribe": "الاشتراك",
    "subscribe_monthly": "الاشتراك الشهري",
    "subscribe_yearly": "الاشتراك سنوي",
    "sure": "حسنا بالتأكيد",
    "try": "محاولة",
    "try_again": "حاول ثانية",
    "unlock": "فتح",
    "unsubscribe": "إلغاء الاشتراك",
    "update": "تحديث",
    "whats_new": "ما هو الجديد",
    "yes": "نعم"
  },
  "dialogs": {
    "about": "حول",
    "about_text": "ونحن نعتقد أنه من المهم السماح المبدعين للتعبير عن جهد وجهات النظر والمواقف تجاه حياتهم، دون أن تفقد إخلاص الرغبة والالهام الإبداعية.",
    "account": "حساب",
    "all_filters": "جميع المرشحات",
    "all_filters_text": "فتح كامل جمع Polarr من المرشحات قسط",
    "backup_filters": "مزامنة وعمل نسخة احتياطية جميع المرشحات المخصصة الخاصة بك بحيث يمكنك استخدامها عبر الأجهزة والمنصات. تتطلب هذه الميزة تقوم بتسجيل الدخول باستخدام حساب Polarr.",
    "backup_filters_title": "مرشحات مخصص النسخ الاحتياطي",
    "brush_limit": "الحد قناع فرشاة الحالي </limit>",
    "denoise": "لدينا عملية دينويسي الوقت الحقيقي يجعل من السهل لإزالة الضوضاء والحبوب في الصور. على حدة التحكم مكونات اللون والضوضاء الإنارة.",
    "featured": "متميز",
    "filter_collections": "مجموعة فلتر",
    "filter_name": "اسم هذا المرشح",
    "get_picky": "تعبت من تحرير الصور؟ جرب التطبيق Polarr الجديد أن التعديلات الصور مع الذكاء الاصطناعي!",
    "get_updates": "الاشتراك في التحديثات",
    "get_updates_about": "كن أول من محاولة الخروج من الميزات الجديدة! <br/> إضافة عنوان البريد الإلكتروني الذي، وسوف نقوم بإعلامك حول ميزات ومنتجات جديدة من Polarr.",
    "get_updates_thankyou": "سوف الآن الحصول على التحديثات حول ميزات ومنتجات جديدة من Polarr! <br/> إذا كنت ترغب في إيقاف تلقي التحديثات، يمكنك \"إلغاء الاشتراك\" من البريد الإلكتروني الخاص بك في أي وقت.",
    "help": "مساعدة",
    "invite": "رابط تركيب سهم. سوف تكون مقفلة الموالية ميزات بعد تثبيت ثلاثة أشخاص باستخدام الرابط الخاص بك.",
    "local_adjustments": "تعديلات محلية يتيح لك استخدام مجموعة Polarr القوية من التعديلات على المناطق المستهدفة. وتشمل الأدوات اخفاء شعاعي، التدرج، اللون، وفرشاة.",
    "lock_rotation": "تناوب القفل",
    "max_export_size": "حجم تصدير ماكس",
    "new_filter": "حفظ التعديلات كمرشح",
    "no_purchases": "لا توجد مشتريات لاستعادة.",
    "offline": "يبدو جهازك غير متصل. يرجى المحاولة مرة أخرى في وقت لاحق.",
    "photos": "الصور",
    "polarr_premium": "Polarr بريميوم",
    "pro_all_filters": "ملامح برو <br/> + <br/> جميع المرشحات",
    "pro_all_filters_text": "فتح جميع الميزات بما في ذلك أدوات للمحترفين وجميع المرشحات قسط Polarr ل. استخدام حساب Polarr للوصول إلى كافة الميزات على منصات أخرى. النسخ الاحتياطي ومزامنة المرشحات المخصصة الخاصة بك.",
    "pro_features": "أدوات برو",
    "pro_features_all": "جميع أدوات برو",
    "purchase": "فتح شعاعي، التدرج، اللون، فرشاة، دينويسي، وأدوات النص. النسخ الاحتياطي ومزامنة المرشحات المخصصة الخاصة بك.",
    "purchase_failed": "تم إلغاء الشراء",
    "purchase_header": "فك كل مزايا الإحتراف",
    "purchase_successful": "لديك الآن إمكانية الوصول إلى الإصدار المميز",
    "purchase_unavailable": "المشتريات غير متوافرة في هذا الوقت  للأسف",
    "purchases": "مشتريات",
    "rate_review": "تقييم وتعليق",
    "rename_filter": "إعادة تسمية تصفية",
    "restore_failed": "لم يكن لديك حسابك الوصول ميزة الكامل.",
    "restore_successful": "تتم استعادة كافة مشترياتك السابقة الآن.",
    "review": "استمعت في محرر صور polar حتى الآن",
    "review_no": "هل تمانع إعطائنا بعض الإنطباعات",
    "review_yes": "إذاً ماذا عن تقييمنا لدى متجر التطبيقات؟",
    "save_adjustments": "جعل تصفية مخصص يمكنك مشاركة.",
    "send_feedback": "أرسل التغذية المرتدة",
    "signin_polarr_account": "تسجيل الدخول إلى الحساب الخاص بك Polarr",
    "store": "متجر",
    "subscribe": "الاشتراك في <strong>Polarr بريميوم</strong>",
    "subscribe_access": "من أجل الوصول الكامل إلى:",
    "subscribe_cancel": "يمكنك \"إلغاء الاشتراك\" في الإعدادات في أي وقت",
    "subscribe_caveat": "* وتشمل الميزات برو تعديل التدرج والتكيف شعاعي، اخفاء اللون، وبالفرشاة لون. سيتم تطبيق عضويتك على جميع الأجهزة والمنصات هذا العام.",
    "subscribe_china": "الاشتراك في polaxiongkeji حساب WeChat وجعل الدفع في WeChat، ثم الدخول مع حساب Polarr لتنشيط الميزات للمحترفين.",
    "subscribe_error": "عفوا! كان هناك خطأ الاشتراك في Polarr. حاول ثانية؟",
    "subscribe_features": "<li style=\";text-align:right;direction:rtl\"> ملامح برو (4 أدوات التكيف المحلية) </li><li style=\";text-align:right;direction:rtl\"> الوصول إلى جميع مرشح حزم 12 قسط </li><li style=\";text-align:right;direction:rtl\"> البقاء مستكملة عن الأشكال الخام </li><li style=\";text-align:right;direction:rtl\"> إضافة محتوى جديد شهريا! </li><li style=\";text-align:right;direction:rtl\"> عضوية الوصول على جميع المنابر * </li>",
    "subscribe_notify": "يخطر لي عندما بلدي Polarr عضوية مميزة يمكن استخدامها على المزيد من المنصات والأجهزة",
    "subscribe_price": "</price>",
    "subscribe_refund": "سبق لك أن قمت بشراء شيء مع Polarr وكنت مؤهلا ل",
    "subscribe_success": "شكرا لك على الاشتراك في Polarr بريميوم!",
    "subscribe_unlock_success": "أنت الآن Polarr قسط الأعضاء!",
    "subscription_expired": "انتهت عضويتك Polarr بريميوم",
    "text_tool": "أداة النص يتيح لك إضافة النص والأشكال إلى الصور. مع ميزات مثل الرسالة وتباعد الأسطر، والمحاذاة، والتغليف، لديك السيطرة الكاملة النص الخاص بك. لقد شملت حتى الميزات المتقدمة مثل التحجيم ناقلات ديناميكي وتعديل مزج فريد من نوعه.",
    "text_tool_description": "إضافة وتحرير النص على الشاشة. تدوير، وتغير اللون، تعيين المزج، وحتى ضبط إلكتروني التباعد.",
    "thankyou": "شكرا!",
    "unlock_all": "فتح كل شيء",
    "unlock_all_note": "تسجيل الدخول Polarr حساب المؤيدة أو إدخال مفتاح الترخيص.",
    "unlock_alternate": "يمكنك أيضا ترقية عن طريق تسجيل الدخول إلى حساب موجود أو عن طريق إدخال مفتاح الترخيص في",
    "unlock_failed": "برو الميزات غير متوفرة لهذا الحساب.",
    "unlock_success": "لديك الآن الوصول إلى جميع الميزات.",
    "unsplash_info": "<p style=\";text-align:right;direction:rtl\"> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> و <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> ، أفضل مصدر مجانا وعالية الدقة معرض الصور الفوتوغرافية، واتفقتا على تجلب لك مجموعة من المرشحات قسط 10 إنشاؤها من قبل أعضاء المجتمع مصور Unsplash. </p>",
    "upgrade": "الترقية إلى النسخة الكاملة لفتح جميع الميزات التالية.",
    "upgrade_caveat": "* وتشمل 12 حزم تصفية إضافية محاكاة الكلاسيكية السينمائي، عتيقة فيلم، الأشعة تحت الحمراء فيلم، نغمات الجلد والعديد من التعديلات الأخرى مناسبة لمختلف الموضوعات والمشاهد.",
    "upgrade_features": "<li style=\";text-align:right;direction:rtl\"> التعديلات التدرج </li><li style=\";text-align:right;direction:rtl\"> التعديلات دائرية </li><li style=\";text-align:right;direction:rtl\"> اللون اخفاء </li><li style=\";text-align:right;direction:rtl\"> تعديل تفريش </li><li style=\";text-align:right;direction:rtl\"> 12 حزم مرشح قسط إضافية </li><li style=\";text-align:right;direction:rtl\"> تصدير دفعة الصورة </li>",
    "use_polarrfilter": "استخدام <strong>#polarrfilter</strong> الحصول على لاحظ!",
    "user_in_paid": "يمكنك تسجيل الدخول إلى Polarr على منصات أخرى للوصول إلى كافة الميزات.",
    "user_in_unpaid": "إذا كنت تطلق كل شيء، عليك أن تكون قادرا على تسجيل الدخول إلى Polarr على منصات أخرى والوصول إلى جميع الميزات.",
    "verification_sent": "تم إرسال البريد الإلكتروني التحقق. <br/> يرجى التحقق من بريدك الالكتروني للحصول على تعليمات التحقق.",
    "verify_account": "لتسجيل الدخول، يرجى التحقق من حسابك. <br/> تحقق من بريدك الالكتروني للحصول على تعليمات التحقق.",
    "webgl_not_supported": "نحن آسفون للغاية ولكن الجهاز لا تلبية متطلبات Polarr في الرسم، يرجى تحديث إلى أحدث نظام التشغيل وحاول مرة أخرى."
  },
  "labels": {
    "adjustments": "التعديلات",
    "after": "بعد",
    "alignment": "المحاذاة",
    "all": "الكل",
    "amount": "الكمية",
    "angle": "زاوية",
    "aqua": "آكوا",
    "artwork": "العمل الفني",
    "auto": "أوتوماتيكي",
    "balance": "التوازن",
    "basic": "أساسي",
    "batch_rename": "إعادة تسمية دفعة",
    "batch_resize": "دفعة تغيير حجم",
    "before": "قبل",
    "best": "الأفضل",
    "blacks": "تحويل إلى أسود",
    "blend": "مزيج",
    "blend_mode": "وضع مزيج",
    "blue": "الأزرق",
    "blur": "شىء ضبابي",
    "brightness": "السطوع",
    "brush_flow": "تدفق فرشاة",
    "brush_opacity": "فرشاة التعتيم",
    "brush_size": "حجم فرشاة",
    "clarity": "الوضوح",
    "collection": "مجموعة",
    "color": "اللون",
    "contrast": "التباين",
    "crop": "قطع",
    "crop_size": "أبعاد المحاصيل",
    "curves": "منحنيات",
    "custom": "مخصص",
    "dehaze": "Dehaze",
    "denoise": "ضد الضوضاء",
    "detail": "تفاصيل",
    "diffuse": "الإنتشار",
    "distortion": "التشويه",
    "distortion_amount": "التشويه",
    "distortion_horizontal": "تشويه الأفقي",
    "distortion_vertical": "تشويه العمودي",
    "dividers": "فواصل",
    "edit": "تحرير",
    "effects": "المؤثرات",
    "email": "البريد الإلكتروني",
    "enable": "تمكين",
    "estimated_size": "يقدر حجم",
    "exposure": "التعريض",
    "eyes": "عيون",
    "face": "وجه",
    "favorites": "المفضلة",
    "feather": "ريشة",
    "fit": "صالح",
    "font": "الخط",
    "format": "شكل",
    "free": "حر",
    "frequent": "الأكثر استخداما",
    "fringing": "التهديب",
    "grain": "سطح محبب",
    "green": "الأخضر",
    "guides": "أدلة",
    "high": "العالي",
    "highlights": "تسليط الضوء",
    "histogram": "رسم بياني",
    "history": "تاريخ",
    "hsl": "HSL",
    "hue": "تعدد الالوان",
    "hue_blue": "الأعرج - ونه الازرق",
    "hue_red": "درجة لون الاحمر",
    "invert": "عكس",
    "lens": "العدسة",
    "lens_distortion": "عدسة تشويه",
    "letter_spacing": "المسافات بين الحروف",
    "license_key": "مفتاح الترخيص",
    "light": "الضوء",
    "line_spacing": "تباعد الأسطر",
    "lips": "شفاه",
    "look": "فلتر",
    "looks": "مرشحات",
    "low": "الخفيف",
    "luminance": "النصوع",
    "luminance_blue": "أناره الزرقاء",
    "luminance_red": "أناره الحمراء",
    "magenta": "المعدني",
    "master": "الإحتراف",
    "max_size": "أبعاد ماكس",
    "med": "المتوسط",
    "mix": "ضبط الكل",
    "mosaic": "تقطيع",
    "name": "اسم",
    "none": "لا شيء",
    "nose": "أنف",
    "opacity": "غموض",
    "optics": "بصريات",
    "options": "خيارات",
    "orange": "البرتقالي",
    "original": "الأصلي",
    "outline": "الخطوط العريضة",
    "password": "كلمه السر",
    "position": "التموضع",
    "prefix": "قطاع البادئة",
    "presets": "المسبقة",
    "preview": "معاينة",
    "purple": "البنفسجي",
    "quality": "الجودة",
    "ratio": "النسبة",
    "red": "الأحمر",
    "reflect": "تعكس",
    "resolution": "قرار",
    "rgb": "الالوان الاحمر و أخضر و أزرق",
    "roundness": "استدارة",
    "saturation": "التشبّع",
    "saturation_blue": "تشبع الازرق",
    "saturation_red": "تشبع الاحمر",
    "sayings": "أقوال",
    "shadow": "ظل",
    "shadows": "الظلال",
    "shape": "شكل",
    "shapes": "الأشكال",
    "shapes_presets": "الأشكال والمسبقة",
    "sharpen": "شحذ",
    "size": "الحجم",
    "skin": "بشرة",
    "smoothness": "تمهيد",
    "split_tone": "التناغم",
    "teeth": "اسنان",
    "temperature": "حرارة",
    "text": "نص",
    "texture": "الملمس",
    "threshold": "عتبة",
    "tint": "لون خفيف",
    "tone": "نغمة",
    "toning": "التنغيم",
    "use_prefix": "استخدام بادئة",
    "use_watermark": "استخدام العلامة المائية",
    "user_name": "اسم المستخدم",
    "vibrance": "التردد",
    "vignette": "المصغّر",
    "vignette_amount": "التصغير",
    "watermark_image": "العلامة المائية صورة",
    "wb": "توازن الأبيض",
    "whitening": "تبييض",
    "whites": "تحويل إلى أبيض",
    "width": "عرض",
    "yellow": "الأصفر"
  },
  "menus": {
    "batch_export": "تصدير دفعة",
    "change_language": "غير اللغة",
    "copy_edit": "نسخة التحرير",
    "edit_watermark": "تحرير العلامة المائية",
    "export": "شكل، تغيير الحجم، العلامة المائية",
    "filters_users": "مرشحات من قبل Polarrists",
    "guide": "دليل",
    "image_quality": "جودة الصورة",
    "image_settings": "إعدادات التصدير",
    "language": "لغة",
    "more_filters": "مزيد من الفلاتر",
    "open": "فتح",
    "open_in": "فتح في",
    "paste_edit": "لصق التعديلات",
    "remove": "إلغاء",
    "rename": "إعادة تسمية",
    "restore_purchases": "استعادة المشتريات",
    "save_cameraroll": "حفظ إلى لفة الكاميرا",
    "save_copy": "حفظ نسخة",
    "settings": "إعدادات",
    "share": "شارك",
    "share_edits": "حصة التحرير",
    "share_filter": "حصة فلتر",
    "show_instructions": "دليل معرض تعديلات",
    "show_intro": "مشاهدة مقدمة",
    "show_video_tutorial": "مشاهدة فيديو تعليمي",
    "single_export": "تصدير"
  },
  "onboarding": {
    "become_master": "تصبح على درجة الماجستير تحرير الصور.",
    "step_0": "مرحبا بكم في Polarr! <br/> دعونا نبدأ عن طريق عرض بعض المرشحات.",
    "step_1": "انقر لتحديد مرشح. <br/> لوحة التمرير السريع لرؤية المزيد من المرشحات.",
    "step_10": "يمكنك سحب لإعادة ترتيب كل الرموز الأداة.",
    "step_2": "حنفية <i class='icon-check'></i> لتطبيق عامل التصفية. <br/> استخدم شريط التمرير لضبط كثافة من التصفية.",
    "step_3": "كل مرشح للتحرير تماما. <br/> الذهاب إلى تعديلات لرؤية تعديلاتك.",
    "step_4": "يمكنك تغيير تعديلا طريق سحب شريط التمرير.",
    "step_4a": "الضغط لفترة طويلة ثم اسحب لضبط التمرير.",
    "step_5": "رؤية جميع التعديلات التي قمت بها في لوحة التاريخ.",
    "step_6": "يمكنك التراجع عن تحرير والعودة إلى أي نقطة في التاريخ.",
    "step_7": "حفظ الصور الخاصة بك هنا. <br/> يمكنك تغيير الحجم، إضافة العلامة المائية أو حفظ صور متعددة.",
    "step_7a": "حفظ الصور الخاصة بك هنا. <br/> يمكنك أيضا تبادل وحفظ صور متعددة.",
    "step_8": "فتح الصور الخاصة بك هنا.",
    "step_9": "عرض الصور الخاصة بك فتح في لوحة الصور."
  },
  "status": {
    "account_suspended": "يتم وضع حسابك في الانتظار نظرا لنشاط مشبوه. لمزيد من المعلومات، يرجى التحقق من البريد الإلكتروني أو الاتصال <a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "حنفية <i class='icon-add-face'></i> لإضافة وجه",
    "adjustment_copied": "التعديلات التي تم نسخها",
    "adjustment_pasted": "التعديلات لصق",
    "demo_mode": "تم إنشاء هذا الإصدار من برنامج تحسين الصور Polarr خصيصا لأغراض العرض التوضيحي",
    "downloading_font": "تحميل الخط",
    "drag_icons": "اسحب الرموز لإعادة ترتيب أدوات",
    "drop_files": "إسقاط الملفات إلى فتح",
    "email_invalid": "عذرا، يبدو أن عنوان البريد الإلكتروني غير صالح.",
    "email_reset": "تم إرسال رابط إعادة تعيين. <br/> يرجى التحقق من البريد الإلكتروني الخاص بك.",
    "exporting_filter": "مرشح إنقاذ",
    "field_invalid": "غير صالح.",
    "file_exists": "تلك الصورة هي بالفعل مفتوحة",
    "file_not_supported": "عذرا، لا يتم اعتماد هذا النوع من الملفات",
    "filter_exported": "تم حفظ الفلتر للفة الكاميرا.",
    "filter_saved": "تصفية محفوظ",
    "filters_sync_error": "مرشحات خطأ المزامنة",
    "filters_synced": "مرشحات مزامنتها",
    "filters_syncing": "المزامنة المرشحات ...",
    "import_filter_error": "عذرا، لا يمكن استيرادها هذا المرشح",
    "import_limit": "Polarr يمكن فتح فقط حتى 50 صورة، الرجاء إزالة بعض الصور قبل فتح أكثر.",
    "importing_filter": "مرشح المستوردة",
    "importing_photo": "فتح الصورة",
    "importing_photos": "فتح الصور",
    "invalid_credentials": "البريد الإلكتروني غير صحيح أو كلمة المرور",
    "loading_photo": "يتم تحميل الصورة",
    "login_success": "لقد تم تسجيل بنجاح في!",
    "no_faces": "لا وجوه الكشف.",
    "no_user": "عدم وجود هذا المستخدم.",
    "offline": "تعذر الاتصال بالخادم.",
    "photo_load_error": "غير قادر على تحميل الصور",
    "photo_saved": "حُفِظت الصورة",
    "photo_shared": "تمت مشاركة الصورة",
    "photos_saved": "الصور المحفوظة",
    "purchase_required": "شراء مطلوب",
    "raw_failed": "غير قادر على قراءة صور RAW. دعم الخام لا يزال تجريبيا.",
    "registration_success": "لقد نجحت في جعل حساب جديد! <br/> لتسجيل الدخول على أجهزة أخرى، يجب عليك أولا التحقق من حسابك. لقد أرسلت لك رسالة بالبريد الالكتروني مع تعليمات التحقق.",
    "saving_photo": "إنقاذ صورة",
    "saving_photo_error": "لا يمكن حفظ الصورة",
    "saving_photo_error_sd": "Polarr لا تملك صلاحية لدخول بطاقة SD الخاصة بك أو التخزين الداخلية. يرجى مراجعة آخر تحديث نظام أندرويد لإصلاح المشكلة.",
    "saving_photos": "حفظ الصور",
    "server_offline": "عذرا، الملقم لا يستجيب. <br/> هل أنت متصل بالإنترنت؟",
    "sharing_photo": "تبادل الصور",
    "sharing_photo_error": "لن تستطيع أن تشارك الصورة",
    "show_me_how": "تبين لي كيفية استخدام </thing>",
    "supported_files": "Polarr يدعم JPG، PNG، BMP، وبعض الملفات الخام",
    "unlocks_remaining": "عندك </unlocks> يفتح المتبقية",
    "user_exists": "وهناك بالفعل حساب مع هذا البريد الإلكتروني أو اسم مستخدم.",
    "wrong_password": "كلمة مرور خاطئة"
  },
  "tooltips": {
    "add_face": "إضافة الوجه",
    "add_text": "إضافة نص",
    "adjustments": "التعديلات",
    "apply_adjustments": "تطبيق التعديلات",
    "auto_adjust": "تعديلات السيارات",
    "brush_filter": "قناع فرشاة",
    "color_filter": "قناع اللون",
    "crop": "محصول",
    "crop_straighten": "المحاصيل وزاوية",
    "current_adjustments": "التعديلات الحالية",
    "double_tap_edit": "انقر نقرا مزدوجا لتحرير",
    "drag_watermark": "اسحب لنقل مائية",
    "eraser": "ممحاة",
    "face": "أدوات وجه",
    "filmstrip": "صور فتح",
    "flip": "نقف",
    "flip_h": "الوجه أفقيا",
    "flip_v": "الوجه عموديا",
    "global_adjustments": "التعديلات الرئيسية",
    "gradient_filter": "قناع التدرج",
    "guides": "أدلة",
    "history": "تحرير التاريخ",
    "info": "معلومات",
    "local_adjustments": "تعديلات انتقائية",
    "looks": "مرشحات",
    "masks": "أقنعة",
    "move_face": "الخطوة الوجه",
    "open": "مفتوحة صور",
    "pan": "مقلاة",
    "radial_filter": "قناع شعاعي",
    "redo": "إعادة",
    "remove_face": "إزالة الوجه",
    "remove_text": "إزالة النص",
    "rotate": "تناوب",
    "rotate_ccw": "تدوير عكس عقارب الساعة",
    "rotate_cw": "تدوير مع عقارب الساعة",
    "save": "احفظ الصورة",
    "settings": "إعدادات",
    "show_original": "إظهار النسخة الأصلية",
    "text": "أداة النص",
    "undo": "تراجع"
  },
  "tutorial": {
    "blacks": "تغيير سطوع الأجزاء السوداء من الصورة",
    "brightness": "تغيير سطوع أو \"غاما\" من الصورة",
    "brush_mask_0": "استخدم إصبعك لطلاء مع الفرشاة.",
    "brush_mask_1": "قناع فرشاة يؤثر فقط على منطقة المصقول.",
    "clarify": "وضح",
    "clarify_1": "<b>لحد الضباب</b> <br/> الضباب هو قضية مشتركة في الصور وانه من السهل إزالتها في Polarr. <br/> لإزالة الضباب، وزيادة <b>Dehaze</b> إلى 85",
    "clarify_2": "<b>استعادة التعرض</b> <br/> Dehaze يظلم أحيانا الصورة كثيرا. <br/> لاسترداد بعض الضوء، وزيادة <b>التعرض</b> ل30",
    "clarify_3": "<b>زيادة التباين</b> <br/> هو ذهب الضباب معظمها الآن، لكننا يمكن أن تعزز ديناميكية الخفيفة أكثر من ذلك بقليل. زيادة <b>التباين</b> إلى 60",
    "clarify_4": "<b>تعزيز التفاصيل</b> <br/> لتعزيز التفاصيل في الصورة، يمكننا استخدام أداة وضوح. زيادة <b>وضوح</b> إلى 40",
    "clarify_5": "عمل جيد! لقد تعلمت كيفية جعل الصورة ضبابية أفضل كثيرا في 4 خطوات بسيطة.",
    "clarify_description": "في هذا البرنامج التعليمي، وسوف تتعلم كيفية الحد من الضباب، وتحديد الخطوط العريضة وإضافة الحيويه لموضوعك.",
    "clarity": "زيادة أو تخفيف من التفاصيل في الصورة",
    "color_mask_0": "قناع اللون يؤثر فقط على اللون المحدد.",
    "color_mask_1": "اسحب لتغيير اللون المحدد.",
    "color_mask_2": "<b>عتبة</b> يحدد حساسية التحديد.",
    "contrast": "جعل المشهد أكثر تميزاً",
    "curves": "تغيير منحنى لهجة من الصورة",
    "deep_space": "الفضاء السحيق",
    "deep_space_1": "<b>استعادة ضوء</b> <br/> التعرض للتسوية Polarr يحاكي التعرض للكاميرا. <br/> لاستعادة ضوء، تعيين <b>التعرض</b> إلى 100",
    "deep_space_2": "<b>تعزيز التفاصيل</b> <br/> دعونا نجعل النجوم تبدو أكثر إثارة للإعجاب. <br/> مضيفا الوضوح مساعدة من خلال الكشف عن مزيد من التفاصيل. <br/> لتعزيز التفاصيل، تعيين <b>وضوح</b> إلى 100",
    "deep_space_3": "<b>تضيف عمقا</b> <br/> لجلب مزيد من التركيز على وسط الصورة، يمكننا أن نضيف المصغر لتلقي بظلالها على حواف الصورة. <br/> لإضافة عمق، والحد من <b>المقالة القصيرة</b> ل-100",
    "deep_space_4": "<b>المقالة القصيرة على نحو سلس</b> <br/> الحق الآن المصغر تبدو حادة جدا. <br/> لجعل المقالة القصيرة تبدو ناعمة ودقيقة، <br/> زيادة <b>الريشة</b> الى 100",
    "deep_space_5": "<b>استعادة يسلط الضوء على</b> <br/> بعض النجوم في حافة الصورة نتطلع إلى الظلام. نحن يمكن استعادتها عن طريق زيادة <b>ويبرز</b> إلى 100",
    "deep_space_6": "<b>غير اللون</b> <br/> هسل يسمح لك بتغيير سمات لون معين. لتغيير لون السماء، وزيادة <b>أكوا هوى</b> الى 100",
    "deep_space_7": "عمل جيد! لقد تعلمت كيفية جعل السماء المرصعة بالنجوم تبدو مثيرة للإعجاب حقا.",
    "deep_space_description": "في هذا البرنامج التعليمي، وسوف تتعلم طرق جديدة لإضافة عمق، وضبط اللون والتغيير تعرض سماء الليل.",
    "dehaze": "إضافة أو إزالة الضباب من الصورة",
    "denoise": "إزالة الضجيج من الصور",
    "diffuse": "تعطي الصورة تأثير \"وهج\"",
    "distortion": "تشويه",
    "distortion_1": "<b>تشويه الصحيح</b> <br/> ونحن نرى في كثير من الأحيان المباني والجدران تبحث ملتوية في الصور. عادة ما يحدث هذا من تصميم عدسات الكاميرا زاوية واسعة. نحن يمكن اصلاحها بسهولة هذا في Polarr مع أدوات التشويه. <br/> لإصلاح التشوه، وزيادة <b>عمودي تشويه</b> ل70",
    "distortion_2": "<b>المقالة القصيرة الصحيحة</b> <br/> وبالإضافة إلى تشوهات بصرية، معظم عدسات الكاميرا لديها ضوء تداعيات قرب حوافها. نحن يمكن أن تقلل من هذه الحواف الداكنة باستخدام أداة المقالة القصيرة. <br/> لتصحيح المقالة القصيرة، وزيادة <b>المقالة القصيرة</b> إلى 20",
    "distortion_3": "<b>تعزيز التفاصيل</b> <br/> وأخيرا، من أجل تعزيز التفاصيل في الصورة، يمكننا استخدام أداة الوضوح. <br/> زيادة <b>وضوح</b> إلى 100",
    "distortion_4": "عمل جيد! كنت قد تعلمت كيفية تصحيح العيوب عدسة مشتركة في 3 خطوات بسيطة.",
    "distortion_amount": "أجعل زاوية واسعها او أجعل الصوره بشكل عين السمكة",
    "distortion_description": "في هذا البرنامج التعليمي، وسوف تتعلم كيفية تصحيح التشوهات عدسة والمقالة القصيرة في طلقات المعمارية.",
    "distortion_horizontal": "تغيير تشويه على طول المحور الأفقي",
    "distortion_vertical": "تغيير تشويه على طول المحور الرأسي",
    "exposure": "تغيير سطوع دون غسل خارج الصورة",
    "face_0": "Polarr تلقائيا بالكشف عن وجوه ويفصل السمات الفردية.",
    "face_1": "استخدام أداة تشويه لضبط حجم وموضع من ملامح الوجه.",
    "fringing": "اضافة دوخة الى صورتك",
    "gradient_mask_0": "يؤثر قناع التدرج المناطق في خط مستقيم.",
    "gradient_mask_1": "قرصة تغيير حجمه. <br/> السحب لنقل وتدوير.",
    "gradient_mask_2": "<b>عكس</b> مفاتيح الاتجاه التدرج.",
    "gradient_mask_3": "<b>تعكس</b> يجعل التدرج تشغيل في كلا الاتجاهين.",
    "grain": "إضافة تأثير الحبوب فيلم",
    "highlights": "تغيير سطوع الضوء دون غسل خارج الصورة",
    "hsl": "تغيير هوى، والتشبع، أو خفة من لون معين.",
    "hsl_header": "هسل",
    "hue_blue": "إزاحة اللون الأزرق ليبدو كباقي الألوان",
    "hue_red": "إزاحة اللون الأحمر ليبدو كباقي الألوان",
    "luminance_blue": "تغيير سطوع الأزرق",
    "luminance_red": "تغيير سطوع الأحمر",
    "mastering_adjustments": "اتقان التعديلات",
    "mosaic": "تعطي الصورة تأثير الفسيفساء",
    "radial_mask_0": "قناع شعاعي يؤثر فقط على منطقة دائرية.",
    "radial_mask_1": "قرصة تغيير حجمه. <br/> السحب لنقل وتدوير.",
    "radial_mask_2": "تغيير <b>ريشة</b> ضبابي من القناع.",
    "radial_mask_3": "<b>عكس</b> مفاتيح المنطقة المصابة.",
    "saturation": "جعل صورتك أكثر ألواناً",
    "saturation_blue": "تغيير التلوّن الأزرق",
    "saturation_red": "تغيير التلوّن الأحمر",
    "shadows": "تغيير سطوع الظلال دون غسل خارج الصورة",
    "sharpen": "جعل الصورة أكثر وضوحا",
    "temperature": "جعل الصورة أكثر دفئا أو برودة",
    "tint": "لوّن الصورة حسب الوقت من اليوم",
    "toning_highlights": "تغيير لون الضوء",
    "toning_shadows": "تغيير لون الظل",
    "vibrance": "تغيير التشبع دون غسل من لون البشرة",
    "vignette_amount": "تتلاشى أو يسلط الضوء على حواف الصورة",
    "vignette_feather": "تغيير ضبابي من المقالة القصيرة",
    "vignette_highlights": "السماح أبرز تبين من خلال المصغر",
    "vignette_roundness": "جعل المقالة القصيرة أكثر مستديرة أو مربعة",
    "vignette_size": "تغيير حجم المصغر",
    "white_balance": "توازن اللون الأبيض",
    "white_balance_1": "<b>توازن اللون الأبيض الصحيح</b> <br/> معظم مصادر الضوء (المصابيح الكهربائية، والبطاريات، وغيرها) لا تنبعث منها اللون الأبيض بحتة. هذا يمكن أن يسبب البيض في الصور ليكون الزهر الأصفر أو الأزرق. لتصحيح هذا، وانخفاض <b>درجة الحرارة</b> إلى -30",
    "white_balance_2": "<b>استعادة الضوء من الظلال</b> <br/> أحيانا أجزاء من صورة أكثر قتامة مما هو مقصود لأن عيوننا يمكن أن يرى مزيدا من الضوء من الكاميرا. يمكننا استرداد الضوء من الظلال باستخدام أداة الظلال. <br/> زيادة <b>الظلال</b> إلى 100",
    "white_balance_3": "<b>تعزيز التفاصيل</b> <br/> وأخيرا، من أجل تعزيز التفاصيل في الصورة، يمكننا استخدام أداة الوضوح. <br/> زيادة <b>وضوح</b> إلى 100",
    "white_balance_4": "عمل جيد! كنت قد تعلمت كيفية إزالة الزهر الأصفر واستعادة ضوء من الظلال في 3 خطوات بسيطة.",
    "white_balance_description": "في هذا البرنامج التعليمي، وسوف تتعلم مفهوم توازن اللون الأبيض وكيفية تصحيح في ضوء الاصطناعي الباب.",
    "whites": "يتغير سطوع الأجزاء البيضاء من الصورة"
  }
};
},{}],321:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "Ychwanegu",
    "add_image": "Dewiswch ddelwedd",
    "add_to_favorites": "Ychwanegu at ffefrynnau",
    "aspect": "Agwedd",
    "back": "Yn ôl",
    "buy": "Prynu",
    "buy_full_version": "Prynu fersiwn llawn",
    "cancel": "Canslo",
    "check_again": "Gwiriwch eto",
    "choose_folder": "Dewiswch ffolder",
    "clear": "Clirio",
    "clear_history": "Clirio Hanes",
    "close": "Cau",
    "cloud": "Cwmwl",
    "continue_anyway": "Parhau beth bynnag",
    "current_image": "Cyfredol",
    "delete": "Dileu",
    "device": "Dyfais",
    "dismiss": "Gwrthod",
    "done": "Wedi gorffen",
    "dont_ask": "Yana so'ramayman",
    "duplicate": "dyblygu",
    "enter_license": "Rhowch allwedd trwydded",
    "enter_license_key": "Rhowch allwedd trwydded",
    "exit": "ymadael",
    "export": "Allforio",
    "export_filter": "Cadw fel cod QR",
    "forgot_password": "Anghofio cyfrinair?",
    "free_membership": "aelodaeth am ddim",
    "get": "cael",
    "get_license": "Cael allwedd trwydded",
    "get_started": "Dechrau",
    "got_it": "ei gael",
    "have_account": "Oes cyfrif gennych eisoes?",
    "import": "Mewnforio",
    "import_filter": "hidlydd mewnforio",
    "import_filter_photos": "Mewnforio o Photos",
    "import_photos": "lluniau agored",
    "info": "Gwybodaeth",
    "installed": "Gorseddedig",
    "invite": "Anfon cyswllt",
    "learn_more": "Dysgu mwy",
    "load_last_photo": "Llwytho llun olaf ar lansiad",
    "new": "Newydd",
    "new_filter": "creu Hidlen",
    "next": "Nesaf",
    "no": "Na",
    "no_thanks": "Dim diolch",
    "not_really": "Ddim wir",
    "not_this_time": "Emas, balki bu vaqt",
    "open_sample": "photo sampl Agored",
    "paste_adjustments": "addasiadau Gludo",
    "pay_wechat": "Wechat agored",
    "prev": "Blaenorol",
    "preview": "Rhagolwg",
    "pro": "Proffesiynol",
    "purchased": "Prynwyd",
    "remove": "Tynnu",
    "remove_all_photos": "Tynnu pob lluniau",
    "remove_photo": "Tynnu llun",
    "remove_selected_photo": "Tynnu llun a ddewiswyd",
    "reset": "Ailosod",
    "reset_all": "Ailosod y Cwbl",
    "reset_password": "Ailosod cyfrinair",
    "reset_photos": "Ailosod labeli hachub a'u golygu",
    "reset_tips": "awgrymiadau Ailosod",
    "restore": "adfer",
    "revert": "Dychwelyd i Gwreiddiol",
    "sample": "Sampl",
    "save": "Cadw",
    "save_qr": "Arbed cod QR",
    "save_share": "Save + Share",
    "scan_filter": "hidlydd Scan",
    "select_all": "Dewis Popeth",
    "select_changed": "Dewiswch Golygwyd",
    "send": "Anfon",
    "send_verification": "Ail-anfon e-bost dilysu",
    "show_in_folder": "Dangos yn y ffolder",
    "show_me": "Dangos imi",
    "signin": "Mewngofnodi",
    "signout": "Allgofnodi",
    "signup": "Creu cyfrif Polarr",
    "skip": "Skip",
    "submit_license_key": "Cyflwyno allwedd trwydded",
    "subscribe": "Tanysgrifio",
    "subscribe_monthly": "Tanysgrifio misol",
    "subscribe_yearly": "Tanysgrifio Blynyddol",
    "sure": "Iawn, pam lai",
    "try": "Rhowch gynnig",
    "try_again": "Ceisio eto",
    "unlock": "Datglo",
    "unsubscribe": "Dad-danysgrifio",
    "update": "Diweddaru",
    "whats_new": "Beth sy'n newydd",
    "yes": "Ie!"
  },
  "dialogs": {
    "about": "Ynglŷn",
    "about_text": "Credwn ei bod yn bwysig i ganiatáu crewyr i fynegi eu safbwyntiau ac agweddau tuag at fywyd ddiymdrech, heb golli ffyddlondeb eu dymuniad creadigol ac ysbrydoliaeth.",
    "account": "cyfrif",
    "all_filters": "pob hidlydd",
    "all_filters_text": "Datglo casgliad cyfan Polarr o hidlwyr premiwm",
    "backup_filters": "Cydamseru ac yn ôl i fyny eich holl hidlwyr arfer fel y gallwch eu defnyddio ar draws dyfeisiau a llwyfannau. Mae'r nodwedd hon yn ei gwneud yn ofynnol i chi logio i mewn gyda chyfrif Polarr.",
    "backup_filters_title": "Hidlau Custom Backup",
    "brush_limit": "Mae'r terfyn Mask Brush presennol yw </limit>",
    "denoise": "Mae ein proses denoise amser real yn ei gwneud yn hawdd i gael gwared sŵn a grawn mewn lluniau. Ar wahân rheoli cydrannau lliw a sŵn goleuder.",
    "featured": "Sylw",
    "filter_collections": "Casgliadau Hidlo",
    "filter_name": "Enwch hidlen yma",
    "get_picky": "Wedi blino o llun golygu? Rhowch gynnig ar app newydd Polarr sy'n golygiadau lluniau gyda Deallusrwydd Artiffisial!",
    "get_updates": "Tanysgrifio i ddiweddariadau",
    "get_updates_about": "Byddwch y cyntaf i roi cynnig ar nodweddion newydd! <br/> Ychwanegwch eich bod cyfeiriad e-bost, a byddwn yn rhoi gwybod i chi am nodweddion a chynhyrchion newydd o Polarr.",
    "get_updates_thankyou": "Byddwch yn awr yn cael y newyddion diweddaraf am nodweddion a chynhyrchion newydd o Polarr! <br/> Os ydych yn dymuno rhoi'r gorau i dderbyn diweddariadau, gallwch \"dad-danysgrifio\" gan eich e-bost ar unrhyw adeg.",
    "help": "Help",
    "invite": "Cyswllt gosod Share. Bydd nodweddion pro yn cael eu datgloi ar ôl tri o bobl eu gosod gan ddefnyddio eich cysylltiad.",
    "local_adjustments": "addasiadau lleol yn gadael i chi ddefnyddio set pwerus Polarr o addasiadau ar ardaloedd a dargedwyd. Yn cynnwys offer masgio Radial, Graddiant, Lliw, a Brush.",
    "lock_rotation": "cylchdro Lock",
    "max_export_size": "Maint y allforio Max",
    "new_filter": "Save golygiadau fel hidlydd",
    "no_purchases": "Nid oes unrhyw bryniadau i adfer.",
    "offline": "Qurilmangiz Foydalanuvchi bilan aloqa Foydalanuvchining ko'rinadi. Undan keyin yana harakat qilib ko'ring.",
    "photos": "Lluniau",
    "polarr_premium": "Premiwm Polarr",
    "pro_all_filters": "nodweddion pro <br/> + <br/> pob hidlydd",
    "pro_all_filters_text": "Datglo holl nodweddion gan gynnwys offer pro a phob o hidlwyr premiwm Polarr yn. Defnyddiwch eich cyfrif Polarr i gael mynediad at yr holl nodweddion ar lwyfannau eraill. Backup a sync eich hidlyddion arferiad.",
    "pro_features": "pro Tools",
    "pro_features_all": "Mae pob Pro Tools",
    "purchase": "Datglo Radial, Graddiant, Lliw, Brush, Denoise, ac offer Testun. Backup a sync eich hidlyddion arferiad.",
    "purchase_failed": "Wedi canslo'r broses prynu",
    "purchase_header": "Datgloi'r holl nodweddion proffesiynol",
    "purchase_successful": "Mae gennych fynediad at y fersiwn proffesiynol nawr.",
    "purchase_unavailable": "Nid oes modd prynu eitemau ar hyn o bryd. Mae'n flin gennym.",
    "purchases": "pryniannau",
    "rate_review": "Cyfradd ac adolygu",
    "rename_filter": "Ailenwi Hidlen",
    "restore_failed": "Nid oes gan fynediad nodwedd llawn eich cyfrif.",
    "restore_successful": "Mae pob eich pryniannau blaenorol bellach yn cael eu hadfer.",
    "review": "Yn mwynhau Golygydd Lluniau Polarr hyd yn hyn?",
    "review_no": "A fyddai ots gyda chi rhoi ychydig o adborth i ni?",
    "review_yes": "Beth am roi sgôr ar yr App Store?",
    "save_adjustments": "Gwnewch hidl addasu gallwch rannu.",
    "send_feedback": "Anfon Adborth",
    "signin_polarr_account": "Log i mewn i'ch cyfrif Polarr",
    "store": "Store",
    "subscribe": "Cofrestrwch am <strong>Premiwm Polarr</strong>",
    "subscribe_access": "ar gyfer mynediad llawn i:",
    "subscribe_cancel": "Gallwch \"Dad-danysgrifio\" mewn Lleoliadau anytime",
    "subscribe_caveat": "* Nodweddion Pro yn cynnwys addasiad graddiant, addasiad rheiddiol, masgio lliw, a brwsio lliw. Bydd eich aelodaeth yn berthnasol ar bob dyfais a llwyfannau eleni.",
    "subscribe_china": "Tanysgrifio i polaxiongkeji cyfrif WeChat ac yn gwneud taliad mewn WeChat, yna mewngofnodwch gyda'ch cyfrif Polarr at activate nodweddion pro.",
    "subscribe_error": "Wps! Roedd gwall wrth danysgrifio i Polarr. Ceisio eto?",
    "subscribe_features": "<li> Nodweddion Pro (4 offer addasiad lleol) </li><li> Mynediad i bob pecynnau hidlo 12 premiwm </li><li> Arhoswch diweddaru ar gyfer fformatau RAW </li><li> cynnwys newydd a ychwanegwyd misol! </li><li> aelodaeth Mynediad ar bob platfform * </li>",
    "subscribe_notify": "Rhoi gwybod i mi pryd y gall fy Aelodaeth Premiwm Polarr yn cael ei ddefnyddio ar fwy lwyfannau a dyfeisiau",
    "subscribe_price": "</price>",
    "subscribe_refund": "Rydych yn flaenorol prynu rhywbeth gyda Polarr a ydych yn gymwys i",
    "subscribe_success": "Diolch i chi am danysgrifio i Premiwm Polarr!",
    "subscribe_unlock_success": "Rydych yn awr yn Aelod Premiwm Polarr!",
    "subscription_expired": "Mae eich aelodaeth Premiwm Polarr wedi dod i ben",
    "text_tool": "offeryn Testun yn gadael i chi ychwanegu testun a siapiau i luniau. Gyda nodweddion fel llythyr a bylchiad llinell, aliniad, a lapio, mae gennych reolaeth lawn eich testun. Rydym wedi cynnwys hyd yn oed nodweddion uwch megis graddio fector deinamig a addasiad cymysgu unigryw.",
    "text_tool_description": "Ychwanegwch a golygu testun ar y sgrîn. Cylchdroi, newid lliw, gosod cymysgu, a hyd yn oed addasu bylchiad llythyr.",
    "thankyou": "Diolch!",
    "unlock_all": "datglo popeth",
    "unlock_all_note": "Login eich cyfrif pro Polarr neu roi allwedd trwydded.",
    "unlock_alternate": "Gallwch hefyd uwchraddio drwy fewngofnodi i gyfrif cyfredol, neu gan fewnosod allwedd trwydded yn",
    "unlock_failed": "Nid Nodweddion Pro ar gael ar gyfer y cyfrif hwn.",
    "unlock_success": "Yr ydych yn awr yn cael mynediad at yr holl nodweddion.",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> a <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> , y ffynhonnell orau ar gyfer ffotograffiaeth stoc am ddim a cydraniad uchel, wedi uno i ddod â chasgliad o 10 o hidlwyr premiwm a grëwyd gan aelodau o'r gymuned ffotograffydd Unsplash chi. </p>",
    "upgrade": "Uwchraddio i fersiwn llawn i ddatgloi holl nodweddion canlynol.",
    "upgrade_caveat": "* 12 pecynnau hidlo ychwanegol yn cynnwys dynwarediadau o Classic Ffilm, Hen Ffilm, is-goch Ffilm, Tones Croen a llawer o addasiadau eraill sy'n addas ar gyfer gwahanol bynciau a golygfeydd.",
    "upgrade_features": "<li> addasiadau graddiant </li><li> addasiadau Cylchlythyr </li><li> masgio lliw </li><li> addasiad brwsio </li><li> 12 pecynnau hidlo premiwm ychwanegol </li><li> allforio photo Swp </li>",
    "use_polarrfilter": "Defnyddiwch <strong>#polarrfilter</strong> i gael sylw!",
    "user_in_paid": "Gallwch fewngofnodi i mewn i Polarr ar lwyfannau eraill i gael mynediad at yr holl nodweddion.",
    "user_in_unpaid": "Os byddwch yn datgloi popeth, byddwch yn gallu mewngofnodi i mewn i Polarr ar lwyfannau eraill a mynediad i'r holl nodweddion.",
    "verification_sent": "Mae e-bost dilysu wedi ei anfon. <br/> Gwiriwch eich e-bost ar gyfer cyfarwyddiadau dilysu.",
    "verify_account": "I logio i mewn, os gwelwch yn dda gwirio eich cyfrif. <br/> Edrychwch ar eich e-bost ar gyfer cyfarwyddiadau dilysu.",
    "webgl_not_supported": "Biz juda Kechirasiz lekin Polarr ning grafik talabini qondirish emas qurilma, eng so'nggi operatsion tizimi yangilash va yana urinib ko'ring iltimos."
  },
  "labels": {
    "adjustments": "Addasiadau",
    "after": "Ar ôl",
    "alignment": "aliniad",
    "all": "Y cwbl",
    "amount": "Swm",
    "angle": "Ongl",
    "aqua": "Gwyrddlas Golau",
    "artwork": "Gwaith Celf",
    "auto": "awto",
    "balance": "Cydbwysedd",
    "basic": "Sylfaenol",
    "batch_rename": "ail-enwi swp",
    "batch_resize": "newid maint swp",
    "before": "cyn",
    "best": "Gorau",
    "blacks": "Duon",
    "blend": "Blend",
    "blend_mode": "Ddelw Blend",
    "blue": "Glas",
    "blur": "Blur",
    "brightness": "Disgleirdeb",
    "brush_flow": "Brush Llif",
    "brush_opacity": "Brush Didreiddiad",
    "brush_size": "Maint Brush",
    "clarity": "Eglurder",
    "collection": "Casglu",
    "color": "Lliw",
    "contrast": "Cyferbynnu",
    "crop": "Tocio",
    "crop_size": "dimensiynau cnydau",
    "curves": "Cromlin",
    "custom": "Personol",
    "dehaze": "Dehaze",
    "denoise": "Denoise",
    "detail": "Manylion",
    "diffuse": "Ymledu",
    "distortion": "Afluniad",
    "distortion_amount": "Afluniad",
    "distortion_horizontal": "Afluniad Llorweddol",
    "distortion_vertical": "Afluniad Fertigol",
    "dividers": "rhanwyr",
    "edit": "golygu",
    "effects": "Effeithiau",
    "email": "E-bost",
    "enable": "Galluogi",
    "estimated_size": "Amcangyfrif Maint",
    "exposure": "Dadleniad",
    "eyes": "llygaid",
    "face": "wyneb",
    "favorites": "ffefrynnau",
    "feather": "Pluen",
    "fit": "Fit",
    "font": "ffont",
    "format": "fformat",
    "free": "Am ddim",
    "frequent": "Mae&#39;r rhan fwyaf o ddefnyddir",
    "fringing": "Ymylon",
    "grain": "Graeanu",
    "green": "Gwyrdd",
    "guides": "Canllawiau",
    "high": "Uchel",
    "highlights": "Amlygu",
    "histogram": "Histogram",
    "history": "Hanes",
    "hsl": "HSL",
    "hue": "Arlliwliw",
    "hue_blue": "HSL - lliw glas",
    "hue_red": "HSL - Arlliw Coch",
    "invert": "Fflip",
    "lens": "Lens",
    "lens_distortion": "Ystumio lens",
    "letter_spacing": "llythyr Bylchu",
    "license_key": "Allwedd trwydded",
    "light": "Golau",
    "line_spacing": "Bylchu Line",
    "lips": "gwefusau",
    "look": "Hidlo",
    "looks": "Hidlau",
    "low": "Isel",
    "luminance": "Goleuder",
    "luminance_blue": "HSL - Goleuder y Glas",
    "luminance_red": "HSL - Goleuder y Coch",
    "magenta": "Magenta",
    "master": "Meistr",
    "max_size": "dimensiynau Max",
    "med": "Canolig",
    "mix": "Addasu'r Cwbl",
    "mosaic": "Picseleiddio",
    "name": "enw",
    "none": "Dim",
    "nose": "trwyn",
    "opacity": "Didreiddiad",
    "optics": "Opteg",
    "options": "Dewisiadau",
    "orange": "Oren",
    "original": "Gwreiddiol",
    "outline": "Amlinelliad",
    "password": "Cyfrinair",
    "position": "Safle",
    "prefix": "segment rhagddodiad",
    "presets": "presets",
    "preview": "Rhagolwg",
    "purple": "Porffor",
    "quality": "Ansawdd",
    "ratio": "Cymhareb",
    "red": "Coch",
    "reflect": "Myfyrio",
    "resolution": "Penderfyniad",
    "rgb": "RGB",
    "roundness": "Roundness",
    "saturation": "Dirlawnder",
    "saturation_blue": "HSL - Dirlawnder Glas",
    "saturation_red": "HSL - Dirlawnder Coch",
    "sayings": "Sayings",
    "shadow": "Cysgodol",
    "shadows": "Chysgod",
    "shape": "siâp",
    "shapes": "siapiau",
    "shapes_presets": "Siapiau & Presets",
    "sharpen": "Minio",
    "size": "Maint",
    "skin": "croen",
    "smoothness": "llyfnu",
    "split_tone": "Arlliwio",
    "teeth": "dannedd",
    "temperature": "Tymheredd",
    "text": "Testun",
    "texture": "gwead",
    "threshold": "trothwy",
    "tint": "Arlliw",
    "tone": "Tone",
    "toning": "Arlliwio",
    "use_prefix": "Defnyddiwch rhagddodiad",
    "use_watermark": "defnydd Watermark",
    "user_name": "Ffugenw",
    "vibrance": "Bywiog",
    "vignette": "Vignette",
    "vignette_amount": "Vignette",
    "watermark_image": "Watermark Delwedd",
    "wb": "WB",
    "whitening": "whitening",
    "whites": "Gwynion",
    "width": "lled",
    "yellow": "Melyn"
  },
  "menus": {
    "batch_export": "allforio swp",
    "change_language": "Newid Iaith",
    "copy_edit": "copi golygiadau",
    "edit_watermark": "golygu Watermark",
    "export": "Fformat, Resize Watermark",
    "filters_users": "Hidlau gan Polarrists",
    "guide": "canllaw",
    "image_quality": "ansawdd y ddelwedd",
    "image_settings": "lleoliadau allforio",
    "language": "iaith",
    "more_filters": "Mwy o Hidlau",
    "open": "agored",
    "open_in": "agor mewn",
    "paste_edit": "golygiadau Gludo",
    "remove": "Tynnu",
    "rename": "Ailenwi",
    "restore_purchases": "Adfer Prynu",
    "save_cameraroll": "Achub i Roll Camera",
    "save_copy": "Gadw copi",
    "settings": "gosodiadau",
    "share": "Share",
    "share_edits": "golygiadau Share",
    "share_filter": "Rhannu hidlo",
    "show_instructions": "Canllaw Dangos Addasiadau",
    "show_intro": "Dangos intro",
    "show_video_tutorial": "Gwylio Fideo Tutorial",
    "single_export": "allforio"
  },
  "onboarding": {
    "become_master": "Dod yn feistr golygu lluniau.",
    "step_0": "Croeso i Polarr! <br/> Dewch i ddechrau arni drwy edrych ar rhai hidlwyr.",
    "step_1": "Tap i ddewis hidlo. <br/> panel Swipe i weld mwy o hidlwyr.",
    "step_10": "gallwch lusgo'r i ail-drefnu pob eiconau offeryn.",
    "step_2": "tap <i class='icon-check'></i> i gymhwyso'r hidlo. <br/> Defnyddiwch y llithrydd i addasu dwysedd y hidlo.",
    "step_3": "Mae pob hidlydd yn gwbl eu golygu. <br/> Ewch i Addasiadau i weld eich golygiadau.",
    "step_4": "Gallwch newid addasiad drwy lusgo'r llithrydd.",
    "step_4a": "wasg hir yna llusgwch i addasu y llithrydd.",
    "step_5": "Gweler yr holl golygiadau eich bod wedi gwneud ym mhanel hanes.",
    "step_6": "Gallwch dadwneud yn golygu a mynd yn ôl i unrhyw bwynt mewn hanes.",
    "step_7": "Arbedwch eich delwedd yma. <br/> Gallwch newid maint, ychwanegu ddyfrnod neu arbed delweddau lluosog.",
    "step_7a": "Arbedwch eich delwedd yma. <br/> Gallwch hefyd rannu ac arbed delweddau lluosog.",
    "step_8": "Agorwch eich lluniau eich hun yma.",
    "step_9": "Gweld eich lluniau a agorwyd yn y panel lluniau."
  },
  "status": {
    "account_suspended": "Mae eich cyfrif yn cael ei ohirio o ganlyniad i weithgaredd amheus. Am fwy o wybodaeth, os gwelwch yn dda gwiriwch eich e-bost neu gyswllt <a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "tap <i class='icon-add-face'></i> i ychwanegu wyneb",
    "adjustment_copied": "addasiadau Anfonwyd copi",
    "adjustment_pasted": "addasiadau Pasted",
    "demo_mode": "Mae'r fersiwn hwn o'r Polarr Photo Golygydd wedi cael ei chreu yn benodol ar gyfer dibenion arddangos",
    "downloading_font": "Lawrlwytho ffont",
    "drag_icons": "Llusgwch eiconau i aildrefnu offer",
    "drop_files": "Gollwng ffeiliau i agor",
    "email_invalid": "Mae'n ddrwg gennym, mae'n edrych fel nad yw cyfeiriad e-bost yn ddilys.",
    "email_reset": "Mae cyswllt ailosod wedi cael ei anfon. <br/> Gwiriwch eich e-bost.",
    "exporting_filter": "arbed hidlo",
    "field_invalid": "Nid yn ddilys.",
    "file_exists": "Bod llun eisoes ar agor",
    "file_not_supported": "Mae'n ddrwg gennym, nid y math ffeil yn cael ei gefnogi",
    "filter_exported": "Mae eich hidlydd wedi ei gadw i gofrestr camera.",
    "filter_saved": "Filter arbed",
    "filters_sync_error": "hidlwyr syncing Gwall",
    "filters_synced": "Hidlau synced",
    "filters_syncing": "Syncing hidlwyr ...",
    "import_filter_error": "Mae'n ddrwg gennym, na ellid hidlydd yn cael ei fewnforio",
    "import_limit": "Gall Polarr yn unig agor i luniau 50, os gwelwch yn dda tynnu rhai lluniau cyn agor mwy.",
    "importing_filter": "mewnforio hidlo",
    "importing_photo": "photo agor",
    "importing_photos": "agor lluniau",
    "invalid_credentials": "e-bost anghywir neu gyfrinair",
    "loading_photo": "Wrthi'n llwytho'r llun",
    "login_success": "Rydych wedi cael eich mewngofnodi&#39;n llwyddiannus!",
    "no_faces": "Dim wynebau ganfod.",
    "no_user": "Nid yw defnyddiwr yn bodoli.",
    "offline": "Methu cysylltu â'r gweinydd.",
    "photo_load_error": "Methu llwytho llun",
    "photo_saved": "Cadwyd y llun",
    "photo_shared": "rhannu Photo",
    "photos_saved": "Lluniau achub",
    "purchase_required": "ofynnol prynu",
    "raw_failed": "Methu darllen delwedd RAW. cefnogaeth RAW yn dal yn arbrofol.",
    "registration_success": "Yr ydych wedi gwneud cyfrif newydd yn llwyddiannus! <br/> I logio i mewn ar ddyfeisiau eraill, rhaid i chi yn gyntaf wirio eich cyfrif. Rydyn ni wedi anfon e-bost atoch gyda chyfarwyddiadau dilysu.",
    "saving_photo": "arbed llun",
    "saving_photo_error": "Methu arbed llun",
    "saving_photo_error_sd": "Nid oes gennych ganiatâd i weld eich cerdyn SD neu storio mewnol Polarr. Gwiriwch y diweddariad system Android diweddaraf at atgyweiria &#39;r mater.",
    "saving_photos": "arbed lluniau",
    "server_offline": "Mae&#39;n ddrwg gennym, nid yw&#39;r gweinydd yn ymateb. <br/> A ydych yn cysylltu â&#39;r rhyngrwyd?",
    "sharing_photo": "rhannu llun",
    "sharing_photo_error": "Methu rhannu llun",
    "show_me_how": "Dangoswch i mi sut i ddefnyddio </thing>",
    "supported_files": "Polarr cefnogi JPG, PNG, BMP, a rhai ffeiliau RAW",
    "unlocks_remaining": "Mae gennych </unlocks> datgloi sy'n weddill",
    "user_exists": "Mae cyfrif gyda y neges e-bost neu ddefnyddiwr enw eisoes.",
    "wrong_password": "Cyfrinair anghywir"
  },
  "tooltips": {
    "add_face": "Ychwanegu Face",
    "add_text": "Ychwanegu testun",
    "adjustments": "addasiadau",
    "apply_adjustments": "Gwneud cais Addasiadau",
    "auto_adjust": "Addasiadau auto",
    "brush_filter": "Mwgwd Brush",
    "color_filter": "Mwgwd lliw",
    "crop": "cnydau",
    "crop_straighten": "Cnydau & Angle",
    "current_adjustments": "Addasiadau ar hyn o bryd",
    "double_tap_edit": "tap dwbl i olygu",
    "drag_watermark": "Llusgwch i symud dyfrnod",
    "eraser": "Rwber",
    "face": "Offer wyneb",
    "filmstrip": "Lluniau hagor",
    "flip": "Flip",
    "flip_h": "Flip yn llorweddol",
    "flip_v": "Flip fertigol",
    "global_adjustments": "prif Addasiadau",
    "gradient_filter": "mwgwd graddiant",
    "guides": "Canllawiau",
    "history": "Hanes golygu",
    "info": "Gwybodaeth",
    "local_adjustments": "Addasiadau Dewisol",
    "looks": "Hidlau",
    "masks": "mygydau",
    "move_face": "Wyneb symud",
    "open": "Lluniau agored",
    "pan": "Pan",
    "radial_filter": "mwgwd rheiddiol",
    "redo": "Ailwneud",
    "remove_face": "Tynnu Wyneb",
    "remove_text": "Tynnu Testun",
    "rotate": "Cylchdroi",
    "rotate_ccw": "Cylchdroi Gwrthglocwedd",
    "rotate_cw": "Cylchdroi Clocwedd",
    "save": "Save Photo",
    "settings": "Gosodiadau",
    "show_original": "Dangos Gwreiddiol",
    "text": "Offeryn Testun",
    "undo": "Dadwneud"
  },
  "tutorial": {
    "blacks": "Newid disgleirdeb y rhannau du y llun",
    "brightness": "Newid y disgleirdeb neu'r \"gamma\" y llun",
    "brush_mask_0": "Defnyddiwch eich bys i beintio gyda'r brwsh.",
    "brush_mask_1": "Mwgwd brwsh yn unig effeithio ar yr ardal brwsio.",
    "clarify": "egluro",
    "clarify_1": "<b>lleihau niwl</b> <br/> Niwl yn broblem gyffredin mewn lluniau ac mae'n hawdd i gael gwared yn Polarr. <br/> I gael gwared ar niwl, yn cynyddu i 85 <b>Dehaze</b>",
    "clarify_2": "<b>adfer amlygiad</b> <br/> Dehaze weithiau yn tywyllu y llun gormod. <br/> I adennill rhywfaint o oleuni, cynyddu <b>Dod</b> i <b>gysylltiad</b> â 30",
    "clarify_3": "<b>cynyddu cyferbyniad</b> <br/> Mae'r tes wedi mynd yn bennaf erbyn hyn, ond gallwn roi hwb i'r deinamig ysgafn ychydig yn fwy. Cynyddu <b>Cyferbyniad</b> i 60",
    "clarify_4": "<b>gwella manylion</b> <br/> Gwella'r manylion yn y llun, gallwn ddefnyddio'r offeryn Eglurder. Cynyddu <b>Eglurder</b> i 40",
    "clarify_5": "Gwaith da! Rydych chi wedi dysgu sut i wneud llun niwlog llawer gwell yn 4 cam syml.",
    "clarify_description": "Yn y tiwtorial hwn, byddwch yn dysgu sut i leihau niwl, diffinio amlinelliadau ac ychwanegu un byw at eich pwnc.",
    "clarity": "Cynyddu neu llyfnhau manylion yn y llun",
    "color_mask_0": "Mwgwd Lliw yn unig yn effeithio ar y lliw a ddewiswyd.",
    "color_mask_1": "Llusgwch i newid y lliw a ddewiswyd.",
    "color_mask_2": "<b>Trothwy</b> yn gosod sensitifrwydd y dewis.",
    "contrast": "Gwneud yr olygfa yn fwy  gweladwy.",
    "curves": "Newid y gromlin naws y llun",
    "deep_space": "Space Deep",
    "deep_space_1": "<b>adfer golau</b> <br/> addasiad amlygiad Polarr yn efelychu yr amlygiad o camera. <br/> I adennill golau, gosod <b>Dod</b> i <b>gysylltiad</b> â 100",
    "deep_space_2": "<b>gwella manylion</b> <br/> Gadewch i ni wneud y sêr yn edrych yn fwy trawiadol. <br/> Bydd ychwanegu eglurder helpu drwy ddatgelu mwy o fanylion. <br/> Er mwyn gwella fanylion, gosod <b>Eglurder</b> at 100",
    "deep_space_3": "<b>ychwanegu dyfnder</b> <br/> Er mwyn dod â mwy o ffocws i ganol y llun, gallwn ychwanegu vignette i tywyllu ymylon y llun. <br/> I ychwanegu dyfnder, gostwng <b>Vignette</b> i -100",
    "deep_space_4": "<b>Vignette Smooth</b> <br/> Ar hyn o bryd mae'r vignette yn edrych yn rhy sydyn. <br/> I wneud y vignette edrych yn llyfn ac yn gynnil, <br/> cynyddu <b>Feather</b> at 100",
    "deep_space_5": "<b>adfer uchafbwyntiau</b> <br/> Mae rhai o'r sêr yn ymyl y llun yn edrych i dywyll. Gallwn eu adennill drwy gynyddu <b>Uchafbwyntiau</b> at 100",
    "deep_space_6": "<b>Newid lliw</b> <br/> HSL yn eich galluogi i newid y nodweddion o liw penodol. I newid lliw'r awyr, cynyddu <b>Aqua Hue</b> at 100",
    "deep_space_7": "Gwaith da! Rydych chi wedi dysgu sut i wneud awyr serennog edrych yn drawiadol.",
    "deep_space_description": "Yn y tiwtorial hwn, byddwch yn dysgu ffyrdd o ychwanegu dyfnder, addasu lliw a newid y amlygiad o awyr y nos.",
    "dehaze": "Ychwanegu neu ddileu tes o'r llun",
    "denoise": "Tynnwch sŵn o'r llun",
    "diffuse": "Rhowch y llun yn \"glow\" effaith",
    "distortion": "ystumio",
    "distortion_1": "<b>afluniad cywir</b> <br/> Rydym yn aml yn gweld adeiladau a waliau yn edrych cam mewn lluniau. Mae hyn fel arfer yn cael ei achosi gan y dyluniad lensys camera ongl lydan. Gallwn yn hawdd atgyweiria hon yn Polarr gydag offer afluniad. <br/> I atgyweiria ystumio, yn cynyddu <b>Fertigol Ystumio</b> i 70",
    "distortion_2": "<b>vignette cywir</b> <br/> Yn ychwanegol at afluniadau optegol, y rhan fwyaf o lensys camera cael fallout golau ger eu ymylon. Gallwn leihau ymylon tywyllach hyn gan ddefnyddio'r teclyn vignette. <br/> I gywiro vignette, cynyddu <b>Vignette</b> i 20",
    "distortion_3": "<b>gwella manylion</b> <br/> Yn olaf, er mwyn gwella manylion yn y llun, gallwn ddefnyddio'r offeryn eglurder. <br/> Cynyddu <b>Eglurder</b> at 100",
    "distortion_4": "Gwaith da! Rydych chi wedi dysgu sut i gywiro diffygion lens cyffredin mewn 3 camau syml.",
    "distortion_amount": "Gwneud lluniau sy'n edrych fel rhai ongl eang neu lygad pysgodyn.",
    "distortion_description": "Yn y tiwtorial hwn, byddwch yn dysgu sut i gywiro gwyriadau lens a vignette mewn ergydion pensaernïol.",
    "distortion_horizontal": "Newid y afluniad ar hyd yr echelin lorweddol",
    "distortion_vertical": "Newid y afluniad ar hyd yr echelin fertigol",
    "exposure": "Newid disgleirdeb heb olchi allan y llun",
    "face_0": "Polarr yn awtomatig detects wynebau ac yn gwahanu nodweddion unigol.",
    "face_1": "Defnyddiwch y teclyn ystumio'r i addasu maint a lleoliad nodweddion wynebol.",
    "fringing": "Ychwanegu penysgafnder i'ch llun.",
    "gradient_mask_0": "mwgwd graddiant yn effeithio ar ardaloedd mewn llinell syth.",
    "gradient_mask_1": "Pinsiad i newid maint. <br/> Llusgwch i symud ac yn cylchdroi.",
    "gradient_mask_2": "<b>Gwrthdroi</b> switsys cyfeiriad graddiant.",
    "gradient_mask_3": "<b>Myfyrio</b> yn gwneud y graddiant yn rhedeg i'r ddau gyfeiriad.",
    "grain": "Ychwanegu effaith grawn ffilm",
    "highlights": "Newid disgleirdeb o uchafbwyntiau heb olchi allan y llun",
    "hsl": "Newid y lliw, dirlawnder, neu ysgafnder o liw penodol.",
    "hsl_header": "HSL",
    "hue_blue": "Symud y lliw glas i edrych fel y lliwiau eraill.",
    "hue_red": "Symud y lliw coch i edrych fel y lliwiau eraill.",
    "luminance_blue": "Newid disgleirdeb y glas.",
    "luminance_red": "Newid disgleirdeb y coch.",
    "mastering_adjustments": "meistroli Addasiadau",
    "mosaic": "Rhowch y llun yn cael effaith mosaig",
    "radial_mask_0": "Mwgwd rheiddiol yn unig yn effeithio ardal crwn.",
    "radial_mask_1": "Pinsiad i newid maint. <br/> Llusgwch i symud ac yn cylchdroi.",
    "radial_mask_2": "<b>Feather</b> yn newid yr aneglurder y mwgwd.",
    "radial_mask_3": "<b>Gwrthdroi</b> switsys yr ardal yr effeithiwyd arni.",
    "saturation": "Gwneud eich llun yn fwy lliwgar.",
    "saturation_blue": "Newid ystent lliw'r glas.",
    "saturation_red": "Newid ystent lliw'r coch.",
    "shadows": "Newid disgleirdeb y cysgodion heb olchi allan y llun",
    "sharpen": "Gwneud i'ch llun edrych yn fwy ffres.",
    "temperature": "Gwnewch y llun cynhesach neu oerach",
    "tint": "Lliwio'ch llun yn ôl amser y diwrnod.",
    "toning_highlights": "Newid lliw uchafbwyntiau",
    "toning_shadows": "Newid lliw y cysgodion",
    "vibrance": "Newid y dirlawnder heb olchi allan arlliwiau croen",
    "vignette_amount": "Pylu neu yn tynnu sylw at ymylon y llun",
    "vignette_feather": "Newid yr aneglurder y vignette",
    "vignette_highlights": "Gadewch yr uchafbwyntiau yn dangos drwy'r vignette",
    "vignette_roundness": "Gwneud y vignette mwy crwn neu sgwâr",
    "vignette_size": "Newid maint y vignette",
    "white_balance": "Balans Gwyn",
    "white_balance_1": "<b>Cydbwysedd gwyn Cywir</b> <br/> Nid yw'r rhan fwyaf ffynonellau golau (bylbiau golau, flashlights, ac ati) yn allyrru lliw gwyn pur. Gall hyn achosi gwyn yn y lluniau i gael cast melyn neu las. I gywiro hyn, gostwng <b>Tymheredd</b> i -30",
    "white_balance_2": "<b>Adenillwch golau o cysgodion</b> <br/> Weithiau rhannau o llun yn dywyllach nag a fwriadwyd oherwydd y gall ein llygaid yn gweld mwy o olau na'r camera. Gallwn adennill y golau o cysgodion ddefnyddio'r offeryn cysgodion. <br/> Cynyddu <b>Cysgodion</b> at 100",
    "white_balance_3": "<b>gwella manylion</b> <br/> Yn olaf, er mwyn gwella manylion yn y llun, gallwn ddefnyddio'r offeryn eglurder. <br/> Cynyddu <b>Eglurder</b> at 100",
    "white_balance_4": "Gwaith da! Rydych chi wedi dysgu sut i gael gwared ar cast melyn ac adennill golau o cysgodion mewn 3 cam syml.",
    "white_balance_description": "Yn y tiwtorial hwn, byddwch yn dysgu y cysyniad o gydbwysedd gwyn a sut i gywiro i mewn drws golau artiffisial.",
    "whites": "Newidiadau disgleirdeb y rhannau gwyn y llun"
  }
};
},{}],322:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "Hinzufügen",
    "add_image": "Bild auswählen",
    "add_to_favorites": "Zu Favoriten hinzufügen",
    "aspect": "Aspekt",
    "back": "Zurück",
    "buy": "Kaufen",
    "buy_full_version": "Vollversion kaufen",
    "cancel": "Abbrechen",
    "check_again": "Nochmal Überprüfen",
    "choose_folder": "Wähle einen Ordner",
    "clear": "Löschen",
    "clear_history": "Verlauf löschen",
    "close": "Schließen",
    "cloud": "Cloud",
    "continue_anyway": "Trotzdem fortfahren",
    "current_image": "Aktuells Bild",
    "delete": "Löschen",
    "device": "Gerät",
    "dismiss": "Entlassen",
    "done": "Fertig",
    "dont_ask": "Nicht mehr fragen",
    "duplicate": "Duplikat",
    "enter_license": "Lizenzschlüssel eingeben",
    "enter_license_key": "Lizenzschlüssel eingeben",
    "exit": "Ausgang",
    "export": "Export",
    "export_filter": "Speichern als QR-Code",
    "forgot_password": "Passwort vergessen?",
    "free_membership": "Kostenlose Mitgliedschaft",
    "get": "Bekommen",
    "get_license": "Erhalten Sie Lizenzschlüssel",
    "get_started": "Sich aufmachen",
    "got_it": "Ich habs",
    "have_account": "Hast du schon ein Konto?",
    "import": "Import",
    "import_filter": "Importfilter",
    "import_filter_photos": "Import von Fotos",
    "import_photos": "Öffnen von Fotos",
    "info": "Info",
    "installed": "Installiert",
    "invite": "Link senden",
    "learn_more": "Mehr erfahren",
    "load_last_photo": "Laden letzte Foto auf Start",
    "new": "Neu",
    "new_filter": "Filter erstellen",
    "next": "Weiter",
    "no": "Nein",
    "no_thanks": "Nein Danke",
    "not_really": "Nicht wirklich",
    "not_this_time": "Nicht jetzt",
    "open_sample": "Beispielfoto öffnen",
    "paste_adjustments": "Paste Anpassungen",
    "pay_wechat": "Offene WeChat",
    "prev": "Vorherige",
    "preview": "Vorschau",
    "pro": "Pro",
    "purchased": "Gekauft",
    "remove": "Entfernen",
    "remove_all_photos": "Entfernen Sie alle Fotos",
    "remove_photo": "Foto entfernen",
    "remove_selected_photo": "Entfernen Sie das ausgewählte Foto",
    "reset": "Zurücksetzen",
    "reset_all": "Alle Zurücksetzen",
    "reset_password": "Passwort zurücksetzen",
    "reset_photos": "Zurücksetzen gespeichert und bearbeitet Etiketten",
    "reset_tips": "Tipps zurücksetzen",
    "restore": "Wiederherstellen",
    "revert": "Zurück zum Original",
    "sample": "Beispiel",
    "save": "Speichern",
    "save_qr": "Speichern QR Code",
    "save_share": "Speichern + Teilen",
    "scan_filter": "Scan-Filter",
    "select_all": "Alle wählen",
    "select_changed": "Auswahl bearbeiten",
    "send": "Senden",
    "send_verification": "Bestätigungsmail erneut senden",
    "show_in_folder": "In Ordner anzeigen",
    "show_me": "Zeig es mir",
    "signin": "Einloggen",
    "signout": "Ausloggen",
    "signup": "Erstellen Polarr Konto",
    "skip": "Überspringen",
    "submit_license_key": "Senden Lizenzschlüssel",
    "subscribe": "Abonnieren",
    "subscribe_monthly": "Abonnieren Monats",
    "subscribe_yearly": "Abonnieren Jahres",
    "sure": "Ok",
    "try": "Versuchen",
    "try_again": "Erneut versuchen",
    "unlock": "Freischalten",
    "unsubscribe": "Abmelden",
    "update": "Update",
    "whats_new": "Was gibt's Neues",
    "yes": "Ja"
  },
  "dialogs": {
    "about": "Über",
    "about_text": "Wir sind der Meinung, dass es wichtig ist, Künstlern die Möglichkeit zu geben, sich einfach und mühelos bezüglich deren Perspektiven und ohne Verlust von Inspirationen ausdrücken zu können.",
    "account": "Konto",
    "all_filters": "Alle Filter",
    "all_filters_text": "Schalte Polarr die gesamte Sammlung von Premium-Filter",
    "backup_filters": "Synchronisieren und sichern Sie alle Ihre benutzerdefinierte Filter, damit Sie sie auf unterschiedlichen Geräten und Plattformen nutzen können. Diese Funktion erfordert, dass Sie mit einem Polarr Konto einloggen.",
    "backup_filters_title": "Backup-Benutzerdefinierte Filter",
    "brush_limit": "Die aktuelle Pinsel Maske Grenze </limit>",
    "denoise": "Unsere Echtzeit-denoise Prozess macht es einfach, Lärm und Korn in den Fotos zu entfernen. Getrennt steuern Farb- und Helligkeitsrauschen Komponenten.",
    "featured": "Hervorgehoben",
    "filter_collections": "Filter Sammlungen",
    "filter_name": "Nennen Sie diesen Filter",
    "get_picky": "Müde von Fotobearbeitung? Probieren Sie Polarr die neue App, die Fotos mit Artificial Intelligence bearbeitet!",
    "get_updates": "Updates abonnieren",
    "get_updates_about": "Sei der Erste, neue Funktionen auszuprobieren! <br/> Fügen Sie E-Mail-Adresse und wir werden Sie über neue Funktionen und Produkte von Polarr informieren.",
    "get_updates_thankyou": "Sie werden nun erhalten Updates über neue Funktionen und Produkte von Polarr! <br/> Wenn Sie wünschen, empfangen Updates zu stoppen, können Sie \"Abmelden\" aus Ihrer E-Mail zu jeder Zeit.",
    "help": "Hilfe",
    "invite": "Installationslink teilen. Pro Funktionen werden freigeschaltet, nachdem drei Personen Polarr mit Ihrem Link installiert haben.",
    "local_adjustments": "Lokale Anpassungen können Sie Polarr leistungsstarke Reihe von Anpassungen an gezielten Bereichen verwenden. Inklusive Radial, Gradient, Farbe und Pinsel Maskierung Werkzeuge.",
    "lock_rotation": "Lock-Rotation",
    "max_export_size": "Max Exportgröße",
    "new_filter": "Änderungen speichern als Filter",
    "no_purchases": "Es sind keine Käufe wiederherzustellen.",
    "offline": "Ihr Gerät ist nicht mit dem Internet verbunden. Bitte versuchen Sie es später erneut.",
    "photos": "Fotos",
    "polarr_premium": "Polarr Premium-",
    "pro_all_filters": "Pro-Funktionen <br/> + <br/> Alle Filter",
    "pro_all_filters_text": "Schalte alle Funktionen, einschließlich Pro Tools und alle Polarr Premium-Filter. Verwenden Sie Ihre Polarr-Konto Zugriff auf alle Funktionen auf anderen Plattformen. Sichern und Ihre benutzerdefinierte Filter synchronisieren.",
    "pro_features": "Pro tools",
    "pro_features_all": "Alle Pro Tools",
    "purchase": "Schalte Radial, Gradient, Farbe, Pinsel, Entrauschen und Text-Tools. Sichern und Ihre benutzerdefinierte Filter synchronisieren.",
    "purchase_failed": "Kauf abgebrochen",
    "purchase_header": "Alle Pro-Funktionen freischalten",
    "purchase_successful": "Sie haben nun Zugriff auf die Pro Version.",
    "purchase_unavailable": "Käufe sind aktuell nicht verfügbar. Sorry.",
    "purchases": "Käufe",
    "rate_review": "Bewerten und Kommentieren",
    "rename_filter": "Umbenennen Filter",
    "restore_failed": "Ihr Konto hat nicht die volle Funktion Zugang.",
    "restore_successful": "Alle Ihre früheren Einkäufe sind jetzt wiederhergestellt.",
    "review": "Gefällt Ihnen Polarr?",
    "review_no": "Würden Sie uns gerne Feedback senden?",
    "review_yes": "Wie sieht es in diesem Fall mit einer Bewertung aus?",
    "save_adjustments": "Erstellen Sie eine benutzerdefinierte Filter, den Sie teilen können.",
    "send_feedback": "Feedback Senden",
    "signin_polarr_account": "Melden Sie sich bei Ihrem Konto Polarr",
    "store": "Geschäft",
    "subscribe": "Melden Sie sich für <strong>Polarr Premium</strong> -",
    "subscribe_access": "für vollen Zugriff auf:",
    "subscribe_cancel": "Sie können \"Abmelden\" in den Einstellungen jederzeit",
    "subscribe_caveat": "* Pro-Funktionen umfassen Gradienten Einstellung, radiale Einstellung, Farbmaskierung und Farbe Bürsten. Ihre Mitgliedschaft wird in diesem Jahr auf allen Geräten und Plattformen gelten.",
    "subscribe_china": "Abonnieren WeChat Konto polaxiongkeji und eine Zahlung in WeChat machen, dann melden Sie sich mit Ihrem Konto Polarr Pro-Features zu aktivieren.",
    "subscribe_error": "Hoppla! Es gab Fehler zu Polarr abonnieren möchte. Versuch es noch einmal?",
    "subscribe_features": "<li> Pro-Funktionen (4 lokale Anpassungswerkzeuge) </li><li> Zugriff auf alle 12 Premium-Filter-Packs </li><li> Bleiben Sie für RAW-Formate aktualisiert </li><li> Neue Inhalte hinzugefügt monatlich! </li><li> Zugang Mitgliedschaft auf allen Plattformen * </li>",
    "subscribe_notify": "Benachrichtigen Sie mich, wenn meine Polarr Premium-Mitgliedschaft kann auf mehr Plattformen und Geräte genutzt werden",
    "subscribe_price": "</price>",
    "subscribe_refund": "Sie gekauft vorher etwas mit Polarr und Sie sind für",
    "subscribe_success": "Vielen Dank für Ihre Anmeldung zu Polarr Premium-!",
    "subscribe_unlock_success": "Du bist jetzt ein Polarr Premium-Mitglied!",
    "subscription_expired": "Ihre Polarr Premium-Mitgliedschaft ist abgelaufen",
    "text_tool": "Text-Werkzeug können Sie Text und Formen zu Fotos hinzufügen. Mit Funktionen wie Buchstaben und Zeilenabstand, Ausrichtung und Verpackung, haben Sie die volle Kontrolle Ihren Text. Wir haben auch erweiterte Funktionen wie dynamische Vektor-Skalierung und eine einzigartige Mischung Anpassung einbezogen werden.",
    "text_tool_description": "Hinzufügen und Bearbeiten von Text auf dem Bildschirm. Drehen, die Farbe ändern, setzen Sie Blending und sogar Buchstabenabstand einzustellen.",
    "thankyou": "Vielen Dank!",
    "unlock_all": "Alles freischalten",
    "unlock_all_note": "Einloggen Ihr Polarr pro Konto oder Lizenzschlüssel eingeben.",
    "unlock_alternate": "Sie können auch, indem Sie sich auf ein bestehendes Konto aktualisieren oder durch Eingabe eines Lizenzschlüssels in",
    "unlock_failed": "Pro Funktionen sind für dieses Konto nicht zur Verfügung.",
    "unlock_success": "Sie haben nun Zugriff auf alle Funktionen.",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> und <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> , die beste Quelle für freie und hochauflösende stock photography, haben sich zusammengetan , um Ihnen eine Sammlung von 10 Premium - Filter von den Mitgliedern der Unsplash Fotografen Community gestaltet zu bringen. </p>",
    "upgrade": "Upgrade auf die Vollversion alle folgenden Funktionen freizuschalten.",
    "upgrade_caveat": "* 12 zusätzliche Filterpackungen sind Simulationen der klassischen Film, Vintage Film, Infrarot-Film, Hauttöne und viele andere Anpassungen für verschiedene Themen und Szenen.",
    "upgrade_features": "<li> Gradient Anpassungen </li><li> Rund Anpassungen </li><li> Farbmaskierung </li><li> Bürsten Einstellung </li><li> 12 weitere Premium-Filter-Packs </li><li> Batch Foto Export </li>",
    "use_polarrfilter": "Verwenden Sie <strong>#polarrfilter</strong> , um aufzufallen!",
    "user_in_paid": "Sie können auf anderen Plattformen in Polarr einloggen, um alle Funktionen zugreifen.",
    "user_in_unpaid": "Wenn Sie alles zu entsperren, werden Sie in der Lage sein, in Polarr auf anderen Plattformen zu protokollieren und alle Funktionen zugreifen.",
    "verification_sent": "Eine Überprüfung E-Mail wurde gesendet. <br/> Bitte überprüfen Sie Ihre E-Mail zur Bestätigung Anweisungen.",
    "verify_account": "Um sich anzumelden, überprüfen Sie bitte Ihr Konto. <br/> Überprüfen Sie Ihre E-Mail zur Bestätigung Anweisungen.",
    "webgl_not_supported": "Es tut uns leid, aber dieses Gerät entspricht nicht den Anforderungen für Polarr, bitte aktualisieren Sie das Betriebssystem und versuchen es erneut."
  },
  "labels": {
    "adjustments": "Anpassungen",
    "after": "Nachher",
    "alignment": "Ausrichtung",
    "all": "Alle",
    "amount": "Menge",
    "angle": "Winkel",
    "aqua": "Aqua",
    "artwork": "Kunstwerk",
    "auto": "Auto",
    "balance": "Balance",
    "basic": "Basic",
    "batch_rename": "Batch rename",
    "batch_resize": "Batch-resize",
    "before": "Vorher",
    "best": "Beste",
    "blacks": "Schwarz",
    "blend": "Mischung",
    "blend_mode": "Mischmodus",
    "blue": "Blau",
    "blur": "Verwischen",
    "brightness": "Helligkeit",
    "brush_flow": "Pinsel Fluss",
    "brush_opacity": "Pinsel Opazität",
    "brush_size": "Pinselgröße",
    "clarity": "Schärfe",
    "collection": "Kollektion",
    "color": "Farbe",
    "contrast": "Kontrast",
    "crop": "Zuschneiden",
    "crop_size": "Crop Dimensionen",
    "curves": "Kurven",
    "custom": "Angepasst",
    "dehaze": "Dehaze",
    "denoise": "Denoise",
    "detail": "Detail",
    "diffuse": "Zerstreuen",
    "distortion": "Biegen",
    "distortion_amount": "Biegen",
    "distortion_horizontal": "Horizontale Beigen",
    "distortion_vertical": "Vertikale Biegen",
    "dividers": "Zirkel",
    "edit": "Bearbeiten",
    "effects": "Effekte",
    "email": "Email",
    "enable": "Aktivieren",
    "estimated_size": "Geschätzte Größe",
    "exposure": "Belichtung",
    "eyes": "Augen",
    "face": "Gesicht",
    "favorites": "Favoriten",
    "feather": "Erweichen",
    "fit": "Passen",
    "font": "Schriftart",
    "format": "Format",
    "free": "Frei",
    "frequent": "Am meisten verwendet",
    "fringing": "Farbsäume",
    "grain": "Körnung",
    "green": "Grün",
    "guides": "Guides",
    "high": "Hoch",
    "highlights": "Highlights",
    "histogram": "Histogramm",
    "history": "Verlauf",
    "hsl": "HSL",
    "hue": "Farbton",
    "hue_blue": "HSL – Blauer Farbton",
    "hue_red": "HSL – Roter Farbton",
    "invert": "Umkehren",
    "lens": "Objektiv",
    "lens_distortion": "Linsenverzerrung",
    "letter_spacing": "Buchstaben-Abstand",
    "license_key": "Lizenzschlüssel",
    "light": "Licht",
    "line_spacing": "Zeilenabstand",
    "lips": "Lippen",
    "look": "Filter",
    "looks": "Filter",
    "low": "Niedrig",
    "luminance": "Luminanz",
    "luminance_blue": "HSL – Leuchtdichte Blau",
    "luminance_red": "HSL –Leuchtdichte Rot",
    "magenta": "Magenta",
    "master": "Master",
    "max_size": "Max Dimensionen",
    "med": "Mittel",
    "mix": "Alle anpassen",
    "mosaic": "Pixelate",
    "name": "Name",
    "none": "Keiner",
    "nose": "Nase",
    "opacity": "Opazität",
    "optics": "Optik",
    "options": "Optionen",
    "orange": "Orange",
    "original": "Original",
    "outline": "Skizzieren",
    "password": "Passwort",
    "position": "Position",
    "prefix": "Präfix Segment",
    "presets": "Presets",
    "preview": "Vorschau",
    "purple": "Lila",
    "quality": "Qualität",
    "ratio": "Verhältnis",
    "red": "Rot",
    "reflect": "Reflektieren",
    "resolution": "Lösung",
    "rgb": "RGB",
    "roundness": "Rundung",
    "saturation": "Sättigung",
    "saturation_blue": "HSL – Blau-Sättigung",
    "saturation_red": "HSL – Rot-Sättigung",
    "sayings": "Sprüche",
    "shadow": "Schatten",
    "shadows": "Schatten",
    "shape": "Gestalten",
    "shapes": "Shapes",
    "shapes_presets": "Formen und Vorgaben",
    "sharpen": "Schärfen",
    "size": "Größe",
    "skin": "Haut",
    "smoothness": "Glättung",
    "split_tone": "Toning",
    "teeth": "Zähne",
    "temperature": "Temperatur",
    "text": "Text",
    "texture": "Textur",
    "threshold": "Schwelle",
    "tint": "Farbton",
    "tone": "Ton",
    "toning": "Toning",
    "use_prefix": "Verwenden Präfix",
    "use_watermark": "Wasserzeichen benutzen",
    "user_name": "Benutzername",
    "vibrance": "Lebendigkeit",
    "vignette": "Vignette",
    "vignette_amount": "Vignette",
    "watermark_image": "Wasserzeichen Bild",
    "wb": "Weißabgleich",
    "whitening": "Whitening",
    "whites": "Weiß",
    "width": "Breite",
    "yellow": "Gelb"
  },
  "menus": {
    "batch_export": "Batch-Export",
    "change_language": "Sprache ändern",
    "copy_edit": "Kopieren Edits",
    "edit_watermark": "Wasserzeichen bearbeiten",
    "export": "Format, Größe ändern, Wasserzeichen",
    "filters_users": "Filter von Polarrists",
    "guide": "Guide",
    "image_quality": "Bildqualität",
    "image_settings": "Exporteinstellungen",
    "language": "Sprache",
    "more_filters": "Mehr Filter",
    "open": "Öffnen",
    "open_in": "Öffnen in",
    "paste_edit": "Änderungen einfügen",
    "remove": "Entfernen",
    "rename": "Umbenennen",
    "restore_purchases": "Käufe wiederherstellen",
    "save_cameraroll": "In der Galerie speichern",
    "save_copy": "Kopie speichern",
    "settings": "Einstellungen",
    "share": "Teilen",
    "share_edits": "Teilen Bearbeitungen",
    "share_filter": "Teilen Filter",
    "show_instructions": "Anpassungsanleitung",
    "show_intro": "Einführung zeigen",
    "show_video_tutorial": "Videoanleitung",
    "single_export": "Export"
  },
  "onboarding": {
    "become_master": "Werden Sie ein Foto-Bearbeitungs-Master.",
    "step_0": "Willkommen bei Polarr! <br/> Ermöglicht durch Betrachten einige Filter zu beginnen.",
    "step_1": "Tippen Sie auf einen Filter auswählen. <br/> Swipe-Panel mehrere Filter zu sehen.",
    "step_10": "Sie können per Drag alle Werkzeug Symbole neu zu ordnen.",
    "step_2": "Tippen <i class='icon-check'></i> um den Filter anzuwenden. <br/> Verwenden Sie den Regler, um die Intensität des Filters einzustellen.",
    "step_3": "Jeder Filter ist vollständig editierbar. <br/> Zum Anpassungen, um die Änderungen zu sehen.",
    "step_4": "Sie können durch Ziehen der Schieber eine Einstellung ändern.",
    "step_4a": "Lange drücken dann ziehen Sie den Regler einstellen.",
    "step_5": "Alle Änderungen, die Sie in der Geschichte Bedienfeld vorgenommen haben.",
    "step_6": "Sie können bearbeitet rückgängig machen und zu einem beliebigen Punkt in der Geschichte zurückgehen.",
    "step_7": "Speichern Sie Ihr Bild hier. <br/> Sie können die Größe, Wasserzeichen hinzufügen oder mehrere Bilder zu speichern.",
    "step_7a": "Speichern Sie Ihr Bild hier. <br/> Sie können auch mehrere Bilder gemeinsam nutzen und speichern.",
    "step_8": "Öffnen Sie Ihre eigenen Fotos hier.",
    "step_9": "Sehen Sie Ihre geöffneten Fotos in der Fototafel."
  },
  "status": {
    "account_suspended": "Ihr Konto ist aufgrund verdächtiger Aktivitäten auf Eis gelegt. Für mehr Informationen, überprüfen Sie bitte Ihre E - Mail oder kontaktieren Sie <a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "Tippen <i class='icon-add-face'></i> ein Gesicht hinzufügen",
    "adjustment_copied": "Anpassungen Kopierte",
    "adjustment_pasted": "Anpassungen Pasted",
    "demo_mode": "Diese Version von Polarr Photo Editor wurde speziell für Demonstrationszwecke erstellt",
    "downloading_font": "Download Schrift",
    "drag_icons": "Ziehen Sie Symbole neu zu ordnen Werkzeuge",
    "drop_files": "Drop-Dateien zu öffnen,",
    "email_invalid": "Sorry, es sieht aus wie die E-Mail-Adresse ist nicht gültig.",
    "email_reset": "Ein Link zum Zurücksetzen wurde gesendet. <br/> Bitte überprüfen Sie Ihre E-Mail.",
    "exporting_filter": "Speichern Filter",
    "field_invalid": "Ist nicht gültig.",
    "file_exists": "Das Foto ist bereits geöffnet",
    "file_not_supported": "Sorry, dass Dateityp wird nicht unterstützt",
    "filter_exported": "Ihre Filter wurde Kamerarolle gespeichert.",
    "filter_saved": "Filter gespeichert",
    "filters_sync_error": "Fehler bei der Synchronisierung Filter",
    "filters_synced": "Filter synchronisiert",
    "filters_syncing": "Syncing Filter ...",
    "import_filter_error": "Es tut uns Leid, dass Filter konnte nicht importiert werden",
    "import_limit": "Polarr kann nur zu 50 Fotos öffnen, entfernen Sie bitte ein paar Fotos vor mehr öffnen.",
    "importing_filter": "Importieren von Filter",
    "importing_photo": "Öffnungs Foto",
    "importing_photos": "Öffnungs Fotos",
    "invalid_credentials": "falsche Email oder Passwort",
    "loading_photo": "Lade Foto",
    "login_success": "Sie haben sich erfolgreich angemeldet worden!",
    "no_faces": "Keine Gesichter erkannt.",
    "no_user": "Dieser Benutzer existiert nicht.",
    "offline": "Server konnte nicht kontaktiert werden.",
    "photo_load_error": "Konnte Bild nicht laden",
    "photo_saved": "Foto gespeichert",
    "photo_shared": "Bild geteilt",
    "photos_saved": "Fotos gespeichert",
    "purchase_required": "Kauf erforderlich",
    "raw_failed": "Können Sie RAW-Bild zu lesen. RAW-Unterstützung ist noch experimentell.",
    "registration_success": "Sie haben gemacht erfolgreich ein neues Konto! <br/> So melden Sie sich auf anderen Geräten, müssen Sie zunächst Ihr Konto zu überprüfen. Wir haben Ihnen eine E-Mail mit der Überprüfung Anweisungen gesendet.",
    "saving_photo": "Speichern Foto",
    "saving_photo_error": "Fehler beim Speichern :(",
    "saving_photo_error_sd": "Polarr hat keinen Zugriff auf die SD Karte oder den internen Speicher. Bitte aktualisieren Sie auf das neueste Android Update um dieses Problem zu beheben.",
    "saving_photos": "Speichern von Fotos",
    "server_offline": "Sorry, der Server reagiert nicht. <br/> Sind Sie mit dem Internet verbunden?",
    "sharing_photo": "teilen Foto",
    "sharing_photo_error": "Fehler beim Teilen :(",
    "show_me_how": "Zeig mir, wie zu verwenden, </thing>",
    "supported_files": "Polarr unterstützt JPG, PNG, BMP, und bestimmte RAW-Dateien",
    "unlocks_remaining": "Du hast </unlocks> Rest entriegelt",
    "user_exists": "Es ist bereits ein Konto mit dieser E-Mail oder Benutzername.",
    "wrong_password": "Falsches Passwort"
  },
  "tooltips": {
    "add_face": "In Gesicht",
    "add_text": "Text hinzufügen",
    "adjustments": "Anpassungen",
    "apply_adjustments": "Anpassungen anwenden",
    "auto_adjust": "Auto Anpassungen",
    "brush_filter": "Pinsel Maske",
    "color_filter": "Farbmaske",
    "crop": "Schneiden",
    "crop_straighten": "Crop & Winkel",
    "current_adjustments": "Aktuelle Anpassungen",
    "double_tap_edit": "Tippen Sie zweimal bearbeiten",
    "drag_watermark": "Ziehen Sie nach Wasserzeichen zu bewegen",
    "eraser": "Radiergummi",
    "face": "Gesichts-Werkzeuge",
    "filmstrip": "Eröffnet Fotos",
    "flip": "Spiegeln",
    "flip_h": "Horizontal spiegeln",
    "flip_v": "Vertikal spiegeln",
    "global_adjustments": "Globale Anpassungen",
    "gradient_filter": "Verlaufsmaske",
    "guides": "Guides",
    "history": "Bearbeiten Geschichte",
    "info": "Info",
    "local_adjustments": "Lokale Anpassungen",
    "looks": "Filter",
    "masks": "Masken",
    "move_face": "Fläche verschieben",
    "open": "Öffnen Sie Fotos",
    "pan": "Pfanne",
    "radial_filter": "Radialfilter",
    "redo": "Wiederholen",
    "remove_face": "entfernen Sie Gesicht",
    "remove_text": "entfernen von Text",
    "rotate": "Drehen",
    "rotate_ccw": "Im Gegenuhrzeigersinn drehen",
    "rotate_cw": "Im Uhrzeigersinn drehen",
    "save": "Foto speichern",
    "settings": "Einstellungen",
    "show_original": "Original zeigen",
    "text": "Text-Werkzeug",
    "undo": "Rückgängig"
  },
  "tutorial": {
    "blacks": "Ändern Sie die Helligkeit der schwarzen Teile des Fotos",
    "brightness": "Ändern Sie die Helligkeit oder \"Gamma\" des Fotos",
    "brush_mask_0": "Benutzen Sie Ihre Finger mit dem Pinsel zu malen.",
    "brush_mask_1": "Pinsel Maske wirkt sich nur auf den Bereich gebürstet.",
    "clarify": "Klären",
    "clarify_1": "<b>Reduzieren</b> Sie <b>Nebel</b> <br/> Nebel ist ein häufiges Problem in Fotos und es ist leicht in Polarr zu entfernen. <br/> Zum Entfernen von Nebel, erhöhen <b>DEHAZE</b> bis 85",
    "clarify_2": "<b>Recover Exposition</b> <br/> DEHAZE dunkelt manchmal das Foto zu viel. <br/> Um etwas Licht zu erholen, erhöhen Sie die <b>Belichtung</b> auf 30",
    "clarify_3": "<b>Kontrast erhöhen</b> <br/> Der Dunst ist vor allem jetzt weg, aber wir können das Licht dynamisch ein wenig mehr zu steigern. <b>Kontrast</b> erhöhen bis 60",
    "clarify_4": "<b>Verbessern</b> Sie <b>Details</b> <br/> Um die Details auf dem Foto zu verbessern, können wir das Clarity-Tool verwenden. Erhöhen Sie <b>Klarheit</b> bis 40",
    "clarify_5": "Gute Arbeit! Sie haben gelernt, wie man ein trübes Bild in 4 einfachen Schritten viel besser zu machen.",
    "clarify_description": "In diesem Tutorial erfahren Sie, wie Nebel zu reduzieren, Konturen definieren und Anschaulichkeit zu Ihrem Thema hinzufügen.",
    "clarity": "Erhöhen oder glätten Details auf dem Foto",
    "color_mask_0": "Farbmaske wirkt sich nur auf die ausgewählte Farbe.",
    "color_mask_1": "Ziehen Sie, um die ausgewählte Farbe.",
    "color_mask_2": "<b>Schwelle</b> wird die Empfindlichkeit der Auswahl.",
    "contrast": "Machen Sie das Objekt besser erkennbar.",
    "curves": "Ändern Sie die Tonwertkurve des Fotos",
    "deep_space": "Weltraum",
    "deep_space_1": "<b>Recover Licht</b> <br/> Polarr die Belichtungseinstellung simuliert die Belichtung einer Kamera. <br/> Zur Wiederherstellung Licht, Einstellung <b>Belichtung</b> auf 100",
    "deep_space_2": "<b>Verbessern</b> Sie <b>Details</b> <br/> Lasst uns die Sterne mehr beeindruckend aussehen. <br/> Hinzufügen Klarheit wird dazu beitragen, um mehr Details verraten. <br/> Zur Verbesserung der Details, Set <b>Clarity</b> 100",
    "deep_space_3": "<b>In</b> der <b>Tiefe</b> <br/> Um mehr Fokus auf die Mitte des Bildes zu bringen, können wir Vignette fügen Sie die Ränder des Fotos abzudunkeln. <br/> So fügen Sie Tiefe, verringern <b>Vignette</b> auf -100",
    "deep_space_4": "<b>Glatte Vignette</b> <br/> Im Augenblick ist die Vignette sieht auch scharf. <br/> Damit sehen die Vignette glatt und subtil, <br/> erhöhen <b>Feder</b> auf 100",
    "deep_space_5": "<b>Recover Highlights</b> <br/> Einige der Sterne am Rand des Fotos zu dunkel aussehen. Wir können sie wiederherstellen , indem <b>Höhepunkte</b> auf 100 zu erhöhen",
    "deep_space_6": "<b>Farbe ändern</b> <br/> HSL können Sie die Attribute einer bestimmten Farbe zu ändern. Um die Farbe des Himmels ändern, erhöhen <b>Aqua Hue</b> bis 100",
    "deep_space_7": "Gute Arbeit! Sie haben gelernt, wie ein Sternenhimmel beeindruckend aussehen wirklich zu machen.",
    "deep_space_description": "In diesem Tutorial werden Sie lernen Möglichkeiten Tiefe zu verleihen, die Farbe und die Änderung der Belichtung eines Nachthimmel.",
    "dehaze": "Hinzufügen oder Entfernen von Dunst aus dem Foto",
    "denoise": "Entfernen Sie das Rauschen aus dem Foto",
    "diffuse": "Geben Sie dem Foto einen \"Glow-Effekt\"",
    "distortion": "Verzerrung",
    "distortion_1": "<b>Richtige Verzerrung</b> <br/> Wir sehen oft Gebäuden und Mauern in Fotos krumm suchen. Dies wird üblicherweise durch die Gestaltung der Weitwinkelkamera Linsen verursacht. Wir können dies leicht zu beheben in Polarr mit Verzerrung-Tools. <br/> Um Verzerrungen zu beheben, erhöhen die <b>vertikale Verzerrung</b> bis 70",
    "distortion_2": "<b>Richtige Vignette</b> <br/> Zusätzlich zu optischen Verzerrungen, die meisten Kamera-Objektive haben Licht Fallout in der Nähe ihrer Kanten. Wir können diese dunkleren Kanten mit der Vignette Werkzeug reduzieren. <br/> Zur Korrektur Vignette, erhöhen <b>Vignette</b> bis 20",
    "distortion_3": "<b>Verbessern</b> Sie <b>Details</b> <br/> Schließlich die Details in den Fotos zu verbessern, können wir die Klarheit Tool verwenden. <br/> Erhöhen Sie <b>Klarheit</b> zu 100",
    "distortion_4": "Gute Arbeit! Sie haben gelernt, wie man gemeinsame Linsenfehler in 3 einfachen Schritten zu korrigieren.",
    "distortion_amount": "Breitbild oder Fischauge zu Fotos hinzufügen.",
    "distortion_description": "In diesem Tutorial erfahren Sie, wie Linsenverzeichnungen und Vignette Architekturaufnahmen zu korrigieren.",
    "distortion_horizontal": "Ändern, um die Verzerrung entlang der horizontalen Achse",
    "distortion_vertical": "Ändern, um die Verzerrung entlang der vertikalen Achse",
    "exposure": "Ändern Sie die Helligkeit, ohne das Foto Auswaschen",
    "face_0": "Polarr erkennt automatisch Gesichter und trennt einzelne Merkmale.",
    "face_1": "Verwenden Sie das Verzerren-Werkzeug die Größe und Position der Gesichtszüge einzustellen.",
    "fringing": "Einen \"schwummrigen\" Effekt hinzufügen.",
    "gradient_mask_0": "Gradient Maske wirkt sich Bereiche in einer geraden Linie.",
    "gradient_mask_1": "Pinch zu ändern. <br/> Ziehen zu bewegen und drehen.",
    "gradient_mask_2": "<b>Invert</b> schaltet die Gradientenrichtung.",
    "gradient_mask_3": "<b>Reflektieren</b> Sie macht den Gradienten in beide Richtungen laufen.",
    "grain": "Fügen Sie einen Film Grain-Effekt",
    "highlights": "Ändern Sie die Helligkeit der Highlights ohne Auswaschen des Foto",
    "hsl": "Verändern den Farbton, die Sättigung oder die Helligkeit einer bestimmten Farbe.",
    "hsl_header": "HSL",
    "hue_blue": "Bestimmte Farben nach Blau ändern.",
    "hue_red": "Bestimmte Farben nach Rot ändern.",
    "luminance_blue": "Die Helligkeit von Blau ändern.",
    "luminance_red": "Die Helligkeit von Rot ändern.",
    "mastering_adjustments": "Die Beherrschung Anpassungen",
    "mosaic": "Geben Sie dem Foto einen Mosaikeffekt",
    "radial_mask_0": "Radial Maske wirkt sich nur auf eine Kreisfläche.",
    "radial_mask_1": "Pinch zu ändern. <br/> Ziehen zu bewegen und drehen.",
    "radial_mask_2": "<b>Feder</b> ändert sich die Verschwommenheit der Maske.",
    "radial_mask_3": "<b>Invert</b> schaltet den betroffenen Bereich.",
    "saturation": "Sättigung erhöhen oder verringern.",
    "saturation_blue": "Die \"Bläue\" von blauen Farben ändern.",
    "saturation_red": "Die \"Röte\" von roten Farben ändern.",
    "shadows": "Ändern Sie die Helligkeit der Schatten ohne Auswaschen des Foto",
    "sharpen": "Erhöht den Kanten- und Eckenkontrast für ein schärferes Foto.",
    "temperature": "Machen Sie das Foto wärmer oder kälter",
    "tint": "Farbe entalng des Magenta-Grün-Spektrums ändern.",
    "toning_highlights": "Ändern Sie die Farbe der Höhepunkte",
    "toning_shadows": "Ändern Sie die Farbe der Schatten",
    "vibrance": "Ändern Sie die Sättigung ohne Hauttöne Auswaschen",
    "vignette_amount": "Verblassen oder hebt die Ränder des Fotos",
    "vignette_feather": "Ändern Sie den Unschärfen der Vignette",
    "vignette_highlights": "Lassen Sie die Highlights der Vignette durchscheinen",
    "vignette_roundness": "Machen Sie die Vignette mehr rund oder quadratisch",
    "vignette_size": "Ändern Sie die Größe der Vignette",
    "white_balance": "Weißabgleich",
    "white_balance_1": "Die <b>richtige Weißabgleich</b> <br/> Die meisten Lichtquellen (Glühbirnen, Taschenlampen, etc.) nicht rein weiße Farbe emittieren. Dies kann dazu führen Weißen in Fotos einen gelben oder blauen gegossen zu haben. Um dies zu korrigieren, verringern <b>Temperatur</b> auf -30",
    "white_balance_2": "<b>Recover Licht Schatten</b> <br/> Manchmal Teile eines Fotos sind dunkler als gedacht, weil unsere Augen mehr Licht als die Kamera wahrnehmen kann. Wir können das Licht von Schatten mit Schatten Werkzeug erholen. <br/> Erhöhen Sie <b>Schatten</b> auf 100",
    "white_balance_3": "<b>Verbessern</b> Sie <b>Details</b> <br/> Schließlich die Details in den Fotos zu verbessern, können wir die Klarheit Tool verwenden. <br/> Erhöhen Sie <b>Klarheit</b> zu 100",
    "white_balance_4": "Gute Arbeit! Sie haben gelernt, wie Gelbstich zu entfernen und zurückzugewinnen Licht von Schatten in 3 einfachen Schritt.",
    "white_balance_description": "In diesem Tutorial werden Sie das Konzept der Weißabgleich lernen und wie in der Tür künstlichem Licht zu korrigieren.",
    "whites": "Ändert die Helligkeit der weißen Teile des Fotos"
  }
};
},{}],323:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "Add",
    "add_image": "Choose image",
    "add_to_favorites": "Add to Favorites",
    "aspect": "Aspect",
    "back": "Back",
    "buy": "Buy",
    "buy_full_version": "Buy full version",
    "cancel": "Cancel",
    "check_again": "Check again",
    "choose_folder": "Choose folder",
    "clear": "Clear",
    "clear_history": "Clear history",
    "close": "Close",
    "cloud": "Cloud",
    "continue_anyway": "Continue anyway",
    "current_image": "Current",
    "delete": "Delete",
    "device": "Device",
    "dismiss": "Dismiss",
    "done": "Done",
    "dont_ask": "Don’t ask again",
    "duplicate": "Duplicate",
    "enter_license": "Enter license key",
    "enter_license_key": "Enter license key",
    "exit": "Exit",
    "export": "Export",
    "export_filter": "Save as QR code",
    "forgot_password": "Forgot password?",
    "free_membership": "free membership",
    "get": "Get",
    "get_license": "Get license key",
    "get_started": "Get started",
    "got_it": "Got it",
    "have_account": "Already have an account?",
    "import": "Import",
    "import_filter": "Import filter",
    "import_filter_photos": "Import from Photos",
    "import_photos": "Open photos",
    "info": "Info",
    "installed": "Installed",
    "invite": "Send link",
    "learn_more": "Learn more",
    "load_last_photo": "Load last photo on launch",
    "new": "New",
    "new_filter": "Create filter",
    "next": "Next",
    "no": "No",
    "no_thanks": "No, thanks",
    "not_really": "Not really",
    "not_this_time": "Not this time",
    "open_sample": "Open sample photo",
    "paste_adjustments": "Paste adjustments",
    "pay_wechat": "Open Wechat",
    "prev": "Prev",
    "preview": "Preview",
    "pro": "Pro",
    "purchased": "Purchased",
    "remove": "Remove",
    "remove_all_photos": "Remove all photos",
    "remove_photo": "Remove photo",
    "remove_selected_photo": "Remove selected photo",
    "reset": "Reset",
    "reset_all": "Reset All",
    "reset_password": "Reset Password",
    "reset_photos": "Reset saved and edited labels",
    "reset_tips": "Reset tips",
    "restore": "Restore",
    "revert": "Revert to original",
    "sample": "Sample",
    "save": "Save",
    "save_qr": "Save QR code",
    "save_share": "Save + Share",
    "scan_filter": "Scan filter",
    "select_all": "Select all",
    "select_changed": "Select edited",
    "send": "Send",
    "send_verification": "Resend verification email",
    "show_in_folder": "Show in folder",
    "show_me": "Show me",
    "signin": "Log in",
    "signout": "Log out",
    "signup": "Create Polarr account",
    "skip": "Skip",
    "submit_license_key": "Submit license key",
    "subscribe": "Subscribe",
    "subscribe_monthly": "Subscribe Monthly",
    "subscribe_yearly": "Subscribe Yearly",
    "sure": "Ok, sure",
    "try": "Try",
    "try_again": "Try again",
    "unlock": "Unlock",
    "unsubscribe": "Unsubscribe",
    "update": "Update",
    "whats_new": "What's new",
    "yes": "Yes"
  },
  "dialogs": {
    "about": "About",
    "about_text": "We believe it is important to allow creators to effortlessly express their perspectives and attitudes toward life, without losing the fidelity of their creative desire and inspirations.",
    "account": "Account",
    "all_filters": "All filters",
    "all_filters_text": "Unlock Polarr's entire collection of premium filters",
    "backup_filters": " Synchronize and back up all your custom filters so that you can use them across devices and platforms. This feature requires you log in with a Polarr account.",
    "backup_filters_title": "Backup Custom Filters",
    "brush_limit": "The current Brush Mask limit is </limit>",
    "denoise": "Our real-time denoise process makes it easy to remove noise and grain in photos. Separately control color and luminance noise components.",
    "featured": "Featured",
    "filter_collections": "Filter Collections",
    "filter_name": "Name this filter",
    "get_picky": "Tired of photo editing? Try out Polarr's new app that edits photos with Artificial Intelligence!",
    "get_updates": "Subscribe to updates",
    "get_updates_about": "Be the first to try out new features! <br/> Add you email address, and we will notify you about new features and products from Polarr.",
    "get_updates_thankyou": "You will now get updates about new features and products from Polarr! If you wish to stop receiving updates, you can \"unsubscribe\" from your email at any time.",
    "help": "Help",
    "invite": "Share installation link. Pro features will be unlocked after three people installed using your link.",
    "local_adjustments": "Local adjustments lets you use Polarr's powerful set of adjustments on targeted areas. Includes Radial, Gradient, Color, and Brush masking tools.",
    "lock_rotation": "Lock rotation",
    "max_export_size": "Max export size",
    "new_filter": "Save edits as a filter",
    "no_purchases": "There are no purchases to restore.",
    "offline": "Your device seems to be offline. Please try again later.",
    "photos": "Photos",
    "polarr_premium": "Polarr Premium",
    "pro_all_filters": "Pro features <br/> + <br/> All filters",
    "pro_all_filters_text": "Unlock all features including pro tools and all of Polarr’s premium filters. Use your Polarr account to access all features on other platforms. Backup and sync your custom filters.",
    "pro_features": "Pro Tools",
    "pro_features_all": "All Pro Tools",
    "purchase": "Unlock Radial, Gradient, Color, Brush, Denoise, and Text tools. Backup and sync your custom filters.",
    "purchase_failed": "Purchase cancelled",
    "purchase_header": "Unlock all pro features",
    "purchase_successful": "You now have access to all features!",
    "purchase_unavailable": "Purchases aren't available at this time. Sorry.",
    "purchases": "Purchases",
    "rate_review": "Rate and review",
    "rename_filter": "Rename Filter",
    "restore_failed": "Sorry, we couldn't restore your purchases.",
    "restore_successful": "All your previous purchases are now restored.",
    "review": "Enjoying Polarr Photo Editor so far?",
    "review_no": "Would you mind giving us some feedback?",
    "review_yes": "How about a rating on the App Store then?",
    "save_adjustments": "Make a custom filter you can share.",
    "send_feedback": "Send feedback",
    "signin_polarr_account": "Log into your Polarr account",
    "store": "Store",
    "subscribe": "Sign up for <strong>Polarr Premium</strong>",
    "subscribe_access": "for full access to:",
    "subscribe_cancel": "You can \"Unsubscribe\" in Settings anytime",
    "subscribe_caveat": "*Pro features include gradient adjustment, radial adjustment, color masking, and color brushing. Your membership will apply on all devices and platforms this year.",
    "subscribe_china": "Follow polaxiongkeji WeChat account and make a payment in WeChat, then login with your Polarr account to activate pro features.",
    "subscribe_error": "Oops! There was error subscribing to Polarr. Try again?",
    "subscribe_features": "<li>Pro features (4 local adjustment tools)</li>               <li>Access all 12 premium filter packs</li>         <li>Stay updated for RAW formats</li>         <li>New content added monthly!</li>               <li>Access membership on all platforms*</li>",
    "subscribe_notify": "Notify me when my Polarr Premium Membership can be used on more platforms and devices",
    "subscribe_price": "</price>",
    "subscribe_refund": "You previously purchased something with Polarr and you are eligible for",
    "subscribe_success": "Thank you for subscribing to Polarr Premium!",
    "subscribe_unlock_success": "You’re now a Polarr Premium Member!",
    "subscription_expired": "Your Polarr Premium membership has expired",
    "text_tool": "Text tool lets you add text and shapes to photos. With features like letter and line spacing, alignment, and wrapping, you have full control your text. We've even included advanced features like dynamic vector scaling and a unique blending adjustment.",
    "text_tool_description": "Add and edit text on screen. Rotate, change color, set blending, and even adjust letter spacing.",
    "thankyou": "Thank you!",
    "unlock_all": "Unlock everything",
    "unlock_all_note": "Login your Polarr pro account or enter license key.",
    "unlock_alternate": "You can also upgrade by logging in to an existing account or by entering a license key in",
    "unlock_failed": "Pro Features aren't available for this account.",
    "unlock_success": "You now have access to all features!",
    "unsplash_info": "<p><a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> and <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a>, the best source for free and high-resolution stock photography, have teamed up to bring you a collection of 10 premium filters created by members of the Unsplash photographer community.</p> ",
    "upgrade": "Upgrade to full version to unlock all following features.",
    "upgrade_caveat": "* 12 additional filter packs include simulations of Classic Film, Vintage Film, Infrared Film, Skin Tones and many other adjustments suitable for various subjects and scenes.",
    "upgrade_features": "<li>Gradient adjustments</li><li>Circular adjustments</li><li>Color masking</li><li>Brushing adjustment</li><li>12 additional premium filter packs</li><li>Batch photo export</li>",
    "use_polarrfilter": "Use <strong>#polarrfilter</strong> to get noticed!",
    "user_in_paid": "You can log into Polarr on other platforms to access all features.",
    "user_in_unpaid": "If you unlock everything, you'll be able to log into Polarr on other platforms and access all features.",
    "verification_sent": "A verification email has been sent. <br/> Please check your email for verification instructions.",
    "verify_account": "To log in, please verify your account. <br/> Check your email for verification instructions.",
    "webgl_not_supported": "We’re extremely sorry but your device does not satisfy Polarr's graphic requirement, please update to the latest operating system and try again."
  },
  "labels": {
    "adjustments": "Adjustments",
    "after": "After",
    "alignment": "Alignment",
    "all": "All",
    "amount": "Amount",
    "angle": "Angle",
    "aqua": "Aqua",
    "artwork": "Artwork",
    "auto": "Auto",
    "balance": "Balance",
    "basic": "Basic",
    "batch_rename": "Batch rename",
    "batch_resize": "Batch resize",
    "before": "Before",
    "best": "Best",
    "blacks": "Blacks",
    "blend": "Blend",
    "blend_mode": "Blend Mode",
    "blue": "Blue",
    "blur": "Blur",
    "brightness": "Brightness",
    "brush_flow": "Brush Flow",
    "brush_opacity": "Brush Opacity",
    "brush_size": "Brush Size",
    "clarity": "Clarity",
    "collection": "Collection",
    "color": "Color",
    "contrast": "Contrast",
    "crop": "Crop",
    "crop_size": "Crop dimensions",
    "curves": "Curves",
    "custom": "Custom",
    "dehaze": "Dehaze",
    "denoise": "Denoise",
    "detail": "Detail",
    "diffuse": "Diffuse",
    "distortion": "Distort",
    "distortion_amount": "Distortion",
    "distortion_horizontal": "Horizontal Distortion",
    "distortion_vertical": "Vertical Distortion",
    "dividers": "Dividers",
    "edit": "Edit",
    "effects": "Effects",
    "email": "Email",
    "enable": "Enable",
    "estimated_size": "Estimated Size",
    "exposure": "Exposure",
    "eyes": "Eyes",
    "face": "Face",
    "favorites": "Favorites",
    "feather": "Feather",
    "fit": "Fit",
    "font": "Font",
    "format": "Format",
    "free": "Free",
    "frequent": "Most used",
    "fringing": "Fringing",
    "grain": "Grain",
    "green": "Green",
    "guides": "Guides",
    "high": "High",
    "highlights": "Highlights",
    "histogram": "Histogram",
    "history": "History",
    "hsl": "HSL",
    "hue": "Hue",
    "hue_blue": "HSL - blue hue",
    "hue_red": "HSL - red hue",
    "invert": "Invert",
    "lens": "Lens",
    "lens_distortion": "Lens Distortion",
    "letter_spacing": "Letter Spacing",
    "license_key": "License key",
    "light": "Light",
    "line_spacing": "Line Spacing",
    "lips": "Lips",
    "look": "Filter",
    "looks": "Filters",
    "low": "Low",
    "luminance": "Luminance",
    "luminance_blue": "HSL - blue luminance",
    "luminance_red": "HSL - red luminance",
    "magenta": "Magenta",
    "master": "Master",
    "max_size": "Max dimensions ",
    "med": "Med",
    "mix": "Adjust All",
    "mosaic": "Pixelate ",
    "name": "Name",
    "none": "None",
    "nose": "Nose",
    "opacity": "Opacity",
    "optics": "Optics",
    "options": "Options",
    "orange": "Orange",
    "original": "Original",
    "outline": "Outline",
    "password": "Password",
    "position": "Position",
    "prefix": "Prefix segment",
    "presets": "Presets",
    "preview": "Preview",
    "purple": "Purple",
    "quality": "Quality",
    "ratio": "Ratio",
    "red": "Red",
    "reflect": "Reflect",
    "resolution": "Resolution",
    "rgb": "RGB",
    "roundness": "Roundness",
    "saturation": "Saturation",
    "saturation_blue": "HSL - blue saturation",
    "saturation_red": "HSL - red saturation",
    "sayings": "Sayings",
    "shadow": "Shadow",
    "shadows": "Shadows",
    "shape": "Shape",
    "shapes": "Shapes",
    "shapes_presets": "Shapes & Presets",
    "sharpen": "Sharpen",
    "size": "Size",
    "skin": "Skin",
    "smoothness": "Smoothing",
    "split_tone": "Toning",
    "teeth": "Teeth",
    "temperature": "Temp",
    "text": "Text",
    "texture": "Texture",
    "threshold": "Threshold",
    "tint": "Tint",
    "tone": "Tone",
    "toning": "Toning",
    "use_prefix": "Use prefix",
    "use_watermark": "Use Watermark",
    "user_name": "User name",
    "vibrance": "Vibrance",
    "vignette": "Vignette",
    "vignette_amount": "Vignette",
    "watermark_image": "Watermark Image",
    "wb": "WB",
    "whitening": "Whitening",
    "whites": "Whites",
    "width": "Width",
    "yellow": "Yellow"
  },
  "menus": {
    "batch_export": "Batch export",
    "change_language": "Change language",
    "copy_edit": "Copy Edits",
    "edit_watermark": "Edit Watermark",
    "export": "Format, Resize, Watermark",
    "filters_users": "Filters by Polarrists",
    "guide": "Guide",
    "image_quality": "Image quality",
    "image_settings": "Export settings",
    "language": "Language",
    "more_filters": "More filters",
    "open": "Open",
    "open_in": "Open in",
    "paste_edit": "Paste Edits",
    "remove": "Remove",
    "rename": "Rename",
    "restore_purchases": "Restore purchases",
    "save_cameraroll": "Save to Camera Roll",
    "save_copy": "Save a copy",
    "settings": "Settings",
    "share": "Share",
    "share_edits": "Share edits",
    "share_filter": "Share filter",
    "show_instructions": "Adjustments guide",
    "show_intro": "Show intro",
    "show_video_tutorial": "Video tutorial",
    "single_export": "Export"
  },
  "onboarding": {
    "become_master": "Become a photo editing master.",
    "step_0": "Welcome to Polarr! <br/> Lets get started by viewing some filters.",
    "step_1": "Tap to select a filter. <br/> Swipe panel to see more filters.",
    "step_10": "You can drag to re-arrange all tool icons.",
    "step_2": "Tap <i class='icon-check'></i> to apply the filter. <br/> Use the slider to adjust the intensity of the filter.",
    "step_3": "Every filter is completely editable. <br/> Go to Adjustments to see your edits.",
    "step_4": "You can change an adjustment by dragging the slider.",
    "step_4a": "Long press then drag to adjust the slider.",
    "step_5": "See all the edits you've made in history panel.",
    "step_6": "You can undo an edit and go back to any point in history.",
    "step_7": "Save your image here. <br/> You can resize, add watermark or save multiple images.",
    "step_7a": "Save your image here. <br/> You can also share and save multiple images.",
    "step_8": "Open your own photos here.",
    "step_9": "View your opened photos in the photos panel."
  },
  "status": {
    "account_suspended": " Your account is put on hold due to suspicious activity. For more info, please check your email or contact <a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "Tap <i class='icon-add-face'></i> to add a face",
    "adjustment_copied": "Adjustments Copied",
    "adjustment_pasted": "Adjustments Pasted",
    "demo_mode": "This version of Polarr Photo Editor has been created specifically for demonstration purposes",
    "downloading_font": "Downloading font",
    "drag_icons": "Drag icons to rearrange tools",
    "drop_files": "Drop files to open",
    "email_invalid": "Sorry, it looks like that email address is not valid.",
    "email_reset": "A reset link has been sent. <br/> Please check your email.",
    "exporting_filter": "Saving filter",
    "field_invalid": "isn't valid.",
    "file_exists": "That photo is already open",
    "file_not_supported": "Sorry, that file type isn't supported",
    "filter_exported": "Your filter has been saved to camera roll.",
    "filter_saved": "Filter saved",
    "filters_sync_error": "Error syncing filters",
    "filters_synced": "Filters synced",
    "filters_syncing": "Syncing filters...",
    "import_filter_error": "Sorry, that filter could not be imported",
    "import_limit": "Polarr can only open up to 50 photos, please remove some photos before opening more. ",
    "importing_filter": "Importing filter",
    "importing_photo": "Opening photo",
    "importing_photos": "Opening photos",
    "invalid_credentials": "Wrong email or password",
    "loading_photo": "Loading photo",
    "login_success": "You have been successfully logged in!",
    "no_faces": "No faces detected.",
    "no_user": "That user doesn't exist.",
    "offline": "Couldn't connect to the server.",
    "photo_load_error": "Unable to load photo",
    "photo_saved": "Photo saved",
    "photo_shared": "Photo shared",
    "photos_saved": "Photos saved",
    "purchase_required": "Purchase required",
    "raw_failed": "Unable to read RAW image. RAW support is still experimental.",
    "registration_success": "You have successfully made a new account! <br/> To log in on other devices, you must first verify your account. We've sent you an email with verification instructions.",
    "saving_photo": "Saving photo",
    "saving_photo_error": "Couldn't save photo",
    "saving_photo_error_sd": "Polarr doesn’t have permission to access your SD card or internal storage. Please check the latest Android system update to fix the issue.",
    "saving_photos": "Saving photos",
    "server_offline": "Sorry, the server isn't responding. <br/> Are you connected to the internet?",
    "sharing_photo": "Sharing photo",
    "sharing_photo_error": "Couldn't share photo",
    "show_me_how": "Show me how to use <b></thing></b>",
    "supported_files": "Polarr supports JPG, PNG, BMP, and certain RAW files",
    "unlocks_remaining": "You have </unlocks> unlocks remaining",
    "user_exists": "There is already an account with that email or user name.",
    "wrong_password": "Wrong password"
  },
  "tooltips": {
    "add_face": "Add Face",
    "add_text": "Add Text",
    "adjustments": "Adjustments",
    "apply_adjustments": "Apply Adjustments",
    "auto_adjust": "Auto Adjustments",
    "brush_filter": "Brush Mask",
    "color_filter": "Color Mask",
    "crop": "Crop",
    "crop_straighten": "Crop & Angle",
    "current_adjustments": "Current Adjustments",
    "double_tap_edit": "Double tap to edit",
    "drag_watermark": "Drag to move watermark",
    "eraser": "Eraser",
    "face": "Face Tools",
    "filmstrip": "Opened Photos",
    "flip": "Flip",
    "flip_h": "Flip horizontally",
    "flip_v": "Flip vertically",
    "global_adjustments": "Main Adjustments",
    "gradient_filter": "Gradient Mask",
    "guides": "Guides",
    "history": "Editing History",
    "info": "Info",
    "local_adjustments": "Local Adjustments",
    "looks": "Filters",
    "masks": "Masks",
    "move_face": "Move Face",
    "open": "Open Photos",
    "pan": "Pan",
    "radial_filter": "Radial Mask",
    "redo": "Redo",
    "remove_face": "Remove Face",
    "remove_text": "Remove Text",
    "rotate": "Rotate",
    "rotate_ccw": "Rotate CCW",
    "rotate_cw": "Rotate CW",
    "save": "Save Photo",
    "settings": "Settings",
    "show_original": "Show Original",
    "text": "Text Tool",
    "undo": "Undo"
  },
  "tutorial": {
    "blacks": "Change the brightness of the black parts of the photo",
    "brightness": "Change the brightness or \"gamma\" of the photo",
    "brush_mask_0": "Use your finger to paint with the brush.",
    "brush_mask_1": "Brush mask only affects the brushed area.",
    "clarify": "Clarify",
    "clarify_1": "<b>Reduce fog</b><br/>Fog is a common issue in photos and it's easy to remove in Polarr. <br/> To remove fog, increase <b>Dehaze</b> to 85",
    "clarify_2": "<b>Recover exposure</b><br/>Dehaze sometimes darkens the photo too much. <br/> To recover some light, increase <b>Exposure</b> to 30",
    "clarify_3": "<b>Increase contrast</b><br/>The haze is mostly gone now, but we can boost the light dynamic a little more. Increase <b>Contrast</b> to 60",
    "clarify_4": "<b>Enhance details</b><br/>To enhance the details in the photo, we can use the Clarity tool. Increase <b>Clarity</b> to 40",
    "clarify_5": "Good work! You've learned how to make a hazy photo a lot better in 4 simple steps.",
    "clarify_description": "In this tutorial, you will learn how to reduce fog, define outlines and add vividness to your subject.",
    "clarity": "Increase or smooth out details in the photo",
    "color_mask_0": "Color mask only affects the selected color.",
    "color_mask_1": "Drag to change the selected color.",
    "color_mask_2": "<b>Threshold</b> sets the sensitivity of the selection.",
    "contrast": "Make your subject more distinguishable.",
    "curves": "Change the tone curve of the photo",
    "deep_space": "Deep Space",
    "deep_space_1": "<b>Recover light</b><br/>Polarr's exposure adjustment simulates the exposure of a camera. <br/> To recover light, set <b>Exposure</b> to 100",
    "deep_space_2": "<b>Enhance details</b><br/>Let's make the stars look more impressive. <br/> Adding clarity will help by revealing more details. <br/> To enhance details, set <b>Clarity</b> to 100",
    "deep_space_3": "<b>Add depth</b><br/>To bring more focus to the center of the image, we can add vignette to darken the edges of the photo. <br/> To add depth, decrease <b>Vignette</b> to -100",
    "deep_space_4": "<b>Smooth Vignette</b><br/>Right now the vignette looks too sharp. <br/> To make the vignette look smooth and subtle, <br/> increase <b>Feather</b> to 100",
    "deep_space_5": "<b>Recover highlights</b><br/>Some of the stars in the edge of the photo look too dark. We can recover them by increasing <b>Highlights</b> to 100",
    "deep_space_6": "<b>Change color</b><br/>HSL allows you to change the attributes of a certain color. To change the color of the sky, increase <b>Aqua Hue</b> to 100",
    "deep_space_7": "Good work! You've learned how to make a starry sky look really impressive.",
    "deep_space_description": "In this tutorial, you will learn ways to add depth, adjust color and the change the exposure of a night sky.",
    "dehaze": "Add or remove haze from the photo",
    "denoise": "Remove noise from the photo",
    "diffuse": "Give the photo a \"glow\" effect",
    "distortion": "Distortion",
    "distortion_1": "<b>Correct distortion</b><br/>We often see buildings and walls looking crooked in photos. This is usually caused by the design of wide-angle camera lenses. We can easily fix this in Polarr with distortion tools. <br/> To fix distortion, increase <b>Vertical Distortion</b> to 70",
    "distortion_2": "<b>Correct vignette</b><br/>In additional to optical distortions, most camera lenses have light fallout near their edges. We can reduce these darker edges using the vignette tool.<br/> To correct vignette, increase <b>Vignette</b> to 20",
    "distortion_3": "<b>Enhance details</b><br/>Lastly, to enhance the details in the photo, we can use the clarity tool. <br/> Increase <b>Clarity</b> to 100",
    "distortion_4": "Good work! You've learned how to correct common lens defects in 3 simple steps.",
    "distortion_amount": "Add wide-angle or fish-eye effect to photos.",
    "distortion_description": "In this tutorial, you will learn how to correct lens distortions and vignette in architectural shots.",
    "distortion_horizontal": "Change the distortion along the horizontal axis",
    "distortion_vertical": "Change the distortion along the vertical axis",
    "exposure": "Change the brightness without washing out the photo",
    "face_0": "Polarr automatically detects faces and separates individual features.",
    "face_1": "Use the distort tool to adjust the size and position of facial features.",
    "fringing": "Add a \"dizzy\" effect to your photo.",
    "gradient_mask_0": "Gradient mask affects areas in a straight line.",
    "gradient_mask_1": "Pinch to resize. <br/> Drag to move and rotate.",
    "gradient_mask_2": "<b>Invert</b> switches the gradient direction.",
    "gradient_mask_3": "<b>Reflect</b> makes the gradient run in both directions.",
    "grain": "Add a film grain effect",
    "highlights": "Change the brightness of the highlights without washing out the photo",
    "hsl": "Change the hue, saturation, or lightness of a specific color.",
    "hsl_header": "HSL",
    "hue_blue": "Shift specific colors to blue.",
    "hue_red": "Shift specific colors to red.",
    "luminance_blue": "Change the brightness of blue.",
    "luminance_red": "Change the brightness of red.",
    "mastering_adjustments": "Mastering Adjustments",
    "mosaic": "Give the photo a mosaic effect",
    "radial_mask_0": "Radial mask only affects a circular area.",
    "radial_mask_1": "Pinch to resize. <br/> Drag to move and rotate.",
    "radial_mask_2": "<b>Feather</b> changes the fuzziness of the mask.",
    "radial_mask_3": "<b>Invert</b> switches the affected area.",
    "saturation": "Increase or decrease intensity of color.",
    "saturation_blue": "Change the \"blueness\" of blue colors.",
    "saturation_red": "Change the \"redness\" of red colors.",
    "shadows": "Change the brightness of the shadows without washing out the photo",
    "sharpen": "Increase edge contrast to make photo more crisp.",
    "temperature": "Make the photo warmer or cooler",
    "tint": "Adjust color along the magenta-green spectrum.",
    "toning_highlights": "Change the color of the highlights",
    "toning_shadows": "Change the color of the shadows",
    "vibrance": "Change the saturation without washing out skin tones",
    "vignette_amount": "Fade or highlight the edges of the photo",
    "vignette_feather": "Change the fuzziness of the vignette",
    "vignette_highlights": "Let the highlights show through the vignette",
    "vignette_roundness": "Make the vignette more round or square",
    "vignette_size": "Change the size of the vignette",
    "white_balance": "White Balance",
    "white_balance_1": "<b>Correct white balance</b><br/>Most light sources (light bulbs, flashlights, etc) do not emit purely white color. This can cause whites in photos to have a yellow or blue cast. To correct this, </b> decrease <b>Temperature</b> to -30",
    "white_balance_2": "<b>Recover light from shadows</b><br/>Sometimes parts of a photo are darker than intended because our eyes can perceive more light than the camera. We can recover the light from shadows using shadows tool.<br/> Increase <b>Shadows</b> to 100",
    "white_balance_3": "<b>Enhance details</b><br/>Lastly, to enhance the details in the photo, we can use the clarity tool. <br/> Increase <b>Clarity</b> to 100",
    "white_balance_4": "Good work! You've learned how to remove yellow cast and recover light from shadows in 3 simple step.",
    "white_balance_description": "In this tutorial, you will learn the concept of white balance and how to correct in door artificial light.",
    "whites": "Change the brightness of the white parts of the photo"
  }
};
},{}],324:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "Agregar",
    "add_image": "Elige la imagen",
    "add_to_favorites": "Agregar a favoritos",
    "aspect": "Aspecto",
    "back": "Volver",
    "buy": "Comprar",
    "buy_full_version": "Compra la version completa",
    "cancel": "Cancelar",
    "check_again": "Revisar otra vez",
    "choose_folder": "Elegir la carpeta",
    "clear": "Limpiar",
    "clear_history": "Borrar historial",
    "close": "Cerrar",
    "cloud": "Nube",
    "continue_anyway": "Continuar de todas formas",
    "current_image": "Actual",
    "delete": "Borrar",
    "device": "Dispositivo",
    "dismiss": "Despedir",
    "done": "Listo",
    "dont_ask": "No preguntarme de nuevo",
    "duplicate": "Duplicar",
    "enter_license": "Introduzca la clave de la licencia",
    "enter_license_key": "Introduzca la clave de la licencia",
    "exit": "Salida",
    "export": "Exportar",
    "export_filter": "Guardar como código QR",
    "forgot_password": "Se te olvidó tu contraseña?",
    "free_membership": "Membresía gratis",
    "get": "Obtener",
    "get_license": "Obtener clave de licencia",
    "get_started": "Comience",
    "got_it": "Lo tengo",
    "have_account": "¿Ya tienes una cuenta?",
    "import": "Importar",
    "import_filter": "filtro de importación",
    "import_filter_photos": "Importar de Fotos",
    "import_photos": "Abrir fotografías",
    "info": "información",
    "installed": "Instalado",
    "invite": "Enviar enlace",
    "learn_more": "Aprende más",
    "load_last_photo": "Cargar la última foto en el lanzamiento",
    "new": "Nuevo",
    "new_filter": "Crear filtro",
    "next": "Siguiente",
    "no": "No",
    "no_thanks": "No, gracias",
    "not_really": "No realmente",
    "not_this_time": "No ahora",
    "open_sample": "Abrir fotografía de muestra",
    "paste_adjustments": "Pegar ajustes",
    "pay_wechat": "WeChat abierto",
    "prev": "Anterior",
    "preview": "Avance",
    "pro": "Pro",
    "purchased": "Comprado",
    "remove": "Eliminar",
    "remove_all_photos": "Retirar todas las fotos",
    "remove_photo": "Retire la foto",
    "remove_selected_photo": "Retire foto seleccionada",
    "reset": "Reiniciar",
    "reset_all": "Reiniciar todo",
    "reset_password": "Restablecer la contraseña",
    "reset_photos": "Restablecer las etiquetas guardadas y editado",
    "reset_tips": "Tips de reinicio",
    "restore": "Restaurar",
    "revert": "Revertir al original",
    "sample": "Muestra",
    "save": "Guardar",
    "save_qr": "Guarde el código QR",
    "save_share": "Guardar + Compartir",
    "scan_filter": "filtro de análisis",
    "select_all": "Seleccionar todo",
    "select_changed": "Seleccionar editado",
    "send": "Enviar",
    "send_verification": "Reenviar correo electrónico de verificación",
    "show_in_folder": "Mostrar en la carpeta",
    "show_me": "Muéstrame",
    "signin": "Iniciar sesión",
    "signout": "Cerrar sesión",
    "signup": "Crear cuenta Polarr",
    "skip": "Saltar",
    "submit_license_key": "Presentar clave de licencia",
    "subscribe": "Suscribir",
    "subscribe_monthly": "Suscribirse mensual",
    "subscribe_yearly": "Suscribirse anual",
    "sure": "Ok, está bien",
    "try": "Tratar",
    "try_again": "Inténtelo de nuevo",
    "unlock": "Desbloquear",
    "unsubscribe": "Darse de baja",
    "update": "Actualizar",
    "whats_new": "Qué hay de nuevo",
    "yes": "Sí"
  },
  "dialogs": {
    "about": "Acerca",
    "about_text": "Nosotros creemos que es importante permitirle a los creadores expresar fácilmente sus perspectivas y actitudes hacia la vida, sin perder la fidelidad de sus deseos e inspiraciones creativas.",
    "account": "Cuenta",
    "all_filters": "todos los filtros",
    "all_filters_text": "Desbloquear toda la colección de filtros premium del Polarr",
    "backup_filters": "Sincronizar y copia de seguridad de todos sus filtros personalizados para que pueda utilizarlos en diferentes dispositivos y plataformas. Esta función requiere que inicie sesión con una cuenta de Polarr.",
    "backup_filters_title": "De copia de seguridad de filtros especiales",
    "brush_limit": "El límite actual es la máscara del cepillo </limit>",
    "denoise": "Nuestro proceso de eliminación de ruido en tiempo real hace que sea fácil de eliminar el ruido y el grano en las fotos. Por separado controlar componentes de color y luminancia de ruido.",
    "featured": "Representado",
    "filter_collections": "Colecciones de filtro",
    "filter_name": "Nombre de este filtro",
    "get_picky": "Cansado de edición de fotos? Pruebe nueva aplicación de Polarr que edita fotografías con Inteligencia Artificial!",
    "get_updates": "Suscribirse a las actualizaciones",
    "get_updates_about": "Sé el primero en probar las nuevas características! <br/> Añadir que la dirección de correo electrónico, y le notificará acerca de las nuevas características y productos de Polarr.",
    "get_updates_thankyou": "Ahora va a obtener actualizaciones sobre nuevas características y productos de Polarr! <br/> Si desea dejar de recibir actualizaciones, puede \"darse de baja\" de su correo electrónico en cualquier momento.",
    "help": "Ayuda",
    "invite": "Enlace de instalación compartida. Las funciones Pro serán desbloqueadas después de que tres personas hayan instalado la aplicación usando su enlace.",
    "local_adjustments": "ajustes locales le permite utilizar potente conjunto de ajustes realizados en zonas seleccionadas de Polarr. Incluye herramientas de máscaras radial, gradiente, color y cepillo.",
    "lock_rotation": "Bloquear la rotación",
    "max_export_size": "Tamaño máximo de exportación",
    "new_filter": "Guardar edita como un filtro",
    "no_purchases": "No hay compras para restaurar.",
    "offline": "Su dispositivo parece no estar conectado a Internet. Por favor vuélvalo a intentar más tarde.",
    "photos": "fotos",
    "polarr_premium": "Polarr premium",
    "pro_all_filters": "Pro características <br/> + <br/> todos los filtros",
    "pro_all_filters_text": "Desbloquear todas las características incluyendo herramientas profesionales y todos los filtros premium del Polarr. Utilice su cuenta de Polarr acceder a todas las funciones en otras plataformas. Copias de seguridad y sincronizar sus filtros personalizados.",
    "pro_features": "Herramientas profesionales",
    "pro_features_all": "Todos los Pro Tools",
    "purchase": "Desbloquear radial, Gradiente, Color, Pincel, Denoise, y las herramientas de texto. Copias de seguridad y sincronizar sus filtros personalizados.",
    "purchase_failed": "Compra cancelada",
    "purchase_header": "Desbloquear todas las funciones Pro",
    "purchase_successful": "Ahora tiene acceso a la versión Pro.",
    "purchase_unavailable": "Las compras no están disponibles en este momento. Lo siento.",
    "purchases": "Compras",
    "rate_review": "Valorar y comentar",
    "rename_filter": "Cambiar el nombre de filtro",
    "restore_failed": "Su cuenta no tiene acceso a la función completa.",
    "restore_successful": "Todas sus compras anteriores ahora se restablecen.",
    "review": "¿Está disfrutando hasta ahora el editor de fotografías Polarr?",
    "review_no": "¿Le importaría calificarnos o dejar sus comentarios?",
    "review_yes": "¿Entonces qué tal si califica la aplicación en la App Store?",
    "save_adjustments": "Hacer un filtro personalizado se puede compartir.",
    "send_feedback": "Enviar comentario",
    "signin_polarr_account": "Ingrese a su cuenta Polarr",
    "store": "Almacenar",
    "subscribe": "Suscribirse a <strong>Polarr premium</strong>",
    "subscribe_access": "para tener acceso completo a:",
    "subscribe_cancel": "Puede \"Cancelar subscripción\" en la configuración en cualquier momento",
    "subscribe_caveat": "* Pro características incluyen el ajuste del gradiente, ajuste radial, encubre el color y el cepillado de color. Su membresía se aplicará en todos los dispositivos y plataformas de este año.",
    "subscribe_china": "Suscribirse a polaxiongkeji cuenta WeChat y hacer un pago en WeChat, a continuación, iniciar sesión con su cuenta de Polarr para activar funciones profesionales.",
    "subscribe_error": "Ups! Se produjo un error al suscribirse a Polarr. ¿Inténtalo de nuevo?",
    "subscribe_features": "<li> Pro características (4 herramientas de ajuste locales) </li><li> Acceder a todos los paquetes de filtro 12 de primera calidad </li><li> Manténgase actualizado para formatos RAW </li><li> El nuevo contenido agregado mensual! </li><li> El acceso de miembros en todas las plataformas * </li>",
    "subscribe_notify": "Notificarme cuando mi suscripción Premium Polarr se puede utilizar en más plataformas y dispositivos",
    "subscribe_price": "</price>",
    "subscribe_refund": "Usted ya compraste algo con Polarr y usted es elegible para",
    "subscribe_success": "Gracias por suscribirse a Polarr Premium!",
    "subscribe_unlock_success": "Ahora se encuentra en un miembro Premium Polarr!",
    "subscription_expired": "Su membresía premium Polarr ha expirado",
    "text_tool": "herramienta de texto le permite agregar texto y formas a las fotos. Con características como la letra y el interlineado, alineación, y de envasado, que tiene el control total de su texto. Incluso hemos incluido funciones avanzadas como la escala del vector dinámico y un ajuste de mezcla única.",
    "text_tool_description": "Añadir y editar texto en la pantalla. Rotar, cambiar de color, ajuste de mezcla, e incluso ajustar el espaciado entre caracteres.",
    "thankyou": "¡Gracias!",
    "unlock_all": "Desbloquea todo",
    "unlock_all_note": "Iniciar sesión en su cuenta Pro Polarr o introduzca la clave de licencia.",
    "unlock_alternate": "También puede actualizar iniciando sesión en una cuenta existente o mediante la introducción de una clave de licencia en",
    "unlock_failed": "Características Pro no están disponibles para esta cuenta.",
    "unlock_success": "Ahora tiene acceso a todas las funciones.",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> y <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> , la mejor fuente de forma gratuita y de alta resolución stock photography, se han unido para traer una colección de 10 filtros de primera calidad creados por los miembros de la comunidad fotógrafo Unsplash. </p>",
    "upgrade": "Actualizar a la versión completa para desbloquear todas las características siguientes.",
    "upgrade_caveat": "* 12 paquetes de filtros adicionales incluyen simulaciones de Película clásica, película de la vendimia, la película infrarroja, los tonos de piel y muchos otros ajustes adecuados para los diversos temas y escenas.",
    "upgrade_features": "<li> ajustes de gradiente </li><li> ajustes circulares </li><li> máscaras de color </li><li> El cepillado de ajuste </li><li> 12 paquetes de filtro prima adicional </li><li> exportación de fotos por lotes </li>",
    "use_polarrfilter": "Utilice <strong>#polarrfilter</strong> de llamar la atención!",
    "user_in_paid": "Puede iniciar sesión en Polarr en otras plataformas para acceder a todas las funciones.",
    "user_in_unpaid": "Si desbloquea todo, podrás iniciar sesión en Polarr en otras plataformas y acceder a todas las funciones.",
    "verification_sent": "Un mensaje de verificación ha sido enviada. <br/> Por favor, consultar su correo electrónico para obtener instrucciones de verificación.",
    "verify_account": "Para iniciar la sesión, por favor verificar su cuenta. <br/> Consultar su correo electrónico para obtener instrucciones de verificación.",
    "webgl_not_supported": "Nosotros lo sentimos mucho, pero su dispositivo no satisface los requerimientos gráficos de Polarr, por favor actualice al último sistema operativo y vuélvalo a intentar."
  },
  "labels": {
    "adjustments": "Ajustes",
    "after": "Después",
    "alignment": "Alineación",
    "all": "Todo",
    "amount": "Cantidad",
    "angle": "Ángulo",
    "aqua": "Aguamarina",
    "artwork": "ilustraciones",
    "auto": "Auto",
    "balance": "Equilibro",
    "basic": "Básico",
    "batch_rename": "cambio de nombre por lotes",
    "batch_resize": "cambio de tamaño de lote",
    "before": "Antes",
    "best": "Mejor",
    "blacks": "Negros",
    "blend": "Mezcla",
    "blend_mode": "Modo de mezcla",
    "blue": "Azul",
    "blur": "Difuminar",
    "brightness": "Brillo",
    "brush_flow": "Flujo cepillo",
    "brush_opacity": "Cepillo de opacidad",
    "brush_size": "Tamaño del pincel",
    "clarity": "Claridad",
    "collection": "Colección",
    "color": "Color",
    "contrast": "Contraste",
    "crop": "Recortar",
    "crop_size": "dimensiones de recorte",
    "curves": "Curvas",
    "custom": "Personalizado",
    "dehaze": "Borrar Neblina",
    "denoise": "Reducir ruido",
    "detail": "Detalle",
    "diffuse": "Difusión",
    "distortion": "Distorsión",
    "distortion_amount": "Distorsión",
    "distortion_horizontal": "Distorsión Horizontal",
    "distortion_vertical": "Distorsión Vertical",
    "dividers": "divisores",
    "edit": "Editar",
    "effects": "Efectos",
    "email": "Email",
    "enable": "Habilitar",
    "estimated_size": "Tamaño estimado",
    "exposure": "Exposición",
    "eyes": "ojos",
    "face": "Cara",
    "favorites": "Favoritos",
    "feather": "Pluma",
    "fit": "Ajuste",
    "font": "Fuente",
    "format": "Formato",
    "free": "Gratis",
    "frequent": "Más usado",
    "fringing": "Borde",
    "grain": "Granulado",
    "green": "Verde",
    "guides": "guías",
    "high": "Alto",
    "highlights": "Reflejos",
    "histogram": "Histogram",
    "history": "Historial",
    "hsl": "HSL",
    "hue": "Tono",
    "hue_blue": "HSL – tono azul",
    "hue_red": "HSL – tono rojo",
    "invert": "Invertir",
    "lens": "Prisma",
    "lens_distortion": "La distorsión de la lente",
    "letter_spacing": "Espaciado de letras",
    "license_key": "Clave de licencia",
    "light": "Luz",
    "line_spacing": "Interlineado",
    "lips": "Labios",
    "look": "Filtro",
    "looks": "Filtros",
    "low": "Bajo",
    "luminance": "Luminosidad",
    "luminance_blue": "HSL – luminosidad azul",
    "luminance_red": "HSL – luminosidad roja",
    "magenta": "Magenta",
    "master": "Maestro",
    "max_size": "dimensiones máximas",
    "med": "Mediano",
    "mix": "Ajustar todo",
    "mosaic": "Pixelate",
    "name": "Nombre",
    "none": "Ninguna",
    "nose": "Nariz",
    "opacity": "Opacidad",
    "optics": "Óptica",
    "options": "opciones",
    "orange": "Anaranjado",
    "original": "Original",
    "outline": "contorno",
    "password": "Contraseña",
    "position": "Posición",
    "prefix": "segmento prefijo",
    "presets": "presets",
    "preview": "Avance",
    "purple": "Púrpura",
    "quality": "Calidad",
    "ratio": "Radio",
    "red": "Rojo",
    "reflect": "Reflejar",
    "resolution": "Resolución",
    "rgb": "RGB",
    "roundness": "Redondez",
    "saturation": "Saturación",
    "saturation_blue": "HSL – saturación azul",
    "saturation_red": "HSL – saturación roja",
    "sayings": "refranes",
    "shadow": "Sombra",
    "shadows": "Sombras",
    "shape": "Forma",
    "shapes": "formas",
    "shapes_presets": "Formas y ajustes preestablecidos",
    "sharpen": "Enfocar",
    "size": "Tamaño",
    "skin": "Piel",
    "smoothness": "alisando",
    "split_tone": "Entonación",
    "teeth": "Dientes",
    "temperature": "Temp",
    "text": "Texto",
    "texture": "Textura",
    "threshold": "Límite",
    "tint": "Matiz",
    "tone": "Tono",
    "toning": "Tono",
    "use_prefix": "El uso del prefijo",
    "use_watermark": "Utilizar marca de agua",
    "user_name": "Nombre de usuario",
    "vibrance": "Intensidad",
    "vignette": "Viñeta",
    "vignette_amount": "Viñeta",
    "watermark_image": "Marca de agua Fotografía",
    "wb": "WB",
    "whitening": "Blanqueo",
    "whites": "Blancos",
    "width": "Anchura",
    "yellow": "Amarillo"
  },
  "menus": {
    "batch_export": "Exportar en grupo",
    "change_language": "Cambiar idioma",
    "copy_edit": "Las ediciones de copia",
    "edit_watermark": "Editar marca de agua",
    "export": "Formato, cambiar el tamaño, marca de agua",
    "filters_users": "Filtros de Polarrists",
    "guide": "Guía",
    "image_quality": "Calidad de imagen",
    "image_settings": "Exportar ajustes",
    "language": "Idioma",
    "more_filters": "Más filtros",
    "open": "Abrir",
    "open_in": "Abrir en",
    "paste_edit": "Pegar las modificaciones",
    "remove": "Remover",
    "rename": "Renombrar",
    "restore_purchases": "Restaurar compras",
    "save_cameraroll": "Guarda en el rollo de la cámara",
    "save_copy": "Guardar una copia",
    "settings": "Ajustes",
    "share": "Compartir",
    "share_edits": "Compartir ediciones",
    "share_filter": "Compartir Filtro",
    "show_instructions": "Guía de ajustes",
    "show_intro": "Ver introducción",
    "show_video_tutorial": "Video tutorial",
    "single_export": "Exportar"
  },
  "onboarding": {
    "become_master": "Convertirse en un maestro de edición de fotos.",
    "step_0": "Bienvenido a Polarr! <br/> Vamos a empezar por ver algunos filtros.",
    "step_1": "Toque para seleccionar un filtro. <br/> panel de deslizar para ver más filtros.",
    "step_10": "Usted puede arrastrar para volver a organizar todos los iconos de herramientas.",
    "step_2": "Grifo <i class='icon-check'></i> para aplicar el filtro. <br/> Utilice el control deslizante para ajustar la intensidad del filtro.",
    "step_3": "Cada filtro es completamente editable. <br/> Ir a Ajustes para ver las modificaciones.",
    "step_4": "Puede cambiar un ajuste arrastrando el control deslizante.",
    "step_4a": "pulsación larga a continuación, arrastre para ajustar el deslizador.",
    "step_5": "Ver todos los cambios que hayas hecho en el panel de la historia.",
    "step_6": "Puede deshacer una edición y volver a cualquier punto de la historia.",
    "step_7": "Guarde su imagen aquí. <br/> Puede cambiar el tamaño, añadir agua o guardar varias imágenes.",
    "step_7a": "Guarde su imagen aquí. <br/> También puede compartir y guardar múltiples imágenes.",
    "step_8": "Abrir sus propias fotos aquí.",
    "step_9": "Ver sus fotos abiertas en el panel de fotos."
  },
  "status": {
    "account_suspended": "Su cuenta se pone en espera debido a la actividad sospechosa. Para obtener más información, por favor, consultar su correo electrónico o contacto <a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "Grifo <i class='icon-add-face'></i> añadir una cara",
    "adjustment_copied": "Los ajustes copiados",
    "adjustment_pasted": "ajustes pegan",
    "demo_mode": "Esta versión de Polarr Photo Editor se ha creado específicamente para fines de demostración",
    "downloading_font": "La descarga de la fuente",
    "drag_icons": "Arrastrar iconos para reorganizar herramientas",
    "drop_files": "Soltar archivos para abrir",
    "email_invalid": "Lo sentimos, parece que esa dirección de correo electrónico no es válida.",
    "email_reset": "Un enlace de restablecimiento ha sido enviada. <br/> Por favor, consultar su correo electrónico.",
    "exporting_filter": "filtro de ahorro",
    "field_invalid": "no es válido.",
    "file_exists": "Esa foto ya está abierto",
    "file_not_supported": "Lo sentimos, no se admite ese tipo de archivo",
    "filter_exported": "Su filtro se ha guardado en rollo de la cámara.",
    "filter_saved": "filtro guardado",
    "filters_sync_error": "filtros de sincronización de error",
    "filters_synced": "filtros sincronizan",
    "filters_syncing": "Sincronización de filtros ...",
    "import_filter_error": "Lo sentimos, ese filtro no podía ser importado",
    "import_limit": "Polarr sólo se puede abrir hasta 50 fotos, por favor, elimine algunas fotos antes de abrir más.",
    "importing_filter": "filtro de Importación",
    "importing_photo": "foto de apertura",
    "importing_photos": "la apertura de fotos",
    "invalid_credentials": "Correo o contraseña equivocada",
    "loading_photo": "Cargando la fotografía",
    "login_success": "Se le ha iniciado la sesión correctamente!",
    "no_faces": "No se detectan rostros.",
    "no_user": "no existe ese usuario.",
    "offline": "No se pudo conectar al servidor.",
    "photo_load_error": "No se pudo cargar la fotografía",
    "photo_saved": "Fotografía grabada",
    "photo_shared": "Fotografía compartida",
    "photos_saved": "fotos guardadas",
    "purchase_required": "compra requerida",
    "raw_failed": "No se puede leer la imagen RAW. soporte RAW es todavía experimental.",
    "registration_success": "Usted ha hecho con éxito una nueva cuenta! <br/> Para iniciar la sesión en otros dispositivos, primero debe verificar su cuenta. Te hemos enviado un correo electrónico con instrucciones de verificación.",
    "saving_photo": "ahorro de la foto",
    "saving_photo_error": "Error al grabar la fotografía :(",
    "saving_photo_error_sd": "Polarr no tiene permiso para acceder a su tarjeta SD o almacenamiento interno. Por favor verifique que tenga la última actualización del sistema Android para solucionar el problema.",
    "saving_photos": "Almacenamiento de fotografías",
    "server_offline": "Lo sentimos, el servidor no está respondiendo. <br/> ¿Está conectado a Internet?",
    "sharing_photo": "uso compartido de fotos",
    "sharing_photo_error": "Error al compartir la fotografía :(",
    "show_me_how": "Muéstrame cómo usar </thing>",
    "supported_files": "Polarr soporta JPG, PNG, BMP, y ciertos archivos RAW",
    "unlocks_remaining": "Tienes </unlocks> desbloquea restante",
    "user_exists": "Ya existe una cuenta con que el correo electrónico o nombre de usuario.",
    "wrong_password": "Contraseña incorrecta"
  },
  "tooltips": {
    "add_face": "Añadir Cara",
    "add_text": "Añadir texto",
    "adjustments": "ajustes",
    "apply_adjustments": "aplicar ajustes",
    "auto_adjust": "Ajustes Auto",
    "brush_filter": "Máscara Brush",
    "color_filter": "Máscara de color",
    "crop": "Recortar",
    "crop_straighten": "Recortar y ángulo",
    "current_adjustments": "Los ajustes actuales",
    "double_tap_edit": "Doble click para editar",
    "drag_watermark": "Arrastra para mover la marca de agua",
    "eraser": "Borrador",
    "face": "Herramientas cara",
    "filmstrip": "Fotos abiertas",
    "flip": "Voltear",
    "flip_h": "Voltear horizontalmente",
    "flip_v": "Voltear verticalmente",
    "global_adjustments": "Ajustes globales",
    "gradient_filter": "Máscara gradiente",
    "guides": "guías",
    "history": "edición de la Historia",
    "info": "Info",
    "local_adjustments": "Ajustes locales",
    "looks": "filtros",
    "masks": "máscaras",
    "move_face": "Mover cara",
    "open": "abra Fotos",
    "pan": "Pan",
    "radial_filter": "Máscara radial",
    "redo": "Rehacer",
    "remove_face": "Retire la cara",
    "remove_text": "Retire texto",
    "rotate": "Girar",
    "rotate_ccw": "Rotación antihoraria",
    "rotate_cw": "Rotación horaria",
    "save": "Salvar Foto",
    "settings": "Ajustes",
    "show_original": "Mostrar original",
    "text": "Herramienta de texto",
    "undo": "Deshacer"
  },
  "tutorial": {
    "blacks": "Cambiar el brillo de las partes negras de la foto",
    "brightness": "Cambiar el brillo o \"gamma\" de la foto",
    "brush_mask_0": "Use su dedo para pintar con el pincel.",
    "brush_mask_1": "Máscara pincel sólo afecta a la zona de cepillado.",
    "clarify": "Aclarar",
    "clarify_1": "<b>reducir el empañamiento</b> <br/> La niebla es un problema común en las fotos y que es fácil de eliminar en Polarr. <br/> Para eliminar la niebla, aumentar <b>Dehaze</b> a 85",
    "clarify_2": "<b>recuperar la exposición</b> <br/> Dehaze veces se oscurece demasiado la fotografía. <br/> Para recuperar un poco de luz, aumentar la <b>exposición</b> a 30",
    "clarify_3": "<b>Aumentar contraste</b> <br/> La neblina es en su mayoría ido ahora, pero podemos aumentar la dinámica de luz un poco más. Elevar el <b>contraste,</b> el 60",
    "clarify_4": "<b>mejorar los detalles</b> <br/> Para mejorar los detalles en la foto, podemos utilizar la herramienta de Claridad. Aumentar la <b>claridad</b> para 40",
    "clarify_5": "¡Buen trabajo! Usted ha aprendido cómo hacer una foto borrosa mucho mejor en 4 sencillos pasos.",
    "clarify_description": "En este tutorial, aprenderá cómo reducir la niebla, definir contornos y añadir viveza a su tema.",
    "clarity": "Aumentar o suavizar los detalles de la fotografía",
    "color_mask_0": "máscara de color sólo afecta al color seleccionado.",
    "color_mask_1": "Arrastre para cambiar el color seleccionado.",
    "color_mask_2": "<b>Threshold</b> ajusta la sensibilidad de la selección.",
    "contrast": "Hace que los sujetos sean más distinguibles.",
    "curves": "Cambiar la curva de tonos de la foto",
    "deep_space": "Espacio profundo",
    "deep_space_1": "<b>recuperar la luz</b> <br/> ajuste de la exposición de Polarr simula la exposición de una cámara. <br/> Para recuperar la luz, ajustar la <b>exposición</b> a 100",
    "deep_space_2": "<b>mejorar los detalles</b> <br/> Vamos a hacer las estrellas se ven más impresionante. <br/> La adición de la claridad ayudará al revelar más detalles. <br/> Para mejorar los detalles, establecer <b>claridad</b> a 100",
    "deep_space_3": "<b>añadir profundidad</b> <br/> Para lograr un mayor enfoque hacia el centro de la imagen, podemos añadir una viñeta para oscurecer los bordes de la foto. <br/> Para añadir profundidad, disminuir la <b>viñeta</b> a -100",
    "deep_space_4": "<b>Viñeta suave</b> <br/> En este momento la viñeta se ve demasiado agudo. <br/> Para hacer la viñeta vea suave y sutil, <br/> aumentar la <b>pluma</b> al 100",
    "deep_space_5": "<b>recuperar destacados</b> <br/> Algunas de las estrellas en el borde de la foto se ven a oscuro. Podemos recuperarlos mediante el aumento <b>más destacado</b> a 100",
    "deep_space_6": "<b>Cambiar color</b> <br/> HSL le permite cambiar los atributos de un determinado color. Para cambiar el color del cielo, aumentar <b>la aguamarina Hue</b> a 100",
    "deep_space_7": "¡Buen trabajo! Usted ha aprendido cómo hacer un cielo estrellado parece realmente impresionante.",
    "deep_space_description": "En este tutorial, aprenderá formas de añadir profundidad, ajustar el color y el cambio de la exposición de un cielo nocturno.",
    "dehaze": "Añadir o eliminar la neblina de la foto",
    "denoise": "Eliminar el ruido de la foto",
    "diffuse": "Dar a la foto un efecto \"brillo\"",
    "distortion": "Distorsión",
    "distortion_1": "<b>corregir la distorsión</b> <br/> A menudo vemos edificios y paredes en busca torcida en las fotos. Esto es generalmente causado por el diseño de lentes de cámara de gran angular. Podemos arreglar esto fácilmente en Polarr con herramientas de distorsión. <br/> Para corregir la distorsión, aumentar la <b>distorsión vertical</b> de 70",
    "distortion_2": "<b>viñeta correcta</b> <br/> En adicional a las distorsiones ópticas, la mayoría de los lentes de las cámaras tienen consecuencias luz cerca de sus bordes. Podemos reducir estos bordes oscuros utilizando la herramienta de viñeta. <br/> Para corregir viñeta, aumentar la <b>Vignette</b> para 20",
    "distortion_3": "<b>mejorar los detalles</b> <br/> Por último, para mejorar los detalles en la foto, podemos utilizar la herramienta de la claridad. <br/> Aumentar la <b>claridad</b> para 100",
    "distortion_4": "¡Buen trabajo! Usted ha aprendido cómo corregir defectos de la lente comunes en 3 sencillos pasos.",
    "distortion_amount": "Agrega a las fotografías el efecto de ángulo amplio u ojo de pez",
    "distortion_description": "En este tutorial, aprenderá cómo corregir distorsiones de lente y una viñeta en tiros arquitectónicos.",
    "distortion_horizontal": "Cambie la distorsión a lo largo del eje horizontal",
    "distortion_vertical": "Cambie la distorsión a lo largo del eje vertical",
    "exposure": "Cambiar el brillo sin lavarse la foto",
    "face_0": "Polarr detecta automáticamente las caras y separa características individuales.",
    "face_1": "Utilice la herramienta Distorsionar para ajustar el tamaño y la posición de los rasgos faciales.",
    "fringing": "Agrega un efecto de “mareo” a su fotografía",
    "gradient_mask_0": "máscara de degradado afecta a áreas en una línea recta.",
    "gradient_mask_1": "Pellizcar para cambiar el tamaño. <br/> Arrastre para mover y rotar.",
    "gradient_mask_2": "<b>Invertir</b> cambia la dirección del gradiente.",
    "gradient_mask_3": "<b>Reflejar</b> hace que el gradiente de funcionar en ambas direcciones.",
    "grain": "Añadir un efecto de grano de la película",
    "highlights": "Cambiar el brillo de los aspectos más destacados sin lavarse la foto",
    "hsl": "Cambiar el tono, la saturación o la luminosidad de un color específico.",
    "hsl_header": "HSL",
    "hue_blue": "Cambia colores específicos al azul.",
    "hue_red": "Cambia colores específicos al rojo.",
    "luminance_blue": "Cambia el brillo del azul.",
    "luminance_red": "Cambia el brillo del rojo.",
    "mastering_adjustments": "El dominio de ajustes",
    "mosaic": "Dar a la foto un efecto de mosaico",
    "radial_mask_0": "máscara Radial sólo afecta a una zona circular.",
    "radial_mask_1": "Pellizcar para cambiar el tamaño. <br/> Arrastre para mover y rotar.",
    "radial_mask_2": "<b>Pluma</b> cambia la borrosidad de la máscara.",
    "radial_mask_3": "<b>Invertir</b> cambia el área afectada.",
    "saturation": "Incrementa o reduce la intensidad del color.",
    "saturation_blue": "Cambia el “efecto azul” de los colores azules.",
    "saturation_red": "Cambia el “efecto rojizo” de los colores rojos.",
    "shadows": "Cambiar el brillo de las sombras sin lavarse la foto",
    "sharpen": "Incrementa el contraste de los bordes para hacer que la fotografía luzca más nítida.",
    "temperature": "Hacer la foto más caliente o fría",
    "tint": "Ajustar el color junto al espectro magenta-verde",
    "toning_highlights": "Cambiar el color de los aspectos más destacados",
    "toning_shadows": "Cambiar el color de las sombras",
    "vibrance": "Cambiar la saturación sin lavarse los tonos de piel",
    "vignette_amount": "Desvanecen o resalta los bordes de la foto",
    "vignette_feather": "Cambie la falta de claridad de la viñeta",
    "vignette_highlights": "Deje que los aspectos más destacados muestran a través de la viñeta",
    "vignette_roundness": "Hacer la viñeta más redondo o cuadrado",
    "vignette_size": "Cambiar el tamaño de la viñeta",
    "white_balance": "balance de blancos",
    "white_balance_1": "<b>Balance de blancos correcto</b> <br/> La mayoría de las fuentes de luz (bombillas, linternas, etc.) no emiten el color puramente blanco. Esto puede hacer que los blancos en las fotos para tener un tono amarillo o azul. Para corregir esto, disminuir la <b>temperatura</b> hasta -30",
    "white_balance_2": "<b>Recuperar la luz de las sombras</b> <br/> A veces, partes de una foto es más oscura que la intención porque nuestros ojos pueden percibir más luz que la cámara. Podemos recuperar la luz de las sombras con la herramienta sombras. <br/> Aumenta a 100 <b>Sombras</b>",
    "white_balance_3": "<b>mejorar los detalles</b> <br/> Por último, para mejorar los detalles en la foto, podemos utilizar la herramienta de la claridad. <br/> Aumentar la <b>claridad</b> para 100",
    "white_balance_4": "¡Buen trabajo! Usted ha aprendido cómo eliminar tono amarillo y recuperar la luz de las sombras en 3 pasos simples.",
    "white_balance_description": "En este tutorial, aprenderá el concepto de balance de blancos y la forma en que se rectifique en puerta de la luz artificial.",
    "whites": "Cambia el brillo de la parte blanca de la foto"
  }
};
},{}],325:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "Ajouter",
    "add_image": "Choisissez l'image",
    "add_to_favorites": "Ajouter aux favoris",
    "aspect": "Aspect",
    "back": "Retour",
    "buy": "Acheter",
    "buy_full_version": "Acheter la version complète",
    "cancel": "Annuler",
    "check_again": "Revérifier",
    "choose_folder": "Choisissez le dossier",
    "clear": "Eclaircir",
    "clear_history": "Effacer l’historique",
    "close": "Fermer",
    "cloud": "Cloud",
    "continue_anyway": "Continuer de toute façon",
    "current_image": "En cours",
    "delete": "Supprimer",
    "device": "Périphérique",
    "dismiss": "Rejeter",
    "done": "Done",
    "dont_ask": "Ne plus demander",
    "duplicate": "Dupliquer",
    "enter_license": "Entrez la clé de licence",
    "enter_license_key": "Entrez la clé de licence",
    "exit": "Sortie",
    "export": "Exporter",
    "export_filter": "Enregistrer sous QR code",
    "forgot_password": "mot de passe oublié?",
    "free_membership": "adhésion gratuite",
    "get": "Obtenez",
    "get_license": "Obtenir une clé de licence",
    "get_started": "Commencer",
    "got_it": "Je l'ai",
    "have_account": "Vous avez déjà un compte?",
    "import": "Importer",
    "import_filter": "Filtre d'importation",
    "import_filter_photos": "Importer à partir de Photos",
    "import_photos": "Ouverture de photos",
    "info": "Info",
    "installed": "Installé",
    "invite": "Envoyer le lien",
    "learn_more": "Apprendre encore plus",
    "load_last_photo": "Charger dernière photo sur le lancement",
    "new": "Nouveau",
    "new_filter": "Créer un filtre",
    "next": "Prochain",
    "no": "Non",
    "no_thanks": "Non, merci",
    "not_really": "Pas vraiment",
    "not_this_time": "Pas en ce moment",
    "open_sample": "Ouvrir une photo d’échantillon",
    "paste_adjustments": "ajustements Coller",
    "pay_wechat": "Ouvrir WeChat",
    "prev": "Prev",
    "preview": "Aperçu",
    "pro": "Pro",
    "purchased": "Acheté",
    "remove": "Enlever",
    "remove_all_photos": "Retirer toutes les photos",
    "remove_photo": "Supprimer la photo",
    "remove_selected_photo": "Supprimer la photo sélectionnée",
    "reset": "Réinitialiser",
    "reset_all": "Réinitialiser tout",
    "reset_password": "réinitialiser le mot de passe",
    "reset_photos": "Réinitialiser étiquettes enregistrées et éditées",
    "reset_tips": "Réinitialiser les infobulles",
    "restore": "Restorer",
    "revert": "Revenir à l’original",
    "sample": "Simple",
    "save": "Sauvegarder",
    "save_qr": "Enregistrer QR code",
    "save_share": "Enregistrer + Partager",
    "scan_filter": "filtre d'analyse",
    "select_all": "Tout sélectionner",
    "select_changed": "Choisir les édités",
    "send": "Enjoyer",
    "send_verification": "Renvoyer vérification email",
    "show_in_folder": "Afficher dans le dossier",
    "show_me": "Montre moi",
    "signin": "S'identifier",
    "signout": "Se déconnecter",
    "signup": "Créer un compte Polarr",
    "skip": "Ignorer",
    "submit_license_key": "Soumettre clé de licence",
    "subscribe": "Souscrire",
    "subscribe_monthly": "Abonnez-vous mensuel",
    "subscribe_yearly": "Abonnez-vous annuel",
    "sure": "Ok,  d’accord",
    "try": "Essai",
    "try_again": "Réessayer",
    "unlock": "Déverrouiller",
    "unsubscribe": "Se désabonner",
    "update": "Mettre à jour",
    "whats_new": "Quoi de neuf",
    "yes": "OUI"
  },
  "dialogs": {
    "about": "About",
    "about_text": "Nous pensons que c'est important de permettre aux créateurs d'exprimer sans effort leurs points de vue et attitudes envers la vie, sans perdre la fidélité de leur désir créatif et inspirations.",
    "account": "Compte",
    "all_filters": "tous les filtres",
    "all_filters_text": "Débloquez toute la collection de Polarr de filtres haut de gamme",
    "backup_filters": "Synchroniser et sauvegarder tous vos filtres personnalisés afin que vous puissiez les utiliser dans des dispositifs et des plates-formes. Cette fonction nécessite que vous vous connectez avec un compte Polarr.",
    "backup_filters_title": "Filtres personnalisés de sauvegarde",
    "brush_limit": "La limite actuelle de masque de brosse est </limit>",
    "denoise": "Notre processus de débruitage en temps réel, il est facile d'éliminer le bruit et le grain dans les photos. contrôler séparément la couleur et le bruit de luminance composants.",
    "featured": "En vedette",
    "filter_collections": "Filtrer Collections",
    "filter_name": "Nommez ce filtre",
    "get_picky": "Fatigué de retouche photo? Essayez une nouvelle application de Polarr qui édite des photos avec l'intelligence artificielle!",
    "get_updates": "Abonnez-vous aux mises à jour",
    "get_updates_about": "Soyez le premier à essayer de nouvelles fonctionnalités! <br/> Ajouter votre adresse email et nous vous informerons sur les nouvelles fonctionnalités et les produits de Polarr.",
    "get_updates_thankyou": "Vous aurez maintenant des mises à jour sur les nouvelles fonctionnalités et les produits de Polarr! <br/> Si vous souhaitez arrêter de recevoir des mises à jour, vous pouvez \"désinscrire\" de votre e-mail à tout moment.",
    "help": "Aide",
    "invite": " Partagez le lien d'installation. Les caractéristiques Pro seront débloquées après trois personnes installées à l'aide de votre lien.",
    "local_adjustments": "ajustements locaux vous permet d'utiliser puissant jeu de Polarr des ajustements sur les zones ciblées. Comprend des outils de masquage Radial, Gradient, Couleur et Brush.",
    "lock_rotation": "rotation de verrouillage",
    "max_export_size": "Taille maximum d'exportation",
    "new_filter": "Enregistrer les modifications comme un filtre",
    "no_purchases": "Il n'y a aucun achat à restaurer.",
    "offline": "Votre appareil semble être déconnecté. Veuillez réessayer plus tard.",
    "photos": "Photos",
    "polarr_premium": "Polarr prime",
    "pro_all_filters": "caractéristiques Pro <br/> + <br/> tous les filtres",
    "pro_all_filters_text": "Débloquer toutes les fonctionnalités, y compris des outils pro et tous les filtres de prime de Polarr. Utilisez votre compte Polarr pour accéder à toutes les fonctionnalités sur d'autres plates-formes. Sauvegarde et synchroniser vos filtres personnalisés.",
    "pro_features": "Pro Tools",
    "pro_features_all": "Tous Pro Tools",
    "purchase": "Débloquez Radial, Gradient, couleur, Pinceau, Denoise, et des outils de texte. Sauvegarde et synchroniser vos filtres personnalisés.",
    "purchase_failed": "Achat annulé ",
    "purchase_header": "Débloquer toutes les fonctionnalités pro",
    "purchase_successful": "Vous avez maintenant accès à la version pro. ",
    "purchase_unavailable": "Achats ne sont pas disponibles en ce moment. Désolé. ",
    "purchases": "Achats",
    "rate_review": "Notez et évaluez",
    "rename_filter": "renommez Filter",
    "restore_failed": "Votre compte n'a pas accès complet de fonctionnalités.",
    "restore_successful": "Tous vos achats précédents sont maintenant rétablis.",
    "review": "Bénificiez de Polarr Photo Editor jusqu’ici ?",
    "review_no": "Pourriez-vous nous donner quelques commentaires? ?",
    "review_yes": "Que diriez-vous d’un petit commenter sur le App Store avant ?",
    "save_adjustments": "Faites un filtre personnalisé, vous pouvez partager.",
    "send_feedback": "Envoyer une réponse",
    "signin_polarr_account": "Connectez-vous à votre compte Polarr",
    "store": "boutique",
    "subscribe": "Abonnez-vous à <strong>Polarr prime</strong>",
    "subscribe_access": "pour un accès complet à:",
    "subscribe_cancel": "Vous pouvez «désabonner» dans Réglages à tout moment",
    "subscribe_caveat": "* Pro caractéristiques comprennent l'ajustement de gradient, réglage radial, la couleur de masquage, et la couleur de brossage. Votre adhésion sera applicable sur tous les appareils et plateformes cette année.",
    "subscribe_china": "Abonnez-vous à polaxiongkeji compte WeChat et faire un paiement en WeChat, puis connectez-vous avec votre compte Polarr pour activer les fonctionnalités pro.",
    "subscribe_error": "Oops! Il y avait abonnant à Polarr erreur. Réessayer?",
    "subscribe_features": "<li> caractéristiques Pro (4 outils locaux d'ajustement) </li><li> Accéder à tous les paquets de filtres 12 premium </li><li> Restez à jour pour les formats RAW </li><li> Le nouveau contenu ajouté chaque mois! </li><li> adhésion d'accès sur toutes les plateformes * </li>",
    "subscribe_notify": "Avertissez-moi quand mon adhésion Polarr Premium peut être utilisé sur plusieurs plates-formes et appareils",
    "subscribe_price": "</price>",
    "subscribe_refund": "Vous avez déjà acheté quelque chose avec Polarr et vous êtes admissible à",
    "subscribe_success": "Merci de votre abonnement à Polarr Prime!",
    "subscribe_unlock_success": "Vous êtes maintenant un membre Premium Polarr!",
    "subscription_expired": "Votre adhésion Polarr prime a expiré",
    "text_tool": "outil Texte vous permet d'ajouter du texte et des formes à des photos. Avec des fonctionnalités telles que la lettre et l'espacement des lignes, l'alignement et l'emballage, vous avez le plein contrôle de votre texte. Nous avons même inclus des fonctionnalités avancées comme mise à l'échelle de vecteur dynamique et un ajustement de mélange unique.",
    "text_tool_description": "Ajouter et modifier le texte à l'écran. Tourner, changer de couleur, réglez le mélange, et même de régler l'espacement des lettres.",
    "thankyou": "Je vous remercie!",
    "unlock_all": "Tout débloquer",
    "unlock_all_note": "Connectez votre compte pro Polarr ou entrez la clé de licence.",
    "unlock_alternate": "Vous pouvez également mettre à niveau en vous connectant à un compte existant ou en saisissant une clé de licence",
    "unlock_failed": "Caractéristiques Pro ne sont pas disponibles pour ce compte.",
    "unlock_success": "Vous avez maintenant accès à toutes les fonctionnalités.",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> et <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> , la meilleure source gratuitement et haute résolution photographie de stock, se sont associés pour vous apporter une collection de 10 filtres haut de gamme créés par les membres de la communauté photographe Unsplash. </p>",
    "upgrade": "Mise à niveau vers la version complète pour déverrouiller toutes les fonctionnalités suivantes.",
    "upgrade_caveat": "* 12 paquets de filtres supplémentaires comprennent des simulations de classique de film, Vintage, Film infrarouge, tons de peau et beaucoup d'autres ajustements appropriés pour divers sujets et des scènes.",
    "upgrade_features": "<li> Gradient ajustements </li><li> ajustements circulaires </li><li> masquage des couleurs </li><li> réglage brossage </li><li> 12 packs filtrants premium supplémentaires </li><li> Batch photo export </li>",
    "use_polarrfilter": "Utilisez <strong>#polarrfilter</strong> pour se faire remarquer!",
    "user_in_paid": "Vous pouvez vous connecter à Polarr sur d'autres plates-formes pour accéder à toutes les fonctionnalités.",
    "user_in_unpaid": "Si vous déverrouillez tout, vous serez en mesure de se connecter à Polarr sur d'autres plateformes et accéder à toutes les fonctionnalités.",
    "verification_sent": "Un email de vérification a été envoyé. <br/> S'il vous plaît vérifier votre e-mail pour obtenir des instructions de vérification.",
    "verify_account": "Pour vous connecter, s'il vous plaît vérifier votre compte. <br/> Vérifiez votre email pour obtenir des instructions de vérification.",
    "webgl_not_supported": "Nous sommes extrêmement désolés mais votre     périphérique ne satisfait pas l'exigence graphique de Polarr, s'il vous plaît mettez à jour vers le dernier système d'exploitation et réessayez. "
  },
  "labels": {
    "adjustments": "Réglages",
    "after": "Après",
    "alignment": "Alignement",
    "all": "Tout",
    "amount": "Montant",
    "angle": "Angle",
    "aqua": "Aqua",
    "artwork": "Oeuvre",
    "auto": "Auto",
    "balance": "Balance",
    "basic": "Basique",
    "batch_rename": "Batch rename",
    "batch_resize": "Batch resize",
    "before": "Avant",
    "best": "Meilleur",
    "blacks": "Noirs",
    "blend": "Mélange",
    "blend_mode": "Mode de fusion",
    "blue": "Bleu",
    "blur": "flou",
    "brightness": "Luminosité",
    "brush_flow": "Brush débit",
    "brush_opacity": "Brush Opacité",
    "brush_size": "Taille de la brosse",
    "clarity": "Clarté",
    "collection": "Collection",
    "color": "Couleur",
    "contrast": "Contraste",
    "crop": "Crop",
    "crop_size": "dimensions des cultures",
    "curves": "Courbes",
    "custom": "Personnalisé",
    "dehaze": "Dehaze",
    "denoise": "Denoise",
    "detail": "Détail",
    "diffuse": "Diffuser",
    "distortion": "Distorsion",
    "distortion_amount": "Distorsion",
    "distortion_horizontal": "Distortion Horizontal",
    "distortion_vertical": "Distorsion Verticale",
    "dividers": "Intercalaires",
    "edit": "modifier",
    "effects": "Effets",
    "email": "Email",
    "enable": "Activer",
    "estimated_size": "Taille estimée",
    "exposure": "Exposition",
    "eyes": "Les yeux",
    "face": "Visage",
    "favorites": "Favoris",
    "feather": "Plume",
    "fit": "Fit",
    "font": "Police de caractère",
    "format": "Format",
    "free": "Gratuit",
    "frequent": "Le plus utilisé",
    "fringing": "Franges",
    "grain": "Grain",
    "green": "Vert",
    "guides": "Guides",
    "high": "Haut",
    "highlights": "Points forts",
    "histogram": "Histogramme",
    "history": "Histoire",
    "hsl": "HSL",
    "hue": "Teinte",
    "hue_blue": "HSL – teinte bleue",
    "hue_red": "HSL – teinte rouge",
    "invert": "Inverser",
    "lens": "Objectif",
    "lens_distortion": "Distorsion de l'objectif",
    "letter_spacing": "L'espacement des lettres",
    "license_key": "Clé de licence",
    "light": "Lumière",
    "line_spacing": "Interligne",
    "lips": "Lèvres",
    "look": "Filtre",
    "looks": "Filtres",
    "low": "Faible",
    "luminance": "Luminance",
    "luminance_blue": "HSL – luminance bleue",
    "luminance_red": "HSL - luminance",
    "magenta": "Magenta",
    "master": "Maitre",
    "max_size": "dimensions maxi",
    "med": "Med",
    "mix": "Tout régler",
    "mosaic": "Pixéliser",
    "name": "prénom",
    "none": "Aucun",
    "nose": "Nez",
    "opacity": "Opacité",
    "optics": "Optique",
    "options": "options de",
    "orange": "Orange",
    "original": "Original",
    "outline": "Contour",
    "password": "Mot de passe",
    "position": "Position",
    "prefix": "secteur Prefix",
    "presets": "Presets",
    "preview": "Aperçu",
    "purple": "Violet",
    "quality": "Qualité",
    "ratio": "Ratio",
    "red": "Rouge",
    "reflect": "Réfléchir",
    "resolution": "Résolution",
    "rgb": "RGB",
    "roundness": "Rondeur",
    "saturation": "Saturation",
    "saturation_blue": "HSL – saturation bleue",
    "saturation_red": "HSL – saturation rouge",
    "sayings": "énonciations",
    "shadow": "Ombre",
    "shadows": "Ombres",
    "shape": "Forme",
    "shapes": "Formes",
    "shapes_presets": "Formes et Presets",
    "sharpen": "Aigiiser",
    "size": "Taille",
    "skin": "Peau",
    "smoothness": "Lissage",
    "split_tone": "Tonique",
    "teeth": "Les dents",
    "temperature": "Temp",
    "text": "Texte",
    "texture": "Texture",
    "threshold": "Seuil",
    "tint": "Teinte",
    "tone": "Ton",
    "toning": "Tonique",
    "use_prefix": "Utilisez le préfixe",
    "use_watermark": "Utiliser filigrane",
    "user_name": "Nom d'utilisateur",
    "vibrance": "Vibration",
    "vignette": "Vignette",
    "vignette_amount": "Vignette",
    "watermark_image": "Watermark image",
    "wb": "WB",
    "whitening": "Blanchiment",
    "whites": "Blancs",
    "width": "Largeur",
    "yellow": "Jaune"
  },
  "menus": {
    "batch_export": "Batch export",
    "change_language": "Changer le language",
    "copy_edit": "Copier Edits",
    "edit_watermark": "Modifier Watermark",
    "export": "Format, Redimensionner, Watermark",
    "filters_users": "Filtres par Polarrists",
    "guide": "Guide",
    "image_quality": "Image qualité",
    "image_settings": "Paramètres d’image",
    "language": "Language",
    "more_filters": "Plus de filtres",
    "open": "Ouvrir",
    "open_in": "Ouvrir",
    "paste_edit": "Edits Coller",
    "remove": "Supprimer",
    "rename": "Renommer",
    "restore_purchases": "Restorer mes achats",
    "save_cameraroll": "Enregistrer sur pellicule",
    "save_copy": "Sauvegarder une copie",
    "settings": "Paramètres",
    "share": "Partager",
    "share_edits": "Partager les modifications",
    "share_filter": "Partager filtre",
    "show_instructions": "Guide d’ajustements",
    "show_intro": "Afficher l'intro",
    "show_video_tutorial": "Video tutorial",
    "single_export": "Exportation"
  },
  "onboarding": {
    "become_master": "Devenez un maître de retouche photo.",
    "step_0": "Bienvenue à Polarr! <br/> Permet de commencer par l'affichage des filtres.",
    "step_1": "Appuyez sur pour sélectionner un filtre. <br/> panneau Swipe pour plus de filtres.",
    "step_10": "Vous pouvez faire glisser pour réorganiser les icônes d'outils.",
    "step_2": "Robinet <i class='icon-check'></i> pour appliquer le filtre. <br/> Utilisez le curseur pour régler l'intensité du filtre.",
    "step_3": "Chaque filtre est complètement modifiable. <br/> Allez à Ajustements pour voir vos modifications.",
    "step_4": "Vous pouvez modifier un réglage en faisant glisser le curseur.",
    "step_4a": "Appuyez longuement puis faites glisser pour ajuster le curseur.",
    "step_5": "Voir toutes les modifications que vous avez apportées dans le panneau de l'histoire.",
    "step_6": "Vous pouvez annuler une modification et revenir à tout moment dans l'histoire.",
    "step_7": "Enregistrez votre image ici. <br/> Vous pouvez redimensionner, ajouter un filigrane ou d'enregistrer plusieurs images.",
    "step_7a": "Enregistrez votre image ici. <br/> Vous pouvez également partager et enregistrer plusieurs images.",
    "step_8": "Ouvrez vos propres photos ici.",
    "step_9": "Regardez vos photos ouvertes dans le panneau de photos."
  },
  "status": {
    "account_suspended": "Votre compte est mis en attente en raison d'une activité suspecte. Pour plus d' informations, s'il vous plaît vérifier votre e - mail ou contacter <a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "Robinet <i class='icon-add-face'></i> d'ajouter un visage",
    "adjustment_copied": "Ajustements Copié",
    "adjustment_pasted": "Ajustements Pasted",
    "demo_mode": "Cette version de Polarr Photo Editor a été créé spécifiquement à des fins de démonstration",
    "downloading_font": "police Téléchargement",
    "drag_icons": "Faites glisser les icônes pour réorganiser les outils",
    "drop_files": "Déposer les fichiers à ouvrir",
    "email_invalid": "Désolé, il ressemble à cette adresse e-mail est pas valide.",
    "email_reset": "Un lien de réinitialisation a été envoyé. <br/> Merci de consulter vos emails.",
    "exporting_filter": "filtre Enregistrement",
    "field_invalid": "est pas valide.",
    "file_exists": "Cette photo est déjà ouvert",
    "file_not_supported": "Désolé, ce type de fichier est pas pris en charge",
    "filter_exported": "Votre filtre a été enregistré au rôle de la caméra.",
    "filter_saved": "Filtre sauvé",
    "filters_sync_error": "filtres Erreur de synchronisation",
    "filters_synced": "Filtres synchronisés",
    "filters_syncing": "Synchroniser filtres ...",
    "import_filter_error": "Désolé, ce filtre ne pouvait pas être importé",
    "import_limit": "Polarr ne peut ouvrir jusqu'à 50 photos, s'il vous plaît supprimer quelques photos avant d'ouvrir plus.",
    "importing_filter": "filtre Importation",
    "importing_photo": "photo d'ouverture",
    "importing_photos": "Photos d'ouverture",
    "invalid_credentials": "e-mail ou mot de passe incorrect",
    "loading_photo": "Chargement photo",
    "login_success": "Vous avez été connecté avec succès!",
    "no_faces": "Aucun visages détectés.",
    "no_user": "ne pas exister à cet utilisateur.",
    "offline": "Impossible de connecter au serveur.",
    "photo_load_error": "Impossibler de charger les photos",
    "photo_saved": "Photo enregistrée",
    "photo_shared": "Photo partagée",
    "photos_saved": "Photos enregistrées",
    "purchase_required": "Achat requis",
    "raw_failed": "Impossible de lire l'image RAW. support RAW est encore au stade expérimental.",
    "registration_success": "Vous avez réussi à faire un nouveau compte! <br/> Pour vous connecter à d'autres appareils, vous devez d'abord vérifier votre compte. Nous vous avons envoyé un e-mail avec des instructions de vérification.",
    "saving_photo": "photo Enregistrement",
    "saving_photo_error": "Erreur enregistrement photo :(",
    "saving_photo_error_sd": "Polarr n'est pas autorisé à accéder à votre carte SD ou mémoire interne. S'il vous plaît vérifier la dernière mise à jour système Android pour résoudre le problème.",
    "saving_photos": "Enregistrement de photos",
    "server_offline": "Désolé, le serveur ne répond pas. <br/> Êtes-vous connecté à Internet?",
    "sharing_photo": "Partage de photos",
    "sharing_photo_error": "Erreur de partage de la photo :(",
    "show_me_how": "Montrez-moi comment utiliser </thing>",
    "supported_files": "Polarr soutient JPG, PNG, BMP, et certains fichiers RAW",
    "unlocks_remaining": "Tu as </unlocks> déverrouille restant",
    "user_exists": "Il existe déjà un compte avec cet email ou le nom d&#39;utilisateur.",
    "wrong_password": "Mauvais mot de passe"
  },
  "tooltips": {
    "add_face": "Ajouter Visage",
    "add_text": "Ajouter du texte",
    "adjustments": "Ajustements",
    "apply_adjustments": "Appliquer les réglages",
    "auto_adjust": "Ajustements automatiques",
    "brush_filter": "Brush Mask",
    "color_filter": "Masque de couleur",
    "crop": "xouper",
    "crop_straighten": "Crop & Angle",
    "current_adjustments": "Ajustements actuels",
    "double_tap_edit": "Appuyez deux fois pour modifier",
    "drag_watermark": "Faites glisser pour déplacer filigrane",
    "eraser": "Gomme",
    "face": "Outils de visage",
    "filmstrip": "Photos ouvert",
    "flip": "Tourner",
    "flip_h": "Tourner horizontalement",
    "flip_v": "Tourner verticalement",
    "global_adjustments": "Ajustements globaux",
    "gradient_filter": "Gradient Mask",
    "guides": "Guides",
    "history": "Histoire Édition",
    "info": "Info",
    "local_adjustments": "Adjustments Mocaux",
    "looks": "Filtres",
    "masks": "Masques",
    "move_face": "Déplacer la face",
    "open": "Ouvert Photos",
    "pan": "Casserole",
    "radial_filter": "Masque radial",
    "redo": "Refaire",
    "remove_face": "Retirez le visage",
    "remove_text": "Supprimer texte",
    "rotate": "Faire tourner",
    "rotate_ccw": "Tourner le CCW",
    "rotate_cw": "Faire pivoter le CW",
    "save": "Enregistrer la photo",
    "settings": "Paramètres",
    "show_original": "Afficher l'original",
    "text": "Outil Texte",
    "undo": "Annuler"
  },
  "tutorial": {
    "blacks": "Changer la luminosité des parties noires de la photo",
    "brightness": "Changer la luminosité ou «gamma» de la photo",
    "brush_mask_0": "Utilisez votre doigt pour peindre avec le pinceau.",
    "brush_mask_1": "masque de brosse affecte uniquement la zone brossé.",
    "clarify": "Clarifier",
    "clarify_1": "<b>Réduire</b> le <b>brouillard</b> <br/> Le brouillard est un problème commun dans les photos et il est facile à enlever en Polarr. <br/> Pour supprimer le brouillard, augmenter <b>Dehaze</b> à 85",
    "clarify_2": "<b>Récupérer</b> l' <b>exposition</b> <br/> Dehaze obscurcit parfois la photo trop. <br/> Pour récupérer un peu de lumière, d' augmenter l' <b>exposition</b> à 30",
    "clarify_3": "<b>Augmenter le contraste</b> <br/> La brume est surtout maintenant disparu, mais nous pouvons renforcer la dynamique de lumière un peu plus. Augmenter le <b>contraste</b> 60",
    "clarify_4": "<b>Améliorer</b> les <b>détails</b> <br/> Pour améliorer les détails dans la photo, nous pouvons utiliser l'outil Clarity. Augmenter la <b>clarté</b> à 40",
    "clarify_5": "Bon travail! Vous avez appris comment faire une photo floue beaucoup mieux en 4 étapes simples.",
    "clarify_description": "Dans ce tutoriel, vous apprendrez comment réduire le brouillard, définir les contours et ajouter éclat à votre sujet.",
    "clarity": "Augmenter ou lisser les détails de la photo",
    "color_mask_0": "masque de couleur affecte uniquement la couleur sélectionnée.",
    "color_mask_1": "Faites glisser pour changer la couleur sélectionnée.",
    "color_mask_2": "<b>Seuil</b> définit la sensibilité de la sélection.",
    "contrast": "Rendre la scène plus distinguable.",
    "curves": "Modifier la courbe de tonalité de la photo",
    "deep_space": "Espace profond",
    "deep_space_1": "<b>Récupérer</b> la <b>lumière</b> <br/> Le réglage de l'exposition de Polarr simule l'exposition d'un appareil photo. <br/> Pour récupérer la lumière, régler l' <b>exposition</b> à 100",
    "deep_space_2": "<b>Améliorer</b> les <b>détails</b> <br/> Faisons en sorte que les étoiles semblent plus impressionnant. <br/> Ajout de clarté aidera en révélant plus de détails. <br/> Pour améliorer les détails, définissez <b>Clarity</b> 100",
    "deep_space_3": "<b>Ajoutez</b> de la <b>profondeur</b> <br/> Pour mettre davantage l'accent sur le centre de l'image, nous pouvons ajouter une vignette pour assombrir les bords de la photo. <br/> Pour ajouter de la profondeur, diminuer <b>Vignette</b> à -100",
    "deep_space_4": "<b>Vignette lisse</b> <br/> En ce moment, la vignette semble trop forte. <br/> Pour rendre la vignette aspect lisse et subtile, <br/> augmenter à 100 <b>Feather</b>",
    "deep_space_5": "<b>Récupérer</b> les <b>faits saillants</b> <br/> Certains des étoiles dans le bord de la photo regarder à l'obscurité. Nous pouvons les récupérer en augmentant à 100 <b>Faits saillants</b>",
    "deep_space_6": "<b>Changer de couleur</b> <br/> HSL vous permet de modifier les attributs d'une certaine couleur. Pour changer la couleur du ciel, augmenter <b>Aqua Hue</b> à 100",
    "deep_space_7": "Bon travail! Vous avez appris comment faire un ciel étoilé regarder vraiment impressionnant.",
    "deep_space_description": "Dans ce tutoriel, vous apprendrez des moyens d'ajouter la profondeur, ajuster la couleur et le changement de l'exposition d'un ciel nocturne.",
    "dehaze": "Ajouter ou supprimer la brume de la photo",
    "denoise": "Retirer le bruit de la photo",
    "diffuse": "Donnez la photo un effet \"lueur\"",
    "distortion": "Distorsion",
    "distortion_1": "<b>Correction</b> de la <b>distorsion</b> <br/> Nous voyons souvent des bâtiments et des murs en regardant de travers dans les photos. Cela est généralement causé par la conception de grand-angle lentilles de caméra. Nous pouvons facilement résoudre ce problème en Polarr avec des outils de distorsion. <br/> Pour corriger la distorsion, augmentez <b>Vertical Distortion</b> 70",
    "distortion_2": "<b>Corriger vignette</b> <br/> En plus des distorsions optiques, la plupart des lentilles de caméra ont la lumière retombées à proximité de leurs bords. Nous pouvons réduire ces bords plus sombres en utilisant l'outil de vignette. <br/> Pour corriger vignette, augmenter <b>Vignette</b> à 20",
    "distortion_3": "<b>Améliorer</b> les <b>détails</b> <br/> Enfin, pour améliorer les détails dans la photo, nous pouvons utiliser l'outil de clarté. <br/> Augmenter la <b>clarté</b> à 100",
    "distortion_4": "Bon travail! Vous avez appris à corriger les défauts de lentilles communes en 3 étapes simples.",
    "distortion_amount": "Faire un grand angle ou fish eye des photos en regardant",
    "distortion_description": "Dans ce tutoriel, vous allez apprendre à corriger les distorsions de lentille et vignette dans les coups architecturaux.",
    "distortion_horizontal": "Changer la distorsion le long de l'axe horizontal",
    "distortion_vertical": "Changer la distorsion le long de l'axe vertical",
    "exposure": "Changer la luminosité sans laver la photo",
    "face_0": "Polarr détecte automatiquement les visages et sépare les caractéristiques individuelles.",
    "face_1": "Utilisez l'outil de distorsion pour ajuster la taille et la position des traits du visage.",
    "fringing": "Ajouter du vertige à votre photo.",
    "gradient_mask_0": "masque dégradé affecte les zones dans une ligne droite.",
    "gradient_mask_1": "Pincez pour redimensionner. <br/> Faites glisser pour déplacer et faire pivoter.",
    "gradient_mask_2": "<b>Inverser</b> commute la direction de gradient.",
    "gradient_mask_3": "<b>Reflect</b> rend le gradient de fonctionner dans les deux sens.",
    "grain": "Ajouter un effet de grain de film",
    "highlights": "Changer la luminosité des points forts sans se laver la photo",
    "hsl": "Changer la teinte, la saturation ou la légèreté d'une couleur spécifique.",
    "hsl_header": "HSL",
    "hue_blue": "Couleur de shift bleue pour ressembler aux autres couleurs.",
    "hue_red": "Couleur de shift rouge pour ressembler aux autres couleurs.",
    "luminance_blue": "Changer la luminosité du bleu.",
    "luminance_red": "Change la luminosité du rouge.",
    "mastering_adjustments": "Maîtriser Ajustements",
    "mosaic": "Donnez la photo un effet de mosaïque",
    "radial_mask_0": "masque radial affecte uniquement une zone circulaire.",
    "radial_mask_1": "Pincez pour redimensionner. <br/> Faites glisser pour déplacer et faire pivoter.",
    "radial_mask_2": "<b>Feather</b> change le flou du masque.",
    "radial_mask_3": "<b>Inverser</b> commute la zone touchée.",
    "saturation": "Rendez votre photo plus ou moins colorisée.",
    "saturation_blue": "Changer le Colorfulness du bleu.",
    "saturation_red": "Changer le  Colorfulness du rouge.",
    "shadows": "Changer la luminosité de l'ombre sans se laver la photo",
    "sharpen": "Rendrez votre photo paraitre brillante et croquante.",
    "temperature": "Faire la photo chaud ou plus froid",
    "tint": "Régler la couleur le long du spectre magenta-vert.",
    "toning_highlights": "Changer la couleur des points saillants",
    "toning_shadows": "Changer la couleur de l'ombre",
    "vibrance": "Changer la saturation sans lavage des tons de peau",
    "vignette_amount": "Fade ou met en évidence les bords de la photo",
    "vignette_feather": "Changer le flou de la vignette",
    "vignette_highlights": "Que les faits saillants montrent à travers la vignette",
    "vignette_roundness": "Faire la vignette plus rond ou carré",
    "vignette_size": "Modifier la taille de la vignette",
    "white_balance": "Balance des blancs",
    "white_balance_1": "<b>Balance</b> des <b>blancs correcte</b> <br/> La plupart des sources de lumière (ampoules, lampes de poche, etc.) ne dégagent pas de couleur blanc pur. Cela peut provoquer des blancs dans les photos pour avoir une dominante jaune ou bleu. Pour corriger cela, diminuer la <b>température</b> à -30",
    "white_balance_2": "<b>Récupérer</b> la <b>lumière de</b> l' <b>ombre</b> <br/> Parfois, des parties d'une photo sont plus sombres que prévu parce que nos yeux peuvent percevoir plus de lumière que la caméra. Nous pouvons récupérer la lumière de l'ombre à l'aide de l'outil ombres. <br/> Augmenter <b>Ombres</b> 100",
    "white_balance_3": "<b>Améliorer</b> les <b>détails</b> <br/> Enfin, pour améliorer les détails dans la photo, nous pouvons utiliser l'outil de clarté. <br/> Augmenter la <b>clarté</b> à 100",
    "white_balance_4": "Bon travail! Vous avez appris comment enlever fonte jaune et récupérer la lumière de l'ombre en 3 étapes simples.",
    "white_balance_description": "Dans ce tutoriel, vous apprendrez le concept de la balance des blancs et de la façon de corriger la lumière artificielle de la porte.",
    "whites": "Modifie la luminosité des parties blanches de la photo"
  }
};
},{}],326:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "Tambahkan",
    "add_image": "Pilih gambar",
    "add_to_favorites": "Tambahkan ke Favorit",
    "aspect": "Aspek",
    "back": "Kembali",
    "buy": "Beli",
    "buy_full_version": "Membeli versi lengkap",
    "cancel": "Batalkan",
    "check_again": "Periksa lagi",
    "choose_folder": "Pilih folder",
    "clear": "Hapus",
    "clear_history": "Hapus riwayat",
    "close": "Tutup",
    "cloud": "Cloud",
    "continue_anyway": "Tetap lanjutkan",
    "current_image": "Saat ini",
    "delete": "Hapus",
    "device": "Device",
    "dismiss": "Memberhentikan",
    "done": "Selesai",
    "dont_ask": "Jangan tanya lagi",
    "duplicate": "Duplikat",
    "enter_license": "Masukan kode lisensi",
    "enter_license_key": "Masukan kode lisensi",
    "exit": "Keluar",
    "export": "Ekspor",
    "export_filter": "Simpan sebagai kode QR",
    "forgot_password": "Lupa kata sandi?",
    "free_membership": "keanggotaan gratis",
    "get": "Mendapatkan",
    "get_license": "Dapatkan kunci lisensi",
    "get_started": "Memulai",
    "got_it": "Mengerti",
    "have_account": "Sudah memiliki akun?",
    "import": "Impor",
    "import_filter": "Filter impor",
    "import_filter_photos": "Impor dari Foto",
    "import_photos": "terbuka foto",
    "info": "Info",
    "installed": "Terinstal",
    "invite": "Kirimkan link",
    "learn_more": "Belajarlah lagi",
    "load_last_photo": "Memuat foto terakhir pada peluncuran",
    "new": "Baru",
    "new_filter": "Buat filter",
    "next": "Berikutnya",
    "no": "Tidak",
    "no_thanks": "Tidak, terima kasih",
    "not_really": "Tidak terlalu",
    "not_this_time": "Tidak kali ini",
    "open_sample": "Buka foto contoh",
    "paste_adjustments": "penyesuaian Paste",
    "pay_wechat": "terbuka WeChat",
    "prev": "Sebelumnya",
    "preview": "Preview",
    "pro": "Pro",
    "purchased": "Terbeli",
    "remove": "Hilangkan",
    "remove_all_photos": "Menghapus semua foto",
    "remove_photo": "Hapus foto",
    "remove_selected_photo": "Hapus foto yang dipilih",
    "reset": "Reset",
    "reset_all": "Reset Semua",
    "reset_password": "Reset password",
    "reset_photos": "Ulang label disimpan dan diedit",
    "reset_tips": "Tips reset",
    "restore": "Kembalikan",
    "revert": "Kembalikan ke asli",
    "sample": "Contoh",
    "save": "Simpan",
    "save_qr": "Simpan kode QR",
    "save_share": "Simpan + Share",
    "scan_filter": "Filter Scan",
    "select_all": "Pilih semua",
    "select_changed": "Pilih yang diedit",
    "send": "Kirim",
    "send_verification": "Mengirim ulang email verifikasi",
    "show_in_folder": "Tampilkan di dalam folder",
    "show_me": "Perlihatkan pada saya",
    "signin": "Masuk",
    "signout": "Keluar",
    "signup": "Buat akun Polarr",
    "skip": "Lewati",
    "submit_license_key": "Menyerahkan kunci lisensi",
    "subscribe": "Berlangganan",
    "subscribe_monthly": "berlangganan bulanan",
    "subscribe_yearly": "berlangganan tahunan",
    "sure": "Oke, tentu",
    "try": "Mencoba",
    "try_again": "Coba lagi",
    "unlock": "Buka",
    "unsubscribe": "berhenti berlangganan",
    "update": "Perbarui",
    "whats_new": "Apa yang baru",
    "yes": "Ya"
  },
  "dialogs": {
    "about": "Tentang",
    "about_text": "Kami percaya penting halnya untuk memungkinkan para pencipta untuk mengekspresikan perspektif dan kelakukan mereka tentang kehidupan, tanpa kehilangan fidelitas keinginan dan inspirasi kreatif mereka.",
    "account": "Rekening",
    "all_filters": "semua filter",
    "all_filters_text": "Membuka seluruh koleksi Polarr untuk filter premium",
    "backup_filters": "Sinkronisasi dan back up semua filter kustom Anda sehingga Anda dapat menggunakannya di seluruh perangkat dan platform. Fitur ini mengharuskan Anda login dengan akun Polarr.",
    "backup_filters_title": "Filter Kustom cadangan",
    "brush_limit": "Batas saat Brush Mask </limit>",
    "denoise": "Proses denoise real-time kami membuatnya mudah untuk menghapus kebisingan dan biji-bijian di foto. Secara terpisah mengontrol warna dan luminance noise komponen.",
    "featured": "unggulan",
    "filter_collections": "Filter Koleksi",
    "filter_name": "Nama filter ini",
    "get_picky": "Lelah editing foto? Mencoba aplikasi baru Polarr yang mengedit foto dengan Artificial Intelligence!",
    "get_updates": "Berlangganan update",
    "get_updates_about": "Jadilah yang pertama untuk mencoba fitur baru! <br/> Tambahkan alamat email Anda, dan kami akan memberitahu Anda tentang fitur baru dan produk dari Polarr.",
    "get_updates_thankyou": "Anda sekarang akan mendapatkan update tentang fitur baru dan produk dari Polarr! <br/> Jika Anda ingin berhenti menerima pembaruan, Anda dapat \"berhenti berlangganan\" dari email Anda setiap saat.",
    "help": "Bantuan",
    "invite": "Bagikan link instalasi. Fitur pro akan terbuka setelah tiga orang menginstal menggunakan link Anda.",
    "local_adjustments": "penyesuaian lokal memungkinkan Anda menggunakan set kuat Polarr untuk penyesuaian pada daerah target. Termasuk alat masking Radial, Gradient, Color, dan Brush.",
    "lock_rotation": "rotasi kunci",
    "max_export_size": "Ukuran ekspor Max",
    "new_filter": "Simpan suntingan sebagai filter",
    "no_purchases": "Tidak ada pembelian untuk mengembalikan.",
    "offline": "Device Anda offline. Mohon coba lagi nanti.",
    "photos": "foto-foto",
    "polarr_premium": "Polarr Premium",
    "pro_all_filters": "fitur Pro <br/> + <br/> semua filter",
    "pro_all_filters_text": "Membuka semua fitur termasuk alat pro dan semua filter premium Polarr ini. Gunakan akun Polarr Anda untuk mengakses semua fitur pada platform lainnya. Backup dan sinkronisasi filter kustom Anda.",
    "pro_features": "Pro Tools",
    "pro_features_all": "Semua Pro Tools",
    "purchase": "Membuka Radial, Gradient, Color, Brush, Denoise, dan alat-alat Teks. Backup dan sinkronisasi filter kustom Anda.",
    "purchase_failed": "Pembelian dibatalkan",
    "purchase_header": "Buka semua fitur pro",
    "purchase_successful": "Anda kini memiliki akses ke versi pro.",
    "purchase_unavailable": "Pembelian tidak tersedia saat ini. Maaf.",
    "purchases": "Pembelian",
    "rate_review": "Tingkat dan review",
    "rename_filter": "Ganti nama Filter",
    "restore_failed": "Akun Anda tidak memiliki akses fitur penuh.",
    "restore_successful": "Semua pembelian Anda sebelumnya kini dipulihkan.",
    "review": "Menikmati Polarr Editor Foto sejauh ini?",
    "review_no": "Apakah Anda ingin memberikan kami masukan?",
    "review_yes": "Bagaimana dengan rating di App Store?",
    "save_adjustments": "Membuat filter kustom Anda dapat berbagi.",
    "send_feedback": "Kirim masukan",
    "signin_polarr_account": "Masuk ke akun Polarr Anda",
    "store": "Toko",
    "subscribe": "Mendaftar untuk <strong>Polarr Premium</strong>",
    "subscribe_access": "untuk akses penuh ke:",
    "subscribe_cancel": "Anda bisa \"Unsubscribe\" di Pengaturan kapan saja",
    "subscribe_caveat": "* Fitur Pro termasuk penyesuaian gradien, penyesuaian radial, masking warna, dan menyikat warna. keanggotaan Anda akan berlaku pada semua perangkat dan platform tahun ini.",
    "subscribe_china": "Berlangganan polaxiongkeji akun WeChat dan melakukan pembayaran di WeChat, kemudian login dengan akun Polarr Anda untuk mengaktifkan fitur pro.",
    "subscribe_error": "Ups! Ada kesalahan berlangganan Polarr. Coba lagi?",
    "subscribe_features": "<li> fitur Pro (4 alat penyesuaian lokal) </li><li> Mengakses semua paket filter yang 12 premium </li><li> Tetap diperbarui untuk format RAW </li><li> konten baru ditambahkan bulanan! </li><li> keanggotaan akses pada semua platform * </li>",
    "subscribe_notify": "Beritahu saya jika saya Polarr Premium Keanggotaan dapat digunakan pada lebih banyak platform dan perangkat",
    "subscribe_price": "</price>",
    "subscribe_refund": "Anda sebelumnya dibeli sesuatu dengan Polarr dan Anda berhak untuk",
    "subscribe_success": "Terima kasih untuk berlangganan Polarr Premium!",
    "subscribe_unlock_success": "Anda sekarang Polarr Premium Anggota!",
    "subscription_expired": "keanggotaan Polarr Premium Anda telah berakhir",
    "text_tool": "Text tool memungkinkan Anda menambahkan teks dan bentuk untuk foto. Dengan fitur seperti surat dan spasi baris, keselarasan, dan pembungkus, Anda memiliki kontrol penuh teks. Kami bahkan sudah termasuk fitur-fitur canggih seperti skala vektor dinamis dan penyesuaian blending yang unik.",
    "text_tool_description": "Menambah dan mengedit teks pada layar. Memutar, perubahan warna, mengatur blending, dan bahkan menyesuaikan huruf spasi.",
    "thankyou": "Terima kasih!",
    "unlock_all": "membuka semuanya",
    "unlock_all_note": "Masuk pro akun Polarr Anda atau masukkan kunci lisensi.",
    "unlock_alternate": "Anda juga dapat meng-upgrade dengan masuk ke akun yang sudah ada atau dengan memasukkan kunci lisensi di",
    "unlock_failed": "Fitur Pro tidak tersedia untuk akun ini.",
    "unlock_success": "Anda sekarang memiliki akses ke semua fitur.",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> dan <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> , sumber terbaik untuk fotografi saham-resolusi tinggi gratis dan, telah bekerja sama untuk membawa Anda koleksi 10 filter premium yang dibuat oleh anggota komunitas fotografer Unsplash. </p>",
    "upgrade": "Upgrade ke versi lengkap untuk membuka semua fitur berikut.",
    "upgrade_caveat": "* 12 paket filter tambahan mencakup simulasi Classic Film, Vintage Film, Infrared Film, Warna Kulit dan banyak penyesuaian lainnya yang cocok untuk berbagai mata pelajaran dan adegan.",
    "upgrade_features": "<li> penyesuaian gradien </li><li> penyesuaian melingkar </li><li> masking warna </li><li> menyikat penyesuaian </li><li> 12 tambahan paket penyaring premium </li><li> batch ekspor foto </li>",
    "use_polarrfilter": "Gunakan <strong>#polarrfilter</strong> untuk mendapatkan perhatian!",
    "user_in_paid": "Anda dapat login ke Polarr pada platform lain untuk mengakses semua fitur.",
    "user_in_unpaid": "Jika Anda membuka semuanya, Anda akan dapat login ke Polarr pada platform lain dan mengakses semua fitur.",
    "verification_sent": "Email verifikasi telah dikirim. <br/> Silahkan cek email Anda untuk petunjuk verifikasi.",
    "verify_account": "Untuk login, verifikasi akun Anda. <br/> Periksa email Anda untuk petunjuk verifikasi.",
    "webgl_not_supported": "Kami minta maaf tetapi device Anda tidak memenuhi persyaratan grafis Polarr, mohon perbarui ke sistem operasi terbaru dan coba lagi."
  },
  "labels": {
    "adjustments": "Pengaturan",
    "after": "Sesudah",
    "alignment": "Penjajaran",
    "all": "Semua",
    "amount": "Jumlah",
    "angle": "Sudut",
    "aqua": "Aqua",
    "artwork": "Artwork",
    "auto": "Otomatis",
    "balance": "Keseimbangan",
    "basic": "Dasar",
    "batch_rename": "Batch rename",
    "batch_resize": "Batch resize",
    "before": "Sebelum",
    "best": "Terbaik",
    "blacks": "Hitam",
    "blend": "Campur",
    "blend_mode": "Blend mode",
    "blue": "Biru",
    "blur": "Mengaburkan",
    "brightness": "Kecerahan",
    "brush_flow": "Brush Arus",
    "brush_opacity": "Brush Opacity",
    "brush_size": "Ukuran kuas",
    "clarity": "Kejelasan",
    "collection": "Koleksi",
    "color": "Warna",
    "contrast": "Kontras",
    "crop": "Crop",
    "crop_size": "dimensi tanaman",
    "curves": "Bentuk",
    "custom": "Kustom",
    "dehaze": "Embun",
    "denoise": "Denoise",
    "detail": "Detil",
    "diffuse": "Baur",
    "distortion": "Distorsi",
    "distortion_amount": "Distorsi",
    "distortion_horizontal": "Distorsi Horisontal",
    "distortion_vertical": "Distorsi Vertikal",
    "dividers": "Jangka pembagi garis",
    "edit": "mengedit",
    "effects": "Efek",
    "email": "E-mail",
    "enable": "Nyalakan",
    "estimated_size": "perkiraan Ukuran",
    "exposure": "Pencahayaan",
    "eyes": "mata",
    "face": "Menghadapi",
    "favorites": "Favorit",
    "feather": "Bulu",
    "fit": "Cocok",
    "font": "fon",
    "format": "Format",
    "free": "Bebas",
    "frequent": "Paling banyak digunakan",
    "fringing": "Tepian",
    "grain": "Butiran",
    "green": "Hijau",
    "guides": "panduan",
    "high": "Tinggi",
    "highlights": "Highlight",
    "histogram": "Histogram",
    "history": "Riwayat",
    "hsl": "HSL",
    "hue": "Corak",
    "hue_blue": "HSL - warna biru",
    "hue_red": "HSL - warna merah",
    "invert": "Balikkan",
    "lens": "Lens",
    "lens_distortion": "Distorsi lensa",
    "letter_spacing": "surat Spasi",
    "license_key": "Kunci lisensi",
    "light": "Terang",
    "line_spacing": "Spasi baris",
    "lips": "bibir",
    "look": "Filter",
    "looks": "Filter",
    "low": "Rendah",
    "luminance": "Luminansi",
    "luminance_blue": "HSL - luminansi biru",
    "luminance_red": "HSL - luminanasi merah",
    "magenta": "Magenta",
    "master": "Master",
    "max_size": "dimensi Max",
    "med": "Sedang",
    "mix": "Atur Semua",
    "mosaic": "Pixelate",
    "name": "Nama",
    "none": "tak satupun",
    "nose": "Hidung",
    "opacity": "Kegelapan",
    "optics": "Optik",
    "options": "Pilihan",
    "orange": "Oranye",
    "original": "Asli",
    "outline": "Garis besar",
    "password": "Kata sandi",
    "position": "Posisi",
    "prefix": "segmen awalan",
    "presets": "Presets",
    "preview": "Preview",
    "purple": "Ungu",
    "quality": "Kualitas",
    "ratio": "Rasio",
    "red": "Merah",
    "reflect": "Mencerminkan",
    "resolution": "Resolusi",
    "rgb": "MHB",
    "roundness": "Kebulatan",
    "saturation": "Saturasi",
    "saturation_blue": "HSL - saturasi biru",
    "saturation_red": "HSL - saturasi merah",
    "sayings": "ucapan",
    "shadow": "Bayangan",
    "shadows": "Bayangan",
    "shape": "Bentuk",
    "shapes": "bentuk",
    "shapes_presets": "Bentuk & Presets",
    "sharpen": "Pertajam",
    "size": "Ukuran",
    "skin": "Kulit",
    "smoothness": "smoothing",
    "split_tone": "Toning",
    "teeth": "gigi",
    "temperature": "Temp",
    "text": "Teks",
    "texture": "Tekstur",
    "threshold": "Ambang",
    "tint": "Warna",
    "tone": "Nada",
    "toning": "Toning",
    "use_prefix": "Gunakan awalan",
    "use_watermark": "Gunakan Watermark",
    "user_name": "Nama pengguna",
    "vibrance": "Vibran",
    "vignette": "Sketsa",
    "vignette_amount": "Sketsa",
    "watermark_image": "watermark Gambar",
    "wb": "SP",
    "whitening": "Whitening",
    "whites": "Putih",
    "width": "Lebar",
    "yellow": "Kuning"
  },
  "menus": {
    "batch_export": "batch ekspor",
    "change_language": "Ganti bahasa",
    "copy_edit": "Copy Editan",
    "edit_watermark": "mengedit Watermark",
    "export": "Format, Resize, Watermark",
    "filters_users": "Filter oleh Polarrists",
    "guide": "Panduan",
    "image_quality": "Kualitas gambar",
    "image_settings": "Pengaturan ekspor",
    "language": "Bahasa",
    "more_filters": "Lebih banyak filter",
    "open": "Buka",
    "open_in": "Buka di",
    "paste_edit": "Edit Paste",
    "remove": "Hapus",
    "rename": "Namai ulang",
    "restore_purchases": "Kembalikan pembelian",
    "save_cameraroll": "Simpan ke Rol Kamera",
    "save_copy": "Simpan salinan",
    "settings": "Pengaturan",
    "share": "Bagikan",
    "share_edits": "Share suntingan",
    "share_filter": "Share penyaring",
    "show_instructions": "Panduan pengaturan",
    "show_intro": "Tampilkan intro",
    "show_video_tutorial": "Tutorial video",
    "single_export": "Ekspor"
  },
  "onboarding": {
    "become_master": "Menjadi master editing foto.",
    "step_0": "Selamat Datang di Polarr! <br/> Mari kita mulai dengan melihat beberapa filter.",
    "step_1": "Tekan untuk memilih filter. <br/> panel geser untuk melihat lebih banyak filter.",
    "step_10": "Anda dapat menarik untuk menata kembali semua ikon alat.",
    "step_2": "Keran <i class='icon-check'></i> untuk menerapkan filter. <br/> Gunakan slider untuk menyesuaikan intensitas filter.",
    "step_3": "Setiap filter yang benar-benar dapat diedit. <br/> Pergi ke Penyesuaian untuk melihat hasil edit Anda.",
    "step_4": "Anda dapat mengubah penyesuaian dengan menyeret slider.",
    "step_4a": "Tekan lama kemudian tarik untuk menyesuaikan slider.",
    "step_5": "Lihat semua yang telah diedit di panel sejarah.",
    "step_6": "Anda dapat membatalkan edit dan kembali ke setiap titik dalam sejarah.",
    "step_7": "Menyimpan gambar Anda di sini. <br/> Anda dapat mengubah ukuran, menambahkan watermark atau menyimpan beberapa gambar.",
    "step_7a": "Menyimpan gambar Anda di sini. <br/> Anda juga dapat berbagi dan menyimpan beberapa gambar.",
    "step_8": "Buka foto Anda sendiri di sini.",
    "step_9": "Lihat foto Anda dibuka di foto panel."
  },
  "status": {
    "account_suspended": "Akun Anda ditahan karena aktivitas yang mencurigakan. Untuk info lebih lanjut, silahkan cek email atau kontak Anda <a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "Keran <i class='icon-add-face'></i> menambahkan wajah",
    "adjustment_copied": "penyesuaian Disalin",
    "adjustment_pasted": "penyesuaian ditempelkan",
    "demo_mode": "Versi Polarr Photo Editor telah dibuat khusus untuk tujuan demonstrasi",
    "downloading_font": "download font yang",
    "drag_icons": "Seret ikon untuk mengatur ulang alat",
    "drop_files": "Drop file untuk membuka",
    "email_invalid": "Maaf, sepertinya alamat email tidak valid.",
    "email_reset": "Sebuah link ulang telah dikirim. <br/> Silahkan cek email Anda.",
    "exporting_filter": "menyimpan filter",
    "field_invalid": "tidak valid.",
    "file_exists": "foto yang sudah terbuka",
    "file_not_supported": "Maaf, jenis file tidak didukung",
    "filter_exported": "filter Anda telah disimpan ke kamera roll.",
    "filter_saved": "Filter tersimpan",
    "filters_sync_error": "filter kesalahan sinkronisasi",
    "filters_synced": "filter disinkronkan",
    "filters_syncing": "Menyelaraskan filter ...",
    "import_filter_error": "Maaf, filter yang tidak dapat diimpor",
    "import_limit": "Polarr hanya bisa membuka hingga 50 foto, hapus beberapa foto sebelum membuka lagi.",
    "importing_filter": "mengimpor penyaring",
    "importing_photo": "Tidak membuka",
    "importing_photos": "membuka foto",
    "invalid_credentials": "Email atau password salah",
    "loading_photo": "Memuat foto",
    "login_success": "Anda telah berhasil login!",
    "no_faces": "Tidak ada wajah yang terdeteksi.",
    "no_user": "pengguna yang tidak ada.",
    "offline": "Tidak dapat terhubung ke server.",
    "photo_load_error": "Tidak dapat memuat foto ",
    "photo_saved": "Foto tersimpan",
    "photo_shared": "Foto dibagikan",
    "photos_saved": "foto disimpan",
    "purchase_required": "pembelian diperlukan",
    "raw_failed": "Tidak dapat membaca gambar RAW. dukungan RAW masih eksperimental.",
    "registration_success": "Anda telah berhasil membuat account baru! <br/> Untuk login pada perangkat lain, Anda harus terlebih dahulu memverifikasi akun Anda. Kami telah mengirimkan email dengan petunjuk verifikasi.",
    "saving_photo": "Tidak menyimpan",
    "saving_photo_error": "Error dalam menyimpan foto :(",
    "saving_photo_error_sd": "Polarr tidak memiliki ijin untuk mengakses kartu SD atau penyimpanan internal Anda. Mohon cek pembaruan sistem Android terbaru untuk memperbaiki masalahnya.",
    "saving_photos": "menyimpan foto",
    "server_offline": "Maaf, server tidak merespon. <br/> Apakah Anda terhubung ke internet?",
    "sharing_photo": "berbagi foto",
    "sharing_photo_error": "Error dalam membagikan foto :(",
    "show_me_how": "Tunjukkan bagaimana menggunakan </thing>",
    "supported_files": "Polarr mendukung JPG, PNG, BMP, dan file RAW tertentu",
    "unlocks_remaining": "Kamu punya </unlocks> membuka tersisa",
    "user_exists": "Sudah ada akun dengan itu email atau nama pengguna.",
    "wrong_password": "Kata sandi salah"
  },
  "tooltips": {
    "add_face": "Tambahkan Wajah",
    "add_text": "Tambahkan teks",
    "adjustments": "penyesuaian",
    "apply_adjustments": "Terapkan Penyesuaian",
    "auto_adjust": "Penyesuaian otomatis",
    "brush_filter": "Brush Masker",
    "color_filter": "Masker warna",
    "crop": "Crop",
    "crop_straighten": "Tanaman & Angle",
    "current_adjustments": "Penyesuaian saat ini",
    "double_tap_edit": "Ketuk dua kali untuk mengedit",
    "drag_watermark": "Seret untuk memindahkan watermark",
    "eraser": "Penghapus",
    "face": "Alat wajah",
    "filmstrip": "Foto dibuka",
    "flip": "Balik",
    "flip_h": "Balik secara horizontal",
    "flip_v": "Balik secara vertikal",
    "global_adjustments": "Pengaturan Global",
    "gradient_filter": "Topeng Gradien",
    "guides": "panduan",
    "history": "editing Sejarah",
    "info": "Info",
    "local_adjustments": "Pengaturan Lokal",
    "looks": "filter",
    "masks": "masker",
    "move_face": "langkah Wajah",
    "open": "Buka Foto",
    "pan": "Panci",
    "radial_filter": "Topeng Radial",
    "redo": "Ulangi",
    "remove_face": "Hapus Wajah",
    "remove_text": "Hapus Teks",
    "rotate": "Memutar",
    "rotate_ccw": "Putar CCW",
    "rotate_cw": "Putar CW",
    "save": "Menyimpan foto",
    "settings": "Pengaturan",
    "show_original": "Menunjukkan yang asli",
    "text": "Alat teks",
    "undo": "Undo"
  },
  "tutorial": {
    "blacks": "Mengubah kecerahan bagian hitam dari foto",
    "brightness": "Mengubah kecerahan atau \"gamma\" dari foto",
    "brush_mask_0": "Gunakan jari Anda untuk melukis dengan kuas.",
    "brush_mask_1": "Kuas masker hanya mempengaruhi daerah disikat.",
    "clarify": "Menjelaskan",
    "clarify_1": "<b>mengurangi kabut</b> <br/> Kabut adalah masalah umum di foto dan sangat mudah untuk menghapus dalam Polarr. <br/> Untuk menghapus kabut, meningkatkan <b>Dehaze</b> ke 85",
    "clarify_2": "<b>Recover paparan</b> <br/> Dehaze terkadang menggelapkan foto terlalu banyak. <br/> Untuk memulihkan beberapa cahaya, meningkatkan <b>Paparan</b> 30",
    "clarify_3": "<b>meningkatkan kontras</b> <br/> kabut ini kebanyakan pergi sekarang, tapi kita bisa meningkatkan dinamis cahaya sedikit lebih. Meningkatkan <b>Kontras</b> untuk 60",
    "clarify_4": "<b>meningkatkan rincian</b> <br/> Untuk meningkatkan rincian di foto, kita dapat menggunakan alat Clarity. Meningkatkan <b>Kejelasan</b> ke 40",
    "clarify_5": "Kerja bagus! Anda telah belajar bagaimana membuat foto kabur jauh lebih baik dalam 4 langkah sederhana.",
    "clarify_description": "Dalam tutorial ini, Anda akan belajar bagaimana untuk mengurangi kabut, mendefinisikan garis besar dan menambahkan kejelasan dengan subjek Anda.",
    "clarity": "Meningkatkan atau memperlancar rincian dalam foto",
    "color_mask_0": "Warna topeng hanya mempengaruhi warna yang dipilih.",
    "color_mask_1": "Seret untuk mengubah warna yang dipilih.",
    "color_mask_2": "<b>Ambang</b> set sensitivitas seleksi.",
    "contrast": "Buat subyek Anda menjadi lebih dapat dibedakan.",
    "curves": "Mengubah kurva nada foto",
    "deep_space": "Ruang yang dalam",
    "deep_space_1": "<b>Recover cahaya</b> <br/> penyesuaian eksposur Polarr ini mensimulasikan paparan dari kamera. <br/> Untuk memulihkan cahaya, set <b>Paparan</b> 100",
    "deep_space_2": "<b>meningkatkan rincian</b> <br/> Mari kita membuat bintang terlihat lebih mengesankan. <br/> Menambahkan kejelasan akan membantu dengan mengungkapkan rincian lebih lanjut. <br/> Untuk meningkatkan rincian, mengatur <b>Kejelasan</b> ke 100",
    "deep_space_3": "<b>menambah kedalaman</b> <br/> Untuk membawa lebih fokus ke tengah gambar, kita dapat menambahkan sketsa untuk menggelapkan tepi foto. <br/> Untuk menambah kedalaman, menurunkan <b>Vignette</b> ke -100",
    "deep_space_4": "<b>Vignette halus</b> <br/> Sekarang sketsa terlihat terlalu tajam. <br/> Untuk membuat sketsa terlihat halus dan halus, <br/> meningkatkan <b>Feather</b> untuk 100",
    "deep_space_5": "<b>memulihkan highlights</b> <br/> Beberapa bintang di tepi foto terlihat gelap. Kami dapat memulihkan mereka dengan meningkatkan <b>Highlights</b> ke 100",
    "deep_space_6": "<b>Ganti warna</b> <br/> HSL memungkinkan Anda untuk mengubah atribut dari warna tertentu. Untuk mengubah warna langit, meningkatkan <b>Aqua Hue</b> ke 100",
    "deep_space_7": "Kerja bagus! Anda telah belajar bagaimana membuat langit berbintang terlihat benar-benar mengesankan.",
    "deep_space_description": "Dalam tutorial ini, Anda akan mempelajari cara-cara untuk menambah kedalaman, mengatur warna dan perubahan eksposur dari langit malam.",
    "dehaze": "Menambah atau menghapus kabut dari foto",
    "denoise": "Menghapus suara dari foto",
    "diffuse": "Memberikan foto efek \"cahaya\"",
    "distortion": "Distorsi",
    "distortion_1": "<b>distorsi yang benar</b> <br/> Kita sering melihat bangunan dan dinding mencari bengkok di foto. Hal ini biasanya disebabkan oleh desain lensa kamera wide-angle. Kita bisa dengan mudah memperbaiki ini di Polarr dengan alat distorsi. <br/> Untuk memperbaiki distorsi, meningkatkan <b>vertikal Distortion</b> ke 70",
    "distortion_2": "<b>sketsa yang benar</b> <br/> Dalam tambahan untuk distorsi optik, lensa kamera yang paling memiliki dampak cahaya dekat tepi mereka. Kita bisa mengurangi ini tepi gelap menggunakan alat sketsa. <br/> Untuk memperbaiki sketsa, meningkatkan <b>Vignette</b> ke 20",
    "distortion_3": "<b>meningkatkan rincian</b> <br/> Terakhir, untuk meningkatkan detail di foto, kita dapat menggunakan alat kejelasan. <br/> Meningkatkan <b>Kejelasan</b> 100",
    "distortion_4": "Kerja bagus! Anda telah belajar bagaimana untuk memperbaiki cacat lensa umum di 3 langkah sederhana.",
    "distortion_amount": "Menambahkan sudut lebar atau efek mata ikan pada foto.",
    "distortion_description": "Dalam tutorial ini, Anda akan belajar bagaimana untuk memperbaiki distorsi lensa dan sketsa di gambar arsitektur.",
    "distortion_horizontal": "Mengubah distorsi sepanjang sumbu horisontal",
    "distortion_vertical": "Mengubah distorsi sepanjang sumbu vertikal",
    "exposure": "Mengubah kecerahan tanpa mencuci foto",
    "face_0": "Polarr secara otomatis mendeteksi wajah dan memisahkan fitur individu.",
    "face_1": "Gunakan alat distort untuk menyesuaikan ukuran dan posisi fitur wajah.",
    "fringing": "Menambahkan efek \"pusing\" pada foto Anda.",
    "gradient_mask_0": "topeng gradien mempengaruhi daerah dalam garis lurus.",
    "gradient_mask_1": "Pinch untuk mengubah ukuran. <br/> Drag untuk bergerak dan memutar.",
    "gradient_mask_2": "<b>Balikkan</b> beralih arah gradien.",
    "gradient_mask_3": "<b>Mencerminkan</b> membuat gradien berjalan di kedua arah.",
    "grain": "Menambahkan efek film grain",
    "highlights": "Mengubah kecerahan highlights tanpa mencuci foto",
    "hsl": "Mengubah warna, saturasi, atau terang warna tertentu.",
    "hsl_header": "HSL",
    "hue_blue": "Mengubah warna tertentu menjadi biru.",
    "hue_red": "Mengubah warna tertentu menjadi merah.",
    "luminance_blue": "Mengubah kecerahan dari warna biru.",
    "luminance_red": "Mengubah kecerahan dari warna merah.",
    "mastering_adjustments": "menguasai Penyesuaian",
    "mosaic": "Memberikan foto efek mosaik",
    "radial_mask_0": "topeng radial hanya mempengaruhi area melingkar.",
    "radial_mask_1": "Pinch untuk mengubah ukuran. <br/> Drag untuk bergerak dan memutar.",
    "radial_mask_2": "<b>Bulu</b> mengubah ketidakjelasan dari topeng.",
    "radial_mask_3": "<b>Balikkan</b> beralih daerah yang terkena.",
    "saturation": "Meningkatkan atau menurunkan intensitas warna.",
    "saturation_blue": "Mengubah \"kebiruan\" dari warna biru.",
    "saturation_red": "Mengubah \"kemerahan\" dari warna merah.",
    "shadows": "Mengubah kecerahan dari bayang-bayang tanpa mencuci foto",
    "sharpen": "Tingkatkan kontras untuk membuat foto terlihat lebih tajam.",
    "temperature": "Membuat foto lebih hangat atau dingin",
    "tint": "Atur warna dengan spektrum magenta-hijau.",
    "toning_highlights": "Mengubah warna highlights",
    "toning_shadows": "Mengubah warna dari bayang-bayang",
    "vibrance": "Mengubah saturasi tanpa mencuci keluar warna kulit",
    "vignette_amount": "Memudar atau menyoroti tepi foto",
    "vignette_feather": "Mengubah ketidakjelasan dari sketsa",
    "vignette_highlights": "Biarkan highlights menunjukkan melalui sketsa",
    "vignette_roundness": "Membuat sketsa yang lebih bulat atau persegi",
    "vignette_size": "Mengubah ukuran sketsa",
    "white_balance": "Saldo putih",
    "white_balance_1": "<b>White balance yang benar</b> <br/> Sebagian besar sumber cahaya (lampu, senter, dll) tidak memancarkan warna putih murni. Hal ini dapat menyebabkan kulit putih di foto untuk memiliki pemain kuning atau biru. Untuk memperbaiki ini, menurunkan <b>suhu</b> ke -30",
    "white_balance_2": "<b>Memulihkan cahaya dari bayang-bayang</b> <br/> Kadang-kadang bagian dari sebuah foto yang lebih gelap daripada yang dimaksudkan karena mata kita dapat melihat lebih banyak cahaya dari kamera. Kami dapat memulihkan cahaya dari bayangan menggunakan alat bayangan. <br/> Meningkatkan <b>Bayangan</b> ke 100",
    "white_balance_3": "<b>meningkatkan rincian</b> <br/> Terakhir, untuk meningkatkan detail di foto, kita dapat menggunakan alat kejelasan. <br/> Meningkatkan <b>Kejelasan</b> 100",
    "white_balance_4": "Kerja bagus! Anda telah belajar bagaimana untuk menghapus cor kuning dan memulihkan cahaya dari bayang-bayang di 3 langkah sederhana.",
    "white_balance_description": "Dalam tutorial ini, Anda akan mempelajari konsep white balance dan bagaimana memperbaiki di pintu cahaya buatan.",
    "whites": "Mengubah kecerahan bagian putih dari foto"
  }
};
},{}],327:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "Aggiungi",
    "add_image": "Scegli immagine",
    "add_to_favorites": "Aggiungi ai Preferiti",
    "aspect": "Aspetto",
    "back": "Indietro",
    "buy": "Compra",
    "buy_full_version": "Compra la versione completa",
    "cancel": "Annulla",
    "check_again": "Controlla di nuovo",
    "choose_folder": "Scegliete la cartella",
    "clear": "Svuota",
    "clear_history": "Svuota cronologia",
    "close": "Chiudi",
    "cloud": "Cloud",
    "continue_anyway": "Continua lo stesso",
    "current_image": "Attuale",
    "delete": "Elimina",
    "device": "Dispositivo",
    "dismiss": "Respingere",
    "done": "Fatto",
    "dont_ask": "Non chiedere più",
    "duplicate": "Duplicare",
    "enter_license": "Inserisci la chiave di licenza",
    "enter_license_key": "Inserisci la chiave di licenza",
    "exit": "Uscita",
    "export": "Esporta",
    "export_filter": "Salva come codice QR",
    "forgot_password": "Ha dimenticato la password?",
    "free_membership": "iscrizione gratuita",
    "get": "Ottenere",
    "get_license": "Get chiave di licenza",
    "get_started": "Iniziare",
    "got_it": "Fatto",
    "have_account": "Hai già un account?",
    "import": "Importa",
    "import_filter": "filtro di importazione",
    "import_filter_photos": "Importa da Foto",
    "import_photos": "Aprire le foto",
    "info": "Informazioni",
    "installed": "Installato",
    "invite": "Invia link",
    "learn_more": "Per saperne di più",
    "load_last_photo": "Caricare ultima foto sul lancio",
    "new": "Nuovo",
    "new_filter": "Crea filtro",
    "next": "Prossimo",
    "no": "No",
    "no_thanks": "No, grazie",
    "not_really": "Non proprio",
    "not_this_time": "Non questa volta",
    "open_sample": "Apri foto campione",
    "paste_adjustments": "regolazioni incolla",
    "pay_wechat": "aperto wechat",
    "prev": "Prec",
    "preview": "Anteprima",
    "pro": "Pro",
    "purchased": "Acquistato",
    "remove": "Rimuovi",
    "remove_all_photos": "Rimuovere tutte le foto",
    "remove_photo": "rimuovere foto",
    "remove_selected_photo": "Rimuovere foto selezionata",
    "reset": "Resetta",
    "reset_all": "Resetta tutto",
    "reset_password": "Resetta la password",
    "reset_photos": "Ripristinare le etichette salvate e modificate",
    "reset_tips": "Resetta suggerimenti",
    "restore": "Ripristina",
    "revert": "Ritorna all’originale",
    "sample": "Campione",
    "save": "Salva",
    "save_qr": "Salva codice QR",
    "save_share": "Salva + Condividi",
    "scan_filter": "filtro di scansione",
    "select_all": "Seleziona tutto",
    "select_changed": "Seleziona modificate",
    "send": "Invia",
    "send_verification": "Invia nuovamente email di verifica",
    "show_in_folder": "Mostra nella cartella",
    "show_me": "Fammi vedere",
    "signin": "Accesso",
    "signout": "Disconnettersi",
    "signup": "Crea conto Polarr",
    "skip": "Salta",
    "submit_license_key": "Invia chiave di licenza",
    "subscribe": "sottoscrivi",
    "subscribe_monthly": "Iscriviti mensile",
    "subscribe_yearly": "Iscriviti annuo",
    "sure": "Ok, sicuro",
    "try": "Provare",
    "try_again": "Prova ancora",
    "unlock": "Sblocca",
    "unsubscribe": "Annulla l'iscrizione",
    "update": "Aggiorna",
    "whats_new": "Cosa c'è di nuovo",
    "yes": "Si"
  },
  "dialogs": {
    "about": "Info",
    "about_text": "Crediamo sia importante permettere ai creatori di esprimere facilmente le loro prospettive e attitudini verso la vita, senza perdere la lealtà verso il loro desiderio creative e ispirazioni.",
    "account": "account",
    "all_filters": "tutti i filtri",
    "all_filters_text": "Sbloccare intera collezione di Polarr di filtri di alta qualità",
    "backup_filters": "Sincronizzare ed eseguire il backup di tutti i filtri personalizzati in modo da poterli utilizzare su dispositivi e piattaforme. Questa funzionalità richiede che si accede con un account Polarr.",
    "backup_filters_title": "Filtri personalizzati di backup",
    "brush_limit": "Il limite di corrente Mask Brush è </limit>",
    "denoise": "Il nostro processo Denoise in tempo reale lo rende facile rimuovere il rumore e la grana in foto. Separatamente controllare componenti di colore e il rumore di luminanza.",
    "featured": "In primo piano",
    "filter_collections": "Filtro collezioni",
    "filter_name": "Nome questo filtro",
    "get_picky": "Stanco di editing foto? Prova nuova applicazione di Polarr che pubblica foto con intelligenza artificiale!",
    "get_updates": "Iscriviti agli aggiornamenti",
    "get_updates_about": "Puoi essere il primo a provare le nuove funzionalità! <br/> Aggiungi il tuo indirizzo e-mail, e vi informeremo sulle nuove funzionalità e prodotti da Polarr.",
    "get_updates_thankyou": "A questo punto ricevere aggiornamenti sulle nuove funzionalità e prodotti da Polarr! <br/> Se si desidera interrompere la ricezione di aggiornamenti, è possibile \"annullare\" dal tuo e-mail in qualsiasi momento.",
    "help": "Aiuto",
    "invite": "Condividi link installazione. Le funzioni pro saranno sbloccate dopo che tre persone installino utilizzando il tuo link.",
    "local_adjustments": "regolazioni locali consente di utilizzare potente set di rettifiche su aree mirate di Polarr. Include strumenti di mascheramento radiale, pendenza, colori, e pennello.",
    "lock_rotation": "rotazione di blocco",
    "max_export_size": "Dimensione massima esportazione",
    "new_filter": "Salva modifiche come filtro",
    "no_purchases": "Non ci sono acquisti da ripristinare.",
    "offline": "Il tuo dispositivo sembra essere offline. Riprova più tardi.",
    "photos": "Fotografie",
    "polarr_premium": "Polarr Premium",
    "pro_all_filters": "Pro caratteristiche <br/> + <br/> tutti i filtri",
    "pro_all_filters_text": "Sblocca tutte le funzioni, tra cui strumenti di pro e tutti i filtri di premio di Polarr. Utilizza il tuo account Polarr per accedere a tutte le funzioni su altre piattaforme. Backup e sincronizzare i filtri personalizzati.",
    "pro_features": "Pro Tools",
    "pro_features_all": "Tutti Pro Tools",
    "purchase": "Sblocca radiale, sfumatura, colori, pennelli, Denoise, e gli strumenti di testo. Backup e sincronizzare i filtri personalizzati.",
    "purchase_failed": "Acquisto annullato",
    "purchase_header": "Sblocca tutte le funzioni pro",
    "purchase_successful": "Ora hai accesso alla versione pro.",
    "purchase_unavailable": "Non è possibile acquistare al momento. Spiacenti.",
    "purchases": "Acquisti",
    "rate_review": "Valuta e recensisci",
    "rename_filter": "Rinomina filtro",
    "restore_failed": "Il tuo account non ha accesso completo di funzionalità.",
    "restore_successful": "Tutti i tuoi acquisti precedenti sono ora ripristinate.",
    "review": "Ti piace Polarr Photo Editor?",
    "review_no": "Potresti darci la tua opinione?",
    "review_yes": "Allora perchè non votarla su App Store?",
    "save_adjustments": "Fare un filtro personalizzato è possibile condividere.",
    "send_feedback": "Invia recensione",
    "signin_polarr_account": "Accedi al tuo account Polarr",
    "store": "Negozio",
    "subscribe": "Abbonarsi a <strong>Polarr Premium</strong>",
    "subscribe_access": "per il pieno accesso a:",
    "subscribe_cancel": "È possibile \"Cancellati\" in Impostazioni in qualsiasi momento",
    "subscribe_caveat": "* Pro caratteristiche includono la regolazione pendenza, regolazione radiale, mascheramento di colore, e spazzolatura colore. La tua iscrizione sarà applicato su tutti i dispositivi e le piattaforme di quest'anno.",
    "subscribe_china": "Sottoscrivi polaxiongkeji conto wechat e effettuare un pagamento in wechat, poi effettuare il login con il tuo account Polarr per attivare le funzioni pro.",
    "subscribe_error": "Oops! C'era errore di sottoscrizione di Polarr. Riprova?",
    "subscribe_features": "<li> Pro caratteristiche (4 strumenti di regolazione locali) </li><li> Accedere a tutti i pacchi filtranti 12 premium </li><li> Rimani aggiornato per i formati RAW </li><li> Nuovi contenuti aggiunti ogni mese! </li><li> l'adesione di accesso su tutte le piattaforme * </li>",
    "subscribe_notify": "Avvisami quando il mio Polarr Premium può essere utilizzato su più piattaforme e dispositivi",
    "subscribe_price": "</price>",
    "subscribe_refund": "È acquistato in precedenza qualcosa con Polarr e si ha diritto a",
    "subscribe_success": "Grazie per la sottoscrizione di Polarr Premium!",
    "subscribe_unlock_success": "Ora sei un Polarr Premium membro!",
    "subscription_expired": "La tua iscrizione Polarr Premium è scaduto",
    "text_tool": "strumento Testo consente di aggiungere testo e forme per le foto. Con caratteristiche come la lettera e l'interlinea, allineamento, ed avvolgente, si ha il controllo completo il testo. Abbiamo anche incluso funzionalità avanzate come il ridimensionamento vettore dinamico e una regolazione miscela unica.",
    "text_tool_description": "Aggiungere e modificare il testo sullo schermo. Ruota, cambiano colore, impostare la miscelazione, e anche regolare la spaziatura tra le lettere.",
    "thankyou": "Grazie!",
    "unlock_all": "Sblocca tutto",
    "unlock_all_note": "Effettua il login tuo account pro Polarr o inserire la chiave di licenza.",
    "unlock_alternate": "È inoltre possibile aggiornare accedendo a un account esistente o inserendo un codice di licenza in",
    "unlock_failed": "Pro funzioni non sono disponibili per questo account.",
    "unlock_success": "Ora è possibile accedere a tutte le funzioni.",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> e <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> , la migliore fonte gratuitamente e ad alta risoluzione stock photography, hanno unito le forze per portare una collezione di 10 filtri di alta qualità creati dai membri del fotografo comunità di Unsplash. </p>",
    "upgrade": "Aggiornamento alla versione completa per sbloccare tutte le seguenti caratteristiche.",
    "upgrade_caveat": "* 12 confezioni di filtro aggiuntive comprendono simulazioni di Classic Film, film d'epoca, film a raggi infrarossi, tonalità della pelle e molte altre regolazioni adatte a vari soggetti e scene.",
    "upgrade_features": "<li> regolazioni Gradiente </li><li> regolazioni circolari </li><li> mascheramento di colore </li><li> regolazione spazzolatura </li><li> 12 confezioni di filtro premio supplementare </li><li> Batch foto esportazione </li>",
    "use_polarrfilter": "Utilizzare <strong>#polarrfilter</strong> per farsi notare!",
    "user_in_paid": "È possibile accedere al Polarr su altre piattaforme per accedere a tutte le funzioni.",
    "user_in_unpaid": "Se si sblocca tutto, sarete in grado di accedere al Polarr su altre piattaforme e accedere a tutte le funzioni.",
    "verification_sent": "Una e-mail di verifica è stato inviato. <br/> Si prega di controllare la posta elettronica per le istruzioni di verifica.",
    "verify_account": "Per accedere, si prega di verificare il tuo account. <br/> Controlla la tua e-mail per le istruzioni di verifica.",
    "webgl_not_supported": "Siamo davvero dispiaciuti che il tuo dispositivo non soddisfi i requisiti grafici di Polarr, aggiorna all’ultimo sistema operativo e riprova."
  },
  "labels": {
    "adjustments": "Regolazioni",
    "after": "Dopo",
    "alignment": "Allineamento",
    "all": "Tutto",
    "amount": "Quantità",
    "angle": "Angolo",
    "aqua": "Acqua",
    "artwork": "Opera d'arte",
    "auto": "Auto",
    "balance": "Equilibrio",
    "basic": "Base",
    "batch_rename": "Batch Rename",
    "batch_resize": "Batch ridimensionamento",
    "before": "Prima",
    "best": "Migliore",
    "blacks": "Neri",
    "blend": "Miscela",
    "blend_mode": "metodo di fusione",
    "blue": "Blu",
    "blur": "blur",
    "brightness": "Luminosità",
    "brush_flow": "spazzola di flusso",
    "brush_opacity": "spazzola Opacità",
    "brush_size": "Dimensione del pennello",
    "clarity": "Nitidezza",
    "collection": "Raccolta",
    "color": "Colore",
    "contrast": "Contrasto",
    "crop": "Taglia",
    "crop_size": "dimensioni crop",
    "curves": "Curve",
    "custom": "Personalizza",
    "dehaze": "Nitidezza",
    "denoise": "Riduci disturbo",
    "detail": "Dettaglio",
    "diffuse": "Attenua",
    "distortion": "Distorcere",
    "distortion_amount": "Distorcere",
    "distortion_horizontal": "Distorsione Orizzontale",
    "distortion_vertical": "Distorsione Verticale",
    "dividers": "Divisori",
    "edit": "Modifica",
    "effects": "Effetti",
    "email": "E-mail",
    "enable": "Attiva",
    "estimated_size": "dimensione stimata",
    "exposure": "Esposizione",
    "eyes": "occhi",
    "face": "Faccia",
    "favorites": "Preferiti",
    "feather": "Piuma",
    "fit": "In forma",
    "font": "Carattere",
    "format": "Formato",
    "free": "Gratuito",
    "frequent": "Più usati",
    "fringing": "Bordatura",
    "grain": "Grana",
    "green": "verde",
    "guides": "Guide",
    "high": "Alta",
    "highlights": "Highlights",
    "histogram": "Histogram",
    "history": "Storia",
    "hsl": "HSL",
    "hue": "Sfumature",
    "hue_blue": "HSL – tinte blu",
    "hue_red": "HSL – tinte rosse",
    "invert": "Inverti",
    "lens": "Lente",
    "lens_distortion": "distorsione della lente",
    "letter_spacing": "Spaziatura del carattere",
    "license_key": "Chiave di licenza",
    "light": "Luce",
    "line_spacing": "Interlinea",
    "lips": "Labbra",
    "look": "Filtra",
    "looks": "Filtri",
    "low": "Bassa",
    "luminance": "Luminosità",
    "luminance_blue": "HSL – luminosità blu",
    "luminance_red": "HSL – luminosità rossa",
    "magenta": "Magenta",
    "master": "Master",
    "max_size": "dimensioni massime",
    "med": "Media",
    "mix": "Regola Tutto",
    "mosaic": "Pixelate",
    "name": "Nome",
    "none": "Nessuna",
    "nose": "Naso",
    "opacity": "Opacità",
    "optics": "Ottica",
    "options": "Opzioni",
    "orange": "Arancione",
    "original": "Originale",
    "outline": "Contorno",
    "password": "parola d'ordine",
    "position": "Posizione",
    "prefix": "segmento prefisso",
    "presets": "Presets",
    "preview": "Anteprima",
    "purple": "Viola",
    "quality": "Qualità",
    "ratio": "Rapporto",
    "red": "Rosso",
    "reflect": "Riflettere",
    "resolution": "Risoluzione",
    "rgb": "RGB",
    "roundness": "Sfericità",
    "saturation": "Saturazione",
    "saturation_blue": "HSL – saturazione blu",
    "saturation_red": "HSL – saturazione rosso",
    "sayings": "detti",
    "shadow": "Ombra",
    "shadows": "Ombre",
    "shape": "Forma",
    "shapes": "forme",
    "shapes_presets": "Forme & preset",
    "sharpen": "Metti a fuoco",
    "size": "Dimensione",
    "skin": "Pelle",
    "smoothness": "levigatura",
    "split_tone": "Tonalità",
    "teeth": "Denti",
    "temperature": "Temp",
    "text": "Testo",
    "texture": "Struttura",
    "threshold": "Soglia",
    "tint": "Tinta",
    "tone": "Tono",
    "toning": "Tonalità",
    "use_prefix": "Usa prefisso",
    "use_watermark": "Utilizza filigrana",
    "user_name": "nome utente",
    "vibrance": "Brillantezza",
    "vignette": "Vignetta",
    "vignette_amount": "Vignetta",
    "watermark_image": "watermark Immagine",
    "wb": "WB",
    "whitening": "imbiancamento",
    "whites": "Bianchi",
    "width": "Larghezza",
    "yellow": "Giallo"
  },
  "menus": {
    "batch_export": "esportazione in batch",
    "change_language": "Cambia Lingua",
    "copy_edit": "copia modifiche",
    "edit_watermark": "Modifica filigrana",
    "export": "Format, ridimensionare, filigrana",
    "filters_users": "Filtri di Polarrists",
    "guide": "Guida",
    "image_quality": "Qualità immagine",
    "image_settings": "Esporta impostazioni",
    "language": "Lingua",
    "more_filters": "Più filtri",
    "open": "Apri",
    "open_in": "Apri in",
    "paste_edit": "modifiche incolla",
    "remove": "Rimuovi",
    "rename": "Rinomina",
    "restore_purchases": "Ripristina acquisti",
    "save_cameraroll": "Salva nel rullino",
    "save_copy": "Salva una copia",
    "settings": "Impostazioni",
    "share": "Condividi",
    "share_edits": "Condividi le modifiche",
    "share_filter": "Filtro Condividi",
    "show_instructions": "Guida Regolazioni",
    "show_intro": "Mostra intro",
    "show_video_tutorial": "Video tutorial",
    "single_export": "Esportare"
  },
  "onboarding": {
    "become_master": "Diventa un maestro di fotoritocco.",
    "step_0": "Benvenuto in Polarr! <br/> Consente di iniziare con la visualizzazione di alcuni filtri.",
    "step_1": "Toccare per selezionare un filtro. <br/> Pannello Swipe per vedere più filtri.",
    "step_10": "È possibile trascinare per riorganizzare tutte le icone degli strumenti.",
    "step_2": "Rubinetto <i class='icon-check'></i> per applicare il filtro. <br/> Utilizzare il cursore per regolare l'intensità del filtro.",
    "step_3": "Ogni filtro è completamente modificabile. <br/> Vai a regolazioni per vedere le modifiche.",
    "step_4": "È possibile modificare una regolazione trascinando il cursore.",
    "step_4a": "Premere a lungo quindi trascinare per regolare il cursore.",
    "step_5": "Vedi tutte le modifiche che hai fatto nel pannello Cronologia.",
    "step_6": "È possibile annullare una modifica e tornare a qualsiasi punto della storia.",
    "step_7": "Salvare l'immagine qui. <br/> È possibile ridimensionare, aggiungere watermark o salvare più immagini.",
    "step_7a": "Salvare l'immagine qui. <br/> È inoltre possibile condividere e salvare più immagini.",
    "step_8": "Aprire le tue foto qui.",
    "step_9": "Visualizzare le foto aperte nella foto del pannello."
  },
  "status": {
    "account_suspended": "Il tuo account è messo in attesa a causa di attività sospette. Per maggiori informazioni, si prega di controllare la posta elettronica o il contatto <a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "Rubinetto <i class='icon-add-face'></i> aggiungere una faccia",
    "adjustment_copied": "Regolazioni copiati",
    "adjustment_pasted": "Regolazioni incollati",
    "demo_mode": "Questa versione di Polarr Photo Editor è stato creato appositamente per scopi dimostrativi",
    "downloading_font": "il download dei font",
    "drag_icons": "Trascinare le icone per riorganizzare gli strumenti",
    "drop_files": "Drop dei file da aprire",
    "email_invalid": "Ci dispiace, sembra che l'indirizzo e-mail non è valido.",
    "email_reset": "Un link di reimpostazione è stato inviato. <br/> Si prega di controllare la tua email.",
    "exporting_filter": "Filtro Salvataggio",
    "field_invalid": "non è valido.",
    "file_exists": "Quella foto è già aperto",
    "file_not_supported": "Spiacenti, questo tipo di file non è supportato",
    "filter_exported": "Il filtro è stato salvato al rullino fotografico.",
    "filter_saved": "filtro salvato",
    "filters_sync_error": "filtri di sincronizzazione Errore",
    "filters_synced": "filtri sincronizzati",
    "filters_syncing": "Sincronizzazione filtri ...",
    "import_filter_error": "Spiacenti, questo filtro non poteva essere importato",
    "import_limit": "Polarr può aprire solo fino a 50 foto, rimuovere alcune foto prima di aprire di più.",
    "importing_filter": "Filtro Importazione",
    "importing_photo": "foto di apertura",
    "importing_photos": "apertura foto",
    "invalid_credentials": "email o password errate",
    "loading_photo": "Caricamento foto",
    "login_success": "Sei stato disconnesso con successo!",
    "no_faces": "Nessun volti rilevati.",
    "no_user": "Questo utente non esiste.",
    "offline": "Impossibile connettersi al server.",
    "photo_load_error": "Impossibile caricare foto",
    "photo_saved": "Foto salvata",
    "photo_shared": "Foto condivisa",
    "photos_saved": "foto salvate",
    "purchase_required": "acquisto richiesto",
    "raw_failed": "Impossibile leggere immagini RAW. supporto RAW è ancora in fase sperimentale.",
    "registration_success": "Hai fatto con successo un nuovo account! <br/> Per eseguire il login su altri dispositivi, è necessario prima verificare il tuo account. Ti abbiamo inviato una e-mail con le istruzioni di verifica.",
    "saving_photo": "Salvataggio foto",
    "saving_photo_error": "Errore salvataggio foto :(",
    "saving_photo_error_sd": "Polarr non ha il permesso di accedere alla tua SD card o memoria interna. Controllare l’ultimo aggiornamento di sistema Android per risolvere il problema.",
    "saving_photos": "Salvataggio foto",
    "server_offline": "Spiacenti, il server non risponde. <br/> State connessi a Internet?",
    "sharing_photo": "photo Sharing",
    "sharing_photo_error": "Errore condivisione foto :(",
    "show_me_how": "Mostrami come utilizzare </thing>",
    "supported_files": "Polarr supporta JPG, PNG, BMP, e alcuni file RAW",
    "unlocks_remaining": "Hai </unlocks> sblocca rimanenti",
    "user_exists": "C&#39;è già un account con quella e-mail o il nome utente.",
    "wrong_password": "Password errata"
  },
  "tooltips": {
    "add_face": "Aggiungere viso",
    "add_text": "Aggiungi testo",
    "adjustments": "Regolazioni",
    "apply_adjustments": "applicare regolazioni",
    "auto_adjust": "Regolazioni automatiche",
    "brush_filter": "Maschera pennello",
    "color_filter": "Maschera di colore",
    "crop": "Taglia",
    "crop_straighten": "Ritaglia e Angle",
    "current_adjustments": "le regolazioni correnti",
    "double_tap_edit": "Doppio tap per modificare",
    "drag_watermark": "Trascinare per spostare filigrana",
    "eraser": "Gomma per cancellare",
    "face": "Strumenti viso",
    "filmstrip": "Foto aperti",
    "flip": "Capovolgi",
    "flip_h": "Capovolgi orizzontalmente",
    "flip_v": "Capovolgi verticalmente",
    "global_adjustments": "Regolazioni Globali",
    "gradient_filter": "Maschera Gradienti",
    "guides": "Guide",
    "history": "editing Storia",
    "info": "Info",
    "local_adjustments": "Regolazioni Locali",
    "looks": "filtri",
    "masks": "Maschere",
    "move_face": "Sposta faccia",
    "open": "aperte Foto",
    "pan": "Padella",
    "radial_filter": "Maschera Radiale",
    "redo": "Rifai",
    "remove_face": "rimuovere viso",
    "remove_text": "rimuovere testo",
    "rotate": "Ruotare",
    "rotate_ccw": "Ruota a sinitra",
    "rotate_cw": "Ruota a destra",
    "save": "Salva foto",
    "settings": "Impostazioni",
    "show_original": "Spettacolo originale",
    "text": "Strumento Testo",
    "undo": "Annulla"
  },
  "tutorial": {
    "blacks": "Modificare la luminosità delle parti nere della foto",
    "brightness": "Modificare la luminosità o \"gamma\" della fotografia",
    "brush_mask_0": "Usare le dita per dipingere con il pennello.",
    "brush_mask_1": "Maschera pennello interessa solo l'area spazzolato.",
    "clarify": "Chiarire",
    "clarify_1": "<b>ridurre nebbia</b> <br/> La nebbia è un problema comune nelle foto ed è facile da rimuovere in Polarr. <br/> Per rimuovere la nebbia, aumentare <b>Dehaze</b> a 85",
    "clarify_2": "<b>recuperare l'esposizione</b> <br/> Dehaze a volte oscura la foto troppo. <br/> Per recuperare un po 'di luce, aumentare <b>l'esposizione</b> a 30",
    "clarify_3": "<b>Aumenta il contrasto</b> <br/> La foschia è in gran parte andato ora, ma siamo in grado di aumentare la dinamica di luce un po 'di più. Aumenta <b>contrasto</b> a 60",
    "clarify_4": "<b>migliorare i dettagli</b> <br/> Per migliorare i dettagli nella foto, è possibile utilizzare lo strumento chiarezza. Aumentare la <b>chiarezza</b> per 40",
    "clarify_5": "Buon lavoro! Hai imparato come fare una foto velato molto meglio in 4 semplici passi.",
    "clarify_description": "In questo tutorial, imparerete come ridurre la nebbia, definire i contorni e aggiungere vivacità al soggetto.",
    "clarity": "Aumentare o appianare i dettagli nella foto",
    "color_mask_0": "maschera di colore riguarda solo il colore selezionato.",
    "color_mask_1": "Trascinare per cambiare il colore selezionato.",
    "color_mask_2": "<b>Threshold</b> imposta la sensibilità della selezione.",
    "contrast": "Rendi il tuo soggetto più evidente.",
    "curves": "Modificare la curva toni della fotografia",
    "deep_space": "Spazio profondo",
    "deep_space_1": "<b>recuperare luce</b> <br/> regolazione dell'esposizione di Polarr simula l'esposizione di una macchina fotografica. <br/> Per recuperare la luce, impostare <b>l'esposizione</b> a 100",
    "deep_space_2": "<b>migliorare i dettagli</b> <br/> Facciamo in modo che le stelle sembrano più impressionante. <br/> L'aggiunta di chiarezza contribuirà rivelando ulteriori dettagli. <br/> Per migliorare i dettagli, impostare <b>chiarezza</b> a 100",
    "deep_space_3": "<b>aggiungere profondità</b> <br/> Per portare maggiore attenzione al centro dell'immagine, possiamo aggiungere vignetta per scurire i bordi della foto. <br/> Per aggiungere profondità, diminuire <b>Vignette</b> a -100",
    "deep_space_4": "<b>Vignette Smooth</b> <br/> In questo momento la vignetta sembra troppo forte. <br/> Per rendere la vignetta aspetto liscio e sottile, <br/> aumentare <b>Feather</b> a 100",
    "deep_space_5": "<b>recuperare punti salienti</b> <br/> Alcune delle stelle nel bordo della foto guardano a scuro. Possiamo recuperare aumentando <b>evidenza</b> a 100",
    "deep_space_6": "<b>Cambia colore</b> <br/> HSL consente di modificare gli attributi di un determinato colore. Per cambiare il colore del cielo, aumentare <b>Aqua Hue</b> a 100",
    "deep_space_7": "Buon lavoro! Hai imparato come fare un cielo stellato look davvero impressionante.",
    "deep_space_description": "In questo tutorial, imparerete modi per aggiungere profondità, colore e il cambiamento l'esposizione di un cielo notturno.",
    "dehaze": "Aggiungere o rimuovere foschia dalla foto",
    "denoise": "Rimuovere il rumore dalla foto",
    "diffuse": "Dare la foto un effetto \"glow\"",
    "distortion": "Distorsione",
    "distortion_1": "<b>distorsione corretta</b> <br/> Vediamo spesso edifici e muri guardando storto nelle foto. Questo di solito è causato dalla progettazione delle lenti della fotocamera grandangolari. Possiamo facilmente risolvere questo problema in Polarr con gli strumenti di distorsione. <br/> Per risolvere la distorsione, aumentare <b>verticale distorsione</b> a 70",
    "distortion_2": "<b>vignetta corretta</b> <br/> In aggiunta alle distorsioni ottiche, la maggior parte delle lenti della fotocamera sono dotate di luce ricaduta vicino i loro bordi. Siamo in grado di ridurre questi bordi scuri utilizzando lo strumento vignetta. <br/> Per correggere vignettatura, aumentare <b>Vignette</b> a 20",
    "distortion_3": "<b>migliorare i dettagli</b> <br/> Infine, per migliorare i dettagli nella foto, è possibile utilizzare lo strumento chiarezza. <br/> Aumentare la <b>chiarezza</b> a 100",
    "distortion_4": "Buon lavoro! Hai imparato a correggere i difetti delle lenti comuni in 3 semplici passi.",
    "distortion_amount": "Aggiungi un effetto panoramico o fish-eye alle foto.",
    "distortion_description": "In questo tutorial, imparerete come correggere distorsioni delle lenti e vignetta negli scatti architettonici.",
    "distortion_horizontal": "Cambiare la distorsione lungo l'asse orizzontale",
    "distortion_vertical": "Cambiare la distorsione lungo l'asse verticale",
    "exposure": "Modificare la luminosità senza lavare le foto",
    "face_0": "Polarr rileva automaticamente i volti e separa le singole caratteristiche.",
    "face_1": "Utilizzare lo strumento Distort per regolare la dimensione e la posizione dei lineamenti del viso.",
    "fringing": "Aggiungi un effetto \"vorticoso\" alla tua foto.",
    "gradient_mask_0": "Maschera Gradiente colpisce le aree in una linea retta.",
    "gradient_mask_1": "Pinch ridimensionare. <br/> Trascinare per spostare e ruotare.",
    "gradient_mask_2": "<b>Inverti</b> cambia la direzione del gradiente.",
    "gradient_mask_3": "<b>Riflettere</b> rende il gradiente di funzionare in entrambe le direzioni.",
    "grain": "Aggiungere un effetto grana della pellicola",
    "highlights": "Modificare la luminosità dei punti salienti senza lavare le foto",
    "hsl": "Cambiare la tonalità, la saturazione, o la leggerezza di un colore specifico.",
    "hsl_header": "HSL",
    "hue_blue": "Scala colori specifici al blu.",
    "hue_red": "Scala colori specifici al rosso.",
    "luminance_blue": "Cambia la luminosità del blu.",
    "luminance_red": "Cambia la luminosità del rosso.",
    "mastering_adjustments": "mastering Regolazioni",
    "mosaic": "Dare la foto un effetto mosaico",
    "radial_mask_0": "maschera radiale riguarda solo una zona circolare.",
    "radial_mask_1": "Pinch ridimensionare. <br/> Trascinare per spostare e ruotare.",
    "radial_mask_2": "<b>Feather</b> cambia la sfocatura della maschera.",
    "radial_mask_3": "<b>Inverti</b> passa la zona interessata.",
    "saturation": "Aumenta o diminuisci l’intensità del colore.",
    "saturation_blue": "Cambia il \"blu\" dei colori blu.",
    "saturation_red": "Cambia il \"rosso\" dei colori rossi.",
    "shadows": "Modificare la luminosità delle ombre senza lavare le foto",
    "sharpen": "Incrementa il contrasto dei contorni per rendere la foto più definita.",
    "temperature": "Fai la foto più calda o più fredda",
    "tint": "Regola colore assieme alla gamma magenta-verde.",
    "toning_highlights": "Cambiare il colore dei punti salienti",
    "toning_shadows": "Cambiare il colore delle ombre",
    "vibrance": "Modificare la saturazione senza lavarsi i toni della pelle",
    "vignette_amount": "Fade o evidenzia i bordi della foto",
    "vignette_feather": "Cambiare la confusione della vignetta",
    "vignette_highlights": "Lasciate che i punti salienti mostrano attraverso la vignetta",
    "vignette_roundness": "Rendere la vignetta più rotondo o quadrato",
    "vignette_size": "Modificare la dimensione della vignetta",
    "white_balance": "Bilanciamento del bianco",
    "white_balance_1": "<b>Corretto bilanciamento del bianco</b> <br/> La maggior parte delle sorgenti luminose (lampadine, torce elettriche, ecc) non emettono colore bianco puro. Ciò può causare i bianchi nelle foto di avere un cast giallo o blu. Per correggere questo, diminuzione della <b>temperatura</b> a -30",
    "white_balance_2": "<b>Recuperare luce dalle ombre</b> <br/> A volte le parti di una foto sono più scuri del previsto perché i nostri occhi possono percepire più luce rispetto alla fotocamera. Siamo in grado di recuperare la luce dalle ombre utilizzando lo strumento ombre. <br/> Aumentare <b>Shadows</b> a 100",
    "white_balance_3": "<b>migliorare i dettagli</b> <br/> Infine, per migliorare i dettagli nella foto, è possibile utilizzare lo strumento chiarezza. <br/> Aumentare la <b>chiarezza</b> a 100",
    "white_balance_4": "Buon lavoro! Hai imparato come rimuovere dominante gialla e recuperare luce dalle ombre in 3 semplici step.",
    "white_balance_description": "In questo tutorial, si impara il concetto di bilanciamento del bianco e come correggere alla luce artificiale porta.",
    "whites": "Cambia la luminosità delle parti bianche della foto"
  }
};
},{}],328:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "追加",
    "add_image": "画像を選択してください",
    "add_to_favorites": "お気に入りに追加",
    "aspect": "様相",
    "back": "戻る",
    "buy": "購入",
    "buy_full_version": "フルバージョンを購入",
    "cancel": "キャンセル",
    "check_again": "もう一度チェック",
    "choose_folder": "フォルダを選択します",
    "clear": "クリア",
    "clear_history": "履歴を削除",
    "close": "閉じる",
    "cloud": "クラウド",
    "continue_anyway": "このまま続行",
    "current_image": "現在",
    "delete": "削除",
    "device": "デバイス",
    "dismiss": "退けます",
    "done": "完了",
    "dont_ask": "今後表示しない",
    "duplicate": "複製",
    "enter_license": "ライセンスキーを入力します",
    "enter_license_key": "ライセンスキーを入力します",
    "exit": "出口",
    "export": "エクスポート",
    "export_filter": "QRコードとして保存",
    "forgot_password": "パスワードをお忘れですか？",
    "free_membership": "無料のメンバーシップ",
    "get": "取得する",
    "get_license": "ライセンスキーを取得します。",
    "get_started": "始めます",
    "got_it": "とった",
    "have_account": "すでにアカウントをお持ちですか？",
    "import": "インポート",
    "import_filter": "インポートフィルタ",
    "import_filter_photos": "写真からインポート",
    "import_photos": "オープン写真",
    "info": "インフォ",
    "installed": "インストール済み",
    "invite": "リンクを送信",
    "learn_more": "もっと詳しく知る",
    "load_last_photo": "起動時にロード最後の写真",
    "new": "新規",
    "new_filter": "フィルターを作成",
    "next": "次へ",
    "no": "いいえ",
    "no_thanks": "いいえ、結構です",
    "not_really": "そういう訳ではない",
    "not_this_time": "今はやめておく",
    "open_sample": "サンプル写真を開く",
    "paste_adjustments": "ペーストの調整",
    "pay_wechat": "オープン微信",
    "prev": "前へ",
    "preview": "プレビュー",
    "pro": "有償版",
    "purchased": "購入済み",
    "remove": "削除",
    "remove_all_photos": "すべての写真を削除します",
    "remove_photo": "写真を削除",
    "remove_selected_photo": "選択した写真を削除します",
    "reset": "リセット",
    "reset_all": "全てリセット",
    "reset_password": "パスワードを再設定する",
    "reset_photos": "保存され、編集されたラベルをリセット",
    "reset_tips": "ポイントをリセット",
    "restore": "リストア",
    "revert": "最初に戻す",
    "sample": "サンプル",
    "save": "保存",
    "save_qr": "QRコードを保存",
    "save_share": "保存+シェア",
    "scan_filter": "スキャンフィルタ",
    "select_all": "全て選択",
    "select_changed": "編集済みを選択",
    "send": "送信",
    "send_verification": "確認メールを再送",
    "show_in_folder": "フォルダで見て",
    "show_me": "見せて",
    "signin": "ログイン",
    "signout": "ログアウト",
    "signup": "Polarrアカウントを作成",
    "skip": "スキップ",
    "submit_license_key": "ライセンスキーを登録します",
    "subscribe": "申し込む",
    "subscribe_monthly": "毎月購読",
    "subscribe_yearly": "年間購読",
    "sure": "はい、もちろんです",
    "try": "試します",
    "try_again": "再試行",
    "unlock": "ロック解除",
    "unsubscribe": "登録解除",
    "update": "更新",
    "whats_new": "新着情報",
    "yes": "はい"
  },
  "dialogs": {
    "about": "本アプリについて",
    "about_text": "私たちは、想像への情熱やインスピレーションを失うことなく、誰もが簡単に自分自身の考えや人生の在り方を表現できることが重要だと考えています。",
    "account": "アカウント",
    "all_filters": "すべてのフィルタ",
    "all_filters_text": "プレミアムフィルターのPolarrのコレクション全体のロックを解除",
    "backup_filters": "あなたはデバイスやプラットフォーム間でそれらを使用できるように、すべてのカスタムフィルタを同期とバックアップします。この機能を使用すると、Polarrアカウントでログインが必要です。",
    "backup_filters_title": "バックアップカスタムフィルタ",
    "brush_limit": "現在のブラシマスクの制限があります</limit>",
    "denoise": "当社のリアルタイムのノイズ除去処理は、写真のノイズや穀物を除去することが容易になります。これとは別に、色と輝度ノイズ成分を制御します。",
    "featured": "おすすめ",
    "filter_collections": "フィルターのコレクション",
    "filter_name": "このフィルタに名前を付けます",
    "get_picky": "写真編集のうんざりしていませんか？人工知能で写真を編集するPolarrの新しいアプリを試してみてください！",
    "get_updates": "更新情報を購読します",
    "get_updates_about": "新機能を試してみて最初に！ <br/>あなたのメールアドレスを追加し、我々はPolarrから新機能と製品についてお知らせします。",
    "get_updates_thankyou": "あなたは今Polarrから新機能および製品に関する最新情報を取得します！ <br/>あなたはアップデートの受信を停止したい場合は、いつでもあなたの電子メールから「退会」することができます。",
    "help": "ヘルプ",
    "invite": "インストール用のリンクを共有して下さい。有償版の機能は、3人がこのリンク経由でアプリをインストールした後に、使用可能となります。",
    "local_adjustments": "地元の調整では、対象地域での調整のPolarrの強力なセットを使用することができます。ラジアル、グラデーション、色、およびブラシマスキングツールが含まれています。",
    "lock_rotation": "ロック回転",
    "max_export_size": "最大書き出しサイズ",
    "new_filter": "保存は、フィルタとして編集します",
    "no_purchases": "復元する購入はありません。",
    "offline": "お使いのデバイスはオフラインです。後で再試行して下さい。",
    "photos": "写真",
    "polarr_premium": "Polarrプレミアム",
    "pro_all_filters": "Proの機能<br/> + <br/>すべてのフィルタ",
    "pro_all_filters_text": "Pro ToolsとPolarrのプレミアムすべてのフィルタを含むすべての機能のロックを解除します。他のプラットフォーム上のすべての機能にアクセスするには、Polarrアカウントを使用してください。バックアップとカスタムフィルタを同期します。",
    "pro_features": "Pro Toolsの",
    "pro_features_all": "すべてのPro Tools",
    "purchase": "ラジアル、グラデーション、色、ブラシ、ノイズ除去、およびテキストツールのロックを解除します。バックアップとカスタムフィルタを同期します。",
    "purchase_failed": "購入がキャンセルされました",
    "purchase_header": "全ての有償版の機能を解除する",
    "purchase_successful": "有償版へアクセスが可能です。",
    "purchase_unavailable": "申し訳御座いませんが、現在、購入ができません。",
    "purchases": "購入一覧",
    "rate_review": "評価とクチコミ",
    "rename_filter": "フィルターの名前を変更します",
    "restore_failed": "あなたのアカウントには、完全な機能へのアクセスを持っていません。",
    "restore_successful": "あなたのすべての以前の購入が復元されています。",
    "review": "Polarr写真エディタはお楽しみ頂けていますか？",
    "review_no": "ご感想を頂けますか？",
    "review_yes": "App Storeでご評価を頂けますか？",
    "save_adjustments": "あなたが共有できるカスタムフィルタを作成します。",
    "send_feedback": "感想を送る",
    "signin_polarr_account": "あなたのPolarrアカウントにログイン",
    "store": "格納",
    "subscribe": "<strong>Polarrプレミアム</strong>にサインアップ",
    "subscribe_access": "へのフルアクセスのために：",
    "subscribe_cancel": "設定はいつでもですることができます「登録解除」",
    "subscribe_caveat": "* Proの機能は、勾配調整、ラジアル調整、カラーマスキング、カラーブラッシングが含まれます。あなたのメンバーシップは、今年のすべてのデバイスやプラットフォームに適用されます。",
    "subscribe_china": "プロの機能を有効にするには、あなたのPolarrアカウントでログインした後、微信アカウントをpolaxiongkejiと微信で支払いを購読。",
    "subscribe_error": "おっとっと！ Polarrに加入してエラーが発生しました。再試行する？",
    "subscribe_features": "<li> Proの機能（4ローカル調整ツール） </li><li>全12プレミアムフィルターパックにアクセス</li><li>滞在RAW形式の更新</li><li>新しいコンテンツが毎月追加しました！ </li><li>すべてのプラットフォーム上でのアクセスのメンバーシップ* </li>",
    "subscribe_notify": "私Polarrプレミアム会員は、より多くのプラットフォームやデバイス上で使用することができたら通知します",
    "subscribe_price": "</price>",
    "subscribe_refund": "あなたは以前にPolarrで何かを購入し、あなたがのために資格があります",
    "subscribe_success": "Polarrプレミアムに加入していただきありがとうございます！",
    "subscribe_unlock_success": "あなたは今Polarrプレミアムメンバーです！",
    "subscription_expired": "あなたのPolarrプレミアムメンバーシップの有効期限が切れています",
    "text_tool": "テキストツールを使用すると、写真にテキストや図形を追加することができます。文字と行間隔、配置、ラッピングなどの機能を使用すると、完全に制御あなたのテキストを持っています。私たちも、動的なベクトルスケーリングと独自のブレンド調整などの高度な機能を用意しました。",
    "text_tool_description": "追加し、画面上のテキストを編集します。回転させ、変化の色は、ブレンドし、さらには文字間隔を調整します。",
    "thankyou": "ありがとうございました！",
    "unlock_all": "すべてのロックを解除します",
    "unlock_all_note": "あなたのPolarrプロのアカウントを、ログインまたはライセンスキーを入力してください。",
    "unlock_alternate": "また、既存のアカウントにログインするか、でライセンスキーを入力することでアップグレードすることができます",
    "unlock_failed": "Proの機能は、このアカウントでは使用できません。",
    "unlock_success": "これで、すべての機能にアクセスできます。",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a>と<a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> 、無料の高解像度ストック写真のための最良のソースは、あなたのUnsplashカメラマンコミュニティのメンバーが作成した10プレミアムフィルタのコレクションをもたらすために提携しています。 </p>",
    "upgrade": "以下のすべての機能のロックを解除するためにフルバージョンにアップグレードしてください。",
    "upgrade_caveat": "* 12追加のフィルタパックはクラシック映画、ヴィンテージフィルム、赤外線フィルム、スキントーンと様々なテーマやシーンに適した他の多くの調整のシミュレーションを含みます。",
    "upgrade_features": "<li>グラデーションの調整</li><li>円形の調整</li><li>カラーマスキング</li><li>ブラッシング調整</li><li> 12追加のプレミアムフィルターパック</li><li>バッチ写真のエクスポート</li>",
    "use_polarrfilter": "注目を集めるために<strong>#polarrfilter</strong>使用してください！",
    "user_in_paid": "あなたはすべての機能にアクセスするには、他のプラットフォーム上でPolarrにログインすることができます。",
    "user_in_unpaid": "あなたはすべてのロックを解除する場合は、他のプラットフォーム上でPolarrにログインし、すべての機能にアクセスできるようになります。",
    "verification_sent": "確認メールが送信されました。 <br/>検証手順については、お使いのメールをご確認ください。",
    "verify_account": "ログインするには、アカウントを確認してください。 <br/>検証手順については、お使いのメールをチェックしてください。",
    "webgl_not_supported": "申し訳御座いませんが、Polarrのグラフィック設定にお使いのデバイスが対応していません。最新のOSにアップデートし再試行して下さい。"
  },
  "labels": {
    "adjustments": "調整",
    "after": "後",
    "alignment": "アラインメント",
    "all": "全て",
    "amount": "量",
    "angle": "角度",
    "aqua": "水色",
    "artwork": "芸術作品",
    "auto": "自動",
    "balance": "バランス",
    "basic": "基本",
    "batch_rename": "一括リネーム",
    "batch_resize": "バッチサイズ変更",
    "before": "前",
    "best": "最高",
    "blacks": "黒",
    "blend": "ブレンド",
    "blend_mode": "ブレンドモード",
    "blue": "青",
    "blur": "ブラー",
    "brightness": "明度",
    "brush_flow": "ブラシの流れ",
    "brush_opacity": "ブラシの不透明度",
    "brush_size": "ブラシサイズ",
    "clarity": "鮮明度",
    "collection": "コレクション",
    "color": "色",
    "contrast": "明暗",
    "crop": "切り抜き",
    "crop_size": "クロップ寸法",
    "curves": "カーブ",
    "custom": "カスタム",
    "dehaze": "霞の除去",
    "denoise": "ノイズの除去",
    "detail": "詳細",
    "diffuse": "放散",
    "distortion": "ゆがみ",
    "distortion_amount": "ゆがみ",
    "distortion_horizontal": "水平方向の歪み",
    "distortion_vertical": "垂直歪み",
    "dividers": "ディバイダー",
    "edit": "編集",
    "effects": "効果",
    "email": "Eメール",
    "enable": "有効化",
    "estimated_size": "推定サイズ",
    "exposure": "露光",
    "eyes": "視線",
    "face": "面",
    "favorites": "お気に入り",
    "feather": "羽",
    "fit": "フィット",
    "font": "フォント",
    "format": "フォーマット",
    "free": "フリー",
    "frequent": "最も多く使用",
    "fringing": "フリンジ",
    "grain": "粒子",
    "green": "緑",
    "guides": "ガイド",
    "high": "高",
    "highlights": "強調",
    "histogram": "ヒストグラム",
    "history": "履歴",
    "hsl": "HSL",
    "hue": "色相",
    "hue_blue": "HSL - 青 色相",
    "hue_red": "HSL -  赤 色相",
    "invert": "反転",
    "lens": "レンズ",
    "lens_distortion": "レンズの歪み",
    "letter_spacing": "文字間隔",
    "license_key": "ライセンスキー",
    "light": "光",
    "line_spacing": "行間隔",
    "lips": "唇",
    "look": "フィルター",
    "looks": "フィルター",
    "low": "低",
    "luminance": "輝度",
    "luminance_blue": "HSL - 青 輝度",
    "luminance_red": "HSL - 赤 輝度",
    "magenta": "赤紫",
    "master": "マスター",
    "max_size": "最大寸法",
    "med": "中",
    "mix": "全て調整",
    "mosaic": "ピクセル化",
    "name": "名",
    "none": "なし",
    "nose": "鼻",
    "opacity": "不透明度",
    "optics": "光学",
    "options": "オプション",
    "orange": "オレンジ",
    "original": "オリジナル",
    "outline": "アウトライン",
    "password": "パスワード",
    "position": "位置",
    "prefix": "プレフィックスセグメント",
    "presets": "プリセット",
    "preview": "プレビュー",
    "purple": "紫",
    "quality": "質",
    "ratio": "比率",
    "red": "赤",
    "reflect": "反映します",
    "resolution": "解決",
    "rgb": "RGB",
    "roundness": "真円度",
    "saturation": "彩度",
    "saturation_blue": "HSL - 青 彩度",
    "saturation_red": "HSL - 赤 彩度",
    "sayings": "ことわざ",
    "shadow": "影",
    "shadows": "シャドー",
    "shape": "形状",
    "shapes": "シェイプ",
    "shapes_presets": "シェイプ＆プリセット",
    "sharpen": "シャープ",
    "size": "寸法",
    "skin": "肌",
    "smoothness": "スムージング",
    "split_tone": "トーン",
    "teeth": "ティース",
    "temperature": "一時的",
    "text": "テキスト",
    "texture": "テクスチャー",
    "threshold": "しきい値",
    "tint": "ティント",
    "tone": "トーン",
    "toning": "トーン",
    "use_prefix": "接頭辞を使用してください",
    "use_watermark": "ウォーターマークを使用してください",
    "user_name": "ユーザー名",
    "vibrance": "自然な彩度",
    "vignette": "寸描",
    "vignette_amount": "ビネット",
    "watermark_image": "透かし画像",
    "wb": "ホワイトバランス",
    "whitening": "ホワイトニング",
    "whites": "白",
    "width": "幅",
    "yellow": "黄色"
  },
  "menus": {
    "batch_export": "バッチエクスポート",
    "change_language": "言語を変更",
    "copy_edit": "コピー編集",
    "edit_watermark": "ウォーターマークの編集",
    "export": "フォーマット、サイズ変更、透かし",
    "filters_users": "Polarristsによってフィルタ",
    "guide": "ガイド",
    "image_quality": "画質",
    "image_settings": "設定をエクスポート",
    "language": "言語",
    "more_filters": "他のフィルター",
    "open": "開く",
    "open_in": "以下で開く：",
    "paste_edit": "貼り付け編集",
    "remove": "削除",
    "rename": "名前を変更",
    "restore_purchases": "購入をリストア",
    "save_cameraroll": "カメラロールに保存します",
    "save_copy": "コピーを保存",
    "settings": "設定",
    "share": "シェア",
    "share_edits": "シェアの編集",
    "share_filter": "共有フィルタ",
    "show_instructions": "調整ガイド",
    "show_intro": "表示イントロ",
    "show_video_tutorial": "動画での説明",
    "single_export": "輸出する"
  },
  "onboarding": {
    "become_master": "写真編集マスターになります。",
    "step_0": "Polarrへようこそ！ <br/>いくつかのフィルタを表示して始めることができます。",
    "step_1": "フィルタを選択するためにタップします。 <br/>複数のフィルタを表示するには、パネルをスワイプします。",
    "step_10": "あなたは、すべてのツールアイコンを再配置するドラッグすることができます。",
    "step_2": "タップ<i class='icon-check'></i>フィルタを適用します。 <br/>フィルタの強さを調整するには、スライダを使用してください。",
    "step_3": "すべてのフィルタは完全に編集可能です。 <br/>編集内容を確認するために調整に移動します。",
    "step_4": "あなたはスライダーをドラッグして調整を変更することができます。",
    "step_4a": "長押しは、次にスライダーを調整するためにドラッグします。",
    "step_5": "あなたは歴史のパネルで行ったすべての編集を参照してください。",
    "step_6": "あなたが編集を取り消して、歴史の中で任意の時点に戻ることができます。",
    "step_7": "ここにあなたのイメージを保存します。 <br/>あなたは、サイズ変更透かしを追加したり、複数の画像を保存することができます。",
    "step_7a": "ここにあなたのイメージを保存します。 <br/>また、複数の画像を共有し、保存することができます。",
    "step_8": "ここに自分の写真を開きます。",
    "step_9": "写真パネルでご開いた写真を表示します。"
  },
  "status": {
    "account_suspended": "アカウントが原因で不審なアクティビティに保留になります。詳細情報については、お使いのメールや連絡先をご確認ください<a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "タップ<i class='icon-add-face'></i>顔を追加します",
    "adjustment_copied": "コピーした調整",
    "adjustment_pasted": "貼り付けの調整",
    "demo_mode": "Polarrフォトエディタのこの​​バージョンは、デモ目的のために特別に作成されています",
    "downloading_font": "ダウンロードフォント",
    "drag_icons": "ツールを再配置するアイコンをドラッグ",
    "drop_files": "開くために、ファイルをドロップ",
    "email_invalid": "そのメールアドレスが有効ではないように申し訳ありませんが、それが見えます。",
    "email_reset": "リセットのリンクが送信されました。 <br/>メールを確認してください。",
    "exporting_filter": "フィルタを保存します",
    "field_invalid": "有効ではありません。",
    "file_exists": "その写真はすでにオープンしています",
    "file_not_supported": "申し訳ありませんが、そのファイルの種類がサポートされていません。",
    "filter_exported": "あなたのフィルターはカメラロールに保存されています。",
    "filter_saved": "フィルタ保存",
    "filters_sync_error": "エラー同期フィルタ",
    "filters_synced": "同期されたフィルタ",
    "filters_syncing": "フィルタを同期しています...",
    "import_filter_error": "申し訳ありませんが、そのフィルタをインポートできませんでした",
    "import_limit": "Polarrのみ50枚までの写真を開くことができ、より多くを開く前にいくつかの写真を削除してください。",
    "importing_filter": "フィルタのインポート",
    "importing_photo": "オープニングの写真",
    "importing_photos": "写真を開きます",
    "invalid_credentials": "間違ったメールアドレスまたはパスワード",
    "loading_photo": "写真を読み込み中",
    "login_success": "あなたが正常にログインされています！",
    "no_faces": "・顔が検出されません。",
    "no_user": "そのユーザーは存在しません。",
    "offline": "サーバーへ接続できませんでした。",
    "photo_load_error": "写真の読み込みができません",
    "photo_saved": "写真の保存完了",
    "photo_shared": "写真が共有されました",
    "photos_saved": "写真が保存され",
    "purchase_required": "購入必要",
    "raw_failed": "RAW画像を読み取ることができません。 RAWサポートはまだ実験的なものです。",
    "registration_success": "あなたは成功した新しいアカウントを作りました！ <br/>他のデバイスにログインするには、まずアカウントを確認する必要があります。私たちは、確認手順を記載したメールを送信しました。",
    "saving_photo": "保存写真",
    "saving_photo_error": "写真の保存エラー",
    "saving_photo_error_sd": "PolarrにはSDカードや内部ストレージへのアクセス権限がありません。問題を解決するには、最新のAndroidシステムアップデートを確認して下さい。",
    "saving_photos": "写真を保存します",
    "server_offline": "申し訳ありませんが、サーバーが応答していません。 <br/>あなたがインターネットに接続されていますか？",
    "sharing_photo": "写真を共有します",
    "sharing_photo_error": "写真の共有エラー",
    "show_me_how": "どのように使用する方法私を見ます</thing>",
    "supported_files": "PolarrはJPG、PNG、BMP、および特定のRAWファイルをサポートしています",
    "unlocks_remaining": "あなたが持っている</unlocks>残りのロックを解除",
    "user_exists": "そのメールまたはユーザー名を持つアカウントがすでに存在します。",
    "wrong_password": "間違ったパスワード"
  },
  "tooltips": {
    "add_face": "フェイスを追加",
    "add_text": "テキストを追加します。",
    "adjustments": "調整",
    "apply_adjustments": "調整を適用",
    "auto_adjust": "自動調整",
    "brush_filter": "ブラシマスク",
    "color_filter": "カラーマスク",
    "crop": "切り抜き",
    "crop_straighten": "トリミング＆アングル",
    "current_adjustments": "現在の調整",
    "double_tap_edit": "編集するダブルタップ",
    "drag_watermark": "透かしを​​移動するためにドラッグして",
    "eraser": "消しゴム",
    "face": "フェイスツール",
    "filmstrip": "開かれた写真",
    "flip": "回転",
    "flip_h": "左右反転",
    "flip_v": "上下反転",
    "global_adjustments": "グローバル調整",
    "gradient_filter": "勾配マスク",
    "guides": "ガイド",
    "history": "編集履歴",
    "info": "情報",
    "local_adjustments": "ローカル調整",
    "looks": "フィルター",
    "masks": "マスク",
    "move_face": "顔を移動します",
    "open": "オープン写真",
    "pan": "パン",
    "radial_filter": "放射マスク",
    "redo": "先に進める",
    "remove_face": "顔を削除",
    "remove_text": "テキストを削除します",
    "rotate": "ローテート",
    "rotate_ccw": "逆時計回りに回転",
    "rotate_cw": "時計回りに回転",
    "save": "写真を保存する",
    "settings": "設定",
    "show_original": "オリジナルを表示",
    "text": "テキストツール",
    "undo": "前に戻す"
  },
  "tutorial": {
    "blacks": "写真の黒い部分の明るさを変更します",
    "brightness": "写真の明るさや「ガンマ」に変更",
    "brush_mask_0": "ブラシでペイントするために指を使用してください。",
    "brush_mask_1": "ブラシマスクは起毛部に影響を与えます。",
    "clarify": "明らかにする",
    "clarify_1": "<b>霧を減らします</b> <br/>霧は、写真内の一般的な問題であり、それはPolarrで削除するのは簡単です。 <br/>霧を削除するには、85に<b>Dehazeを</b>高めます",
    "clarify_2": "<b>露出を回復</b> <br/> Dehazeは時々あまりにも多くの写真を暗くします。 <br/>いくつかの光を回復するには、30への<b>露出を</b>増やします",
    "clarify_3": "<b>コントラストを上げます</b> <br/>ヘイズはほとんどが今なくなっているが、我々は少しより多くの光のダイナミックを向上させることができます。 60 <b>コントラストを</b>上げます",
    "clarify_4": "<b>詳細を強化</b> <br/>写真の詳細を強化するために、我々は、クラリティツールを使用することができます。 40に<b>クラリティを</b>増やします",
    "clarify_5": "よくできました！あなたは4簡単な手順で多くの方かすんで写真を作る方法を学びました。",
    "clarify_description": "このチュートリアルでは、霧を減らすアウトラインを定義し、対象に鮮やかさを追加する方法を学習します。",
    "clarity": "写真で詳細を増やすか、滑らか",
    "color_mask_0": "カラーマスクは選択した色に影響を与えます。",
    "color_mask_1": "選択した色を変更するには、ドラッグします。",
    "color_mask_2": "<b>しきい値は、</b>選択の感度を設定します。",
    "contrast": "写真の対象をより明確にする効果です",
    "curves": "写真のトーンカーブを変更します",
    "deep_space": "深宇宙",
    "deep_space_1": "<b>光を回復</b> <br/> Polarrの露出調整は、カメラの露出をシミュレートします。 <br/>光を回復するには、100への<b>露出を</b>設定します",
    "deep_space_2": "<b>詳細を強化</b> <br/>それでは、星をより印象的に見えるようにしましょう​​。 <br/>明瞭さを追加すると、詳細を明らかにすることによって役立ちます。 <br/>詳細を強化するために、100に<b>クラリティを</b>設定",
    "deep_space_3": "<b>深さを追加します。</b> <br/>画像の中心に、よりフォーカスを移動するために、我々は写真の縁を暗くするビネットを追加することができます。 <br/>深さを追加するには、-100に<b>ビネットを</b>減少<b>さ</b>せます",
    "deep_space_4": "<b>滑らかなビネット</b> <br/>今ビネットはあまりにもシャープに見えます。 <br/>ビネットは滑らかで、微妙に見えるようにするには、 <br/> 100に<b>フェザーを</b>高めます",
    "deep_space_5": "<b>ハイライトを回復</b> <br/>写真のエッジの星の一部が暗いに見えます。私たちは、100に<b>ハイライトを</b>増加させることにより、それらを回復することができます",
    "deep_space_6": "<b>変色します</b> <br/> HSLは、あなたが特定の色の属性を変更することができます。空の色を変更するには、 <b>アクアフエは</b> 100に増加します",
    "deep_space_7": "よくできました！あなたは星空が本当に印象的に見えるようにする方法を学びました。",
    "deep_space_description": "このチュートリアルでは、夜空の露出を、深さを追加色を調整し、変更する方法を学びます。",
    "dehaze": "写真から曇りを追加または削除",
    "denoise": "写真からノイズを除去",
    "diffuse": "写真に「グロー」効果を与えます",
    "distortion": "ねじれ",
    "distortion_1": "<b>正しい歪み</b> <br/>私たちはしばしば、建物や壁が写真に曲がって見て参照してください。これは通常、広角カメラレンズの設計に起因します。私たちは簡単に歪みツールでPolarrでこの問題を解決することができます。 <br/>歪みを修正するには、 <b>垂直方向の歪みは</b> 70に増加します",
    "distortion_2": "<b>正しいビネット</b> <br/>光学的歪みへの追加では、ほとんどのカメラレンズは、それらの縁付近の光の放射性降下物を持っています。私たちは、ビネットツールを使用して、これらの暗い縁を減らすことができます。 <br/>ビネットを修正するには、20に<b>ビネットを</b>高めます",
    "distortion_3": "<b>詳細を強化</b> <br/>最後に、写真の細部を強化するために、我々は明快ツールを使用することができます。 <br/> 100に<b>クラリティを</b>増やします",
    "distortion_4": "よくできました！あなたは3簡単な手順で一般的なレンズの欠陥を修正する方法を学びました。",
    "distortion_amount": "写真に広角や魚眼効果を追加します",
    "distortion_description": "このチュートリアルでは、建築のショットでレンズの歪みとビネットを修正する方法を学習します。",
    "distortion_horizontal": "水平軸に沿って歪みを変更",
    "distortion_vertical": "縦軸に沿って歪みを変更",
    "exposure": "写真を洗浄せずに明るさを変更",
    "face_0": "Polarrが自動的に顔を検出し、個々の機能を分離します。",
    "face_1": "顔の特徴の大きさと位置を調整するために歪まツールを使用します。",
    "fringing": "写真にめまいに似たような効果を追加します",
    "gradient_mask_0": "グラデーションマスクは、直線内の領域に影響を与えます。",
    "gradient_mask_1": "サイズを変更するピンチ。 <br/>ドラッグして移動し、回転させます。",
    "gradient_mask_2": "<b>反転は、</b>勾配方向を切り替えます。",
    "gradient_mask_3": "<b>反映は</b>両方向に実行勾配を作ります。",
    "grain": "フィルムグレイン効果を追加",
    "highlights": "写真を洗浄することなく、ハイライトの明るさを変更します",
    "hsl": "色相、彩度、または特定の色の明るさを変更します。",
    "hsl_header": "HSL",
    "hue_blue": "特定の色を青に変えます",
    "hue_red": "特定の色を赤に変えます",
    "luminance_blue": "青の明るさを変更します",
    "luminance_red": "赤の明るさを変更します",
    "mastering_adjustments": "マスタリング調整",
    "mosaic": "写真にモザイク効果を与えます",
    "radial_mask_0": "ラジアルマスクは円形領域に影響を与えます。",
    "radial_mask_1": "サイズを変更するピンチ。 <br/>ドラッグして移動し、回転させます。",
    "radial_mask_2": "<b>フェザーは、</b>マスクの曖昧さを変更します。",
    "radial_mask_3": "<b>反転は、</b>患部を切り替えます。",
    "saturation": "色の程度を高めたり下げたりする効果です",
    "saturation_blue": "青の度合いを変更します",
    "saturation_red": "赤の度合いを変更します",
    "shadows": "写真を洗浄することなく、影の明るさを変更します",
    "sharpen": "コントラストを高め写真を引き締めます",
    "temperature": "写真は暖かいまたはクーラー作ります",
    "tint": "赤紫─緑に基づき色を調整します",
    "toning_highlights": "ハイライトの色を変更",
    "toning_shadows": "影の色を変更",
    "vibrance": "肌のトーンを洗浄せずに彩度を変更",
    "vignette_amount": "フェードや写真のエッジを強調",
    "vignette_feather": "ビネットのあいまいさを変更します",
    "vignette_highlights": "ハイライトはビネットを通してお見せしましょう",
    "vignette_roundness": "ビネットより円形または正方形を作ります",
    "vignette_size": "ビネットのサイズを変更",
    "white_balance": "ホワイトバランス",
    "white_balance_1": "<b>正しいホワイトバランス</b> <br/>ほとんどの光源（電球、懐中電灯など）は、純粋に白色を放出しません。これは、写真の中の白人が黄色または青色のキャストを持たせることができます。これを修正するには、-30に<b>温度を</b>低下<b>さ</b>せます",
    "white_balance_2": "<b>影からの光を回復</b> <br/>私たちの目がカメラより多くの光を知覚することができるので、時々、写真の一部が意図したよりも暗いです。私たちは、影ツールを使用して、影からの光を回復することができます。 <br/> 100に<b>影を</b>増やします",
    "white_balance_3": "<b>詳細を強化</b> <br/>最後に、写真の細部を強化するために、我々は明快ツールを使用することができます。 <br/> 100に<b>クラリティを</b>増やします",
    "white_balance_4": "よくできました！あなたは黄色のキャストを削除し、3簡単なステップで影から光を回復する方法を学びました。",
    "white_balance_description": "このチュートリアルでは、ホワイトバランス、どのようにドア人工光の中で修正するの概念を学びます。",
    "whites": "写真の白い部分の明るさを変更します"
  }
};
},{}],329:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "추가",
    "add_image": "이미지를 선택",
    "add_to_favorites": "즐겨 찾기에 추가",
    "aspect": "면",
    "back": "뒤로",
    "buy": "구매",
    "buy_full_version": "풀 버젼 구매",
    "cancel": "취소",
    "check_again": "재확인",
    "choose_folder": "폴더를 선택",
    "clear": "제거",
    "clear_history": "기록 삭제",
    "close": "닫다",
    "cloud": "구름",
    "continue_anyway": "어째든 계속하다",
    "current_image": "현재",
    "delete": "삭제",
    "device": "장치",
    "dismiss": "버리다",
    "done": "완료",
    "dont_ask": "다시 물어보지 마세요",
    "duplicate": "복제",
    "enter_license": "라이센스 키를 입력",
    "enter_license_key": "라이센스 키를 입력",
    "exit": "출구",
    "export": "수출",
    "export_filter": "QR 코드로 저장",
    "forgot_password": "비밀번호를 잊으 셨나요?",
    "free_membership": "무료 회원",
    "get": "도망",
    "get_license": "라이센스 키를 가져옵니다",
    "get_started": "시작하다",
    "got_it": "알았다",
    "have_account": "이미 계정을 가지고?",
    "import": "수입",
    "import_filter": "가져 오기 필터",
    "import_filter_photos": "사진에서 가져 오기",
    "import_photos": "열기 사진",
    "info": "정보",
    "installed": "설치됨",
    "invite": "링크 보내기",
    "learn_more": "자세히 알아보기",
    "load_last_photo": "발사에 마지막 사진을로드",
    "new": "새로운",
    "new_filter": "필터 만들기",
    "next": "다음",
    "no": "아니오",
    "no_thanks": "아니오, 괜찮습니다",
    "not_really": "설마",
    "not_this_time": "지금은 말고요",
    "open_sample": "샘플 사진 열기",
    "paste_adjustments": "붙여 넣기 조정",
    "pay_wechat": "열기 위챗",
    "prev": "이전",
    "preview": "시사",
    "pro": "프로",
    "purchased": "구매됨",
    "remove": "지우다",
    "remove_all_photos": "모든 사진을 삭제",
    "remove_photo": "사진 삭제",
    "remove_selected_photo": "선택한 사진을 삭제",
    "reset": "다시설정",
    "reset_all": "모두 다시설정",
    "reset_password": "암호를 재설정",
    "reset_photos": "저장 및 편집 라벨을 재설정",
    "reset_tips": "팁들 재설정",
    "restore": "복구",
    "revert": "원본으로 돌아가다",
    "sample": "샘플",
    "save": "저장",
    "save_qr": "QR 코드를 저장",
    "save_share": "저장 + 공유",
    "scan_filter": "검색 필터",
    "select_all": "모두 선택",
    "select_changed": "편집 선택",
    "send": "보내기",
    "send_verification": "확인 이메일을 다시 보내",
    "show_in_folder": "폴더보기",
    "show_me": "보여줘",
    "signin": "로그인",
    "signout": "로그 아웃",
    "signup": "Polarr 계정 만들기",
    "skip": "건너뛰다",
    "submit_license_key": "라이센스 키를 제출",
    "subscribe": "구독",
    "subscribe_monthly": "월간 구독",
    "subscribe_yearly": "연간 구독",
    "sure": "좋아요",
    "try": "시험",
    "try_again": "다시 시도",
    "unlock": "풀다",
    "unsubscribe": "탈퇴",
    "update": "업데이트",
    "whats_new": "별일 없어요",
    "yes": "네"
  },
  "dialogs": {
    "about": "대하여",
    "about_text": "저희는 그들의 창조 의지와 영감을 잃지 않고 사람들이 쉽게 그들의 견해와 삶의 태도를 표현하는 것이 중요하다고 생각합니다.",
    "account": "계정",
    "all_filters": "모든 필터",
    "all_filters_text": "프리미엄 필터의 Polarr의 전체 컬렉션을 잠금 해제",
    "backup_filters": "동기화 및 장치와 플랫폼에서 사용할 수 있도록 모든 사용자 지정 필터를 백업합니다. 이 기능을 사용하면 Polarr 계정으로 로그인이 필요합니다.",
    "backup_filters_title": "백업 사용자 정의 필터",
    "brush_limit": "현재 브러쉬 마스크 제한이 </limit>",
    "denoise": "우리 실시간 노이즈 제거 과정은 쉽게 사진 노이즈 및 그레인을 제거 할 수있다. 별도로 색상 및 휘도 노이즈 구성 요소를 제어 할 수 있습니다.",
    "featured": "추천",
    "filter_collections": "필터 모음",
    "filter_name": "이 필터의 이름을",
    "get_picky": "사진 편집의 피곤 한가? 인공 지능과 사진을 편집 Polarr의 새로운 응용 프로그램을 사용 해보세요!",
    "get_updates": "업데이트 구독",
    "get_updates_about": "새로운 기능을 시도 할 첫번째! <br/> 당신의 이메일 주소를 추가하고 우리는 Polarr에서 새로운 기능과 제품에 대해 알려드립니다.",
    "get_updates_thankyou": "이제 Polarr에서 새로운 기능과 제품에 대한 업데이트를 얻을 것이다! <br/> 업데이트를 수신을 중지하려면, 당신은 언제든지 이메일에서 \"취소\"할 수 있습니다.",
    "help": "도움",
    "invite": "설치 링크 공유. 세 명의 사람들이 당신의 링크를 이용해 설치한 이후에 프로 특징들이 풀릴 것입니다.",
    "local_adjustments": "지역 조정은 대상 지역에 조정 Polarr의 강력한 세트를 사용할 수 있습니다. 방사형, 그라데이션, 색상 및 브러쉬 마스킹 도구가 포함되어 있습니다.",
    "lock_rotation": "잠금 회전",
    "max_export_size": "최대 수출 크기",
    "new_filter": "저장 필터로 편집",
    "no_purchases": "복원 할 구매가 없습니다.",
    "offline": "당신의 기기가 오프라인입니다. 잠시 후 다시 시도해주세요.",
    "photos": "사진",
    "polarr_premium": "Polarr 프리미엄",
    "pro_all_filters": "프로 기능 <br/> + <br/> 모든 필터",
    "pro_all_filters_text": "프로 도구와 Polarr의 프리미엄 모든 필터를 포함한 모든 기능의 잠금을 해제합니다. 다른 플랫폼에서 모든 기능에 액세스 할 Polarr 계정을 사용합니다. 백업 및 사용자 정의 필터를 동기화 할 수 있습니다.",
    "pro_features": "프로 도구",
    "pro_features_all": "모든 프로 도구",
    "purchase": "방사형, 그라데이션, 색상, 브러쉬, 노이즈 제거, 및 텍스트 도구의 잠금을 해제합니다. 백업 및 사용자 정의 필터를 동기화 할 수 있습니다.",
    "purchase_failed": "구매가 취소되었습니다",
    "purchase_header": "모든 프로 특징들을 푸세요",
    "purchase_successful": "당신은 이제 프로 버전에 접속 가능합니다",
    "purchase_unavailable": "구매가 지금 가능하지 않습니다, 죄송합니다.",
    "purchases": "구매",
    "rate_review": "평가 및 리뷰",
    "rename_filter": "필터의 이름을 바꿉니다",
    "restore_failed": "귀하의 계정은 전체 기능에 액세스 할 수 없습니다.",
    "restore_successful": "모든 이전 구매는 이제 복원됩니다.",
    "review": "Polarr 사진 편집을 즐기고 계신가요?",
    "review_no": "저희에게 피드백을 남겨주시겠어요?",
    "review_yes": "그럼 앱 스토어에 등급을 매겨주시는 건 어때요?",
    "save_adjustments": "당신이 공유 할 수있는 사용자 정의 필터를 확인합니다.",
    "send_feedback": "피드백을 보내기",
    "signin_polarr_account": "당신의 Polarr 계정에 로그인",
    "store": "저장",
    "subscribe": "<strong>Polarr 프리미엄</strong> 가입",
    "subscribe_access": "전체 액세스 :",
    "subscribe_cancel": "설정 언제든지에서 할 수 있습니다 \"수신 거부\"",
    "subscribe_caveat": "* 프로 기능은 기울기 조정, 반경 방향 조정, 컬러 마스킹 및 색상 칫솔질을 포함한다. 회원은 올해 모든 장치와 플랫폼에 적용됩니다.",
    "subscribe_china": "위챗 계정을 polaxiongkeji과 위챗의 지불을, 다음 프로 기능을 활성화하기 위해 Polarr 계정으로 로그인 구독.",
    "subscribe_error": "아차! Polarr에 가입 오류가 발생했습니다. 다시 시도?",
    "subscribe_features": "<li> 프로 기능 (4 지역 조정 도구) </li><li> 12 개의 프리미엄 필터 팩에 액세스 </li><li> RAW 포맷에 대한 업데이트 유지 </li><li> 새로운 내용이 매달 추가! </li><li> 모든 플랫폼에서 액세스 회원 * </li>",
    "subscribe_notify": "내 Polarr 프리미엄 회원은 더 많은 플랫폼과 장치에서 사용할 수 있습니다 때 알림",
    "subscribe_price": "</price>",
    "subscribe_refund": "이전에 Polarr 뭔가를 구입하면받을 수 있습니다",
    "subscribe_success": "Polarr 프리미엄에 가입 해 주셔서 감사합니다!",
    "subscribe_unlock_success": "이제 Polarr 프리미엄 회원 야!",
    "subscription_expired": "당신의 Polarr 프리미엄 회원이 만료되었습니다",
    "text_tool": "텍스트 도구를 사용하면 사진에 텍스트와 도형을 추가 할 수 있습니다. 편지 쓰기 및 줄 간격, 정렬 및 포장 등의 기능으로, 당신은 완벽하게 제어 할 텍스트가 있습니다. 우리는 심지어 동적 벡터 확장 및 고유 한 혼합 조정과 같은 고급 기능을 포함 시켰습니다.",
    "text_tool_description": "추가하고 화면의 텍스트를 편집 할 수 있습니다. 회전, 색상 변경, 설정 혼합, 심지어 문자 간격을 조정합니다.",
    "thankyou": "고맙습니다!",
    "unlock_all": "모든 잠금을 해제",
    "unlock_all_note": "당신의 Polarr 프로 계정을 로그인 또는 라이센스 키를 입력합니다.",
    "unlock_alternate": "또한 기존 계정에 로그인하거나 라이센스 키를 입력하여 업그레이드 할 수 있습니다",
    "unlock_failed": "프로 특징이 계정을 사용할 수 없습니다.",
    "unlock_success": "이제 모든 기능에 액세스 할 수 있습니다.",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> 및 <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> , 무료 높은 해상도 stock photography위한 최고의 소스는 당신에게 Unsplash 사진 작가 커뮤니티의 회원들에 의해 생성 된 열 프리미엄 필터의 컬렉션을 가지고 협력하고있다. </p>",
    "upgrade": "모든 다음과 같은 기능의 잠금을 해제 정식 버전으로 업그레이드합니다.",
    "upgrade_caveat": "* 12 추가 필터 팩 클래식 필름, 빈티지 필름, 적외선 필름, 피부 톤과 다양한 피사체와 장면에 적합한 다른 많은 조정 시뮬레이션을 포함한다.",
    "upgrade_features": "<li> 그라데이션 조정 </li><li> 원형 조정 </li><li> 컬러 마스킹 </li><li> 칫솔질 ​​조정 </li><li> 12 추가 프리미엄 필터 팩 </li><li> 일괄 사진 내보내기 </li>",
    "use_polarrfilter": "<strong>#polarrfilter</strong> 사용주의하세요!",
    "user_in_paid": "당신은 모든 기능에 액세스 할 수있는 다른 플랫폼에서 Polarr에 로그인 할 수 있습니다.",
    "user_in_unpaid": "당신이 모든 잠금을 해제하면 모든 기능을 다른 플랫폼에서 Polarr에 로그인하여 액세스 할 수 있습니다.",
    "verification_sent": "확인 이메일이 전송되었습니다. <br/> 검증 지침에 대한 귀하의 이메일을 확인하시기 바랍니다.",
    "verify_account": "로그인하려면 계정을 확인하시기 바랍니다. <br/> 검증 지침 이메일을 확인합니다.",
    "webgl_not_supported": "정말로 죄송하지만 당신의 기기가Polarr 의 그래픽 요구 조건을 충족하지 못합니다, 최신 운영체제로 업그레이드 하신 후 다시 시도해주세요."
  },
  "labels": {
    "adjustments": "조정",
    "after": "이후",
    "alignment": "조정",
    "all": "모든",
    "amount": "양",
    "angle": "각도",
    "aqua": "아쿠아 색",
    "artwork": "작품",
    "auto": "자동",
    "balance": "균형",
    "basic": "기본",
    "batch_rename": "일괄 이름 변경",
    "batch_resize": "일괄 크기 조정",
    "before": "이전",
    "best": "최고",
    "blacks": "검은색",
    "blend": "혼합",
    "blend_mode": "혼합 모드",
    "blue": "파랑",
    "blur": "흐림",
    "brightness": "밝기",
    "brush_flow": "브러쉬 흐름",
    "brush_opacity": "브러시 불투명도",
    "brush_size": "브러쉬 크기",
    "clarity": "투명성",
    "collection": "콜렉션",
    "color": "색깔",
    "contrast": "대조",
    "crop": "자르다",
    "crop_size": "자르기 크기",
    "curves": "곡선",
    "custom": "주문제작",
    "dehaze": "안개 제거",
    "denoise": "노이즈 제거",
    "detail": "디테일",
    "diffuse": "분산",
    "distortion": "왜곡",
    "distortion_amount": "왜곡",
    "distortion_horizontal": "수평 왜곡",
    "distortion_vertical": "수직 왜곡",
    "dividers": "분할기",
    "edit": "편집하다",
    "effects": "효과",
    "email": "이메일",
    "enable": "가능함",
    "estimated_size": "예상 크기",
    "exposure": "노출",
    "eyes": "눈",
    "face": "얼굴",
    "favorites": "즐겨찾기",
    "feather": "깃털",
    "fit": "적당한",
    "font": "세례반",
    "format": "체재",
    "free": "비어 있는",
    "frequent": "가장 많이 사용된",
    "fringing": "술달기",
    "grain": "갈다",
    "green": "녹색",
    "guides": "가이드",
    "high": "높은",
    "highlights": "하이라이트",
    "histogram": "막대그래프",
    "history": "기록",
    "hsl": "HSL",
    "hue": "색조",
    "hue_blue": "HSL – 파랑 색조",
    "hue_red": "HSL – 빨강 색조",
    "invert": "도치",
    "lens": "렌즈",
    "lens_distortion": "렌즈 왜곡",
    "letter_spacing": "문자 간격",
    "license_key": "라이센스 키",
    "light": "밝은",
    "line_spacing": "줄 간격",
    "lips": "입술",
    "look": "필터",
    "looks": "필터들",
    "low": "낮은",
    "luminance": "밝기",
    "luminance_blue": "HSL – 파랑 밝기",
    "luminance_red": "HSL – 빨강 밝기",
    "magenta": "자홍색",
    "master": "마스터",
    "max_size": "최대 크기",
    "med": "중간",
    "mix": "모두 조정",
    "mosaic": "픽셀 화",
    "name": "이름",
    "none": "없음",
    "nose": "코",
    "opacity": "불투명",
    "optics": "광학",
    "options": "옵션",
    "orange": "오렌지",
    "original": "원본",
    "outline": "개요",
    "password": "암호",
    "position": "포지션",
    "prefix": "접두사 세그먼트",
    "presets": "사전",
    "preview": "시사",
    "purple": "보라색",
    "quality": "질",
    "ratio": "비율",
    "red": "빨강",
    "reflect": "비추다",
    "resolution": "해결",
    "rgb": "빨강녹색파랑",
    "roundness": "원형도",
    "saturation": "포화도",
    "saturation_blue": "HSL – 파랑 포화도",
    "saturation_red": "HSL – 빨강 포화도",
    "sayings": "말",
    "shadow": "그림자",
    "shadows": "그림자",
    "shape": "모양",
    "shapes": "모양",
    "shapes_presets": "모양 및 사전 설정",
    "sharpen": "선명함",
    "size": "크기",
    "skin": "피부",
    "smoothness": "스무딩",
    "split_tone": "색조",
    "teeth": "이",
    "temperature": "온도",
    "text": "본문",
    "texture": "조직",
    "threshold": "문지방",
    "tint": "틴트",
    "tone": "음정",
    "toning": "색조",
    "use_prefix": "사용 접두사",
    "use_watermark": "워터 마크 사용",
    "user_name": "사용자 이름",
    "vibrance": "생동감",
    "vignette": "삽화",
    "vignette_amount": "비네트",
    "watermark_image": "워터 마크 이미지",
    "wb": "화이트 밸런스",
    "whitening": "희게 함",
    "whites": "하얀색",
    "width": "폭",
    "yellow": "노랑"
  },
  "menus": {
    "batch_export": "일괄 수출",
    "change_language": "언어 변경",
    "copy_edit": "복사 편집",
    "edit_watermark": "워터 마크 편집",
    "export": "형식, 크기 조정, 워터 마크",
    "filters_users": "Polarrists에 의해 필터",
    "guide": "가이드",
    "image_quality": "이미지 품질",
    "image_settings": "수출 설정",
    "language": "언어",
    "more_filters": "더 많은 필터들",
    "open": "열다",
    "open_in": "열다",
    "paste_edit": "붙여 넣기 편집",
    "remove": "제거",
    "rename": "이름 재설정",
    "restore_purchases": "구매 복구",
    "save_cameraroll": "카메라 롤에 저장",
    "save_copy": "복사본 저장",
    "settings": "설정들",
    "share": "공유",
    "share_edits": "공유 편집",
    "share_filter": "공유 필터",
    "show_instructions": "조정 가이드",
    "show_intro": "보기 소개",
    "show_video_tutorial": "영상 강의",
    "single_export": "수출"
  },
  "onboarding": {
    "become_master": "사진 편집 마스터가 되십시오.",
    "step_0": "Polarr에 오신 것을 환영합니다! <br/> 어떤 필터를 확인하여 시작할 수 있습니다.",
    "step_1": "필터를 선택 누릅니다. <br/> 스 와이프 ​​패널 이상의 필터를 볼 수 있습니다.",
    "step_10": "당신은 모든 도구 아이콘을 재 배열 할 드래그 할 수 있습니다.",
    "step_2": "꼭지 <i class='icon-check'></i> 필터를 적용합니다. <br/> 상기 필터의 강도를 조절하기 위해 슬라이더를 사용한다.",
    "step_3": "모든 필터는 완전히 편집 할 수 있습니다. <br/> 편집 한 내용을 볼 수 조정으로 이동합니다.",
    "step_4": "당신은 슬라이더를 드래그하여 조정을 변경할 수 있습니다.",
    "step_4a": "길게 눌러 다음 슬라이더를 조정 드래그합니다.",
    "step_5": "당신은 역사 패널에서 변경 한 모든 수정 사항을 참조하십시오.",
    "step_6": "당신은 편집을 취소하고 역사의 어느 시점으로 돌아갈 수 있습니다.",
    "step_7": "여기에 이​​미지를 저장합니다. <br/> 당신은 크기를 조정 워터 마크를 추가하거나 여러 이미지를 저장할 수 있습니다.",
    "step_7a": "여기에 이​​미지를 저장합니다. <br/> 또한 공유하고 여러 이미지를 저장할 수 있습니다.",
    "step_8": "여기에 자신의 사진을 엽니 다.",
    "step_9": "사진 패널에서 열 사진을 볼 수 있습니다."
  },
  "status": {
    "account_suspended": "계정이 때문에 의심스러운 활동에 보류됩니다. 더 많은 정보를 원하시면, 이메일 또는 연락처 확인하시기 바랍니다 <a href=\"mailto:support@polarr.co\">support@polarr.co을</a>",
    "add_face": "꼭지 <i class='icon-add-face'></i> 얼굴을 추가",
    "adjustment_copied": "조정 복사",
    "adjustment_pasted": "조정 붙여",
    "demo_mode": "Polarr 사진 편집기의 버전은 데모 용으로 특별히 제작 된",
    "downloading_font": "다운로드 글꼴",
    "drag_icons": "도구를 다시 정렬하는 아이콘을 드래그",
    "drop_files": "열 파일을 드롭",
    "email_invalid": "이메일 주소가 유효하지 않은 것처럼 죄송합니다, 그것은 본다.",
    "email_reset": "재설정 링크가 전송되었습니다. <br/> 이메일을 확인하시기 바랍니다.",
    "exporting_filter": "저장 필터",
    "field_invalid": "유효하지 않습니다.",
    "file_exists": "그 사진은 이미 열려",
    "file_not_supported": "죄송합니다, 해당 파일 형식이 지원되지 않습니다",
    "filter_exported": "필터는 카메라 롤에 저장되었습니다.",
    "filter_saved": "필터 저장",
    "filters_sync_error": "오류 동기화 필터",
    "filters_synced": "필터 동기화",
    "filters_syncing": "필터를 동기화 ...",
    "import_filter_error": "죄송합니다, 그 필터를 가져올 수 없습니다",
    "import_limit": "Polarr가 50 장의 사진을 열 수 있습니다, 자세한 내용을 열기 전에 약간의 사진을 삭제하시기 바랍니다.",
    "importing_filter": "가져 오기 필터",
    "importing_photo": "열기 사진",
    "importing_photos": "사진 열기",
    "invalid_credentials": "잘못된 이메일 또는 비밀번호",
    "loading_photo": "사지 로딩 중",
    "login_success": "당신은 성공적으로 로그인 한!",
    "no_faces": "아니 얼굴이 감지되지 않습니다.",
    "no_user": "즉, 사용자가 존재하지 않습니다.",
    "offline": "서버에 접속할 수 없습니다.",
    "photo_load_error": "사진을 불러올 수 없습니다",
    "photo_saved": "사진이 저장됨",
    "photo_shared": "사진이 공유되었습니다",
    "photos_saved": "사진 저장",
    "purchase_required": "구매 필요",
    "raw_failed": "RAW 이미지를 읽을 수 없습니다. RAW 지원은 아직 실험 단계입니다.",
    "registration_success": "성공적으로 새 계정을 만들었습니다! <br/> 다른 장치에 로그인하려면 먼저 계정을 확인해야합니다. 우리는 확인 안내가 포함 된 이메일을 보냈습니다.",
    "saving_photo": "저장 사진",
    "saving_photo_error": "사진 저장 에러:(",
    "saving_photo_error_sd": "Polarr 는 당신의 SD카드나 내부 저장에 접근할 권한이 없습니다. 이 문제를 고치려면 최신의 안드로이드 시스템 업그레이드 를 확인 해주세요.",
    "saving_photos": "사진 저장",
    "server_offline": "죄송합니다, 서버가 응답하지 않습니다. <br/> 인터넷에 연결되어 있습니까?",
    "sharing_photo": "공유 사진",
    "sharing_photo_error": "사진 공유 에러:(",
    "show_me_how": "사용하는 방법 알아보기 </thing>",
    "supported_files": "Polarr은 JPG, PNG, BMP, 특정 RAW 파일을 지원합니다",
    "unlocks_remaining": "당신은 </unlocks> 나머지 잠금을 해제",
    "user_exists": "이메일 또는 사용자 이름의 계정이 이미 있습니다.",
    "wrong_password": "잘못된 비밀번호"
  },
  "tooltips": {
    "add_face": "얼굴 추가",
    "add_text": "텍스트 추가",
    "adjustments": "조정",
    "apply_adjustments": "조정 적용",
    "auto_adjust": "자동 조정",
    "brush_filter": "브러쉬 마스크",
    "color_filter": "색상 마스크",
    "crop": "자르다",
    "crop_straighten": "자르기 및 각도",
    "current_adjustments": "현재 조정",
    "double_tap_edit": "두 번 탭 편집",
    "drag_watermark": "워터 마크를 이동하려면 드래그",
    "eraser": "지우개",
    "face": "얼굴 도구",
    "filmstrip": "열린 사진",
    "flip": "뒤집다",
    "flip_h": "수평으로 뒤집다",
    "flip_v": "수직으로 뒤집다",
    "global_adjustments": "세계적 조정",
    "gradient_filter": "경사도 마스크",
    "guides": "가이드",
    "history": "편집 역사",
    "info": "정보",
    "local_adjustments": "지방 조정",
    "looks": "필터",
    "masks": "마스크",
    "move_face": "면 이동",
    "open": "열기 사진",
    "pan": "팬",
    "radial_filter": "방사 마스크",
    "redo": "다시 하다",
    "remove_face": "얼굴을 제거",
    "remove_text": "텍스트를 제거",
    "rotate": "회전",
    "rotate_ccw": "회전CCW",
    "rotate_cw": "회전CW",
    "save": "저장 사진",
    "settings": "설정들",
    "show_original": "원본보기",
    "text": "텍스트 도구",
    "undo": "무효하다"
  },
  "tutorial": {
    "blacks": "사진의 검은 부분의 밝기를 변경",
    "brightness": "사진의 밝기 나 \"감마\"로 변경",
    "brush_mask_0": "브러시와 페인트 손가락을 사용합니다.",
    "brush_mask_1": "브러쉬 마스크는 브러시 영역에 영향을 미친다.",
    "clarify": "밝히다",
    "clarify_1": "<b>안개 감소</b> <br/> 안개는 사진에서 일반적인 문제이며 Polarr에서 쉽게 제거 할 수 있습니다. <br/> 안개를 제거하려면, 85 <b>Dehaze을</b> 증가",
    "clarify_2": "<b>노출을 복구</b> <br/> Dehaze 가끔 사진을 너무 많이 어두워집니다. <br/> 약간의 빛을 복구하려면 30에 <b>노출을</b> 증가",
    "clarify_3": "<b>대비를 증가</b> <br/> 헤이즈는 대부분 지금 사라,하지만 우리는 조금 더 빛 동적을 높일 수 있습니다. 60 <b>대비</b> 증가",
    "clarify_4": "<b>자세한 강화</b> <br/> 사진의 세부 사항을 향상시키기 위해, 우리는 명료 도구를 사용할 수 있습니다. 40 <b>선명도를</b> 증가",
    "clarify_5": "잘 했어! 당신은 4 가지 간단한 단계에서 훨씬 더 흐릿한 사진을 만드는 방법을 배웠다.",
    "clarify_description": "이 튜토리얼에서는, 당신은 안개를 감소 윤곽선을 정의하고 피사체에 선명도를 추가하는 방법을 배울 것입니다.",
    "clarity": "증가 또는 사진에서 세부 사항을 부드럽게",
    "color_mask_0": "색상 마스크는 선택한 색상에 영향을 미친다.",
    "color_mask_1": "드래그하여 선택한 색상을 변경합니다.",
    "color_mask_2": "<b>임계 값은</b> 선택의 감도를 설정합니다.",
    "contrast": "당신의 소재가 좀 더 식별될 수 있게 합니다.",
    "curves": "사진의 톤 커브를 변경",
    "deep_space": "딥 스페이스",
    "deep_space_1": "<b>빛을 복구</b> <br/> Polarr 노출 조정은 카메라의 노출을 시뮬레이션한다. <br/> 빛을 복구하려면 100 <b>노출</b> 설정",
    "deep_space_2": "<b>자세한 강화</b> <br/> 의이 별이 더 인상적 보이게 할 수 있습니다. <br/> 선명도를 추가하면 자세한 내용을 공개하여 도움이 될 것입니다. <br/> 100 <b>선명도를</b> 설정, 세부 사항을 향상시키기 위해",
    "deep_space_3": "<b>깊이를 추가</b> <br/> 이미지의 중심에 더 초점을 가지고, 우리는 사진의 가장자리를 어둡게 네트를 추가 할 수 있습니다. <br/> 깊이를 추가하려면 -100에 <b>네트를</b> 감소",
    "deep_space_4": "<b>부드러운 네트</b> <br/> 지금 림이 너무 날카로운 보인다. <br/> 네트가 부드럽고 미묘한 보이게하기 위해, <br/> (100)에 <b>깃털을</b> 증가",
    "deep_space_5": "<b>하이라이트 복구</b> <br/> 사진의 가장자리에있는 별 중 일부는 어둠에 보인다. 우리는 100에 <b>하이라이트를</b> 증가시킴으로써을 복구 할 수 있습니다",
    "deep_space_6": "<b>색상 변경</b> <br/> HSL은 특정 색상의 속성을 변경할 수 있습니다. 하늘의 색을 변경하려면, <b>아쿠아 색조</b> (100)로 증가",
    "deep_space_7": "잘 했어! 당신은 별이 빛나는 하늘이 정말 인상적 보이게하는 방법을 배웠다.",
    "deep_space_description": "이 튜토리얼에서는, 깊이를 추가 색상 변경 밤 하늘의 노출을 조정하는 방법을 배울 것입니다.",
    "dehaze": "추가 사진에서 안개를 제거",
    "denoise": "사진에서 노이즈를 제거",
    "diffuse": "사진에게 \"빛\"효과 보내기",
    "distortion": "왜곡",
    "distortion_1": "<b>올바른 왜곡</b> <br/> 우리는 종종 건물과 벽의 사진에 비뚤어진 찾고 참조하십시오. 이것은 일반적으로 광각 카메라 렌즈의 디자인에 기인한다. 우리는 쉽게 왜곡 도구 Polarr에서이 문제를 해결할 수 있습니다. <br/> 왜곡 문제를 해결하려면, (70)에 <b>수직 왜곡을</b> 증가",
    "distortion_2": "<b>올바른 림</b> <br/> 광학 왜곡에 추가, 대부분의 카메라 렌즈는 가장자리에 가까운 빛 다툼이있다. 우리는 짤막한 도구를 사용하여 이러한 어두운 가장자리를 줄일 수 있습니다. <br/> 네트를 해결하려면 (20)에 <b>네트를</b> 증가",
    "distortion_3": "<b>자세한 강화</b> <br/> 마지막으로, 사진의 세부 사항을 향상시키기 위해, 우리는 명확하게 도구를 사용할 수 있습니다. <br/> 100 <b>선명도를</b> 증가",
    "distortion_4": "잘 했어! 당신은 3 간단한 단계에서 일반적으로 렌즈 결함을 수정하는 방법을 배웠다.",
    "distortion_amount": "사진에 넓은 각도 추가 또는 어안 렌즈 효과 추가.",
    "distortion_description": "이 자습서에서는 건축 샷에서 렌​​즈 왜곡 및 짤막한를 해결하는 방법을 배우게됩니다.",
    "distortion_horizontal": "가로축 왜곡 변경",
    "distortion_vertical": "세로축 왜곡 변경",
    "exposure": "사진을 세척하지 않고 밝기를 변경",
    "face_0": "Polarr가 자동으로 얼굴을 인식하고 각각의 기능을 분리한다.",
    "face_1": "크기와 얼굴 특징의 위치를​​ 조정하기 위해 왜곡 도구를 사용합니다.",
    "fringing": "사진에 \"어지러움\" 효과를 추가.",
    "gradient_mask_0": "그라데이션 마스크는 직선 영역에 영향을 미칩니다.",
    "gradient_mask_1": "크기를 조정 핀치. <br/> 드래그하여 이동 및 회전합니다.",
    "gradient_mask_2": "<b>반전</b> 그라디언트의 방향을 전환합니다.",
    "gradient_mask_3": "<b>반영은</b> 두 방향에서 실행되는 그라데이션한다.",
    "grain": "필름 그레인 효과 추가",
    "highlights": "사진을 세척하지 않고 하이라이트의 밝기를 변경",
    "hsl": "특정 색상의 색조, 채도 또는 명도를 변경합니다.",
    "hsl_header": "HSL",
    "hue_blue": "특정 색을 파랑으로 변경.",
    "hue_red": "특정 색을 빨강으로 변경.",
    "luminance_blue": "파랑의 밝기를 변경.",
    "luminance_red": "빨강의 밝기를 변경.",
    "mastering_adjustments": "조정 마스터",
    "mosaic": "사진을 모자이크 효과를 줘",
    "radial_mask_0": "레이디 얼 마스크는 원형 영역에 영향을 미칩니다.",
    "radial_mask_1": "크기를 조정 핀치. <br/> 드래그하여 이동 및 회전합니다.",
    "radial_mask_2": "<b>깃털</b> 마스크의 흐릿한를 변경합니다.",
    "radial_mask_3": "<b>반전은</b> 영향을받는 영역을 전환합니다.",
    "saturation": "색의 강도를 증가 또는 감소.",
    "saturation_blue": "파랑 색조의 \"푸르름\"을 변경.",
    "saturation_red": "빨강 색조의 \"붉음\"을 변경.",
    "shadows": "사진을 세척하지 않고 그림자의 밝기를 변경",
    "sharpen": "사진을 좀 더 날카롭게 보이도록 가장자리 대조를 증가시킵니다.",
    "temperature": "사진이 높거나 낮은 확인",
    "tint": "자홍색-녹색 스펙트럼을 따라 색을 조정.",
    "toning_highlights": "하이라이트의 색상을 변경",
    "toning_shadows": "그림자의 색상을 변경",
    "vibrance": "피부 톤을 세척하지 않고 채도 변경",
    "vignette_amount": "페이드 나 사진 가장자리 강조",
    "vignette_feather": "네트의 흐릿한 변경",
    "vignette_highlights": "하이라이트는 네트를 통해 보여 드리죠",
    "vignette_roundness": "네트 더 원형 또는 사각형을",
    "vignette_size": "네트의 크기를 변경",
    "white_balance": "화이트 밸런스",
    "white_balance_1": "<b>정확한 화이트 밸런스</b> <br/> 대부분의 광원 (전구, 손전등 등) 순전히 흰 색을 방출하지 않습니다. 이 사진에서 백인 노란색 또는 파란색 캐스트가 발생할 수 있습니다. 이 문제를 해결하려면, -30 <b>온도를</b> 감소",
    "white_balance_2": "<b>그림자에서 빛을 복구</b> <br/> 때로는 사진의 부분은 우리의 눈은 카메라보다 더 많은 빛을 감지 할 수 있기 때문에 의도 한 것보다 더 어둡다. 우리는 그림자 도구를 사용하여 그림자에서 빛을 복구 할 수 있습니다. <br/> (100)에 <b>그림자를</b> 증가",
    "white_balance_3": "<b>자세한 강화</b> <br/> 마지막으로, 사진의 세부 사항을 향상시키기 위해, 우리는 명확하게 도구를 사용할 수 있습니다. <br/> 100 <b>선명도를</b> 증가",
    "white_balance_4": "잘 했어! 당신은 노란색 캐스트를 제거하고 간단한 3 단계의 그림자에서 빛을 복구하는 방법을 배웠다.",
    "white_balance_description": "이 자습서에서는 화이트 밸런스 어떻게 문 인공 조명에 해결하는 방법의 개념을 배울 것입니다.",
    "whites": "사진의 흰색 부분의 밝기를 변경합니다"
  }
};
},{}],330:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "Toevoegen",
    "add_image": "Kies afbeelding",
    "add_to_favorites": "Aan Favorieten toevoegen",
    "aspect": "Aspect",
    "back": "Terug",
    "buy": "Kopen",
    "buy_full_version": "Koop de volledige versie",
    "cancel": "Annuleren",
    "check_again": "Controleer nog eens",
    "choose_folder": "Kies map",
    "clear": "Wissen",
    "clear_history": "Historie wissen",
    "close": "Sluiten",
    "cloud": "Cloud",
    "continue_anyway": "Toch doorgaan",
    "current_image": "Huidige",
    "delete": "Verwijderen",
    "device": "Apparaat",
    "dismiss": "ontslaan",
    "done": "Klaar",
    "dont_ask": "Niet opnieuw vragen",
    "duplicate": "Duplicaat",
    "enter_license": "Voer de licentiecode in",
    "enter_license_key": "Voer de licentiecode in",
    "exit": "Uitgang",
    "export": "Exporteren",
    "export_filter": "Opslaan als QR code",
    "forgot_password": "Wachtwoord vergeten?",
    "free_membership": "gratis lidmaatschap",
    "get": "Krijgen",
    "get_license": "Krijg licentiesleutel",
    "get_started": "Begin",
    "got_it": "Begrepen",
    "have_account": "Heeft u al een account?",
    "import": "Importeren",
    "import_filter": "import filter",
    "import_filter_photos": "Importeren van foto's",
    "import_photos": "Open foto's",
    "info": "info",
    "installed": "Geïnstalleerd",
    "invite": "Link verzenden",
    "learn_more": "Kom meer te weten",
    "load_last_photo": "Laad laatste foto op de lanceerdag",
    "new": "Nieuw",
    "new_filter": "Filter aanmaken",
    "next": "Volgende",
    "no": "Nee",
    "no_thanks": "Nee, bedankt",
    "not_really": "Niet echt",
    "not_this_time": "Nu niet",
    "open_sample": "Voorbeeld foto openen",
    "paste_adjustments": "paste aanpassingen",
    "pay_wechat": "Open Wechat",
    "prev": "Vorige",
    "preview": "Voorbeschouwing",
    "pro": "Pro",
    "purchased": "Gekocht",
    "remove": "Verwijderen",
    "remove_all_photos": "Verwijder alle foto&#39;s",
    "remove_photo": "foto verwijderen",
    "remove_selected_photo": "Verwijder geselecteerde foto",
    "reset": "Resetten",
    "reset_all": "Alles resetten",
    "reset_password": "Reset Password",
    "reset_photos": "Reset opgeslagen en bewerkt labels",
    "reset_tips": "Tips resetten",
    "restore": "Herstellen",
    "revert": "Terug naar origineel ",
    "sample": "Voorbeeld",
    "save": "Bewaren",
    "save_qr": "Save QR code",
    "save_share": "Save + Share",
    "scan_filter": "scan filter",
    "select_all": "Alles selecteren",
    "select_changed": "Aanpassingen selecteren",
    "send": "Zenden",
    "send_verification": "Opnieuw verzenden verificatie e-mail",
    "show_in_folder": "Weergeven in map",
    "show_me": "Laat het me zien",
    "signin": "Log in",
    "signout": "Uitloggen",
    "signup": "Maak Polarr rekening",
    "skip": "afrit",
    "submit_license_key": "Verzend licentiesleutel",
    "subscribe": "abonneren",
    "subscribe_monthly": "Abonneren Maandelijkse",
    "subscribe_yearly": "Abonneren Jaarlijkse",
    "sure": "Okay, prima",
    "try": "Proberen",
    "try_again": "Opnieuw proberen",
    "unlock": "Open maken",
    "unsubscribe": "Afmelden",
    "update": "Updaten",
    "whats_new": "Wat is er nieuw",
    "yes": "Ja"
  },
  "dialogs": {
    "about": "Over",
    "about_text": "Wij vinden het belangrijk dat makers hun perspectieven en houding tegenover het leven moeiteloos kunnen uiten, zonder verlies van de trouw aan hun creatieve verlangens en inspiraties.",
    "account": "Account",
    "all_filters": "alle filters",
    "all_filters_text": "Unlock gehele collectie van premium filters Polarr's",
    "backup_filters": "Synchroniseren en back-up van al uw aangepaste filters, zodat u ze kunt gebruiken op verschillende apparaten en platforms. Deze functie vereist dat u in te loggen met een Polarr account.",
    "backup_filters_title": "Backup Custom Filters",
    "brush_limit": "De huidige Brush Mask limiet </limit>",
    "denoise": "Onze real-time denoise proces maakt het gemakkelijk om ruis en korrel te verwijderen in foto's. Afzonderlijk controle kleur- en luminantieruis componenten.",
    "featured": "Uitgelicht",
    "filter_collections": "filter collecties",
    "filter_name": "Noem dit filter",
    "get_picky": "Moe van het bewerken van foto's? Probeer nieuwe app Polarr van die foto's met Artificial Intelligence edits!",
    "get_updates": "Abonneren op updates",
    "get_updates_about": "Wees de eerste om uit te proberen nieuwe features! <br/> Voeg je e-mailadres en wij zullen u op de hoogte over nieuwe functies en producten van Polarr.",
    "get_updates_thankyou": "Je krijgt nu updates over nieuwe functies en producten van Polarr! <br/> Indien u wenst te stoppen met het ontvangen van updates, kunt u \"unsubscribe\" van uw e-mail op elk gewenst moment.",
    "help": "Help",
    "invite": "Installatie link delen. Pro features worden ontgrendeld zodra drie personen via uw link hebben geïnstalleerd.",
    "local_adjustments": "Lokale aanpassingen kunt u Polarr's krachtige set van aanpassingen op gerichte gebieden te gebruiken. Inclusief Radial, Gradient, Kleur en Brush maskeren gereedschap.",
    "lock_rotation": "Lock rotatie",
    "max_export_size": "Max export formaat",
    "new_filter": "Save edits als een filter",
    "no_purchases": "Er zijn geen aankopen te herstellen.",
    "offline": "Uw apparaat lijkt offline te zijn. Probeert u het later opnieuw.",
    "photos": "foto's",
    "polarr_premium": "Polarr Premium",
    "pro_all_filters": "Pro-functies <br/> + <br/> alle filters",
    "pro_all_filters_text": "Unlock alle functies, waaronder Pro Tools en alle Polarr premium filters. Gebruik je Polarr account voor toegang tot alle functies op andere platformen. Back-up en synchroniseren van uw aangepaste filters.",
    "pro_features": "Pro tools",
    "pro_features_all": "Alle Pro Tools",
    "purchase": "Unlock Radial, Gradient, kleur, borstel, Denoise en Tekst gereedschap. Back-up en synchroniseren van uw aangepaste filters.",
    "purchase_failed": "Aankoop geannuleerd",
    "purchase_header": "Alle pro functies ontgrendelen",
    "purchase_successful": "U heeft nu toegang tot de pro versie.",
    "purchase_unavailable": "Aankopen zijn nu helaas niet beschikbaar. Sorry.",
    "purchases": "Aankopen",
    "rate_review": "Beoordeel en recenseer",
    "rename_filter": "hernoemen Filter",
    "restore_failed": "Uw account heeft geen volledige toegang tot functies.",
    "restore_successful": "Al uw eerdere aankopen worden nu hersteld.",
    "review": "Geniet u van Polarr Photo Editor tot nu toe?",
    "review_no": "Zou u ons wat feedback willen geven?",
    "review_yes": "Zou u een rating in de App Store willen geven?",
    "save_adjustments": "Maak een aangepast filter u kunt delen.",
    "send_feedback": "Feedback zenden",
    "signin_polarr_account": "Log in op uw rekening Polarr",
    "store": "Winkel",
    "subscribe": "Abonneer je op <strong>Polarr Premium</strong>",
    "subscribe_access": "voor volledige toegang tot:",
    "subscribe_cancel": "U kunt \"Uitschrijven\" op elk gewenst moment Settings",
    "subscribe_caveat": "* Pro-functies zijn onder helling aanpassing, radiale aanpassing, kleur maskeren, en de kleur borstelen. Uw lidmaatschap wordt dit jaar van toepassing op alle apparaten en platforms.",
    "subscribe_china": "Abonneer je op WeChat rekening polaxiongkeji en een betaling in WeChat, dan inloggen met uw account Polarr professionele functies te activeren.",
    "subscribe_error": "Oeps! Er was fout abonneren op Polarr. Probeer het nog eens?",
    "subscribe_features": "<li> Pro-functies (4 lokale bewerkingstools) </li><li> Toegang tot alle 12 premium filter packs </li><li> Blijf op de hoogte voor RAW-formaten </li><li> Nieuwe content toegevoegd per maand! </li><li> Toegang tot het lidmaatschap op alle platforms * </li>",
    "subscribe_notify": "Laat me weten wanneer mijn Polarr Premium lidmaatschap kan worden gebruikt op meer platforms en apparaten",
    "subscribe_price": "</price>",
    "subscribe_refund": "Je eerder gekochte iets met Polarr en u in aanmerking komt voor",
    "subscribe_success": "Hartelijk dank voor het abonneren op Polarr Premium u!",
    "subscribe_unlock_success": "Je bent nu een Polarr Premium Lid!",
    "subscription_expired": "Uw Polarr Premium lidmaatschap is verlopen",
    "text_tool": "Text tool kunt u tekst en vormen toe te voegen aan foto's. Met functies als letter en regelafstand, uitlijning en het verpakken, heb je de volledige controle van uw tekst. We hebben zelfs geavanceerde functies, zoals dynamische vector scaling en een unieke mix aanpassing.",
    "text_tool_description": "Toevoegen en bewerken van tekst op het scherm. Roteren, veranderen van kleur, zet mengen, en zelfs brief spatiëring aan te passen.",
    "thankyou": "Dank je!",
    "unlock_all": "unlock alles",
    "unlock_all_note": "Inloggen Kies uw Polarr pro-account of voer licentiesleutel.",
    "unlock_alternate": "U kunt ook upgraden door in te loggen op een bestaand account of door het invoeren van een licentiesleutel in",
    "unlock_failed": "Pro functies zijn niet beschikbaar voor dit account.",
    "unlock_success": "Je hebt nu toegang tot alle functies.",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> en <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> , de beste bron voor gratis en in hoge resolutie stock photography, hebben de handen ineengeslagen om u een verzameling van 10 premium filters gemaakt door leden van de Unsplash fotograaf gemeenschap. </p>",
    "upgrade": "Upgrade naar de volledige versie aan alle volgende functies te openen.",
    "upgrade_caveat": "* 12 extra filter packs bevatten simulaties van klassieke film, vintage film, Infrared Film, huidtinten en vele andere aanpassingen geschikt voor diverse onderwerpen en scènes.",
    "upgrade_features": "<li> gradient aanpassingen </li><li> Circular aanpassingen </li><li> Kleur maskeren </li><li> borstelen aanpassing </li><li> 12 aanvullende premie filter packs </li><li> Batch foto export </li>",
    "use_polarrfilter": "Gebruik <strong>#polarrfilter</strong> om op te vallen!",
    "user_in_paid": "U kunt in Polarr inloggen op andere platforms voor toegang tot alle functies.",
    "user_in_unpaid": "Als je alles te ontgrendelen, kun je inloggen op Polarr op andere platforms en toegang tot alle functies.",
    "verification_sent": "Een verificatie e-mail is verzonden. <br/> Controleer uw e-mail voor verificatie instructies.",
    "verify_account": "Om in te loggen, controleer uw account. <br/> Controleer uw e-mail voor verificatie instructies.",
    "webgl_not_supported": "Het spijt ons zeer maar uw apparaat komt de grafische eisen van Polarr niet tegemoet, alstublieft eerst updaten tot het meest recente besturingssysteem en opnieuw proberen."
  },
  "labels": {
    "adjustments": "Aanpassingen",
    "after": "Na",
    "alignment": "opstelling",
    "all": "Alle",
    "amount": "Hoeveelheid",
    "angle": "Hoek",
    "aqua": "Aqua",
    "artwork": "artwork",
    "auto": "Auto",
    "balance": "Balans",
    "basic": "Basaal",
    "batch_rename": "batch rename",
    "batch_resize": "batch resize",
    "before": "Voor",
    "best": "Beste",
    "blacks": "Zwart",
    "blend": "Mengsel",
    "blend_mode": "Blend Mode",
    "blue": "Blauw",
    "blur": "vervagen",
    "brightness": "Helderheid",
    "brush_flow": "borstel Flow",
    "brush_opacity": "borstel Dekking",
    "brush_size": "Penseelgrootte",
    "clarity": "Helderheid",
    "collection": "Collectie",
    "color": "Kleur",
    "contrast": "Contrast",
    "crop": "Knippen",
    "crop_size": "Crop afmetingen",
    "curves": "Curven",
    "custom": "Aangepast",
    "dehaze": "Waas verwijderen",
    "denoise": "Ruis verwijderen",
    "detail": "Detail",
    "diffuse": "Diffuus",
    "distortion": "Bocht",
    "distortion_amount": "Bocht Bedrag.",
    "distortion_horizontal": "Horizontale Bocht",
    "distortion_vertical": "Verticale Bocht",
    "dividers": "verdelers",
    "edit": "Bewerk",
    "effects": "Effecten",
    "email": "E-mail",
    "enable": "Inschakelen",
    "estimated_size": "geschatte Maat",
    "exposure": "Belichting",
    "eyes": "Ogen",
    "face": "Gezicht",
    "favorites": "Favorieten",
    "feather": "Veer",
    "fit": "Fit",
    "font": "doopvont",
    "format": "Formaat",
    "free": "Gratis",
    "frequent": "Meest gebruikt",
    "fringing": "Kleurranden aanpassen",
    "grain": "Graan",
    "green": "Groen",
    "guides": "Gidsen",
    "high": "Hoog",
    "highlights": "Highlights",
    "histogram": "Histogram",
    "history": "Historie",
    "hsl": "TVH",
    "hue": "Tint",
    "hue_blue": "TVH - blauwe tint",
    "hue_red": "TVH - rode tint",
    "invert": "Omkeren",
    "lens": "Lens",
    "lens_distortion": "lens Distortion",
    "letter_spacing": "Letterafstand",
    "license_key": "Licentiesleutel",
    "light": "Licht",
    "line_spacing": "Regelafstand",
    "lips": "Lips",
    "look": "Filter",
    "looks": "Filters",
    "low": "Laag",
    "luminance": "Luminantie",
    "luminance_blue": "TVH - blauwe luminantie",
    "luminance_red": "TVH - rode luminantie",
    "magenta": "Magenta",
    "master": "Master",
    "max_size": "Max afmetingen",
    "med": "Med",
    "mix": "Alles aanpassen",
    "mosaic": "Pixelate",
    "name": "Naam",
    "none": "Geen",
    "nose": "Neus",
    "opacity": "ondoorzichtigheid",
    "optics": "Optiek",
    "options": "opties",
    "orange": "Oranje",
    "original": "Origineel",
    "outline": "schets",
    "password": "Wachtwoord",
    "position": "Positie",
    "prefix": "voorvoegsel segment",
    "presets": "presets",
    "preview": "Voorbeschouwing",
    "purple": "Paars",
    "quality": "Kwaliteit",
    "ratio": "Ratio",
    "red": "Rood",
    "reflect": "reflecteren",
    "resolution": "Resolutie",
    "rgb": "RGB",
    "roundness": "Rondheid",
    "saturation": "Verzadiging",
    "saturation_blue": "TVH - blauwe verzadiging",
    "saturation_red": "TVH - red verzadiging",
    "sayings": "Zeggen",
    "shadow": "Schaduw",
    "shadows": "Schaduwen",
    "shape": "Vorm",
    "shapes": "vormen",
    "shapes_presets": "Vormen & Presets",
    "sharpen": "Aanscherpen",
    "size": "Grootte",
    "skin": "Huid",
    "smoothness": "smoothing",
    "split_tone": "Tonen",
    "teeth": "Tanden",
    "temperature": "Temp",
    "text": "Tekst",
    "texture": "structuur",
    "threshold": "Drempel",
    "tint": "Tint",
    "tone": "Toon",
    "toning": "Tonen",
    "use_prefix": "gebruik voorvoegsel",
    "use_watermark": "Watermerk gebruiken",
    "user_name": "Gebruikersnaam",
    "vibrance": "Levendigheid",
    "vignette": "Vignet",
    "vignette_amount": "Vignet",
    "watermark_image": "watermerk Afbeelding",
    "wb": "WB",
    "whitening": "whitening",
    "whites": "Wit",
    "width": "Breedte",
    "yellow": "Geel"
  },
  "menus": {
    "batch_export": "batch export",
    "change_language": "Taal veranderen",
    "copy_edit": "copy Bewerkingen",
    "edit_watermark": "Watermerk bewerken",
    "export": "Format, Resize, Watermark",
    "filters_users": "Filters volgens Polarrists",
    "guide": "Handleiding",
    "image_quality": "Beeldkwalitiet",
    "image_settings": "Instellingen exporteren",
    "language": "Taal",
    "more_filters": "Meer filters",
    "open": "Open",
    "open_in": "Open in",
    "paste_edit": "paste Bewerkingen",
    "remove": "Verwijderen",
    "rename": "Hernoemen",
    "restore_purchases": "Aankopen herstellen",
    "save_cameraroll": "Opslaan op filmrol",
    "save_copy": "Een kopie bewaren",
    "settings": "Instellingen",
    "share": "Delen",
    "share_edits": "Share bewerkingen",
    "share_filter": "Share filter",
    "show_instructions": "Aanpassingen handleiding",
    "show_intro": "Show intro",
    "show_video_tutorial": "Video handleiding",
    "single_export": "Exporteren"
  },
  "onboarding": {
    "become_master": "Word fotobewerking meester.",
    "step_0": "Welkom bij Polarr! <br/> Laten we aan de slag door het bekijken van een aantal filters.",
    "step_1": "Tik op om een ​​filter te selecteren. <br/> Swipe panel om meer filters zien.",
    "step_10": "U kunt slepen om te herschikken alle gereedschap iconen.",
    "step_2": "Kraan <i class='icon-check'></i> het filter toe te passen. <br/> Gebruik de schuifregelaar om de intensiteit van het filter aan te passen.",
    "step_3": "Elk filter is volledig bewerkbaar. <br/> Ga naar Aanpassingen aan uw bewerkingen te zien.",
    "step_4": "U kunt een aanpassing wijzigen door de schuifregelaar te slepen.",
    "step_4a": "Lang indrukken sleept om de schuifknop te passen.",
    "step_5": "Zie alle bewerkingen die u heeft gemaakt in de geschiedenis paneel.",
    "step_6": "U kunt een bewerking ongedaan te maken en terug te keren naar elk punt in de geschiedenis.",
    "step_7": "Bewaar hier uw afbeelding. <br/> U kunt de grootte, voeg watermerk of op te slaan meerdere afbeeldingen.",
    "step_7a": "Bewaar hier uw afbeelding. <br/> U kunt ook delen en op te slaan meerdere afbeeldingen.",
    "step_8": "Open je eigen foto's hier.",
    "step_9": "Bekijk je foto's geopend in het paneel foto's."
  },
  "status": {
    "account_suspended": "Je account is in de wacht gezet als gevolg van verdachte activiteiten. Voor meer info, controleer dan uw e-mail of neem contact op <a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "Kraan <i class='icon-add-face'></i> om een ​​gezicht toe te voegen",
    "adjustment_copied": "aanpassingen Gekopieerde",
    "adjustment_pasted": "aanpassingen Geplakt",
    "demo_mode": "Deze versie van Polarr Photo Editor is speciaal gemaakt voor demonstratiedoeleinden",
    "downloading_font": "Het downloaden van lettertype",
    "drag_icons": "Sleep pictogrammen om gereedschappen herschikken",
    "drop_files": "Drop bestanden te openen",
    "email_invalid": "Sorry, het lijkt erop dat e-mailadres is niet geldig.",
    "email_reset": "Een reset koppeling is verzonden. <br/> Controleer uw e-mail.",
    "exporting_filter": "filter te besparen",
    "field_invalid": "is niet geldig.",
    "file_exists": "Die foto is al open",
    "file_not_supported": "Sorry, dat bestand wordt niet ondersteund",
    "filter_exported": "Uw filter is opgeslagen in filmrol.",
    "filter_saved": "filter gered",
    "filters_sync_error": "Fout bij synchroniseren filters",
    "filters_synced": "filters gesynchroniseerd",
    "filters_syncing": "Synchroniseren filters ...",
    "import_filter_error": "Sorry, dat filter kan niet worden geïmporteerd",
    "import_limit": "Polarr kan alleen open te stellen voor 50 foto's, verwijder een aantal foto's voor het openen van meer.",
    "importing_filter": "Importeren filter",
    "importing_photo": "Opening foto",
    "importing_photos": "Opening foto's",
    "invalid_credentials": "verkeerde email of wachtwoord",
    "loading_photo": "Foto wordt geladen",
    "login_success": "U bent succesvol ingelogd bent!",
    "no_faces": "Geen gezichten gedetecteerd.",
    "no_user": "Die gebruiker bestaat niet.",
    "offline": "Geen connectie met server.",
    "photo_load_error": "Kan foto niet laden",
    "photo_saved": "Foto is bewaard",
    "photo_shared": "Foto gedeeld",
    "photos_saved": "foto's opgeslagen",
    "purchase_required": "aankoop vereist",
    "raw_failed": "Niet in staat om RAW te lezen. RAW-ondersteuning is nog experimenteel.",
    "registration_success": "U hebt gemaakt van een nieuwe account! <br/> Om in te loggen op andere apparaten, moet u eerst uw account te verifiëren. Wij hebben u een e-mail gestuurd met de verificatie-instructies.",
    "saving_photo": "foto saving",
    "saving_photo_error": "Fout opgetreden bij opslaan van foto :(",
    "saving_photo_error_sd": "Polarr heeft geen toegang tot uw SD kaart of interne opslag. Controleer alstublieft de laatste Android system update om het probleem op te lossen.",
    "saving_photos": "foto's opslaan",
    "server_offline": "Sorry, de server reageert niet. <br/> Bent u aangesloten op het internet?",
    "sharing_photo": "Het delen van foto",
    "sharing_photo_error": "Fout opgetreden bij het delen van foto :(",
    "show_me_how": "Toon me hoe te gebruiken </thing>",
    "supported_files": "Polarr ondersteunt JPG, PNG, BMP, en bepaalde RAW-bestanden",
    "unlocks_remaining": "Jij hebt </unlocks> ontsluit resterende",
    "user_exists": "Er is al een account met dat e-mail of gebruikersnaam.",
    "wrong_password": "Verkeerd wachtwoord"
  },
  "tooltips": {
    "add_face": "Face",
    "add_text": "Voeg tekst toe",
    "adjustments": "aanpassingen",
    "apply_adjustments": "Solliciteer Aanpassingen",
    "auto_adjust": "Auto Aanpassingen",
    "brush_filter": "Brush Masker",
    "color_filter": "color Mask",
    "crop": "Knippen",
    "crop_straighten": "Crop & Hoek",
    "current_adjustments": "huidige Aanpassingen",
    "double_tap_edit": "Double tap te bewerken",
    "drag_watermark": "Sleep om watermerk te verplaatsen",
    "eraser": "Gom",
    "face": "gezicht Hulpmiddelen",
    "filmstrip": "geopend Foto's",
    "flip": "Omdraaien",
    "flip_h": "Horizontaal omdraaien",
    "flip_v": "Verticaal omdraaien",
    "global_adjustments": "Globale Aanpassingen",
    "gradient_filter": "Verloop Masker",
    "guides": "Gidsen",
    "history": "editing Geschiedenis",
    "info": "Info",
    "local_adjustments": "Lokale Aanpassingen",
    "looks": "filters",
    "masks": "maskers",
    "move_face": "Move Gezicht",
    "open": "Open Foto's",
    "pan": "Pan",
    "radial_filter": "Radiaal Masker",
    "redo": "Opnieuw doen",
    "remove_face": "Verwijder Gezicht",
    "remove_text": "Verwijder tekst",
    "rotate": "Draaien",
    "rotate_ccw": "CCW Roteren",
    "rotate_cw": "CW Roteren",
    "save": "Foto opslaan",
    "settings": "Instellingen",
    "show_original": "Toon origineel",
    "text": "tekst Tool",
    "undo": "Ongedaan maken"
  },
  "tutorial": {
    "blacks": "Wijzig de helderheid van de zwarte delen van de foto",
    "brightness": "Wijzig de helderheid of \"gamma\" van de foto",
    "brush_mask_0": "Gebruik uw vinger om te schilderen met de borstel.",
    "brush_mask_1": "Borstel masker alleen invloed op de geborstelde omgeving.",
    "clarify": "Verduidelijken",
    "clarify_1": "<b>Verminder mist</b> <br/> Mist is een veel voorkomend probleem in foto's en het is gemakkelijk te verwijderen in Polarr. <br/> Mist verwijderen, te verhogen <b>Dehaze</b> tot 85",
    "clarify_2": "<b>Recover exposure</b> <br/> Dehaze verduistert soms de foto te veel. <br/> Om wat licht te herstellen, te verhogen <b>Blootstelling</b> aan 30",
    "clarify_3": "<b>Verhoog Contrast</b> <br/> De nevel is grotendeels nu verdwenen, maar we kunnen het licht dynamische een beetje meer te stimuleren. Verhoog <b>Contrast</b> tot 60",
    "clarify_4": "<b>Verbeter de details</b> <br/> Om de details in de foto te verbeteren, kunnen we de Clarity tool te gebruiken. Meer <b>duidelijkheid</b> te verschaffen 40",
    "clarify_5": "Goed werk! Je hebt geleerd hoe je een wazige foto te maken een stuk beter in 4 eenvoudige stappen.",
    "clarify_description": "In deze tutorial leer je hoe je mist te verminderen, contouren definiëren en toe te voegen levendigheid aan uw onderwerp.",
    "clarity": "Verhoog of glad uit gegevens in de foto",
    "color_mask_0": "Kleur masker heeft alleen invloed op de geselecteerde kleur.",
    "color_mask_1": "Sleep om de geselecteerde kleur te veranderen.",
    "color_mask_2": "<b>Drempel</b> wordt de gevoeligheid van de selectie.",
    "contrast": "Maak uw onderwerp meer onderscheidbaar.",
    "curves": "Verander de toon curve van de foto",
    "deep_space": "Deep Space",
    "deep_space_1": "<b>herstellen licht</b> <br/> belichtingafstelling Polarr simuleert de belichting van een camera. <br/> Om licht te herstellen, stelt <b>Blootstelling</b> aan 100",
    "deep_space_2": "<b>Verbeter de details</b> <br/> Laten we de sterren kijken meer indrukwekkend. <br/> Het toevoegen van duidelijkheid zal helpen door de onthulling van meer details. <br/> Om gegevens te verbeteren, stelt <b>Clarity</b> 100",
    "deep_space_3": "<b>diepte toevoegen</b> <br/> Meer richten naar het midden van het beeld brengen, kunnen we vignet aan de randen van de foto donkerder. <br/> Om diepte toe te voegen, te verlagen <b>Vignette</b> tot -100",
    "deep_space_4": "<b>Smooth Vignette</b> <br/> Op dit moment is het vignet ziet er te scherp. <br/> Om het vignet kijken glad en subtiel, <br/> verhogen <b>Feather</b> tot 100",
    "deep_space_5": "<b>Recover hoogtepunten</b> <br/> Een deel van de sterren in de rand van de foto kijken naar donker. We kunnen ze te herstellen door het verhogen van <b>Highlights</b> 100",
    "deep_space_6": "<b>Verander kleur</b> <br/> HSL kunt u de kenmerken van een bepaalde kleur te veranderen. Om de kleur van de lucht te veranderen, te verhogen <b>Aqua Hue</b> tot 100",
    "deep_space_7": "Goed werk! Je hebt geleerd hoe je een sterrenhemel kijken echt indrukwekkend.",
    "deep_space_description": "In deze tutorial leer je manieren om diepte toe te voegen, kleuren aanpassen en de verandering van de blootstelling van een nachtelijke hemel.",
    "dehaze": "Toevoegen of verwijderen waas van de foto",
    "denoise": "Verwijder ruis uit de foto",
    "diffuse": "Geef de foto een \"gloed\" effect",
    "distortion": "Vervorming",
    "distortion_1": "<b>correcte vervorming</b> <br/> We zien vaak gebouwen en muren kijken scheve in foto's. Dit wordt meestal veroorzaakt door het ontwerp van groothoek camera lenzen. We kunnen dit eenvoudig vast te stellen in Polarr met vervorming gereedschap. <br/> Om vervorming te repareren, verhogen <b>Verticale vervorming</b> tot 70",
    "distortion_2": "<b>correct vignet</b> <br/> In aanvulling op de optische vervormingen, de meeste camera lenzen hebben lichte neerslag in de buurt van de randen. We kunnen deze donkere randen met behulp van het vignet gereedschap zal verminderen. <br/> Om vignet te corrigeren, te verhogen tot 20 <b>Vignette</b>",
    "distortion_3": "<b>Verbeter de details</b> <br/> Ten slotte, om de details in de foto te verbeteren, kunnen we de helderheid tool te gebruiken. <br/> Verhoog <b>Clarity</b> 100",
    "distortion_4": "Goed werk! Je hebt geleerd hoe u veelvoorkomende lens fouten te herstellen in 3 eenvoudige stappen.",
    "distortion_amount": "Voeg een breed of fish-eye effect aan foto's toe.",
    "distortion_description": "In deze tutorial leert u hoe u lensvervormingen en vignet corrigeren architectonische shots.",
    "distortion_horizontal": "Wijzig de vervorming langs de horizontale as",
    "distortion_vertical": "Wijzig de vervorming langs de verticale as",
    "exposure": "Wijzig de helderheid zonder uitwassen van de foto",
    "face_0": "Polarr detecteert automatisch gezichten en scheidt de individuele functies.",
    "face_1": "Gebruik de Distort tool om de grootte en positie van gelaatstrekken aan te passen.",
    "fringing": "Voeg \"duizeligheid\" aan uw foto toe.",
    "gradient_mask_0": "Gradient masker van invloed op gebieden in een rechte lijn.",
    "gradient_mask_1": "Knijpen om het formaat. <br/> Sleep om te bewegen en draaien.",
    "gradient_mask_2": "<b>Omkeren</b> schakelt de helling richting.",
    "gradient_mask_3": "<b>Reflect</b> maakt het verloop draaien in beide richtingen.",
    "grain": "Voeg een film grain effect",
    "highlights": "Wijzig de helderheid van de hoogtepunten zonder uitwassen van de foto",
    "hsl": "Wijzig de tint, verzadiging of lichtheid van een specifieke kleur.",
    "hsl_header": "HSL",
    "hue_blue": "Verander specifieke kleuren naar blauw.",
    "hue_red": "Verander specifieke kleuren naar rood.",
    "luminance_blue": "Verander de helderheid van blauw.",
    "luminance_red": "Verander de helderheid van rood.",
    "mastering_adjustments": "mastering Aanpassingen",
    "mosaic": "Geef de foto een mozaïek effect",
    "radial_mask_0": "Radial masker alleen van invloed op een cirkelvormig gebied.",
    "radial_mask_1": "Knijpen om het formaat. <br/> Sleep om te bewegen en draaien.",
    "radial_mask_2": "<b>Veer</b> verandert de vaagheid van het masker.",
    "radial_mask_3": "<b>Omkeren</b> schakelt het getroffen gebied.",
    "saturation": "Intensiteit van kleur verhogen of verlagen.",
    "saturation_blue": "Verander de \"blauwheid\" van blauwe kleuren.",
    "saturation_red": "Verander de \"roodheid\" van rode kleuren.",
    "shadows": "Wijzig de helderheid van de schaduwen zonder uitwassen van de foto",
    "sharpen": "Verhoog rand contrast om foto’s scherper te maken.",
    "temperature": "Maak de foto warmer of kouder",
    "tint": "Kleur aanpassen langs het magenta-groene spectrum.",
    "toning_highlights": "Verander de kleur van de hoogtepunten",
    "toning_shadows": "Verander de kleur van de schaduw",
    "vibrance": "Wijzig de kleurverzadiging zonder uitwassen huidtinten",
    "vignette_amount": "Vervagen of wijst op de randen van de foto",
    "vignette_feather": "Verander de vaagheid van het vignet",
    "vignette_highlights": "Laat de hoogtepunten laten zien door het vignet",
    "vignette_roundness": "Maak de vignet meer ronde of vierkante",
    "vignette_size": "Verander de grootte van het vignet",
    "white_balance": "Witbalans",
    "white_balance_1": "<b>Juiste witbalans</b> <br/> De meeste lichtbronnen (lampen, zaklampen, etc) stoten geen zuiver witte kleur. Dit kan ertoe leiden dat de blanken op de foto's om een ​​gele of blauwe cast hebben. Om dit te corrigeren, verminderen <b>temperatuur</b> tot -30",
    "white_balance_2": "<b>Recover licht van schaduwen</b> <br/> Soms delen van een foto donkerder dan de bedoeling was, omdat onze ogen meer licht kan waarnemen dan de camera. We kunnen het licht van het gebruik van schaduwen schaduwen tool terug. <br/> Verhoog <b>Shadows</b> tot 100",
    "white_balance_3": "<b>Verbeter de details</b> <br/> Ten slotte, om de details in de foto te verbeteren, kunnen we de helderheid tool te gebruiken. <br/> Verhoog <b>Clarity</b> 100",
    "white_balance_4": "Goed werk! Je hebt geleerd hoe je gele cast te verwijderen en te herstellen licht van schaduwen in 3 eenvoudige stappen.",
    "white_balance_description": "In deze tutorial, zal je het concept van de witbalans en hoe te corrigeren in de deur kunstlicht te leren.",
    "whites": "Verandert de helderheid van de witte delen van de foto"
  }
};
},{}],331:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "Adicionar",
    "add_image": "Escolha imagem",
    "add_to_favorites": "Adicionar aos Favoritos",
    "aspect": "Aspecto",
    "back": "Voltar",
    "buy": "Comprar",
    "buy_full_version": "Comprar versão completa",
    "cancel": "Cancelar",
    "check_again": "Verifique novamente",
    "choose_folder": "escolher pasta",
    "clear": "Limpar",
    "clear_history": "Limpar histórico",
    "close": "Fechar",
    "cloud": "Nuvem",
    "continue_anyway": "Continuar assim mesmo",
    "current_image": "Atual",
    "delete": "Deletar",
    "device": "Dispositivo",
    "dismiss": "demitir",
    "done": "Pronto",
    "dont_ask": "Não perguntar novamente",
    "duplicate": "Duplicado",
    "enter_license": "Inserir chave de licença",
    "enter_license_key": "Inserir chave de licença",
    "exit": "Saída",
    "export": "Exportar",
    "export_filter": "Salvar como código QR",
    "forgot_password": "Esqueceu a senha?",
    "free_membership": "livre adesão",
    "get": "Obter",
    "get_license": "Obter chave de licença",
    "get_started": "Começar",
    "got_it": "Entendi",
    "have_account": "já tem uma conta?",
    "import": "Importar",
    "import_filter": "Filtro de importação",
    "import_filter_photos": "Importar de Fotos",
    "import_photos": "Abrir fotos",
    "info": "informações",
    "installed": "Instalado",
    "invite": "Enviar link",
    "learn_more": "Aprender mais",
    "load_last_photo": "Coloque a última foto no lançamento",
    "new": "Novo",
    "new_filter": "Criar filtro",
    "next": "Próximo",
    "no": "Não",
    "no_thanks": "Não, obrigado",
    "not_really": "Não mesmo",
    "not_this_time": "Não desta vez",
    "open_sample": "Abrir a foto de amostra",
    "paste_adjustments": "ajustes colar",
    "pay_wechat": "Abrir WeChat",
    "prev": "Ant",
    "preview": "visualização",
    "pro": "Pro",
    "purchased": "Comprado",
    "remove": "Remover",
    "remove_all_photos": "Remover todas as fotos",
    "remove_photo": "Remover foto",
    "remove_selected_photo": "Retirar foto selecionada",
    "reset": "Recompor",
    "reset_all": "Recompor Todos",
    "reset_password": "Trocar a senha",
    "reset_photos": "Repor etiquetas guardadas e editados",
    "reset_tips": "Dicas para recompor",
    "restore": "Restaurar",
    "revert": "Retomar original",
    "sample": "Amostra",
    "save": "Salvar",
    "save_qr": "Salvar o código QR",
    "save_share": "Salvar + Compartilhar",
    "scan_filter": "filtro de varredura",
    "select_all": "Selecionar tudo",
    "select_changed": "Selecionar editado",
    "send": "Enviar",
    "send_verification": "Reenviar email de verificação",
    "show_in_folder": "Mostrar na pasta",
    "show_me": "Mostre-me",
    "signin": "Entrar",
    "signout": "Sair",
    "signup": "Criar conta Polarr",
    "skip": "Pular",
    "submit_license_key": "Enviar chave de licença",
    "subscribe": "Se inscrever",
    "subscribe_monthly": "Assinar Mensal",
    "subscribe_yearly": "Assinar anual",
    "sure": "Ok, claro",
    "try": "Experimentar",
    "try_again": "Tente novamente",
    "unlock": "Desbloquear",
    "unsubscribe": "Cancelar subscrição",
    "update": "Atualização",
    "whats_new": "O que há de novo",
    "yes": "Sim"
  },
  "dialogs": {
    "about": "Sobre",
    "about_text": "Nós acreditamos que é importante permitir que criadores de expressar facilmente suas perspectivas e atitudes em relação à vida, sem perder a fidelidade de seu desejo criativo e inspirações",
    "account": "Conta",
    "all_filters": "Todos os filtros",
    "all_filters_text": "Desbloquear coleção inteira de filtros premium da Polarr",
    "backup_filters": "Sincronizar e fazer backup de todos os seus filtros personalizados para que você possa usá-los em todos os dispositivos e plataformas. Esta funcionalidade exige que faça o login com uma conta de Polarr.",
    "backup_filters_title": "Filtros de backup personalizadas",
    "brush_limit": "O limite de corrente Máscara Brush é </limit>",
    "denoise": "Nosso processo denoise em tempo real torna mais fácil para remover o ruído e grãos em fotos. Separadamente controlar componentes de cor e ruído de luminância.",
    "featured": "Destaque",
    "filter_collections": "filtro de Colecções",
    "filter_name": "Nome este filtro",
    "get_picky": "Cansado de edição de fotos? Experimente novo aplicativo de Polarr que edita fotos com Inteligência Artificial!",
    "get_updates": "Inscrever-se para atualizações",
    "get_updates_about": "Seja o primeiro a experimentar novas funcionalidades! <br/> Adicione-o endereço de email e iremos notificá-lo sobre novas funcionalidades e produtos de Polarr.",
    "get_updates_thankyou": "Agora você irá obter atualizações sobre novos recursos e produtos a partir de Polarr! <br/> Se você desejar parar de receber atualizações, você pode \"anular\" a partir do seu e-mail a qualquer momento.",
    "help": "Ajuda",
    "invite": "Compartilhe o link de instalação. Os recursos pro serão desbloqueados depois que três pessoas instalarem através de seu link.",
    "local_adjustments": "ajustes locais permite que você use poderoso conjunto de ajustes em áreas específicas do Polarr. Inclui ferramentas de máscara radial, inclinação, cor, e escova.",
    "lock_rotation": "rotação de bloqueio",
    "max_export_size": "tamanho máximo de exportação",
    "new_filter": "Salvar edições como um filtro",
    "no_purchases": "Não há compras para restaurar.",
    "offline": "O dispositivo parece estar offline. Por favor, tente mais tarde",
    "photos": "Fotos",
    "polarr_premium": "Polarr premium",
    "pro_all_filters": "Pro apresenta <br/> + <br/> Todos os filtros",
    "pro_all_filters_text": "Desbloquear todos os recursos, incluindo ferramentas Pro e todos os filtros premium da Polarr. Use sua conta Polarr para acessar todos os recursos em outras plataformas. Backup e sincronizar seus filtros personalizados.",
    "pro_features": "Pro Tools",
    "pro_features_all": "Todos os Pro Tools",
    "purchase": "Desbloquear Radial, Gradiente, cores, escova, Denoise e ferramentas de texto. Backup e sincronizar seus filtros personalizados.",
    "purchase_failed": "Compra cancelada",
    "purchase_header": "Desbloquear todos os recursos pro",
    "purchase_successful": "Você agora têm acesso à versão pro.",
    "purchase_unavailable": "As compras não estão disponíveis no momento. Desculpe.",
    "purchases": "Compras",
    "rate_review": "Taxa e avaliação",
    "rename_filter": "renomeie Filtro",
    "restore_failed": "A sua conta não tem acesso à funcionalidade completa.",
    "restore_successful": "Todas as suas compras anteriores estão agora restaurado.",
    "review": "Gostando do Polarr Photo Editor até agora?",
    "review_no": "Você se importaria de nos dar algum feedback?",
    "review_yes": "Que tal uma classificação na App Store, então?",
    "save_adjustments": "Faça um filtro personalizado que você pode compartilhar.",
    "send_feedback": "Enviar comentários",
    "signin_polarr_account": "Entre na sua conta Polarr",
    "store": "Loja",
    "subscribe": "Subscreve a <strong>Polarr premium</strong>",
    "subscribe_access": "Para ter acesso completo a:",
    "subscribe_cancel": "Você pode \"cancelar\" em Configurações a qualquer momento",
    "subscribe_caveat": "* Pro características incluem ajuste de inclinação, ajuste radial, mascaramento cor e escova de cor. Sua filiação será aplicada em todos os dispositivos e plataformas este ano.",
    "subscribe_china": "Inscrever-se para polaxiongkeji conta WeChat e fazer um pagamento em WeChat, em seguida, faça o login com sua conta Polarr para ativar pro características.",
    "subscribe_error": "Ops! Houve erro ao subscrever Polarr. Tente novamente?",
    "subscribe_features": "<li> Recursos Pro (4 ferramentas de ajuste locais) </li><li> Acesse todos os pacotes de filtro 12 de prémio </li><li> Mantenha-se atualizado para formatos RAW </li><li> Novos conteúdos adicionados mensal! </li><li> a adesão de acesso em todas as plataformas * </li>",
    "subscribe_notify": "Notifique-me quando o meu Polarr Premium pode ser utilizado em mais plataformas e dispositivos",
    "subscribe_price": "</price>",
    "subscribe_refund": "Você anteriormente adquirido algo com Polarr e você é elegível para",
    "subscribe_success": "Obrigado por subscrever a Polarr premium!",
    "subscribe_unlock_success": "Você agora é um Polarr Membro Premium!",
    "subscription_expired": "Sua filiação Polarr premium expirou",
    "text_tool": "ferramenta de texto permite adicionar texto e formas para fotos. Com recursos como letra e espaçamento entre linhas, alinhamento e envolvimento, você tem total controle seu texto. Foram incluídos ainda recursos avançados, como o dimensionamento do vetor dinâmico e um ajuste mistura única.",
    "text_tool_description": "Adicionar e editar texto na tela. Girar, mudar de cor, ajuste de mistura, e até mesmo ajustar espaçamento de letras.",
    "thankyou": "Obrigado!",
    "unlock_all": "destravar tudo",
    "unlock_all_note": "Entre a sua conta pro Polarr ou inserir chave de licença.",
    "unlock_alternate": "Você também pode atualizar fazendo login em uma conta existente ou inserindo uma chave de licença em",
    "unlock_failed": "Pro recursos não estão disponíveis para esta conta.",
    "unlock_success": "Agora você tem acesso a todos os recursos.",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> e <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> , a melhor fonte para a fotografia livres e de alta resolução, se uniram para trazer-lhe uma coleção de 10 filtros de prémios criados por membros do fotógrafo comunidade Unsplash. </p>",
    "upgrade": "Upgrade para a versão completa para destravar todas as seguintes características.",
    "upgrade_caveat": "* 12 pacotes de filtro adicionais incluem simulações de Classic Film, Film Vintage, filme infravermelho, tons de pele e muitos outros ajustes adequados para vários assuntos e cenas.",
    "upgrade_features": "<li> ajustes de gradiente </li><li> ajustes circulares </li><li> mascaramento de cor </li><li> escovar ajuste </li><li> 12 pacotes de filtro prémio adicional </li><li> lote exportação foto </li>",
    "use_polarrfilter": "Use <strong>#polarrfilter</strong> para serem notados!",
    "user_in_paid": "Você pode entrar em Polarr em outras plataformas para acessar todos os recursos.",
    "user_in_unpaid": "Se você desbloquear tudo, você vai ser capaz de entrar no Polarr em outras plataformas e acessar todos os recursos.",
    "verification_sent": "Um e-mail de confirmação foi enviado. <br/> Por favor, verifique seu e-mail para obter instruções de verificação.",
    "verify_account": "Para iniciar sessão, por favor, verifique sua conta. <br/> Verifique se o seu e-mail para obter instruções de verificação.",
    "webgl_not_supported": "Nós lamentamos muito, mas o dispositivo não satisfazer a exigência gráfica do Polarr, atualize para o mais recente sistema operacional e tente novamente."
  },
  "labels": {
    "adjustments": "Ajustes",
    "after": "Depois",
    "alignment": "Alinhamento",
    "all": "Todos",
    "amount": "Quantidade",
    "angle": "Ângulo",
    "aqua": "Água",
    "artwork": "Obra",
    "auto": "Auto",
    "balance": "Equilíbrio",
    "basic": "Básico",
    "batch_rename": "lote renomear",
    "batch_resize": "redimensionamento lote",
    "before": "Antes",
    "best": "Melhor",
    "blacks": "Pretos",
    "blend": "Mistura",
    "blend_mode": "Modo de mistura",
    "blue": "Azul",
    "blur": "Borrão",
    "brightness": "Brilho",
    "brush_flow": "Fluxo de escova",
    "brush_opacity": "escova de opacidade",
    "brush_size": "Tamanho de escova",
    "clarity": "Claridade",
    "collection": "Coleção",
    "color": "Cor",
    "contrast": "Contraste",
    "crop": "Cortar",
    "crop_size": "dimensões de corte",
    "curves": "Curvas",
    "custom": "Customizar",
    "dehaze": "Dehaze",
    "denoise": "Denoise",
    "detail": "Detalhe",
    "diffuse": "Difuso",
    "distortion": "Distorção",
    "distortion_amount": "Distorção",
    "distortion_horizontal": "Distorção Horizontal",
    "distortion_vertical": "Distorção Vertical",
    "dividers": "divisores",
    "edit": "Editar",
    "effects": "Efeito",
    "email": "O email",
    "enable": "Habilitar",
    "estimated_size": "Estima Tamanho",
    "exposure": "Exposição",
    "eyes": "olhos",
    "face": "Cara",
    "favorites": "Favoritos",
    "feather": "Pena",
    "fit": "Caber",
    "font": "Fonte",
    "format": "Formato",
    "free": "Livre",
    "frequent": "Mais usados",
    "fringing": "Franjas",
    "grain": "Granulação",
    "green": "Verde",
    "guides": "Guides",
    "high": "Alto",
    "highlights": "Destaques",
    "histogram": "Histograma",
    "history": "História",
    "hsl": "HSL",
    "hue": "Tonalidade",
    "hue_blue": "HSL - tom azul",
    "hue_red": "HSL - tom vermelho",
    "invert": "Inverter",
    "lens": "Lentes",
    "lens_distortion": "distorção da lente",
    "letter_spacing": "Espaçamento carta",
    "license_key": "chave de licença",
    "light": "Luz",
    "line_spacing": "Espaçamento entre linhas",
    "lips": "Lábios",
    "look": "Filtro",
    "looks": "Filtros",
    "low": "Baixo",
    "luminance": "Luminância",
    "luminance_blue": "HSL - luminância azul",
    "luminance_red": "HSL - luminância vermelha",
    "magenta": "Magenta",
    "master": "Mestre",
    "max_size": "dimensões máximas",
    "med": "Médio",
    "mix": "Ajustar Todos",
    "mosaic": "Pixelate",
    "name": "Nome",
    "none": "Nenhum",
    "nose": "Nariz",
    "opacity": "Opacidade",
    "optics": "ótica",
    "options": "opções",
    "orange": "Laranja",
    "original": "Original",
    "outline": "Esboço",
    "password": "Senha",
    "position": "Posição",
    "prefix": "segmento prefixo",
    "presets": "presets",
    "preview": "visualização",
    "purple": "Roxo",
    "quality": "Qualidade",
    "ratio": "Taxa",
    "red": "Vermelho",
    "reflect": "refletir",
    "resolution": "Resolução",
    "rgb": "RGB",
    "roundness": "Redondeza",
    "saturation": "Saturação",
    "saturation_blue": "HSL - saturação azul",
    "saturation_red": "HSL - saturação vermelha",
    "sayings": "provérbios",
    "shadow": "Sombra",
    "shadows": "Sombras",
    "shape": "Forma",
    "shapes": "Shapes",
    "shapes_presets": "Shapes & Presets",
    "sharpen": "Afiar",
    "size": "Tamanho",
    "skin": "Pele",
    "smoothness": "suavização",
    "split_tone": "Tonalidade",
    "teeth": "dentes",
    "temperature": "Temperatura",
    "text": "Texto",
    "texture": "Textura",
    "threshold": "Limite",
    "tint": "Cor",
    "tone": "Tom",
    "toning": "Tom",
    "use_prefix": "Use prefixo",
    "use_watermark": "Use Watermark",
    "user_name": "nome de usuário",
    "vibrance": "Vibração",
    "vignette": "Vinheta",
    "vignette_amount": "Vinheta",
    "watermark_image": "Marca de água imagem",
    "wb": "WB",
    "whitening": "Branqueamento",
    "whites": "Brancos",
    "width": "Largura",
    "yellow": "Amarelo"
  },
  "menus": {
    "batch_export": "lote exportação",
    "change_language": "Alterar o idioma",
    "copy_edit": "cópia Edita",
    "edit_watermark": "Editar marca d'água",
    "export": "Formato, Resize, Watermark",
    "filters_users": "Filtros por Polarrists",
    "guide": "Guia",
    "image_quality": "Qualidade da imagem",
    "image_settings": "Configurações para exportar",
    "language": "Idiomas",
    "more_filters": "Mais filtros",
    "open": "Abrir",
    "open_in": "Abrir em",
    "paste_edit": "colar edições",
    "remove": "Remover",
    "rename": "Renomear",
    "restore_purchases": "Restaurar compras",
    "save_cameraroll": "Salve a Camera Roll",
    "save_copy": "Salvar uma cópia",
    "settings": "Configurações",
    "share": "Compartilhar",
    "share_edits": "edições Partilhar",
    "share_filter": "Filtro Partilhar",
    "show_instructions": "Guia de Ajustes",
    "show_intro": "Mostrar introdução,",
    "show_video_tutorial": "Tutorial em vídeo",
    "single_export": "Exportar"
  },
  "onboarding": {
    "become_master": "Torne-se um mestre de edição de fotos.",
    "step_0": "Bem-vindo ao Polarr! <br/> Vamos começar vendo alguns filtros.",
    "step_1": "Toque para selecionar um filtro. <br/> painel de furto para ver mais filtros.",
    "step_10": "Você pode arrastar para reorganizar todos os ícones de ferramentas.",
    "step_2": "Toque <i class='icon-check'></i> para aplicar o filtro. <br/> Use o controle deslizante para ajustar a intensidade do filtro.",
    "step_3": "Cada filtro é completamente editáveis. <br/> Ir para Ajustes para ver as suas edições.",
    "step_4": "Você pode mudar um ajuste arrastando o controle deslizante.",
    "step_4a": "Pressão longa em seguida, arraste o controle deslizante para ajustar.",
    "step_5": "Veja todas as edições que você fez no painel história.",
    "step_6": "Você pode desfazer uma edição e voltar a qualquer momento da história.",
    "step_7": "Salve sua imagem aqui. <br/> Você pode redimensionar, adicionar marca d'água ou salvar várias imagens.",
    "step_7a": "Salve sua imagem aqui. <br/> Você também pode compartilhar e salvar várias imagens.",
    "step_8": "Abra suas próprias fotos aqui.",
    "step_9": "Veja suas fotos abertas no painel de fotos."
  },
  "status": {
    "account_suspended": "A sua conta é colocado em espera devido à atividade suspeita. Para mais informações, por favor, verifique seu e-mail ou contato <a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "Toque <i class='icon-add-face'></i> adicionar uma cara",
    "adjustment_copied": "ajustes copiado",
    "adjustment_pasted": "ajustes Colado",
    "demo_mode": "Esta versão do Polarr Photo Editor foi criado especificamente para fins de demonstração",
    "downloading_font": "font Baixando",
    "drag_icons": "Arrastar ícones para reorganizar ferramentas",
    "drop_files": "Soltar arquivos para abrir",
    "email_invalid": "Desculpe, parece que esse endereço de e-mail não é válido.",
    "email_reset": "Um link de redefinição foi enviada. <br/> Por favor verifique seu email.",
    "exporting_filter": "filtro de poupança",
    "field_invalid": "não é válido.",
    "file_exists": "Essa foto já está aberto",
    "file_not_supported": "Desculpe, esse tipo de ficheiro não é suportado",
    "filter_exported": "O filtro foi salvo no rolo da câmera.",
    "filter_saved": "filtro salvo",
    "filters_sync_error": "filtros de sincronização erro",
    "filters_synced": "filtros sincronizado",
    "filters_syncing": "Sincronizando filtros ...",
    "import_filter_error": "Desculpe, esse filtro não pôde ser importado",
    "import_limit": "Polarr só pode abrir até 50 fotos, remova algumas fotos antes de abrir mais.",
    "importing_filter": "filtro de importação de",
    "importing_photo": "foto de abertura",
    "importing_photos": "abrindo fotos",
    "invalid_credentials": "e-mail ou senha incorretos",
    "loading_photo": "Carregando foto",
    "login_success": "Você foi desconectado com sucesso em!",
    "no_faces": "Nenhum detectado caras.",
    "no_user": "Esse utilizador não existe.",
    "offline": "Não foi possível conectar ao servidor.",
    "photo_load_error": "Não é possível carregar a foto",
    "photo_saved": "Foto salva",
    "photo_shared": "Foto compartilhada",
    "photos_saved": "Fotos salvo",
    "purchase_required": "compra necessária",
    "raw_failed": "Não é possível ler imagem RAW. suporte RAW ainda é experimental.",
    "registration_success": "Você fez com sucesso uma nova conta! <br/> Para efetuar login em outros dispositivos, você deve primeiro verificar a sua conta. Enviamos um e-mail com as instruções de verificação.",
    "saving_photo": "foto poupança",
    "saving_photo_error": "Erro ao salvar foto :(",
    "saving_photo_error_sd": "Pollar não tem permissão para acessar o seu cartão SD ou armazenamento interno. Por favor, verifique a mais recente atualização do sistema Android para corrigir o problema.",
    "saving_photos": "Como salvar fotos",
    "server_offline": "Lamentamos, mas o servidor não está respondendo. <br/> Você está conectado à internet?",
    "sharing_photo": "compartilhamento de fotos",
    "sharing_photo_error": "Erro ao compartilhar foto :(",
    "show_me_how": "Mostre-me como usar </thing>",
    "supported_files": "Polarr suporta JPG, PNG, BMP, e certos arquivos RAW",
    "unlocks_remaining": "Você tem </unlocks> desbloqueia restantes",
    "user_exists": "Já existe uma conta com esse e-mail ou nome de usuário.",
    "wrong_password": "Senha incorreta"
  },
  "tooltips": {
    "add_face": "Adicionar Rosto",
    "add_text": "Adicione texto",
    "adjustments": "ajustes",
    "apply_adjustments": "aplicar Ajustes",
    "auto_adjust": "Ajustes auto",
    "brush_filter": "Máscara escova",
    "color_filter": "Máscara cor",
    "crop": "Cortar",
    "crop_straighten": "Cortar e Angle",
    "current_adjustments": "Ajustes atuais",
    "double_tap_edit": "Toque duas vezes para editar",
    "drag_watermark": "Arraste para mover marca d'água",
    "eraser": "Apagador",
    "face": "Ferramentas de face",
    "filmstrip": "Fotos abertas",
    "flip": "Giro",
    "flip_h": "Virar para horizontal",
    "flip_v": "Virar para vertical",
    "global_adjustments": "Ajustes Globais",
    "gradient_filter": "Máscara Gradiente",
    "guides": "Guides",
    "history": "edição Histórico",
    "info": "Info",
    "local_adjustments": "Ajustes Locais",
    "looks": "filtros",
    "masks": "máscaras",
    "move_face": "mover face",
    "open": "Open Fotos",
    "pan": "panela",
    "radial_filter": "Máscara Radial",
    "redo": "Refazer",
    "remove_face": "Remover Rosto",
    "remove_text": "Remover texto",
    "rotate": "Rodar",
    "rotate_ccw": "Rodar CCW",
    "rotate_cw": "Rodar CW",
    "save": "Salvar foto",
    "settings": "Configurações",
    "show_original": "Mostrar original",
    "text": "Ferramenta de texto",
    "undo": "Desfazer"
  },
  "tutorial": {
    "blacks": "Alterar o brilho das partes escuras da foto",
    "brightness": "Alterar o brilho ou \"gamma\" da fotografia",
    "brush_mask_0": "Use o dedo para pintar com o pincel.",
    "brush_mask_1": "máscara escova afeta apenas a área escovado.",
    "clarify": "Esclarecer",
    "clarify_1": "<b>reduzir nevoeiro</b> <br/> Nevoeiro é um problema comum em fotografias e é fácil de remover, em Polarr. <br/> Para remover nevoeiro, aumentar <b>Dehaze</b> a 85",
    "clarify_2": "<b>recuperar a exposição</b> <br/> Dehaze vezes escurece demais a foto. <br/> Para recuperar um pouco de luz, aumentar a <b>exposição</b> a 30",
    "clarify_3": "<b>Aumentar contraste</b> <br/> A neblina é principalmente ido agora, mas que pode impulsionar a dinâmica de luz um pouco mais. Aumentar o <b>contraste</b> para 60",
    "clarify_4": "<b>realçar detalhes</b> <br/> Para melhorar os detalhes na foto, podemos usar a ferramenta Clarity. Aumentar a <b>clareza</b> para 40",
    "clarify_5": "Bom trabalho! Você aprendeu como fazer uma foto nebulosa muito melhor em 4 passos simples.",
    "clarify_description": "Neste tutorial, você vai aprender a reduzir nevoeiro, definem contornos e adicionar vivacidade ao seu assunto.",
    "clarity": "Aumentar ou suavizar detalhes na foto",
    "color_mask_0": "máscara de cor só afecta a cor selecionada.",
    "color_mask_1": "Arraste para alterar a cor selecionada.",
    "color_mask_2": "<b>Limiar</b> define a sensibilidade da selecção.",
    "contrast": "Faça o seu assunto mais distinguíveis.",
    "curves": "Alterar a curva de tons da foto",
    "deep_space": "Espaço profundo",
    "deep_space_1": "<b>recuperar luz</b> <br/> ajuste de exposição de Polarr simula a exposição de uma câmera. <br/> Para recuperar luz, definir a <b>exposição</b> a 100",
    "deep_space_2": "<b>realçar detalhes</b> <br/> Vamos fazer as estrelas parecem mais impressionante. <br/> Adicionando clareza vai ajudar, revelando mais detalhes. <br/> Para realçar detalhes, definir <b>Clarity</b> a 100",
    "deep_space_3": "<b>adicionar profundidade</b> <br/> Para trazer mais foco para o centro da imagem, podemos adicionar vinheta para escurecer as bordas da foto. <br/> Para adicionar profundidade, diminua <b>Vignette</b> para -100",
    "deep_space_4": "<b>Vignette suave</b> <br/> Agora a vinheta parece muito afiada. <br/> Para fazer a vinheta olhar suave e sutil, <br/> aumentar a <b>pena</b> para 100",
    "deep_space_5": "<b>recuperar destaques</b> <br/> Algumas das estrelas na borda da foto olhar para o escuro. Nós podemos recuperá-los, aumentando <b>Destaques</b> 100",
    "deep_space_6": "<b>Mudar cor</b> <br/> HSL permite alterar os atributos de uma determinada cor. Para alterar a cor do céu, aumente <b>do Aqua Hue</b> para 100",
    "deep_space_7": "Bom trabalho! Você aprendeu como fazer um céu estrelado olhar realmente impressionante.",
    "deep_space_description": "Neste tutorial, você vai aprender maneiras de adicionar profundidade, ajustar a cor ea mudança a exposição de um céu nocturno.",
    "dehaze": "Adicionar ou remover embaçamento da foto",
    "denoise": "Remover ruído da foto",
    "diffuse": "Dar à foto um efeito \"brilho\"",
    "distortion": "Distorção",
    "distortion_1": "<b>corrigir a distorção</b> <br/> Muitas vezes vemos prédios e paredes olhando torto nas fotos. Isso geralmente é causado pelo design do grande-angular lentes de câmera. Nós podemos facilmente corrigir isso em Polarr com ferramentas de distorção. <br/> Para corrigir a distorção, aumentar a <b>Vertical Distortion</b> a 70",
    "distortion_2": "<b>vinheta correta</b> <br/> Em adicional a distorções ópticas, a maioria das lentes de câmera têm luz precipitação perto de suas bordas. Podemos reduzir essas bordas mais escuras usando a ferramenta vinheta. <br/> Para corrigir vinheta, aumentar a <b>Vignette</b> a 20",
    "distortion_3": "<b>realçar detalhes</b> <br/> Por último, para melhorar os detalhes na foto, podemos usar a ferramenta clareza. <br/> Aumentar a <b>clareza</b> para 100",
    "distortion_4": "Bom trabalho! Você aprendeu como corrigir defeitos comuns de lente em 3 passos simples.",
    "distortion_amount": "Faça uma foto com aparência grande angular ou olho de peixe",
    "distortion_description": "Neste tutorial, você vai aprender a corrigir as distorções da lente e vinheta em fotos de arquitetura.",
    "distortion_horizontal": "Alterar a distorção ao longo do eixo horizontal",
    "distortion_vertical": "Alterar a distorção ao longo do eixo vertical",
    "exposure": "Alterar o brilho sem lavar para fora a foto",
    "face_0": "Polarr detecta automaticamente os rostos e separa características individuais.",
    "face_1": "Use a ferramenta Distort para ajustar o tamanho ea posição das características faciais.",
    "fringing": "Adicionar vertigem à sua foto.",
    "gradient_mask_0": "máscara gradiente afeta áreas em uma linha reta.",
    "gradient_mask_1": "Beliscar para redimensionar. <br/> Arraste para mover e girar.",
    "gradient_mask_2": "<b>Inverter</b> muda a direção do gradiente.",
    "gradient_mask_3": "<b>Reflita</b> faz com que o gradiente de correr em ambos os sentidos.",
    "grain": "Adicionar um efeito de grão de filme",
    "highlights": "Alterar o brilho dos destaques sem lavar para fora a foto",
    "hsl": "Alterar o matiz, saturação ou luminosidade de uma cor específica.",
    "hsl_header": "HSL",
    "hue_blue": "Substituir a cor azul para aparecer como outras cores.",
    "hue_red": "Substituir a cor vermelha para parecer como outras cores.",
    "luminance_blue": "Mudar o brilho do azul.",
    "luminance_red": "Mudar o brilho do vermelho.",
    "mastering_adjustments": "dominando Ajustes",
    "mosaic": "Dar à foto um efeito de mosaico",
    "radial_mask_0": "máscara Radial afeta apenas uma área circular.",
    "radial_mask_1": "Beliscar para redimensionar. <br/> Arraste para mover e girar.",
    "radial_mask_2": "<b>Pena</b> muda a imprecisão da máscara.",
    "radial_mask_3": "<b>Inverter</b> muda a área afectada.",
    "saturation": "Deixe sua foto mais colorida.",
    "saturation_blue": "Mudar o colorido do azul.",
    "saturation_red": "Mudar o colorido do vermelho.",
    "shadows": "Alterar o brilho das sombras sem lavar para fora a foto",
    "sharpen": "Faça sua foto parecer mais nítida.",
    "temperature": "Fazer a foto mais quente ou fria",
    "tint": "Colora sua foto pela hora do dia.",
    "toning_highlights": "Alterar a cor dos destaques",
    "toning_shadows": "Mudar a cor das sombras",
    "vibrance": "Alterar a saturação sem lavar os tons de pele",
    "vignette_amount": "Desaparecer ou destaca as bordas da foto",
    "vignette_feather": "Alterar a imprecisão da vinheta",
    "vignette_highlights": "Deixe os destaques mostrar através da vinheta",
    "vignette_roundness": "Faça a vinheta mais redondo ou quadrado",
    "vignette_size": "Alterar o tamanho da vinheta",
    "white_balance": "Balanço de branco",
    "white_balance_1": "<b>Balanço de branco correto</b> <br/> A maioria das fontes de luz (lâmpadas, lanternas, etc.) não emitem cor puramente branco. Isso pode causar brancos em fotos para ter um elenco amarelo ou azul. Para corrigir isso, diminuir a <b>temperatura</b> até -30",
    "white_balance_2": "<b>Recuperar a luz de sombras</b> <br/> Às vezes, partes de uma foto são mais escuros do que o pretendido, porque os nossos olhos podem perceber mais luz do que a câmera. Podemos recuperar a luz das sombras utilizando a ferramenta de sombras. <br/> Aumentar <b>sombras</b> para 100",
    "white_balance_3": "<b>realçar detalhes</b> <br/> Por último, para melhorar os detalhes na foto, podemos usar a ferramenta clareza. <br/> Aumentar a <b>clareza</b> para 100",
    "white_balance_4": "Bom trabalho! Você aprendeu como remover elenco amarelo e recuperar a luz de sombras em 3 simples passos.",
    "white_balance_description": "Neste tutorial, você vai aprender o conceito de balanço de branco e como corrigir na luz artificial porta.",
    "whites": "Altera o brilho das partes brancas da foto"
  }
};
},{}],332:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "Добавить",
    "add_image": "Выберите изображение",
    "add_to_favorites": "Добавить в избранное",
    "aspect": "Аспект",
    "back": "Назад",
    "buy": "Купить",
    "buy_full_version": "Купить полную версию",
    "cancel": "Отменить",
    "check_again": "Проверить снова",
    "choose_folder": "Выберите папку",
    "clear": "Очистить",
    "clear_history": "Очистить историю",
    "close": "Закрыть",
    "cloud": "Облако",
    "continue_anyway": "Всё равно продолжить",
    "current_image": "Текущий",
    "delete": "Удалить",
    "device": "Устройство",
    "dismiss": "отклонять",
    "done": "Готово",
    "dont_ask": "Больше не спрашивать",
    "duplicate": "дублировать",
    "enter_license": "Введите лицензионный ключ",
    "enter_license_key": "Введите лицензионный ключ",
    "exit": "Выход",
    "export": "Экспорт",
    "export_filter": "Сохранить как QR-кода",
    "forgot_password": "Забыли пароль?",
    "free_membership": "бесплатное членство",
    "get": "Получить",
    "get_license": "Получить лицензионный ключ",
    "get_started": "Начать",
    "got_it": "Понял",
    "have_account": "У Вас уже есть аккаунт?",
    "import": "Импорт",
    "import_filter": "Импорт фильтра",
    "import_filter_photos": "Импорт из фотографий",
    "import_photos": "Открыть фото",
    "info": "Информация",
    "installed": "Установлено",
    "invite": "Отправить ссылку",
    "learn_more": "Выучить больше",
    "load_last_photo": "Загрузите последнюю фотографию на старте",
    "new": "Новый",
    "new_filter": "Создать фильтр",
    "next": "След.",
    "no": "Нет",
    "no_thanks": "Нет, спасибо",
    "not_really": "Не совсем",
    "not_this_time": "Не сейчас",
    "open_sample": "Открыть образец фото",
    "paste_adjustments": "корректировки пасты",
    "pay_wechat": "Открыть WeChat",
    "prev": "Пред.",
    "preview": "предварительный просмотр",
    "pro": "Pro",
    "purchased": "Куплено",
    "remove": "Удалить",
    "remove_all_photos": "Удалить все фотографии",
    "remove_photo": "Удалить фото",
    "remove_selected_photo": "Удалить выбранную фотографию",
    "reset": "Сбросить",
    "reset_all": "Сбросить всё",
    "reset_password": "Сброс пароля",
    "reset_photos": "Сброс сохраненных и отредактированные этикетки",
    "reset_tips": "Сбросить подсказки",
    "restore": "Восстановить",
    "revert": "Вернуться к оригиналу",
    "sample": "Образец",
    "save": "Сохранить",
    "save_qr": "Сохранить QR-код",
    "save_share": "Сохранить + Поделиться",
    "scan_filter": "фильтр сканирования",
    "select_all": "Выбрать всё",
    "select_changed": "Выбрать отредактированные",
    "send": "Отправить",
    "send_verification": "Отправить письмо с подтверждением",
    "show_in_folder": "Показать в папке",
    "show_me": "Покажи мне",
    "signin": "Авторизоваться",
    "signout": "Выйти",
    "signup": "Создать учетную запись Polarr",
    "skip": "Выход",
    "submit_license_key": "Отправить лицензионный ключ",
    "subscribe": "Подписывайся",
    "subscribe_monthly": "Подписаться Ежемесячно",
    "subscribe_yearly": "Подписка Ежегодно",
    "sure": "Ok, конечно",
    "try": "Пытаться",
    "try_again": "Попробовать еще раз",
    "unlock": "Разблокировать",
    "unsubscribe": "Отказаться от подписки",
    "update": "Обновить",
    "whats_new": "Что нового",
    "yes": "Да"
  },
  "dialogs": {
    "about": "О нас",
    "about_text": "Мы убеждены, что очень важно позволять творцам легко выражать свою точку зрения и отношение к жизни, не теряя точность творческого начала и вдохновения.",
    "account": "Счет",
    "all_filters": "Все фильтры",
    "all_filters_text": "Разблокировка всю коллекцию Polarr в премиум-фильтров",
    "backup_filters": "Синхронизация и резервное копирование всех пользовательских фильтров, так что вы можете использовать их для различных устройств и платформ. Эта функция требует, чтобы вы войти с учетной записью Polarr.",
    "backup_filters_title": "Резервное копирование пользовательских фильтров",
    "brush_limit": "Ограничение тока Кисть Маска </limit>",
    "denoise": "Наш процесс DeNoise в режиме реального времени позволяет легко удалить шум и зерно в фотографиях. Отдельно контролировать цвет и яркость шума компонентов.",
    "featured": "Рекомендуемые",
    "filter_collections": "Фильтр Коллекции",
    "filter_name": "Назовите этот фильтр",
    "get_picky": "Устали от редактирования фотографий? Попробуйте новое приложение Polarr, что редактирует фотографии с искусственным интеллектом!",
    "get_updates": "Подписаться на обновления",
    "get_updates_about": "Будьте первым, чтобы опробовать новые возможности! <br/> Добавьте вы адрес электронной почты, и мы сообщим вам о новых возможностях и продуктах из Polarr.",
    "get_updates_thankyou": "Теперь вы будете получать информацию о новых возможностях и продуктах из Polarr! <br/> Если вы хотите получать обновления, вы можете \"отписаться\" от вашей электронной почте в любое время.",
    "help": "Помощь",
    "invite": "Поделиться ссылкой установки. Опции Pro разблокируются, когда три человека установят приложение, пройдя по Вашей ссылке.",
    "local_adjustments": "Локальные настройки позволяет использовать Polarr мощный набор корректировок на целевых областях. Включает в себя Радиальная, градиент, цвет, и кисть маскирующие средства.",
    "lock_rotation": "Блокировка вращения",
    "max_export_size": "Максимальный размер экспорта",
    "new_filter": "Сохранить изменения в качестве фильтра",
    "no_purchases": "Там нет никаких предложений по восстановлению.",
    "offline": "Ваше устройство не подключено к сети. Попробуйте еще раз позже.",
    "photos": "Фото",
    "polarr_premium": "Polarr Премиум",
    "pro_all_filters": "Про особенности <br/> + <br/> Все фильтры",
    "pro_all_filters_text": "Разблокировать все функции, включая Pro Tools и все премиум-класса фильтров Polarr в. Используйте свою учетную запись Polarr, чтобы получить доступ ко всем функциям и на других платформах. Резервное копирование и синхронизация пользовательских фильтров.",
    "pro_features": "Pro Tools",
    "pro_features_all": "Все Pro Tools",
    "purchase": "Разблокировка Radial, градиент, цвет, кисть, Denoise и текстовые инструменты. Резервное копирование и синхронизация пользовательских фильтров.",
    "purchase_failed": "Покупки отменены",
    "purchase_header": "Открыть все опции pro",
    "purchase_successful": "Теперь у вас есть доступ к версии pro.",
    "purchase_unavailable": "Сейчас покупки недоступны. Очень жаль.",
    "purchases": "Покупки",
    "rate_review": "Оценка и отзыв",
    "rename_filter": "Переименовать фильтр",
    "restore_failed": "Ваша учетная запись не имеет полный доступ функции.",
    "restore_successful": "Все ваши предыдущие покупки будут восстановлены.",
    "review": "Нравится Polarr Photo Editor?",
    "review_no": "Не  хотите оставить нам отзыв?",
    "review_yes": "Как насчет оценить нас на App Store?",
    "save_adjustments": "Сделайте пользовательский фильтр можно разделить.",
    "send_feedback": "Отправить отзыв",
    "signin_polarr_account": "Войдите в свой аккаунт Polarr",
    "store": "хранить",
    "subscribe": "Подпишитесь на <strong>Polarr Премиум</strong>",
    "subscribe_access": "для полного доступа к:",
    "subscribe_cancel": "Вы можете \"Отменить подписку\" в настройках в любое время",
    "subscribe_caveat": "* Pro функции включают в себя регулируемый градиент, радиальная регулировка, цвет маскирование и цвет чистки зубов. Ваше членство будет применяться на всех устройствах и платформах в этом году.",
    "subscribe_china": "Подписаться на polaxiongkeji WeChat счет и произвести оплату в WeChat, а затем войти в систему с учетной записью Polarr для активации про особенности.",
    "subscribe_error": "К сожалению! Был ошибка, подписавшись на Polarr. Попробуй еще раз?",
    "subscribe_features": "<li> Про особенности (4-х местные инструменты регулировки) </li><li> Доступ ко всем фильтров пакетов 12 премиум </li><li> Будьте в курсе для RAW форматов </li><li> Новое содержание добавляются ежемесячно! </li><li> членство доступа на всех платформах * </li>",
    "subscribe_notify": "Сообщите мне, когда мой Polarr Премиум членство может быть использован на большем количестве платформ и устройств",
    "subscribe_price": "</price>",
    "subscribe_refund": "Вы уже приобрели что-то с Polarr и вы имеете право на",
    "subscribe_success": "Благодарим Вас за подписку на Polarr премии!",
    "subscribe_unlock_success": "Вы теперь Polarr премиум Пользователь!",
    "subscription_expired": "Ваше членство Polarr Премиум истекло",
    "text_tool": "Текстовый инструмент позволяет добавлять текст и формы для фотографий. С такими функциями, как буквы и межстрочный интервал, выравнивание, и обертывания, вы имеете полный контроль вашего текста. Мы даже включили расширенные функции, такие как динамический вектора масштабирования и уникальной регулировки смешивания.",
    "text_tool_description": "Добавление и редактирование текста на экране. Поворот, изменение цвета, установите смешивание, и даже настроить расстояние между буквами.",
    "thankyou": "Спасибо!",
    "unlock_all": "Разблокировать все",
    "unlock_all_note": "Вход ваш счет PRO Polarr или ввести лицензионный ключ.",
    "unlock_alternate": "Вы также можете обновить, войдя в систему с существующей учетной записью или путем ввода лицензионного ключа в",
    "unlock_failed": "Pro Особенности недоступны для этой учетной записи.",
    "unlock_success": "Теперь у вас есть доступ ко всем функциям.",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> и <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> , лучший источник бесплатно и с высоким разрешением фотографии, объединили свои усилия, чтобы принести Вам коллекцию из 10 премиальных фильтров , созданных членами сообщества Unsplash фотографом. </p>",
    "upgrade": "Обновление до полной версии, чтобы разблокировать все следующие функции.",
    "upgrade_caveat": "* 12 дополнительных пакетов фильтрации включают в себя моделирование классический фильм, Vintage Film, инфракрасная пленка, тонизирует кожу и многих других корректировок, подходящих для различных объектов и сцен.",
    "upgrade_features": "<li> градиентные корректировки </li><li> Круговые корректировки </li><li> Цвет маскирующего </li><li> Чистить регулировка </li><li> 12 дополнительных пакетов премии фильтр </li><li> экспорт Пакетный фото </li>",
    "use_polarrfilter": "Используйте <strong>#polarrfilter</strong> , чтобы вас заметили!",
    "user_in_paid": "Вы можете войти в Polarr на других платформах, чтобы получить доступ ко всем функциям.",
    "user_in_unpaid": "Если вы разблокировать все, вы будете иметь возможность войти в Polarr на других платформах и доступ ко всем функциям.",
    "verification_sent": "Проверка на сообщение было отправлено. <br/> Пожалуйста, проверьте свою электронную почту для получения инструкций по проверке.",
    "verify_account": "Для того, чтобы войти, пожалуйста, подтвердите свой аккаунт. <br/> Проверьте свою электронную почту для получения инструкций по проверке.",
    "webgl_not_supported": "Нам очень жаль, но ваше устройство не соответствует графическим требованиям Polarr. Обновите операционную систему и попробуйте еще раз."
  },
  "labels": {
    "adjustments": "Настройки",
    "after": "После",
    "alignment": "центровка",
    "all": "Все",
    "amount": "Значение",
    "angle": "Угол",
    "aqua": "Морская волна",
    "artwork": "произведение искусства",
    "auto": "Авто",
    "balance": "Баланс",
    "basic": "Основной",
    "batch_rename": "Пакетное переименование",
    "batch_resize": "Групповое изменение размера",
    "before": "До",
    "best": "Лучший",
    "blacks": "Черный",
    "blend": "смесь",
    "blend_mode": "Режим смешивания",
    "blue": "Синий",
    "blur": "пятно",
    "brightness": "Яркость",
    "brush_flow": "Кисть Flow",
    "brush_opacity": "Кисть Непрозрачность",
    "brush_size": "Размер кисти",
    "clarity": "Четкость",
    "collection": "Коллекция",
    "color": "Цвет",
    "contrast": "Контраст",
    "crop": "Обрезка",
    "crop_size": "размеры Crop",
    "curves": "Кривые",
    "custom": "Установить",
    "dehaze": "Резкость",
    "denoise": "Убрать шум",
    "detail": "Деталь",
    "diffuse": "Рассеивание",
    "distortion": "Изгиб",
    "distortion_amount": "Изгиб",
    "distortion_horizontal": "Горизонтальный изгиб",
    "distortion_vertical": "Вертикальный изгиб",
    "dividers": "циркуль",
    "edit": "редактировать",
    "effects": "EFX",
    "email": "Эл. адрес",
    "enable": "Включить",
    "estimated_size": "Предполагаемый размер",
    "exposure": "Осветление",
    "eyes": "Глаза",
    "face": "Лицо",
    "favorites": "Избранные",
    "feather": "Растушевка",
    "fit": "Поместиться",
    "font": "Шрифт",
    "format": "Формат",
    "free": "Свободно",
    "frequent": "Частые",
    "fringing": "Отклонение",
    "grain": "Зернистость",
    "green": "Зеленый",
    "guides": "Путеводители",
    "high": "Высокий",
    "highlights": "Особенности",
    "histogram": "Истограм",
    "history": "История",
    "hsl": "HSL",
    "hue": "Оттенок",
    "hue_blue": "HSL – синий оттенок",
    "hue_red": "HSL – красный оттенок",
    "invert": "Инверсия",
    "lens": "Линза",
    "lens_distortion": "Объектив Искажение",
    "letter_spacing": "Межбуквенное расстояние",
    "license_key": "Лицензионный ключ",
    "light": "Светлый",
    "line_spacing": "Межстрочный интервал",
    "lips": "губы",
    "look": "Фильтр",
    "looks": "Фильтры",
    "low": "Низкий",
    "luminance": "Освещение",
    "luminance_blue": "HSL – синее свечение",
    "luminance_red": "HSL – красное свечение",
    "magenta": "Пурпурный",
    "master": "Мастер",
    "max_size": "Максимальные размеры",
    "med": "Средний",
    "mix": "Настроить всё",
    "mosaic": "Пикселизация",
    "name": "имя",
    "none": "Никто",
    "nose": "Нос",
    "opacity": "помутнение",
    "optics": "Оптика",
    "options": "Опции",
    "orange": "Оранжевый",
    "original": "Оригинал",
    "outline": "Контур",
    "password": "пароль",
    "position": "Расположение",
    "prefix": "Приставка сегмент",
    "presets": "Предварительные настройки",
    "preview": "предварительный просмотр",
    "purple": "Фиолетовый",
    "quality": "Качество",
    "ratio": "Пропорции",
    "red": "Красный",
    "reflect": "отражать",
    "resolution": "разрешение",
    "rgb": "RGB",
    "roundness": "Скругление",
    "saturation": "Яркость",
    "saturation_blue": "HSL – яркость синего",
    "saturation_red": "HSL – яркость красного",
    "sayings": "Высказывания",
    "shadow": "Тень",
    "shadows": "Тени",
    "shape": "форма",
    "shapes": "Формы",
    "shapes_presets": "Формы и пресеты",
    "sharpen": "Заострить",
    "size": "Размер",
    "skin": "кожа",
    "smoothness": "Сглаживание",
    "split_tone": "Тонирование",
    "teeth": "Зубы",
    "temperature": "Цв.темп.",
    "text": "Текст",
    "texture": "текстура",
    "threshold": "порог",
    "tint": "Тон",
    "tone": "тон",
    "toning": "тон",
    "use_prefix": "Используйте префикс",
    "use_watermark": "Использовать водяной знак",
    "user_name": "имя пользователя",
    "vibrance": "Насыщенность",
    "vignette": "Виньетка",
    "vignette_amount": "Виньетка",
    "watermark_image": "Watermark Image",
    "wb": "Баланс белого",
    "whitening": "беление",
    "whites": "Белый",
    "width": "Ширина",
    "yellow": "Желтый"
  },
  "menus": {
    "batch_export": "экспорт Пакетный",
    "change_language": "Изменить язык",
    "copy_edit": "Копирование Правок",
    "edit_watermark": "Правка водяного знака",
    "export": "Формат, изменение размера, водяные знаки",
    "filters_users": "Фильтры по Polarrists",
    "guide": "Гид",
    "image_quality": "Качество изображения",
    "image_settings": "Экспорт настроек",
    "language": "Язык",
    "more_filters": "Больше фильтров",
    "open": "Открыть",
    "open_in": "Открыть в",
    "paste_edit": "Вставить Правок",
    "remove": "Удалить",
    "rename": "Переименовать",
    "restore_purchases": "Восстановить покупки",
    "save_cameraroll": "Сохранить в ленту камеры",
    "save_copy": "Сохранить копию",
    "settings": "Настройки",
    "share": "Поделиться",
    "share_edits": "Доля правки",
    "share_filter": "Доля фильтр",
    "show_instructions": "Гид по настройкам",
    "show_intro": "Показать интро",
    "show_video_tutorial": "Видео обучение",
    "single_export": "экспорт"
  },
  "onboarding": {
    "become_master": "Стать мастером редактирования фотографий.",
    "step_0": "Добро пожаловать в Polarr! <br/> Давайте начать с просмотра некоторых фильтров.",
    "step_1": "Нажмите, чтобы выбрать фильтр. <br/> Размах панель, чтобы увидеть больше фильтров.",
    "step_10": "Вы можете перетащить повторно организовать все иконки инструментов.",
    "step_2": "Нажмите <i class='icon-check'></i> чтобы применить фильтр. <br/> Используйте ползунок для регулировки интенсивности фильтра.",
    "step_3": "Каждый фильтр полностью редактируемые. <br/> Перейти к Корректировки, чтобы увидеть изменения.",
    "step_4": "Вы можете изменить настройку путем перетаскивания ползунка.",
    "step_4a": "Длительное нажатие затем перетащить для настройки регулятора.",
    "step_5": "Просмотреть все изменения, внесенные в панели истории.",
    "step_6": "Вы можете отменить редактирование и вернуться в любую точку в истории.",
    "step_7": "Сохранить изображение здесь. <br/> Вы можете изменить размер, добавить водяной знак или сохранить несколько изображений.",
    "step_7a": "Сохранить изображение здесь. <br/> Вы также можете поделиться и сохранить несколько изображений.",
    "step_8": "Откройте свои собственные фотографии здесь.",
    "step_9": "Просмотр открытых фотографий в панели фотографий."
  },
  "status": {
    "account_suspended": "Ваш аккаунт будет поставлен на удержание из-за подозрительной активности. Для получения дополнительной информации, пожалуйста , проверьте ваш адрес электронной почты или контактный <a href=\"mailto:support@polarr.co\">support@polarr.co~~dobj</a>",
    "add_face": "Нажмите <i class='icon-add-face'></i> добавить лицо",
    "adjustment_copied": "Корректировки Скопировано",
    "adjustment_pasted": "Корректировки Pasted",
    "demo_mode": "Эта версия Polarr Photo Editor был создан специально для демонстрационных целей",
    "downloading_font": "Загрузка шрифта",
    "drag_icons": "Перетащите значки переставить инструменты",
    "drop_files": "Перетащите файлы, чтобы открыть",
    "email_invalid": "К сожалению, похоже, что адрес электронной почты не является действительным.",
    "email_reset": "Ссылка сброса было отправлено. <br/> Пожалуйста, проверьте свою электронную почту.",
    "exporting_filter": "Сохранение фильтра",
    "field_invalid": "не является действительным.",
    "file_exists": "Эта фотография уже открыта",
    "file_not_supported": "К сожалению, этот тип файла не поддерживается",
    "filter_exported": "Ваш фильтр был сохранен в камеру ролл.",
    "filter_saved": "Фильтр сохранен",
    "filters_sync_error": "Синхронизация Ошибка фильтров",
    "filters_synced": "Фильтры синхронизируются",
    "filters_syncing": "Синхронизация фильтров ...",
    "import_filter_error": "К сожалению, этот фильтр не могут быть импортированы",
    "import_limit": "Polarr может открыть только до 50 фотографий, пожалуйста, удалите некоторые фотографии, прежде чем открывать больше.",
    "importing_filter": "Импорт фильтров",
    "importing_photo": "фото Открытие",
    "importing_photos": "Открытие фотографий",
    "invalid_credentials": "Неправильный адрес электронной почты или пароль",
    "loading_photo": "Загрузка фото",
    "login_success": "Вы успешно вошли в систему!",
    "no_faces": "Лиц не обнаружено.",
    "no_user": "Этот пользователь не существует.",
    "offline": "Не удалось подключиться к сети.",
    "photo_load_error": "Не удалось загрузить фото",
    "photo_saved": "Фото сохранено",
    "photo_shared": "Вы поделились фото",
    "photos_saved": "фотографии сохраняются",
    "purchase_required": "Покупка требуется",
    "raw_failed": "Невозможно прочитать RAW изображение. RAW поддержка все еще является экспериментальной.",
    "registration_success": "Вы успешно сделали новый счет! <br/> Для того, чтобы войти на других устройствах, необходимо сначала подтвердить свой аккаунт. Мы отправили вам письмо с инструкциями по проверке.",
    "saving_photo": "Сохранение фото",
    "saving_photo_error": "Не удалось сохранить фото :(",
    "saving_photo_error_sd": "Polarr не имеет разрешение на доступ в вашу SD-карту или во внутреннее хранилище.  Обновите Android до последней версии, чтобы решить проблему.",
    "saving_photos": "Сохранение фотографий",
    "server_offline": "К сожалению, сервер не отвечает. <br/> Вы подключены к Интернету?",
    "sharing_photo": "Обмен фото",
    "sharing_photo_error": "Не удалось поделиться фото :(",
    "show_me_how": "Покажите мне, как использовать </thing>",
    "supported_files": "Polarr поддерживает JPG, PNG, BMP, и некоторые RAW файлов",
    "unlocks_remaining": "У тебя есть </unlocks> отпирает остальные",
    "user_exists": "Существует уже учетная запись с этим адресом электронной или имя пользователя.",
    "wrong_password": "Неправильный пароль"
  },
  "tooltips": {
    "add_face": "Добавить Face",
    "add_text": "Добавить текст",
    "adjustments": "корректировок",
    "apply_adjustments": "Применить Корректировки",
    "auto_adjust": "Авто регулировка",
    "brush_filter": "Кисть маски",
    "color_filter": "Цвет маски",
    "crop": "Обрезать",
    "crop_straighten": "Crop & Angle",
    "current_adjustments": "Текущие корректировки",
    "double_tap_edit": "Дважды нажмите, чтобы изменить",
    "drag_watermark": "Перетащите для перемещения водяного знака",
    "eraser": "ластик",
    "face": "Лицо Инструменты",
    "filmstrip": "Открытые фотографии",
    "flip": "Повернуть",
    "flip_h": "Повернуть горизонтально",
    "flip_v": "Повернуть вертикально",
    "global_adjustments": "Общие настройки",
    "gradient_filter": "Градиентная маска",
    "guides": "Путеводители",
    "history": "История Редактирование",
    "info": "Инфо",
    "local_adjustments": "Локализованные настройки",
    "looks": "фильтры",
    "masks": "Маски",
    "move_face": "Переместить грань",
    "open": "Открыть фото",
    "pan": "Кастрюля",
    "radial_filter": "Радиальная маска",
    "redo": "Повторить",
    "remove_face": "Удалить лицо",
    "remove_text": "Удалить текст",
    "rotate": "Поворот",
    "rotate_ccw": "Против ч.с.",
    "rotate_cw": "По ч.с.",
    "save": "Сохранить фото",
    "settings": "Настройки",
    "show_original": "Показать оригинал",
    "text": "Текст Инструмент",
    "undo": "Отменить"
  },
  "tutorial": {
    "blacks": "Изменение яркости черных частей фото",
    "brightness": "Изменение яркости или \"Гамма\" фото",
    "brush_mask_0": "Используйте свой палец, чтобы рисовать с помощью кисти.",
    "brush_mask_1": "Кисть маска воздействует только на матового область.",
    "clarify": "Прояснить",
    "clarify_1": "<b>Уменьшить туман</b> <br/> Туман является общей проблемой в фотографиях, и это легко удалить в Polarr. <br/> Чтобы удалить туман, увеличить <b>Dehaze</b> до 85",
    "clarify_2": "<b>Восстановление экспозиции</b> <br/> Dehaze иногда темнеет фото слишком много. <br/> Чтобы восстановить некоторый свет, увеличение <b>экспозиции</b> до 30",
    "clarify_3": "<b>Увеличить контраст</b> <br/> Помутнение в основном уже нет, но мы можем увеличить световой динамической немного больше. Увеличение <b>контрастности</b> до 60",
    "clarify_4": "<b>Повышать детали</b> <br/> Для повышения детали на фото, мы можем использовать инструмент ясности. Увеличение <b>Clarity</b> до 40",
    "clarify_5": "Хорошая работа! Вы узнали, как сделать мутную фотографию намного лучше в 4 простых шага.",
    "clarify_description": "В этом уроке, вы узнаете, как уменьшить туман, определить контуры и добавить живости к объекту съемки.",
    "clarity": "Увеличение или сгладить детали в фото",
    "color_mask_0": "Цвет маски влияет только на выбранный цвет.",
    "color_mask_1": "Перетащите, чтобы изменить выбранный цвет.",
    "color_mask_2": "<b>Порог</b> устанавливает чувствительность отбора.",
    "contrast": "Сделать объект на фото заметнее.",
    "curves": "Изменение тона кривой фото",
    "deep_space": "Глубокий космос",
    "deep_space_1": "<b>Восстановить свет</b> <br/> регулировка экспозиции Polarr в имитирует экспозицию камеры. <br/> Чтобы восстановить свет, установить <b>экспозицию</b> до 100",
    "deep_space_2": "<b>Повышать детали</b> <br/> Давайте сделаем звезды выглядят более впечатляющими. <br/> Добавление ясности поможет раскрывая больше деталей. <br/> Для повышения подробности, установите <b>Clarity</b> 100",
    "deep_space_3": "<b>Добавить глубину</b> <br/> Для того, чтобы привлечь больше внимания к центру изображения, мы можем добавить виньетка, чтобы затемнить края фотографии. <br/> Для того, чтобы добавить глубину, уменьшить <b>Виньетку</b> до -100",
    "deep_space_4": "<b>Гладкая виньетирования</b> <br/> Сейчас виньетка выглядит слишком резким. <br/> Для того, чтобы сделать виньетка выглядеть гладким и тонким, <br/> увеличить <b>перо</b> до 100",
    "deep_space_5": "<b>Восстановить основные моменты</b> <br/> Некоторые из звезд в край фотографии смотреть темноте. Мы можем восстановить их за счет увеличения <b>Основные моменты</b> 100",
    "deep_space_6": "<b>Изменить цвет</b> <br/> HSL позволяет изменять атрибуты определенного цвета. Чтобы изменить цвет неба, увеличить <b>Аква Hue</b> до 100",
    "deep_space_7": "Хорошая работа! Вы узнали, как сделать звездное небо выглядят очень внушительно.",
    "deep_space_description": "В этом учебнике вы узнаете способы, чтобы добавить глубину, выбрать цвет и изменение экспозиции при съемке ночного неба.",
    "dehaze": "Добавление или удаление дымки из фотографии",
    "denoise": "Удалить шум из фото",
    "diffuse": "Дайте фотография \"свечение\" эффект",
    "distortion": "Искажение",
    "distortion_1": "<b>Правильное искажение</b> <br/> Мы часто видим здания и стены смотрит криво на фотографиях. Обычно это вызвано конструкцией широкоугольных объективов. Мы можем легко исправить это в Polarr с искажением инструментов. <br/> Чтобы устранить искажения, увеличить <b>вертикальные искажения</b> до 70",
    "distortion_2": "<b>Правильная виньетка</b> <br/> В дополнение к оптических искажений, большинство линзы камеры имеют светло-радиоактивные осадки вблизи их краев. Мы можем уменьшить эти темные края с помощью инструмента виньетка. <br/> Чтобы исправить виньетка, увеличить <b>Виньетку</b> до 20",
    "distortion_3": "<b>Повышать детали</b> <br/> И, наконец, для повышения детали на фото, мы можем использовать инструмент ясности. <br/> Увеличение <b>Clarity</b> до 100",
    "distortion_4": "Хорошая работа! Вы узнали, как исправить распространенные дефекты линз в 3 простых шага.",
    "distortion_amount": "Добавить эффект широкоугольной линзы или рыбьего глаза.",
    "distortion_description": "В этом уроке, вы узнаете, как исправить искажения линз и виньетка в архитектурных снимков.",
    "distortion_horizontal": "Изменение искажение вдоль горизонтальной оси",
    "distortion_vertical": "Изменение искажение вдоль вертикальной оси",
    "exposure": "Изменение яркости без вымывания фото",
    "face_0": "Polarr автоматически распознает лица и разделяет индивидуальные особенности.",
    "face_1": "С помощью инструмента искажают для регулировки размера и положения черт лица.",
    "fringing": "Сделать фото размытым.",
    "gradient_mask_0": "Градиент маска влияет на участки в прямой линии.",
    "gradient_mask_1": "Pinch, чтобы изменить размер. <br/> Перетащите для перемещения и вращения.",
    "gradient_mask_2": "<b>Инверсия</b> переключает направление градиента.",
    "gradient_mask_3": "<b>Отражать</b> делает градиент может работать в обоих направлениях.",
    "grain": "Добавьте эффект зернистости пленки",
    "highlights": "Изменение яркости подсветок без вымывания фото",
    "hsl": "Изменение цветового тона, насыщенности, или легкость определенного цвета.",
    "hsl_header": "HSL",
    "hue_blue": "Заменить выбранный цвет на синий.",
    "hue_red": "Заменить выбранный цвет на красный.",
    "luminance_blue": "Изменить яркость синего.",
    "luminance_red": "Изменить яркость красного.",
    "mastering_adjustments": "Овладение Корректировки",
    "mosaic": "Дайте фотографию чтобы эффект мозаики",
    "radial_mask_0": "Радиальная маска влияет только на круговую область.",
    "radial_mask_1": "Pinch, чтобы изменить размер. <br/> Перетащите для перемещения и вращения.",
    "radial_mask_2": "<b>Перо</b> меняет размытость маски.",
    "radial_mask_3": "<b>Инверсия</b> переключает пораженную область.",
    "saturation": "Уменьшать или увеличивать насыщенность цвета.",
    "saturation_blue": "Изменить «синеву» синего цвета.",
    "saturation_red": "Изменить «красноту» красного цвета.",
    "shadows": "Изменение яркости теней, не вымывая фото",
    "sharpen": "Усилить контраст по краям, чтобы фото выглядело ярче.",
    "temperature": "Сделать фото теплее или холоднее",
    "tint": "Настроить цвет по пурпурно-зеленому спектру.",
    "toning_highlights": "Изменение цвета подсветок",
    "toning_shadows": "Изменение цвета теней",
    "vibrance": "Измените насыщенность без вымывания тона кожи",
    "vignette_amount": "Fade или выделяет края фото",
    "vignette_feather": "Изменение размытости виньетка",
    "vignette_highlights": "Пусть основные моменты показывают через виньетка",
    "vignette_roundness": "Сделайте виньетка более круглой или квадратной формы",
    "vignette_size": "Изменение размера виньетка",
    "white_balance": "Баланс белого",
    "white_balance_1": "<b>Правильный баланс белого</b> <br/> Большинство источников света (лампы, фонари и т.д.) не излучают чисто белый цвет. Это может привести к белых в фотографии, чтобы иметь желтый или синий оттенок. Чтобы исправить это, понижение <b>температуры</b> до -30",
    "white_balance_2": "<b>Восстановить свет от тени</b> <br/> Иногда части фотографии темнее, чем предполагалось, потому что наши глаза могут воспринимать больше света, чем камеры. Мы можем восстановить свет от тени, используя тени инструмент. <br/> Увеличение <b>тени</b> до 100",
    "white_balance_3": "<b>Повышать детали</b> <br/> И, наконец, для повышения детали на фото, мы можем использовать инструмент ясности. <br/> Увеличение <b>Clarity</b> до 100",
    "white_balance_4": "Хорошая работа! Вы узнали, как удалить желтый оттенок и восстановить свет от тени в 3 простых шага.",
    "white_balance_description": "В этом учебнике вы узнаете концепцию баланса белого и как исправить в двери искусственном освещении.",
    "whites": "Изменение яркости белых частей фотографии"
  }
};
},{}],333:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "เพิ่ม",
    "add_image": "เลือกภาพ",
    "add_to_favorites": "เพิ่มในรายการโปรด",
    "aspect": "มุมมอง",
    "back": "กลับ",
    "buy": "ซื้อ",
    "buy_full_version": "ซื้อเวอร์ชันแบบตัวเต็ม",
    "cancel": "ยกเลิก",
    "check_again": "ตรวจสอบอีกครั้ง",
    "choose_folder": "เลือกโฟลเดอร์",
    "clear": "ล้าง",
    "clear_history": "ล้างประวัติ",
    "close": "ปิด",
    "cloud": "คลาวด์",
    "continue_anyway": "ดำเนินการต่อ",
    "current_image": "ล่าสุด",
    "delete": "ลบ",
    "device": "เครื่อง",
    "dismiss": "ยกเลิก",
    "done": "เสร็จ",
    "dont_ask": "อย่าถามอีก",
    "duplicate": "ซ้ำ",
    "enter_license": "ใส่คีย์ใบอนุญาต",
    "enter_license_key": "ใส่คีย์ใบอนุญาต",
    "exit": "ทางออก",
    "export": "ส่งออก",
    "export_filter": "บันทึกเป็นรหัส QR",
    "forgot_password": "ลืมรหัสผ่าน?",
    "free_membership": "สมาชิกฟรี",
    "get": "ได้รับ",
    "get_license": "รับคีย์ใบอนุญาต",
    "get_started": "เริ่ม",
    "got_it": "ได้รับมัน",
    "have_account": "มีบัญชีอยู่แล้ว?",
    "import": "นำเข้า",
    "import_filter": "กรองนำเข้า",
    "import_filter_photos": "นำเข้าจากภาพถ่าย",
    "import_photos": "ภาพถ่ายเปิด",
    "info": "ข้อมูล",
    "installed": "ติดตั้งแล้ว",
    "invite": "ส่งลิงค์",
    "learn_more": "เรียนรู้เพิ่มเติม",
    "load_last_photo": "โหลดภาพสุดท้ายในการเปิดตัว",
    "new": "ใหม่",
    "new_filter": "สร้างฟิลเตอร์",
    "next": "ถัดไป",
    "no": "ไม่",
    "no_thanks": "ไม่ ขอบคุณ",
    "not_really": "ไม่",
    "not_this_time": "ไม่ตอนนี้",
    "open_sample": "เปิดตัวอย่างภาพ",
    "paste_adjustments": "การปรับเปลี่ยนการวาง",
    "pay_wechat": "เปิด Wechat",
    "prev": "ก่อนหน้า",
    "preview": "ดูตัวอย่าง",
    "pro": "โปร",
    "purchased": "ซื้อแล้ว",
    "remove": "นำออก",
    "remove_all_photos": "นำภาพถ่ายทั้งหมด",
    "remove_photo": "นำรูปภาพ",
    "remove_selected_photo": "ลบภาพที่เลือก",
    "reset": "ตั้งค่าใหม่",
    "reset_all": "ตั้งค่าใหม่ทั้งหมด",
    "reset_password": "รีเซ็ตรหัสผ่าน",
    "reset_photos": "รีเซ็ตบันทึกและแก้ไขฉลาก",
    "reset_tips": "กำหนดเคล็ดลับใหม่",
    "restore": "กู้คืน",
    "revert": "คืนสภาพเดิม",
    "sample": "ตัวอย่าง",
    "save": "บันทึก",
    "save_qr": "บันทึกรหัส QR",
    "save_share": "บันทึกแบ่งปัน +",
    "scan_filter": "ตัวกรองการสแกน",
    "select_all": "เลือกทั้งหมด",
    "select_changed": "เลือกที่แก้ไข",
    "send": "ส่ง",
    "send_verification": "ส่งอีเมล์ยืนยันอีกครั้ง",
    "show_in_folder": "แสดงในโฟลเดอร์",
    "show_me": "แสดงให้ฉันเห็น",
    "signin": "เข้าสู่ระบบ",
    "signout": "ออกจากระบบ",
    "signup": "สร้างบัญชี Polarr",
    "skip": "ข้าม",
    "submit_license_key": "ส่งคีย์ใบอนุญาต",
    "subscribe": "สมัครสมาชิก",
    "subscribe_monthly": "สมัครสมาชิกรายเดือน",
    "subscribe_yearly": "สมัครสมาชิกประจำปี",
    "sure": "ตกลง แน่นอน",
    "try": "ลอง",
    "try_again": "ลองอีกครั้ง",
    "unlock": "ปลดล็อก",
    "unsubscribe": "ยกเลิกการรับข่าวสาร",
    "update": "อัพเดท",
    "whats_new": "มีอะไรใหม่",
    "yes": "ใช่"
  },
  "dialogs": {
    "about": "เกี่ยวกับ",
    "about_text": "เราเชื่ออย่างยิ่งว่าเป็นเรื่องที่สำคัญในการทำให้ผู้สร้างสรรค์สามารถแสดงออกถึงมุมมองและทัศนคติที่มีต่อชีวิตได้อย่างเต็มที่โดยที่ไม่สูญเสียความมั่นใจในความคิดสร้างสรรค์และแรงบันดาลใจของตนเอง ",
    "account": "บัญชี",
    "all_filters": "ตัวกรองทั้งหมด",
    "all_filters_text": "ปลดล็อกเก็บทั้งหมด Polarr ของฟิลเตอร์พรีเมี่ยม",
    "backup_filters": "ประสานและสำรองกรองแบบกำหนดเองของคุณเพื่อให้คุณสามารถใช้พวกเขาในอุปกรณ์และแพลตฟอร์ม คุณลักษณะนี้จะต้องให้คุณเข้าสู่ระบบด้วยบัญชี Polarr",
    "backup_filters_title": "กรองที่กำหนดเองการสำรองข้อมูล",
    "brush_limit": "วงเงินหมุนเวียนแปรงหน้ากาก </limit>",
    "denoise": "กระบวนการ denoise เวลาจริงของเราทำง่ายที่จะลบเสียงรบกวนและข้าวในรูปถ่าย แยกการควบคุมสีและความสว่างส่วนประกอบเสียง",
    "featured": "แนะนำ",
    "filter_collections": "คอลเลกชันกรอง",
    "filter_name": "ชื่อตัวกรองนี้",
    "get_picky": "เหนื่อยกับการแก้ไขภาพ? ลอง app ใหม่ Polarr ที่แก้ไขภาพถ่ายที่มีปัญญาประดิษฐ์!",
    "get_updates": "สมัครรับการปรับปรุง",
    "get_updates_about": "เป็นคนแรกที่ทดลองใช้คุณลักษณะใหม่! <br/> เพิ่มที่อยู่อีเมลที่คุณและเราจะแจ้งให้คุณทราบเกี่ยวกับคุณลักษณะใหม่และผลิตภัณฑ์จาก Polarr",
    "get_updates_thankyou": "ตอนนี้คุณจะได้รับการปรับปรุงเกี่ยวกับคุณลักษณะใหม่และผลิตภัณฑ์จาก Polarr! <br/> หากคุณต้องการที่จะหยุดได้รับการปรับปรุงคุณสามารถ \"ยกเลิกการเป็นสมาชิก\" จากอีเมลของคุณได้ตลอดเวลา",
    "help": "ช่วยเหลือ",
    "invite": "แบ่งปันลิงค์ติดตั้ง ฟีเจอร์โปรจะปลดล็อกหลังจากที่มีคนติดตั้งแอปผ่านลิงค์ของคุณ ",
    "local_adjustments": "การปรับท้องถิ่นช่วยให้คุณใช้ชุดที่มีประสิทธิภาพ Polarr ของการปรับเปลี่ยนในพื้นที่เป้าหมาย รวมเรเดียลไล่โทนสีและแปรงเครื่องมือกำบัง",
    "lock_rotation": "ล็อคการหมุน",
    "max_export_size": "ขนาดการส่งออกสูงสุด",
    "new_filter": "บันทึกการแก้ไขเป็นตัวกรอง",
    "no_purchases": "มีการซื้อเพื่อเรียกคืนไม่มี",
    "offline": "ดูเหมือนว่าเครื่องของคุณออฟไลน์อยู่ โปรดลองอีกครั้งในภายหลัง ",
    "photos": "ภาพถ่าย",
    "polarr_premium": "Polarr พรีเมี่ยม",
    "pro_all_filters": "คุณสมบัติ Pro <br/> + <br/> ตัวกรองทั้งหมด",
    "pro_all_filters_text": "ปลดล็อคคุณสมบัติทั้งหมดรวมทั้งเครื่องมือ Pro และตัวกรองทั้งหมดพรีเมี่ยมของ Polarr ใช้บัญชีผู้ใช้ของคุณ Polarr เพื่อเข้าถึงคุณลักษณะทั้งหมดบนแพลตฟอร์มอื่น ๆ การสำรองข้อมูลและซิงค์ฟิลเตอร์ของคุณเอง",
    "pro_features": "เครื่องมือ Pro",
    "pro_features_all": "เครื่องมือ Pro",
    "purchase": "ปลดล็อคเรเดียลไล่โทนสี, แปรง, denoise และเครื่องมือข้อความ การสำรองข้อมูลและซิงค์ฟิลเตอร์ของคุณเอง",
    "purchase_failed": "ยกเลิกการซื้อแล้ว",
    "purchase_header": "ปลดล็อกฟีเจอร์โปรทั้งหมด",
    "purchase_successful": "ตอนนี้คุณเข้าถึงเวอร์ชั่นโปรได้",
    "purchase_unavailable": "ไม่มีบริการซื้อในขณะนี้ ขออภัยด้วย",
    "purchases": "การซื้อ",
    "rate_review": "อัตราและทบทวน",
    "rename_filter": "เปลี่ยนชื่อตัวกรอง",
    "restore_failed": "บัญชีของคุณไม่สามารถเข้าถึงคุณสมบัติเต็มรูปแบบ",
    "restore_successful": "การสั่งซื้อทั้งหมดก่อนหน้าของคุณจะถูกเรียกคืนในขณะนี้",
    "review": "ชื่นชอบแอป Polarr Photo Editor ใช่ไหม?",
    "review_no": "คุณเขียนฟีดแบ็คให้เราได้ไหม?",
    "review_yes": "คุณช่วยให้คะแนนแอปของเราที่ App Store ได้ไหม?",
    "save_adjustments": "ทำให้การกรองที่กำหนดเองคุณสามารถแบ่งปัน",
    "send_feedback": "ส่งฟีดแบ็ค",
    "signin_polarr_account": "เข้าสู่ระบบบัญชีของคุณ Polarr",
    "store": "เก็บ",
    "subscribe": "ลงทะเบียนเพื่อรับ <strong>Polarr พรีเมี่ยม</strong>",
    "subscribe_access": "สำหรับการเข้าถึงแบบเต็มไปยัง:",
    "subscribe_cancel": "คุณสามารถ \"ยกเลิกการสมัคร\" ในการตั้งค่าได้ตลอดเวลา",
    "subscribe_caveat": "* คุณสมบัติ Pro รวมถึงการปรับการไล่ระดับสี, ปรับรัศมีกาวสีและแปรงสี เป็นสมาชิกของคุณจะนำไปใช้บนอุปกรณ์และแพลตฟอร์มทั้งหมดในปีนี้",
    "subscribe_china": "สมัครสมาชิก polaxiongkeji บัญชี WeChat และทำให้การชำระเงินใน WeChat แล้วเข้าสู่ระบบด้วยบัญชีของคุณ Polarr เพื่อเปิดใช้งานคุณสมบัติ Pro",
    "subscribe_error": "อ๊ะ! มีข้อผิดพลาดในการสมัครเป็นสมาชิก Polarr เป็น ลองอีกครั้ง?",
    "subscribe_features": "<li> คุณสมบัติ Pro (4 เครื่องมือปรับท้องถิ่น) </li><li> เข้าถึงทุกแพ็ค 12 ตัวกรองพรีเมี่ยม </li><li> ปรับปรุงอยู่สำหรับรูปแบบ RAW </li><li> เพิ่มเนื้อหาใหม่รายเดือน! </li><li> สมาชิก Access บนแพลตฟอร์มทั้งหมด * </li>",
    "subscribe_notify": "แจ้งเตือนเมื่อ Polarr พรีเมี่ยมของฉันสมาชิกสามารถนำมาใช้บนแพลตฟอร์มอื่น ๆ และอุปกรณ์",
    "subscribe_price": "</price>",
    "subscribe_refund": "ก่อนหน้านี้คุณซื้ออะไรกับ Polarr และคุณมีสิทธิ์ได้รับ",
    "subscribe_success": "ขอขอบคุณสำหรับการสมัครเป็นสมาชิกพรีเมี่ยม Polarr!",
    "subscribe_unlock_success": "ตอนนี้คุณเป็นสมาชิกพรีเมี่ยม Polarr!",
    "subscription_expired": "สมาชิก Polarr พรีเมี่ยมของคุณหมดอายุ",
    "text_tool": "เครื่องมือข้อความช่วยให้คุณสามารถเพิ่มข้อความและรูปร่างให้กับภาพถ่าย ที่มีคุณสมบัติเช่นตัวอักษรและระยะห่างระหว่างบรรทัดการจัดตำแหน่งและการวางรูปภาพคุณมีการควบคุมเต็มข้อความของคุณ เราได้รวมแม้คุณสมบัติขั้นสูงเช่นการปรับเวกเตอร์แบบไดนามิกและการปรับการผสมที่ไม่ซ้ำกัน",
    "text_tool_description": "เพิ่มและแก้ไขข้อความบนหน้าจอ หมุนเปลี่ยนสีตั้งค่าการผสมและแม้กระทั่งการปรับระยะห่างตัวอักษร",
    "thankyou": "ขอขอบคุณ!",
    "unlock_all": "ปลดล็อคทุกอย่าง",
    "unlock_all_note": "เข้าสู่ระบบบัญชี Pro Polarr หรือป้อนคีย์ใบอนุญาต",
    "unlock_alternate": "นอกจากนี้คุณยังสามารถอัพเกรดโดยเข้าสู่บัญชีที่มีอยู่หรือโดยการป้อนคีย์ใบอนุญาตใน",
    "unlock_failed": "คุณสมบัติ Pro จะไม่สามารถใช้ได้สำหรับบัญชีนี้",
    "unlock_success": "ตอนนี้คุณสามารถเข้าถึงคุณลักษณะทั้งหมด",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> และ <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> , แหล่งที่ดีที่สุดสำหรับฟรีและมีความละเอียดสูงถ่ายภาพสต็อกได้ร่วมขึ้นที่จะนำคุณชุดของ 10 ฟิลเตอร์พรีเมี่ยมที่สร้างขึ้นโดยสมาชิกของชุมชนช่างภาพ Unsplash </p>",
    "upgrade": "อัพเกรดเป็นเวอร์ชั่นเต็มรูปแบบเพื่อปลดล็อคคุณสมบัติดังต่อไปนี้ทั้งหมด",
    "upgrade_caveat": "* 12 แพ็คกรองเพิ่มเติมรวมถึงการจำลองของหนังคลาสสิก, ภาพยนตร์วินเทจ, ภาพยนตร์อินฟาเรด, โทนสีผิวและการปรับอื่น ๆ อีกมากมายเหมาะสำหรับวิชาต่างๆและฉาก",
    "upgrade_features": "<li> การปรับเปลี่ยนการไล่โทนสี </li><li> การปรับวงกลม </li><li> กาวสี </li><li> แปรงปรับ </li><li> 12 แพ็คตัวกรองพิเศษเพิ่มเติม </li><li> การส่งออกภาพ Batch </li>",
    "use_polarrfilter": "ใช้ <strong>#polarrfilter</strong> จะได้รับการสังเกตเห็น!",
    "user_in_paid": "คุณสามารถเข้าสู่ Polarr บนแพลตฟอร์มอื่น ๆ เพื่อเข้าถึงคุณลักษณะทั้งหมด",
    "user_in_unpaid": "หากคุณปลดล็อคทุกสิ่งที่คุณจะสามารถที่จะเข้าสู่ Polarr บนแพลตฟอร์มอื่น ๆ และเข้าถึงคุณลักษณะทั้งหมด",
    "verification_sent": "อีเมลยืนยันถูกส่งไปแล้ว <br/> โปรดตรวจสอบอีเมลของคุณสำหรับคำแนะนำการยืนยัน",
    "verify_account": "เพื่อเข้าสู่ระบบโปรดตรวจสอบบัญชีของคุณ <br/> ตรวจสอบอีเมลของคุณสำหรับคำแนะนำการยืนยัน",
    "webgl_not_supported": "เราต้องขออภัยอย่างยิ่งแต่เครื่องของคุณไม่เป็นไปตามข้อกำหนดภาพกราฟฟิกของแอป Polarr โปรดอัพเดทเป็นระบบปฏิบัติการล่าสุดและลองอีกครั้ง "
  },
  "labels": {
    "adjustments": "การปรับ",
    "after": "หลัง",
    "alignment": "การวางแนว",
    "all": "ทั้งหมด",
    "amount": "จำนวน",
    "angle": "มุม",
    "aqua": "ฟ้า",
    "artwork": "งานศิลปะ",
    "auto": "อัตโนมัติ์",
    "balance": "ความสมดุล",
    "basic": "พื้นฐาน",
    "batch_rename": "ชุดเปลี่ยนชื่อ",
    "batch_resize": "ปรับขนาด Batch",
    "before": "ก่อน",
    "best": "ดีที่สุด",
    "blacks": "ดำ",
    "blend": "การผสมผสาน",
    "blend_mode": "โหมดการผสมผสาน",
    "blue": "น้ำเงิน",
    "blur": "เบลอ",
    "brightness": "ความสว่าง",
    "brush_flow": "แปรงไหล",
    "brush_opacity": "แปรงทึบ",
    "brush_size": "ขนาดแปรง",
    "clarity": "ความกระจ่าง",
    "collection": "คอลเล็คชั่น",
    "color": "สี",
    "contrast": "คอนแทรส",
    "crop": "ตัดบางส่วน",
    "crop_size": "มิติการเพาะปลูก",
    "curves": "โค้ง",
    "custom": "ปรับเอง",
    "dehaze": "Dehaze",
    "denoise": "Denoise",
    "detail": "รายละเอียด",
    "diffuse": "กระจาย",
    "distortion": "โค้งูป",
    "distortion_amount": "โค้งูป",
    "distortion_horizontal": "บิดเบือนแนวนอน",
    "distortion_vertical": "บิดเบือนแนวตั้ง",
    "dividers": "วงเวียน",
    "edit": "แก้ไข",
    "effects": "ผล",
    "email": "อีเมล์",
    "enable": "เปิดใช้งาน",
    "estimated_size": "ขนาดโดยประมาณ",
    "exposure": "รับแสง",
    "eyes": "ตา",
    "face": "ใบหน้า",
    "favorites": "รายการโปรด",
    "feather": "พู่",
    "fit": "พอดี",
    "font": "ตัวอักษร",
    "format": "รูป",
    "free": "ฟรี",
    "frequent": "ใช้มากที่สุด",
    "fringing": "ขอบ",
    "grain": "หยาบ",
    "green": "เขียว",
    "guides": "คู่มือ",
    "high": "สูง",
    "highlights": "ไฮไลท์",
    "histogram": "ฮิสโทแกรม",
    "history": "ประวัติ",
    "hsl": "HSL",
    "hue": "สีสัน",
    "hue_blue": "HSL – สีน้ำเงิน",
    "hue_red": "HSL – สีแดง",
    "invert": "กลับหัว",
    "lens": "เลนส์",
    "lens_distortion": "เลนส์บิดเบือน",
    "letter_spacing": "การเว้นวรรคตัวอักษร",
    "license_key": "คีย์ใบอนุญาต",
    "light": "สว่าง",
    "line_spacing": "ระยะห่างระหว่างบรรทัด",
    "lips": "โอษฐ์",
    "look": "ฟิลเตอร์",
    "looks": "ฟิลเตอร์",
    "low": "ต่ำ",
    "luminance": "ความสว่าง",
    "luminance_blue": "HSL – ความสว่างสีน้ำเงิน",
    "luminance_red": "HSL – ความสว่างสีแดง",
    "magenta": "ม่วงแดง",
    "master": "มาสเตอร์",
    "max_size": "ขนาดแม็กซ์",
    "med": "กลาง",
    "mix": "ปรับทั้งหมด",
    "mosaic": "พิกเซล",
    "name": "ชื่อ",
    "none": "ไม่มี",
    "nose": "จมูก",
    "opacity": "ความทึบแสง",
    "optics": "เลนส์",
    "options": "ตัวเลือก",
    "orange": "ส้ม",
    "original": "ดั้งเดิม",
    "outline": "เค้าโครง",
    "password": "รหัสผ่าน",
    "position": "ตำแหน่ง",
    "prefix": "ส่วนคำนำหน้า",
    "presets": "ที่ตั้งไว้ล่วงหน้า",
    "preview": "ดูตัวอย่าง",
    "purple": "ม่วง",
    "quality": "คุณภาพ",
    "ratio": "สัดส่วน",
    "red": "แดง",
    "reflect": "สะท้อน",
    "resolution": "มติ",
    "rgb": "RGB",
    "roundness": "ความกลม",
    "saturation": "การอิ่มตัว",
    "saturation_blue": "HSL – การอิ่มตัวสีน้ำเงิน",
    "saturation_red": "HSL – การอิ่มตัวสีแดง",
    "sayings": "สุนทรพจน์",
    "shadow": "เงา",
    "shadows": "เงา",
    "shape": "รูปร่าง",
    "shapes": "รูปร่าง",
    "shapes_presets": "รูปร่างและค่าที่ตั้งไว้",
    "sharpen": "ทำให้คมชัด",
    "size": "ขนาด",
    "skin": "ผิว",
    "smoothness": "การปรับให้เรียบ",
    "split_tone": "โทนนิ่ง",
    "teeth": "ฟัน",
    "temperature": "อุณหภูมิ",
    "text": "ข้อความ",
    "texture": "เนื้อผ้า",
    "threshold": "ธรณีประตู",
    "tint": "ถังสี",
    "tone": "โทน",
    "toning": "โทนนิ่ง",
    "use_prefix": "ใช้คำนำหน้า",
    "use_watermark": "ใช้ลายน้ำ",
    "user_name": "ชื่อผู้ใช้",
    "vibrance": "มีชีวิตชีวา",
    "vignette": "วิกเน็ตต์",
    "vignette_amount": "วิกเน็ตต์",
    "watermark_image": "ภาพลายน้ำ",
    "wb": "ขาวดำ",
    "whitening": "ไวท์เทนนิ่ง",
    "whites": "ขาว",
    "width": "ความกว้าง",
    "yellow": "เหลือง"
  },
  "menus": {
    "batch_export": "การส่งออก Batch",
    "change_language": "เปลี่ยนภาษา",
    "copy_edit": "คัดลอกแก้ไข",
    "edit_watermark": "แก้ไขลายน้ำ",
    "export": "รูปแบบปรับขนาดลายน้ำ",
    "filters_users": "ตัวกรองโดย Polarrists",
    "guide": "แนะนำ",
    "image_quality": "คุณภาพรูป",
    "image_settings": "ส่งออกการตั้งค่า",
    "language": "ภาษา",
    "more_filters": "ฟิลเตอร์เพิ่มเติม",
    "open": "เปิด",
    "open_in": "เปิดใน",
    "paste_edit": "วางการแก้ไข",
    "remove": "นำออก",
    "rename": "ตั้งชื่อใหม่",
    "restore_purchases": "กู้คืนการซื้อ",
    "save_cameraroll": "บันทึกลงในม้วนกล้อง",
    "save_copy": "บันทึกสำเนา",
    "settings": "การตั้งค่า",
    "share": "แชร์",
    "share_edits": "แบ่งปันการแก้ไข",
    "share_filter": "แบ่งปันกรอง",
    "show_instructions": "คำแนะนำการปรับ",
    "show_intro": "แสดงบทนำ",
    "show_video_tutorial": "วิดีโอฝึกสอน",
    "single_export": "ส่งออก"
  },
  "onboarding": {
    "become_master": "เป็นหลักในการแก้ไขภาพ",
    "step_0": "ยินดีต้อนรับสู่ Polarr! <br/> ให้เริ่มต้นด้วยการดูตัวกรองบางส่วน",
    "step_1": "แตะเพื่อเลือกตัวกรอง <br/> แผงรูดเพื่อดูตัวกรองมากขึ้น",
    "step_10": "คุณสามารถลากอีกครั้งจัดไอคอนเครื่องมือทั้งหมด",
    "step_2": "แตะเบา ๆ <i class='icon-check'></i> ใช้ตัวกรอง <br/> ใช้แถบเลื่อนเพื่อปรับความเข้มของตัวกรอง",
    "step_3": "ทุกตัวกรองที่สามารถแก้ไขได้อย่างสมบูรณ์ <br/> ไปที่การปรับเปลี่ยนเพื่อดูการแก้ไขของคุณ",
    "step_4": "คุณสามารถเปลี่ยนการปรับตัวโดยการลากแถบเลื่อน",
    "step_4a": "ลองกดแล้วลากเพื่อปรับตัวเลื่อน",
    "step_5": "ดูการแก้ไขทั้งหมดที่คุณได้ทำในแผงประวัติศาสตร์",
    "step_6": "คุณสามารถยกเลิกการแก้ไขและกลับไปที่จุดใด ๆ ในประวัติศาสตร์",
    "step_7": "บันทึกภาพของคุณที่นี่ <br/> คุณสามารถปรับขนาดเพิ่มลายน้ำหรือบันทึกภาพหลายภาพ",
    "step_7a": "บันทึกภาพของคุณที่นี่ <br/> นอกจากนี้คุณยังสามารถแบ่งปันและบันทึกภาพหลายภาพ",
    "step_8": "เปิดภาพถ่ายของคุณเองที่นี่",
    "step_9": "เปิดดูรูปภาพของคุณในรูปถ่ายแผง"
  },
  "status": {
    "account_suspended": "บัญชีของคุณถูกวางไว้เนื่องจากกิจกรรมที่น่าสงสัย สำหรับข้อมูลเพิ่มเติมกรุณาตรวจสอบอีเมลหรือการติดต่อของคุณ <a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "แตะเบา ๆ <i class='icon-add-face'></i> เพื่อเพิ่มใบหน้า",
    "adjustment_copied": "การปรับคัดลอก",
    "adjustment_pasted": "การปรับวาง",
    "demo_mode": "รุ่นของ Polarr แก้ไขรูปนี้ได้รับการสร้างขึ้นเฉพาะสำหรับวัตถุประสงค์ในการสาธิต",
    "downloading_font": "ตัวอักษรดาวน์โหลด",
    "drag_icons": "ลากไอคอนเพื่อจัดเรียงเครื่องมือ",
    "drop_files": "วางไฟล์ที่จะเปิด",
    "email_invalid": "ขออภัยดูเหมือนว่าที่อยู่อีเมลที่ไม่ถูกต้อง",
    "email_reset": "การเชื่อมโยงการตั้งค่าได้ถูกส่งไป <br/> โปรดตรวจสอบอีเมลของคุณ",
    "exporting_filter": "บันทึกตัวกรอง",
    "field_invalid": "ไม่ถูกต้อง",
    "file_exists": "รูปถ่ายที่เปิดอยู่แล้ว",
    "file_not_supported": "ขออภัยที่ประเภทไฟล์ที่ไม่สนับสนุน",
    "filter_exported": "ตัวกรองของคุณได้รับการบันทึกไว้ในม้วนกล้อง",
    "filter_saved": "กรองที่บันทึกไว้",
    "filters_sync_error": "ฟิลเตอร์ข้อผิดพลาดในการซิงค์",
    "filters_synced": "ฟิลเตอร์ซิงค์",
    "filters_syncing": "ซิงค์ฟิลเตอร์ ...",
    "import_filter_error": "ขออภัยกรองที่ไม่สามารถนำเข้า",
    "import_limit": "Polarr เท่านั้นที่สามารถเปิดได้ถึง 50 ภาพโปรดลบภาพบางส่วนก่อนที่จะเปิดมากขึ้น",
    "importing_filter": "การนำเข้ากรอง",
    "importing_photo": "ภาพเปิด",
    "importing_photos": "เปิดภาพถ่าย",
    "invalid_credentials": "อีเมลหรือรหัสผ่านไม่ถูกต้อง",
    "loading_photo": "กำลังโหลดรูปภาพ",
    "login_success": "คุณได้รับการประสบความสำเร็จในการเข้าสู่ระบบ!",
    "no_faces": "ไม่มีใบหน้าที่ตรวจพบ",
    "no_user": "ผู้ใช้ที่ไม่มีอยู่",
    "offline": "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้",
    "photo_load_error": "ไม่สามารถโหลดรูปภาพได้",
    "photo_saved": "บันทึกรูปภาพแล้ว",
    "photo_shared": "แชร์รูปภาพแล้ว",
    "photos_saved": "ภาพถ่ายที่บันทึกไว้",
    "purchase_required": "จำเป็นต้องสั่งซื้อ",
    "raw_failed": "ไม่สามารถอ่านภาพ RAW สนับสนุน RAW ยังคงทดลอง",
    "registration_success": "คุณได้ทำประสบความสำเร็จในบัญชีใหม่! <br/> เข้าสู่ระบบในอุปกรณ์อื่น ๆ ก่อนอื่นคุณต้องยืนยันบัญชีของคุณ เราได้ส่งอีเมลถึงคุณพร้อมคำแนะนำการยืนยัน",
    "saving_photo": "รูปภาพออมทรัพย์",
    "saving_photo_error": "ข้อขัดข้องในการบันทึกรูปภาพ :(",
    "saving_photo_error_sd": "แอป Polarr ไม่ได้รับอนุญาตให้เข้าถึงการ์ด SD หรือการจัดเก็บภายในของคุณ โปรดตรวจสอบการอัพเดทระบบ Android เพื่อแก้ไขปัญหา",
    "saving_photos": "การบันทึกภาพ",
    "server_offline": "ขออภัยเซิร์ฟเวอร์ไม่ตอบสนอง <br/> คุณเชื่อมต่อกับอินเทอร์เน็ตหรือไม่",
    "sharing_photo": "Photo Sharing",
    "sharing_photo_error": "ข้อขัดข้องในการแชร์รูปภาพ :(",
    "show_me_how": "แสดงให้ฉันเห็นวิธีการใช้งาน </thing>",
    "supported_files": "Polarr สนับสนุน JPG, PNG, BMP, และไฟล์ RAW บางอย่าง",
    "unlocks_remaining": "คุณมี </unlocks> ปลดล็อคที่เหลือ",
    "user_exists": "มีอยู่แล้วบัญชีกับอีเมลหรือชื่อผู้ใช้ว่า",
    "wrong_password": "รหัสผ่านผิด"
  },
  "tooltips": {
    "add_face": "เพิ่มใบหน้า",
    "add_text": "เพิ่มข้อความ",
    "adjustments": "การปรับเปลี่ยน",
    "apply_adjustments": "สมัครปรับ",
    "auto_adjust": "ปรับอัตโนมัติ",
    "brush_filter": "หน้ากากแปรง",
    "color_filter": "หน้ากากสี",
    "crop": "ตัดบางส่วน",
    "crop_straighten": "การเพาะปลูกและมุม",
    "current_adjustments": "ปรับปัจจุบัน",
    "double_tap_edit": "แตะสองครั้งเพื่อแก้ไข",
    "drag_watermark": "ลากเพื่อย้ายลายน้ำ",
    "eraser": "ยางลบ",
    "face": "เครื่องมือใบหน้า",
    "filmstrip": "เปิดภาพถ่าย",
    "flip": "กลับด้าน",
    "flip_h": "กลับด้านแนวนอน",
    "flip_v": "กลับด้านแนวตั้ง",
    "global_adjustments": "การปรับทั้งหมด",
    "gradient_filter": "หน้ากากไล่โทนสี",
    "guides": "คู่มือ",
    "history": "ประวัติความเป็นมาการแก้ไข",
    "info": "ข้อมูล",
    "local_adjustments": "การปรับเฉพาะที่",
    "looks": "ฟิลเตอร์",
    "masks": "มาสก์",
    "move_face": "ย้ายใบหน้า",
    "open": "เปิดภาพถ่าย",
    "pan": "กระทะ",
    "radial_filter": "หน้ากากไล่รัศมี",
    "redo": "ทำซ้ำ",
    "remove_face": "นำหน้า",
    "remove_text": "ลบข้อความที่",
    "rotate": "หมุน",
    "rotate_ccw": "หมุน CCW",
    "rotate_cw": "หมุน CW",
    "save": "บันทึกภาพ",
    "settings": "การตั้งค่า",
    "show_original": "แสดงต้นฉบับ",
    "text": "เครื่องมือ text",
    "undo": "เลิกทำ"
  },
  "tutorial": {
    "blacks": "เปลี่ยนความสว่างในส่วนที่เป็นสีดำของภาพ",
    "brightness": "การเปลี่ยนแปลงความสว่างหรือ \"แกมมา\" ของภาพ",
    "brush_mask_0": "ใช้นิ้วของคุณในการวาดด้วยแปรง",
    "brush_mask_1": "หน้ากากแปรงเพียงส่งผลกระทบต่อพื้นที่ปัด",
    "clarify": "อธิบาย",
    "clarify_1": "<b>ลดหมอก</b> <br/> หมอกเป็นปัญหาที่พบบ่อยในภาพถ่ายและมันเป็นเรื่องง่ายที่จะลบใน Polarr <br/> ในการลบหมอกเพิ่ม <b>Dehaze</b> 85",
    "clarify_2": "<b>การกู้คืนการสัมผัส</b> <br/> Dehaze บางครั้งมืดลงภาพมากเกินไป <br/> <b>การกู้คืนแสงบางส่วนเพิ่มที่ได้รับถึงวันที่</b> 30",
    "clarify_3": "<b>เพิ่มความคมชัด</b> <br/> หมอกควันจะหายไปส่วนใหญ่ในขณะนี้ แต่เราสามารถเพิ่มแบบไดนามิกไฟมากกว่า <b>เพิ่มความคมชัดถึง</b> 60",
    "clarify_4": "<b>เพิ่มศักยภาพในรายละเอียด</b> <br/> เพื่อเพิ่มรายละเอียดในภาพที่เราสามารถใช้เครื่องมือชัดเจน <b>เพิ่มความชัดเจนถึง</b> 40",
    "clarify_5": "การทำงานที่ดี! คุณได้เรียนรู้วิธีที่จะทำให้ภาพมีหมอกมากขึ้นใน 4 ขั้นตอนง่ายๆ",
    "clarify_description": "ในการกวดวิชานี้คุณจะได้เรียนรู้วิธีการลดหมอกกำหนดโครงร่างและเพิ่มความสดใสกับเรื่องของคุณ",
    "clarity": "เพิ่มหรือเรียบออกรายละเอียดในภาพ",
    "color_mask_0": "หน้ากากสีจะมีผลต่อสีที่เลือก",
    "color_mask_1": "ลากเพื่อเปลี่ยนสีที่เลือก",
    "color_mask_2": "<b>เกณฑ์การกำหนดความไวของการเลือก</b>",
    "contrast": "ทำให้วัตถุโดดเด่นยิ่งขึ้น",
    "curves": "เปลี่ยนโทนสีของภาพ",
    "deep_space": "ห้วงอวกาศ",
    "deep_space_1": "<b>การกู้คืนแสง</b> <br/> ปรับการสัมผัส Polarr ของเลียนแบบการเปิดรับแสงของกล้อง <br/> <b>การกู้คืนแสงตั้งค่าการเปิดรับ</b> 100",
    "deep_space_2": "<b>เพิ่มศักยภาพในรายละเอียด</b> <br/> ขอให้ดาวดูน่าประทับใจมากขึ้น <br/> เพิ่มความคมชัดจะช่วยด้วยการเปิดเผยรายละเอียดเพิ่มเติม <br/> <b>เพื่อเพิ่มรายละเอียดการตั้งค่าความคมชัดที่</b> 100",
    "deep_space_3": "<b>เพิ่มความลึก</b> <br/> เพื่อนำมามุ่งเน้นไปยังศูนย์กลางของภาพที่เราสามารถเพิ่มบทความมืดขอบของภาพ <br/> เพื่อเพิ่มความลึกลด <b>Vignette</b> -100",
    "deep_space_4": "<b>Vignette เรียบ</b> <br/> ตอนนี้บทความดูคมเกินไป <br/> ที่จะทำให้บทความดูเรียบเนียนและบอบบาง <br/> <b>เพิ่มขน</b> 100",
    "deep_space_5": "<b>การกู้คืนไฮไลท์</b> <br/> บางส่วนของดาวในขอบของภาพที่มองไปในความมืด <b>เราสามารถกู้คืนได้โดยการเพิ่มไฮไลท์</b> 100",
    "deep_space_6": "<b>เปลี่ยนสี</b> <br/> HSL ช่วยให้คุณสามารถเปลี่ยนคุณสมบัติของสีบางอย่าง ในการเปลี่ยนสีของท้องฟ้าเพิ่ม <b>Aqua เว้</b> 100",
    "deep_space_7": "การทำงานที่ดี! คุณได้เรียนรู้วิธีที่จะทำให้ดาวบนท้องฟ้ามีลักษณะที่น่าประทับใจจริงๆ",
    "deep_space_description": "ในการกวดวิชานี้คุณจะได้เรียนรู้วิธีการที่จะเพิ่มความลึก, ปรับสีและการเปลี่ยนแปลงการเปิดรับแสงของท้องฟ้ายามค่ำ​​คืนที่",
    "dehaze": "เพิ่มหรือลบหมอกควันจากรูปภาพ",
    "denoise": "ลบเสียงรบกวนจากภาพ",
    "diffuse": "ให้ภาพเป็น \"เรืองแสง\" ผล",
    "distortion": "การบิดเบือน",
    "distortion_1": "<b>การบิดเบือนที่ถูกต้อง</b> <br/> เรามักจะเห็นอาคารและผนังมองคดเคี้ยวในภาพถ่าย นี้มักจะเกิดจากการออกแบบของเลนส์มุมกว้างกล้อง เราสามารถแก้ไขปัญหานี้ใน Polarr ด้วยเครื่องมือบิดเบือน <br/> <b>ในการแก้ไขปัญหาการบิดเบือนผิดเพี้ยนเพิ่มแนวตั้ง</b> 70",
    "distortion_2": "<b>บทความที่ถูกต้อง</b> <br/> ในการเพิ่มการบิดเบือนแสงมากที่สุดเลนส์กล้องถ่ายรูปมีผลกระทบแสงที่อยู่ใกล้กับขอบของพวกเขา เราสามารถลดความเข้มขอบเหล่านี้โดยใช้เครื่องมือที่มีบทความสั้น <br/> เพื่อแก้ไขบทความเพิ่ม <b>Vignette</b> 20",
    "distortion_3": "<b>เพิ่มศักยภาพในรายละเอียด</b> <br/> สุดท้ายเพื่อเพิ่มรายละเอียดในภาพที่เราสามารถใช้เครื่องมือชัดเจน <br/> <b>เพิ่มความชัดเจนถึง</b> 100",
    "distortion_4": "การทำงานที่ดี! คุณได้เรียนรู้วิธีการที่จะแก้ไขข้อบกพร่องเลนส์ที่พบบ่อยใน 3 ขั้นตอนง่ายๆ",
    "distortion_amount": "เพิ่มเอฟเฟกต์มุมกว้างหรือฟิชอายบนรูปภาพของคุณ",
    "distortion_description": "ในการกวดวิชานี้คุณจะได้เรียนรู้วิธีการแก้ไขบิดเบือนเลนส์และบทความในภาพสถาปัตยกรรม",
    "distortion_horizontal": "เปลี่ยนการบิดเบือนแนวนอน",
    "distortion_vertical": "เปลี่ยนบิดเบือนตามแนวแกนตั้ง",
    "exposure": "เปลี่ยนความสว่างโดยไม่ต้องล้างออกภาพ",
    "face_0": "Polarr โดยอัตโนมัติใบหน้าและแยกคุณลักษณะของแต่ละบุคคล",
    "face_1": "ใช้เครื่องมือบิดเบือนในการปรับขนาดและตำแหน่งของใบหน้า",
    "fringing": "เพิ่มเอฟเฟกต์ “สับสน” บนรูปภาพของคุณ",
    "gradient_mask_0": "หน้ากากไล่โทนสีมีผลกระทบต่อพื้นที่ในแนวเส้นตรง",
    "gradient_mask_1": "หยิกเพื่อปรับขนาด <br/> ลากเพื่อย้ายและหมุน",
    "gradient_mask_2": "<b>สวิทช์สลับทิศทางการไล่ระดับสี</b>",
    "gradient_mask_3": "<b>สะท้อนให้เห็นถึงทำให้การไล่ระดับสีทำงานในทั้งสองทิศทาง</b>",
    "grain": "เพิ่มผลเนื้อฟิล์ม",
    "highlights": "เปลี่ยนความสว่างของไฮไลท์โดยไม่ต้องล้างออกภาพ",
    "hsl": "เปลี่ยนสีสันความอิ่มตัวของสีหรือความสว่างของสีที่เฉพาะเจาะจง",
    "hsl_header": "HSL",
    "hue_blue": "เปลี่ยนสีจำเพาะเป็นสีน้ำเงิน",
    "hue_red": "เปลี่ยนสีจำเพาะเป็นสีแดง",
    "luminance_blue": "เปลี่ยนความสว่างของสีน้ำเงิน",
    "luminance_red": "เปลี่ยนความสว่างของสีแดง",
    "mastering_adjustments": "การเรียนรู้การปรับ",
    "mosaic": "ให้ภาพผลกระเบื้องโมเสค",
    "radial_mask_0": "หน้ากากรัศมีเพียงส่งผลกระทบต่อพื้นที่วงกลม",
    "radial_mask_1": "หยิกเพื่อปรับขนาด <br/> ลากเพื่อย้ายและหมุน",
    "radial_mask_2": "<b>ขนเปลี่ยนแปลงเลือนของหน้ากาก</b>",
    "radial_mask_3": "<b>สวิทช์สลับพื้นที่ได้รับผลกระทบ</b>",
    "saturation": "เพิ่มหรือลดความเข้มข้นของสี",
    "saturation_blue": "เปลี่ยน “ความเป็นน้ำเงิน” ของสีน้ำเงิน",
    "saturation_red": "เปลี่ยน “ความแดง” ของสีแดง",
    "shadows": "เปลี่ยนความสว่างของเงาโดยไม่ต้องล้างออกภาพ",
    "sharpen": "เพิ่มคอนแทรสต์ขอบเพื่อให้ภาพคมชัดยิ่งขึ้น",
    "temperature": "ทำให้ภาพอุ่นหรือเย็น",
    "tint": "ปรับสีตามแถบสีม่วงแดง-เขียว",
    "toning_highlights": "เปลี่ยนสีของไฮไลท์ที่",
    "toning_shadows": "เปลี่ยนสีของเงา",
    "vibrance": "เปลี่ยนความอิ่มตัวของสีโดยไม่ต้องล้างออกโทนสีผิว",
    "vignette_amount": "จางหายหรือไฮไลท์ขอบของภาพ",
    "vignette_feather": "เปลี่ยนเลือนของบทความนี้",
    "vignette_highlights": "ให้ไฮไลท์แสดงผ่านบทความสั้น",
    "vignette_roundness": "ทำให้รอบบทความสั้นมากหรือตาราง",
    "vignette_size": "เปลี่ยนขนาดของบทความนี้",
    "white_balance": "สมดุลแสงสีขาว",
    "white_balance_1": "<b>สมดุลสีขาวที่ถูกต้อง</b> <br/> ส่วนใหญ่ที่มาแสง (หลอดไฟไฟฉาย ฯลฯ ) ไม่ปล่อยสีขาวล้วน นี้สามารถทำให้ผิวขาวในภาพจะมีสีเหลืองหรือสีฟ้า <b>การแก้ไขปัญหานี้ลดอุณหภูมิ</b> -30",
    "white_balance_2": "<b>การกู้คืนแสงออกมาจากเงามืด</b> <br/> บางครั้งชิ้นส่วนของภาพที่มีสีเข้มกว่าที่ตั้งใจไว้เพราะดว​​งตาของเราสามารถรับรู้แสงมากกว่ากล้อง เราสามารถกู้คืนแสงออกมาจากเงามืดใช้เครื่องมือเงา <br/> <b>เพิ่มเงา</b> 100",
    "white_balance_3": "<b>เพิ่มศักยภาพในรายละเอียด</b> <br/> สุดท้ายเพื่อเพิ่มรายละเอียดในภาพที่เราสามารถใช้เครื่องมือชัดเจน <br/> <b>เพิ่มความชัดเจนถึง</b> 100",
    "white_balance_4": "การทำงานที่ดี! คุณได้เรียนรู้วิธีการเอาสีเหลืองและกู้คืนแสงจากเงาใน 3 ขั้นตอนง่ายๆ",
    "white_balance_description": "ในการกวดวิชานี้คุณจะได้เรียนรู้แนวคิดของสมดุลสีขาวและวิธีการที่ถูกต้องในประตูแสงประดิษฐ์",
    "whites": "การเปลี่ยนแปลงความสว่างของชิ้นส่วนสีขาวของภาพ"
  }
};
},{}],334:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "Ekle",
    "add_image": "görüntüyü seçin",
    "add_to_favorites": "Favorilere Ekle",
    "aspect": "Görünüm",
    "back": "Geri",
    "buy": "Satın Al",
    "buy_full_version": "Tam sürümü satın al",
    "cancel": "İptal",
    "check_again": "Tekrar kontrol et",
    "choose_folder": "klasörü seçin",
    "clear": "Temizle",
    "clear_history": "Geçmişi temizle",
    "close": "Kapat",
    "cloud": "Cloud",
    "continue_anyway": "Yine de devam et",
    "current_image": "Şimdiki",
    "delete": "Sil",
    "device": "Aygıt",
    "dismiss": "reddetmek",
    "done": "Tamamlandı",
    "dont_ask": "Tekrar sorma",
    "duplicate": "Çift",
    "enter_license": "Lisans anahtarını girin",
    "enter_license_key": "Lisans anahtarını girin",
    "exit": "çıkış",
    "export": "Dışa Aktar",
    "export_filter": "QR kodu olarak kaydet",
    "forgot_password": "Parolanızı mı unuttunuz?",
    "free_membership": "ücretsiz üyelik",
    "get": "almak",
    "get_license": "Lisans anahtarını alın",
    "get_started": "Başlamak",
    "got_it": "Anladım",
    "have_account": "Zaten hesabınız var mı?",
    "import": "İçe Aktar",
    "import_filter": "İthalat filtresi",
    "import_filter_photos": "Fotoğraflar İthalat",
    "import_photos": "Açık fotoğrafları",
    "info": "Bilgi",
    "installed": "Yüklendi",
    "invite": "Link gönder",
    "learn_more": "Daha fazla bilgi edin",
    "load_last_photo": "başlatılması üzerine son fotoğraf yüklemek",
    "new": "Yeni",
    "new_filter": "Filtreyi temizle",
    "next": "Sonra",
    "no": "Hayır",
    "no_thanks": "Hayır, teşekkürler",
    "not_really": "Pek sayılmaz",
    "not_this_time": "Şimdi değil ",
    "open_sample": "Örnek resmi aç",
    "paste_adjustments": "Yapıştır ayarlamaları",
    "pay_wechat": "Açık Wechat",
    "prev": "Önce",
    "preview": "Önizleme",
    "pro": "Pro",
    "purchased": "Satın Alındı",
    "remove": "Kaldır",
    "remove_all_photos": "Tüm fotoğrafları kaldır",
    "remove_photo": "fotoğrafı kaldır",
    "remove_selected_photo": "Seçilen fotoğrafı kaldırmak",
    "reset": "Sıfırla",
    "reset_all": "Tümünü Sıfırla",
    "reset_password": "Şifreyi yenile",
    "reset_photos": "kaydedilir ve düzenlenebilir etiketleri Reset",
    "reset_tips": "İpuçlarını Sıfırla",
    "restore": "Önceki boyut",
    "revert": "Orijinale Geri Çevir",
    "sample": "Örnek",
    "save": "Kaydet",
    "save_qr": "QR kodu kaydetmek",
    "save_share": "Kaydet + Paylaş",
    "scan_filter": "tarama filtresi",
    "select_all": "Tümünü seç",
    "select_changed": "Düzenlenenleri seç ",
    "send": "Gönder",
    "send_verification": "Doğrulama e-postasını tekrar gönder",
    "show_in_folder": "Klasörde göster",
    "show_me": "Göster bana",
    "signin": "Oturum aç",
    "signout": "Çıkış Yap",
    "signup": "Polarr hesabı oluşturun",
    "skip": "Atla",
    "submit_license_key": "Lisans anahtarını Gönder",
    "subscribe": "Abone",
    "subscribe_monthly": "Aylık Abone",
    "subscribe_yearly": "Yıllık abone",
    "sure": "Tamam, tabii ki",
    "try": "denemek",
    "try_again": "Tekrar dene",
    "unlock": "Kilidi aç",
    "unsubscribe": "Aboneliği iptal",
    "update": "Güncelle",
    "whats_new": "Ne var ne yok",
    "yes": "Evet"
  },
  "dialogs": {
    "about": "Hakkında",
    "about_text": "Biz kreatörlerin zorlanmadan kendi bakış açılarını ve yaklaşımlarını kendi yaratıcılıklarını ve esin kaynaklarını kaybetmeden yansıtması taraftarıyız.",
    "account": "hesap",
    "all_filters": "Tüm filtreler",
    "all_filters_text": "prim filtreleri Polarr en tüm koleksiyonu kilidini",
    "backup_filters": "Senkronize ve cihazlar ve platformlarda bunları kullanmak, böylece tüm özel filtreler yedekleyin. Bu özellik bir Polarr hesabıyla giriş gerektirir.",
    "backup_filters_title": "Yedekleme Özel Filtreler",
    "brush_limit": "Geçerli Fırça Maske sınırı </limit>",
    "denoise": "Bizim gerçek zamanlı denoise süreci kolay fotoğraf gürültü ve tahıl çıkarmak için yapar. Ayrı renk ve parlaklık gürültü bileşenlerini kontrol eder.",
    "featured": "Öne",
    "filter_collections": "filtre Koleksiyonlar",
    "filter_name": "Bu filtreyi Ad",
    "get_picky": "fotoğraf düzenleme bıktınız mı? Yapay Zeka ile fotoğraf düzenlemeleri Polarr yeni uygulamayı deneyin!",
    "get_updates": "Abone güncellemeleri",
    "get_updates_about": "yeni özellikleri denemek için ilk olun! <br/> size e-posta adresini ekleyin ve biz Polarr gelen yeni özellikler ve ürünler hakkında sizi bilgilendireceğiz.",
    "get_updates_thankyou": "Artık Polarr gelen yeni özellikler ve ürünler hakkında güncelleştirmeleri almak olacak! <br/> Eğer güncellemeleri almayı durdurmak isterseniz, istediğiniz zaman e-posta \"abonelikten çık\" olabilir.",
    "help": "Yardım",
    "invite": "Yükleme linkini paylaşın. Pro özellikleri üç kişinin sizin verdiğiniz linki kullanarak yükleme gerçekleştirdiğinde açılacaktır.",
    "local_adjustments": "Yerel ayarlamalar hedeflenen alanlarda ayarlamalar Polarr güçlü bir set kullanmanızı sağlar. Radyal Gradient, Renk ve Fırça maskeleme araçları içerir.",
    "lock_rotation": "kilit rotasyon",
    "max_export_size": "Max ihracat boyutu",
    "new_filter": "Kaydet bir filtre olarak düzenler",
    "no_purchases": "geri satın alma yok bulunmamaktadır.",
    "offline": "Cihazınız çevrimdışı görünüyor. Lütfen daha sonra tekrar deneyin.",
    "photos": "Fotoğraflar",
    "polarr_premium": "Polarr Premium",
    "pro_all_filters": "Pro özellikleri <br/> + <br/> Tüm filtreler",
    "pro_all_filters_text": "pro araçlar ve Polarr prim filtreleri dahil olmak üzere tüm tüm özellikleri kilidini. diğer platformlarda tüm özelliklere erişmek için Polarr hesabı kullanın. Yedekleme ve özel filtreler senkronize edin.",
    "pro_features": "Pro Tools",
    "pro_features_all": "Tüm Pro Tools",
    "purchase": "Radyal, Gradient, Renk, Fırça, DeNoise ve Metin araçlarını kilidini. Yedekleme ve özel filtreler senkronize edin.",
    "purchase_failed": "Satın alma iptal edildi",
    "purchase_header": "Tüm pro özelliklerini aç",
    "purchase_successful": "Artık pro versiyona erişiminiz var.",
    "purchase_unavailable": "Satın alma işlemi şu an kullanılabilir değil. Özür dileriz.",
    "purchases": "Satın Almalar",
    "rate_review": "Oran ve yorum",
    "rename_filter": "Filtre yeniden adlandırma",
    "restore_failed": "Hesabınız tam özellik erişimi yok.",
    "restore_successful": "Bütün önceki alımları artık geri yüklenir.",
    "review": "Polarr Photo Editor’ü sevdiniz mi?",
    "review_no": "Bize geri bildirim yapmak ister misiniz?",
    "review_yes": "O halde App Store’da puan vermeye ne dersiniz?",
    "save_adjustments": "paylaşabilirsiniz özel bir filtre yapın.",
    "send_feedback": "Geribildirim gönder",
    "signin_polarr_account": "senin Polarr hesabınıza giriş yapın",
    "store": "mağaza",
    "subscribe": "<strong>Polarr Premium</strong> kaydol",
    "subscribe_access": "tam erişim için:",
    "subscribe_cancel": "Ayarlar zaman içinde yapabilirsiniz \"çık\"",
    "subscribe_caveat": "* Pro özellikleri degrade ayarı, radyal ayarlama, renk maskeleme ve renk fırçalama içerir. Üyeliğiniz bu yıl tüm cihazlar ve platformlarda geçerli olacaktır.",
    "subscribe_china": "WeChat hesabını polaxiongkeji ve wechat bir ödeme yapmak, daha sonra profesyonel özellikleri etkinleştirmek için Polarr hesabınızla oturum için abone olun.",
    "subscribe_error": "Oops! Polarr abone hata oluştu. Tekrar dene?",
    "subscribe_features": "<li> Pro özellikleri (4 yerel ayar araçları) </li><li> 12 prim filtre paketleri erişmek </li><li> RAW formatları için güncelleştirilmiş kalın </li><li> Her ay yeni içerik eklendi! </li><li> tüm platformlarda erişim üyelik * </li>",
    "subscribe_notify": "Benim Polarr Premium Üyelik fazla platform ve aygıtlar üzerinde kullanılabilir olduğunda bana bildir",
    "subscribe_price": "</price>",
    "subscribe_refund": "Daha önce Polarr bir şey satın alınan ve sizin için uygun olan",
    "subscribe_success": "Polarr Premium abone olduğunuz için teşekkür ederiz!",
    "subscribe_unlock_success": "Artık Polarr Premium Üye demektir!",
    "subscription_expired": "Sizin Polarr Premium üyeliği sona eren",
    "text_tool": "Metin aracı fotoğraflar metin ve şekiller eklemenizi sağlar. Harf ve satır aralığı, hizalama ve sarma gibi özellikleri sayesinde, tam kontrol metin var. Biz bile dinamik vektör ölçekleme ve benzersiz bir karıştırma ayarı gibi gelişmiş özellikler ekledik.",
    "text_tool_description": "Ekle ve ekrandaki metni düzenleyin. Döndürmek değişim renk, set karıştırma ve hatta harf aralığını ayarlamak.",
    "thankyou": "Teşekkür ederim!",
    "unlock_all": "her şeyi kilidini",
    "unlock_all_note": "senin Polarr yanlısı bir hesap giriş veya lisans anahtarını girin.",
    "unlock_alternate": "Ayrıca, mevcut bir hesaba giriş yaparak veya bir lisans anahtarı girerek yükseltebilirsiniz",
    "unlock_failed": "Pro Özellikleri Bu hesap için geçerli değildir.",
    "unlock_success": "Artık tüm özelliklere erişebilirsiniz.",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a> ve <a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> , ücretsiz ve yüksek çözünürlüklü stok fotoğrafçılığı için en iyi kaynak, size Unsplash fotoğrafçı topluluk üyeleri tarafından oluşturulan 10 prim filtreleri bir koleksiyon getirmek için bir araya geldi. </p>",
    "upgrade": "Aşağıdaki tüm özellikleri kilidini açmak için tam sürüme yükseltin.",
    "upgrade_caveat": "* 12 ek filtre paketleri Klasik Film, Vintage Film, Kızılötesi Film, Cilt Tonları ve çeşitli konularda ve sahnelere uygun diğer birçok ayarlamaları simülasyonlar yer almaktadır.",
    "upgrade_features": "<li> gradyan ayarlamalar </li><li> dairesel ayarlamalar </li><li> renk maskeleme </li><li> fırçalama ayarlama </li><li> 12 ek prim filtre paketleri </li><li> Toplu fotoğraf ihracat </li>",
    "use_polarrfilter": "<strong>#polarrfilter</strong> Kullanın fark almak için!",
    "user_in_paid": "Tüm özelliklere erişmek için diğer platformlarda Polarr giriş yapabilirsiniz.",
    "user_in_unpaid": "Her şeyi kilidini, tüm özellikleri diğer platformlarda Polarr giriş ve erişmek mümkün olacak.",
    "verification_sent": "Bir doğrulama e-gönderildi. <br/> Doğrulama talimatlar için e-postanızı kontrol edin.",
    "verify_account": "Oturum açmak için, hesabınızı doğrulayın. <br/> Doğrulama talimatları için e-posta kontrol edin.",
    "webgl_not_supported": "Çok üzgünüz fakat cihazınız Polarr grafik gereksinimlerini karşılamıyor, lütfen en son işletme sistemine yükseltme yapın ve tekrar deneyin."
  },
  "labels": {
    "adjustments": "Ayarlamlar",
    "after": "Sonra",
    "alignment": "hiza",
    "all": "Tümü",
    "amount": "Miktar",
    "angle": "Açı",
    "aqua": "Aqua",
    "artwork": "sanat eseri",
    "auto": "Oto",
    "balance": "Denge",
    "basic": "Temel",
    "batch_rename": "toplu adlandırmak",
    "batch_resize": "toplu yeniden boyutlandırma",
    "before": "Önce",
    "best": "En İyi",
    "blacks": "Siyahlar",
    "blend": "Harman",
    "blend_mode": "Blend Mode",
    "blue": "Mavi",
    "blur": "Bulanıklık",
    "brightness": "Aydınlık",
    "brush_flow": "fırça Akışı",
    "brush_opacity": "fırça Saydamlık",
    "brush_size": "Fırça boyutu",
    "clarity": "Netlik",
    "collection": "Koleksiyon",
    "color": "Renk",
    "contrast": "Kontrast",
    "crop": "Kırp",
    "crop_size": "Mahsul boyutları",
    "curves": "Eğriler",
    "custom": "Özel Ayar",
    "dehaze": "Netleştir",
    "denoise": "Gürültü Azalt",
    "detail": "Detay",
    "diffuse": "Yay",
    "distortion": "Boz",
    "distortion_amount": "Boz",
    "distortion_horizontal": "Yatay Bozulma",
    "distortion_vertical": "Dikey Bozulma",
    "dividers": "pergel",
    "edit": "Düzenleme",
    "effects": "Etkileri",
    "email": "E-posta",
    "enable": "Aktive et",
    "estimated_size": "tahmini Boyut",
    "exposure": "Poz",
    "eyes": "gözler",
    "face": "yüz",
    "favorites": "Favoriler",
    "feather": "Yumuşatma",
    "fit": "Fit",
    "font": "Yazı tipi",
    "format": "Biçim",
    "free": "Ücretsiz",
    "frequent": "En Çok Kullanılanlar",
    "fringing": "Saçaklama",
    "grain": "Tanecik",
    "green": "Yeşil",
    "guides": "Kılavuzlar",
    "high": "Yüksek",
    "highlights": "Aydınlık",
    "histogram": "Histogram",
    "history": "Geçmiş",
    "hsl": "HSL",
    "hue": "Nüans",
    "hue_blue": "HSL – mavi nüans",
    "hue_red": "HSL – kırmızı nüans",
    "invert": "Ters Çevir",
    "lens": "Lens",
    "lens_distortion": "mercek bozulması",
    "letter_spacing": "Harf boşluğu",
    "license_key": "Lisans anahtarı",
    "light": "Işık",
    "line_spacing": "Satır aralığı",
    "lips": "dudaklar",
    "look": "Filtre",
    "looks": "Filtreler",
    "low": "Düşük",
    "luminance": "Parlaklık",
    "luminance_blue": "HSL – mavi parlaklık ",
    "luminance_red": "HSL - kırmızı parlaklık",
    "magenta": "Magenta",
    "master": "Master",
    "max_size": "Max boyutları",
    "med": "Orta",
    "mix": "Hepsini Ayarla",
    "mosaic": "Pikselleştir",
    "name": "isim",
    "none": "Hiçbiri",
    "nose": "Burun",
    "opacity": "Saydamlık",
    "optics": "Optik",
    "options": "Seçenekler",
    "orange": "Turuncu",
    "original": "Orijinal",
    "outline": "taslak",
    "password": "Parola",
    "position": "Pozisyon",
    "prefix": "önek segmenti",
    "presets": "Presets",
    "preview": "Önizleme",
    "purple": "Mor",
    "quality": "Kalite",
    "ratio": "Oran",
    "red": "Kırmızı",
    "reflect": "yansıtmak",
    "resolution": "çözüm",
    "rgb": "RGB",
    "roundness": "Yuvarlaklık",
    "saturation": "Doygunluk",
    "saturation_blue": "HSL – mavi doygunluk",
    "saturation_red": "HSL – kırmızı doygunluk",
    "sayings": "Atasözü",
    "shadow": "Gölge",
    "shadows": "Gölgeler",
    "shape": "şekil",
    "shapes": "şekiller",
    "shapes_presets": "Şekiller ve Presets",
    "sharpen": "Keskinleştir",
    "size": "Boyut",
    "skin": "cilt",
    "smoothness": "Pürüzsüzleştirici",
    "split_tone": "Tonlama",
    "teeth": "Diş",
    "temperature": "Sıcaklık",
    "text": "Metin",
    "texture": "Doku",
    "threshold": "eşik",
    "tint": "Renk Tonu",
    "tone": "ton",
    "toning": "Tonlama",
    "use_prefix": "Kullanım öneki",
    "use_watermark": "Filigran Kullan",
    "user_name": "Kullanıcı adı",
    "vibrance": "Titreşim",
    "vignette": "Örtü",
    "vignette_amount": "Örtü",
    "watermark_image": "Filigran Görüntü",
    "wb": "BS",
    "whitening": "Beyazlatma",
    "whites": "Beyazlar",
    "width": "genişlik",
    "yellow": "Sarı"
  },
  "menus": {
    "batch_export": "toplu ihracat",
    "change_language": "Dili değiştir",
    "copy_edit": "Kopya Düzenlemeleri",
    "edit_watermark": "Filigran Düzenle",
    "export": "Biçim, yeniden boyutlandırma, Filigran",
    "filters_users": "Polarrists tarafından Filtreler",
    "guide": "Rehber",
    "image_quality": "Resim kalitesi",
    "image_settings": "Ayarları dışa aktar",
    "language": "Dil",
    "more_filters": "Daha fazla filtre",
    "open": "Aç",
    "open_in": "Birlikte aç",
    "paste_edit": "Yapıştır Düzenlemeleri",
    "remove": "Kaldır",
    "rename": "Yeniden Adlandır",
    "restore_purchases": "Satın almaları yinele",
    "save_cameraroll": "Kamera Roll kaydet",
    "save_copy": "Kopyayı kaydet",
    "settings": "Ayarlar",
    "share": "Paylaş",
    "share_edits": "Hisse düzenlemeler",
    "share_filter": "Hisse filtresi",
    "show_instructions": "Ayarlama kılavuzu",
    "show_intro": "Göster intro",
    "show_video_tutorial": "Video rehberi",
    "single_export": "İhracat"
  },
  "onboarding": {
    "become_master": "Bir fotoğraf düzenleme usta olmak.",
    "step_0": "Polarr hoş geldiniz! <br/> Bazı filtreler görüntüleyerek başlamak sağlar.",
    "step_1": "Bir filtre seçmek için dokunun. <br/> Tokatlamak paneli daha fazla filtre görmek için.",
    "step_10": "Tüm araç simgeleri yeniden düzenlemek için sürükleyebilirsiniz.",
    "step_2": "Musluk <i class='icon-check'></i> Filtreyi uygulamak. <br/> Filtrenin yoğunluğunu ayarlamak için kaydırma çubuğunu kullanın.",
    "step_3": "Her filtre tamamen düzenlenebilir. <br/> Yaptığınız düzenlemeleri görmek için Ayarlamalar gidin.",
    "step_4": "Sen kaydırma çubuğunu sürükleyerek bir ayarlama değiştirebilirsiniz.",
    "step_4a": "Uzun basış sonra kaydırıcıyı ayarlamak için sürükleyin.",
    "step_5": "Eğer tarih panelinde yaptığınız tüm düzenlemeleri bakın.",
    "step_6": "Bir düzenlemeyi geri almak ve tarihin herhangi bir noktaya geri dönebilirsiniz.",
    "step_7": "Burada görüntü kaydetme. <br/> Sen, yeniden boyutlandırmak filigran eklemek veya birden fazla görüntü kaydedebilirsiniz.",
    "step_7a": "Burada görüntü kaydetme. <br/> Ayrıca paylaşmak ve birden fazla görüntü kaydedebilirsiniz.",
    "step_8": "Burada kendi fotoğraflarınızı açın.",
    "step_9": "Fotoğraf panelinde açılan fotoğrafları görüntüleyin."
  },
  "status": {
    "account_suspended": "Hesabınız şüpheli etkinlik için beklemeye alınır. Daha fazla bilgi için, e-posta veya iletişim kontrol edin <a href=\"mailto:support@polarr.co\">support@polarr.co~~dobj</a>",
    "add_face": "Musluk <i class='icon-add-face'></i> Bir yüz eklemek için",
    "adjustment_copied": "Düzeltmeler Kopyalanan",
    "adjustment_pasted": "Düzeltmeler yapıştırıldı",
    "demo_mode": "Polarr Photo Editor bu sürümü gösteri amaçlı özel olarak oluşturulmuş olan",
    "downloading_font": "indiriliyor yazı",
    "drag_icons": "araçları yeniden düzenlemek için simgeleri sürükleyin",
    "drop_files": "açmak için açılan dosyaları",
    "email_invalid": "Bu e-posta adresi geçerli değil gibi Maalesef görünüyor.",
    "email_reset": "Bir sıfırlama bağlantısı gönderildi. <br/> Lütfen emailinizi kontrol edin.",
    "exporting_filter": "Tasarruf filtre",
    "field_invalid": "geçerli değildir.",
    "file_exists": "Bu fotoğraf zaten açık",
    "file_not_supported": "Maalesef, bu dosya türü desteklenmiyor",
    "filter_exported": "Filtre kamera rulo kaydedildi.",
    "filter_saved": "filtre kaydedildi",
    "filters_sync_error": "Hata senkronizasyon filtreleri",
    "filters_synced": "Filtreler senkronize",
    "filters_syncing": "filtreleri senkronize ediliyor ...",
    "import_filter_error": "Maalesef, bu filtre alınamadı",
    "import_limit": "Polarr sadece 50 adede kadar fotoğraf açabilirsiniz, daha açmadan önce bazı fotoğrafları kaldırın.",
    "importing_filter": "İthalat filtre",
    "importing_photo": "Açılış fotoğraf",
    "importing_photos": "fotoğrafları Açılış",
    "invalid_credentials": "Yanlış e-posta veya şifre",
    "loading_photo": "Resim yükleniyor",
    "login_success": "Başarıyla giriş edilmiştir ki!",
    "no_faces": "Hiçbir yüzler tespit edildi.",
    "no_user": "Bu kullanıcı yok.",
    "offline": "Server’a bağlanılamadı.",
    "photo_load_error": "Resim yüklenemedi",
    "photo_saved": "Resim kaydedildi",
    "photo_shared": "Resim paylaşıldı",
    "photos_saved": "Fotoğraflar kaydedildi",
    "purchase_required": "satın alınması gerekir",
    "raw_failed": "RAW görüntü okunamıyor. RAW desteği hala deneysel olduğunu.",
    "registration_success": "Başarılı bir şekilde yeni bir hesap yaptık! <br/> diğer cihazlarda oturum açmak için, öncelikle hesabınızı doğrulamanız gerekir. Doğrulama talimatları içeren bir e-posta gönderdik.",
    "saving_photo": "Tasarruf fotoğraf",
    "saving_photo_error": "Resim kaydetme hatası :(",
    "saving_photo_error_sd": "Polarr uygulamasının SD kart veya hafızaya erişim izni yok. Lütfen bu sorunu çözmek için son Android sistem güncellemesini kontrol ediniz.",
    "saving_photos": "fotoğrafları kaydetme",
    "server_offline": "Maalesef, sunucu yanıt vermiyor. <br/> Eğer internete bağlı mı?",
    "sharing_photo": "Paylaşım fotoğraf",
    "sharing_photo_error": "Resim paylaşma hatası :(",
    "show_me_how": "nasıl kullanmak bana göster </thing>",
    "supported_files": "Polarr JPG, PNG, BMP, ve bazı RAW dosyalarını destekler",
    "unlocks_remaining": "Var </unlocks> kalan kilidini",
    "user_exists": "Bu e-posta veya kullanıcı adı ile bir hesap zaten var.",
    "wrong_password": "Yanlış şifre"
  },
  "tooltips": {
    "add_face": "Yüz ekle",
    "add_text": "Yazı ekle",
    "adjustments": "Düzeltmeler",
    "apply_adjustments": "Düzenlemeler uygula",
    "auto_adjust": "Otomatik Düzeltmeler",
    "brush_filter": "fırça Maske",
    "color_filter": "renk Maskesi",
    "crop": "Kırp",
    "crop_straighten": "Kırpma ve Açılı",
    "current_adjustments": "Güncel Düzenlemeler",
    "double_tap_edit": "Iki kez hafifçe vurun düzenlemek için",
    "drag_watermark": "filigran taşımak için sürükleyin",
    "eraser": "Silgi",
    "face": "yüz Araçları",
    "filmstrip": "açılmış Fotoğraflar",
    "flip": "Çevir",
    "flip_h": "Yatay çevir",
    "flip_v": "Dikey çevir",
    "global_adjustments": "Global Ayarlamalar",
    "gradient_filter": "Eğim Maskesi",
    "guides": "Kılavuzlar",
    "history": "düzenleme Geçmişi",
    "info": "Bilgi",
    "local_adjustments": "Yerel Ayarlamalar",
    "looks": "Filtreler",
    "masks": "Maskeler",
    "move_face": "Taşı Yüz",
    "open": "Açık Resimleri",
    "pan": "Tava",
    "radial_filter": "Radyal Maske",
    "redo": "Tekrarla",
    "remove_face": "Face kaldır",
    "remove_text": "Metin kaldır",
    "rotate": "Döndür",
    "rotate_ccw": "SYT döndür",
    "rotate_cw": "SY döndür",
    "save": "Fotoğrafı kaydet",
    "settings": "Ayarlar",
    "show_original": "Orjinali göster",
    "text": "metin Aracı",
    "undo": "Geri Al"
  },
  "tutorial": {
    "blacks": "Fotoğrafın siyah parçaların parlaklığını değiştirme",
    "brightness": "Fotoğrafın parlaklığını ya da \"gamma\" olarak değiştirin",
    "brush_mask_0": "fırça ile boyamak için parmağınızı kullanın.",
    "brush_mask_1": "Fırça maskesi sadece fırçalanmış alanı etkiler.",
    "clarify": "açıklamak",
    "clarify_1": "<b>sis azaltmak</b> <br/> Sis fotoğrafları ortak bir sorundur ve Polarr kaldırmak kolaydır. <br/> Sis kaldırmak için, 85 <b>Dehaze</b> artırmak",
    "clarify_2": "<b>pozlama kurtarmak</b> <br/> Dehaze bazen fotoğraf çok koyulaşır. <br/> Biraz ışık kurtarmak için, 30 <b>Pozlama</b> artırmak",
    "clarify_3": "<b>Kontrast arttırın</b> <br/> pus çoğunlukla şimdi gitti, ama biz biraz daha fazla ışık dinamiğini artırabilir. 60 <b>Kontrast</b> artırmak",
    "clarify_4": "<b>ayrıntıları geliştirin</b> <br/> fotoğrafın ayrıntıları geliştirmek için, biz Clarity aracını kullanabilirsiniz. 40 <b>Clarity</b> artırmak",
    "clarify_5": "İyi iş! Sen 4 basit adımda çok daha iyi bir puslu fotoğraf nasıl öğrendim.",
    "clarify_description": "Bu eğitimde, sen sis azaltmak özetliyor tanımlayabilir ve konuya canlılık katmak için nasıl öğreneceksiniz.",
    "clarity": "Artırmak veya fotoğrafın ayrıntıları düzeltmek",
    "color_mask_0": "Renk maskesi yalnızca seçilen rengi etkiler.",
    "color_mask_1": "Sürükle seçilen renk değiştirmek için.",
    "color_mask_2": "<b>Eşik</b> seçimi hassasiyetini ayarlar.",
    "contrast": "Objelerinizin daha belirgin olmasını sağlayın.",
    "curves": "Fotoğrafın ton eğrisini değiştirme",
    "deep_space": "Derin boşluk",
    "deep_space_1": "<b>Işığı kurtarmak</b> <br/> Polarr pozlama ayarı bir kameranın pozlama simüle eder. <br/> Işığı kurtarmak için, 100 <b>Pozlama</b> ayarlayın",
    "deep_space_2": "<b>ayrıntıları geliştirin</b> <br/> en yıldız daha etkileyici bakmak yapalım. <br/> netlik ekleme daha fazla ayrıntı ortaya çıkararak yardımcı olacaktır. <br/> 100 <b>Clarity</b> ayarlamak, ayrıntıları geliştirmek için",
    "deep_space_3": "<b>derinlik ekle</b> <br/> Görüntünün merkezine daha odak getirmek için, biz fotoğrafın kenarlarını koyulaştırmak için skeç ekleyebilirsiniz. <br/> Derinlik katmak için, -100 <b>Vignette</b> azaltmak",
    "deep_space_4": "<b>pürüzsüz Vignette</b> <br/> Şu anda skeç çok keskin görünüyor. <br/> skeç pürüzsüz ve ince görünmesi için, <br/> 100 <b>Feather</b> artırmak",
    "deep_space_5": "<b>olayları kurtarmak</b> <br/> fotoğrafın kenarında yıldızlı Bazı karanlık bekliyoruz. Biz 100 <b>Highlights</b> artırarak onları kurtarabilirsiniz",
    "deep_space_6": "<b>Rengi değiştir</b> <br/> HSL belirli bir rengin özelliklerini değiştirmenize olanak sağlar. Gökyüzünün rengini değiştirmek için, <b>Aqua Hue</b> 100 artırmak",
    "deep_space_7": "İyi iş! Bir yıldızlı gökyüzü gerçekten etkileyici görünmesi için nasıl öğrendim.",
    "deep_space_description": "Bu eğitimde, sen derinlik eklemek renk ve değişim bir gece gökyüzünün maruz ayarlamak yollarını öğreneceksiniz.",
    "dehaze": "Ekleme veya fotoğraf bulanıklığı gidermek",
    "denoise": "fotoğrafın gürültü çıkarın",
    "diffuse": "Fotoğrafa bir \"parıltı\" efekti vermek",
    "distortion": "Çarpıtma",
    "distortion_1": "<b>doğru bozulma</b> <br/> Biz genellikle binalar ve duvarlar fotoğrafları çarpık görünümlü bkz. Bu, genellikle, geniş açılı kamera lensleri tasarım kaynaklanır. Biz kolayca bozulma araçları ile Polarr bu çözebilirsiniz. <br/> Bozulmayı düzeltmek için, 70 <b>Dikey Deformasyon</b> artırmak",
    "distortion_2": "<b>doğru skeç</b> <br/> optik çarpıtma ek olarak, çoğu kamera mercekleri kendi kenarlarına yakın ışık serpinti var. Biz skeç aracını kullanarak bu koyu kenarları azaltabilir. <br/> Vignette düzeltmek için, 20 bir <b>nakış</b> geliştirmek",
    "distortion_3": "<b>ayrıntıları geliştirin</b> <br/> Son olarak, fotoğrafın ayrıntıları geliştirmek için, biz netlik aracını kullanabilirsiniz. <br/> 100 <b>Clarity</b> artırmak",
    "distortion_4": "İyi iş! 3 basit adımda ortak mercek kusurlarını düzeltmek için nasıl öğrendim.",
    "distortion_amount": "Resimlere geniş açı ve balıkgözü efekti ekleyin.",
    "distortion_description": "Bu eğitimde, mimari çekimlerde objektif bozulmaları ve skeç düzeltmek için nasıl öğreneceksiniz.",
    "distortion_horizontal": "yatay eksen boyunca bozulma değiştirme",
    "distortion_vertical": "dikey eksen boyunca bozulma değiştirme",
    "exposure": "Fotoğrafı yıkama olmadan parlaklığını değiştirin",
    "face_0": "Polarr yüzleri otomatik olarak algılar ve bireysel özellikleri ayırır.",
    "face_1": "boyutu ve yüz özelliklerinin konumunu ayarlamak bozucu aracını kullanın.",
    "fringing": "Resimlerinize \"baş döndürücü\" efekti ekleyin.",
    "gradient_mask_0": "Degrade maskesi düz bir çizgide alanları etkiler.",
    "gradient_mask_1": "yeniden boyutlandırmak için sıkıştırın. <br/> Sürükle taşımak ve döndürmek için.",
    "gradient_mask_2": "<b>Invert</b> degrade yönünü değiştirir.",
    "gradient_mask_3": "<b>Reflect</b> her iki yönde de degrade yapar.",
    "grain": "film tahıl etkisi ekle",
    "highlights": "Fotoğrafı yıkama olmadan vurgulamaktadır parlaklığını değiştirin",
    "hsl": "Belirli bir renk tonu, doygunluk, veya açıklığını değiştirin.",
    "hsl_header": "HSL",
    "hue_blue": "Belli renkleri mavi ile değiştirin.",
    "hue_red": "Belli renkleri kırmızı ile değiştirin.",
    "luminance_blue": "Mavinin parlaklığını değiştirin.",
    "luminance_red": "Kırmızının parlaklığını değiştirin.",
    "mastering_adjustments": "Ayarlamaları mastering",
    "mosaic": "Fotoğrafa bir mozaik efekti vermek",
    "radial_mask_0": "Radyal maske sadece dairesel alanı etkiler.",
    "radial_mask_1": "yeniden boyutlandırmak için sıkıştırın. <br/> Sürükle taşımak ve döndürmek için.",
    "radial_mask_2": "<b>Tüy</b> maske bulanıklık değiştirir.",
    "radial_mask_3": "<b>Invert</b> etkilenen bölgeyi geçer.",
    "saturation": "Renk yoğunluğunu artırın veya düşürün.",
    "saturation_blue": "Mavi resimlerin \"maviliğini\" değiştirin.",
    "saturation_red": "Kırmızı renklerin \"kırmızılığını\" değiştirin.",
    "shadows": "Fotoğrafı yıkama olmadan gölgelerin parlaklığını değiştirin",
    "sharpen": "Resimleri daha keskin yapmak için kenar kontrastını arttırın.",
    "temperature": "Fotoğraf sıcak veya soğuk yapmak",
    "tint": "Rengi magenta-yeşil spektrumu boyunca uyarlayın.",
    "toning_highlights": "vurgulamaktadır rengini değiştirme",
    "toning_shadows": "gölgelerin rengini değiştirme",
    "vibrance": "cilt tonları dışarı yıkama olmadan doygunluğunu değiştirmek",
    "vignette_amount": "Fade veya fotoğrafın kenarlarını vurgular",
    "vignette_feather": "Vignette'in bulanıklık değiştirme",
    "vignette_highlights": "olayları skeç ile göstermesine izin verin",
    "vignette_roundness": "skeç daha yuvarlak veya kare yapmak",
    "vignette_size": "Vignette'in boyutunu değiştirme",
    "white_balance": "Beyaz dengesi",
    "white_balance_1": "<b>Doğru beyaz dengesi</b> <br/> Çoğu ışık kaynakları (ampuller, el feneri, vb) tamamen beyaz renk yaymazlar. Bu fotoğraflarda beyazlar sarı veya mavi döküm olması neden olabilir. Bunu düzeltmek için, -30 <b>Sıcaklık</b> azaltmak",
    "white_balance_2": "<b>Gölgelerden ışık kurtarmak</b> <br/> Bazen bir fotoğrafın parçaları gözlerimiz kamera daha fazla ışık algıladıkları çünkü amaçlanan daha koyudur. Biz gölgeler aracını kullanarak gölgelerden ışık kurtarabilirsiniz. <br/> 100 <b>Gölgeler</b> artırmak",
    "white_balance_3": "<b>ayrıntıları geliştirin</b> <br/> Son olarak, fotoğrafın ayrıntıları geliştirmek için, biz netlik aracını kullanabilirsiniz. <br/> 100 <b>Clarity</b> artırmak",
    "white_balance_4": "İyi iş! Sarı döküm çıkarmak ve 3 basit adımda gölgelerden ışık kurtarmak için nasıl öğrendim.",
    "white_balance_description": "Bu eğitimde, beyaz dengesi ve nasıl kapı yapay ışıkta düzeltmek için kavramını öğreneceksiniz.",
    "whites": "Fotoğraftaki beyaz parçaların parlaklığını değiştirir"
  }
};
},{}],335:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "添加",
    "add_image": "選擇圖片",
    "add_to_favorites": "添加到收藏夾",
    "aspect": "比例",
    "back": "後退",
    "buy": "購買",
    "buy_full_version": "購買完整版",
    "cancel": "取消",
    "check_again": "再檢查一遍",
    "choose_folder": "選擇文件夾",
    "clear": "清除",
    "clear_history": "清除曆史記錄",
    "close": "關閉",
    "cloud": "雲",
    "continue_anyway": "仍然繼續",
    "current_image": "當前",
    "delete": "刪除",
    "device": "設備",
    "dismiss": "撤銷",
    "done": "完成",
    "dont_ask": "不要再問了",
    "duplicate": "重複",
    "enter_license": "輸入許可證密鑰",
    "enter_license_key": "輸入許可證密鑰",
    "exit": "退出",
    "export": "導出",
    "export_filter": "另存爲二維碼",
    "forgot_password": "忘了密碼？",
    "free_membership": "免費會員",
    "get": "獲取",
    "get_license": "獲得許可證密鑰",
    "get_started": "上手",
    "got_it": "已經獲取",
    "have_account": "已經有一個帳戶？",
    "import": "導入",
    "import_filter": "獲取濾鏡",
    "import_filter_photos": "從照片導入",
    "import_photos": "打開照片",
    "info": "信息",
    "installed": "已安裝",
    "invite": "發送鏈接",
    "learn_more": "了解更多",
    "load_last_photo": "在裝載推出最後一張照片",
    "new": "新建",
    "new_filter": "創建濾鏡",
    "next": "下一步",
    "no": "否",
    "no_thanks": "不，謝謝",
    "not_really": "不確定",
    "not_this_time": "這次不用",
    "open_sample": "打開樣本照片",
    "paste_adjustments": "粘貼調整",
    "pay_wechat": "打開微信",
    "prev": "上一步",
    "preview": "預覽",
    "pro": "高級版本",
    "purchased": "購買",
    "remove": "移除",
    "remove_all_photos": "刪除所有照片",
    "remove_photo": "刪除照片",
    "remove_selected_photo": "刪除選定的照片",
    "reset": "重置",
    "reset_all": "全部重置",
    "reset_password": "重設密碼",
    "reset_photos": "復位保存和編輯標籤",
    "reset_tips": "複位提示",
    "restore": "恢複",
    "revert": "恢複到原始",
    "sample": "樣張",
    "save": "保存",
    "save_qr": "保存QR碼",
    "save_share": "保存+分享",
    "scan_filter": "掃描濾鏡",
    "select_all": "全選",
    "select_changed": "選擇編輯過的",
    "send": "發送",
    "send_verification": "重新發送驗證郵件",
    "show_in_folder": "展現在文件夾中",
    "show_me": "給我看",
    "signin": "登錄",
    "signout": "登出",
    "signup": "創建Polarr賬戶",
    "skip": "跳過",
    "submit_license_key": "提交許可證密鑰",
    "subscribe": "訂閱",
    "subscribe_monthly": "訂閱每月",
    "subscribe_yearly": "每年訂閱",
    "sure": "好的",
    "try": "嘗試",
    "try_again": "再試一次",
    "unlock": "解鎖",
    "unsubscribe": "退訂",
    "update": "更新",
    "whats_new": "更新历史",
    "yes": "是!"
  },
  "dialogs": {
    "about": "關于",
    "about_text": "我們認爲，讓創造者毫不費力地表達自己的觀點和對生活的態度，又不失自己的創作欲望和靈感的保真度是很重要的",
    "account": "帳戶",
    "all_filters": "所有的濾鏡",
    "all_filters_text": "解鎖Polarr的優質濾鏡的全套集合",
    "backup_filters": "同步和備份所有自定義過濾器，這樣就可以跨設備和平台使用它們。此功能需要您登錄使用Polarr帳戶。",
    "backup_filters_title": "備份自定義過濾器",
    "brush_limit": "當前畫筆大小是</limit>",
    "denoise": "即時修改和查看頂級的降噪效果。您可以分別控制顔色和亮度噪點去除。",
    "featured": "精選",
    "filter_collections": "濾鏡集合",
    "filter_name": "命名此濾鏡",
    "get_picky": "厭倦了照片編輯的？嘗試Polarr新的應用程序，編輯與人工智能的照片！",
    "get_updates": "訂閱更新",
    "get_updates_about": "想第一個知道新功能？ <br/>添加您的電子郵件地址，我們會通知您有關Polarr的新聞和新産品特性。",
    "get_updates_thankyou": "現在，您將獲得有關新功能和産品更新！ <br/>如果您希望停止接收更新，您可以從您隨時通過電子郵件將“退訂”。",
    "help": "幫助",
    "invite": "分享安裝鏈接，三人用你的鏈接安裝後，Pro的功能將被解鎖",
    "local_adjustments": "局部調整可讓您用各種方式對畫面進行部分調整。包括徑向，漸變，顔色蒙版和畫筆四類工具。",
    "lock_rotation": "旋轉鎖",
    "max_export_size": "最大輸出尺寸",
    "new_filter": "保存編輯作爲濾鏡",
    "no_purchases": "您沒有購買過高級功能。",
    "offline": "您的設備似乎沒有聯網，請稍後再試",
    "photos": "相片",
    "polarr_premium": "Polarr會員",
    "pro_all_filters": "Pro的功能<br/> + <br/>所有的濾鏡集合",
    "pro_all_filters_text": "包括解鎖的Pro Tools和所有的Polarr的高級過濾器的所有功能。使用您的Polarr帳戶來訪問其它平台上的所有功能。備份和同步您的自定義過濾器。",
    "pro_features": "高級工具",
    "pro_features_all": "所有高級工具",
    "purchase": "解鎖徑向漸變，顏色，畫筆，降噪和文本的工具。備份和同步您的自定義過濾器。",
    "purchase_failed": "購買失敗",
    "purchase_header": "解鎖所有功能",
    "purchase_successful": "現在你已經解鎖了功能",
    "purchase_unavailable": "對不起，暫時無法購買",
    "purchases": "購買",
    "rate_review": "爲應用評分",
    "rename_filter": "重命名濾鏡",
    "restore_failed": "您的帳戶不具有使用全部功能的權限。",
    "restore_successful": "現在你已經還原了購買",
    "review": "你喜歡潑辣修圖嗎?",
    "review_no": "你想要給潑辣修圖一些建議嗎?",
    "review_yes": "要不要爲潑辣修圖打分評價?",
    "save_adjustments": "讓您可以共享自定義濾鏡。",
    "send_feedback": "發送建議",
    "signin_polarr_account": "登錄到您的Polarr帳戶",
    "store": "商店",
    "subscribe": "注冊<strong>Polarr全部功能解鎖賬號</strong>",
    "subscribe_access": "Polarr全部功能解鎖賬號包括：",
    "subscribe_cancel": "賬號解鎖功能屬于終身制",
    "subscribe_caveat": "全部功能包括漸變濾鏡調整，徑向調整，顔色蒙板，筆刷，文字編輯，降噪等功能。您的賬號可用于所有設備和平台。",
    "subscribe_china": "訂閱 polaxiongkeji (潑辣熊科技) 微信賬號，並在微信支付，然後用Polarr帳號登錄激活Pro的功能。",
    "subscribe_error": "哎呀！購買出錯。再試一次？",
    "subscribe_features": "<li> 局部調整功能（4局部調整工具）</li><li>使用所有12套高級濾鏡</li><li>文字編輯功能</li><li>高級降噪工具</li><li>高級功能可在全部平台上解鎖</li>",
    "subscribe_notify": "Polarr的其他可登錄平台包括Web，iOS，Android，macOS，Windows 10",
    "subscribe_price": "</price>",
    "subscribe_refund": "您以前購買過Polarr，您可以點此免費獲得解鎖",
    "subscribe_success": "感謝您購買Polarr！",
    "subscribe_unlock_success": "解鎖成功！",
    "subscription_expired": "您Polarr高級會員資格已過期",
    "text_tool": "文本工具允許您添加文本和形狀。調整行間距，字間距，對齊方式等，你可以完全控制你的文字大小，換行。我們甚至還包括先進的反轉，矢量縮放和獨特的混合調整。",
    "text_tool_description": "添加和編輯屏幕上的文字。旋轉，改變顔色，設置混合模式，調整字母間距。",
    "thankyou": "謝謝！",
    "unlock_all": "解鎖全部功能",
    "unlock_all_note": "登錄您的Polarr帳戶或輸入許可證密鑰",
    "unlock_alternate": "您也可以通過登錄到現有帳戶或通過輸入許可證密鑰升級",
    "unlock_failed": "解鎖出錯",
    "unlock_success": "您現在可以使用所有功能",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a>和<a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> ，爲自由和高分辨率的商業攝影的最佳來源，聯手爲您帶來由Unsplash攝影師創造的10個濾鏡的集合。 </p>",
    "upgrade": "升級到完整版本解鎖所有以下功能。",
    "upgrade_caveat": "* 12額外的濾鏡包包括經典電影，懷舊電影，紅外膠片，膚色和適合各種主題和場景，許多其他的場景模擬濾鏡。",
    "upgrade_features": "<li>漸變調整</li><li>圓形調整</li><li>色彩範圍調整</li><li>筆刷調整</li><li>12個高質量濾鏡組合</li><li>文字編輯功能</li><li>高級降噪功能</li>",
    "use_polarrfilter": "分享到微博使用<strong>#潑辣濾鏡#</strong>得到注意",
    "user_in_paid": "您可以用Polarr賬號在其他平台上登陸解鎖所有功能。",
    "user_in_unpaid": "如果你解鎖全部功能，你可以登錄賬號在其他Polarr平台上，並解鎖其他的平台的全部功能。",
    "verification_sent": "驗證郵件已發送。 <br/>請檢查您的電子郵件驗證說明。",
    "verify_account": "要登錄，請驗證您的帳戶。 <br/>請檢查您的電子郵件驗證說明。",
    "webgl_not_supported": "我們非常抱歉，您的顯卡不支持潑辣修圖需要的一些功能，請升級到最新操作系統之後再試"
  },
  "labels": {
    "adjustments": "調整",
    "after": "之後",
    "alignment": "對准",
    "all": "所有",
    "amount": "程度",
    "angle": "角度",
    "aqua": "青",
    "artwork": "藝術品",
    "auto": "自動增強",
    "balance": "平衡",
    "basic": "基本",
    "batch_rename": "批量重命名",
    "batch_resize": "批量調整大小",
    "before": "之前",
    "best": "最佳",
    "blacks": "黑色色階",
    "blend": "混合",
    "blend_mode": "混合模式",
    "blue": "藍",
    "blur": "模糊",
    "brightness": "亮度",
    "brush_flow": "筆刷大小",
    "brush_opacity": "筆刷透明度",
    "brush_size": "刷子大小",
    "clarity": "清晰度",
    "collection": "集合",
    "color": "色彩",
    "contrast": "對比度",
    "crop": "裁剪",
    "crop_size": "裁剪尺寸",
    "curves": "曲線",
    "custom": "自定義",
    "dehaze": "去霧",
    "denoise": "降噪",
    "detail": "質感",
    "diffuse": "眩光",
    "distortion": "畸變",
    "distortion_amount": "畸變",
    "distortion_horizontal": "水平透視",
    "distortion_vertical": "垂直透視",
    "dividers": "線條",
    "edit": "編輯",
    "effects": "特效",
    "email": "電子郵件",
    "enable": "開啓",
    "estimated_size": "估計大小",
    "exposure": "曝光",
    "eyes": "眼睛",
    "face": "面部",
    "favorites": "收藏夾",
    "feather": "羽化",
    "fit": "適合",
    "font": "字形",
    "format": "格式",
    "free": "免費",
    "frequent": "常用",
    "fringing": "色差",
    "grain": "噪點",
    "green": "綠",
    "guides": "指南",
    "high": "高",
    "highlights": "高光",
    "histogram": "直方圖",
    "history": "曆史記錄",
    "hsl": "HSL",
    "hue": "色相",
    "hue_blue": "HSL - 藍色調",
    "hue_red": "HSL - 紅色色相",
    "invert": "反轉",
    "lens": "鏡頭",
    "lens_distortion": "鏡頭扭曲",
    "letter_spacing": "字母間距",
    "license_key": "注冊碼",
    "light": "光效",
    "line_spacing": "行間距",
    "lips": "嘴唇",
    "look": "濾鏡",
    "looks": "濾鏡",
    "low": "低",
    "luminance": "明度",
    "luminance_blue": "HSL - 藍色明度",
    "luminance_red": "HSL - 紅色明度",
    "magenta": "洋紅",
    "master": "主通道",
    "max_size": "最大尺寸",
    "med": "中",
    "mix": "調整所有",
    "mosaic": "像素化",
    "name": "名稱",
    "none": "默認",
    "nose": "鼻子",
    "opacity": "不透明度",
    "optics": "光學",
    "options": "選項",
    "orange": "橙",
    "original": "原始",
    "outline": "描邊",
    "password": "密碼",
    "position": "位置",
    "prefix": "前綴",
    "presets": "預設",
    "preview": "預覽",
    "purple": "紫",
    "quality": "質量",
    "ratio": "比例",
    "red": "紅",
    "reflect": "倒影",
    "resolution": "解析度",
    "rgb": "RGB",
    "roundness": "圓度",
    "saturation": "飽和度",
    "saturation_blue": "HSL -藍色飽和度",
    "saturation_red": "HSL - 紅色飽和度",
    "sayings": "熟語",
    "shadow": "陰影",
    "shadows": "陰影",
    "shape": "形狀",
    "shapes": "形狀",
    "shapes_presets": "形狀預設",
    "sharpen": "銳化",
    "size": "大小",
    "skin": "皮膚",
    "smoothness": "平滑",
    "split_tone": "色調",
    "teeth": "牙",
    "temperature": "色溫",
    "text": "文本",
    "texture": "質地",
    "threshold": "初值",
    "tint": "色調",
    "tone": "光澤",
    "toning": "色調",
    "use_prefix": "使用前綴",
    "use_watermark": "使用水印",
    "user_name": "用戶名",
    "vibrance": "自然飽和度",
    "vignette": "暗角",
    "vignette_amount": "暗角",
    "watermark_image": "水印圖像",
    "wb": "白平衡",
    "whitening": "美白",
    "whites": "白色色階",
    "width": "寬度",
    "yellow": "黃"
  },
  "menus": {
    "batch_export": "批量導出",
    "change_language": "改變語言",
    "copy_edit": "編輯複制",
    "edit_watermark": "編輯水印",
    "export": "格式，調整大小，水印",
    "filters_users": "浏覽別人制作的濾鏡",
    "guide": "指南",
    "image_quality": "畫面質量",
    "image_settings": "導出設置",
    "language": "語言",
    "more_filters": "更多濾鏡",
    "open": "打開",
    "open_in": "打開",
    "paste_edit": "粘貼修改",
    "remove": "移除",
    "rename": "重命名",
    "restore_purchases": "恢複購買",
    "save_cameraroll": "保存到相機膠卷",
    "save_copy": "保存副本",
    "settings": "設置",
    "share": "分享",
    "share_edits": "分享編輯",
    "share_filter": "分享濾鏡",
    "show_instructions": "顯示調整指南",
    "show_intro": "展示預覽",
    "show_video_tutorial": "訪問潑辣論壇",
    "single_export": "導出"
  },
  "onboarding": {
    "become_master": "成爲一個照片編輯高手。",
    "step_0": "歡迎Polarr！ <br/>讓我們先看看Polarr的濾鏡。",
    "step_1": "點擊選擇一個濾鏡。 <br/>滑動面板查看更多的濾鏡。",
    "step_10": "您可以拖動重新安排所有的工具圖標。",
    "step_2": "點擊<i class='icon-check'></i>應用濾鏡。<br/>使用滑塊來調整濾鏡的強度。",
    "step_3": "您可以進一步編輯每一個濾鏡。 <br/>去調整面板繼續編輯。",
    "step_4": "您可以通過拖動滑塊來調整。",
    "step_4a": "長按然後拖動調整滑塊。",
    "step_5": "查看所有已在曆史面板中所做的編輯。",
    "step_6": "您可以撤消編輯，並返回到曆史上的任何一點。",
    "step_7": "在這裏保存圖像。 <br/>您可以調整大小，添加水印或保存多個圖像。",
    "step_7a": "這裏保存圖像。 <br/>你也可以分享和保存多張圖片。",
    "step_8": "這裏開設自己的照片。",
    "step_9": "查看你的照片打開照片中的面板。"
  },
  "status": {
    "account_suspended": "您的帳戶被擱置，由於可疑活動。欲了解更多信息，請檢查你的電子郵件或聯繫人<a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "點擊<i class='icon-add-face'></i>手動添加人臉",
    "adjustment_copied": "複制調整",
    "adjustment_pasted": "粘貼調整",
    "demo_mode": "該版本的潑辣修圖爲展示專門創建",
    "downloading_font": "字體下載",
    "drag_icons": "拖動圖標重新排列工具",
    "drop_files": "將文件拖放到打開",
    "email_invalid": "對不起，它看起來像該電子郵件地址無效。",
    "email_reset": "重置鏈接已發送。 <br/>請查看你的郵箱。",
    "exporting_filter": "保存濾鏡",
    "field_invalid": "是無效的。",
    "file_exists": "這照片是已經打開",
    "file_not_supported": "對不起，該文件類型不支持",
    "filter_exported": "濾鏡已保存到相機膠卷",
    "filter_saved": "濾鏡保存",
    "filters_sync_error": "錯誤同步器",
    "filters_synced": "過濾器同步",
    "filters_syncing": "同步器...",
    "import_filter_error": "對不起，該濾鏡無法導入",
    "import_limit": "Polarr只能打開多達50張照片，請打開更多之前刪除一些照片。",
    "importing_filter": "導入濾鏡",
    "importing_photo": "打開照片",
    "importing_photos": "打開照片",
    "invalid_credentials": "錯誤的電子郵件或密碼",
    "loading_photo": "載入照片中",
    "login_success": "您已成功登錄！",
    "no_faces": "未偵測到臉部。",
    "no_user": "該用戶不存在。",
    "offline": "無法連接到服務器",
    "photo_load_error": "無法加載照片",
    "photo_saved": "照片已保存.",
    "photo_shared": "照片共享",
    "photos_saved": "照片保存",
    "purchase_required": "需要購買",
    "raw_failed": "無法讀取RAW圖像。支持RAW仍處于試驗階段。",
    "registration_success": "您已成功作出了新的帳戶！ <br/>要登錄其他設備上，您必須先驗證您的帳戶。我們已向您發送一封電子郵件，驗證說明。",
    "saving_photo": "儲存相片",
    "saving_photo_error": "無法保存照片",
    "saving_photo_error_sd": "Polarr沒有權限來訪問你的SD卡或內部存儲，請檢查最新的Andr​​​​​​oid系統更新來解決該問題",
    "saving_photos": "保存照片",
    "server_offline": "對不起，服務器沒有響應。 <br/>你連接到互聯網？",
    "sharing_photo": "分享照片",
    "sharing_photo_error": "無法分享照片",
    "show_me_how": "告訴我怎麽用</thing>",
    "supported_files": "Polarr支持JPG，PNG，BMP和某些RAW文件",
    "unlocks_remaining": "你有</unlocks>解鎖剩余",
    "user_exists": "已經有與該電子郵件或用戶名的帳戶。",
    "wrong_password": "密碼錯誤"
  },
  "tooltips": {
    "add_face": "添加人臉",
    "add_text": "添加文字",
    "adjustments": "調整",
    "apply_adjustments": "應用調整",
    "auto_adjust": "自動調整",
    "brush_filter": "筆刷",
    "color_filter": "彩色範圍",
    "crop": "裁剪",
    "crop_straighten": "裁剪和角度",
    "current_adjustments": "目前的調整",
    "double_tap_edit": "雙擊編輯",
    "drag_watermark": "拖動來移動水印",
    "eraser": "橡皮",
    "face": "面部工具",
    "filmstrip": "打開圖片",
    "flip": "翻轉",
    "flip_h": "水平翻轉",
    "flip_v": "垂直翻轉",
    "global_adjustments": "主要調整",
    "gradient_filter": "漸變濾鏡",
    "guides": "指南",
    "history": "編輯曆史",
    "info": "信息",
    "local_adjustments": "選擇性調整",
    "looks": "濾鏡",
    "masks": "蒙板",
    "move_face": "移動人臉",
    "open": "打開圖片",
    "pan": "滑動",
    "radial_filter": "徑向濾鏡",
    "redo": "重做",
    "remove_face": "面對移除",
    "remove_text": "刪除文本",
    "rotate": "旋轉",
    "rotate_ccw": "逆時針旋轉",
    "rotate_cw": "順時針旋轉",
    "save": "保存照片",
    "settings": "設置",
    "show_original": "顯示原始",
    "text": "文字工具",
    "undo": "撤銷"
  },
  "tutorial": {
    "blacks": "調整畫面中較暗部分的亮度，其影響範圍比陰影更大",
    "brightness": "調整畫面的灰度，我們可以利用它顯現畫面細節",
    "brush_mask_0": "用你的手指畫出一個區域。",
    "brush_mask_1": "調整的參數只會影響你畫出的區域。",
    "clarify": "通透度",
    "clarify_1": "<b>減少霧</b> <br/>霧是在照片中一個常見的​​問題，在Polarr裏去霧非常簡單。 <br/>要除去霧氣，增加<b>去霧</b>至85",
    "clarify_2": "<b>恢複曝光</b> <br/> 有時，去霧調整會降低圖片的曝光。 <br/>我們需要恢複一些曝光，增加<b>曝光</b>至30",
    "clarify_3": "<b>增加對比度</b> <br/>霧霾很大的時候，我們還可以通過對比度進一步加深畫面對比。增加<b>對比度</b>至60",
    "clarify_4": "<b>加強細節</b> <br/>爲了增強照片中的細節，我們可以使用清晰度工具。增加<b>清晰度</b>至40",
    "clarify_5": "幹得漂亮！你學會了簡單4步修複朦胧的照片。",
    "clarify_description": "在本教程中，您將學習如何減少霧氣，增強畫面輪廓。",
    "clarity": "讓照片更加清晰",
    "color_mask_0": "彩色範圍只會影響你選中的顔色。",
    "color_mask_1": "拖動來改變所選擇的顔色。",
    "color_mask_2": "<b>阈</b>設置選擇的靈敏度。",
    "contrast": "可以讓你的圖片更加的醒目、鮮豔",
    "curves": "通過曲線工具我們可以調整畫面亮度、對比度等",
    "deep_space": "深空",
    "deep_space_1": "<b>增亮天空</b> <br/> Polarr的曝光調整算法能夠高質量的還原和模擬真實曝光。相比其他軟件，Polarr的曝光還能動態夠最小化失真度。 <br/>我們首先將<b>曝光</b>增加至100",
    "deep_space_2": "<b>加強細節</b> <br/>爲了讓我們的星星看起來更明顯和清晰。 <br/>我們可以通過增加清晰度會揭示更多的細節。 <br/>爲了增強細節，增加<b>清晰度</b>爲100",
    "deep_space_3": "<b>增加暗角</b> <br/>我們還可以通過壓暗圖片邊緣來讓畫面的中央更明顯飽滿。 <br/>我們可以通過暗角工具，減少<b>暗角</b> 到-100",
    "deep_space_4": "<b>更平滑的暗角</b> <br/>現在暗角看起來有一些突兀，我們可以改變暗角的平滑度。 <br/>爲了使暗角看起來更平滑，我們可以將<b>羽化</b>增加至100",
    "deep_space_5": "<b>恢複高光</b> <br/>這時候因爲暗角，畫面周邊的星星也暗了下來，我們可以通過還原暗角高光來增強星星的亮度。<br/>將<b>暗角高光</b> 增加至100",
    "deep_space_6": "<b>改變天空顔色</b> <br/> HSL工具允許你有針對性地改變某種畫面顔色。要改變天空的顔色，增加<b>青色色相</b> 到100",
    "deep_space_7": "太棒了！你學會了簡單5步修複夜晚天空細節。",
    "deep_space_description": "在本教程中，您將學習修複暗角，調整夜空的顔色和曝光。",
    "dehaze": "爲照片去除或添加陰霾",
    "denoise": "降低照片噪點",
    "diffuse": "給照片增加光暈特效",
    "distortion": "失真",
    "distortion_1": "<b>糾正垂直失真</b> <br/>我們經常看到的建築物和牆壁的照片裏，房子不是垂直于地面的。這通常是由廣角相機鏡頭的設計而引起的。我們可以很容易的使用垂直透視工具解決這個​​問題。 <br/>要解決失真，增加<b>垂直透視</b>至70",
    "distortion_2": "<b>糾正暗角</b> <br/>大多數相機鏡頭在邊緣的鏡面上會有失光現象。我們可以使用暗角工具加亮鏡頭的邊緣。 <br/>修複暗角，增加<b>暗角</b>至20",
    "distortion_3": "<b>加強細節</b> <br/>最後，爲了提高在照片中的細節，我們可以使用清晰度工具。 <br/>增加<b>清晰度</b>至100",
    "distortion_4": "真不錯！你已經學會了如何通過3個簡單步驟修正常見的鏡頭缺陷。",
    "distortion_amount": "矯正鏡頭失真，也可以用來制造廣角或魚眼效果",
    "distortion_description": "在本教程中，您將學習如何針對建築物攝影糾正鏡頭畸變和暗角。",
    "distortion_horizontal": "矯正畫面的水平失真",
    "distortion_vertical": "矯正畫面的垂直失真",
    "exposure": "調整畫面的明亮程度",
    "face_0": "Polarr自動偵測臉部，然後按五官分區調整。",
    "face_1": "使用扭曲工具來調整大小和五官的位置。",
    "fringing": "爲照片增加眩暈的特效",
    "gradient_mask_0": "漸變濾鏡可以做出線性的漸變效果。",
    "gradient_mask_1": "調整區域。 <br/>通過拖動實現移動和旋轉。",
    "gradient_mask_2": "<b>反轉</b>切換漸變方向。",
    "gradient_mask_3": "<b>倒影</b>做出由中間向兩邊漸變的效果。",
    "grain": "爲照片增加膠片顆粒",
    "highlights": "調整畫面中最亮部分的亮度",
    "hsl": "改變特定顔色的色相，飽和度，或明度。",
    "hsl_header": "HSL",
    "hue_blue": "把藍色轉換爲相近的其他顔色",
    "hue_red": "把紅色轉換爲相近的其他顔色",
    "luminance_blue": "改變藍色的明度（明亮程度）",
    "luminance_red": "改變紅色的明度（明亮程度）",
    "mastering_adjustments": "掌握調整",
    "mosaic": "給照片增加馬賽克效果",
    "radial_mask_0": "徑向濾鏡影響的是一個圓形區域。",
    "radial_mask_1": "調整區域。 <br/>通過拖動實現移動和旋轉。",
    "radial_mask_2": "<b>羽化</b>控制邊緣的柔和程度。",
    "radial_mask_3": "<b>反轉</b>切換影響區域。",
    "saturation": "可以讓你的照片更加鮮豔",
    "saturation_blue": "改變藍色的飽和度（鮮豔程度）",
    "saturation_red": "改變紅色的飽和度（鮮豔程度）",
    "shadows": "調整畫面中最暗部分的亮度",
    "sharpen": "可以讓你的照片邊緣更加明顯,與清晰度相比,其效果更細膩",
    "temperature": "使照片偏暖（黃色）或偏冷（藍色）",
    "tint": "讓畫面偏綠色或偏紫色",
    "toning_highlights": "爲畫面的高光區域加入指定色彩",
    "toning_shadows": "爲畫面的陰影區域加入指定色彩",
    "vibrance": "相當于智能飽和度，它不會讓畫面過于飽和",
    "vignette_amount": "控制暗角的強烈程度",
    "vignette_feather": "調整暗角的羽化（柔和）程度",
    "vignette_highlights": "可以讓高光區域（例如天空）不被暗角覆蓋",
    "vignette_roundness": "控制暗角的圓形程度",
    "vignette_size": "控制暗角的影響範圍",
    "white_balance": "白平衡",
    "white_balance_1": "<b>正確的白平衡</b> <br/>大多數光源（燈泡，手電筒等）不發射純粹白色的顔色。這可能會導致在照片白色有黃色或藍色。爲了解決這個問題， <b>降溫</b>至-30",
    "white_balance_2": "<b>恢複從陰影光</b> <br/>有時照片的部分是暗比預期的，因爲我們的眼睛能比相機感知更多的光。我們可以恢複使用陰影工具陰影的光。 <br/>增加<b>陰影</b> 100",
    "white_balance_3": "<b>加強細節</b> <br/>最後，爲了提高在照片中的細節，我們可以使用清晰度工具。 <br/>增加<b>清晰度</b>至100",
    "white_balance_4": "非常好！你已經學會了如何通過3個簡單步驟修複白平衡和畫面中的陰影。",
    "white_balance_description": "在本教程中，您將學習白平衡以及如何調整工光源來色溫。",
    "whites": "調整畫面中較亮部分的亮度，其影響範圍比高光更大"
  }
};
},{}],336:[function(require,module,exports){
module.exports = {
  "buttons": {
    "add": "添加",
    "add_image": "选择图片",
    "add_to_favorites": "添加到收藏夹",
    "aspect": "比例",
    "back": "后退",
    "buy": "购买",
    "buy_full_version": "购买完整版",
    "cancel": "取消",
    "check_again": "再检查一遍",
    "choose_folder": "选择文件夹",
    "clear": "清除",
    "clear_history": "清除历史记录",
    "close": "关闭",
    "cloud": "云",
    "continue_anyway": "仍然继续",
    "current_image": "当前",
    "delete": "删除",
    "device": "设备",
    "dismiss": "撤销",
    "done": "完成",
    "dont_ask": "不要再问了",
    "duplicate": "重复",
    "enter_license": "输入许可证密钥",
    "enter_license_key": "输入许可证密钥",
    "exit": "退出",
    "export": "导出",
    "export_filter": "另存为二维码",
    "forgot_password": "忘记密码？",
    "free_membership": "免费会员",
    "get": "获取",
    "get_license": "获得许可证密钥",
    "get_started": "上手",
    "got_it": "已经获取",
    "have_account": "已经有一个帐户？",
    "import": "导入",
    "import_filter": "获取滤镜",
    "import_filter_photos": "从照片导入",
    "import_photos": "打开照片",
    "info": "信息",
    "installed": "已安装",
    "invite": "发送链接",
    "learn_more": "了解更多",
    "load_last_photo": "在装载推出最后一张照片",
    "new": "新建",
    "new_filter": "创建滤镜",
    "next": "下一步",
    "no": "否",
    "no_thanks": "不，谢谢",
    "not_really": "不确定",
    "not_this_time": "这次不用",
    "open_sample": "打开样本照片",
    "paste_adjustments": "粘贴调整",
    "pay_wechat": "打开微信",
    "prev": "上一步",
    "preview": "预览",
    "pro": "高级版本",
    "purchased": "购买",
    "remove": "移除",
    "remove_all_photos": "删除所有照片",
    "remove_photo": "删除照片",
    "remove_selected_photo": "删除选定的照片",
    "reset": "重置",
    "reset_all": "全部重置",
    "reset_password": "重设密码",
    "reset_photos": "复位保存和编辑标签",
    "reset_tips": "复位提示",
    "restore": "恢复",
    "revert": "恢复到原始",
    "sample": "样张",
    "save": "保存",
    "save_qr": "保存QR码",
    "save_share": "保存+分享",
    "scan_filter": "扫描滤镜",
    "select_all": "全选",
    "select_changed": "选择编辑过的",
    "send": "发送",
    "send_verification": "重新发送验证邮件",
    "show_in_folder": "展现在文件夹中",
    "show_me": "给我看",
    "signin": "登录",
    "signout": "登出",
    "signup": "创建Polarr账户",
    "skip": "跳过",
    "submit_license_key": "提交许可证密钥",
    "subscribe": "订阅",
    "subscribe_monthly": "订阅每月",
    "subscribe_yearly": "每年订阅",
    "sure": "好的",
    "try": "尝试",
    "try_again": "再试一次",
    "unlock": "解锁",
    "unsubscribe": "退订",
    "update": "更新",
    "whats_new": "更新历史",
    "yes": "是!"
  },
  "dialogs": {
    "about": "关于",
    "about_text": "我们认为，让创造者毫不费力地表达自己的观点和对生活的态度，又不失自己的创作欲望和灵感的保真度是很重要的",
    "account": "帐户",
    "all_filters": "所有的滤镜",
    "all_filters_text": "解锁Polarr的优质滤镜的全套集合",
    "backup_filters": "同步和备份所有自定义过滤器，这样就可以跨设备和平台使用它们。此功能需要您登录使用Polarr帐户。",
    "backup_filters_title": "备份自定义过滤器",
    "brush_limit": "当前画笔大小是</limit>",
    "denoise": "即时修改和查看顶级的降噪效果。您可以分别控制颜色和亮度噪点去除。",
    "featured": "精选",
    "filter_collections": "滤镜集合",
    "filter_name": "命名此滤镜",
    "get_picky": "厌倦了照片编辑的？尝试Polarr新的应用程序，编辑与人工智能的照片！",
    "get_updates": "订阅更新",
    "get_updates_about": "想第一个知道新功能？ <br/>添加您的电子邮件地址，我们会通知您有关Polarr的新闻和新产品特性。",
    "get_updates_thankyou": "现在，您将获得有关新功能和产品更新！ <br/>如果您希望停止接收更新，您可以从您随时通过电子邮件将“退订”。",
    "help": "帮助",
    "invite": "分享安装链接，三人用你的链接安装后，Pro的功能将被解锁",
    "local_adjustments": "局部调整可让您用各种方式对画面进行部分调整。包括径向，渐变，颜色蒙版和画笔四类工具。",
    "lock_rotation": "旋转锁",
    "max_export_size": "最大输出尺寸",
    "new_filter": "保存编辑作为滤镜",
    "no_purchases": "您没有购买过高级功能。",
    "offline": "您的设备似乎没有联网，请稍后再试",
    "photos": "相片",
    "polarr_premium": "Polarr会员",
    "pro_all_filters": "Pro的功能<br/> + <br/>所有的滤镜集合",
    "pro_all_filters_text": "包括解锁的Pro Tools和所有的Polarr的高级过滤器的所有功能。使用您的Polarr帐户来访问其它平台上的所有功能。备份和同步您的自定义过滤器。",
    "pro_features": "高级工具",
    "pro_features_all": "所有高级工具",
    "purchase": "解锁径向渐变，颜色，画笔，降噪和文本的工具。备份和同步您的自定义过滤器。",
    "purchase_failed": "购买失败",
    "purchase_header": "解锁所有功能",
    "purchase_successful": "现在你已经解锁了功能",
    "purchase_unavailable": "对不起，暂时无法购买",
    "purchases": "购买",
    "rate_review": "为应用评分",
    "rename_filter": "重命名滤镜",
    "restore_failed": "您的帐户不具有使用全部功能的权限。",
    "restore_successful": "现在你已经还原了购买",
    "review": "你喜欢泼辣修图吗?",
    "review_no": "你想要给泼辣修图一些建议吗?",
    "review_yes": "要不要为泼辣修图打分评价?",
    "save_adjustments": "让您可以共享自定义滤镜。",
    "send_feedback": "发送建议",
    "signin_polarr_account": "登录到您的Polarr帐户",
    "store": "商店",
    "subscribe": "注册<strong>Polarr全部功能解锁账号</strong>",
    "subscribe_access": "Polarr全部功能解锁账号包括：",
    "subscribe_cancel": "账号解锁功能属于终身制",
    "subscribe_caveat": "全部功能包括渐变滤镜调整，径向调整，颜色蒙板，笔刷，文字编辑，降噪等功能。您的账号可用于所有设备和平台。",
    "subscribe_china": "订阅 polaxiongkeji (泼辣熊科技) 微信账号，并在微信支付，然后用Polarr帐号登录激活Pro的功能。",
    "subscribe_error": "哎呀！购买出错。再试一次？",
    "subscribe_features": "<li> 局部调整功能（4局部调整工具）</li><li>使用所有12套高级滤镜</li><li>文字编辑功能</li><li>高级降噪工具</li><li>高级功能可在全部平台上解锁</li>",
    "subscribe_notify": "Polarr的其他可登录平台包括Web，iOS，Android，macOS，Windows 10",
    "subscribe_price": "</price>",
    "subscribe_refund": "您以前购买过Polarr，您可以点此免费获得解锁",
    "subscribe_success": "感谢您购买Polarr！",
    "subscribe_unlock_success": "解锁成功！",
    "subscription_expired": "您Polarr高级会员资格已过期",
    "text_tool": "文本工具允许您添加文本和形状。调整行间距，字间距，对齐方式等，你可以完全控制你的文字大小，换行。我们甚至还包括先进的反转，矢量缩放和独特的混合调整。",
    "text_tool_description": "添加和编辑屏幕上的文字。旋转，改变颜色，设置混合模式，调整字母间距。",
    "thankyou": "谢谢！",
    "unlock_all": "解锁全部功能",
    "unlock_all_note": "登录您的Polarr帐户或输入许可证密钥",
    "unlock_alternate": "您也可以通过登录到现有帐户或通过输入许可证密钥升级",
    "unlock_failed": "解锁出错",
    "unlock_success": "您现在可以使用所有功能",
    "unsplash_info": "<p> <a href=\"https://www.polarr.co/\" target=\"_blank\">Polarr</a>和<a href=\"https://unsplash.com/\" target=\"_blank\">Unsplash</a> ，为自由和高分辨率的商业摄影的最佳来源，联手为您带来由Unsplash摄影师创造的10个滤镜的集合。 </p>",
    "upgrade": "升级到完整版本解锁所有以下功能。",
    "upgrade_caveat": "* 12额外的滤镜包包括经典电影，怀旧电影，红外胶片，肤色和适合各种主题和场景，许多其他的场景模拟滤镜。",
    "upgrade_features": "<li>渐变调整</li><li>圆形调整</li><li>色彩范围调整</li><li>笔刷调整</li><li>12个高质量滤镜组合</li><li>文字编辑功能</li><li>高级降噪功能</li>",
    "use_polarrfilter": "分享到微博使用<strong>#泼辣滤镜#</strong>得到注意",
    "user_in_paid": "您可以用Polarr账号在其他平台上登陆解锁所有功能。",
    "user_in_unpaid": "如果你解锁全部功能，你可以登录账号在其他Polarr平台上，并解锁其他的平台的全部功能。",
    "verification_sent": "验证邮件已发送。 <br/>请检查您的电子邮件验证说明。",
    "verify_account": "要登录，请验证您的帐户。 <br/>检查你的电子邮件验证说明。",
    "webgl_not_supported": "我们非常抱歉，您的显卡不支持泼辣修图需要的一些功能，请升级到最新操作系统之后再试"
  },
  "labels": {
    "adjustments": "调整",
    "after": "之后",
    "alignment": "对准",
    "all": "所有",
    "amount": "程度",
    "angle": "角度",
    "aqua": "青",
    "artwork": "艺术品",
    "auto": "自动增强",
    "balance": "平衡",
    "basic": "基本",
    "batch_rename": "批量重命名",
    "batch_resize": "批量调整大小",
    "before": "之前",
    "best": "最佳",
    "blacks": "黑色色阶",
    "blend": "混合",
    "blend_mode": "混合模式",
    "blue": "蓝",
    "blur": "模糊",
    "brightness": "亮度",
    "brush_flow": "笔刷大小",
    "brush_opacity": "笔刷透明度",
    "brush_size": "刷子大小",
    "clarity": "清晰度",
    "collection": "集合",
    "color": "色彩",
    "contrast": "对比度",
    "crop": "裁剪",
    "crop_size": "裁剪尺寸",
    "curves": "曲线",
    "custom": "自定义",
    "dehaze": "去雾",
    "denoise": "降噪",
    "detail": "质感",
    "diffuse": "眩光",
    "distortion": "畸变",
    "distortion_amount": "畸变",
    "distortion_horizontal": "水平透视",
    "distortion_vertical": "垂直透视",
    "dividers": "线条",
    "edit": "编辑",
    "effects": "特效",
    "email": "电子邮件",
    "enable": "开启",
    "estimated_size": "估计大小",
    "exposure": "曝光",
    "eyes": "眼睛",
    "face": "面部",
    "favorites": "收藏夹",
    "feather": "羽化",
    "fit": "适合",
    "font": "字形",
    "format": "格式",
    "free": "免费",
    "frequent": "常用",
    "fringing": "色差",
    "grain": "噪点",
    "green": "绿",
    "guides": "指南",
    "high": "高",
    "highlights": "高光",
    "histogram": "直方图",
    "history": "历史记录",
    "hsl": "HSL",
    "hue": "色相",
    "hue_blue": "HSL - 蓝色调",
    "hue_red": "HSL - 红色色相",
    "invert": "反转",
    "lens": "镜头",
    "lens_distortion": "镜头扭曲",
    "letter_spacing": "字母间距",
    "license_key": "注册码",
    "light": "光效",
    "line_spacing": "行间距",
    "lips": "嘴唇",
    "look": "滤镜",
    "looks": "滤镜",
    "low": "低",
    "luminance": "明度",
    "luminance_blue": "HSL - 蓝色明度",
    "luminance_red": "HSL - 红色明度",
    "magenta": "洋红",
    "master": "主通道",
    "max_size": "最大尺寸",
    "med": "中",
    "mix": "调整所有",
    "mosaic": "像素化",
    "name": "名称",
    "none": "默认",
    "nose": "鼻子",
    "opacity": "不透明度",
    "optics": "光学",
    "options": "选项",
    "orange": "橙",
    "original": "原始",
    "outline": "描边",
    "password": "密码",
    "position": "位置",
    "prefix": "前缀",
    "presets": "预设",
    "preview": "预览",
    "purple": "紫",
    "quality": "质量",
    "ratio": "比例",
    "red": "红",
    "reflect": "倒影",
    "resolution": "解析度",
    "rgb": "RGB",
    "roundness": "圆度",
    "saturation": "饱和度",
    "saturation_blue": "HSL -蓝色饱和度",
    "saturation_red": "HSL - 红色饱和度",
    "sayings": "熟语",
    "shadow": "阴影",
    "shadows": "阴影",
    "shape": "形状",
    "shapes": "形状",
    "shapes_presets": "形状预设",
    "sharpen": "锐化",
    "size": "大小",
    "skin": "皮肤",
    "smoothness": "平滑",
    "split_tone": "色调",
    "teeth": "牙",
    "temperature": "色温",
    "text": "文本",
    "texture": "质地",
    "threshold": "初值",
    "tint": "色调",
    "tone": "光泽",
    "toning": "色调",
    "use_prefix": "使用前缀",
    "use_watermark": "使用水印",
    "user_name": "用户名",
    "vibrance": "自然饱和度",
    "vignette": "暗角",
    "vignette_amount": "暗角",
    "watermark_image": "水印图像",
    "wb": "白平衡",
    "whitening": "美白",
    "whites": "白色色阶",
    "width": "宽度",
    "yellow": "黄"
  },
  "menus": {
    "batch_export": "批量导出",
    "change_language": "改变语言",
    "copy_edit": "编辑复制",
    "edit_watermark": "编辑水印",
    "export": "格式，调整大小，水印",
    "filters_users": "浏览别人制作的滤镜",
    "guide": "指南",
    "image_quality": "画面质量",
    "image_settings": "导出设置",
    "language": "语言",
    "more_filters": "更多滤镜",
    "open": "打开",
    "open_in": "打开",
    "paste_edit": "粘贴修改",
    "remove": "移除",
    "rename": "重命名",
    "restore_purchases": "恢复购买",
    "save_cameraroll": "保存到相机胶卷",
    "save_copy": "保存副本",
    "settings": "设置",
    "share": "分享",
    "share_edits": "分享编辑",
    "share_filter": "分享滤镜",
    "show_instructions": "显示调整指南",
    "show_intro": "展示预览",
    "show_video_tutorial": "访问泼辣论坛",
    "single_export": "导出"
  },
  "onboarding": {
    "become_master": "成为一个照片编辑高手。",
    "step_0": "欢迎Polarr！ <br/>让我们先看看Polarr的滤镜。",
    "step_1": "点击选择一个滤镜。 <br/>滑动面板查看更多的滤镜。",
    "step_10": "您可以拖动重新安排所有的工具图标。",
    "step_2": "点击<i class='icon-check'></i>应用滤镜。<br/>使用滑块来调整滤镜的强度。",
    "step_3": "您可以进一步编辑每一个滤镜。 <br/>去调整面板继续编辑。",
    "step_4": "您可以通过拖动滑块来调整。",
    "step_4a": "长按然后拖动调整滑块。",
    "step_5": "查看所有已在历史面板中所做的编辑。",
    "step_6": "您可以撤消编辑，并返回到历史上的任何一点。",
    "step_7": "在这里保存图像。 <br/>您可以调整大小，添加水印或保存多个图像。",
    "step_7a": "这里保存图像。 <br/>你也可以分享和保存多张图片。",
    "step_8": "这里开设自己的照片。",
    "step_9": "查看你的照片打开照片中的面板。"
  },
  "status": {
    "account_suspended": "您的帐户被搁置，由于可疑活动。欲了解更多信息，请检查你的电子邮件或联系人<a href=\"mailto:support@polarr.co\">support@polarr.co</a>",
    "add_face": "点击<i class='icon-add-face'></i>手动添加人脸",
    "adjustment_copied": "复制调整",
    "adjustment_pasted": "粘贴调整",
    "demo_mode": "该版本的泼辣修图为展示专门创建",
    "downloading_font": "字体下载",
    "drag_icons": "拖动图标重新排列工具",
    "drop_files": "将文件拖放到打开",
    "email_invalid": "对不起，它看起来像该电子邮件地址无效。",
    "email_reset": "重置链接已发送。 <br/>请查看你的邮箱。",
    "exporting_filter": "保存滤镜",
    "field_invalid": "是无效的。",
    "file_exists": "这张照片已经加载",
    "file_not_supported": "对不起，该文件类型不支持",
    "filter_exported": "滤镜已保存到相机胶卷",
    "filter_saved": "滤镜保存",
    "filters_sync_error": "错误同步器",
    "filters_synced": "过滤器同步",
    "filters_syncing": "同步器...",
    "import_filter_error": "对不起，该滤镜无法导入",
    "import_limit": "Polarr只能打开多达50张照片，请打开更多之前删除一些照片。",
    "importing_filter": "导入滤镜",
    "importing_photo": "打开照片",
    "importing_photos": "打开照片",
    "invalid_credentials": "错误的电子邮件或密码",
    "loading_photo": "载入照片中",
    "login_success": "您已成功登录！",
    "no_faces": "未侦测到脸部。",
    "no_user": "该用户不存在。",
    "offline": "无法连接到服务器",
    "photo_load_error": "无法加载照片",
    "photo_saved": "照片已保存.",
    "photo_shared": "照片共享",
    "photos_saved": "照片保存",
    "purchase_required": "需要购买",
    "raw_failed": "无法读取RAW图像。支持RAW仍处于试验阶段。",
    "registration_success": "您已成功作出了新的帐户！ <br/>要登录其他设备上，您必须先验证您的帐户。我们已向您发送一封电子邮件，验证说明。",
    "saving_photo": "储存相片",
    "saving_photo_error": "无法保存照片",
    "saving_photo_error_sd": "Polarr没有权限来访问你的SD卡或内部存储，请检查最新的Andr​​​​​​oid系统更新来解决该问题",
    "saving_photos": "保存照片",
    "server_offline": "对不起，服务器没有响应。 <br/>你连接到互联网？",
    "sharing_photo": "分享照片",
    "sharing_photo_error": "无法分享照片",
    "show_me_how": "告诉我怎么用</thing>",
    "supported_files": "Polarr支持JPG，PNG，BMP和某些RAW文件",
    "unlocks_remaining": "你有</unlocks>解锁剩余",
    "user_exists": "已经有与该电子邮件或用户名的帐户。",
    "wrong_password": "密码错误"
  },
  "tooltips": {
    "add_face": "添加人脸",
    "add_text": "添加文字",
    "adjustments": "调整",
    "apply_adjustments": "应用调整",
    "auto_adjust": "自动调整",
    "brush_filter": "笔刷",
    "color_filter": "彩色范围",
    "crop": "裁剪",
    "crop_straighten": "裁剪和角度",
    "current_adjustments": "目前的调整",
    "double_tap_edit": "双击编辑",
    "drag_watermark": "拖动来移动水印",
    "eraser": "橡皮",
    "face": "面部工具",
    "filmstrip": "打开图片",
    "flip": "翻转",
    "flip_h": "水平翻转",
    "flip_v": "垂直翻转",
    "global_adjustments": "主要调整",
    "gradient_filter": "渐变滤镜",
    "guides": "指南",
    "history": "编辑历史",
    "info": "信息",
    "local_adjustments": "选择性调整",
    "looks": "滤镜",
    "masks": "蒙板",
    "move_face": "移动人脸",
    "open": "打开图片",
    "pan": "滑动",
    "radial_filter": "径向滤镜",
    "redo": "重做",
    "remove_face": "面对移除",
    "remove_text": "删除文本",
    "rotate": "旋转",
    "rotate_ccw": "逆时针旋转",
    "rotate_cw": "顺时针旋转",
    "save": "保存照片",
    "settings": "设置",
    "show_original": "显示原始",
    "text": "文字工具",
    "undo": "撤销"
  },
  "tutorial": {
    "blacks": "调整画面中较暗部分的亮度，其影响范围比阴影更大",
    "brightness": "调整画面的灰度，我们可以利用它显现画面细节",
    "brush_mask_0": "用你的手指画出一个区域。",
    "brush_mask_1": "调整的参数只会影响你画出的区域。",
    "clarify": "通透度",
    "clarify_1": "<b>减少雾</b> <br/>雾是在照片中一个常见的​​问题，在Polarr里去雾非常简单。 <br/>要除去雾气，增加<b>去雾</b>至85",
    "clarify_2": "<b>恢复曝光</b> <br/> 有时，去雾调整会降低图片的曝光。 <br/>我们需要恢复一些曝光，增加<b>曝光</b>至30",
    "clarify_3": "<b>增加对比度</b> <br/>雾霾很大的时候，我们还可以通过对比度进一步加深画面对比。增加<b>对比度</b>至60",
    "clarify_4": "<b>加强细节</b> <br/>为了增强照片中的细节，我们可以使用清晰度工具。增加<b>清晰度</b>至40",
    "clarify_5": "干得漂亮！你学会了简单4步修复朦胧的照片。",
    "clarify_description": "在本教程中，您将学习如何减少雾气，增强画面轮廓。",
    "clarity": "让照片更加清晰",
    "color_mask_0": "彩色范围只会影响你选中的颜色。",
    "color_mask_1": "拖动来改变所选择的颜色。",
    "color_mask_2": "<b>阈</b>设置选择的灵敏度。",
    "contrast": "可以让你的图片更加的醒目、鲜艳",
    "curves": "通过曲线工具我们可以调整画面亮度、对比度等",
    "deep_space": "深空",
    "deep_space_1": "<b>增亮天空</b> <br/> Polarr的曝光调整算法能够高质量的还原和模拟真实曝光。相比其他软件，Polarr的曝光还能动态够最小化失真度。 <br/>我们首先将<b>曝光</b>增加至100",
    "deep_space_2": "<b>加强细节</b> <br/>为了让我们的星星看起来更明显和清晰。 <br/>我们可以通过增加清晰度会揭示更多的细节。 <br/>为了增强细节，增加<b>清晰度</b>为100",
    "deep_space_3": "<b>增加暗角</b> <br/>我们还可以通过压暗图片边缘来让画面的中央更明显饱满。 <br/>我们可以通过暗角工具，减少<b>暗角</b> 到-100",
    "deep_space_4": "<b>更平滑的暗角</b> <br/>现在暗角看起来有一些突兀，我们可以改变暗角的平滑度。 <br/>为了使暗角看起来更平滑，我们可以将<b>羽化</b>增加至100",
    "deep_space_5": "<b>恢复高光</b> <br/>这时候因为暗角，画面周边的星星也暗了下来，我们可以通过还原暗角高光来增强星星的亮度。<br/>将<b>暗角高光</b> 增加至100",
    "deep_space_6": "<b>改变天空颜色</b> <br/> HSL工具允许你有针对性地改变某种画面颜色。要改变天空的颜色，增加<b>青色色相</b> 到100",
    "deep_space_7": "太棒了！你学会了简单5步修复夜晚天空细节。",
    "deep_space_description": "在本教程中，您将学习修复暗角，调整夜空的颜色和曝光。",
    "dehaze": "为照片去除或添加阴霾",
    "denoise": "降低照片噪点",
    "diffuse": "给照片增加光晕特效",
    "distortion": "失真",
    "distortion_1": "<b>纠正垂直失真</b> <br/>我们经常看到的建筑物和墙壁的照片里，房子不是垂直于地面的。这通常是由广角相机镜头的设计而引起的。我们可以很容易的使用垂直透视工具解决这个​​问题。 <br/>要解决失真，增加<b>垂直透视</b>至70",
    "distortion_2": "<b>纠正暗角</b> <br/>大多数相机镜头在边缘的镜面上会有失光现象。我们可以使用暗角工具加亮镜头的边缘。 <br/>修复暗角，增加<b>暗角</b>至20",
    "distortion_3": "<b>加强细节</b> <br/>最后，为了提高在照片中的细节，我们可以使用清晰度工具。 <br/>增加<b>清晰度</b>至100",
    "distortion_4": "真不错！你已经学会了如何通过3个简单步骤修正常见的镜头缺陷。",
    "distortion_amount": "矫正镜头失真，也可以用来制造广角或鱼眼效果",
    "distortion_description": "在本教程中，您将学习如何针对建筑物摄影纠正镜头畸变和暗角。",
    "distortion_horizontal": "矫正画面的水平失真",
    "distortion_vertical": "矫正画面的垂直失真",
    "exposure": "调整画面的明亮程度",
    "face_0": "Polarr自动侦测脸部，然后按五官分区调整。",
    "face_1": "使用扭曲工具来调整大小和五官的位置。",
    "fringing": "为照片增加眩晕的特效",
    "gradient_mask_0": "渐变滤镜可以做出线性的渐变效果。",
    "gradient_mask_1": "调整区域。 <br/>通过拖动实现移动和旋转。",
    "gradient_mask_2": "<b>反转</b>切换渐变方向。",
    "gradient_mask_3": "<b>倒影</b>做出由中间向两边渐变的效果。",
    "grain": "为照片增加胶片颗粒",
    "highlights": "调整画面中最亮部分的亮度",
    "hsl": "改变特定颜色的色相，饱和度，或明度。",
    "hsl_header": "HSL",
    "hue_blue": "把蓝色转换为相近的其他颜色",
    "hue_red": "把红色转换为相近的其他颜色",
    "luminance_blue": "改变蓝色的明度（明亮程度）",
    "luminance_red": "改变红色的明度（明亮程度）",
    "mastering_adjustments": "掌握调整",
    "mosaic": "给照片增加马赛克效果",
    "radial_mask_0": "径向滤镜影响的是一个圆形区域。",
    "radial_mask_1": "调整区域。 <br/>通过拖动实现移动和旋转。",
    "radial_mask_2": "<b>羽化</b>控制边缘的柔和程度。",
    "radial_mask_3": "<b>反转</b>切换影响区域。",
    "saturation": "可以让你的照片更加鲜艳",
    "saturation_blue": "改变蓝色的饱和度（鲜艳程度）",
    "saturation_red": "改变红色的饱和度（鲜艳程度）",
    "shadows": "调整画面中最暗部分的亮度",
    "sharpen": "可以让你的照片边缘更加明显,与清晰度相比,其效果更细腻",
    "temperature": "使照片偏暖（黄色）或偏冷（蓝色）",
    "tint": "让画面偏绿色或偏紫色",
    "toning_highlights": "为画面的高光区域加入指定色彩",
    "toning_shadows": "为画面的阴影区域加入指定色彩",
    "vibrance": "相当于智能饱和度，它不会让画面过于饱和",
    "vignette_amount": "控制暗角的强烈程度",
    "vignette_feather": "调整暗角的羽化（柔和）程度",
    "vignette_highlights": "可以让高光区域（例如天空）不被暗角覆盖",
    "vignette_roundness": "控制暗角的圆形程度",
    "vignette_size": "控制暗角的影响范围",
    "white_balance": "白平衡",
    "white_balance_1": "<b>正确的白平衡</b> <br/>大多数光源（灯泡，手电筒等）不发射纯粹白色的颜色。这可能会导致在照片白色有黄色或蓝色。为了解决这个问题， <b>降温</b>至-30",
    "white_balance_2": "<b>恢复从阴影光</b> <br/>有时照片的部分是暗比预期的，因为我们的眼睛能比相机感知更多的光。我们可以恢复使用阴影工具阴影的光。 <br/>增加<b>阴影</b> 100",
    "white_balance_3": "<b>加强细节</b> <br/>最后，为了提高在照片中的细节，我们可以使用清晰度工具。 <br/>增加<b>清晰度</b>至100",
    "white_balance_4": "非常好！你已经学会了如何通过3个简单步骤修复白平衡和画面中的阴影。",
    "white_balance_description": "在本教程中，您将学习白平衡以及如何调整工光源来色温。",
    "whites": "调整画面中较亮部分的亮度，其影响范围比高光更大"
  }
};
},{}],337:[function(require,module,exports){
var counterpart, lang, storage;

storage = require("./storage.coffee");

counterpart = require("counterpart");

counterpart.registerTranslations('en', require('./locales/src/public/en.js'));

counterpart.registerTranslations('es', require('./locales/src/public/es.js'));

counterpart.registerTranslations('fr', require('./locales/src/public/fr.js'));

counterpart.registerTranslations('de', require('./locales/src/public/de.js'));

counterpart.registerTranslations('zh', require('./locales/src/public/zh.js'));

counterpart.registerTranslations('zh-tw', require('./locales/src/public/zh-tw.js'));

counterpart.registerTranslations('ja', require('./locales/src/public/ja.js'));

counterpart.registerTranslations('nl', require('./locales/src/public/nl.js'));

counterpart.registerTranslations('it', require('./locales/src/public/it.js'));

counterpart.registerTranslations('ko', require('./locales/src/public/ko.js'));

counterpart.registerTranslations('pt', require('./locales/src/public/pt.js'));

counterpart.registerTranslations('ru', require('./locales/src/public/ru.js'));

counterpart.registerTranslations('tr', require('./locales/src/public/tr.js'));

counterpart.registerTranslations('ar', require('./locales/src/public/ar.js'));

counterpart.registerTranslations('cy', require('./locales/src/public/cy.js'));

counterpart.registerTranslations('th', require('./locales/src/public/th.js'));

counterpart.registerTranslations('id', require('./locales/src/public/id.js'));

lang = (window.navigator.userLanguage || window.navigator.language).toLowerCase();

storage.getItem("locale", function(err, locale) {
  var Main, React, ReactDOM, forceUpdate, instance, message;
  if (typeof locale !== "string") {
    locale = "en";
    if (lang.match("cy")) {
      locale = "cy";
    }
    if (lang.match("it")) {
      locale = "it";
    }
    if (lang.match("nl")) {
      locale = "nl";
    }
    if (lang.match("th")) {
      locale = "th";
    }
    if (lang.match("de")) {
      locale = "de";
    }
    if (lang.match("id")) {
      locale = "id";
    }
    if (lang.match("ja")) {
      locale = "ja";
    }
    if (lang.match("ru")) {
      locale = "ru";
    }
    if (lang.match("fr")) {
      locale = "fr";
    }
    if (lang.match("ko")) {
      locale = "ko";
    }
    if (lang.match("tr")) {
      locale = "tr";
    }
    if (lang.match("ar")) {
      locale = "ar";
    }
    if (lang.match("zh")) {
      locale = "zh";
    }
    if (lang.match("cn")) {
      locale = "zh";
    }
    if (lang.match("tw")) {
      locale = "zh-tw";
    }
    if (lang.match("hk")) {
      locale = "zh-tw";
    }
    if (lang.match("pt")) {
      locale = "pt";
    }
    if (lang.match("es")) {
      locale = "es";
    }
  }
  counterpart.setLocale(locale);
  require("./webgl_support.coffee");
  window.Filtr = require("../filtr/api.coffee");
  React = require("react");
  ReactDOM = require("react-dom");
  Main = require("./components/main.coffee");
  forceUpdate = require("react-deep-force-update");
  instance = ReactDOM.render(React.createElement(Main, null), document.getElementById('app'));
  counterpart.onLocaleChange((function(_this) {
    return function() {
      return forceUpdate(instance);
    };
  })(this));
  if (CONFIG.demoMode && CONFIG.embedded) {
    message = counterpart("status.demo_mode");
    return window.splashScreen.setHTML("<div style='transform: rotate(-90deg); -webkit-transform: rotate(-90deg); width: 80vh;'> " + message + " </div>");
  }
});


},{"../filtr/api.coffee":362,"./components/main.coffee":268,"./locales/src/public/ar.js":320,"./locales/src/public/cy.js":321,"./locales/src/public/de.js":322,"./locales/src/public/en.js":323,"./locales/src/public/es.js":324,"./locales/src/public/fr.js":325,"./locales/src/public/id.js":326,"./locales/src/public/it.js":327,"./locales/src/public/ja.js":328,"./locales/src/public/ko.js":329,"./locales/src/public/nl.js":330,"./locales/src/public/pt.js":331,"./locales/src/public/ru.js":332,"./locales/src/public/th.js":333,"./locales/src/public/tr.js":334,"./locales/src/public/zh-tw.js":335,"./locales/src/public/zh.js":336,"./storage.coffee":343,"./webgl_support.coffee":361,"counterpart":3,"react":215,"react-deep-force-update":33,"react-dom":34}],338:[function(require,module,exports){
var EventEmitter, Manager, adjustmentPanels, manager, storage, utils;

EventEmitter = require("eventemitter2").EventEmitter2;

utils = require("./utils.coffee");

storage = require("./storage.coffee");

adjustmentPanels = {
  color: {
    controls: ["temperature", "tint", "saturation", "vibrance"]
  },
  light: {
    controls: ["dehaze", "exposure", "gamma", "contrast", "highlights", "shadows", "whites", "blacks"]
  },
  detail: {
    controls: ["sharpen", "clarity", "luminance_denoise", "color_denoise"]
  },
  vignette: {
    controls: ["vignette_amount", "vignette_feather", "vignette_highlights", "vignette_roundness", "vignette_size"]
  },
  hsl: {
    controls: ["hue_red", "hue_orange", "hue_yellow", "hue_green", "hue_aqua", "hue_blue", "hue_purple", "hue_magenta", "saturation_red", "saturation_orange", "saturation_yellow", "saturation_green", "saturation_aqua", "saturation_blue", "saturation_purple", "saturation_magenta", "luminance_red", "luminance_orange", "luminance_yellow", "luminance_green", "luminance_aqua", "luminance_blue", "luminance_purple", "luminance_magenta"]
  },
  curves: {
    controls: ["curves_red", "curves_green", "curves_blue", "curves_all"]
  },
  toning: {
    controls: ["highlights_hue", "highlights_saturation", "shadows_hue", "shadows_saturation", "balance"]
  },
  distortion: {
    controls: ["distortion_amount", "distortion_horizontal", "distortion_vertical"]
  },
  effects: {
    controls: ["fringing", "mosaic_size", "diffuse", "grain_amount", "grain_size"]
  }
};

Manager = (function() {
  function Manager() {
    this.emitter = new EventEmitter();
    this.emitter.setMaxListeners(30);
    this.state = {
      dialog: {},
      subscription: null,
      activeMask: null,
      histogram: true,
      histogramPosition: [0, 0],
      filter_tab: 2,
      adjustment_tab: -1,
      curves_tab: null,
      hsl_tab: "red",
      purchases: {},
      currencies: {},
      sharingOptions: [],
      customFilters: [],
      favoriteFilters: [],
      popoutSliders: {
        left: false,
        right: false
      },
      settings: {
        quality: 100
      },
      user: {
        user_name: null,
        email: null,
        picture: null,
        id: null,
        session_token: null,
        verified_email: 0,
        is_unlimited: false,
        plan_type: null,
        credits: 0,
        discount: 0
      },
      panelWidth: 168,
      toolbarWidth: 50,
      adjustmentPanels: adjustmentPanels,
      portraitMode: CONFIG.platform === "mobile" && !CONFIG.debug ? true : false,
      keyboardHeight: 0
    };
    if (CONFIG.platform === "mobile") {
      this.state.panelWidth = 84;
      this.state.toolbars = {
        leftActive: null,
        left: ["guides", "history", "undo", "text"],
        rightActive: null,
        right: ["local-adjustments", "adjustments", "looks", "crop"]
      };
      if (CONFIG.os === "ios") {
        this.state.toolbars.left.push("face");
      }
      if (CONFIG.os === "android") {
        this.state.toolbars.left.push("camera");
      }
    } else {
      this.state.toolbars = {
        leftActive: null,
        left: ["text", "looks", "filmstrip", "history", "undo"],
        rightActive: null,
        right: ["guides", "adjustments", "local-adjustments", "crop"]
      };
      if (CONFIG.embedded) {
        this.state.toolbars = {
          leftActive: null,
          left: ["looks", "history", "undo", "text"],
          rightActive: null,
          right: ["guides", "adjustments", "local-adjustments", "crop"]
        };
      }
      if (CONFIG.os === "osx" || CONFIG.debug) {
        this.state.toolbars.right.push("face");
      }
    }
    this.whitelist = ["subscription", "settings", "toolbars", "histogram", "histogramPosition", "filter_tab", "hsl_tab", "adjustment_tab", "purchases", "currencies", "customFilters", "favoriteFilters", "user"];
    Filtr.on("history", (function(_this) {
      return function(history, index) {
        return _this.emitter.emit("history", history, index);
      };
    })(this));
    this.restoreState = new Promise((function(_this) {
      return function(resolve) {
        return _this._restoreState(resolve);
      };
    })(this));
  }

  Manager.prototype.once = function(event, listener) {
    this.emitter.once(event, listener);
    return (function(_this) {
      return function() {
        return _this.emitter.removeListener(event, listener);
      };
    })(this);
  };

  Manager.prototype.on = function(event, callback) {
    var func;
    func = utils.debounce(callback, 1);
    this.emitter.addListener(event, func);
    return (function(_this) {
      return function() {
        return _this.emitter.removeListener(event, func);
      };
    })(this);
  };

  Manager.prototype.emit = function() {
    return this.emitter.emit.apply(this.emitter, arguments);
  };

  Manager.prototype.set = function(obj, val) {
    var key, results, value;
    this.saveState();
    if (typeof val !== "undefined") {
      this.state[obj] = val;
      this.emitter.emit(obj, val);
      return;
    }
    results = [];
    for (key in obj) {
      value = obj[key];
      this.state[key] = value;
      results.push(this.emitter.emit(key, value));
    }
    return results;
  };

  Manager.prototype.selectWhitelisted = function(state) {
    var j, key, len, ref, whitelisted;
    whitelisted = {};
    ref = this.whitelist;
    for (j = 0, len = ref.length; j < len; j++) {
      key = ref[j];
      if (state[key] != null) {
        whitelisted[key] = state[key];
      }
    }
    return whitelisted;
  };

  Manager.prototype.saveState = utils.debounce(function(callback) {
    var state;
    if (CONFIG.demoMode) {
      if (typeof callback === "function") {
        callback(null, this.state);
      }
      return;
    }
    if (this.stateRestored) {
      state = this.selectWhitelisted(this.state);
      return storage.setItem("manager_state", state, callback);
    }
  }, 1000);

  Manager.prototype.mergeToolbars = function(defaultState, newState) {
    var left, newToolbars, remove, right;
    if (!newState.toolbars) {
      return defaultState.toolbars;
    }
    left = defaultState.toolbars.left.slice(0);
    right = defaultState.toolbars.right.slice(0);
    remove = function(side) {
      var i, j, len, li, ref, results, ri, tool;
      ref = newState.toolbars[side].slice(0);
      results = [];
      for (i = j = 0, len = ref.length; j < len; i = ++j) {
        tool = ref[i];
        li = left.indexOf(tool);
        ri = right.indexOf(tool);
        if (li === -1 && ri === -1) {
          results.push(newState.toolbars[side].splice(i, 1));
        } else if (li !== -1) {
          results.push(left.splice(li, 1));
        } else {
          results.push(right.splice(ri, 1));
        }
      }
      return results;
    };
    remove("left");
    remove("right");
    newToolbars = {
      leftActive: newState.toolbars.leftActive,
      rightActive: newState.toolbars.rightActive,
      left: newState.toolbars.left.concat(left),
      right: newState.toolbars.right.concat(right)
    };
    return newToolbars;
  };

  Manager.prototype._restoreState = function(callback) {
    logTime("restore state");
    if (CONFIG.demoMode) {
      if (typeof callback === "function") {
        callback(null, this.state);
      }
      return;
    }
    return storage.getItem("manager_state", (function(_this) {
      return function(res, state) {
        if (state == null) {
          state = {};
        }
        if (typeof state === "string") {
          state = JSON.parse(state);
        }
        state = _this.selectWhitelisted(state);
        state.toolbars = _this.mergeToolbars(_this.state, state);
        _this.set(state);
        if (!_this.state.customFilters.length) {
          storage.getItem("looks", function(err, val) {
            if (val != null) {
              return _this.set("customFilters", val);
            }
          });
        }
        storage.getItem("UserStore", function(err, val) {
          var e, error, user, userData;
          if (val != null) {
            try {
              userData = JSON.parse(val);
              if (userData.is_unlimited) {
                _this.state.purchases["co.polarr.ppe.purchase.pro_all_filters"] = true;
                _this.set("purchases", _this.state.purchases);
              }
              user = utils.assign(_this.state.user, userData);
              _this.set("user", user);
            } catch (error) {
              e = error;
              console.error("Can't parse UserStore");
            }
            return storage.removeItem("UserStore");
          }
        });
        _this.stateRestored = true;
        logTimeEnd("restore state");
        return callback(state);
      };
    })(this));
  };

  return Manager;

})();

manager = new Manager();

module.exports = manager;


},{"./storage.coffee":343,"./utils.coffee":360,"eventemitter2":13}],339:[function(require,module,exports){
(function (ReactDOM){
var getInput, mixins, store, utils;

store = require("./store.coffee");

utils = require("./utils.coffee");

getInput = function(event) {
  var ref;
  return ((ref = event.targetTouches) != null ? ref[0] : void 0) || event;
};

mixins = {
  pointer: {
    getInput: getInput
  },
  touchRipple: {
    insertRipple: function(el) {
      var ripple;
      ripple = document.createElement("div");
      ripple.className = "ripple";
      el.appendChild(ripple);
      return ripple;
    },
    touchRipple: function(e, el) {
      var height, ripple, width;
      if (!(el instanceof HTMLElement)) {
        el = ReactDOM.findDOMNode(this);
      }
      ripple = this.insertRipple(el);
      width = el.offsetWidth;
      height = el.offsetHeight;
      ripple.style.width = width + "px";
      ripple.style.height = height + "px";
      ripple.style.left = 0;
      ripple.style.top = 0;
      ripple.style.transition = "none";
      ripple.style.opacity = "1";
      requestAnimationFrame(function() {
        return ripple.style.transition = "all 0.3s ease-out";
      });
      return this.ripple = ripple;
    },
    __touchRipple: function(e, el) {
      var bl, br, height, input, offset, radius, ref, ripple, scale, size, tl, tr, width, x, y;
      if (!(el instanceof HTMLElement)) {
        el = ReactDOM.findDOMNode(this);
      }
      ripple = this.insertRipple(el);
      input = ((ref = e.targetTouches) != null ? ref[0] : void 0) || e;
      offset = el.getBoundingClientRect();
      x = input.pageX - offset.left;
      y = input.pageY - offset.top;
      width = el.offsetWidth;
      height = el.offsetHeight;
      tl = utils.calcDiag(x, y);
      tr = utils.calcDiag(width - x, y);
      br = utils.calcDiag(width - x, height - y);
      bl = utils.calcDiag(x, height - y);
      radius = Math.max(tl, tr, br, bl);
      size = radius * 2;
      scale = CONFIG.os === "android" ? 0 : 1;
      ripple.style.width = size + "px";
      ripple.style.height = size + "px";
      ripple.style.left = x - radius + "px";
      ripple.style.top = y - radius + "px";
      ripple.style.transition = "none";
      ripple.style.webkitTransform = "scale(" + scale + ")";
      ripple.style.transform = "scale(" + scale + ")";
      ripple.style.opacity = "1";
      getComputedStyle(ripple).getPropertyValue("display");
      requestAnimationFrame(function() {
        ripple.style.transition = "all 0.3s ease-out";
        ripple.style.webkitTransform = "scale(1)";
        return ripple.style.transform = "scale(1)";
      });
      return this.ripple = ripple;
    },
    preventDefault: function(e) {
      return e.preventDefault();
    },
    touchRippleEnd: function(e) {
      var ripple;
      if (this.ripple) {
        this.ripple.style.opacity = "0";
        ripple = this.ripple;
        setTimeout(function() {
          var ref;
          return (ref = ripple.parentNode) != null ? ref.removeChild(ripple) : void 0;
        }, 300);
      }
      clearTimeout(this._inputTypeTimeout);
      this._inputTypeTimeout = setTimeout((function(_this) {
        return function() {
          return _this._inputType = null;
        };
      })(this), 300);
      if (this.rippleTarget) {
        this.rippleTarget.removeEventListener("touchend", this.touchRippleEnd);
        this.rippleTarget.removeEventListener("touchmove", this.touchRippleEnd);
        this.rippleTarget.removeEventListener("touchcancel", this.touchRippleEnd);
        this.rippleTarget.removeEventListener("mouseup", this.touchRippleEnd);
        this.rippleTarget.removeEventListener("mousemove", this.touchRippleEnd);
        this.rippleTarget.removeEventListener("pointerup", this.touchRippleEnd);
        this.rippleTarget.removeEventListener("pointermove", this.touchRippleEnd);
        this.rippleTarget.removeEventListener("pointercancel", this.touchRippleEnd);
        return this.rippleTarget.removeEventListener("contextmenu", this.preventDefault);
      }
    },
    touchRippleStart: function(e, el) {
      if (this._inputType && this._inputType !== e.type) {
        return;
      }
      this._inputType = e.type;
      this.touchRipple(e, el);
      switch (e.type) {
        case "touchstart":
          this.rippleTarget = e.target;
          this.rippleTarget.addEventListener("touchend", this.touchRippleEnd);
          this.rippleTarget.addEventListener("touchmove", this.touchRippleEnd);
          this.rippleTarget.addEventListener("touchcancel", this.touchRippleEnd);
          break;
        case "pointerdown":
          this.rippleTarget = document;
          this.rippleTarget.addEventListener("pointerup", this.touchRippleEnd);
          this.rippleTarget.addEventListener("pointermove", this.touchRippleEnd);
          this.rippleTarget.addEventListener("pointercancel", this.touchRippleEnd);
          break;
        case "mousedown":
          this.rippleTarget = document;
          this.rippleTarget.addEventListener("mouseup", this.touchRippleEnd);
          this.rippleTarget.addEventListener("mousemove", this.touchRippleEnd);
      }
      return this.rippleTarget.addEventListener("contextmenu", this.preventDefault);
    }
  }
};

module.exports = mixins;


}).call(this,require("react-dom"))
},{"./store.coffee":346,"./utils.coffee":360,"react-dom":34}],340:[function(require,module,exports){

module.exports = [
  {
    pack_name: "The Unsplash Collection",
    pack_id: "unsplash",
    pack_maker: "Unsplash",
    info: "dialogs.unsplash_info",
    filters: [
      {
        "name":"Delta",
        "author": "Luke Chesser",
        "web": "unsplash.com/@lukechesser",
        "id":"V1E5IFF3x",
        "state":{"local_adjustments":[],"orientation":{"rotate90":0,"flip_x":false,"flip_y":false},"flip_y":false,"flip_x":false,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.10416666666666674,"tint":-0.03125,"exposure":0,"gamma":0,"contrast":0,"highlights":-0.05208333333333326,"shadows":0.32291666666666674,"whites":0,"blacks":0.25,"clarity":0,"saturation":0,"vibrance":0,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[66.27403846153847,62.19230769230768],[202.33173076923075,211.8557692307692],[255,255]],"hue_red":0.09375,"hue_orange":-0.041666666666666574,"hue_yellow":0,"hue_green":0.17708333333333348,"hue_aqua":0,"hue_blue":0.05208333333333326,"hue_purple":0.30208333333333326,"hue_magenta":0.14583333333333326,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":-0.052083333333333426,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":-0.08333333333333326,"luminance_orange":-0.17708333333333343,"luminance_yellow":0,"luminance_green":-0.08333333333333343,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":-0.10416666666666657,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {
        "name":"Faded",
        "author": "Samuel Zeller",
        "web": "unsplash.com/@samuelzeller",
        "id":"NJlZ6cJBy-",
        "state":{"local_adjustments":[],"orientation":{"rotate90":0,"flip_y":false,"flip_x":false},"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":-0.020833333333333426,"diffuse":0.14583333333333334,"temperature":0,"tint":0,"exposure":0.35416666666666674,"gamma":0.05208333333333348,"contrast":0.05208333333333326,"highlights":-0.3020833333333334,"shadows":0.25,"whites":-0.14583333333333343,"blacks":0.10416666666666652,"clarity":0.25,"saturation":0.02083333333333326,"vibrance":-0.052083333333333426,"curves_red":[[0,0],[73.07692307692308,75.7980769230769],[164.23557692307693,164.2355769230769],[255,255]],"curves_green":[[0,0],[71.71634615384615,71.71634615384613],[158.79326923076925,160.15384615384616],[255,255]],"curves_blue":[[0,0],[98.92788461538461,100.28846153846155],[187.3653846153846,191.4471153846154],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0.10416666666666674,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":-0.10416666666666657,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":-0.10416666666666674,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0.10416666666666674,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.057291666666666664,"highlights_saturation":0.0375,"shadows_hue":0.08333333333333333,"shadows_saturation":0.025,"balance":0,"sharpen":0.10416666666666666,"grain_amount":0.057291666666666664,"grain_size":0.6041666666666666,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {
        "name":"Urbex",
        "author": "Patrick Tomasso",
        "web": "unsplash.com/@impatrickt",
        "id":"EkHXLiRoe",
        "state":{"local_adjustments":[],"orientation":{"rotate90":-1,"flip_x":false,"flip_y":false},"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.13541666666666674,"diffuse":0.02083333333333337,"temperature":-0.17708333333333326,"tint":0,"exposure":0.10416666666666674,"gamma":0,"contrast":0.3645833333333335,"highlights":0.11458333333333348,"shadows":-0.28125,"whites":0.1875,"blacks":-0.17708333333333326,"clarity":0.14583333333333326,"saturation":-0.03125,"vibrance":0,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,21.769230769230774],[69.52403846153847,74.49038461538461],[192,210],[255,255]],"hue_red":0.25,"hue_orange":0.01041666666666674,"hue_yellow":-0.625,"hue_green":0.3645833333333335,"hue_aqua":0,"hue_blue":-0.4583333333333334,"hue_purple":0,"hue_magenta":0.125,"saturation_red":-0.010416666666666574,"saturation_orange":0.15625,"saturation_yellow":0.13541666666666674,"saturation_green":0.15625,"saturation_aqua":-0.4479166666666666,"saturation_blue":0,"saturation_purple":0.16666666666666674,"saturation_magenta":-0.36458333333333326,"luminance_red":0,"luminance_orange":0.4375,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0.010416666666666519,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.23958333333333326,"denoise":0,"chroma_amount":0}},
      {
        "name":"Slowlived",
        "author": "Annie Spratt",
        "web": "unsplash.com/@fableandfolk",
        "id":"V1qIznNRe",
        "state":{"local_adjustments":[],"orientation":{"rotate90":0,"flip_x":false,"flip_y":false},"flip_y":false,"flip_x":false,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.3125,"diffuse":0,"temperature":0,"tint":0,"exposure":0.5,"gamma":0,"contrast":0,"highlights":0,"shadows":0,"whites":0,"blacks":0,"clarity":-0.010416666666666574,"saturation":0.17708333333333348,"vibrance":0,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,55.78365384615384],[62.19230769230769,68.99519230769232],[130.22115384615384,122.05769230769232],[209.13461538461536,210.49519230769232],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.0625,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.2708333333333333,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {
        "name":"Forest",
        "author": "Josh Felise",
        "web": "unsplash.com/@jfelise",
        "id":"VyBXc9HAl",
        "state":{"local_adjustments":[],"orientation":{"rotate90":0,"flip_x":false,"flip_y":false},"flip_y":false,"flip_x":false,"blend":0,"color_denoise":0.4635416666666667,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":-0.07291666666666674,"tint":-0.09375,"exposure":0,"gamma":0.10416666666666674,"contrast":0.25,"highlights":-0.30208333333333326,"shadows":0.5,"whites":0.14583333333333348,"blacks":0.10416666666666674,"clarity":0.10416666666666652,"saturation":-0.10416666666666657,"vibrance":0.25,"curves_red":[[0,5.442307692307679],[255,255]],"curves_green":[[0,13.605769230769226],[255,255]],"curves_blue":[[2.7211538461538463,0],[250.91826923076923,255]],"curves_all":[[0,20.40865384615384],[51.30769230769231,51.30769230769232],[85.32211538461539,89.40384615384616],[123.41826923076923,131.58173076923077],[169.6778846153846,176.48076923076925],[255,255]],"hue_red":0.14583333333333326,"hue_orange":0,"hue_yellow":0,"hue_green":0.17708333333333326,"hue_aqua":0,"hue_blue":0.05208333333333348,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0.19791666666666652,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0.25,"luminance_aqua":0,"luminance_blue":0.14583333333333326,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.5,"grain_amount":0.10416666666666667,"grain_size":0.203125,"distortion_amount":0,"fringing":0,"vignette_amount":-0.14583333333333326,"vignette_feather":0.75,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {
        "name":"Warm",
        "author": "Elizabeth Lies",
        "web": "unsplash.com/@elizabethlies",
        "id":"VkIvel50g",
        "state":{"vignette_feather":0.5,"hue_magenta":0,"saturation_aqua":0,"local_adjustments":[],"luminance_blue":0,"highlights":-0.22916666666666674,"shadows":-0.10416666666666674,"fringing":0,"curves_all":[[21.76923076923077,0],[255,255]],"vignette_size":1,"curves_blue":[[14.966346153846153,0],[255,245.47596153846155]],"flip_y":false,"luminance_green":0,"tint":0.0625,"whites":0.25,"saturation_yellow":0,"orientation":{"flip_y":false,"rotate90":0,"flip_x":false},"luminance_denoise":0.05208333333333333,"hue_aqua":0,"sharpen":0,"saturation_green":0,"luminance_aqua":0,"luminance_purple":0,"grain_amount":0.020833333333333336,"vignette_highlights":0.5,"chroma_amount":0,"vignette_roundness":0.01041666666666674,"vibrance":0.125,"hue_orange":0,"diffuse":0.05729166666666667,"denoise":0,"highlights_hue":0.10416666666666667,"luminance_red":0.23958333333333326,"saturation_red":-0.17708333333333326,"contrast":0.15625,"curves_green":[[0,0],[255,255]],"saturation_magenta":0,"dehaze":0.03125,"luminance_orange":0,"balance":0,"blend":0,"blacks":0.13541666666666674,"distortion_amount":0,"saturation":0.08333333333333326,"color_denoise":0.03125,"hue_purple":0,"flip_x":false,"saturation_blue":0,"hue_yellow":0,"shadows_hue":0.08854166666666667,"luminance_yellow":0,"temperature":-0.11458333333333343,"clarity":0,"hue_green":0,"exposure":0.1875,"gamma":0.125,"grain_size":0.25,"highlights_saturation":0.2625,"vignette_exposure":0.0625,"saturation_purple":0,"vignette_amount":0,"shadows_saturation":0.3125,"hue_red":0.14583333333333348,"luminance_magenta":0,"curves_red":[[0,35.375],[255,255]],"hue_blue":0,"saturation_orange":0}},
      {
        "name":"Tokyo",
        "author": "Dmitri Popov",
        "web": "unsplash.com/@dmpop",
        "id":"Nk94W8jCg",
        "state":{"local_adjustments":[],"orientation":{"rotate90":0,"flip_y":false,"flip_x":false},"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0.01041666666666674,"exposure":0,"gamma":0,"contrast":0,"highlights":0,"shadows":0.25,"whites":0,"blacks":-0.3854166666666665,"clarity":0.14583333333333326,"saturation":-0.14583333333333343,"vibrance":0,"curves_red":[[0,0],[62.19230769230769,73.0769230769231],[190.08653846153845,211.85576923076923],[255,255]],"curves_green":[[0,0],[60.831730769230774,78.51923076923077],[184.64423076923075,207.77403846153845],[255,255]],"curves_blue":[[0,0],[66.27403846153847,74.4375],[202.33173076923077,225.46153846153845],[255,255]],"curves_all":[[0,27.211538461538453],[66.27403846153845,122.05769230769232],[128.86057692307696,203.69230769230768],[186.0048076923077,237.7067307692308],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":-0.11458333333333326,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":-0.09375,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":-0.11458333333333326,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0.15,"balance":0,"sharpen":0.1875,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {
        "name":"Hadean",
        "author": "Caleb Morris",
        "web": "unsplash.com/@seemoris",
        "id":"VkJpXC0je",
        "state":{"local_adjustments":[],"orientation":{"rotate90":0,"flip_x":false,"flip_y":false},"flip_y":false,"flip_x":false,"blend":0,"color_denoise":0.13541666666666669,"luminance_denoise":0,"dehaze":0,"diffuse":0.09895833333333333,"temperature":0.04166666666666674,"tint":0,"exposure":-0.17708333333333343,"gamma":0.010416666666666519,"contrast":0.19791666666666652,"highlights":-0.23958333333333343,"shadows":-0.125,"whites":-1,"blacks":0.22916666666666674,"clarity":0.10416666666666674,"saturation":0.25,"vibrance":0.15625,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0.6354166666666665,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0.28125,"saturation_green":0.21875,"saturation_aqua":0.41666666666666674,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0.625,"luminance_green":0,"luminance_aqua":0.22916666666666652,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.15104166666666666,"highlights_saturation":0.3125,"shadows_hue":0.5520833333333334,"shadows_saturation":0.5625,"balance":0.33333333333333326,"sharpen":0,"grain_amount":0.125,"grain_size":0.7447916666666666,"distortion_amount":0,"fringing":0,"vignette_amount":0.07291666666666674,"vignette_feather":0.7239583333333334,"vignette_highlights":0.515625,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {
        "name":"Electric",
        "author": "Scott Webb",
        "web": "unsplash.com/@scottwebb",
        "id":"EyxRbD2UCe",
        "state":{"local_adjustments":[],"orientation":{"rotate90":0,"flip_y":false,"flip_x":false},"rotation":0,"rotate90":0,"flip_y":0,"flip_x":0,"crop":[0,0,1,1],"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":-0.8125,"tint":0,"exposure":0,"gamma":0,"contrast":0,"highlights":0,"shadows":0,"whites":0,"blacks":0,"clarity":0,"saturation":0,"vibrance":0,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {
        "name":"Desert Sky",
        "author": "Greg Rakozy",
        "web": "unsplash.com/@grakozy",
        "id":"41CNd4npl",
        "state":{"local_adjustments":[],"orientation":{"rotate90":0,"flip_x":false,"flip_y":false},"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0.5,"luminance_denoise":0.4010416666666667,"dehaze":0.30208333333333326,"diffuse":1,"temperature":-0.020833333333333426,"tint":0.25,"exposure":0.5208333333333333,"gamma":0.0625,"contrast":0.35416666666666674,"highlights":0.5208333333333333,"shadows":-0.17708333333333343,"whites":0,"blacks":0,"clarity":0.5208333333333333,"saturation":0.34375,"vibrance":0.6979166666666665,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[179.20192307692307,190.08653846153848],[206.41346153846152,217.29807692307693],[255,255]],"curves_all":[[8.163461538461538,19.048076923076934],[73.07692307692308,74.4375],[181.9230769230769,183.28365384615387],[238.67307692307693,255]],"hue_red":0.01041666666666674,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0.32291666666666674,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.23958333333333326,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0.20833333333333326,"saturation_blue":0,"saturation_purple":-0.010416666666666574,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.7604166666666667,"shadows_saturation":0.17499999999999993,"balance":0,"sharpen":0.16145833333333334,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-1,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}}

    ]
  },
  {
    pack_name: {"en":"Modern","nl":"Moderne","fr":"Moderne","de":"Modern","id":"Modern","it":"Moderna","ja":"モダン","ko":"현대","pt":"Moderno","es":"Moderno","ru":"Современность","th":"ทันสมัย","tr":"Modern","vt":"Hiện đại","zh":"现代","zh-tw":"現代"},
    pack_id: "N1aWOS3np0",
    filters: [
      {"name":{"en":"Focal","zh":"焦点","zh-tw":"焦點"},"state":{"blend":1,"temperature":0,"tint":-0.04,"exposure":0.4733333333333336,"gamma":0,"contrast":0,"highlights":0,"shadows":0,"whites":0.24,"blacks":0,"clarity":0.18,"saturation":0,"vibrance":0,"curves_red":[[0,0],[62.44897959183673,63.750000000000014],[123.5969387755102,132.70408163265304],[188.64795918367346,189.94897959183672],[255,255]],"curves_green":[[0,0],[55.94387755102041,55.94387755102042],[139.20918367346937,144.41326530612247],[199.05612244897958,195.1530612244898],[255,255]],"curves_blue":[[0,0],[52.04081632653061,55.94387755102043],[119.6938775510204,113.18877551020408],[202.95918367346937,206.86224489795916],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.5333333333333333,"highlights_saturation":0.02,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":-0.3,"vignette_feather":0.8888888888888888,"vignette_highlights":0.5,"local_adjustments":[{"type":"radial","feather":1,"size":[0.9052823315118397,0.5],"position":[0,0],"invert":true,"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.29487179487179493,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0}}]},"id":"Ek_Sn36X"},
      {"name":{"en":"Warmth","zh":"LOMO","zh-tw":"LOMO"},"state":{"blend":1,"temperature":0.022222222222222143,"tint":0.022222222222222143,"exposure":0.4444444444444444,"gamma":0.06666666666666665,"contrast":0,"highlights":0.3111111111111109,"shadows":0.3111111111111111,"whites":-0.1555555555555555,"blacks":0.022222222222222143,"clarity":0.15555555555555567,"saturation":-0.1777777777777778,"vibrance":0.40000000000000013,"curves_red":[[6.505102040816326,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,15.612244897959183],[46.83673469387755,52.04081632653062],[255,255]],"curves_all":[[0,20.816326530612244],[66.35204081632654,70.25510204081634],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":-0.5111111111111113,"vignette_feather":0.573333334,"vignette_highlights":0.5,"local_adjustments":[{"type":"radial","feather":1,"size":[0.75,0.5],"position":[-0.010121457489878541,0.06477732793522267],"invert":true,"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.4487179487179487,"contrast":0,"temperature":-0.17948717948717952,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0}}]},"id":"Vkl_rnhTX"},
      {"name":{"en":"Cinematic","zh":"冷调聚焦","zh-tw":"冷調聚焦"},"state":{"blend":1,"temperature":0,"tint":0,"exposure":0.33333333333333326,"gamma":-0.06666666666666665,"contrast":0,"highlights":0,"shadows":0,"whites":0,"blacks":0,"clarity":0,"saturation":-0.19999999999999996,"vibrance":0.3111111111111111,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,26.020408163265305],[61.147959183673464,65.05102040816327],[144.41326530612244,144.41326530612244],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":-0.1777777777777778,"vignette_feather":1,"vignette_highlights":1,"local_adjustments":[{"type":"radial","feather":1,"size":[1.0072859744990892,0.6475409836065573],"position":[0,0],"invert":true,"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.5,"contrast":0,"temperature":-0.641025641025641,"tint":0,"highlights":-0.6666666666666665,"shadows":0,"clarity":0,"saturation":-0.20512820512820507}}]},"id":"N1buHnhTX"},
      {"name":{"en":"Chill","zh":"冷艳锐化","zh-tw":"冷艷銳化"},"state":{"blend":1,"temperature":-0.0222222222222222,"tint":0,"exposure":0.19999999999999996,"gamma":0,"contrast":-0.1333333333333333,"highlights":-0.35555555555555557,"shadows":0.24444444444444446,"whites":0,"blacks":0,"clarity":0.15555555555555567,"saturation":0,"vibrance":-0.04444444444444434,"curves_red":[[0,24.71938775510204],[113.18877551020408,110.58673469387756],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,36.42857142857142],[41.63265306122449,44.234693877551024],[130.10204081632654,144.41326530612247],[255,255]],"curves_all":[[0,44.23469387755102],[66.35204081632652,70.25510204081635],[144.41326530612244,148.31632653061223],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":1,"denoise":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"local_adjustments":[{"type":"gradient","startPoint":[-0.0018214936247723133,-0.49043715846994534],"endPoint":[-0.0018214936247723133,0.38934426229508196],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0.03846153846153877,"contrast":-0.20512820512820507,"temperature":-0.20512820512820507,"tint":0,"highlights":-0.4358974358974359,"shadows":0,"clarity":0,"saturation":-0.20512820512820507}},{"type":"radial","feather":1,"size":[1.326047358834244,0.5],"position":[-0.0009107468123861571,-0.042349726775956276],"invert":true,"adjustments":{"exposure":-0.4230769230769233,"contrast":-0.4871794871794872,"temperature":-0.23076923076923084,"tint":0.15384615384615374,"highlights":-0.05128205128205132,"shadows":0,"clarity":-0.05128205128205132,"saturation":-0.07692307692307687}}]},"id":"NkGdBnn6Q"},
      {"name":{"en":"Icy","zh":"电影质感","zh-tw":"電影質感"},"state":{"blend":1,"temperature":-0.11111111111111116,"tint":0,"exposure":0.15555555555555545,"gamma":0.1777777777777778,"contrast":0.37777777777777777,"highlights":-0.6000000000000001,"shadows":0.3111111111111111,"whites":-0.22222222222222227,"blacks":0,"clarity":0.15555555555555567,"saturation":-0.2666666666666666,"vibrance":0.19999999999999996,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[105.38265306122449,111.88775510204081],[187.3469387755102,197.75510204081633],[255,255]],"curves_all":[[0,0],[128.80102040816325,148.31632653061226],[175.6377551020408,205.56122448979593],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.4777777777777778,"denoise":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":-0.3555555555555554,"vignette_feather":0.7777777777777779,"vignette_highlights":0.5,"local_adjustments":[{"type":"radial","feather":1,"size":[0.3333333333333333,0.5],"position":[0,0],"invert":true,"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.3717948717948718,"contrast":0,"temperature":0,"tint":0,"highlights":-0.05128205128205132,"shadows":-1,"clarity":0,"saturation":-0.07692307692307687}}]},"id":"EJ7_r23Tm"},
      {"name":{"en":"Lit","zh":"暖调","zh-tw":"暖調"},"state":{"blend":1,"temperature":-0.06666666666666682,"tint":0.04444444444444451,"exposure":-0.48888888888888893,"gamma":0.04444444444444451,"contrast":0.19999999999999973,"highlights":-0.2666666666666666,"shadows":0.7333333333333334,"whites":0.5333333333333332,"blacks":0.5777777777777777,"clarity":0.15555555555555567,"saturation":-0.24444444444444446,"vibrance":0.5777777777777777,"curves_red":[[0,0],[58.545918367346935,49.43877551020409],[189.94897959183672,193.85204081632654],[255,255]],"curves_green":[[0,0],[167.8316326530612,167.83163265306123],[221.1734693877551,215.96938775510205],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,45.535714285714285],[65.05102040816325,79.36224489795919],[187.34693877551018,199.05612244897958],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0.06666666666666665,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.1333333333333333,"saturation_orange":0.04444444444444451,"saturation_yellow":0,"saturation_green":0.19999999999999996,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.18888888888888888,"highlights_saturation":0.01,"shadows_hue":0.15555555555555556,"shadows_saturation":0.17777777777777776,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":-0.6444444444444444,"vignette_feather":0.7444444444444445,"vignette_highlights":1,"local_adjustments":[]},"id":"N1EOH33p7"},
      {"name":{"en":"Clear","zh":"浓郁色彩","zh-tw":"濃郁色彩"},"state":{"blend":1,"temperature":-0.08888888888888896,"tint":0.04444444444444451,"exposure":0.11111111111111094,"gamma":-0.11111111111111116,"contrast":-0.2888888888888888,"highlights":-0.06666666666666665,"shadows":0.1777777777777776,"whites":-0.1333333333333333,"blacks":-0.1777777777777778,"clarity":-0.1777777777777778,"saturation":-0.1555555555555555,"vibrance":0.4444444444444444,"curves_red":[[15.612244897959183,0],[91.07142857142857,92.37244897959184],[165.22959183673467,158.72448979591837],[255,255]],"curves_green":[[0,0],[74.15816326530611,81.96428571428572],[188.64795918367346,197.75510204081633],[239.3877551020408,255]],"curves_blue":[[15.612244897959183,0],[67.65306122448979,63.750000000000014],[201.65816326530611,213.36734693877548],[255,255]],"curves_all":[[0,23.418367346938773],[110.58673469387755,115.79081632653063],[165.2295918367347,192.55102040816328],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.3555555555555556,"saturation_orange":0.288888888888889,"saturation_yellow":0.1333333333333333,"saturation_green":0,"saturation_aqua":-0.08888888888888896,"saturation_blue":-0.19999999999999996,"saturation_purple":-0.1333333333333333,"saturation_magenta":-0.2888888888888888,"luminance_red":-0.1555555555555555,"luminance_orange":0,"luminance_yellow":0.11111111111111116,"luminance_green":0.1333333333333333,"luminance_aqua":0.06666666666666665,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":-0.3333333333333334,"vignette_feather":1,"vignette_highlights":0.5,"local_adjustments":[]},"id":"VySurn3pX"},
      {"name":{"en":"Foam","zh":"泡沫色","zh-tw":"泡沫色"},"state":{"blend":1,"temperature":-0.06666666666666665,"tint":-0.04444444444444434,"exposure":0.5111111111111111,"gamma":0.1777777777777778,"contrast":0.22222222222222232,"highlights":-0.7777777777777777,"shadows":0.1777777777777778,"whites":0.2666666666666666,"blacks":-0.04444444444444451,"clarity":0.24444444444444446,"saturation":0.1777777777777776,"vibrance":0.6222222222222222,"curves_red":[[0,91.07142857142856],[50.739795918367335,83.265306122449],[123.5969387755102,126.19897959183673],[193.85204081632654,199.05612244897958],[255,255]],"curves_green":[[0,74.15816326530613],[67.65306122448979,88.46938775510205],[128.80102040816325,137.90816326530611],[201.65816326530611,195.15306122448982],[255,255]],"curves_blue":[[0,0],[59.846938775510196,80.66326530612247],[130.10204081632654,130.10204081632654],[255,255]],"curves_all":[[0,92.37244897959184],[62.44897959183673,91.07142857142858],[126.19897959183675,127.50000000000001],[188.64795918367344,197.75510204081633],[255,255]],"hue_red":0,"hue_orange":0.0888888888888888,"hue_yellow":0.04444444444444451,"hue_green":0,"hue_aqua":0.04444444444444451,"hue_blue":0.0888888888888888,"hue_purple":0.04444444444444451,"hue_magenta":0.04444444444444451,"saturation_red":0.04444444444444451,"saturation_orange":0.04444444444444451,"saturation_yellow":0.06666666666666665,"saturation_green":0.06666666666666665,"saturation_aqua":0.2666666666666666,"saturation_blue":0.11111111111111116,"saturation_purple":0.04444444444444451,"saturation_magenta":0.04444444444444451,"luminance_red":0.04444444444444451,"luminance_orange":0.04444444444444451,"luminance_yellow":0.04444444444444451,"luminance_green":0.11111111111111116,"luminance_aqua":0.04444444444444451,"luminance_blue":0.04444444444444451,"luminance_purple":0.04444444444444451,"luminance_magenta":0.04444444444444451,"highlights_hue":0.033333333333333326,"highlights_saturation":0.022222222222222223,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.14444444444444443,"denoise":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":-0.44444444444444425,"vignette_feather":1,"vignette_highlights":0.8222222222222222,"local_adjustments":[]},"id":"4kUuH2haX"},
      {"name":{"en":"Calming","zh":"阳光下","zh-tw":"陽光下"},"state":{"blend":1,"temperature":-0.2222222222222221,"tint":0,"exposure":0.89,"gamma":-0.2888888888888888,"contrast":-1,"highlights":-0.19999999999999996,"shadows":0,"whites":0,"blacks":0,"clarity":-0.06666666666666665,"saturation":0.42222222222222205,"vibrance":-0.288888888888889,"curves_red":[[0,0],[255,255]],"curves_green":[[0,49.43877551020408],[61.147959183673464,79.36224489795921],[255,255]],"curves_blue":[[0,45.535714285714285],[36.42857142857143,59.84693877551021],[255,255]],"curves_all":[[0,53.34183673469388],[22.11734693877551,63.750000000000014],[49.43877551020408,63.750000000000014],[145.71428571428572,130.10204081632654],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.15555555555555556,"highlights_saturation":0.04444444444444445,"shadows_hue":0.25555555555555554,"shadows_saturation":0.1,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":-0.06666666666666665,"vignette_feather":0.5444444444444444,"vignette_highlights":0.5,"local_adjustments":[]},"id":"NyPOS32p7"},
      {"name":{"en":"Lowkey","zh":"低调","zh-tw":"低調"},"state":{"blend":1,"temperature":-0.20000000000000012,"tint":-0.06666666666666665,"exposure":0.15555555555555545,"gamma":0.3111111111111111,"contrast":0.3333333333333335,"highlights":-1,"shadows":0.11111111111111116,"whites":0.06666666666666665,"blacks":-0.08888888888888896,"clarity":-0.3555555555555556,"saturation":-0.5555555555555556,"vibrance":0.9555555555555555,"curves_red":[[0,0],[255,240.6887755102041]],"curves_green":[[7.8061224489795915,1.3010204081632653],[255,241.98979591836735]],"curves_blue":[[0,15.612244897959183],[54.64285714285714,55.94387755102042],[255,255]],"curves_all":[[0,22.11734693877551],[228.9795918367347,255]],"hue_red":0,"hue_orange":-0.0222222222222222,"hue_yellow":0,"hue_green":0,"hue_aqua":0.1777777777777778,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.24444444444444446,"saturation_orange":0.19999999999999996,"saturation_yellow":0.24444444444444446,"saturation_green":-0.11111111111111116,"saturation_aqua":-0.24444444444444446,"saturation_blue":-0.1333333333333333,"saturation_purple":-0.3333333333333334,"saturation_magenta":0,"luminance_red":-0.08888888888888896,"luminance_orange":-0.08888888888888896,"luminance_yellow":-0.24444444444444446,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.16666666666666674,"highlights_saturation":0,"shadows_hue":0.6333333333333333,"shadows_saturation":0.1,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"local_adjustments":[{"type":"radial","feather":1,"size":[0.34224841341795104,0.5],"position":[0,0],"invert":true,"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.5128205128205128,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":-0.5128205128205128,"clarity":0,"saturation":-0.025641025641025605}}]},"id":"EJ__ShnTQ"},
      {"name":{"en":"Triple Scan","zh":"胶片色","zh-tw":"膠片色"},"state":{"blend":1,"temperature":0,"tint":0,"exposure":0.04444444444444451,"gamma":0.1,"contrast":0.08,"highlights":0.21333333333333337,"shadows":0,"whites":0,"blacks":0,"clarity":0,"saturation":0.36,"vibrance":0,"curves_red":[[0,44.23469387755102],[80.66326530612244,89.7704081632653],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,24.71938775510204],[45.535714285714285,45.535714285714306],[128.80102040816325,127.50000000000001],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":-0.3555555555555556,"saturation_orange":-0.17777777777777798,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0.19999999999999996,"luminance_orange":0.0888888888888888,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.044444444444444446,"shadows_saturation":0.03333333333333333,"balance":0,"sharpen":0.12,"denoise":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":-0.4222222222222223,"vignette_feather":0.8,"vignette_highlights":1,"local_adjustments":[]},"id":"E1Y_S2hpm"},
      {"name":{"en":"Kapa","zh":"浓郁黑白","zh-tw":"濃郁黑白"},"state":{"blend":1,"temperature":0,"tint":0,"exposure":-0.4155555555555553,"gamma":0.18222222222222229,"contrast":1,"highlights":-0.19999999999999996,"shadows":-0.28222222222222215,"whites":0.8,"blacks":0.1333333333333333,"clarity":0.06666666666666665,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[173.03571428571428,174.33673469387753],[255,255]],"curves_green":[[0,0],[83.26530612244898,83.26530612244898],[208.16326530612244,202.95918367346937],[255,255]],"curves_blue":[[0,0],[150.91836734693877,157.42346938775512],[255,255]],"curves_all":[[0,54.64285714285714],[20.81632653061224,63.75000000000001],[59.84693877551021,67.65306122448982],[92.37244897959182,93.67346938775512],[130.10204081632654,128.80102040816325],[208.16326530612244,206.8622448979592],[255,239.3877551020408]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.43333333333333335,"denoise":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":-0.08888888888888896,"vignette_feather":1,"vignette_highlights":1,"local_adjustments":[{"type":"radial","feather":1,"size":[0.6102003642987249,0.73224043715847],"position":[0,0],"invert":true,"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.1923076923076923,"contrast":0.41025641025641013,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0}}]},"id":"Ny9uBhhT7"}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.free.filters.pack1",
    "pack_name":{"en":"Art","nl":"Kunst","fr":"Art","de":"Kunst","id":"Seni","it":"Arte","ja":"アート","ko":"예술","pt":"Arte","es":"Arte","ru":"Искусство","th":"ศิลปะ","tr":"Art","vt":"Nghệ thuật","zh":"艺术","zh-tw":"藝術","ja":"アート","ar":"فن"},
    "pack_description":{"en":"A preset designed for aggressively tuning colors for simple, clean looking images. Ideal for scenes with fewer subjects and minimal colors.","nl":"Een preset ontworpen voor het agressief afstemmen van kleuren voor simpele, schoon uitziende afbeeldingen. Ideaal voor scènes met minder onderwerpen en minimale kleuren.","fr":"Un filtre conçu pour affiner intensément les couleurs et obtenir des images simples et épurées. Idéal pour des scènes avec peu d'objets et de couleurs.","de":"Ein Filter, um Farben radikal anzupassen, für einfache und sauber aussehende Bilder. Ideal für Szenen mit wenig Gegenständen und minimalen Farben.","id":"Sebuah Preset yang didesain untuk warna tuning agresif yang sederhana, gambar tampak bersih. Ideal untuk adegan dengan subyek yang lebih sedikit dan warna minimal.","it":"Un filtro ideato per calibrare aggressivamente i colori su immagini semplici e nitide. Ideale per scene con pochi soggetti e colori minimi.","ja":"シンプルできれいに見える画像のための積極的な色調のために設計されたプリセット。色や物は少ないシーンに最適です。","ko":"프리셋은 적극적으로 간단하고 깨끗한 보이는 이미지에 대한 조정 색상을 디자인했다. 최소한의 적은 과목과 색상 장면에 적합합니다.","pt":"Um set desenvolvido para realçar drasticamente cores em imagens simples e minimalistas. Ideal para cenas com poucos objetos e poucas cores.","es":"Un filtro diseñado para cambiar agresivamente los colores para imágenes simples y de aspecto limpio. Ideal para escenas con menos temas y colores mínimos.","ru":"Фильтр предназначен для полного изменения цвета простого, чистого изображения. Идеально подходит для сцен с небольшим количеством предметов и минимальным диапазоном красок","th":"ที่ตั้งไว้ล่วงหน้าได้รับการออกแบบอย่างจริงจังสำหรับสีที่เรียบง่ายปรับแต่งสำหรับภาพที่สะอาดมอง เหมาะอย่างยิ่งสำหรับฉากที่มีน้อยที่สุดวิชาน้อยลงและสี","tr":"Filtre, resimlerin daha ne görünmesi için renklerle oynanmasına verilen addır. Daha az nesnenin ve minimal renklerin olduğu sahneler için idealdir.","vt":"Một bộ lọc được thiết kế cho việc điều chỉnh màu sắc mạnh mẽ để tạo ra các bức ảnh đơn giản, nhìn thanh thoát. Lý tưởng cho các cảnh với ít đối tượng và có màu tối thiểu","zh":"为构图简单，场面分明类型照片设计的重度调整色调。","zh-tw":"為構圖簡單，場面分明類型照片設計的重度調整色調。","ja":"写真をシンプルで清潔感のある印象にするための複雑な色調補正を一瞬でかけられるフィルター集。被写体の数が限られていて色合いの少ない画像に最適。","ar":"ومسبقا المصممة للألوان توليف بقوة ل، صور نظيفة المظهر بسيطة. مثالية للمشاهد مع عدد أقل من المواد والحد الأدنى من الألوان."},
    "pack_maker":"Polarr",
    "is_free":1,
    "version":125,
    "preview_images":["a1.jpg","a2.jpg","a3.jpg"],
    "filters":[
      {"name":{"en":"A1 Budapest","zh":"A1 轻盈"},"state":{"temperature":-0.02305943488145501,"tint":0.08087041247158178,"exposure":-0.0022734654108477192,"gamma":0,"contrast":0.24715816823644032,"highlights":0.2679441377070477,"shadows":0.829165313413446,"whites":-0.5634946411172459,"blacks":-0.37642091588177984,"clarity":0,"saturation":-0.29327703799935045,"vibrance":-0.18934719064631378,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,30],[255,255]],"hue_red":0,"hue_orange":-0.0022734654108477192,"hue_yellow":-0.02305943488145501,"hue_green":0.4342318934719065,"hue_aqua":-0.08541734329327699,"hue_blue":-0.25170509905813576,"hue_purple":-0.4595647937642091,"hue_magenta":0.7044494965898018,"saturation_red":0.4758038324131211,"saturation_orange":0.4758038324131211,"saturation_yellow":0.4550178629425139,"saturation_green":-0.08541734329327699,"saturation_aqua":0,"saturation_blue":-0.0022734654108477192,"saturation_purple":0.12244235141279636,"saturation_magenta":0.14322832088340376,"luminance_red":0,"luminance_orange":0.6420915881779798,"luminance_yellow":0,"luminance_green":0.7044494965898018,"luminance_aqua":0.6628775576485872,"luminance_blue":0.6628775576485872,"luminance_purple":0.03929847353036697,"luminance_magenta":0,"highlights_hue":0.01851250405975957,"highlights_saturation":-0.0022734654108477192,"shadows_hue":-0.7921403052939266,"shadows_saturation":0.14322832088340376,"balance":0,"sharpen":0.12244235141279636,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"N13edShh6Q"},
      {"name":{"en":"A2 Zissou","zh":"A2 相差"},"state":{"temperature":0.03929847353036697,"tint":0.01851250405975957,"exposure":0.22637219876583314,"gamma":-0.0022734654108477192,"contrast":0.5797336797661579,"highlights":0.03929847353036697,"shadows":-0.37642091588177984,"whites":-0.12698928223449168,"blacks":0.16401429035401094,"clarity":0,"saturation":-0.39720688535238713,"vibrance":0.7044494965898018,"curves_red":[[0,3],[255,255]],"curves_green":[[0,10],[255,255]],"curves_blue":[[0,20],[255,255]],"curves_all":[[0,15],[30,40],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":-0.0022734654108477192,"hue_green":0,"hue_aqua":-0.25170509905813576,"hue_blue":-0.39720688535238713,"hue_purple":-0.04384540435206241,"hue_magenta":0,"saturation_red":0.5173757713543359,"saturation_orange":0.060084443000974375,"saturation_yellow":0.4134459240012991,"saturation_green":0.4758038324131211,"saturation_aqua":0.12244235141279636,"saturation_blue":0.4965898018837285,"saturation_purple":-0.25170509905813576,"saturation_magenta":0.4550178629425139,"luminance_red":-0.6882104579408899,"luminance_orange":0,"luminance_yellow":0,"luminance_green":-0.5427086716466385,"luminance_aqua":0,"luminance_blue":0.03929847353036697,"luminance_purple":0.03929847353036697,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.16401429035401094,"shadows_saturation":0.03929847353036697,"balance":0,"sharpen":0.3303020461188697,"denoise":0,"grain_amount":-0.0022734654108477192,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":-0.02305943488145501,"vignette_feather":1,"vignette_highlights":0},"id":"41aguBn3am"},
      {"name":{"en":"A3 Moonrise","zh":"A3 月光"},"state":{"temperature":0.03929847353036697,"tint":0.01851250405975957,"exposure":0.01851250405975957,"gamma":-0.0022734654108477192,"contrast":0.060084443000974375,"highlights":0.03929847353036697,"shadows":0.2887301071776551,"whites":-0.12698928223449168,"blacks":0.16401429035401094,"clarity":0,"saturation":-0.12698928223449168,"vibrance":0.5173757713543359,"curves_red":[[0,3],[255,255]],"curves_green":[[0,10],[255,255]],"curves_blue":[[0,10],[255,255]],"curves_all":[[0,5],[30,40],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":-0.0022734654108477192,"hue_green":0,"hue_aqua":-0.18934719064631378,"hue_blue":-0.23091912958752847,"hue_purple":-0.04384540435206241,"hue_magenta":0,"saturation_red":0.6213056187073724,"saturation_orange":0.060084443000974375,"saturation_yellow":-0.02305943488145501,"saturation_green":0.6005196492367653,"saturation_aqua":0.12244235141279636,"saturation_blue":0.2679441377070477,"saturation_purple":-0.25170509905813576,"saturation_magenta":0,"luminance_red":-0.6882104579408899,"luminance_orange":0,"luminance_yellow":0,"luminance_green":-0.5427086716466385,"luminance_aqua":0,"luminance_blue":0.03929847353036697,"luminance_purple":0.03929847353036697,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.16401429035401094,"shadows_saturation":0.03929847353036697,"balance":0,"sharpen":0.3303020461188697,"denoise":0,"grain_amount":-0.0022734654108477192,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":-0.02305943488145501,"vignette_feather":1,"vignette_highlights":0},"id":"VyCxdS2nTX"},
      {"name":{"en":"A4 Pearl","zh":"A4 珍珠"},"state":{"temperature":0.060084443000974375,"tint":0.01851250405975957,"exposure":0.3303020461188697,"gamma":0,"contrast":0.16401429035401094,"highlights":0,"shadows":0.16401429035401094,"whites":0.3303020461188697,"blacks":0,"clarity":-0.1477752517050991,"saturation":-0.21013316011692107,"vibrance":0.8083793439428386,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,20],[30,45], [255,255]],"hue_red":-0.0022734654108477192,"hue_orange":0,"hue_yellow":-0.0022734654108477192,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":-0.0022734654108477192,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":-0.0022734654108477192,"luminance_green":0,"luminance_aqua":0.2679441377070477,"luminance_blue":0.4342318934719065,"luminance_purple":0.3926599545306917,"luminance_magenta":0.2679441377070477,"highlights_hue":-0.0022734654108477192,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":-0.0022734654108477192,"balance":-0.0022734654108477192,"sharpen":0.20558622929522574,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"411bOrh3aX"},
      {"name":{"en":"A5 Amelie","zh":"A5 绿阶"},"state":{"temperature":0.10165638194218896,"tint":0.03929847353036697,"exposure":0.18480025982461834,"gamma":0.08087041247158178,"contrast":0.22637219876583314,"highlights":-0.27249106852874305,"shadows":0.22637219876583314,"whites":0.14322832088340376,"blacks":-0.16856122117570638,"clarity":-0.0022734654108477192,"saturation":-0.1477752517050991,"vibrance":0.14322832088340376,"curves_red":[[0,5],[255,255]],"curves_green":[[0,20],[255,255]],"curves_blue":[[0,10],[255,255]],"curves_all":[[0,20],[30,45],[255,255]],"hue_red":-0.0022734654108477192,"hue_orange":0,"hue_yellow":-0.0022734654108477192,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.2887301071776551,"saturation_orange":0.22637219876583314,"saturation_yellow":-0.18934719064631378,"saturation_green":0.01851250405975957,"saturation_aqua":-0.0022734654108477192,"saturation_blue":0.9746670997076974,"saturation_purple":0,"saturation_magenta":-0.6882104579408899,"luminance_red":-0.27249106852874305,"luminance_orange":-0.21013316011692107,"luminance_yellow":-0.0646313738226697,"luminance_green":0.6836635271191946,"luminance_aqua":0.2679441377070477,"luminance_blue":0.4342318934719065,"luminance_purple":-0.08541734329327699,"luminance_magenta":0.01851250405975957,"highlights_hue":-0.0022734654108477192,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":-0.0022734654108477192,"balance":-0.0022734654108477192,"sharpen":0.10165638194218896,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"41x-dS3na7"},
      {"name":{"en":"A6 Wander","zh":"A6 秋色"},"state":{"temperature":-0.0022734654108477192,"tint":-0.0022734654108477192,"exposure":0.2679441377070477,"gamma":0,"contrast":0.4550178629425139,"highlights":-0.0022734654108477192,"shadows":0.24715816823644032,"whites":-0.0022734654108477192,"blacks":-0.0022734654108477192,"clarity":0,"saturation":0.08087041247158178,"vibrance":0.2887301071776551,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,12],[80,90],[255,255]],"hue_red":0.01851250405975957,"hue_orange":-0.0022734654108477192,"hue_yellow":-0.27249106852874305,"hue_green":-0.0022734654108477192,"hue_aqua":-0.12698928223449168,"hue_blue":-0.1062033127638844,"hue_purple":-0.21013316011692107,"hue_magenta":0.14322832088340376,"saturation_red":-0.1477752517050991,"saturation_orange":-0.31406300746995774,"saturation_yellow":-0.25170509905813576,"saturation_green":-0.0022734654108477192,"saturation_aqua":0.4550178629425139,"saturation_blue":-0.21013316011692107,"saturation_purple":0.4342318934719065,"saturation_magenta":0.24715816823644032,"luminance_red":-0.0022734654108477192,"luminance_orange":0,"luminance_yellow":-0.0022734654108477192,"luminance_green":0,"luminance_aqua":0,"luminance_blue":-0.0022734654108477192,"luminance_purple":-0.0022734654108477192,"luminance_magenta":0,"highlights_hue":-0.8752841831763559,"highlights_saturation":0.01851250405975957,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"4kb-uS33am"},
      {"name":{"en":"A7 Rouge","zh":"A7 青"},"state":{"temperature":-0.0646313738226697,"tint":-0.0022734654108477192,"exposure":0.4965898018837285,"gamma":0.01851250405975957,"contrast":0.5589477102955505,"highlights":-0.04384540435206241,"shadows":-0.7089964274114973,"whites":-0.0022734654108477192,"blacks":1,"clarity":0,"saturation":-0.02305943488145501,"vibrance":0.6213056187073724,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,30],[75,100],[120,125],[255,255]],"hue_red":-0.0022734654108477192,"hue_orange":-0.0022734654108477192,"hue_yellow":0.3303020461188697,"hue_green":0.3718739850600845,"hue_aqua":-0.27249106852874305,"hue_blue":-0.27249106852874305,"hue_purple":1,"hue_magenta":-0.0022734654108477192,"saturation_red":0.3510880155894771,"saturation_orange":-0.0022734654108477192,"saturation_yellow":-0.0022734654108477192,"saturation_green":-0.0022734654108477192,"saturation_aqua":-0.0022734654108477192,"saturation_blue":0.30951607664826253,"saturation_purple":-0.0022734654108477192,"saturation_magenta":-0.0022734654108477192,"luminance_red":-0.0022734654108477192,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":-0.0022734654108477192,"luminance_blue":-0.0022734654108477192,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":-0.7713543358233192,"highlights_saturation":0.01851250405975957,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"VyMWOrnnam"},
      {"name":{"en":"A8 Vivere","zh":"A8 赤红"},"state":{"temperature":0,"tint":-0.04384540435206241,"exposure":0.30951607664826253,"gamma":0,"contrast":0.2887301071776551,"highlights":-0.6050665800584605,"shadows":0.7252354660604092,"whites":-0.5842806105878532,"blacks":0.10165638194218896,"clarity":0,"saturation":-0.0646313738226697,"vibrance":0.18480025982461834,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[75,95],[255,255]],"hue_red":-0.02305943488145501,"hue_orange":0,"hue_yellow":-0.5219227021760312,"hue_green":0.7044494965898018,"hue_aqua":0,"hue_blue":0.20558622929522574,"hue_purple":-0.6674244884702826,"hue_magenta":0.3718739850600845,"saturation_red":0.3510880155894771,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0.30951607664826253,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":-0.5634946411172459,"highlights_saturation":0.01851250405975957,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"4kQ-Or3hTX"},
      {"name":{"en":"A9 Vertigo","zh":"A9 干燥"},"state":{"temperature":0.01851250405975957,"tint":0.01851250405975957,"exposure":0.5381617408249431,"gamma":-0.0022734654108477192,"contrast":0.3926599545306917,"highlights":0.5797336797661579,"shadows":-0.1062033127638844,"whites":-0.5842806105878532,"blacks":0.9330951607664826,"clarity":0,"saturation":-0.23091912958752847,"vibrance":0.16401429035401094,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[170,175],[255,255]],"hue_red":0.3926599545306917,"hue_orange":-0.0022734654108477192,"hue_yellow":0.10165638194218896,"hue_green":0.7044494965898018,"hue_aqua":-0.6258525495290679,"hue_blue":-0.5219227021760312,"hue_purple":-0.4595647937642091,"hue_magenta":0.4550178629425139,"saturation_red":0.3510880155894771,"saturation_orange":-0.0022734654108477192,"saturation_yellow":-0.0022734654108477192,"saturation_green":-0.0022734654108477192,"saturation_aqua":-0.0022734654108477192,"saturation_blue":0.30951607664826253,"saturation_purple":-0.0022734654108477192,"saturation_magenta":-0.0022734654108477192,"luminance_red":-0.0022734654108477192,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":-0.0022734654108477192,"luminance_blue":-0.0022734654108477192,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":-0.29327703799935045,"highlights_saturation":0.01851250405975957,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"VkN-dB2hpQ"},
      {"name":{"en":"A10 Concrete","zh":"A10 混凝土"},"state":{"temperature":-0.0022734654108477192,"tint":0.08087041247158178,"exposure":0.4758038324131211,"gamma":0,"contrast":0.22637219876583314,"highlights":-0.0022734654108477192,"shadows":0,"whites":0,"blacks":0,"clarity":-0.0022734654108477192,"saturation":-0.5427086716466385,"vibrance":1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,30],[60,65],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":-0.0022734654108477192,"hue_green":-0.0022734654108477192,"hue_aqua":0.3303020461188697,"hue_blue":-0.5427086716466385,"hue_purple":0,"hue_magenta":0.2679441377070477,"saturation_red":0,"saturation_orange":-0.0022734654108477192,"saturation_yellow":0,"saturation_green":-0.0022734654108477192,"saturation_aqua":0,"saturation_blue":-0.0022734654108477192,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":-0.4387788242936018,"highlights_saturation":0.03929847353036697,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"VJH-_rnhp7"},
      {"name":{"en":"AB1 Paper","zh":"AB1 纸"},"state":{"temperature":1,"tint":-1,"exposure":-0.4387788242936018,"gamma":0,"contrast":1,"highlights":-0.18934719064631378,"shadows":0.4550178629425139,"whites":-0.0022734654108477192,"blacks":1,"clarity":0.01851250405975957,"saturation":-1,"vibrance":1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,10],[30,10],[50,55],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":-0.0022734654108477192,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":-0.0022734654108477192,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":-0.0022734654108477192,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":-0.12698928223449168,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"N18b_H3npm"},
      {"name":{"en":"AB2 Charcoal","zh":"AB2 对比"},"state":{"temperature":1,"tint":-1,"exposure":-0.25170509905813576,"gamma":0,"contrast":0.6213056187073724,"highlights":0.5173757713543359,"shadows":-0.27249106852874305,"whites":-0.16856122117570638,"blacks":0.9954530691783046,"clarity":0,"saturation":-1,"vibrance":0.3926599545306917,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,30],[50,40],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":-0.0022734654108477192,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":-0.0022734654108477192,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":-0.0022734654108477192,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.3510880155894771,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"NkwWdHhnTQ"}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.free.filters.pack2",
    "pack_name":{"en":"Film","nl":"Film","fr":"Film","de":"Film","id":"Film","it":"Film","ja":"フィルム","ko":"필름","pt":"Filme","es":"Filme","ru":"Фотопленка","th":"ฟิล์ม","tr":"Film","vt":"Film","zh":"胶片","zh-tw":"膠片","ja":"フィルム","ar":"فيلم"},
    "pack_description":{"en":"Computationally crafted filters emulating general purpose films. Each filter represents one typical film color variation.","nl":"Computationeel gemaakte filters die universele films emuleren. Elk filter vertegenwoordigt een typische film kleurvariatie.","fr":"Filtres conçus informatiquement émulant des films à usage général. Chaque filtre représente une variation de couleur typique de film.","de":"Rechnerisch generierter Filter, der den Look von herkömmlichen Filmen nachahmt. Jeder Filter gibt eine typische Filmfarbvariation wieder.","id":"Disusun – secara Komputasi filter emulasi film untuk tujuan umum. Setiap filter merupakan salah satu variasi warna film yang khas.","it":"Filtri creati tramite calcoli precisi per emulare accuratamente pellicole per uso generale. Ogni filtro rappresenta una tipica variazione di colore di pellicola.","ja":"汎用的なフィルムをエミュレートする計算され細工されたフィルタ。各フィルタは、1つ典型的なフィルムの色の変化を示します。","ko":"범용 필름을 모방 계산 만들어진 필터. 각 필터는 일반적인 영화 한 색상 변화를 나타냅니다.","pt":"Filtros criados por computação gráfica para emular filmes de uso geral. Cada filtro representa uma variação de cor de filmes padrão.","es":"Filtros computacionalmente diseñados, emulan filmes de propósito general. Cada filtro representa una variación típica del color de filme.","ru":"M Вычислительно-созданные фильтры для преображения общих кадров. Каждый фильтр представляет собой одну типичную фотопленку в цветовых вариациях.","th":"ฟิลเตอร์คอมพิวเตอร์ที่สร้างขึ้นเลียนแบบภาพยนตร์วัตถุประสงค์ทั่วไป กรองแต่ละหมายถึงภาพยนตร์โดยทั่วไปรูปแบบสีหนึ่ง","tr":"Genel-amaçlı filmlerden özenerek, hesaplanarak ortaya çıkarılmış filtreler. Her bir filtre tipik bir film rengi varyasyonunu simgelemektedir.","vt":"Bộ lọc thủ công một cách có chủ ý tạo ra các thước film chung. Mỗi bộ lọc thể hiện một sự thay đổi màu sắc film điển hình","zh":"由数据运算模型得出的胶片模拟滤镜组，每个滤镜模拟一种胶片色彩倾向。","zh-tw":"由數據運算模型得出的膠片模擬濾鏡組，每個濾鏡模擬一種膠片色彩傾向。","ja":"一般的な写真フィルムをコンピューターで再現したフィルター集。それぞれのフィルターが一般的によく利用される特定のフィルムの色調を再現しています。","ar":"مرشحات حسابيا الحرفية محاكاة الأفلام للأغراض العامة. ويمثل كل مرشح واحد نموذجي اختلاف اللون الفيلم."},
    "pack_maker":"Polarr",
    "is_free":1,
    "version":125,
    "preview_images":["f1.jpg","f2.jpg","f3.jpg"],
    "filters":[
      {"name":{"en":"F1 lucky","zh":"F1 高调"},"state":{"temperature":-0.1477752517050991,"tint":0.03929847353036697,"exposure":0.10165638194218896,"gamma":0,"contrast":0.5589477102955505,"highlights":-0.0022734654108477192,"shadows":0.7460214355310166,"whites":-0.0022734654108477192,"blacks":-0.0022734654108477192,"clarity":0,"saturation":-0.08541734329327699,"vibrance":0.4342318934719065,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,15],[100,90],[255,255]],"hue_red":0.10165638194218896,"hue_orange":-0.02305943488145501,"hue_yellow":-0.0022734654108477192,"hue_green":-0.0022734654108477192,"hue_aqua":0.3718739850600845,"hue_blue":-0.37642091588177984,"hue_purple":-0.750568366352712,"hue_magenta":0.2679441377070477,"saturation_red":0.2887301071776551,"saturation_orange":-0.6050665800584605,"saturation_yellow":-0.31406300746995774,"saturation_green":0,"saturation_aqua":0,"saturation_blue":-0.16856122117570638,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":-0.750568366352712,"highlights_saturation":0.01851250405975957,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"VksOHn2Tm"},
      {"name":{"en":"F2 Fujicolor","zh":"F2 富士"},"state":{"temperature":-0.1477752517050991,"tint":0.03929847353036697,"exposure":0.18480025982461834,"gamma":0,"contrast":0.5173757713543359,"highlights":-0.0022734654108477192,"shadows":0.7252354660604092,"whites":-0.0022734654108477192,"blacks":-0.0022734654108477192,"clarity":0,"saturation":-0.0022734654108477192,"vibrance":0.4965898018837285,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,32],[70,100],[255,255]],"hue_red":0.24715816823644032,"hue_orange":-0.02305943488145501,"hue_yellow":-0.0022734654108477192,"hue_green":-0.0022734654108477192,"hue_aqua":0.3510880155894771,"hue_blue":-0.37642091588177984,"hue_purple":-0.4803507632348165,"hue_magenta":0.24715816823644032,"saturation_red":0.2887301071776551,"saturation_orange":-0.1477752517050991,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":-0.16856122117570638,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":-0.750568366352712,"highlights_saturation":0.01851250405975957,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"Nk3OS32pQ"},
      {"name":{"en":"F3 Solaris","zh":"F3 暖色"},"state":{"temperature":0.10165638194218896,"tint":0.03929847353036697,"exposure":0.4758038324131211,"gamma":0,"contrast":0.3926599545306917,"highlights":-0.0022734654108477192,"shadows":0.4342318934719065,"whites":-0.0022734654108477192,"blacks":0.20558622929522574,"clarity":0,"saturation":0.10165638194218896,"vibrance":-0.12698928223449168,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,12],[80,90],[255,255]],"hue_red":0.03929847353036697,"hue_orange":-0.0022734654108477192,"hue_yellow":-0.0022734654108477192,"hue_green":-0.0022734654108477192,"hue_aqua":0.3718739850600845,"hue_blue":-0.0022734654108477192,"hue_purple":-0.0022734654108477192,"hue_magenta":0.2679441377070477,"saturation_red":-0.02305943488145501,"saturation_orange":-0.1062033127638844,"saturation_yellow":-0.0022734654108477192,"saturation_green":-0.0022734654108477192,"saturation_aqua":0.10165638194218896,"saturation_blue":0.10165638194218896,"saturation_purple":0.10165638194218896,"saturation_magenta":-0.0022734654108477192,"luminance_red":-0.0022734654108477192,"luminance_orange":-0.25170509905813576,"luminance_yellow":-0.6882104579408899,"luminance_green":0,"luminance_aqua":0,"luminance_blue":-0.0022734654108477192,"luminance_purple":-1,"luminance_magenta":0,"highlights_hue":-0.937642091588178,"highlights_saturation":0.01851250405975957,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"VJTdB33TQ"},
      {"name":{"en":"F4 Supra","zh":"F4 柯达"},"state":{"temperature":-0.08541734329327699,"tint":0.01851250405975957,"exposure":0.12244235141279636,"gamma":0,"contrast":0.5173757713543359,"highlights":-0.0022734654108477192,"shadows":0.5381617408249431,"whites":-0.0022734654108477192,"blacks":-0.0022734654108477192,"clarity":0,"saturation":-0.04384540435206241,"vibrance":0.30951607664826253,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,22],[80,90],[255,255]],"hue_red":0.2679441377070477,"hue_orange":-0.02305943488145501,"hue_yellow":-0.0022734654108477192,"hue_green":-0.0022734654108477192,"hue_aqua":0.3718739850600845,"hue_blue":-0.1062033127638844,"hue_purple":-0.0022734654108477192,"hue_magenta":0.2679441377070477,"saturation_red":0.2679441377070477,"saturation_orange":-0.1477752517050991,"saturation_yellow":-0.0022734654108477192,"saturation_green":-0.0022734654108477192,"saturation_aqua":-0.0022734654108477192,"saturation_blue":-0.16856122117570638,"saturation_purple":-0.0022734654108477192,"saturation_magenta":-0.0022734654108477192,"luminance_red":-0.0022734654108477192,"luminance_orange":0,"luminance_yellow":-0.0022734654108477192,"luminance_green":0,"luminance_aqua":0,"luminance_blue":-0.0022734654108477192,"luminance_purple":-0.0022734654108477192,"luminance_magenta":0,"highlights_hue":-0.937642091588178,"highlights_saturation":0.01851250405975957,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"4k0_H23T7"},
      {"name":{"en":"F5 Vista","zh":"F5 复古"},"state":{"temperature":0.12244235141279636,"tint":-0.0022734654108477192,"exposure":0.14322832088340376,"gamma":0,"contrast":0.20558622929522574,"highlights":-0.0022734654108477192,"shadows":0.14322832088340376,"whites":-0.0022734654108477192,"blacks":0.14322832088340376,"clarity":0,"saturation":-0.08541734329327699,"vibrance":0.5381617408249431,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,32],[90,90],[165,192],[255,255]],"hue_red":0.060084443000974375,"hue_orange":-0.08541734329327699,"hue_yellow":-0.25170509905813576,"hue_green":-0.08541734329327699,"hue_aqua":-0.0022734654108477192,"hue_blue":-0.33484897694056515,"hue_purple":-0.7089964274114973,"hue_magenta":1,"saturation_red":0.20558622929522574,"saturation_orange":-0.37642091588177984,"saturation_yellow":-0.18934719064631378,"saturation_green":0,"saturation_aqua":0,"saturation_blue":-0.1477752517050991,"saturation_purple":0.4134459240012991,"saturation_magenta":0.4550178629425139,"luminance_red":0.20558622929522574,"luminance_orange":0,"luminance_yellow":-0.0022734654108477192,"luminance_green":0,"luminance_aqua":0,"luminance_blue":-0.6050665800584605,"luminance_purple":1,"luminance_magenta":0,"highlights_hue":-0.750568366352712,"highlights_saturation":-0.0022734654108477192,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"4JyeOHnnp7"},
      {"name":{"en":"F6 Efinity","zh":"F6 反转"},"state":{"temperature":-0.0022734654108477192,"tint":-0.0022734654108477192,"exposure":-0.0022734654108477192,"gamma":0,"contrast":0.20558622929522574,"highlights":-0.0022734654108477192,"shadows":0.14322832088340376,"whites":-0.0022734654108477192,"blacks":-0.0022734654108477192,"clarity":0,"saturation":-0.08541734329327699,"vibrance":0.5381617408249431,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,32],[90,90],[165,192],[255,255]],"hue_red":0.060084443000974375,"hue_orange":0.01851250405975957,"hue_yellow":-0.1062033127638844,"hue_green":-0.0022734654108477192,"hue_aqua":-0.0022734654108477192,"hue_blue":-0.16856122117570638,"hue_purple":1,"hue_magenta":1,"saturation_red":-0.02305943488145501,"saturation_orange":-0.4387788242936018,"saturation_yellow":-0.6674244884702826,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0.4342318934719065,"saturation_purple":0.4134459240012991,"saturation_magenta":0.4550178629425139,"luminance_red":0.4758038324131211,"luminance_orange":0,"luminance_yellow":1,"luminance_green":0,"luminance_aqua":0,"luminance_blue":-0.6050665800584605,"luminance_purple":1,"luminance_magenta":0,"highlights_hue":-0.750568366352712,"highlights_saturation":-0.0022734654108477192,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"4JexOHn2om"},
      {"name":{"en":"F7 Redscale","zh":"F7 红移"},"state":{"temperature":0,"tint":0,"exposure":-0.0022734654108477192,"gamma":0,"contrast":0.03929847353036697,"highlights":-0.1477752517050991,"shadows":0.2679441377070477,"whites":0,"blacks":0.10165638194218896,"clarity":0,"saturation":-0.12698928223449168,"vibrance":0.4758038324131211,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,50],[100,100],[180,205],[255,255]],"hue_red":0.08087041247158178,"hue_orange":0.01851250405975957,"hue_yellow":-0.1062033127638844,"hue_green":0,"hue_aqua":-0.0022734654108477192,"hue_blue":-0.1477752517050991,"hue_purple":0.3303020461188697,"hue_magenta":1,"saturation_red":0.24715816823644032,"saturation_orange":0.3303020461188697,"saturation_yellow":0.4134459240012991,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0.01851250405975957,"saturation_purple":-0.37642091588177984,"saturation_magenta":0.4550178629425139,"luminance_red":0.4758038324131211,"luminance_orange":0,"luminance_yellow":1,"luminance_green":0,"luminance_aqua":-0.0022734654108477192,"luminance_blue":-1,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"4JexOHn3pm"},
      {"name":{"en":"F8 Lomo","zh":"F8 宝丽来"},"state":{"temperature":0,"tint":0,"exposure":0.12244235141279636,"gamma":0,"contrast":0.2679441377070477,"highlights":0,"shadows":0.5381617408249431,"whites":0,"blacks":0,"clarity":0,"saturation":-0.16856122117570638,"vibrance":0.24715816823644032,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,60],[80,90],[255,255]],"hue_red":0.060084443000974375,"hue_orange":-0.0646313738226697,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":-0.12698928223449168,"hue_purple":0,"hue_magenta":0,"saturation_red":0.16401429035401094,"saturation_orange":-0.4595647937642091,"saturation_yellow":0,"saturation_green":0.30951607664826253,"saturation_aqua":0.5381617408249431,"saturation_blue":-0.27249106852874305,"saturation_purple":0,"saturation_magenta":0.22637219876583314,"luminance_red":0.5381617408249431,"luminance_orange":0.4550178629425139,"luminance_yellow":1,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"NJbg_r23pX"},
      {"name":{"en":"F9 Porta","zh":"F9 禄来"},"state":{"temperature":-0.08541734329327699,"tint":0.01851250405975957,"exposure":0.08087041247158178,"gamma":0,"contrast":0.18480025982461834,"highlights":-0.0022734654108477192,"shadows":0.7875933744722312,"whites":0.14322832088340376,"blacks":0.2887301071776551,"clarity":-0.0022734654108477192,"saturation":-0.1062033127638844,"vibrance":0.4134459240012991,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,30],[65,70],[255,255]],"hue_red":0.2679441377070477,"hue_orange":0.01851250405975957,"hue_yellow":-0.0022734654108477192,"hue_green":-0.0022734654108477192,"hue_aqua":0.6420915881779798,"hue_blue":-0.35563494641117244,"hue_purple":-0.29327703799935045,"hue_magenta":0.24715816823644032,"saturation_red":0.2679441377070477,"saturation_orange":0.2679441377070477,"saturation_yellow":0,"saturation_green":-0.0022734654108477192,"saturation_aqua":0,"saturation_blue":-0.0022734654108477192,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":-0.31406300746995774,"highlights_saturation":0.03929847353036697,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"4JGeuSnhpQ"},
      {"name":{"en":"F10 Industrial","zh":"F10 工业"},"state":{"temperature":-0.04384540435206241,"tint":-0.0022734654108477192,"exposure":0.5381617408249431,"gamma":-0.0646313738226697,"contrast":0.060084443000974375,"highlights":-0.0022734654108477192,"shadows":0.22637219876583314,"whites":-0.0022734654108477192,"blacks":0.16401429035401094,"clarity":0,"saturation":-0.1062033127638844,"vibrance":0.30951607664826253,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,25],[65,70],[255,255]],"hue_red":-0.0022734654108477192,"hue_orange":-0.0022734654108477192,"hue_yellow":-0.29327703799935045,"hue_green":0,"hue_aqua":0.6836635271191946,"hue_blue":-0.25170509905813576,"hue_purple":1,"hue_magenta":-0.0022734654108477192,"saturation_red":0.3510880155894771,"saturation_orange":-0.0022734654108477192,"saturation_yellow":-0.0022734654108477192,"saturation_green":-0.0022734654108477192,"saturation_aqua":-0.0022734654108477192,"saturation_blue":0.30951607664826253,"saturation_purple":-0.0022734654108477192,"saturation_magenta":-0.0022734654108477192,"luminance_red":-0.0022734654108477192,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":-0.0022734654108477192,"luminance_blue":-0.0022734654108477192,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":-0.7713543358233192,"highlights_saturation":0.01851250405975957,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"VkmgOS2hTm"},
      {"name":{"en":"FB1 Grey","zh":"FB1 灰阶"},"state":{"temperature":-0.0022734654108477192,"tint":-0.0022734654108477192,"exposure":0,"gamma":0,"contrast":0.22637219876583314,"highlights":0,"shadows":0.24715816823644032,"whites":0.16401429035401094,"blacks":-0.27249106852874305,"clarity":0,"saturation":-1,"vibrance":0.9123091912958752,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.3510880155894771,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"NyNeOrnhaX"},
      {"name":{"en":"FB2 Pan","zh":"FB2 白阶"},"state":{"temperature":-0.0022734654108477192,"tint":-0.0022734654108477192,"exposure":0.22637219876583314,"gamma":0,"contrast":-0.6050665800584605,"highlights":0.3303020461188697,"shadows":-0.04384540435206241,"whites":0.16401429035401094,"blacks":0.10165638194218896,"clarity":0,"saturation":-1,"vibrance":0.12244235141279636,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,30],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":-0.0022734654108477192,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":-0.0022734654108477192,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":-0.0022734654108477192,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.3510880155894771,"denoise":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"chroma_amount":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0},"id":"V1SedB3nT7"}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.purchase.filters.pack5",
    "pack_name":{"en":"Classic Film","nl":"Klassieke Film","fr":"Film classique","de":"Klassischer Film","id":"Klasik  Film","it":"Classic Film","ja":"Mクラシックフィルム","ko":"고전 영화","pt":"Filme Clássico","es":"Filme Clásico","ru":"Классическая Фотопленка","th":"ภาพยนตร์คลาสสิก","tr":"Klasik Film","vt":"Film cổ điển","zh":"传统胶片","zh-tw":"傳統膠片","ar":"الكلاسيكية السينمائي"},
    "pack_description":{"en":"A collection of film emulation filters with strong identity and effects more aggressively tuned than the Film collection.","nl":"Een collectie van film-emulatie filters met sterke identiteit en effecten, meer agressief afgestemd dan de Film collectie.","fr":"Une collection de filtres émulant des films avec des effets et une forte identité, plus affinés intensivement que la collection Film.","de":"Eine Sammlung von Filtern, die den Look von Filmen nachahmen, mit eindrucksvollem Charakter und starken Effekten. Radikalere Anpassungen als mit der Film-Sammlung.","id":"Koleksi filter film emulasi dengan identitas yang kuat dan efek, disetel dengan- lebih agresif daripada koleksi Film.","it":"Una collezione di filtri emulatori di pellicole con forti identità ed effetti, calibrati più aggressivamente rispetto alla collezione Film.","ja":"強いアイデンティティと効果をがあるフィルムエミュレーションフィルタコレクションは、フィルムコレクションより積極的にチューニングされました。","ko":"강한 정체성 필터 및 효과와 영화 에뮬레이션의 컬렉션, 더 필름 컬렉션보다 적극적으로 조정.","pt":"Uma coleção de filtros que emulam filmes de efeitos e cores de caráter forte, com realce mais acentuado do que a coleção Filme.","es":"Una colección de filtros que emulan un un filme clásico, con una fuerte identidad y efectos, más agresivamente afinados que la colección de Filme.","ru":"Kоллекция фильтров для преображения кадров с высокой идентичностью и эффектами, которые настроены более ярко, чем в коллекции Фотопленка.","th":"คอลเลกชันของภาพยนตร์จำลองที่มีตัวกรองตัวตนที่แข็งแกร่งและผลกระทบเชิงรุกมากขึ้นปรับกว่าการเก็บฟิล์ม","tr":"Film-emülasyon filtrelerinin bir koleksiyonu. Film koleksiyonuna göre daha agresif bir şekilde ayarlanmış.  Güçlü efektler.","vt":"Một tập hợp các bộ lọc film với màu sắc mạnh mẽ và hiệu ứng, điều chỉnh nhiều hơn so với bộ Film","zh":"一组有色彩效果更强，风格特点更明显的传统胶片滤镜，适用于各类人像和风光场景。","zh-tw":"一組有色彩效果更強，風格特點更明顯的傳統膠片濾鏡，適用於各類人像和風光場景。","ar":"مجموعة من المرشحات فيلم مضاهاة مع هوية قوية والآثار، وأكثر ضبطها بقوة مما جمع الفيلم."},
    "pack_maker":"Polarr",
    "is_free":0,
    "version":130,
    "preview_images":["cf1.jpg","cf2.jpg","cf3.jpg"],
    "filters":[
      {"name":{"en":"CF1 Crayon","zh":"CF1 蜡质"},"id":"k605md0lik9","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0.29166666666666674,"gamma":-0.2708333333333334,"contrast":-1,"highlights":0.4791666666666665,"shadows":0.46875,"whites":-0.22916666666666657,"blacks":0.23958333333333326,"clarity":0,"saturation":0,"vibrance":0,"curves_red":[[43.53846153846153,0],[119.33653846153845,113.89423076923077],[255,255]],"curves_green":[[14.966346153846153,0],[109.8125,108.4519230769231],[255,255]],"curves_blue":[[0,29.932692307692292],[58.11057692307693,58.110576923076934],[149.2692307692308,139.7451923076923],[255,255]],"curves_all":[[0,0],[54.028846153846146,75.79807692307693],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.19270833333333331,"highlights_saturation":0.0875,"shadows_hue":0,"shadows_saturation":0.0875,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CF2 Flat","zh":"CF2 平滑"},"id":"u8yu6av2t9","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.04166666666666674,"tint":0,"exposure":0.25,"gamma":0.14583333333333326,"contrast":-0.5833333333333333,"highlights":0.33333333333333326,"shadows":0.125,"whites":0.11458333333333326,"blacks":0,"clarity":0,"saturation":-0.15625,"vibrance":0.36458333333333326,"curves_red":[[6.802884615384615,0],[123.41826923076923,131.58173076923077],[255,255]],"curves_green":[[0,34.014423076923094],[123.41826923076924,123.41826923076923],[255,255]],"curves_blue":[[0,36.735576923076906],[142.46634615384616,147.90865384615384],[255,255]],"curves_all":[[0,8.163461538461547],[130.22115384615384,132.94230769230768],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.22916666666666652,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.35416666666666663,"highlights_saturation":0.1125,"shadows_hue":0.20312500000000003,"shadows_saturation":0.1875,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CF3 Iceage","zh":"CF3 冰原"},"id":"qvd1mcuwhfr","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":-0.08333333333333326,"tint":0,"exposure":0.44791666666666674,"gamma":0.25,"contrast":0.23958333333333326,"highlights":-0.4791666666666666,"shadows":-0.25000000000000006,"whites":0,"blacks":0,"clarity":0,"saturation":0,"vibrance":0.5104166666666665,"curves_red":[[0,17.6875],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[21.76923076923077,0],[255,255]],"curves_all":[[0,36.735576923076906],[71.71634615384616,78.51923076923077],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.59375,"highlights_saturation":0.0875,"shadows_hue":0.7083333333333334,"shadows_saturation":0.275,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CF4 Bleach","zh":"CF4 漂染"},"id":"bzf1eylow29","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.09375,"diffuse":0,"temperature":-0.15625,"tint":0.11458333333333326,"exposure":0.4791666666666665,"gamma":0.21875,"contrast":0.5729166666666665,"highlights":0.17708333333333348,"shadows":0.13541666666666652,"whites":-0.10416666666666652,"blacks":-0.3125,"clarity":0.13541666666666674,"saturation":-0.020833333333333426,"vibrance":-0.21875,"curves_red":[[0,44.89903846153845],[73.07692307692307,96.20673076923077],[117.97596153846152,131.58173076923077],[255,253.63942307692307]],"curves_green":[[38.09615384615385,0],[67.63461538461539,77.15865384615384],[165.59615384615384,190.08653846153845],[255,255]],"curves_blue":[[62.58653846153846,0],[92.125,62.19230769230768],[171.03846153846155,139.74519230769232],[255,255]],"curves_all":[[0,66.66826923076923],[104.3701923076923,115.25480769230771],[255,255]],"hue_red":-0.07291666666666657,"hue_orange":-0.09375,"hue_yellow":0.125,"hue_green":0.0625,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.125,"saturation_orange":0.09375,"saturation_yellow":0.04166666666666674,"saturation_green":0,"saturation_aqua":-0.07291666666666657,"saturation_blue":0.3125,"saturation_purple":-0.19791666666666674,"saturation_magenta":-0.15625,"luminance_red":-0.052083333333333426,"luminance_orange":0.08333333333333348,"luminance_yellow":-0.14583333333333343,"luminance_green":0.125,"luminance_aqua":-0.09375,"luminance_blue":-0.052083333333333426,"luminance_purple":-0.11458333333333343,"luminance_magenta":0.11458333333333326,"highlights_hue":0.109375,"highlights_saturation":0.12499999999999999,"shadows_hue":0.671875,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CF5 Gold","zh":"CF5 金黄"},"id":"dzepg2pgb9","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.09375,"diffuse":0,"temperature":0,"tint":0,"exposure":0.4791666666666665,"gamma":0.21875,"contrast":0.5729166666666665,"highlights":0.17708333333333348,"shadows":0.13541666666666652,"whites":-0.10416666666666652,"blacks":-0.3125,"clarity":0.13541666666666674,"saturation":-0.020833333333333426,"vibrance":-0.21875,"curves_red":[[0,14.966346153846132],[117.97596153846152,131.58173076923077],[255,253.63942307692307]],"curves_green":[[0,19.048076923076934],[58.11057692307692,77.15865384615384],[177.84134615384616,180.5625],[255,255]],"curves_blue":[[0,29.932692307692292],[92.125,62.19230769230768],[171.03846153846155,139.74519230769232],[255,255]],"curves_all":[[0,38.09615384615384],[108.45192307692308,117.97596153846155],[255,255]],"hue_red":-0.07291666666666657,"hue_orange":-0.09375,"hue_yellow":0.125,"hue_green":0.0625,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.125,"saturation_orange":0.09375,"saturation_yellow":0.04166666666666674,"saturation_green":0,"saturation_aqua":-0.07291666666666657,"saturation_blue":0.3125,"saturation_purple":-0.19791666666666674,"saturation_magenta":-0.15625,"luminance_red":-0.052083333333333426,"luminance_orange":0.08333333333333348,"luminance_yellow":-0.14583333333333343,"luminance_green":0.125,"luminance_aqua":-0.09375,"luminance_blue":-0.052083333333333426,"luminance_purple":-0.11458333333333343,"luminance_magenta":0.11458333333333326,"highlights_hue":0.109375,"highlights_saturation":0.12499999999999999,"shadows_hue":0.671875,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CF6 Cloudy","zh":"CF6 云"},"id":"mnev41dzpvi","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.09375,"diffuse":0,"temperature":0,"tint":0,"exposure":0.5208333333333333,"gamma":0.15625,"contrast":0.25,"highlights":0.17708333333333348,"shadows":0.13541666666666652,"whites":-0.10416666666666652,"blacks":-0.3125,"clarity":0.13541666666666674,"saturation":-0.375,"vibrance":0.26041666666666674,"curves_red":[[0,20.40865384615384],[128.8605769230769,126.1394230769231],[255,255]],"curves_green":[[0,20.40865384615384],[58.11057692307692,63.55288461538461],[177.84134615384616,180.5625],[255,255]],"curves_blue":[[0,32.65384615384613],[85.32211538461537,82.60096153846155],[255,255]],"curves_all":[[0,23.12980769230768],[255,255]],"hue_red":-0.07291666666666657,"hue_orange":-0.09375,"hue_yellow":0.125,"hue_green":0.0625,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.125,"saturation_orange":0.09375,"saturation_yellow":0.04166666666666674,"saturation_green":0,"saturation_aqua":-0.07291666666666657,"saturation_blue":0.3125,"saturation_purple":-0.19791666666666674,"saturation_magenta":-0.15625,"luminance_red":-0.052083333333333426,"luminance_orange":0.08333333333333348,"luminance_yellow":-0.14583333333333343,"luminance_green":0.125,"luminance_aqua":-0.09375,"luminance_blue":-0.052083333333333426,"luminance_purple":-0.11458333333333343,"luminance_magenta":0.11458333333333326,"highlights_hue":0.078125,"highlights_saturation":0.175,"shadows_hue":0.671875,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CF7 Damp","zh":"CF7 潮湿"},"id":"gmuqymvlsor","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0.14583333333333326,"gamma":0,"contrast":0.46875,"highlights":0.22916666666666674,"shadows":-0.16666666666666657,"whites":0,"blacks":0,"clarity":0,"saturation":-0.16666666666666657,"vibrance":0.125,"curves_red":[[0,20.40865384615384],[128.8605769230769,126.1394230769231],[255,255]],"curves_green":[[0,20.40865384615384],[58.11057692307692,63.55288461538461],[177.84134615384616,180.5625],[255,255]],"curves_blue":[[0,32.65384615384613],[85.32211538461537,82.60096153846155],[255,255]],"curves_all":[[0,23.12980769230768],[255,255]],"hue_red":-0.07291666666666657,"hue_orange":-0.09375,"hue_yellow":-0.052083333333333426,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.125,"saturation_orange":0.09375,"saturation_yellow":0.125,"saturation_green":0.08333333333333326,"saturation_aqua":-0.07291666666666657,"saturation_blue":0.3125,"saturation_purple":-0.19791666666666674,"saturation_magenta":-0.15625,"luminance_red":-0.052083333333333426,"luminance_orange":0.08333333333333348,"luminance_yellow":-0.14583333333333343,"luminance_green":0.125,"luminance_aqua":-0.09375,"luminance_blue":-0.052083333333333426,"luminance_purple":-0.11458333333333343,"luminance_magenta":0.11458333333333326,"highlights_hue":0.6354166666666666,"highlights_saturation":0.0625,"shadows_hue":0.546875,"shadows_saturation":0.275,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CF8 Cherry","zh":"CF8 樱桃"},"id":"dsj7spmbo6r","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0.2604166666666665,"gamma":0,"contrast":0.46875,"highlights":0.22916666666666674,"shadows":-0.16666666666666657,"whites":0,"blacks":0,"clarity":0,"saturation":0,"vibrance":0,"curves_red":[[0,27.211538461538453],[128.8605769230769,126.1394230769231],[255,255]],"curves_green":[[0,21.769230769230774],[58.11057692307692,66.27403846153845],[177.84134615384616,180.5625],[255,255]],"curves_blue":[[0,32.65384615384613],[85.32211538461537,90.76442307692307],[255,255]],"curves_all":[[0,23.12980769230768],[255,255]],"hue_red":-0.07291666666666657,"hue_orange":-0.09375,"hue_yellow":-0.052083333333333426,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.25,"saturation_orange":-0.16666666666666657,"saturation_yellow":0.35416666666666674,"saturation_green":0.08333333333333326,"saturation_aqua":0.09375,"saturation_blue":0.11458333333333326,"saturation_purple":-0.11458333333333343,"saturation_magenta":-0.09375,"luminance_red":0.17708333333333326,"luminance_orange":0.08333333333333348,"luminance_yellow":-0.14583333333333343,"luminance_green":0.125,"luminance_aqua":-0.052083333333333426,"luminance_blue":-0.052083333333333426,"luminance_purple":-0.11458333333333343,"luminance_magenta":0.11458333333333326,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.purchase.filters.pack10",
    "pack_name":{"en":"Vintage Film","nl":"Vintage Film","fr":"Film vintage","de":"Vintage Film","id":"Vintage Film","it":"Vintage Film","ja":"ヴィンテージフィルム","ko":"빈티지 필름","pt":"Filme Vintage","es":"Filme Antiguo","ru":"Винтажная Фотопленка","th":"ภาพยนตร์แนววินเทจ","tr":"Vintage Film","vt":"Film cổ","zh":"复古胶片","zh-tw":"復古膠片","ar":"خمر السينمائي"},
    "pack_description":{"en":"Emulate the look of old-school, faded, vintage photography. Use to give your images a distinguished air, and a beautiful patina.","nl":"Emuleer de look van ouderwetse, vervaagde, vintage fotografie. Gebruik om uw afbeeldingen een gedistingeerd gevoel en een mooie glans te geven.","fr":"Émulez l’apparence de la photographie Old-school, fanée et vintage. Utilisez ce filtre pour donner à vos images un air distingué, et une belle patine.","de":"Stellen Sie den Look von verblichenen, alten Vintage Fotos der alten Schule her. Verwenden Sie diesen Filter, um Ihren Bildern einen besonderen Charakter und eine wunderschöne Patina zu verleihen.","id":"Meniru tampilan old-school, pudar, fotografi vintage. Gunakan untuk memberikan gambar Anda sedikit udara berbeda, dan patina yang indah.","it":"Emula il look della fotografia d’epoca, sfumata, vintage. Usala per dare alle tue immagini un’aria distinta, ed una bellissima patina.","ja":"オールドスクール、色あせ、ヴィンテージ写真の外観にします。画像に異なる雰囲気や美しい古色を与えるために使えます。","ko":"구식, 머 금고, 빈티지 사진의 모양을 모방. 사진 저명한 공기, 아름다운 푸른 녹를 제공하기 위해 사용합니다.","pt":"Emule o aspecto envelhecido, tradicionalista, da fotografia vintage. Use ele para dar as suas imagens uma atmosfera única e uma bela pátina.","es":"Emula el aspecto de la vieja escuela, descolorido, fotografía antigua. Se utiliza para darle a tus imágenes un aire de distinción, y una hermoso patinado.","ru":"Создает олд-скульный вид выцветших старинных фотографий. Придает изображению изысканный воздух  и красивую патину.","th":"เลียนแบบรูปลักษณ์ของโรงเรียนเก่าจางหายไป, ถ่ายภาพวินเทจ ใช้เพื่อให้ภาพของคุณอากาศที่โดดเด่นและคราบที่สวยงาม","tr":"Eski, solmuş vintage fotoğrafçılığı emülasyonu. Resimlerinize farklı bir hava ve güzel bir patina verin.","vt":"Mô phỏng diện mạo của ảnh cổ điển, phai màu. Dùng để mang lại cho hình ảnh của bạn một không khí cũ kĩ và một lớp màu đẹp","zh":"这一组滤镜通过模拟物体色彩的自然衰变,然后加入相应的暖 色调来渲染复古氛围,色调古典而优雅","zh-tw":"這一組濾鏡通過模擬物體色彩的自然衰變,然後加入相應的暖 色調來渲染復古氛圍,色調古典而優雅","ar":"محاكاة نظرة من المدرسة القديمة، وتلاشى، والتصوير الفوتوغرافي خمر. استخدام لاعطاء الصور الخاصة بك على الهواء حاليا، ومظهر جميل."},
    "pack_maker":"Polarr",
    "is_free":0,
    "version":133,
    "preview_images":["tm1.jpg","tm2.jpg","tm3.jpg"],
    "filters":[
      {"name":{"en":"TM1 ","zh":"TM1 "},"id":"VkM--9J6Lg","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.20833333333333326,"tint":0,"exposure":0.4895833333333335,"gamma":-0.3854166666666665,"contrast":0.7291666666666667,"highlights":-0.17708333333333343,"shadows":0.23958333333333326,"whites":-0.23958333333333343,"blacks":0.25,"clarity":0,"saturation":0.09375,"vibrance":-0.20833333333333343,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,50.34134615384616],[255,255]],"hue_red":0.30208333333333326,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0.375,"saturation_red":0.5208333333333335,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":-0.07291666666666657,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.6197916666666667,"shadows_saturation":0.7,"balance":0,"sharpen":0.21875,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.6979166666666667,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0.5729166666666667,"vignette_size":1,"vignette_exposure":0.14583333333333348,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"TM2 ","zh":"TM2 "},"id":"VkL-Z9kTUx","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.22916666666666652,"diffuse":0,"temperature":0.20833333333333326,"tint":0,"exposure":0.25,"gamma":-0.35416666666666674,"contrast":0.14583333333333348,"highlights":0.46875,"shadows":0.23958333333333326,"whites":0.19791666666666652,"blacks":0.25,"clarity":0,"saturation":0.14583333333333326,"vibrance":0.47916666666666674,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,47.62019230769232],[255,255]],"hue_red":0.30208333333333326,"hue_orange":0,"hue_yellow":0.6770833333333333,"hue_green":1,"hue_aqua":-0.27083333333333326,"hue_blue":0,"hue_purple":0,"hue_magenta":0.375,"saturation_red":0.5208333333333335,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":-0.07291666666666657,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.6197916666666667,"shadows_saturation":0.7,"balance":0,"sharpen":0.21875,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.65625,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0.5729166666666667,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"TM3 ","zh":"TM3 "},"id":"NySWWcypLe","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.14583333333333326,"diffuse":0,"temperature":0,"tint":0,"exposure":0.25,"gamma":-0.23958333333333348,"contrast":0.5104166666666667,"highlights":0.32291666666666674,"shadows":-0.08333333333333343,"whites":0.15625,"blacks":-0.28125,"clarity":0.15625,"saturation":0,"vibrance":0.25,"curves_red":[[0,0],[130.22115384615387,122.05769230769232],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,40.81730769230768],[255,255]],"hue_red":0.40625,"hue_orange":0,"hue_yellow":0.36458333333333326,"hue_green":0.71875,"hue_aqua":-0.3333333333333334,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.17708333333333326,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0.13541666666666674,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.16145833333333331,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":-0.09375,"vignette_amount":-0.6770833333333335,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0.46875,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"TM4 ","zh":"TM4 "},"id":"4yNbb9yaUe","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.27083333333333326,"diffuse":0,"temperature":-0.0625,"tint":-0.056376857179862405,"exposure":0.08333333333333348,"gamma":-0.07291666666666657,"contrast":0.5357004089033612,"highlights":0.5416666666666667,"shadows":-0.3524154902214742,"whites":0,"blacks":0.5357004089033612,"clarity":0,"saturation":0,"vibrance":0.3876810923825553,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[116.61538461538461,116.61538461538464],[198.25,190.08653846153845],[255,255]],"curves_all":[[0,35.44230769230768],[94.84615384615385,101.64903846153845],[255,255]],"hue_red":0.34375,"hue_orange":-0.1875,"hue_yellow":0.5104166666666665,"hue_green":0.8229166666666667,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0.3506762632523539,"saturation_red":0.5208333333333333,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0.2211593612966487,"saturation_aqua":0.2581641904268501,"saturation_blue":0.12864728847114493,"saturation_purple":0.16565211760134635,"saturation_magenta":0.16565211760134635,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.14583333333333334,"highlights_saturation":0.6125,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.05463763021074208,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.6875,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.17708333333333348,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"TM5 ","zh":"TM5 "},"id":"41mbZcJ68x","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.13541666666666674,"diffuse":0.02083333333333337,"temperature":0,"tint":0,"exposure":0.10416666666666674,"gamma":0,"contrast":0.3645833333333335,"highlights":-0.13541666666666674,"shadows":0,"whites":0.1875,"blacks":0.19791666666666674,"clarity":0,"saturation":0,"vibrance":0.39583333333333326,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,21.769230769230774],[69.52403846153847,74.49038461538461],[192,210],[255,255]],"hue_red":0.46875,"hue_orange":0,"hue_yellow":-0.625,"hue_green":0.3645833333333335,"hue_aqua":0,"hue_blue":-0.46875,"hue_purple":0,"hue_magenta":0.125,"saturation_red":0.14583333333333326,"saturation_orange":0.20833333333333326,"saturation_yellow":0.13541666666666674,"saturation_green":0.15625,"saturation_aqua":0.2581641904268501,"saturation_blue":0,"saturation_purple":0.16666666666666674,"saturation_magenta":-0.36458333333333326,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-1,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.23958333333333326,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"TM6 ","zh":"TM6 "},"id":"NJbbWq1aUx","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.4375,"tint":0,"exposure":0.4895833333333335,"gamma":-0.09374999999999994,"contrast":1,"highlights":-0.34375000000000006,"shadows":0.17708333333333326,"whites":-0.48958333333333326,"blacks":-0.20833333333333343,"clarity":0,"saturation":-0.5208333333333333,"vibrance":0,"curves_red":[[0,0],[101.64903846153847,100.28846153846155],[207.77403846153845,214.5769230769231],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[112.53365384615384,111.1730769230769],[203.6923076923077,194.16826923076923],[255,255]],"curves_all":[[0,43.53846153846155],[255,255]],"hue_red":0.30208333333333326,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":-0.40625,"hue_purple":0,"hue_magenta":0.375,"saturation_red":0.5208333333333335,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":-0.07291666666666657,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.6197916666666667,"shadows_saturation":0.7,"balance":0,"sharpen":0.21875,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.6979166666666667,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0.5729166666666667,"vignette_size":1,"vignette_exposure":0.14583333333333348,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"TM7 ","zh":"TM7 "},"id":"4JxbZq1pUe","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.6979166666666667,"diffuse":0,"temperature":0,"tint":0,"exposure":0.4270833333333335,"gamma":-0.3229166666666666,"contrast":0.6145833333333333,"highlights":-0.16666666666666674,"shadows":0.17708333333333326,"whites":0.13541666666666674,"blacks":0.625,"clarity":0,"saturation":-1,"vibrance":0.29516901955705155,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,54.42307692307696],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0.07291666666666674,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.25,"highlights_saturation":0.825,"shadows_hue":0.10416666666666663,"shadows_saturation":0.65,"balance":0,"sharpen":0.06770833333333337,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":-0.0008696134845601611,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"TM8 ","zh":"TM8 "},"id":"EkZ-5yTLx","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.21875,"tint":0,"exposure":0.25,"gamma":-0.40625,"contrast":0.14583333333333348,"highlights":-0.17708333333333343,"shadows":0.23958333333333326,"whites":-0.23958333333333343,"blacks":0.25,"clarity":0,"saturation":0,"vibrance":0.47916666666666674,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,50.34134615384616],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0.375,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.6197916666666667,"shadows_saturation":0.7,"balance":0,"sharpen":0.21875,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.6979166666666667,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0.5729166666666667,"vignette_size":1,"vignette_exposure":0.14583333333333348,"denoise":0,"chroma_amount":0}}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.purchase.filters.pack7",
    "pack_name":{"en":"Infrared Film","nl":"Infrarood Film","fr":"Film infrarouge","de":"Infrarotfilm","id":"Infrared Film","it":"Film Infrarosso","ja":"赤外線フィルム","ko":"적외선 필름","pt":"Filme Infravermelho","es":"Filme Infrarrojo","ru":"Инфракрасная Фотопленка","th":"ฟิล์มอินฟาเรด","tr":"İnfrared Film","vt":"Film tương phản","zh":"红外","zh-tw":"紅外","ar":"الأشعة تحت الحمراء السينمائي"},
    "pack_description":{"en":"Simulates the look of infrared-sensitive film cameras. Create dramatic landscapes with exquisite and unusual contrast.","nl":"Simuleert de look van infraroodgevoelige film camera's. Creëer dramatische landschappen met prachtige en ongebruikelijke contrasten.","fr":"Simule l’aspect des caméras de cinéma sensibles à l’infrarouge. Permet de créer des paysages spectaculaires avec un contraste exquis et inédit.","de":"Simuliert den Look einer infrarotempfindlichen Kamera. Kreieren Sie dramatische Landschaften und ungewöhnliche Kontraste.","id":"Mensimulasikan tampilan kamera film inframerah-sensitif. Menciptakan pemandangan dramatis dengan kontras yang indah dan tidak biasa.","it":"Simula il look delle fotocamere ad infrarosso. Crea panorami drammatici dal contrasto squisito e inusuale.","ja":"赤外線感受性フィルムカメラのような外観にします。絶妙で、珍しいコントラストの劇的な風景を作ります。","ko":"적외선에 민감한 영화 카메라의 모양을 시뮬레이션합니다. 절묘한 특이한 대비 극적인 풍경을 만듭니다.","pt":"Simula o aspecto de filmes de câmeras sensíveis ao infravermelho. Crie paisagens expressivas com belíssimos e inusitados contrastes.","es":"Simula el aspecto de las cámaras de filme sensible al infrarrojo. Crea paisajes dramáticos con un contraste exquisito e inusual.","ru":"Имитирует вид ИК-чувствительных пленочных фотоаппаратов. Создает драматические пейзажи с изысканным и необычным контрастом.","th":"จำลองรูปลักษณ์ของกล้องถ่ายภาพยนตร์อินฟราเรดที่สำคัญ สร้างภูมิทัศน์ที่น่าทึ่งมีความคมชัดสวยงามและผิดปกติ","tr":"İnfrarede duyarlı film kameraları görünüşünü taklit eder. Harika ve sıradışı kontrastlarla dramatik bir manzara yaratın.","vt":"Mô phỏng giao diện máy ảnh phim hồng ngoại nhạy cảm. Tạo ra cảnh quan ấn tượng với độ tương phản tinh tế khác thường","zh":"这一组滤镜通过模拟红外照片的色彩,打造出离奇而又梦 幻的色彩氛围,尤其适合表现风光照片,特立独行的你不容错过。","zh-tw":"這一組濾鏡通過模擬紅外照片的色彩,打造出離奇而又夢 幻的色彩氛圍,尤其適合表現風光照片,特立獨行的你不容錯過。","ar":"يحاكي مظهر كاميرات السينما الأشعة تحت الحمراء الحساسة. إنشاء المناظر الطبيعية الخلابة مع تباين رائعة وغير عادية."},
    "pack_maker":"Polarr",
    "is_free":0,
    "version":133,
    "preview_images":["if1.jpg","if2.jpg","if3.jpg"],
    "filters":[
      {"name":{"en":"IF1 ","zh":"IF1 "},"id":"EkUsnhXAUe","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.20833333333333348,"tint":0,"exposure":0.33333333333333326,"gamma":0,"contrast":-0.53125,"highlights":0.5208333333333335,"shadows":-0.27083333333333326,"whites":0.1875,"blacks":-0.28125,"clarity":0,"saturation":0,"vibrance":0.23958333333333326,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":-0.5208333333333335,"hue_green":-0.8541666666666667,"hue_aqua":-0.3854166666666666,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0.3125,"saturation_green":0.3125,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"IF2 ","zh":"IF2 "},"id":"Nyri237CLl","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.18415453216644706,"diffuse":0,"temperature":-0.16666666666666657,"tint":0,"exposure":0,"gamma":0.11014487390604422,"contrast":0.5357004089033612,"highlights":0.2211593612966487,"shadows":-0.3524154902214742,"whites":0,"blacks":0.5357004089033612,"clarity":0,"saturation":0,"vibrance":0.3876810923825553,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,30],[255,255]],"hue_red":0.2211593612966487,"hue_orange":-0.0008696134845601611,"hue_yellow":-0.47916666666666674,"hue_green":-0.7083333333333335,"hue_aqua":-0.125,"hue_blue":-0.5416666666666665,"hue_purple":0,"hue_magenta":0.3506762632523539,"saturation_red":0.0916424593409435,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0.2211593612966487,"saturation_aqua":-0.36458333333333326,"saturation_blue":-0.59375,"saturation_purple":0.16565211760134635,"saturation_magenta":0.16565211760134635,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.2211593612966487,"shadows_saturation":0.27666660499195084,"balance":0,"sharpen":0.05463763021074208,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.14714970303624564,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"IF3 ","zh":"IF3 "},"id":"E1Ejnn7CUg","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.125,"diffuse":0,"temperature":0.40625,"tint":0,"exposure":0.44791666666666674,"gamma":-0.28125,"contrast":0.13541666666666674,"highlights":0.6875,"shadows":-0.3125,"whites":-0.30208333333333326,"blacks":-0.3541666666666666,"clarity":0,"saturation":-0.3229166666666666,"vibrance":0.5729166666666667,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,42.17788461538461],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":-0.53125,"hue_green":-0.5729166666666667,"hue_aqua":-0.4583333333333334,"hue_blue":0.6666666666666665,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.6197916666666666,"shadows_saturation":0.375,"balance":0,"sharpen":0.078125,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.40625,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0.14583333333333326,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"IF4 ","zh":"IF4 "},"id":"NyXsn2XAIx","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.35416666666666674,"tint":0,"exposure":0.25,"gamma":-0.40625,"contrast":0.14583333333333348,"highlights":-0.17708333333333343,"shadows":0.23958333333333326,"whites":-0.23958333333333343,"blacks":0.25,"clarity":0,"saturation":0,"vibrance":0.47916666666666674,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,50.34134615384616],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":-0.71875,"hue_green":-0.875,"hue_aqua":-0.40624999999999994,"hue_blue":-0.4166666666666666,"hue_purple":0,"hue_magenta":0.375,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0.3854166666666665,"saturation_green":0.6145833333333333,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.6197916666666667,"shadows_saturation":0.7,"balance":0,"sharpen":0.21875,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.6979166666666667,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0.5729166666666667,"vignette_size":1,"vignette_exposure":0.14583333333333348,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"IF5 ","zh":"IF5 "},"id":"NJzi2hX0Lg","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":-0.010416666666666519,"diffuse":0,"temperature":0.09375,"tint":0,"exposure":0.1875,"gamma":0,"contrast":0.4166666666666665,"highlights":-0.21875,"shadows":-0.26041666666666674,"whites":0.14583333333333326,"blacks":0.11458333333333326,"clarity":0,"saturation":0,"vibrance":0.3125,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[124.77884615384615,122.05769230769232],[217.29807692307693,192.8076923076923],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":-0.6458333333333335,"hue_green":-0.5,"hue_aqua":-0.3229166666666666,"hue_blue":-0.31249999999999994,"hue_purple":0,"hue_magenta":0,"saturation_red":0.08333333333333348,"saturation_orange":0.10416666666666674,"saturation_yellow":0.13541666666666674,"saturation_green":0.14583333333333326,"saturation_aqua":-0.7604166666666667,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.202656946731548,"shadows_saturation":0.29516901955705155,"balance":0,"sharpen":0.140625,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.15625,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"IF6 ","zh":"IF6 "},"id":"Ekbo3370Ue","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.20833333333333348,"tint":0,"exposure":0.32291666666666674,"gamma":-0.15625,"contrast":0.7083333333333335,"highlights":0.5208333333333335,"shadows":-0.27083333333333326,"whites":0.1875,"blacks":-0.28125,"clarity":0,"saturation":0,"vibrance":0.23958333333333326,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,39.456730769230774],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":-0.5208333333333335,"hue_green":-0.8541666666666667,"hue_aqua":-0.3854166666666666,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0.3125,"saturation_green":0.3125,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.7291666666666667,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.45833333333333326,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"IF7 ","zh":"IF7 "},"id":"Vyls22mAUe","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.20833333333333326,"diffuse":0,"temperature":0.625,"tint":-0.3229166666666666,"exposure":0.02083333333333326,"gamma":-0.10416666666666657,"contrast":0.29166666666666674,"highlights":0.4166666666666665,"shadows":-0.2604166666666666,"whites":-0.3541666666666666,"blacks":0.4375,"clarity":0,"saturation":0.13541666666666674,"vibrance":0.5208333333333333,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,39.456730769230774],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":-0.6041666666666667,"hue_green":-1,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":-0.26041666666666674,"saturation_green":-1,"saturation_aqua":-0.7708333333333333,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0.27083333333333326,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.6614583333333334,"shadows_saturation":0.7375,"balance":0,"sharpen":0.28125,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.47916666666666674,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0.5208333333333333,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"IF8 ","zh":"IF8 "},"id":"41jhhQ0Ix","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.4479166666666665,"tint":0,"exposure":0.33333333333333326,"gamma":0,"contrast":-0.53125,"highlights":0.5208333333333335,"shadows":-0.27083333333333326,"whites":0.1875,"blacks":-0.28125,"clarity":0,"saturation":-0.375,"vibrance":0.23958333333333326,"curves_red":[[0,0],[100.28846153846155,98.92788461538464],[187.3653846153846,206.41346153846155],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":-0.5208333333333335,"hue_green":-0.8541666666666667,"hue_aqua":-0.5104166666666665,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":-0.5104166666666667,"saturation_yellow":0,"saturation_green":0.3125,"saturation_aqua":0.39583333333333326,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0.8020833333333333,"luminance_orange":0.48958333333333326,"luminance_yellow":0.5833333333333333,"luminance_green":0.6145833333333335,"luminance_aqua":-0.75,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.purchase.filters.pack4",
    "pack_name":{"en":"Golden Age","nl":"Gouden Eeuw","fr":"Âge d'or","de":"Goldenes Zeitalter","id":"Zaman keemasan","it":"Età dell’Oro","ja":"黄金時代","ko":"황금 시대","pt":"Era de Ouro","es":"Era Dorada","ru":"Золотой Век","th":"โกลเด้นอายุ","tr":"Altın Çağ","vt":"Thời hoàng kim","zh":"黄金时代","zh-tw":"黃金時代","ar":"العصر الذهبي"},
    "pack_description":{"en":"A comprehensive black and white filter pack including signature looks of classic black and white films.","nl":"Een uitgebreid zwart-wit filterpakket dat de signature looks van klassieke zwart-wit films bevat.","fr":"Un pack complet de filtres noir et blanc, comprenant les apparences de signature des films classiques en noir et blanc.","de":"Ein umfangreiches Schwarz-Weiß Filterpaket, inklusive typischer Looks aus klassischen Schwarz-Weiß-Filmen.","id":"Sebuah filter paket hitam-putih yang luas termasuk tanda terlihat dari film hitam-putih klasik.","it":"Un esteso pacchetto di filtri in bianco e nero contenente alcuni dei più distintivi look di film in bianco e nero.","ja":"クラシックな白黒フィルムのシグニチャルックを含む総合的な白黒フィルタパック。","ko":"서명을 포함하는 포괄적 인 흑백 필터 팩은 고전 흑백 막으로 보인다.","pt":"Um pacote com extensas opções de filtros preto e branco, incluindo visuais autorais ou filmes preto e branco clássicos","es":"Un paquete integral de filtros en blanco y negro que incluye estilos propios de los filmes clásicos en blanco y negro.","ru":"Комплексный черно-белый фильтр включает в себя вид классических черно-белых снимков.","th":"แพ็คตัวกรองสีขาวสีดำและครบวงจรรวมทั้งลายเซ็นลักษณะของภาพยนตร์ขาวดำและคลาสสิก","tr":"Klasik siyah-beyaz filmlerin imzasını taşıyan kapsamlı bir siyah-beyaz filtre paketi.","vt":"Một gói bộ lọc trắng - đen bao gồm các bộ ảnh đen trắng kinh điểm của các bộ phim trắng - đen cổ điển","zh":"一组完整的，覆盖主要黑白胶片质感和光效动态的滤镜集。","zh-tw":"一組完整的，覆蓋主要黑白膠片質感和光效動態的濾鏡集。","ar":"علبة فلتر الأسود والأبيض شاملة بما في ذلك توقيع تبدو من الأفلام بالأسود والأبيض الكلاسيكي."},
    "pack_maker":"Polarr",
    "is_free":0,
    "version":130,
    "preview_images":["ga1.jpg","ga2.jpg","ga3.jpg"],
    "filters":[
      {"name":{"en":"G1 Drama","zh":"G1 强烈"},"id":"4fw0ij2a9k9","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0.75,"gamma":-0.28125,"contrast":0.5104166666666667,"highlights":-0.53125,"shadows":-1,"whites":0,"blacks":0,"clarity":0.4791666666666665,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,40.81730769230768],[78.51923076923076,89.40384615384619],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.25520833333333337,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"G2 Intense","zh":"G2 质感"},"id":"yqjn5ovfgvi","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0.3333333333333335,"gamma":0.33333333333333326,"contrast":1,"highlights":0.09375,"shadows":0.25,"whites":0,"blacks":0,"clarity":0.4166666666666665,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.48958333333333337,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-1,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"G3 Burn","zh":"G3 燃烧"},"id":"jtyv87low29","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":-0.45833333333333326,"tint":0,"exposure":-0.5416666666666665,"gamma":0,"contrast":0.6979166666666667,"highlights":0,"shadows":0,"whites":0.5,"blacks":0.5,"clarity":0.19791666666666674,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,63.94711538461539],[126.13942307692307,149.26923076923077],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"G4 Washed","zh":"G4 漂洗"},"id":"4qhf78sq0k9","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0,"gamma":0,"contrast":0.47916666666666674,"highlights":0.46875,"shadows":0,"whites":0,"blacks":0,"clarity":0,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,93.87980769230768],[120.69711538461539,130.22115384615387],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"G5 Contrast","zh":"G5 对比"},"id":"dn04ihsq0k9","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.3229166666666665,"diffuse":0,"temperature":0,"tint":0,"exposure":0.625,"gamma":0,"contrast":0.6354166666666665,"highlights":0,"shadows":0,"whites":0,"blacks":0,"clarity":0,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[161.5144230769231,164.2355769230769],[255,233.23076923076925]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"G6 Crystal","zh":"G6 透"},"id":"ixdhl1sjor","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0.4791666666666665,"gamma":0,"contrast":-0.4895833333333334,"highlights":-0.5104166666666665,"shadows":0.46875,"whites":-0.46875000000000006,"blacks":0.46875,"clarity":0.46875,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,10.884615384615387],[123.41826923076923,124.77884615384613],[255,255]],"curves_all":[[0,65.30769230769232],[60.83173076923078,82.60096153846155],[162.875,164.2355769230769],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.24479166666666669,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.01041666666666674,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"G7 Fuzzy","zh":"G7 平坦"},"id":"6eoadtb7qfr","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0.484375,"temperature":0,"tint":0,"exposure":-0.5104166666666665,"gamma":0.5,"contrast":-0.4895833333333334,"highlights":0.29166666666666674,"shadows":0.5625,"whites":-0.010416666666666574,"blacks":-0.02083333333333326,"clarity":0.47916666666666674,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,34.014423076923094],[74.4375,82.60096153846152],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.22916666666666666,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.5520833333333333,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"G8 Classic","zh":"G8 经典"},"id":"6utmdyrpb9","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0,"gamma":0,"contrast":0.25,"highlights":-0.4166666666666666,"shadows":0.4791666666666665,"whites":0.23958333333333348,"blacks":0.2708333333333335,"clarity":0.2708333333333335,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,34.014423076923094],[74.4375,82.60096153846152],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.22916666666666666,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.5520833333333333,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.purchase.filters.pack12",
    "pack_name":{"en":"Decay","nl":"Verval","fr":"Décomposition","de":"Zerfall","id":"Kerusakan","it":"Decadimento","ja":"ディケイ","ko":"부식","pt":"Decadente","es":"Descomposición","ru":"Распад","th":"ผุ","tr":"Çürüme","vt":"Sự suy tàn","zh":"腐蚀","zh-tw":"腐蝕","ar":"الاضمحلال"},
    "pack_description":{"en":"Create dramatic photos with black-and-white filters tinted with a hint of color. This collection emulates large-format camera and the days of silver-plated photography.","nl":"Creëer dramatische foto's met zwart-wit filters getint met een vleugje kleur. Deze collectie emuleert grootformaat camera's en de dagen van verzilverde fotografie.","fr":"Créez des photos spectaculaires avec des filtres en noir et blanc teintés avec un soupçon de couleur. Cette collection émule la caméra grand format et l’époque de la photographie argentée.","de":"Kreieren Sie dramatische Fotos mit Schwarz-Weiß-Filtern, die einen Hauch von Farbe haben. Diese Sammlung ahmt Großformatkameras und Silberfotografie nach.","id":"Buat foto dramatis dengan filter hitam-putih berwarna dengan petunjuk warna. Koleksi ini mengemulasi kamera berformat besar dan hari-hari fotografi yang berlapis perak.","it":"Crea foto impressionanti con questi filtri in bianco e nero leggermente tinti con una punta di colore. Questa collezione emula fotocamere a formato largo nei giorni della fotografia patinata in argento.","ja":"少しの色を使用し、着色した白黒フィルタの劇的な写真を作成します。このコレクションは、大判カメラやシルバープレート写真の頃をエミュレートします。","ko":"색상의 힌트와 착색 흑백 필터 극적인 사진을 만듭니다. 이 컬렉션은 와이드 스크린 카메라와 실버 도금 사진의 일을 에뮬레이트합니다.","pt":"Crie fotos dramáticas em preto e branco com manchas em cores. Essa coleção emula a foto das clássicas câmeras grandes e os dias da fotografia com sais de prata.","es":"Crea dramáticas fotografías con filtros en blanco y negro teñidos con un toque de color. Esta colección emula una cámara de gran formato y los días de la fotografía plateada.","ru":"Создает драматические фотографии благодаря черно-белому фильтру с цветной тонировкой. Эта коллекция имитирует широкоформатную камеру и возвращает времена посеребренных снимков.","th":"สร้างภาพที่น่าทึ่งกับฟิลเตอร์สีขาวสีดำและสีกับคำใบ้ของสี คอลเลกชันนี้จำลองกล้องหน้าจอกว้างและวันของการถ่ายภาพสีเงินชุบ","tr":"Renklerle hafifçe tonlanmış siyah ve beyaz filtreler ile dramatik fotoğraflar oluşturun. Bu koleksiyon geniş-formatlı kamera ve gümüş plakalı fotoğrafçılık günlerini taklid eder.","vt":"Tạo ra các bức ảnh ấn tượng với bộ lọc trắng - đen hòa với một chút màu. Bộ sưu tập này giả lập máy ảnh định dạng lớn và thời kì của nhiếp ảnh mạ bạc","zh":"一组专为打造片色效果和银板效果黑白片设计的滤镜。","zh-tw":"一組專為打造片色效果和銀板效果黑白片設計的濾鏡。","ar":"إنشاء الصور المثيرة مع مرشحات الأسود والأبيض ملون مع اشارة اللون. هذه المجموعة يحاكي كاميرا كبيرة الحجم وأيام التصوير مطلية بالفضة."},
    "pack_maker":"Polarr",
    "is_free":0,
    "version":133,
    "preview_images":["dc1.jpg","dc2.jpg","dc3.jpg"],
    "filters":[
      {"name":{"en":"DC1 ","zh":"DC1 "},"id":"VyLrQqLrwe","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.20833333333333326,"diffuse":0,"temperature":-0.08888888888888896,"tint":0.04444444444444451,"exposure":0.11111111111111094,"gamma":-0.11111111111111116,"contrast":-0.2888888888888888,"highlights":-0.06666666666666665,"shadows":0.1777777777777776,"whites":-0.1333333333333333,"blacks":-0.1777777777777778,"clarity":-0.1777777777777778,"saturation":-1,"vibrance":-1,"curves_red":[[0,46.25961538461539],[81.5473901098901,99.17533359497645],[165.22959183673467,158.72448979591837],[255,255]],"curves_green":[[0,0],[74.15816326530611,81.96428571428572],[188.64795918367346,197.75510204081633],[239.3877551020408,255]],"curves_blue":[[15.612244897959183,0],[67.65306122448979,63.750000000000014],[201.65816326530611,213.36734693877548],[255,255]],"curves_all":[[0,23.418367346938773],[110.58673469387755,115.79081632653063],[165.2295918367347,192.55102040816328],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.3555555555555556,"saturation_orange":0.288888888888889,"saturation_yellow":0.1333333333333333,"saturation_green":0,"saturation_aqua":-0.08888888888888896,"saturation_blue":-0.19999999999999996,"saturation_purple":-0.1333333333333333,"saturation_magenta":-0.2888888888888888,"luminance_red":-0.1555555555555555,"luminance_orange":0,"luminance_yellow":0.11111111111111116,"luminance_green":0.1333333333333333,"luminance_aqua":0.06666666666666665,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.3333333333333334,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0.5208333333333333,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"DC2 ","zh":"DC2 "},"id":"N1BHmqLSPl","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.20833333333333326,"diffuse":0,"temperature":-0.0022734654108477192,"tint":-0.0022734654108477192,"exposure":0.2679441377070477,"gamma":0,"contrast":0.4550178629425139,"highlights":-0.0022734654108477192,"shadows":0.24715816823644032,"whites":-0.0022734654108477192,"blacks":-0.0022734654108477192,"clarity":0,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,32.65384615384613],[77.15865384615385,83.96153846153845],[255,255]],"curves_all":[[0,12],[80,90],[255,255]],"hue_red":0.01851250405975957,"hue_orange":-0.0022734654108477192,"hue_yellow":-0.27249106852874305,"hue_green":-0.0022734654108477192,"hue_aqua":-0.12698928223449168,"hue_blue":-0.1062033127638844,"hue_purple":-0.21013316011692107,"hue_magenta":0.14322832088340376,"saturation_red":-0.1477752517050991,"saturation_orange":-0.31406300746995774,"saturation_yellow":-0.25170509905813576,"saturation_green":-0.0022734654108477192,"saturation_aqua":0.4550178629425139,"saturation_blue":-0.21013316011692107,"saturation_purple":0.4342318934719065,"saturation_magenta":0.24715816823644032,"luminance_red":-0.0022734654108477192,"luminance_orange":0,"luminance_yellow":-0.0022734654108477192,"luminance_green":0,"luminance_aqua":0,"luminance_blue":-0.0022734654108477192,"luminance_purple":-0.0022734654108477192,"luminance_magenta":0,"highlights_hue":-0.8752841831763559,"highlights_saturation":0.01851250405975957,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0.5208333333333333,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"DC3 ","zh":"DC3 "},"id":"VyNS79IHPl","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.20833333333333326,"diffuse":0,"temperature":0.060084443000974375,"tint":0.01851250405975957,"exposure":0.3303020461188697,"gamma":0,"contrast":0.16401429035401094,"highlights":0,"shadows":0.16401429035401094,"whites":0.3303020461188697,"blacks":0,"clarity":-0.1477752517050991,"saturation":-1,"vibrance":-1,"curves_red":[[0,9.524038461538453],[255,255]],"curves_green":[[0,21.769230769230774],[58.11057692307693,66.27403846153845],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,20],[30,45],[255,255]],"hue_red":-0.0022734654108477192,"hue_orange":0,"hue_yellow":-0.0022734654108477192,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":-0.0022734654108477192,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":-0.0022734654108477192,"luminance_green":0,"luminance_aqua":0.2679441377070477,"luminance_blue":0.4342318934719065,"luminance_purple":0.3926599545306917,"luminance_magenta":0.2679441377070477,"highlights_hue":-0.0022734654108477192,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":-0.0022734654108477192,"balance":-0.0022734654108477192,"sharpen":0.20558622929522574,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0.5208333333333333,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"DC4 ","zh":"DC4 "},"id":"NJXHX5IBDl","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.20833333333333326,"diffuse":0,"temperature":-0.0022734654108477192,"tint":-0.0022734654108477192,"exposure":0,"gamma":0,"contrast":0.22637219876583314,"highlights":0,"shadows":0.24715816823644032,"whites":0.16401429035401094,"blacks":-0.27249106852874305,"clarity":0,"saturation":-1,"vibrance":-1,"curves_red":[[0,12.24519230769232],[255,255]],"curves_green":[[0,8.163461538461547],[105.73076923076923,108.45192307692307],[255,255]],"curves_blue":[[13.60576923076923,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.3510880155894771,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0.5208333333333333,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"DC5 ","zh":"DC5 "},"id":"41fS7cLBvg","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.20833333333333326,"diffuse":0,"temperature":-0.0022734654108477192,"tint":-0.0022734654108477192,"exposure":0.22637219876583314,"gamma":0,"contrast":-0.6050665800584605,"highlights":0.3303020461188697,"shadows":-0.04384540435206241,"whites":0.16401429035401094,"blacks":0.10165638194218896,"clarity":0,"saturation":-1,"vibrance":-1,"curves_red":[[0,17.6875],[83.96153846153845,89.40384615384613],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[10.884615384615385,0],[255,255]],"curves_all":[[0,30],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":-0.0022734654108477192,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":-0.0022734654108477192,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":-0.0022734654108477192,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.3510880155894771,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0.5208333333333333,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"DC6 ","zh":"DC6 "},"id":"E1-B79Lrvx","state":{"local_adjustments":[{"type":"gradient","startPoint":[0.4666666666666667,-0.6583333333333333],"endPoint":[0,0.5],"adjustments":{"exposure":0.5,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.13541666666666652,"diffuse":0.28125,"temperature":0,"tint":0,"exposure":0.11458333333333326,"gamma":0,"contrast":0,"highlights":-0.7395833333333333,"shadows":0,"whites":0,"blacks":0.11458333333333326,"clarity":0,"saturation":-1,"vibrance":-1,"curves_red":[[0,31.293269230769226],[88.04326923076923,96.20673076923077],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,24.490384615384613],[161.51442307692307,184.64423076923077],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0.30208333333333326,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":-0.19791666666666674,"saturation_orange":-0.22916666666666657,"saturation_yellow":0.28125,"saturation_green":-0.3541666666666666,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0.3333333333333335,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.3333333333333334,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0.20833333333333348,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"DC7 ","zh":"DC7 "},"id":"VylSQcUBDe","state":{"local_adjustments":[{"type":"radial","feather":1,"size":[0.6,0.8],"position":[0,0],"angle":0,"invert":false,"adjustments":{"exposure":0.46875,"contrast":0.38541666666666674,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0.21875,"saturation":0},"index":0},{"type":"gradient","startPoint":[0,-0.5],"endPoint":[0,0.5],"adjustments":{"exposure":0.47916666666666674,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":1}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0.23966177586174942,"gamma":0,"contrast":0,"highlights":-0.71875,"shadows":0.8020833333333333,"whites":-0.7916666666666667,"blacks":0.11458333333333348,"clarity":0,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,32.65384615384616],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":-0.21875,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":-0.2708333333333334,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.202656946731548,"shadows_saturation":0.29516901955705155,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"DC8 ","zh":"DC8 "},"id":"EyrX9UBvl","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.6458333333333333,"diffuse":0,"temperature":0,"tint":0,"exposure":0.07291666666666674,"gamma":0,"contrast":0,"highlights":-0.28125,"shadows":0.23958333333333348,"whites":0.09375,"blacks":-0.13541666666666674,"clarity":0,"saturation":-1,"vibrance":-1,"curves_red":[[9.524038461538462,0],[255,255]],"curves_green":[[5.4423076923076925,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[48.58653846153846,100.28846153846155],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.purchase.filters.pack1",
    "pack_name":{"en":"Adventure","nl":"Avontuur","fr":"Aventure","de":"Abenteuer","id":"Petualangan","it":"Aventura","ja":"冒険","ko":"모험","pt":"Aventura","es":"Aventura","ru":"Приключение","th":"การผจญภัย","tr":"Macera","vt":"Phiêu lưu","zh":"旅行","zh-tw":"旅行","ar":"مغامرة","ja":"アドベンチャー"},
    "pack_description":{"en":"Filters designed for a variety of global landscapes to satisfy your wanderlust. Suitable for dense and detailed scene mixtures.","nl":"Filters ontworpen voor verscheidene globale landschappen om uw reislust te bevredigen. Geschikt voor dichte en gedetailleerde scène mengsels.","fr":"Filtres conçus pour divers paysages mondiaux afin de satisfaire votre envie de voyager. Idéal pour les mixtures de scène denses et détaillées.","de":"Diese Filter wurden für eine Vielzahl von Landschaften auf der ganzen Welt entwickelt, um Ihr Fernweh zufriedenzustellen. Geeignet für detailreiche und opulente Szenen.","id":"Filter yang dirancang untuk berbagai lanskap global untuk memenuhi nafsu berkelana Anda. Cocok untuk yang padat dan rinci adegan campuran.","it":"Filtri progettati per una varietà di paesaggi globali così da soddisfare la tua voglia di viaggi. Adatta per composizioni e scene dense e dettagliate.","ja":"放浪癖を満足させる世界的な様々な風景のために設計されたフィルタ。緻密かつ詳細なシーンのミックスに適しています。","ko":"당신의 방랑을 만족하는 글로벌 풍경의 다양한 설계 필터. 치밀하고 상세한 szene 혼합물에 적합합니다.","pt":"Filtros desenvolvidos para uma variedade de paisagens globais para satisfazer sua cede por aventura. Compatíveis com misturas de cenas com grande quantidade de detalhes.","es":"Filtros diseñados para una variedad de paisajes globales que satisfacen tu pasión por los viajes. Adecuado para mezclas de escenas densas y detalladas.","ru":"Фильтры предназначены для различных пейзажей, чтобы удовлетворить вашу страсть к путешествиям. Подходит для насыщенных и детализированных сцен.","th":"ฟิลเตอร์ที่ออกแบบมาสำหรับความหลากหลายของภูมิทัศน์ทั่วโลกเพื่อตอบสนองความ Wanderlust ของคุณ เหมาะสำหรับหนาแน่นและมีรายละเอียดผสมฉาก","tr":"Seyahat duygunuzu tatmin etmek için dünya üzerinden çeşitli manzaralarla tasarlanmış filtreler. Yoğun ve detaylı sahne mikstürleri için uygundur.","vt":"Bộ lọc được thiết kế cho một loạt đa dạng các cảnh quan toàn cầu để đáp ứng sở thích du lịch của bạn. Thích hợp dùng cho các bức hình kết hợp cảnh và chi tiết","zh":"为旅行设计，适合拥挤场面和复杂背景的风光滤镜组。","zh-tw":"為旅行設計，適合擁擠場面和復雜背景的風光濾鏡組。","ja":"世界の様々な風景に合わせて作られた旅行好きにはたまらないフィルター集。色の濃淡や質感のきめ細かさが肝となる写真に最適。","ar":"المرشحات المصممة لمجموعة متنوعة من المناظر الطبيعية العالمية لتلبية التجوال الخاصة بك. مناسبة لالخلطات المشهد كثيفة وتفصيلا."},
    "pack_maker":"Polarr",
    "is_free":0,
    "version":125,
    "preview_images":["av1.jpg","av2.jpg","av3.jpg"],
    "filters":[
      {"name":{"en":"AV1 Depth","zh":"AV1 深度"},"id":"jhsn18oxbt9","state":{"local_adjustments":[{"type":"radial","feather":1,"size":[0.5373333333333334,0.8],"position":[0,0],"angle":0,"invert":false,"adjustments":{"mosaic_size":0,"blur":0,"exposure":0.375,"contrast":0.4166666666666665,"temperature":0,"tint":0,"highlights":0.27083333333333326,"shadows":-0.1875,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.15625,"tint":0,"exposure":0,"gamma":0,"contrast":0.5,"highlights":-0.34375,"shadows":0.21875,"whites":-0.5520833333333335,"blacks":0.35416666666666674,"clarity":0,"saturation":0,"vibrance":-0.26041666666666674,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,32.65384615384616],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0.7395833333333333,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0.26041666666666674,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.34375,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"AV2 Texture","zh":"AV2 质感"},"id":"urukz3q5mi","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.875,"diffuse":0,"temperature":0,"tint":0,"exposure":0.22916666666666674,"gamma":0.15625,"contrast":0,"highlights":-1,"shadows":-1,"whites":1,"blacks":0.2916666666666665,"clarity":0,"saturation":0.38541666666666674,"vibrance":-0.2604166666666666,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[88.04326923076923,134.3028846153846],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.046875,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"AV3 Nightfall","zh":"AV3 天色"},"id":"ir87ee6zuxr","state":{"local_adjustments":[{"type":"gradient","startPoint":[0.0009765625,-0.5],"endPoint":[0.0009765625,0.0703125],"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.4375,"contrast":0,"temperature":0,"tint":0,"highlights":0.04166666666666674,"shadows":-0.5,"clarity":0,"saturation":0},"index":0},{"type":"gradient","startPoint":[-0.01123046875,0.5015625],"endPoint":[-0.01123046875,-0.05156249999999998],"adjustments":{"exposure":0.5,"contrast":0.5,"temperature":0,"tint":0,"highlights":0.35416666666666674,"shadows":0.36458333333333326,"clarity":0,"saturation":0},"index":1}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.5833333333333333,"diffuse":0,"temperature":0,"tint":0,"exposure":0.3645833333333335,"gamma":0,"contrast":0.20833333333333348,"highlights":0,"shadows":0,"whites":0,"blacks":0,"clarity":0,"saturation":0,"vibrance":-0.4895833333333334,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.09895833333333334,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"AVB1 Plain","zh":"AVB1 黑白"},"id":"nl09tgr2j4i","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":-0.0008696134845601611,"temperature":0,"tint":0,"exposure":0,"gamma":-0.1673913445704669,"contrast":0.6097100671637641,"highlights":0,"shadows":-0.4264251484818772,"whites":0.31367143412215226,"blacks":0,"clarity":0,"saturation":-1,"vibrance":0,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,50],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":-0.0008696134845601611,"shadows_saturation":-0.019372028049660983,"balance":0,"sharpen":0.202656946731548,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"AV4 Clear","zh":"AV4 清新"},"id":"timgv8qto6r","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.6097100671637641,"diffuse":0,"temperature":0,"tint":0,"exposure":0.33217384868725297,"gamma":0,"contrast":0,"highlights":0,"shadows":0,"whites":0,"blacks":0.7022221399892679,"clarity":0,"saturation":0.03613521564564137,"vibrance":0.29516901955705155,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.12864728847114493,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"AV5 Fog","zh":"AV5 雾气"},"id":"kxinu7n9udi","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":-0.17708333333333343,"diffuse":0.23958333333333331,"temperature":-0.09375,"tint":0,"exposure":0.17708333333333348,"gamma":0.22916666666666674,"contrast":0,"highlights":0.5729166666666667,"shadows":0.30208333333333326,"whites":0,"blacks":0.4375,"clarity":0,"saturation":0,"vibrance":0.21875,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"AV6 Warm","zh":"AV6 油润"},"id":"yyumhx5hfr","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.6145833333333335,"diffuse":0,"temperature":0,"tint":0,"exposure":0.33333333333333326,"gamma":0,"contrast":0.3020833333333335,"highlights":0,"shadows":0,"whites":0,"blacks":0.6979166666666665,"clarity":0,"saturation":0.04166666666666652,"vibrance":0.30208333333333326,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,31.293269230769226],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0.0625,"saturation_green":0.09375,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0.09375,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.1666666666666667,"shadows_saturation":0.3625,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.3958333333333334,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0.13541666666666652,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"AV7 Highlight","zh":"AV7 高调"},"id":"7w3e2gtx1or","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.6458333333333333,"diffuse":0,"temperature":0,"tint":0,"exposure":0.07291666666666674,"gamma":0,"contrast":0,"highlights":-0.28125,"shadows":0.23958333333333348,"whites":0.09375,"blacks":-0.13541666666666674,"clarity":0,"saturation":0,"vibrance":0.16666666666666652,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[48.58653846153846,100.28846153846155],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.purchase.filters.pack3",
    "pack_name":{"en":"Culinary","nl":"Culinair","fr":"Culinaire","de":"Kulinarisch","id":"Kuliner","it":"Culinario","ja":"料理","ko":"요리","pt":"Culinária","es":"Culinario","ru":"Кулинария","th":"การปรุงอาหาร","tr":"Aşçılık","vt":"Ẩm thực","zh":"美食","zh-tw":"美食","ar":"مطبخي","ja":"フード"},
    "pack_description":{"en":"Filters for food photographers to create appetizing views from main dish to dessert, with slight color and lighting corrections.","nl":"Filters voor voedselfotografen om smakelijke uitzichten te maken, van hoofdgerecht tot dessert, met lichte kleur en licht correcties.","fr":"Filtres pour la photographie culinaire permettant de créer des vues appétissantes, du plat principal au dessert, avec de légères corrections de couleur et d’éclairage.","de":"Ein Filter für Food-Fotografen, um appetitliche Perspektiven zu kreieren, vom Hauptgang bis zum Dessert, mit dezenten Farb- und Lichtkorrekturen.","id":"Filter untuk fotografer makanan untuk membuat tampilan membangkitkan selera, dari hidangan utama ke hidangan penutup, dengan warna tipis dan koreksi pencahayaan.","it":"Filtri per fotografi di cibo in grado di creare visuali appetitose, da portate principali a dessert, con piccole correzioni di colore e illuminazione.","ja":"わずかな色や照明の修正を行うことで、メインディッシュからデザートまで食欲をそそる見た目を作る、料理の写真のためのフィルタ。","ko":"음식 사진 작가를위한 필터 약간의 색상 및 조명 수정과 메인 요리부터 디저트에 식욕을 돋 우는 뷰를 생성합니다.","pt":"Filtros para chefs da fotógrafia criarem captações apetitosas, do prato principal até a sobremesa, com sutis correções de cores e luzes.","es":"Filtros para los fotógrafos de alimentos, para crear vistas apetitosas, desde el plato principal hasta el postre, con ligeras correcciones de luz y color.","ru":"Фильтры для тех, кто любит делать фото еды, чтобы создать аппетитный вид, от основного блюда до десерта, с легким цветом и настройкой освещения.","th":"ฟิลเตอร์สำหรับช่างภาพอาหารที่จะสร้างมุมมองที่น่ารับประทานจากอาหารจานหลักให้กับขนมที่มีสีเล็กน้อยและการแก้ไขแสง","tr":"An yemekten tatlılağa ufak renk ve ışık doğrulamalarıyla yemek fotoğrafçılığı için filtreler.","vt":"Bộ lọc dành cho các nhiếp ảnh ẩm thực để tạo ra các hình ảnh ngon mắt, từ món chính đến món tráng miệng với ít sự điều chỉnh bề màu sắc và ánh sáng","zh":"这组整体颜色偏向偏暖，给厨房设计的滤镜可以帮助改善食物的色彩和光线。","zh-tw":"這組整體顏色偏向偏暖，給廚房設計的濾鏡可以幫助改善食物的色彩和光線。","ja":"メインディッシュからデザートまで食べ物の色や明るさに少し手を加えて食欲をそそる写真に仕上げるフィルター集。","ar":"مرشحات للمصورين الغذائية لإنشاء طرق فاتح للشهية، من الطبق الرئيسي إلى الحلوى، مع الألوان والإضاءة طفيفة التصحيحات."},
    "pack_maker":"Polarr",
    "is_free":0,
    "version":125,
    "preview_images":["c1.jpg","c2.jpg","c3.jpg"],
    "filters":[
      {"name":{"en":"C1 Warm","zh":"C1 暖"},"id":"bf2ih08uxr","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.125,"tint":0,"exposure":0,"gamma":-0.14583333333333343,"contrast":0,"highlights":0,"shadows":0,"whites":0,"blacks":0,"clarity":0,"saturation":0,"vibrance":0.375,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[98.92788461538461,128.86057692307693],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0.13541666666666674,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.21875,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.13541666666666652,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"C2 Shine","zh":"C2 发光"},"id":"qqbduf2yb9","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.13541666666666674,"tint":0,"exposure":0,"gamma":0,"contrast":0.40625,"highlights":0,"shadows":0,"whites":0,"blacks":0,"clarity":0,"saturation":0.09375,"vibrance":0.3645833333333335,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[97.5673076923077,153.35096153846155],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"C3 Highlight","zh":"C3 高光"},"id":"ini0r1wcdi","state":{"local_adjustments":[{"type":"radial","feather":1,"size":[0.8176390157586066,0.8],"position":[0.02,0.0053590568060021436],"angle":-360.40042991263226,"invert":false,"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.40625,"contrast":0.22916666666666674,"temperature":0,"tint":0,"highlights":-0.15625,"shadows":0.22916666666666674,"clarity":0,"saturation":0,"vibrance":0.10416666666666674},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":-0.19791666666666674,"diffuse":0,"temperature":0,"tint":0,"exposure":0,"gamma":0.16666666666666674,"contrast":0,"highlights":0.23958333333333326,"shadows":-0.020833333333333426,"whites":0.03125,"blacks":0.0625,"clarity":0,"saturation":0,"vibrance":0.40625,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[63.55288461538462,58.110576923076906],[166.9567307692308,180.5625],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0.10416666666666674,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.125,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"C4 Clear","zh":"C4 清新"},"id":"3v7lw0cnmi","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0,"gamma":0,"contrast":0,"highlights":0,"shadows":0.46169075064295817,"whites":0,"blacks":0.4801931652080589,"clarity":0,"saturation":0.0731400447758428,"vibrance":0.12864728847114493,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[60,50],[192,210],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0.2211593612966487,"saturation_green":0.14714970303624564,"saturation_aqua":0.2581641904268501,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"C5 Detail","zh":"C5 细节"},"id":"ylih02g9zfr","state":{"local_adjustments":[{"type":"radial","feather":1,"size":[0.6,0.8],"position":[0,0],"angle":0,"invert":false,"adjustments":{"mosaic_size":0,"blur":0,"exposure":0.46875,"contrast":0.38541666666666674,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0.21875,"saturation":0},"index":0},{"type":"gradient","startPoint":[0,-0.5],"endPoint":[0,0.5],"adjustments":{"exposure":0.47916666666666674,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":1}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0.23966177586174942,"gamma":0,"contrast":0,"highlights":-0.71875,"shadows":0.8020833333333333,"whites":-0.7916666666666667,"blacks":0.11458333333333348,"clarity":0,"saturation":0,"vibrance":0,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":-0.21875,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":-0.2708333333333334,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.202656946731548,"shadows_saturation":0.29516901955705155,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"C6 Soften","zh":"C6 柔光"},"id":"ptutlhxgvi","state":{"local_adjustments":[{"type":"gradient","startPoint":[-0.24364973262032086,-0.5439952437574316],"endPoint":[-0.24364973262032086,0.60820451843044],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0.28125,"contrast":0,"temperature":0.21875,"tint":0,"highlights":-0.15625,"shadows":0.28125,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.09375,"tint":0,"exposure":0.1875,"gamma":0,"contrast":0,"highlights":-0.21875,"shadows":0.20833333333333326,"whites":0.14583333333333326,"blacks":0.11458333333333326,"clarity":0,"saturation":0,"vibrance":0.4986955797731596,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.202656946731548,"shadows_saturation":0.29516901955705155,"balance":0,"sharpen":0.140625,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.15625,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"C7 Silk","zh":"C7 顺滑"},"id":"vp25ipb9","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.14714970303624564,"tint":0,"exposure":0.23966177586174942,"gamma":0,"contrast":0.27083333333333326,"highlights":-0.1673913445704669,"shadows":0.31367143412215226,"whites":-0.25990341739597056,"blacks":0.2211593612966487,"clarity":0,"saturation":0,"vibrance":0.4986955797731596,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.202656946731548,"shadows_saturation":0.29516901955705155,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"C8 Tender","zh":"C8 鲜嫩"},"id":"bjay9fi529","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.14714970303624564,"tint":0,"exposure":0.23966177586174942,"gamma":0,"contrast":0,"highlights":-0.1673913445704669,"shadows":0.31367143412215226,"whites":-0.25990341739597056,"blacks":0.2211593612966487,"clarity":0,"saturation":0,"vibrance":0.4986955797731596,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.202656946731548,"shadows_saturation":0.29516901955705155,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.purchase.filters.pack2",
    "pack_name":{"en":"Skin Tone","nl":"Huidskleur","fr":"Teint de la peau","de":"Hautfarbe","id":"Warna kulit","it":"Tono della Pelle","ja":"肌の色合い","ko":"피부 톤","th":"โทนสีผิว","pt":"Tom de Pele","es":"Tono de piel","ru":"Тон Кожи","tr":"Deri Tonu","vt":"Màu da","zh":"肤色","zh-tw":"膚色","ar":"لون البشرة","ja":"スキン"},
    "pack_description":{"en":"High fidelity filters that correct and enhance various skin tones, lighting and details for better portraits.","nl":"HiFi-filters die verschillende huidtinten, belichting en details corrigeren en verbeteren voor betere portretten.","fr":"Filtres haute-fidélité qui corrigent et subliment les divers tons de la peau, l’éclairage et les détails, pour obtenir de meilleurs portraits.","de":"Filter mit hoher Farbtreue, die unterschiedliche Hautfarben, Lichteinfall und Details korrigieren und verbessern, um bessere Porträts zu erhalten.","id":"Filter tinggi ketepatan yang benar dan meningkatkan berbagai warna kulit, pencahayaan, dan detail, untuk potret yang lebih baik.","it":"Filtri ad alta fedeltà per correggere e migliorare diverse tonalità della pelle, illuminazione, e dettagli, per migliori ritratti.","ja":"より良いポートレートを作るため、色んな肌の色合いや照明、詳細を修正し強化する高性能のフィルタ。","ko":"고성능 필터는 괜찮 즉, 강화 및 다양한 피부 색조, 조명, 세부 사항, 더 나은 인물.","pt":"Filtros de alta fidelidade que corrigem e amplificam vários tons de pele, luzes e detalhes, para melhores retratos.","es":"Filtros de alta fidelidad que corrigen y mejoran diversos tonos de piel, iluminación y detalles, para obtener mejores retratos.","ru":"Высококачественные фильтры, корректируют и усиливают различные оттенки кожи, освещение и детали, для создания лучших портретов.","th":"ฟิลเตอร์ความจงรักภักดีสูงโอเคที่ช่วยเพิ่มและโทนสีผิวต่างๆ, ไฟ, และรายละเอียดสำหรับถ่ายภาพบุคคลที่ดีกว่า","tr":"Farklı deri tonlarını, ışığı ve detayları daha iyi portreler için düzelten yüksek duyarlı filtreler.","vt":"Bộ siêu lọc có thể chỉnh sửa và tăng cường nhiều loại màu da, ánh sáng và các chi tiết để bạn có được các bức hình chân dung tốt hơn","zh":"高质量的对肤色进行点缀和调整的滤镜组，适用于明亮光照条件","zh-tw":"高質量的對膚色進行點綴和調整的濾鏡組，適用於明亮光照條件","ja":"肌の色合いや明るさを調整したり、ディテールを加工して、自然な肌色に補正できるフィルター集。","ar":"مرشحات عالية الدقة التي تصحيح وتعزيز نغمات مختلفة الجلد، والإضاءة، والتفاصيل، لصور أفضل."},
    "pack_maker":"Polarr",
    "is_free":0,
    "version":125,
    "preview_images":["st1.jpg","st2.jpg","st3.jpg"],
    "filters":[
      {"name":{"en":"ST1 Light","zh":"ST1 哑光"},"id":"0pmwo8ncdi","state":{"local_adjustments":[{"type":"gradient","startPoint":[0.4666666666666667,-0.6583333333333333],"endPoint":[0,0.5],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0.5,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0},{"type":"radial","feather":1,"size":[0.5333333333333333,0.8],"position":[0,0],"angle":0,"invert":false,"adjustments":{"exposure":0.25,"contrast":0,"temperature":0,"tint":0,"highlights":0.2708333333333335,"shadows":-0.3229166666666666,"clarity":0.10416666666666674,"saturation":0,"vibrance":0},"index":1}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.13541666666666652,"diffuse":0.28125,"temperature":-0.14583333333333326,"tint":0,"exposure":0.11458333333333326,"gamma":0,"contrast":0,"highlights":-0.7395833333333333,"shadows":0,"whites":0,"blacks":0.11458333333333326,"clarity":0,"saturation":0,"vibrance":0.1875,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[162.875,205.0528846153846],[255,255]],"hue_red":0.2604166666666665,"hue_orange":0,"hue_yellow":0.6666666666666665,"hue_green":0.3125,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0.2604166666666665,"saturation_orange":-0.22916666666666657,"saturation_yellow":0,"saturation_green":0.07291666666666652,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":-0.28125,"luminance_orange":0.21875,"luminance_yellow":0.33333333333333326,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.13020833333333331,"shadows_saturation":0.22499999999999998,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.125,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"ST2 Midtone","zh":"ST2 中性"},"id":"dx4hb2zkt9","state":{"local_adjustments":[{"type":"gradient","startPoint":[0.4666666666666667,-0.6583333333333333],"endPoint":[0,0.5],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0.5,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0},{"type":"radial","feather":1,"size":[0.5333333333333333,0.8],"position":[0,0],"angle":0,"invert":false,"adjustments":{"exposure":0.25,"contrast":0,"temperature":0,"tint":0,"highlights":0.2708333333333335,"shadows":-0.3229166666666666,"clarity":0.10416666666666674,"saturation":0,"vibrance":0},"index":1}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.13541666666666652,"diffuse":0.28125,"temperature":-0.14583333333333326,"tint":0,"exposure":0.11458333333333326,"gamma":0,"contrast":0,"highlights":-0.7395833333333333,"shadows":0,"whites":0,"blacks":0.11458333333333326,"clarity":0,"saturation":0,"vibrance":0.1875,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[162.875,205.0528846153846],[255,255]],"hue_red":0.29166666666666674,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":-0.19791666666666674,"saturation_orange":-0.22916666666666657,"saturation_yellow":0,"saturation_green":-0.3541666666666666,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0.3333333333333335,"luminance_orange":0.21875,"luminance_yellow":0.33333333333333326,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.125,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"ST3 Fade","zh":"ST3 低亮"},"id":"878fyap2e29","state":{"local_adjustments":[{"type":"gradient","startPoint":[0,-0.5],"endPoint":[0,0.5],"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.41666666666666674,"contrast":0,"temperature":0,"tint":0,"highlights":0.27083333333333326,"shadows":-0.22916666666666657,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.13541666666666652,"diffuse":0,"temperature":0,"tint":0,"exposure":0.05208333333333348,"gamma":0.04166666666666674,"contrast":0,"highlights":-0.3541666666666666,"shadows":-0.11458333333333343,"whites":0.0625,"blacks":0.17708333333333348,"clarity":0,"saturation":-0.36458333333333326,"vibrance":0.25,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[60.83173076923076,109.8125],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.05729166666666667,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"STB1 Dense","zh":"STB1 重度黑"},"id":"zpj0ajg7gb9","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.3876810923825553,"diffuse":0.6097100671637641,"temperature":-0.13038651544026536,"tint":0,"exposure":0.18415453216644706,"gamma":0,"contrast":0.7762317982496707,"highlights":0.12864728847114493,"shadows":-0.22289858826576903,"whites":0.5727052380335627,"blacks":-0.4264251484818772,"clarity":0.12864728847114493,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,50],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0.23966177586174942,"saturation_aqua":0,"saturation_blue":0.18415453216644706,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.18415453216644706,"shadows_saturation":-0.0008696134845601611,"balance":0,"sharpen":0.0731400447758428,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.11014487390604422,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"ST4 Opaque","zh":"ST4 朦胧"},"id":"yju7r6swcdi","state":{"local_adjustments":[{"type":"gradient","startPoint":[0.4666666666666667,-0.6583333333333333],"endPoint":[0,0.5],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0.5,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.13541666666666652,"diffuse":0.28125,"temperature":0,"tint":0,"exposure":0.11458333333333326,"gamma":0,"contrast":0,"highlights":-0.7395833333333333,"shadows":0,"whites":0,"blacks":0.11458333333333326,"clarity":0,"saturation":0,"vibrance":0.15625,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[162.875,205.0528846153846],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0.30208333333333326,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":-0.19791666666666674,"saturation_orange":-0.22916666666666657,"saturation_yellow":0.28125,"saturation_green":-0.3541666666666666,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0.3333333333333335,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.3333333333333334,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0.20833333333333348,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"ST5 Liquid","zh":"ST5 清新"},"id":"r6shiz1if6r","state":{"local_adjustments":[{"type":"gradient","startPoint":[0.4666666666666667,-0.6583333333333333],"endPoint":[0,0.5],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0.5,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.13541666666666652,"diffuse":0.28125,"temperature":0,"tint":0,"exposure":0.11458333333333326,"gamma":0,"contrast":0,"highlights":-0.7395833333333333,"shadows":0,"whites":0,"blacks":0.11458333333333326,"clarity":0,"saturation":0,"vibrance":0.15625,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[162.875,205.0528846153846],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":-0.19791666666666674,"saturation_orange":-0.22916666666666657,"saturation_yellow":0.28125,"saturation_green":-0.3541666666666666,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0.3333333333333335,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.125,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"ST6 Glow","zh":"ST6 高光"},"id":"jas9zk0rudi","state":{"local_adjustments":[{"type":"gradient","startPoint":[0.004,-0.5],"endPoint":[0.004,0.2586206896551724],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0.6041666666666667,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0.578125,"temperature":0,"tint":0,"exposure":0.11458333333333326,"gamma":0,"contrast":0,"highlights":0,"shadows":0.10416666666666674,"whites":0,"blacks":0.11458333333333326,"clarity":0,"saturation":0,"vibrance":0.15625,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[162.875,205.0528846153846],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":-0.19791666666666674,"saturation_orange":0,"saturation_yellow":0.28125,"saturation_green":0.3125,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0.3333333333333335,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0.4375,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.05208333333333326,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"ST7 Cley","zh":"ST7 白皙"},"id":"2j3mrj54s4i","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0.11458333333333326,"gamma":0,"contrast":0,"highlights":0,"shadows":0.10416666666666674,"whites":0,"blacks":0.11458333333333326,"clarity":0,"saturation":0,"vibrance":0.15625,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[162.875,205.0528846153846],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":-0.19791666666666674,"saturation_orange":0,"saturation_yellow":0.28125,"saturation_green":0.3125,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0.3333333333333335,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.08854166666666667,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0.4375,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.05208333333333326,"denoise":0,"chroma_amount":0}}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.purchase.filters.pack6",
    "pack_name":{"en":"Atmosphere","nl":"Sfeer","fr":"Atmosphère","de":"Atmosphäre","id":"Suasana","it":"Atmosfera","ja":"雰囲気","ko":"분위기","pt":"Atmosfera","es":"Atmósfera","ru":"Атмосфера","th":"บรรยากาศ","tr":"Atmosfer","vt":"Không khí","zh":"空气","zh-tw":"空氣","ar":"الغلاف الجوي"},
    "pack_description":{"en":"Filters designed to emulate different atmospheric and seasonal conditions. Great for landscape manipulation.","nl":"Filters ontworpen voor het emuleren van verschillende sferen en seizoensgebonden condities. Ideaal voor landschap manipulatie.","fr":"Filtres conçus pour émuler les différentes conditions atmosphériques et saisonnières. Idéal pour la manipulation de paysage.","de":"Diese Filter wurden dazu entwickelt, verschiedene atmosphärische und saisonale Bedingungen nachzuahmen. Super für die Bearbeitung von Landschaften.","id":"Filter yang dirancang untuk meniru kondisi atmosfer dan musim yang berbeda. Bagus untuk manipulasi lanskap.","it":"Filtri progettati per emulare diverse condizioni atmosferiche e stagionali. Ottimi per la manipolazione dei paesaggi.","ja":"異なる雰囲気や季節の状態をエミュレートするように設計されたフィルタ。風景の調整に最適です。","ko":"필터는 다른 대기 및 기후 조건을 모방하도록 설계되었습니다. 풍경 조작에 아주 좋아","pt":"Filtros desenvolvidos para emular diferentes condições atmosféricas e climáticas. Ótimo set par manipulação de paisagem.","es":"Filtros diseñados que emulan diferentes condiciones atmosféricas y de temporada. Excelente para manipular paisajes.","ru":"Фильтры предназначены для имитации различных атмосферных и сезонных условий. Отлично подходит для манипуляции пейзажем.","th":"ฟิลเตอร์ที่ออกแบบมาเพื่อเลียนแบบสภาพบรรยากาศและตามฤดูกาลที่แตกต่างกัน ที่ดีสำหรับการจัดการภูมิทัศน์","tr":"Farklı atmosferik ve mevsimsel durumları taklid etmesi için ayarlanmış filtreler. Manzara manipülasyonu için birebir.","vt":"Bộ lọc được thiết kế để cạnh tranh với các điều kiện về thời tiết, khí quyển theo mùa khác nhau.Tuyệt vời dành cho các kiệt tác về cảnh quan","zh":"用来模拟不同大气和空气，以及季节情况的滤镜。适用于修改风光摄影。","zh-tw":"用來模擬不同大氣和空氣，以及季節情況的濾鏡。適用於修改風光攝影。","ar":"المرشحات مصممة لمحاكاة الظروف الجوية والموسمية المختلفة. كبيرة للتلاعب المناظر الطبيعية."},
    "pack_maker":"Polarr",
    "is_free":0,
    "version":130,
    "preview_images":["at1.jpg","at2.jpg","at3.jpg"],
    "filters":[
      {"name":{"en":"AT1 Spotlight","zh":"AT1 聚光"},"id":"zra4gec23xr","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.4583333333333335,"diffuse":1,"temperature":-0.020833333333333426,"tint":-0.07291666666666657,"exposure":0.23958333333333326,"gamma":0,"contrast":0.17708333333333326,"highlights":0,"shadows":0,"whites":0,"blacks":0,"clarity":0.5208333333333333,"saturation":-0.28125,"vibrance":0.6979166666666665,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-1,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"AT2 Dreamy","zh":"AT2 梦境"},"id":"q9zd63fecdi","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":-1,"diffuse":1,"temperature":0.02083333333333326,"tint":0,"exposure":-0.5,"gamma":0,"contrast":0,"highlights":0.6458333333333335,"shadows":-0.08333333333333343,"whites":0.13541666666666652,"blacks":-0.13541666666666674,"clarity":-1,"saturation":0,"vibrance":0.5,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.625,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"AT3 Freeze","zh":"AT3 寒冷"},"id":"kwcyy2jra4i","state":{"local_adjustments":[{"type":"gradient","startPoint":[-0.0018214936247723133,-0.49043715846994534],"endPoint":[-0.0018214936247723133,0.38934426229508196],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0.03846153846153877,"contrast":-0.20512820512820507,"temperature":-0.20512820512820507,"tint":0,"highlights":-0.4358974358974359,"shadows":0,"clarity":0,"saturation":-0.20512820512820507},"index":0},{"type":"radial","feather":1,"size":[1.326047358834244,0.5],"position":[-0.0009107468123861571,-0.042349726775956276],"invert":true,"adjustments":{"exposure":-0.4230769230769233,"contrast":-0.4871794871794872,"temperature":-0.23076923076923084,"tint":0.15384615384615374,"highlights":-0.05128205128205132,"shadows":0,"clarity":-0.05128205128205132,"saturation":-0.07692307692307687},"index":1}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":-0.0222222222222222,"tint":0,"exposure":0.19999999999999996,"gamma":0,"contrast":-0.1333333333333333,"highlights":-0.35555555555555557,"shadows":0.24444444444444446,"whites":0,"blacks":0,"clarity":-0.020833333333333426,"saturation":0,"vibrance":-0.04444444444444434,"curves_red":[[0,24.71938775510204],[113.18877551020408,110.58673469387756],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,36.42857142857142],[41.63265306122449,44.234693877551024],[130.10204081632654,144.41326530612247],[255,255]],"curves_all":[[0,44.23469387755102],[66.35204081632652,70.25510204081635],[144.41326530612244,148.31632653061223],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.47395833333333337,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"AT4 Fog","zh":"AT4 雾气"},"id":"oxd83v50zfr","state":{"local_adjustments":[{"type":"radial","feather":1,"size":[0.902,0.8],"position":[0,0],"angle":-180,"invert":false,"adjustments":{"mosaic_size":0,"blur":0,"exposure":0,"contrast":0,"temperature":-0.46875,"tint":0,"highlights":0,"shadows":0,"clarity":0.22916666666666652,"saturation":-0.8229166666666667},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0,"tint":0,"exposure":0.28125,"gamma":0,"contrast":0,"highlights":0,"shadows":0,"whites":0,"blacks":0,"clarity":0,"saturation":0,"vibrance":0.4895833333333335,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.42708333333333326,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"AT5 Sunset","zh":"AT5 落日"},"id":"cbgft9vbo6r","state":{"local_adjustments":[{"type":"gradient","startPoint":[0,-0.5],"endPoint":[0,0.5],"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.8229166666666665,"contrast":0.29166666666666674,"temperature":-0.2604166666666666,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":-0.020833333333333426,"tint":0.14583333333333326,"exposure":0.5104166666666667,"gamma":0.13541666666666674,"contrast":0,"highlights":0,"shadows":0,"whites":0,"blacks":0,"clarity":0,"saturation":-0.4895833333333334,"vibrance":0.8229166666666667,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,36.735576923076906],[79.87980769230768,92.125],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"AT6 Dust","zh":"AT6 沙尘"},"id":"va7ymnrvn29","state":{"local_adjustments":[{"type":"gradient","startPoint":[0,-0.5],"endPoint":[0,0.5],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0},{"type":"radial","feather":1,"size":[0.8600581375697808,0.8],"position":[0,0],"angle":-180.66619997018313,"invert":false,"adjustments":{"exposure":0.01041666666666674,"contrast":0,"temperature":0.44791666666666674,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":1}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.1875,"diffuse":0,"temperature":0,"tint":0,"exposure":-0.11458333333333326,"gamma":0,"contrast":0,"highlights":0,"shadows":0,"whites":0,"blacks":0,"clarity":0,"saturation":0,"vibrance":0,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,31.293269230769226],[70.35576923076923,82.60096153846155],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.4791666666666666,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.purchase.filters.pack8",
    "pack_name":{"en":"Japan","nl":"Japan","fr":"Japon","de":"Japan","id":"Jepang","it":"Giappone","ja":"日本","ko":"일본","pt":"Japão","es":"Japón","ru":"Япония","th":"ประเทศญี่ปุ่น","tr":"Japon","vt":"Nhật Bản","zh":"日系","zh-tw":"日系","ar":"اليابان"},
    "pack_description":{"en":"Enjoy a softened, quiet, mellow theme inspired by the popular Japanese aesthetic. Ideal for manipulating landscapes and domestic scenes with varying light and seasonal conditions.","nl":"Geniet van een verzacht, rustig, ontspannen thema geïnspireerd door de populaire Japanse esthetiek. Ideaal voor het manipuleren van landschappen en huiselijke scènes met variërende licht en seizoensgebonden condities.","fr":"Profitez du thème adouci, calme et détendu inspiré de l’esthétique populaire japonaise. Idéal pour manipuler des paysages et des scènes domestiques avec diverses conditions de luminosité et de saison.","de":"Erfreuen Sie sich an einem weichen, ruhigen, sanften Thema, das von der so beliebten japanischen Ästhetik inspiriert wurde. Ideal für Landschaften und häusliche Szenen, mit wechselndem Licht und aus verschiedenen Jahreszeiten.","id":"Nikmati kelembutan, tenang, tema mellow terinspirasi oleh estetika populer Jepang. Ideal untuk memanipulasi pemandangan dan adegan negeri dengan berbagai cahaya dan kondisi musiman.","it":"Goditi un tema addolcito, quieto e tranquillo ispirato dalla popolare estetica Giapponese. Ideale per manipolare paesaggi e scene domestiche dalle variabili condizioni di luce e stagione.","ja":"人気の日本の美的に刺激を受けた、柔らかく、静かでな、豊かなテーマをお楽しみください。色んな光や季節での、風景や室内の景色の調整に理想的です。","ko":"일본의 인기 미학에서 영감을 연화, 조용하고 부드러운 테마를 즐길 수 있습니다. 다양한 빛과 계절 조건 풍경과 국내 장면을 조작하기에 적합합니다.","pt":"Desfrute de um tema suavizado, tênue e gracioso inspirado na estética japonesa. Ideal para manipulação de paisagens e cenas cotidianas com variação de luzes e condições climáticas.","es":"Disfruta de un tranquilo, suavizado, y meloso tema inspirado en la estética popular Japonesa. Ideal para manipular paisajes y escenas domésticas en diferentes condiciones de luz y de temporada.","ru":"Наслаждайтесь мягкой, спокойной, сочной темой, навеянной популярной японской эстетикой. Идеально подходит для работы с пейзажем и бытовыми сценами при разном освещении и сезонных условиях.","th":"เพลิดเพลินไปกับการปรับตัวลดลง, เงียบ, ธีมกลมกล่อมแรงบันดาลใจจากความงามที่เป็นที่นิยมในญี่ปุ่น เหมาะอย่างยิ่งสำหรับการจัดการภูมิทัศน์และฉากที่แตกต่างกันในประเทศที่มีน้ำหนักเบาและเงื่อนไขตามฤดูกาล","tr":"Popüler Japon estetiğinden ilhamını almış, yumuşatılmış, sessiz ve tatlı temanın tadını çıkarın. Manzaraları ve yerel sahneleri çeşitli ışık ve mevsimsel durumlar ile manipüle edebilmek mümkün.","vt":"Thưởng thức khung cảnh êm dịu, yên tĩnh được truyền cảm hứng từ mỹ thuật nổi tiếng Nhật Bản. Lý tưởng cho các kiệt tác cảnh quan và cảnh trong nước với ánh sáng và các điều kiện thời tiết khác nhau","zh":"这一组滤镜通过提高画面亮度,增加冷色调,制造色偏等手段 让画面充满迷离与清新的感觉。","zh-tw":"這一組濾鏡通過提高畫面亮度,增加冷色調,製造色偏等手段 讓畫面充滿迷離與清新的感覺。","ar":"تتمتع يلين، هادئ، موضوع يانع مستوحاة من الجمالية اليابانية شعبية. مثالية لمعالجة المناظر الطبيعية ومشاهد المحلية مع اختلاف الضوء والظروف الموسمية."},
    "pack_maker":"Polarr",
    "is_free":0,
    "version":133,
    "preview_images":["jp1.jpg","jp2.jpg","jp3.jpg"],
    "filters":[
      {"name":{"en":"JP1 ","zh":"JP1 "},"id":"4J8xF4k08g","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":-0.07291666666666657,"tint":0,"exposure":0.19791666666666674,"gamma":0.17708333333333348,"contrast":-0.44791666666666674,"highlights":0.4270833333333335,"shadows":-0.125,"whites":0.125,"blacks":-0.21875,"clarity":0,"saturation":0,"vibrance":0.5520833333333333,"curves_red":[[0,0],[96.20673076923076,75.7980769230769],[186.0048076923077,183.28365384615384],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[93.48557692307692,153.35096153846155],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0.3958333333333335,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":-0.25,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"JP2 ","zh":"JP2 "},"id":"N1rxK4JR8x","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.4583333333333335,"diffuse":0,"temperature":-0.07291666666666657,"tint":0,"exposure":0.36458333333333326,"gamma":0.17708333333333348,"contrast":-0.49999999999999994,"highlights":0.4270833333333335,"shadows":-0.125,"whites":0.125,"blacks":-0.21875,"clarity":0,"saturation":0,"vibrance":-0.22916666666666674,"curves_red":[[0,0],[96.20673076923076,75.7980769230769],[186.0048076923077,183.28365384615384],[255,255]],"curves_green":[[0,0],[93.48557692307692,96.20673076923077],[172.39903846153845,186.0048076923077],[255,255]],"curves_blue":[[0,0],[225.06730769230768,255]],"curves_all":[[0,0],[93.48557692307692,153.35096153846155],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0.3958333333333335,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":-0.25,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"JP3 ","zh":"JP3 "},"id":"V1NgYN1CUx","state":{"local_adjustments":[{"type":"gradient","startPoint":[0,-0.5],"endPoint":[0,0.5],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0,"contrast":0,"temperature":1,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.23958333333333326,"tint":0,"exposure":0.5,"gamma":0.11458333333333348,"contrast":0.26041666666666674,"highlights":-0.22916666666666674,"shadows":0.21875,"whites":0.1875,"blacks":-0.09375,"clarity":0,"saturation":0,"vibrance":0.3020833333333335,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":-0.26041666666666674,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":-0.375,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.6510416666666666,"highlights_saturation":0.8499999999999999,"shadows_hue":0.609375,"shadows_saturation":0.5125,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"JP4 ","zh":"JP4 "},"id":"4JbxKNyCUg","state":{"local_adjustments":[{"type":"gradient","startPoint":[0,-0.5],"endPoint":[0,0.5],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0,"contrast":0,"temperature":0.5625,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":-0.4270833333333334,"tint":0,"exposure":-0.3645833333333334,"gamma":0.2708333333333335,"contrast":0.4583333333333335,"highlights":0.34375,"shadows":0.23958333333333326,"whites":0.2604166666666665,"blacks":0.15625,"clarity":0,"saturation":-0.3020833333333334,"vibrance":0.47916666666666674,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":-0.4583333333333334,"hue_yellow":-0.4895833333333334,"hue_green":0.5416666666666667,"hue_aqua":0.27083333333333326,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0.6979166666666665,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"JP5 ","zh":"JP5 "},"id":"VyflFNJALe","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":-0.15625,"tint":0,"exposure":0.3020833333333335,"gamma":0.22916666666666674,"contrast":0,"highlights":0.3020833333333335,"shadows":-0.65625,"whites":0.14583333333333348,"blacks":-0.2708333333333334,"clarity":0,"saturation":0.33333333333333326,"vibrance":-0.44791666666666674,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0.65625,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.20833333333333326,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"JP6 ","zh":"JP6 "},"id":"VkeltNkA8x","state":{"local_adjustments":[{"type":"gradient","startPoint":[0.5324074074074074,-0.6129707112970711],"endPoint":[-0.38425925925925924,0.6589958158995817],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0.3229166666666665,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":-0.16666666666666657,"tint":0,"exposure":0,"gamma":0.34375,"contrast":0.45833333333333326,"highlights":0.28125,"shadows":-0.3125,"whites":0.1875,"blacks":-0.11458333333333343,"clarity":0,"saturation":0,"vibrance":0.40625,"curves_red":[[0,0],[187.3653846153846,177.84134615384616],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,13.605769230769226],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.7708333333333333,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"JP7 ","zh":"JP7 "},"id":"VymeYN1RLl","state":{"local_adjustments":[{"type":"gradient","startPoint":[0,-0.5],"endPoint":[0,0.5],"adjustments":{"mosaic_size":0,"blur":0,"exposure":0,"contrast":0,"temperature":0.7395833333333333,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.4375,"tint":0,"exposure":0.4895833333333335,"gamma":-0.09374999999999994,"contrast":1,"highlights":-0.34375000000000006,"shadows":0.17708333333333326,"whites":-0.48958333333333326,"blacks":-0.20833333333333343,"clarity":0,"saturation":-0.5208333333333333,"vibrance":0,"curves_red":[[0,0],[101.64903846153847,100.28846153846155],[207.77403846153845,214.5769230769231],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[112.53365384615384,111.1730769230769],[203.6923076923077,194.16826923076923],[255,255]],"curves_all":[[0,43.53846153846155],[255,255]],"hue_red":0.30208333333333326,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":-0.40625,"hue_purple":0,"hue_magenta":0.375,"saturation_red":0.5208333333333335,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":-0.07291666666666657,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.6197916666666667,"shadows_saturation":0.7,"balance":0,"sharpen":0.21875,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.6979166666666667,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0.5729166666666667,"vignette_size":1,"vignette_exposure":0.14583333333333348,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"JP8 ","zh":"JP8 "},"id":"NygtNJC8x","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.6979166666666667,"diffuse":0,"temperature":0,"tint":0,"exposure":0.6770833333333335,"gamma":-0.3229166666666666,"contrast":0.6145833333333333,"highlights":0.39583333333333326,"shadows":-0.2604166666666665,"whites":-0.22916666666666657,"blacks":1,"clarity":0,"saturation":0,"vibrance":0,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,55.78365384615387],[120.69711538461539,172.39903846153845],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0.07291666666666674,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.5989583333333335,"highlights_saturation":0.1499999999999999,"shadows_hue":0.5520833333333334,"shadows_saturation":0.1375,"balance":0,"sharpen":0.06770833333333337,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":-0.0008696134845601611,"denoise":0,"chroma_amount":0}}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.purchase.filters.pack9",
    "pack_name":{"en":"Clear","nl":"Helder","fr":"Transparent","de":"Klar","id":"Bersih","it":"Chiarezza","ja":"クリア","ko":"맑은","pt":"Limpo","es":"Despejado","ru":"Ясно","th":"ชัดเจน","tr":"Temiz","vt":"Rõ ràng","zh":"清新","zh-tw":"清新","ar":"واضح"},
    "pack_description":{"en":"Remove fogginess and add extra depth and clarity to images. Suitable for nature scenes with high degree of contrast between atmosphere and remaining scene.","nl":"Verwijder vervaging en voeg extra diepte en helderheid aan de afbeeldingen toe. Geschikt voor natuurtscènes met een hoge mate van contrast tussen atmosfeer en resterende scène.","fr":"Enlève le flou et donne plus de profondeur et de clarté aux images. Convient aux scènes de nature présentant un degré élevé de contraste entre l’atmosphère et le reste de la scène.","de":"Entfernen Sie Nebel und fügen Sie Ihren Bildern zusätzliche Tiefe und Klarheit hinzu. Geeignet für Naturszenen mit starkem Kontrast zwischen der Atmosphäre und dem Rest der Szene.","id":"Hapus yang agak kabur lalu tambahkan tambahan kedalaman dan kejelasan gambar. Cocok untuk pemandangan alam dengan tingkat tinggi kontras antara atmosfer dan sisa adegan.","it":"Rimuove la nebbia ed aggiunge maggiore profondità e chiarezza alle immagini. Adatto per scene dall’alto contrasto tra atmosfera e scenario rimanente.","ja":"くもりを消し、画像に余分な深さと透明度を追加します。雰囲気と残りのシーンとのコントラストの高い自然の風景に適しています。","ko":"흐려짐을 제거하고 이미지에 여분의 깊이와 선명도를 추가 할 수 있습니다. 대비 엔터 분위기 남은 단계 높은 수준의 자연 풍경에 적합합니다.","pt":"Remove efeitos esfumaçados indesejados e adiciona profundidade extra e claridade às imagens. Ideal para cenas da natureza com alto grau de contraste entre a atmosfera e o restante da cena.","es":"Remueve la nebulosidad y añade profundidad y claridad adicional a las imágenes. Adecuado para escenas en la naturaleza con un alto grado de contraste entre la atmósfera y la escena restante.","ru":"Удаление тумана и добавление дополнительной глубины и четкости изображения. Подходит для снимков природы с высокой степенью контрастности между атмосферой и объектами.","th":"นำ fogginess และเพิ่มความลึกเป็นพิเศษและความคมชัดให้กับภาพ เหมาะสำหรับฉากธรรมชาติที่มีระดับสูงของบรรยากาศทางตรงกันข้าม Entre และขั้นตอนที่เหลือ","tr":"Sisi ortadan kaldırın ve resimlere ekstra derinlik ve netlik katın. Doğa sahneleri için atmosfer ve sahnenin geri kalanındaki yüksek dereceli kontrastla için uyumlu.","vt":"Hủy bỏ tư tưởng cũ và bổ sung thêm chiều sâu, đường nét cho bức ảnh. Thích hợp cho cảnh thiên nhiên với độ tương phản cao giữa không khí và toàn cảnh còn lại","zh":"这一组滤镜色调大气明亮,适合表现阳光、温暖的场景。","zh-tw":"這一組濾鏡色調大氣明亮,適合表現陽光、溫暖的場景。","ar":"إزالة الضبابية وإضافة عمق إضافي والوضوح على الصور. مناسبة لمشاهد الطبيعة مع درجة عالية من التباين بين الغلاف الجوي والمشهد المتبقية."},
    "pack_maker":"Polarr",
    "is_free":0,
    "version":133,
    "preview_images":["cl1.jpg","cl2.jpg","cl3.jpg"],
    "filters":[
      {"name":{"en":"CL1 ","zh":"CL1 "},"id":"NJ8FjqxAUg","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.3876810923825553,"diffuse":0.6097100671637641,"temperature":-0.13038651544026536,"tint":0,"exposure":0.18415453216644706,"gamma":0,"contrast":0.7762317982496707,"highlights":0.12864728847114493,"shadows":-0.22289858826576903,"whites":0.5727052380335627,"blacks":-0.4264251484818772,"clarity":0.12864728847114493,"saturation":0,"vibrance":0,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,47.27884615384616],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0.28125,"hue_green":0.22916666666666674,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0.23966177586174942,"saturation_aqua":0,"saturation_blue":0.18415453216644706,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.18415453216644706,"shadows_saturation":-0.0008696134845601611,"balance":0,"sharpen":0.0731400447758428,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.11014487390604422,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CL2 ","zh":"CL2 "},"id":"VyQFj9lAIx","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.202656946731548,"diffuse":0.202656946731548,"temperature":-0.25,"tint":0,"exposure":0,"gamma":0,"contrast":0.29516901955705155,"highlights":-0.3154106610912727,"shadows":0.6282124817288648,"whites":0.2581641904268501,"blacks":0.23966177586174942,"clarity":0,"saturation":0,"vibrance":0.3506762632523539,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[130.22115384615384,158.79326923076923],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.11014487390604422,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.16565211760134635,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CL3 ","zh":"CL3 "},"id":"EkVFscl08e","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.6979166666666667,"diffuse":0,"temperature":-0.5416666666666665,"tint":0,"exposure":0.6770833333333335,"gamma":-0.3229166666666666,"contrast":0.6145833333333333,"highlights":0.5520833333333333,"shadows":-0.2604166666666665,"whites":-0.22916666666666657,"blacks":1,"clarity":0,"saturation":0,"vibrance":0.6979166666666667,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,55.78365384615387],[122.05769230769232,190.08653846153845],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0.07291666666666674,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.5989583333333335,"highlights_saturation":0.1499999999999999,"shadows_hue":0.5520833333333334,"shadows_saturation":0.1375,"balance":0,"sharpen":0.06770833333333337,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-1,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0.5520833333333333,"vignette_size":0.9479166666666667,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CL4 ","zh":"CL4 "},"id":"VkBticeCUx","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.10416666666666674,"diffuse":0,"temperature":0,"tint":0,"exposure":0,"gamma":-0.1875,"contrast":0.6875,"highlights":0.44791666666666674,"shadows":0.1875,"whites":0.02083333333333326,"blacks":0.25,"clarity":0,"saturation":0,"vibrance":0.25,"curves_red":[[0,0],[130.22115384615387,122.05769230769232],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,58.50480769230768],[103.00961538461539,186.0048076923077],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0.7083333333333334,"highlights_saturation":0.7375,"shadows_hue":0.5416666666666666,"shadows_saturation":0.3,"balance":0,"sharpen":0.16145833333333331,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.6354166666666667,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":1,"vignette_size":1,"vignette_exposure":0.11458333333333326,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CL5 ","zh":"CL5 "},"id":"EJGti9gCLe","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.15625,"tint":0,"exposure":-0.4375,"gamma":0,"contrast":0.5,"highlights":-0.2916666666666666,"shadows":0.28125,"whites":-0.31249999999999994,"blacks":0.4583333333333335,"clarity":0,"saturation":0,"vibrance":0,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[115.2548076923077,200.97115384615384],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.046875,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-1,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.15625,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CL6 ","zh":"CL6 "},"id":"VkWFj5e0Ix","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.5625,"diffuse":0,"temperature":0,"tint":0,"exposure":0.33217384868725297,"gamma":-0.125,"contrast":0.41666666666666674,"highlights":-0.40625,"shadows":0.19791666666666674,"whites":0.15625,"blacks":0.4583333333333335,"clarity":0,"saturation":0,"vibrance":0,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[157.4326923076923,139.74519230769232],[255,255]],"curves_all":[[0,0],[108.45192307692308,151.9903846153846],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0.07291666666666674,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.036458333333333336,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":-0.0008696134845601611,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CL7 ","zh":"CL7 "},"id":"N1FiqeRLg","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":-0.0008696134845601611,"diffuse":1,"temperature":0.2604166666666665,"tint":0,"exposure":-0.296908246526172,"gamma":0,"contrast":0.33217384868725297,"highlights":0.14714970303624564,"shadows":0.27666660499195084,"whites":0.3876810923825553,"blacks":0.0731400447758428,"clarity":0.03613521564564137,"saturation":0,"vibrance":0.44318833607785746,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0.16565211760134635,"luminance_orange":0,"luminance_yellow":0.16565211760134635,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.40618350694765604,"shadows_saturation":0.18415453216644706,"balance":0,"sharpen":0.03613521564564137,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.16565211760134635,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CL8 ","zh":"CL8 "},"id":"VJgYsqxCLe","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0,"diffuse":0,"temperature":0.25,"tint":0,"exposure":0.4895833333333335,"gamma":-0.09374999999999994,"contrast":1,"highlights":-0.34375000000000006,"shadows":0.17708333333333326,"whites":-0.48958333333333326,"blacks":-0.20833333333333343,"clarity":0,"saturation":-0.5208333333333333,"vibrance":0.3125,"curves_red":[[0,0],[101.64903846153847,100.28846153846155],[207.77403846153845,214.5769230769231],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[112.53365384615384,111.1730769230769],[203.6923076923077,194.16826923076923],[255,255]],"curves_all":[[0,43.53846153846155],[130.22115384615384,164.2355769230769],[255,255]],"hue_red":0.30208333333333326,"hue_orange":0,"hue_yellow":-0.5833333333333333,"hue_green":-0.53125,"hue_aqua":0,"hue_blue":-0.40625,"hue_purple":0,"hue_magenta":0.375,"saturation_red":0.5208333333333335,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":-0.07291666666666657,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.6197916666666667,"shadows_saturation":0.7,"balance":0,"sharpen":0.21875,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.6979166666666667,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0.5729166666666667,"vignette_size":1,"vignette_exposure":0.14583333333333348,"denoise":0,"chroma_amount":0}}
    ]
  },
  {
    "pack_id":"co.polarr.ppe.purchase.filters.pack11",
    "pack_name":{"en":"Curves","nl":"Curven","fr":"Courbes","de":"Curves","id":"Curves","it":"Curva","ja":"曲線","ko":"곡선","pt":"Curvas","es":"Curvas","ru":"Кривые","th":"เส้นโค้ง","tr":"Eğriler","vt":"Đường nét","zh":"曲线","zh-tw":"曲線","ar":"منحنيات"},
    "pack_description":{"en":"A user-friendly introduction to the types of adjustments usually made in curves tools. Watch as complex and subtle enhancements are made along both the main and RGB channels.","nl":"Een gebruiksvriendelijke Inleiding tot de soorten aanpassingen die meestal in curve tools worden gemaakt. Zie hoe complexe en subtiele verbeteringen worden gemaakt langs de hoofd- en RGB-kanalen beide.","fr":"Une introduction conviviale aux types de réglages habituellement effectués dans les outils courbes. Regardez également comment les améliorations complexes et subtiles sont apportées sur les deux canaux principal et RGB.","de":"Eine benutzerfreundliche Einführung in die Arten der Anpassungen, die man normalerweise mit Curve-Tools vornimmt. Sehen Sie zu, wie komplexe und subtile Verbesserungen vorgenommen werden, sowohl auf dem Haupt- als auch auf dem RGB Kanal.","id":"Sebuah pengantar yang ramah pengguna untuk jenis penyesuaian biasanya dibuat dalam alat kurva. Lihat saat yang kompleks dan perangkat tambahan halus yang dibuat di kedua saluran utama dan RGB.","it":"Una introduzione facilitata per l’utente ai tipi di correzioni solitamente effettuati con gli strumenti per le curve. Guarda come vengono effettuati miglioramenti complessi e sottili sia sul canale principale che sui canali RGB.","ja":"通常の曲線ツールで調整するユーザーに簡単なタイプです。メインとRGBチャンネルで行われる、大小の微妙な強化が行われます。","ko":"조정의 종류에 대한 사용자 친화적 인 도입은 '일반적으로 곡선 도구로했다. 복잡한 시계 미묘한 향상은 모두 주 및 RGB 채널을 따라 만들어집니다.","pt":"Uma introdução amigável ao usuário nos tipos de ajustes que são geralmente feitos com ferramentas de curvas. Veja como ampliações complexas e sutis são feitas tanto no canal principal como nos canais RGB.","es":"Una introducción amigable a los tipos de ajustes que se hacen generalmente con las herramientas de curvas. Observa como complejas y sutiles mejoras se realizan a lo largo de ambos canales, el principal y RGB.","ru":"Понятное введение в варианты настройки, которые обычно делаются посредством регулировки кривыми. Смотрите как сложные и тонкие усовершенствования делаются вдоль основного и RGB-каналов.","th":"แนะนำใช้งานง่ายเพื่อชนิดของการปรับ 'มักจะทำในเครื่องมือเส้นโค้ง ดูเป็นที่ซับซ้อนและการปรับปรุงที่ลึกซึ้งจะทำพร้อมทั้งช่องทางหลักและ RGB","tr":"Genellikle eğri aletleriyle yapılan ayarlama türlerine kullanıcı-dostu bir giriş rehberi. Karışık ve anlaşılması zor geliştirmelerin hem genel hem de RGB kanallarda nasıl yapıldığını görün.","vt":"Giới thiệu đến người dùng những loại điều chỉnh thường được sử dụng trong các công cụ điều chỉnh đường nét. Hãy xem các cải tiến phức tạp lẫn tinh tế được thực hiện dọc theo kênh chính và các kênh RGB","zh":"这一组滤镜通过灵活的勾画曲线,再结合精确的曝光调整,让 画面的色彩与光线都呈现出最佳状态。","zh-tw":"這一組濾鏡通過靈活的勾畫曲線,再結合精確的曝光調整,讓 畫面的色彩與光線都呈現出最佳狀態。","ar":"وسهل الاستعمال مقدمة لأنواع من التعديلات عادة ما تكون مصنوعة في أدوات المنحنيات. مشاهدة معقدة مثل ويتم إجراء التحسينات الطفيفة على طول كل من القنوات الرئيسية وRGB."},
    "pack_maker":"Polarr",
    "is_free":0,
    "version":133,
    "preview_images":["cv1.jpg","cv2.jpg","cv3.jpg"],
    "filters":[
      {"name":{"en":"CV1 ","zh":"CV1 "},"id":"EkUjpLW0Lg","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":-0.10416666666666674,"diffuse":0,"temperature":-0.0022734654108477192,"tint":-0.0022734654108477192,"exposure":0.2679441377070477,"gamma":0,"contrast":0.4550178629425139,"highlights":0.1875,"shadows":0.24715816823644032,"whites":-0.0022734654108477192,"blacks":-0.0022734654108477192,"clarity":0.15625,"saturation":0.08087041247158178,"vibrance":0.2887301071776551,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,235.95192307692307]],"curves_all":[[0,59.62019230769232],[80,90],[255,255]],"hue_red":0.01851250405975957,"hue_orange":-0.0022734654108477192,"hue_yellow":-0.3229166666666666,"hue_green":0.29166666666666674,"hue_aqua":-0.12698928223449168,"hue_blue":-0.1062033127638844,"hue_purple":-0.21013316011692107,"hue_magenta":0.14322832088340376,"saturation_red":-0.08333333333333326,"saturation_orange":-0.31406300746995774,"saturation_yellow":-0.25170509905813576,"saturation_green":-0.34375,"saturation_aqua":0.4550178629425139,"saturation_blue":-0.21013316011692107,"saturation_purple":0.4342318934719065,"saturation_magenta":0.24715816823644032,"luminance_red":-0.0022734654108477192,"luminance_orange":0,"luminance_yellow":-0.0022734654108477192,"luminance_green":0,"luminance_aqua":0,"luminance_blue":-0.0022734654108477192,"luminance_purple":-0.0022734654108477192,"luminance_magenta":0,"highlights_hue":-0.8752841831763559,"highlights_saturation":0.01851250405975957,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CV2 ","zh":"CV2 "},"id":"NyEia8bAIl","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":-0.33333333333333326,"diffuse":0,"temperature":0,"tint":0,"exposure":0.25,"gamma":0,"contrast":0.46875,"highlights":0.1875,"shadows":0.5381617408249431,"whites":-0.2708333333333334,"blacks":-0.16666666666666674,"clarity":0,"saturation":-0.16856122117570638,"vibrance":-0.5208333333333333,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,60],[80,90],[255,255]],"hue_red":0.060084443000974375,"hue_orange":-0.0646313738226697,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":-0.12698928223449168,"hue_purple":0,"hue_magenta":0,"saturation_red":0.16401429035401094,"saturation_orange":-0.4595647937642091,"saturation_yellow":0,"saturation_green":0.30951607664826253,"saturation_aqua":0.5381617408249431,"saturation_blue":-0.27249106852874305,"saturation_purple":0,"saturation_magenta":0.22637219876583314,"luminance_red":0.5381617408249431,"luminance_orange":0.4550178629425139,"luminance_yellow":1,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.6510416666666666,"shadows_saturation":0.5625,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CV3 ","zh":"CV3 "},"id":"4ySia8-AIl","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":-0.33333333333333326,"diffuse":0,"temperature":0.33333333333333326,"tint":0.01851250405975957,"exposure":0.3303020461188697,"gamma":-0.16666666666666674,"contrast":0.5104166666666667,"highlights":0.22916666666666652,"shadows":0.16401429035401094,"whites":0.3303020461188697,"blacks":0.02083333333333348,"clarity":-0.1477752517050991,"saturation":-0.21013316011692107,"vibrance":0,"curves_red":[[0,0],[177.84134615384613,158.79326923076923],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,20],[30,45],[255,255]],"hue_red":-0.0022734654108477192,"hue_orange":0,"hue_yellow":0.20833333333333326,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":-0.0022734654108477192,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":-0.0022734654108477192,"luminance_green":0,"luminance_aqua":0.2679441377070477,"luminance_blue":0.4342318934719065,"luminance_purple":0.3926599545306917,"luminance_magenta":0.2679441377070477,"highlights_hue":-0.0022734654108477192,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":-0.0022734654108477192,"balance":-0.0022734654108477192,"sharpen":0.20558622929522574,"grain_amount":0,"grain_size":0,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":1,"vignette_highlights":0,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CV4 ","zh":"CV4 "},"id":"EJmsaLbCIl","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.3876810923825553,"diffuse":0.6097100671637641,"temperature":0,"tint":0,"exposure":-0.4155555555555553,"gamma":0.18222222222222229,"contrast":1,"highlights":-0.19999999999999996,"shadows":-0.28222222222222215,"whites":0.8,"blacks":0.1333333333333333,"clarity":0.06666666666666665,"saturation":-1,"vibrance":-1,"curves_red":[[0,0],[183.92032967032966,187.94250392464676],[255,255]],"curves_green":[[0,0],[83.26530612244898,83.26530612244898],[208.16326530612244,202.95918367346937],[255,255]],"curves_blue":[[0,0],[183.57221350078493,154.70231554160128],[255,210.10096153846155]],"curves_all":[[0,54.64285714285714],[20.81632653061224,63.75000000000001],[59.84693877551021,67.65306122448982],[92.37244897959182,93.67346938775512],[130.10204081632654,128.80102040816325],[208.16326530612244,206.8622448979592],[255,239.3877551020408]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.2604166666666667,"shadows_saturation":0.1875,"balance":0,"sharpen":0.43333333333333335,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.08888888888888896,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.11014487390604422,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CV5 ","zh":"CV5 "},"id":"E1Zo68ZCUe","state":{"local_adjustments":[{"type":"radial","feather":1,"size":[0.75,0.5],"position":[-0.010121457489878541,0.06477732793522267],"invert":true,"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.4487179487179487,"contrast":0,"temperature":-0.17948717948717952,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":-0.33333333333333326,"diffuse":0,"temperature":0.29166666666666674,"tint":0.022222222222222143,"exposure":0.4444444444444444,"gamma":0.03125,"contrast":0,"highlights":0.5,"shadows":0.3111111111111111,"whites":-0.1555555555555555,"blacks":0.022222222222222143,"clarity":0.15555555555555567,"saturation":-0.1777777777777778,"vibrance":0.40000000000000013,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,15.612244897959183],[46.83673469387755,52.04081632653062],[255,255]],"curves_all":[[0,20.816326530612244],[66.35204081632654,70.25510204081634],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0.30208333333333326,"hue_green":0.375,"hue_aqua":0,"hue_blue":0,"hue_purple":0,"hue_magenta":0,"saturation_red":0,"saturation_orange":-0.3333333333333334,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":-0.3854166666666666,"saturation_purple":-0.3541666666666666,"saturation_magenta":-0.5416666666666667,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0.5,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.5111111111111113,"vignette_feather":0.573333334,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CV6 ","zh":"CV6 "},"id":"4Jzsa8-CLx","state":{"local_adjustments":[{"type":"radial","feather":1,"size":[1.0072859744990892,0.6475409836065573],"position":[0,0],"invert":true,"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.5,"contrast":0,"temperature":-0.641025641025641,"tint":0,"highlights":-0.6666666666666665,"shadows":0,"clarity":0,"saturation":-0.20512820512820507},"index":0},{"type":"gradient","startPoint":[0,-0.5],"endPoint":[0,0.5],"adjustments":{"exposure":0,"contrast":0,"temperature":1,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":1}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":-0.33333333333333326,"diffuse":0,"temperature":0,"tint":0,"exposure":0.33333333333333326,"gamma":-0.06666666666666665,"contrast":0,"highlights":0,"shadows":0,"whites":0,"blacks":0,"clarity":0,"saturation":-0.19999999999999996,"vibrance":0.3111111111111111,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[172.39903846153845,147.90865384615384],[255,255]],"curves_all":[[0,26.020408163265305],[61.147959183673464,65.05102040816327],[144.41326530612244,144.41326530612244],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":0,"hue_green":0,"hue_aqua":0.3229166666666665,"hue_blue":-0.4791666666666666,"hue_purple":0,"hue_magenta":-0.48958333333333326,"saturation_red":0,"saturation_orange":0,"saturation_yellow":0,"saturation_green":0,"saturation_aqua":0,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":-0.1777777777777778,"vignette_feather":1,"vignette_highlights":1,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CV7 ","zh":"CV7 "},"id":"EysTUZ08x","state":{"local_adjustments":[],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.202656946731548,"diffuse":0.202656946731548,"temperature":0,"tint":0,"exposure":0,"gamma":0,"contrast":0.29516901955705155,"highlights":-0.3154106610912727,"shadows":0.6282124817288648,"whites":0.2581641904268501,"blacks":0.23966177586174942,"clarity":0,"saturation":0,"vibrance":0.3506762632523539,"curves_red":[[0,0],[255,255]],"curves_green":[[0,0],[255,255]],"curves_blue":[[0,0],[255,255]],"curves_all":[[0,0],[255,255]],"hue_red":0.16666666666666674,"hue_orange":0,"hue_yellow":0,"hue_green":-0.3333333333333334,"hue_aqua":-0.17708333333333343,"hue_blue":0.35416666666666674,"hue_purple":-0.3958333333333334,"hue_magenta":0.5,"saturation_red":0,"saturation_orange":0,"saturation_yellow":-0.2708333333333334,"saturation_green":0.5,"saturation_aqua":-0.14583333333333326,"saturation_blue":0,"saturation_purple":0,"saturation_magenta":0,"luminance_red":0,"luminance_orange":0,"luminance_yellow":0,"luminance_green":0,"luminance_aqua":0,"luminance_blue":0,"luminance_purple":0,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0,"shadows_saturation":0,"balance":0,"sharpen":0.11014487390604422,"grain_amount":0,"grain_size":0.25,"distortion_amount":0,"fringing":0,"vignette_amount":0,"vignette_feather":0.5,"vignette_highlights":0.5,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0.16565211760134635,"denoise":0,"chroma_amount":0}},
      {"name":{"en":"CV8 ","zh":"CV8 "},"id":"Eklo6IZ0Ie","state":{"local_adjustments":[{"type":"radial","feather":1,"size":[0.9052823315118397,0.5],"position":[0,0],"invert":true,"adjustments":{"mosaic_size":0,"blur":0,"exposure":-0.29487179487179493,"contrast":0,"temperature":0,"tint":0,"highlights":0,"shadows":0,"clarity":0,"saturation":0},"index":0}],"flip_y":0,"flip_x":0,"blend":0,"color_denoise":0,"luminance_denoise":0,"dehaze":0.20833333333333326,"diffuse":0,"temperature":0.125,"tint":0.01851250405975957,"exposure":0.01851250405975957,"gamma":0.14583333333333326,"contrast":0.6041666666666667,"highlights":-0.21875,"shadows":0.2887301071776551,"whites":-0.12698928223449168,"blacks":0.16401429035401094,"clarity":0,"saturation":-0.12698928223449168,"vibrance":0.5173757713543359,"curves_red":[[0,3],[255,255]],"curves_green":[[0,10],[255,255]],"curves_blue":[[0,10],[255,255]],"curves_all":[[0,5],[30,40],[255,255]],"hue_red":0,"hue_orange":0,"hue_yellow":-0.0022734654108477192,"hue_green":0,"hue_aqua":-0.18934719064631378,"hue_blue":-0.23091912958752847,"hue_purple":-0.04384540435206241,"hue_magenta":0,"saturation_red":0.6213056187073724,"saturation_orange":0.060084443000974375,"saturation_yellow":-0.02305943488145501,"saturation_green":0.6005196492367653,"saturation_aqua":0.12244235141279636,"saturation_blue":0.2679441377070477,"saturation_purple":-0.25170509905813576,"saturation_magenta":0,"luminance_red":-0.6882104579408899,"luminance_orange":0,"luminance_yellow":0,"luminance_green":-0.5427086716466385,"luminance_aqua":0,"luminance_blue":0.03929847353036697,"luminance_purple":0.03929847353036697,"luminance_magenta":0,"highlights_hue":0,"highlights_saturation":0,"shadows_hue":0.16401429035401094,"shadows_saturation":0.03929847353036697,"balance":0,"sharpen":0.3303020461188697,"grain_amount":-0.0022734654108477192,"grain_size":0,"distortion_amount":0,"fringing":0,"vignette_amount":-0.02305943488145501,"vignette_feather":1,"vignette_highlights":0,"vignette_roundness":0,"vignette_size":1,"vignette_exposure":0,"denoise":0,"chroma_amount":0}}
    ]
  }
];


},{}],341:[function(require,module,exports){
var $ = require("jquery");
var manager = require("./manager.coffee");
var utils = require("./utils.coffee");
var QRCode = require("./lib/qrcode.min.js");

var qrSize = 340;

var CustomFilterQRCode = function () {
  // nothing to initialize, the qrcode object needs to initialize everytime
  // to avoid dom reference issues or internal onload event firing twice
  // please make sure you initialize the qrcode lib everytime you generate a new code
};

CustomFilterQRCode.prototype.create = function(filter_url, filter_content, callback) {

  filter_content = filter_content || {}; // you should use whatever format that makes sense for you for ths object

  var draw = function(qrImage) {

    var canvas = document.createElement("canvas");
    var ctx = canvas.getContext("2d");
    var image = Filtr.getInfo();
    var width, height;
    var orientation;

    var filterName = decodeURIComponent(filter_content.name);
    var userName = manager.state.user.user_name || "Polarrian";

    var padding = {
      top: 100,
      right: 8,
      bottom: 8,
      left: 8
    };

    var borderWidth = 8;
    var frameBorderWidth = 2;

    canvas.width = 1280;
    canvas.height = 1280;

    if (image.width > image.height) {
      // landscape image
      orientation = "landscape";
      width = canvas.width - padding.left - padding.right;
      height = (canvas.height - padding.top - padding.bottom - borderWidth) / 2;
    } else {
      // portrait image
      orientation = "portrait";
      width = (canvas.width - padding.left - padding.right - borderWidth) / 2;
      height = canvas.height - padding.top - padding.bottom;
    }

    // render before and after images
    var beforeState = Filtr.getDefaultState(true);
    var currentState = Filtr.getState(true);
    var afterState = Filtr.utils.clone(filter_content.state);

    var fillSize = Filtr.utils.resize(
      [image.width, image.height],
      [width, height],
      "fill"
    );

    var h = width / fillSize[0];
    var v = height / fillSize[1];

    beforeState.blur_opacity = 0;
    afterState.blur_opacity = 0;
    afterState.crop = [(1-h)/2, (1-v)/2, h, v];
    afterState.rotate90 = currentState.rotate90;
    afterState.rotation = currentState.rotation;
    beforeState.crop = afterState.crop;
    beforeState.rotate90 = currentState.rotate90;
    beforeState.rotation = currentState.rotation;

    var beforeImage = Filtr.export("data", {
      width: width,
      height: height,
      settings: beforeState
    });

    var afterImage = Filtr.export("data", {
      width: width,
      height: height,
      settings: afterState
    });

    var beforeImageData = ctx.createImageData(width, height);
    beforeImageData.data.set(new Uint8ClampedArray(beforeImage.data));

    var afterImageData = ctx.createImageData(width, height);
    afterImageData.data.set(new Uint8ClampedArray(afterImage.data));

    // canvas background
    ctx.fillStyle = "#aaa";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    // ctx.fillStyle = "#"+((1<<24)*Math.random()|0).toString(16);
    ctx.fillStyle = "white";
    ctx.fillRect(frameBorderWidth, frameBorderWidth, canvas.width-frameBorderWidth*2, canvas.height-frameBorderWidth*2);

    // draw before and after images
    ctx.putImageData(beforeImageData, padding.left, padding.top);
    if (orientation === "landscape") {
      ctx.putImageData(afterImageData, padding.left, padding.top + height + borderWidth);
    } else {
      ctx.putImageData(afterImageData, padding.left + width + borderWidth, padding.top);
    }

    // draw qr code
    ctx.fillRect(canvas.width - qrSize - frameBorderWidth, canvas.height - qrSize - frameBorderWidth, qrSize, qrSize);
    ctx.drawImage(qrImage, canvas.width - qrSize + borderWidth, canvas.height - qrSize + borderWidth);

    // draw header
    ctx.fillStyle = "#222";
    ctx.font = "48px sans-serif";
    ctx.fillText(filterName, 32, padding.top/2 + 18);
    var text = ctx.measureText(filterName);

    // draw by line
    ctx.fillStyle = "#aaa";
    ctx.font = "30px sans-serif";
    ctx.fillText("by " + userName, text.width + 32 + 10, padding.top/2 + 18);

    // polarr mark
    ctx.fillStyle = "#aaa";
    ctx.font = "19px sans-serif";
    ctx.textAlign = "right";
    ctx.fillText("scan me in", canvas.width - 32, padding.top/2 - 10);
    ctx.fillStyle = "#222";
    ctx.font = "36px sans-serif";
    ctx.fillText("polarr", canvas.width - 32, padding.top/2 + 22);
    

    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

    callback(imageData);
  };

  var create_code = function(filter_url){
    this.qrcode_dom = document.createElement("div");
    this.qrcode = new QRCode(this.qrcode_dom, {
      width: 340 - 16,
      height: 340 - 16,
      colorDark : "#000000",
      colorLight : "#ffffff",
      correctLevel : QRCode.CorrectLevel.H
    });
    this.qrcode.makeCode(filter_url);
    var qrImage = this.qrcode_dom.querySelector("img");
    console.log("qr code url:", filter_url);

    // qrImage loaded
    if (qrImage.naturalWidth) {
      draw(qrImage);
    } else {
      qrImage.onload = function() {
        draw(qrImage);
      };
    }
  }.bind(this);

  create_code(filter_url);
};

module.exports = new CustomFilterQRCode();
},{"./lib/qrcode.min.js":317,"./manager.coffee":338,"./utils.coffee":360,"jquery":319}],342:[function(require,module,exports){
function splineGraph(id, options) {

    var self = this;

    var startX, startY;

    var graph, i, ctx, spline, started, curPoint, pos,
        graphFocus = false,
        hover = false,
        changed = false,
        factor = 2,
        radius = 12 * factor,
        pointsX = [], pointsY = [];

    graph = (typeof id == "string") ? document.getElementById(id) : graph = id;
    graph.tabIndex = -1;

    ctx = graph.getContext("2d");

    options = options || {};
    this.mode = options.mode || "cubic";
    this.name = options.name || "curve";
    this.color = options.color || "#FFF";
    this.points = options.points || [];
    this.width = (options.width || 255) * factor + radius * 2;
    this.height = (options.height || 255) * factor + radius * 2;
    this.windowScale = (this.width - radius * 2)/(255 * factor);
    this.mirror = options.mirror || false;
    this.rotated = options.rotated;
    this.coordText = "";
    graph.width = this.width
    graph.height = this.height
    graph.style.position = "absolute";

    var callback = options.onUpdate || function(){};
    var updateEnd = options.onUpdateEnd || function(){};

    var clamp = function(val, min, max) {
        return Math.max(min, Math.min(max, val));
    }

    var lerp = function(t, a, b) {
        return a * (1 - t) + b * t;
    }

    this.sortPoints = function(silent, drawPoints) {

        var px=[],py=[];

        self.points.sort(function(a,b){
            return a[0] - b[0];
        });

        for(i=0; i < self.points.length; i++){
            px.push(self.points[i][0] * factor);
            py.push(self.points[i][1] * factor);
        }

        pointsX=px;
        pointsY=py;

        self.draw(drawPoints, silent);
    };

    this.replacePoints = function(points, drawPoints) {
        self.points = points.slice(0);
        self.sortPoints(true, drawPoints)
    }

    this.addPoint = function(x,y) {
        self.points.push([x / factor, y / factor]);
        self.sortPoints();

    };

    this.getOffsetTouch = function(e){
        var input = e.targetTouches ? e.targetTouches[0] : e;
        var offset = input.target.getBoundingClientRect();
        var scale = graph.clientWidth / graph.width;
        var x, y;
        if (self.rotated) {
          y = graph.width - (input.pageX - offset.left) / scale;
          x = graph.height - (input.pageY - offset.top) / scale;
        } else {
          x = (input.pageX - offset.left) / scale;
          y = graph.height - (input.pageY - offset.top) / scale;
        }
        return {
            x: (x - radius)/self.windowScale,
            y: (y - radius)/self.windowScale
        };
    };

    this.findPoints = function(e) {
        if (started) return;

        var mouse = self.getOffsetTouch(e);

        hover = false;
        graph.style.cursor = "crosshair";

        for(i=0; i < pointsX.length; i++) {
            //if( (mouse.x<=pointsX[i]+radius && mouse.x>=pointsX[i]-radius) &&
            //    (mouse.y<=pointsY[i]+radius && mouse.y>=pointsY[i]-radius) ){
            // x only
            if(mouse.x<=pointsX[i]+radius && mouse.x>=pointsX[i]-radius) {
                hover = true;
                curPoint = i;
                graph.style.cursor = "move";
            }
        }
    };

    this.setPoint = function(e) {
        e.stopPropagation();

        // right click
        if (e.which === 3) return;

        changed = false;

        var input = e.targetTouches ? e.targetTouches[0] : e;

        var startX, startY;
        if (self.rotated) {
            startX = -input.pageY
            startY = input.pageX
        } else {
            startX = input.pageX
            startY = input.pageY
        }

        self.findPoints(e)

        if(!hover){
            pos = self.getOffsetTouch(e);
            self.addPoint(pos.x, pos.y);
            changed = true;
            hover = true;
        } else {
            pos = {
                x: pointsX[curPoint],
                y: pointsY[curPoint]
            };
        }

        self.findPoints(e);

        self.draw(hover ? true : false);

        self.selectedPoint = curPoint;

        //started = true;
        graphFocus = true;

        document.addEventListener('touchmove', self.movePoint, false);
        document.addEventListener('touchend', self.applyPoint, false);
        document.addEventListener('touchcancel', self.applyPoint, false);

        document.addEventListener('mousemove', self.movePoint, false);
        document.addEventListener('pointermove', self.movePoint, false);
        document.addEventListener('mouseup', self.applyPoint, false);
        document.addEventListener('pointerup', self.applyPoint, false);
    };

    this.constrainPoint = function(i,x) {

        if(i > 0)
            x = x<=pointsX[i-1]+radius ? pointsX[i-1]+radius : x;

        if(i < self.points.length-1)
            x = x>=pointsX[i+1]-radius ? pointsX[i+1]-radius : x;

        return x;
    };

    this.movePoint = function(e) {
        e.preventDefault();
        e.stopPropagation();
        var input = e.targetTouches ? e.targetTouches[0] : e;

        var pageX, pageY;
        if (self.rotated) {
            pageX = -input.pageY
            pageY = input.pageX
        } else {
            pageX = input.pageX
            pageY = input.pageY
        }

        if (!started) {
            startX = pageX;
            startY = pageY;
            started = true
            return;
        }

        changed = true;

        var x, y, tl, br;
        var scale = graph.clientWidth / graph.width;
        var movement = {
            x: (pageX - startX) / scale / self.windowScale,
            y: (pageY - startY) / scale / self.windowScale
        };

        icon = 58 * factor
        size = 255 * factor

        x = clamp(pos.x + movement.x, 0, size);
        y = clamp(pos.y - movement.y, 0, size);

        tl = icon * 1.5;
        x = clamp(x, tl - (size-y), size - (tl - y));
        y = size - Math.max(size-y, tl - x);

        i = self.selectedPoint;

        x = self.constrainPoint(i, x);

        self.points[i] = [x / factor, y / factor];
        pointsX[i] = x;
        pointsY[i] = y;

        var last = self.points.length-1;
        if (self.mirror && (i===0 || i===last)) {
            var index = i===0 ? last : 0;
            x = i===0 ? x+graph.width : x-graph.width;
            self.points[index] = [x, y];
            pointsX[index] = x;
            pointsY[index] = y;
        }

        self.draw();

    };

    this.applyPoint = function(e) {
        e.preventDefault();
        e.stopPropagation();

        updateEnd(changed);

        started = false;
        hover = false;
        changed = false;


        console.log("remove event listeners")
        document.removeEventListener('touchmove', self.movePoint, false);
        document.removeEventListener('touchend', self.applyPoint, false);
        document.removeEventListener('touchcancel', self.applyPoint, false);

        document.removeEventListener('mousemove', self.movePoint, false);
        document.removeEventListener('pointermove', self.movePoint, false);
        document.removeEventListener('mouseup', self.applyPoint, false);
        document.removeEventListener('pointerup', self.applyPoint, false);
    };

    this.nudgePoint = function(e) {
        e.preventDefault()

        var inc;

        if (graphFocus && self.selectedPoint) {

            var x = self.points[self.selectedPoint][0],
                i = self.selectedPoint;

            if (e.shiftKey) {
                inc = 10;
            } else {
                inc = 1;
            }

            if (e.which == 37) { //left
                self.points[i][0] = self.constrainPoint(i,x-inc);
                self.sortPoints();
            } else
            if (e.which == 38) { //up
                self.points[i][1] += inc;
                self.sortPoints();
            } else
            if (e.which == 39) { //right
                self.points[i][0] = self.constrainPoint(i,x+inc);
                self.sortPoints();
            } else
            if (e.which == 40) { //down
                self.points[i][1] -= inc;
                self.sortPoints();
            }
        }
    };

    this.selectPoint = function(e) {

        if(e.which == 9 && graphFocus){
            e.preventDefault();

            if(e.shiftKey){
                curPoint = Math.max(curPoint -= 1, 0);
            } else {
                curPoint = Math.min(curPoint += 1, self.points.length-1);
            }
            self.draw();
        }
    };

    this.deletePoint = function(e) {
        var deleteKey = !e || e.which === 46 || e.which === 8
        if(deleteKey && curPoint && pointsX.length>2){
            self.points.splice(curPoint,1);
            curPoint==1 ? (self.points.length>2 ? curPoint=1 : curPoint=null) : curPoint-=1;
            self.sortPoints();
        }
    };

    this.blurGraph = function(e) {
        graphFocus = false;
        curPoint = null;
        self.selectedPoint = null;
        self.draw(true, {silent: true});
    };

    this.draw = function(drawPoints, silent) {

        if(drawPoints === undefined)
            drawPoints = true;

        var cp = curPoint;
        var px = pointsX, py = pointsY;

        if (self.mirror) {
            cp = cp !== null ? curPoint+pointsX.length-1 : cp;
            py = pointsY.slice(0, -1).concat(pointsY).concat(pointsY.slice(1));

            var pBefore = pointsX.slice(0, -1);
            var pAfter = pointsX.slice(1);

            pBefore.forEach(function(v,i,a){
                a[i] -= self.width;
            });
            pAfter.forEach(function(v,i,a){
                a[i] += self.width;
            });

            px = pBefore.concat(pointsX).concat(pAfter);
        }

        if(self.mode === "monotonic"){
            spline = new Filtr.spline.monotonic(px,py);
        } else {
            spline = new Filtr.spline.cubic(px,py);
        }

        graph.height = this.height;
        graph.width = this.width;

        this.drawGrid();
        ctx.font = "100 32px monospace";
        ctx.fillStyle = "white";
        if(self.points[self.selectedPoint]){
            this.coordText = Math.floor(self.points[self.selectedPoint][0]) + ", " + Math.floor(self.points[self.selectedPoint][1]);
            ctx.fillText(this.coordText,factor*(self.points[self.selectedPoint][0]),graph.height-factor*(self.points[self.selectedPoint][1]-radius));
        }
        ctx.strokeStyle = self.color;
        ctx.lineWidth = 1.0 * factor;

        ctx.beginPath();

        var prevPos = {x:radius,y:graph.height - (py[0]*self.windowScale + radius)};

        //var data = [];
        var xi, yi;
        for(i=0;i<=graph.width-radius;i+=factor) {

            xi = Math.min(i*self.windowScale + radius,graph.width-radius);
            yi = graph.height - clamp(spline.interpolate(i)*self.windowScale, 0, graph.height - 2 * radius) - radius;

            if(isNaN(yi))
                yi = prevPos.y; //Math.floor(prevPos.y)+0.5;

            ctx.moveTo(prevPos.x,prevPos.y);
            ctx.lineTo(xi,yi);

            prevPos = {x:xi,y:yi};

            //data[i] = this.height - Math.round(y);

        }

        ctx.stroke();
        ctx.closePath();

        if(drawPoints) {

            var tau = Math.PI*2;

            for(i=0; i < px.length; i++) {

              if(isNaN(px[i]) || isNaN(py[i]))
                continue;

              ctx.beginPath();
              ctx.arc(px[i]*self.windowScale + radius, graph.height - py[i]*self.windowScale - radius, radius-2, 0, tau, false);
              ctx.closePath();

              if(i === cp) {
                ctx.fillStyle = self.color;
              } else {
                ctx.fillStyle = "transparent";
                ctx.save();
                ctx.clip();
                ctx.clearRect(0, 0, graph.width, graph.height);
                ctx.restore();
              }

              ctx.fill();
              ctx.stroke();

            }

        }

        if (!silent) {
            callback(this.points, this.name);
        }

    };

    this.drawGrid = function() {

        ctx.strokeStyle = "rgba(255,255,255,0.3)";
        ctx.lineWidth = factor;

        var w = this.mirror ? graph.width/6 : (graph.width-radius*factor)/4;
        var h = (graph.height-radius*factor)/4;
        var i, pos;

        ctx.beginPath();
        
        // for(i=radius; i < graph.width; i+=w){
        //     pos = Math.floor(i)+0.5;
        //     ctx.moveTo(pos,radius);
        //     ctx.lineTo(pos,graph.height-radius);
        // }
        
        // for (i=radius; i < graph.height; i+=h){
        //     pos = Math.floor(i)+0.5;
        //     ctx.moveTo(radius,pos);
        //     ctx.lineTo(graph.width-radius,pos);
        // }
        
        ctx.stroke();
        ctx.closePath();

        // diag line
        if (!this.mirror) {
            ctx.lineWidth = 1.0 * factor;
            ctx.beginPath();
            ctx.moveTo(radius,graph.height-radius);
            ctx.lineTo(graph.width-radius,radius);
            ctx.stroke();
            ctx.closePath();
        }

    };

    graph.addEventListener('touchstart', this.setPoint, false);
    graph.addEventListener('mousedown', this.setPoint, false);
    graph.addEventListener('pointerdown', this.setPoint, false);
    //graph.addEventListener('mousemove', this.findPoints, false);
    //graph.addEventListener('blur', this.blurGraph, false);
    //graph.addEventListener('focus', this.focusGraph, false);
    //graph.addEventListener('contextmenu', this.applyPoint, false);
    graph.addEventListener('keyup', this.deletePoint, false);
    graph.addEventListener('keydown', this.selectPoint, false);
    graph.addEventListener('keydown', this.nudgePoint, false);

}

module.exports = splineGraph;
;


},{}],343:[function(require,module,exports){
var KVStore, indexeddbWrapper, localStorageWrapper, manager, noop, storage;

KVStore = require("./KVStore.coffee");

noop = function() {};

indexeddbWrapper = {
  setObject: function(key, value, callback) {
    if (callback == null) {
      callback = noop;
    }
    return KVStore.setItem(key, value, callback);
  },
  getObject: function(key, callback) {
    if (callback == null) {
      callback = noop;
    }
    return KVStore.getItem(key, callback);
  },
  removeObject: function(key, callback) {
    if (callback == null) {
      callback = noop;
    }
    return KVStore.removeItem(key, callback);
  },
  removeAll: function(callback) {
    if (callback == null) {
      callback = noop;
    }
    return KVStore.clearStore(callback);
  }
};

localStorageWrapper = {
  setObject: function(key, value, callback) {
    value = JSON.stringify(value);
    localStorage.setItem(key, value);
    if (typeof callback === "function") {
      return callback({
        success: true
      }, value);
    }
  },
  getObject: function(key, callback) {
    var error, error1, value;
    value = localStorage.getItem(key);
    try {
      value = JSON.parse(value);
    } catch (error1) {
      error = error1;
      console.error(error);
    }
    if (typeof callback === "function") {
      return callback({
        success: true
      }, value);
    }
  }
};

if (typeof POIPCManager !== "undefined" && POIPCManager !== null) {
  manager = POIPCManager.sharedManager;
  storage = CONFIG.os === "android" ? indexeddbWrapper : POIPCStorage.sharedStorage;
} else {
  storage = indexeddbWrapper;
}

module.exports = {
  setItem: function(key, value, callback) {
    logTime("set " + key);
    return storage.setObject(key, value, function(res) {
      logTimeEnd("set " + key);
      return callback != null ? callback.call(this, res.success, value) : void 0;
    });
  },
  getItem: function(key, callback) {
    logTime("get " + key);
    return storage.getObject(key, function(res, value) {
      logTimeEnd("get " + key);
      if (typeof res.value !== "undefined") {
        value = res.value;
      }
      return callback != null ? callback.call(this, res.success, value) : void 0;
    });
  },
  removeItem: function(key, callback) {
    var done;
    logTime("remove " + key);
    done = function() {
      logTimeEnd("remove " + key);
      return typeof callback === "function" ? callback() : void 0;
    };
    if (storage !== indexeddbWrapper) {
      return storage.setObject(key, null, done);
    } else {
      return storage.removeObject(key, done);
    }
  },
  removeAll: function(callback) {
    return typeof storage.removeAll === "function" ? storage.removeAll(callback) : void 0;
  },
  manager: manager
};


},{"./KVStore.coffee":228}],344:[function(require,module,exports){
var EventEmitter, PhotoStore, filesystem, filetypes, manager, metadataOverride, readMetadata, storage, thumbnailCanvas, thumbnailImage, utils,
  slice = [].slice,
  extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
  hasProp = {}.hasOwnProperty;

storage = require("./storage.coffee");

manager = require("./manager.coffee");

utils = require("./utils.coffee");

readMetadata = require("./lib/metadata");

filesystem = require("./filesystem.coffee");

filetypes = require("./filesystem-adapters/filetypes.coffee");

metadataOverride = {
  'Orientation': 1
};

thumbnailImage = new Image();

thumbnailCanvas = document.createElement("canvas");

EventEmitter = (function() {
  function EventEmitter() {}

  EventEmitter.prototype.events = {};

  EventEmitter.prototype.trigger = function() {
    var args, e, event, j, len, ref, results;
    event = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
    if (!this.events[event]) {
      return;
    }
    ref = this.events[event];
    results = [];
    for (j = 0, len = ref.length; j < len; j++) {
      e = ref[j];
      results.push(e != null ? e.apply(this, args) : void 0);
    }
    return results;
  };

  EventEmitter.prototype.on = function(event, func) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    return this.events[event].push(func);
  };

  EventEmitter.prototype.off = function(event, func) {
    if (!this.events[event]) {
      return;
    }
    return this.events[event].splice(this.events[event].indexOf(func), 1);
  };

  return EventEmitter;

})();

PhotoStore = (function(superClass) {
  extend(PhotoStore, superClass);

  function PhotoStore() {
    return PhotoStore.__super__.constructor.apply(this, arguments);
  }

  PhotoStore.prototype.storage = storage;

  PhotoStore.prototype.manager = storage.manager;

  PhotoStore.prototype.sampleImage = "default.jpg";

  PhotoStore.prototype.currentPhoto = null;

  PhotoStore.prototype.photos = [];

  PhotoStore.prototype.photosMap = {};

  PhotoStore.prototype.getRootUrl = function() {
    return "./";
  };

  PhotoStore.prototype.getSampleDir = function() {
    var root;
    root = this.getRootUrl();
    return root + "img/sample_images/";
  };

  PhotoStore.prototype.setCurrentPhoto = function(photoId) {
    this.currentPhoto = photoId;
    return this.trigger("currentPhoto", this.currentPhoto);
  };

  PhotoStore.prototype.setExtra = function(id, extra) {
    return this.get("extra", id, (function(_this) {
      return function(data) {
        if (data == null) {
          data = {};
        }
        data = utils.extend(data, extra);
        return _this.set("extra", id, data);
      };
    })(this));
  };

  PhotoStore.prototype.removeAllExtra = function() {
    var i, j, len, photo, ref, results;
    ref = this.photos;
    results = [];
    for (i = j = 0, len = ref.length; j < len; i = ++j) {
      photo = ref[i];
      results.push(this.set("extra", photo, null));
    }
    return results;
  };

  PhotoStore.prototype.getParts = function(id) {
    var extension, filename, filesize, name, parts, segments;
    if (id == null) {
      return {};
    }
    parts = id.split("|");
    filename = parts[0];
    filesize = parts[1];
    segments = filename.split(".");
    extension = segments.pop().toLowerCase();
    name = segments.join("");
    return {
      filename: filename,
      filesize: filesize,
      extension: extension,
      name: name
    };
  };

  PhotoStore.prototype.encodeKey = function(key) {
    return key.replace(/\//g, "-");
  };

  PhotoStore.prototype.getId = function(type, id) {
    return id = (function() {
      switch (type) {
        case "photo":
          return id;
        default:
          return id + "_" + type;
      }
    })();
  };

  PhotoStore.prototype.get = function(type, id, callback) {
    var file;
    if (type === "photo") {
      file = this.photosMap[id];
      this.readFile(file, (function(_this) {
        return function(data) {
          return callback(data);
        };
      })(this));
      return;
    }
    id = this.getId(type, id);
    id = this.encodeKey(id);
    return storage.getItem(id, (function(_this) {
      return function(success, res) {
        return typeof callback === "function" ? callback(res) : void 0;
      };
    })(this));
  };

  PhotoStore.prototype.set = function(type, id, data, callback) {
    this.trigger(type, id, data);
    id = this.getId(type, id);
    id = this.encodeKey(id);
    return storage.setItem(id, data, function(success, res) {
      return typeof callback === "function" ? callback(res) : void 0;
    });
  };

  PhotoStore.prototype.remove = function(type, id, callback) {
    id = this.getId(type, id);
    id = this.encodeKey(id);
    return storage.removeItem(id, function(success, res) {
      return typeof callback === "function" ? callback(res) : void 0;
    });
  };

  PhotoStore.prototype.setPhotos = function(store) {
    if (store == null) {
      store = true;
    }
    this.photos = Object.keys(this.photosMap);
    this.trigger("photos", this.photos);
    if (store) {
      return storage.setItem("photos", this.photosMap);
    }
  };

  PhotoStore.prototype.unload = function() {
    Filtr.unload();
    this.currentPhoto = null;
    storage.setItem("currentPhoto", this.currentPhoto);
    return this.trigger("loadImage");
  };

  PhotoStore.prototype.removeAllPhotos = function() {
    var i, j, key, len, ref;
    ref = this.photos;
    for (i = j = 0, len = ref.length; j < len; i = ++j) {
      key = ref[i];
      this.remove("photo", key);
      this.remove("info", key);
      this.remove("state", key);
      this.remove("thumbnail", key);
      this.remove("extra", key);
      delete this.photosMap[key];
    }
    this.setPhotos();
    return this.unload();
  };

  PhotoStore.prototype.removePhoto = function(key) {
    var index, photo;
    if (this.photosMap[key]) {
      index = this.photos.indexOf(key);
      delete this.photosMap[key];
      this.setPhotos();
      this.remove("photo", key);
      this.remove("info", key);
      this.remove("state", key);
      this.remove("thumbnail", key);
      this.remove("extra", key);
      if (key === this.currentPhoto) {
        if (index >= this.photos.length) {
          index = this.photos.length - 1;
        }
        photo = this.photos[index];
        if (photo != null) {
          return this.loadPhoto(photo);
        } else {
          return this.unload();
        }
      }
    }
  };

  PhotoStore.prototype.loadImageURL = function(url, options) {
    var file, image;
    if (options == null) {
      options = {};
    }
    file = url.split("/").pop();
    image = this.getParts(file);
    this.info = {
      id: image.filename,
      filename: image.filename,
      filetype: image.extension,
      filesize: 0,
      metadata: null
    };
    this.setCurrentPhoto(image.filename);
    if (options.resetState) {
      this.remove("state", this.currentPhoto);
    }
    return this.loadUrl(url);
  };

  PhotoStore.prototype.loadSampleImage = function() {
    if (this.currentPhoto === this.sampleImage) {
      return;
    }
    this.info = {
      id: this.sampleImage,
      filename: this.sampleImage,
      filetype: "jpg",
      filesize: 0,
      metadata: null
    };
    this.setCurrentPhoto(this.sampleImage);
    return this.loadUrl(this.getSampleDir() + this.sampleImage);
  };

  PhotoStore.prototype.loadUrl = function(url, exif, adjustments, callback) {
    var loaded;
    loaded = (function(_this) {
      return function(success) {
        if (success) {
          manager.set("photo_status", {
            status: "loaded"
          });
          storage.setItem("currentPhoto", _this.currentPhoto);
        } else {
          manager.set("photo_status", {
            status: "error",
            message: "status.photo_load_error"
          });
        }
        return typeof callback === "function" ? callback(success) : void 0;
      };
    })(this);
    return this.trigger("loadImage", url, exif, adjustments, loaded);
  };

  PhotoStore.prototype.loadData = function(data, exif, adjustments, callback) {
    var blob, url;
    if (typeof data === "string") {
      url = data;
    } else if (this.info.isRAW) {
      url = data;
    } else {
      blob = new Blob([data]);
      url = URL.createObjectURL(blob);
    }
    return this.loadUrl(url, exif, adjustments, callback);
  };

  PhotoStore.prototype.getPhotoData = function(id, callback, error) {
    var _info, data, file, state;
    file = this.photosMap[id];
    if (!file) {
      return typeof error === "function" ? error() : void 0;
    }
    _info = null;
    state = new Promise((function(_this) {
      return function(resolve) {
        return _this.get("state", id, resolve);
      };
    })(this));
    data = new Promise((function(_this) {
      return function(resolve, reject) {
        return _this.readFile(file, function(data, info) {
          _info = info;
          return resolve(data);
        }, reject);
      };
    })(this));
    return Promise.all([state, data]).then((function(_this) {
      return function(arg) {
        var data, state;
        state = arg[0], data = arg[1];
        return callback(data, _info, state);
      };
    })(this))["catch"](error);
  };

  PhotoStore.prototype.loadPhoto = function(id, callback, error) {
    if (this.currentPhoto === id) {
      return;
    }
    manager.set("photo_status", {
      status: "loading"
    });
    return this.getPhotoData(id, (function(_this) {
      return function(data, info, state) {
        _this.info = info;
        _this.setCurrentPhoto(id);
        return _this.loadData(data, info.metadata, state, callback);
      };
    })(this), (function(_this) {
      return function() {
        manager.set("photo_status", {
          status: "error",
          message: "status.photo_load_error"
        });
        return typeof error === "function" ? error() : void 0;
      };
    })(this));
  };

  PhotoStore.prototype.loadPhotos = function(callback) {
    var path;
    path = Electron.ipcRenderer.sendSync('get-open-file');
    console.log(path);
    return storage.getItem("photos", (function(_this) {
      return function(success, photosMap) {
        var files;
        if (!photosMap) {
          return;
        }
        _this.photosMap = photosMap;
        _this.setPhotos(false);
        files = _this.photos.map(function(key) {
          return photosMap[key];
        });
        _this.getThumbnails(files);
        if (manager.state.settings.loadLastPhoto) {
          return storage.getItem("currentPhoto", function(success, photo) {
            if (photo) {
              return _this.loadPhoto(photo, callback);
            }
          });
        }
      };
    })(this));
  };

  PhotoStore.prototype.readURL = function(url, success, error) {
    var request;
    request = new XMLHttpRequest();
    request.open("GET", url, true);
    request.responseType = "arraybuffer";
    request.onload = function() {
      var filename, filesize, filetype, id, info, metadata;
      filename = url.split("/").pop();
      filetype = filename.split(".").pop();
      filesize = request.response.byteLength;
      id = filename + "|" + filesize;
      metadata = readMetadata(request.response, metadataOverride);
      info = {
        id: id,
        url: url,
        filetype: filetype || "jpg",
        filename: filename,
        filesize: filesize,
        metadata: metadata
      };
      return success(request.response, info);
    };
    request.onerror = function(err) {
      if (error != null) {
        return error(err);
      }
    };
    return request.send(null);
  };

  PhotoStore.prototype.readFile = function(file, success, error) {
    var done, fileReader, filename, filesize, filetype, id, isRAW, read;
    filename = file.name || file.filename;
    filesize = file.size || file.filesize;
    filetype = filename.split(".").pop() || "jpg";
    id = filename + "|" + filesize;
    isRAW = false;
    done = function(data, width, height) {
      var info, metadata;
      if (!isRAW) {
        metadata = readMetadata(data, metadataOverride);
      }
      info = {
        id: id,
        filetype: filetype,
        filename: filename,
        filesize: filesize,
        metadata: metadata,
        isRAW: isRAW,
        width: width,
        height: height
      };
      return success(data, info);
    };
    read = function(data, width, height) {
      if (width && height) {
        isRAW = true;
        done(data, width, height);
      } else {
        done(data.buffer);
      }
      return isRAW = filetypes.isRAW(filename);
    };
    if (!filetypes.isSupported(filename)) {
      return typeof error === "function" ? error({
        message: "status.file_not_supported"
      }) : void 0;
    } else if (file.path) {
      return filesystem.read(file.path, function(data, width, height) {
        return read(data, width, height);
      }, error);
    } else {
      fileReader = new FileReader();
      fileReader.onload = function() {
        return read(this.result);
      };
      fileReader.onerror = function(err) {
        return typeof error === "function" ? error(err) : void 0;
      };
      return fileReader.readAsArrayBuffer(file);
    }
  };

  PhotoStore.prototype.readThumbnail = function(data, info, callback) {
    var blob, crop, ctx, orientation, ref, ref1, thumbnailSize, url;
    if (info == null) {
      info = {};
    }
    if (data instanceof Blob || data instanceof File) {
      blob = data;
    } else {
      blob = new Blob([data]);
    }
    url = URL.createObjectURL(blob);
    ctx = thumbnailCanvas.getContext('2d');
    orientation = ((ref = info.metadata) != null ? (ref1 = ref.exif) != null ? ref1.Orientation : void 0 : void 0) || 0;
    thumbnailSize = info.thumbnailSize || 160;
    crop = info.crop || [0, 0, 1, 1];
    thumbnailImage.onload = function() {
      var angle, height, offsetX, offsetY, ref2, scale, scaleX, scaleY, thumb, width;
      width = thumbnailImage.width, height = thumbnailImage.height;
      orientation = Filtr.getOrientationSettings(orientation);
      angle = orientation.rotate90 * 90;
      scaleX = orientation.flip_x ? -1 : 1;
      scaleY = orientation.flip_y ? -1 : 1;
      if (orientation.rotate90 % 2) {
        ref2 = [height, width], width = ref2[0], height = ref2[1];
      }
      scale = thumbnailSize / crop[2] / width;
      offsetX = width * crop[0] >> 1;
      offsetY = height * crop[1] >> 1;
      width = width * scale * crop[2] >> 0;
      height = height * scale * crop[3] >> 0;
      thumbnailCanvas.width = width;
      thumbnailCanvas.height = height;
      ctx.save();
      ctx.translate(width / 2, height / 2);
      ctx.rotate((Math.PI / 180) * angle);
      ctx.scale(scaleX * scale, scaleY * scale);
      ctx.drawImage(thumbnailImage, -thumbnailImage.width / 2 - offsetX, -thumbnailImage.height / 2 - offsetY);
      ctx.restore();
      thumb = ctx.getImageData(0, 0, width, height);
      URL.revokeObjectURL(url);
      thumbnailImage.onload = null;
      thumbnailImage.src = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
      return callback(thumb);
    };
    thumbnailImage.onerror = function() {
      return callback();
    };
    return thumbnailImage.src = url;
  };

  PhotoStore.prototype.getThumbnails = (function() {
    var _this, complete, next, queue;
    _this = this;
    complete = Function.prototype;
    queue = {
      files: []
    };
    next = function() {
      var file, id;
      file = queue.files.shift();
      if (!file) {
        if (typeof complete === "function") {
          complete();
        }
        return;
      }
      id = file.name + '|' + file.size;
      return _this.get("thumbnail", id, function(data) {
        if (data) {
          _this.trigger("thumbnail", id, data);
          return next();
        } else {
          return filesystem.createThumbnail(file.path, {
            width: 160
          }, function(err, buffer, info) {
            createImageBitmap(new Blob([buffer], {
              type: 'image/png'
            })).then(function(image) {
              return _this.set("thumbnail", id, image);
            });
            return next();
          });
        }
      });
    };
    return function(files, callback) {
      if (callback == null) {
        callback = complete;
      }
      complete = callback;
      _this = this;
      if (files.length) {
        queue.files = files.slice(0);
        return next();
      }
    };
  })();

  PhotoStore.prototype.importFiles = function(files, callback, photoToLoad) {
    var file, index, j, key, len;
    if (files == null) {
      files = [];
    }
    files = Array.from(files).filter((function(_this) {
      return function(file) {
        return !_this.photosMap[file.name + '|' + file.size] && filetypes.isSupported(file.name);
      };
    })(this));
    if (!files || !files.length) {
      return;
    }
    utils.trackEventGA('photo', 'import', files.length);
    for (j = 0, len = files.length; j < len; j++) {
      file = files[j];
      key = file.name + '|' + file.size;
      this.photosMap[key] = file;
    }
    this.setPhotos();
    if (photoToLoad) {
      index = this.photos.indexOf(photoToLoad);
    }
    if (photoToLoad && index !== -1) {
      files = files.slice(0);
      files.splice(index, 1);
      this.loadPhoto(photoToLoad);
    } else {
      file = files[0];
      key = file.name + '|' + file.size;
      this.loadPhoto(key);
      files = files.slice(1);
    }
    return this.getThumbnails(files, callback);
  };

  return PhotoStore;

})(EventEmitter);

module.exports = new PhotoStore();


},{"./filesystem-adapters/filetypes.coffee":305,"./filesystem.coffee":311,"./lib/metadata":315,"./manager.coffee":338,"./storage.coffee":343,"./utils.coffee":360}],345:[function(require,module,exports){
var addListener, addPhoto, encodeKey, events, filetypes, get, getId, getImportDir, getParts, getPhotoData, getRootUrl, getSampleDir, getURI, importFiles, loadData, loadImageURL, loadPhoto, loadPhotos, loadSampleImage, loadUrl, manager, metadataOverride, preloadImages, readFile, readMetadata, readThumbnail, readURL, remove, removeAllExtra, removeAllPhotos, removeListener, removePhoto, set, setCurrentPhoto, setExtra, setSaveable, storage, store, thumbnailCanvas, thumbnailImage, trigger, unload, utils,
  slice = [].slice;

storage = require("./storage.coffee");

manager = require("./manager.coffee");

utils = require("./utils.coffee");

readMetadata = require("./lib/metadata");

filetypes = require("./filesystem-adapters/filetypes.coffee");

thumbnailImage = new Image();

thumbnailCanvas = document.createElement("canvas");

metadataOverride = {
  'Orientation': 1,
  'ColorSpace': 1
};

getRootUrl = function() {
  if (typeof POIPCManager !== "undefined" && POIPCManager !== null) {
    return POIPCManager.serviceImportURL;
  } else {
    return "/";
  }
};

getSampleDir = function() {
  var root;
  root = getRootUrl();
  if (typeof POIPCManager !== "undefined" && POIPCManager !== null) {
    return root + "sample/";
  } else {
    return root + "img/sample_images/";
  }
};

getImportDir = function() {
  var root, sampleDir;
  sampleDir = getSampleDir();
  root = getRootUrl();
  if (typeof POIPCManager !== "undefined" && POIPCManager !== null) {
    return root + "import/";
  } else {
    return sampleDir;
  }
};

getURI = function(id) {
  var root;
  root = id === store.sampleImage ? store.getSampleDir() : store.getImportDir();
  return root + id;
};

setSaveable = function(saveable) {
  store.saveable = saveable;
  return trigger("saveable", saveable);
};

setCurrentPhoto = function(photoId) {
  store.currentPhoto = photoId;
  return trigger("currentPhoto", store.currentPhoto);
};

setExtra = function(id, extra) {
  return get("extra", id, function(data) {
    if (data == null) {
      data = {};
    }
    data = utils.extend(data, extra);
    return set("extra", id, data);
  });
};

removeAllExtra = function() {
  var i, j, len, photo, ref, results;
  ref = store.photos;
  results = [];
  for (i = j = 0, len = ref.length; j < len; i = ++j) {
    photo = ref[i];
    results.push(set("extra", photo, null));
  }
  return results;
};

encodeKey = function(key) {
  return key.replace(/\//g, "-");
};

getParts = function(id) {
  var extension, filename, filesize, name, parts, segments;
  if (id == null) {
    return {};
  }
  parts = id.split("|");
  filename = parts[0];
  filesize = parts[1];
  segments = filename.split(".");
  extension = segments.pop().toLowerCase();
  name = segments.join("");
  return {
    filename: filename,
    filesize: filesize,
    extension: extension,
    name: name
  };
};

getId = function(type, id) {
  var thumbnailKey;
  thumbnailKey = CONFIG.platform === "windows" ? "_thumbnail" : "_thumb";
  return id = (function() {
    switch (type) {
      case "photo":
        return id;
      case "thumbnail":
        return id + thumbnailKey;
      default:
        return id + "_" + type;
    }
  })();
};

get = function(type, id, callback) {
  id = getId(type, id);
  id = encodeKey(id);
  return storage.getItem(id, function(success, res) {
    if (type === "photo" && typeof res === "string") {
      res = res.split("/").pop();
      if (res.indexOf(".") > 0) {
        res = getSampleDir() + res;
      } else {
        res = getImportDir() + res;
      }
    }
    return typeof callback === "function" ? callback(res) : void 0;
  });
};

set = function(type, id, data, callback) {
  trigger(type, id, data);
  if (type === "info" && (typeof POIPCManager !== "undefined" && POIPCManager !== null)) {
    data = utils.deepClone(data);
  }
  id = getId(type, id);
  id = encodeKey(id);
  return storage.setItem(id, data, function(success, res) {
    return typeof callback === "function" ? callback(res) : void 0;
  });
};

remove = function(type, id, callback) {
  var identifier;
  if (type === "photo" && CONFIG.platform === "osx") {
    identifier = id.split("/").pop().split("|").pop();
    storage.manager.sendMessage("ppe.import.remove", {
      identifier: identifier
    });
  }
  id = getId(type, id);
  id = encodeKey(id);
  return storage.removeItem(id, function(success, res) {
    return typeof callback === "function" ? callback(res) : void 0;
  });
};

getPhotoData = function(id, callback) {
  return get("photo", id, function(data) {
    return get("state", id, function(state) {
      return get("info", id, function(info) {
        if (info == null) {
          info = {};
        }
        return callback(data, info, state);
      });
    });
  });
};

unload = function() {
  Filtr.unload();
  store.currentPhoto = null;
  return trigger("loadImage");
};

removeAllPhotos = function() {
  var i, j, len, photo, ref;
  ref = store.photos;
  for (i = j = 0, len = ref.length; j < len; i = ++j) {
    photo = ref[i];
    remove("photo", photo);
    remove("info", photo);
    remove("state", photo);
    remove("thumbnail", photo);
    remove("extra", photo);
  }
  store.photos = [];
  store.currentPhoto = null;
  storage.setItem("photos", store.photos);
  trigger("photos", store.photos);
  return unload();
};

removePhoto = function(id) {
  var index, photo;
  index = store.photos.indexOf(id);
  if (index >= 0) {
    store.photos = store.photos.slice(0);
    store.photos.splice(index, 1);
    trigger("photos", store.photos);
    remove("photo", id);
    remove("info", id);
    remove("state", id);
    remove("thumbnail", id);
    remove("extra", id);
    storage.setItem("photos", store.photos);
    if (id === store.currentPhoto) {
      if (index >= store.photos.length) {
        index = store.photos.length - 1;
      }
      photo = store.photos[index];
      if (photo != null) {
        return loadPhoto(photo);
      } else {
        return unload();
      }
    }
  }
};

addPhoto = function(id) {
  if (store.photos.indexOf(id) === -1) {
    store.photos.push(id);
    store.photos = store.photos.slice(0);
    if (CONFIG.importLimit == null) {
      if (store.photos.length > 50) {
        store.photos = store.photos.slice(-50);
      }
    }
    trigger("photos", store.photos);
    return storage.setItem("photos", store.photos);
  }
};

loadImageURL = function(url, options) {
  var file, image;
  if (options == null) {
    options = {};
  }
  file = url.split("/").pop();
  image = getParts(file);
  store.info = {
    id: image.filename,
    filename: image.filename,
    filetype: image.extension,
    filesize: 0,
    metadata: null
  };
  setCurrentPhoto(image.filename);
  setSaveable(store.READ_ONLY);
  if (options.resetState) {
    remove("state", store.currentPhoto);
  }
  return loadUrl(url);
};

loadSampleImage = function() {
  if (CONFIG.embedded) {
    return;
  }
  store.sampleImage = CONFIG.platform === "mobile" ? "default_portrait.jpg" : "default.jpg";
  if (store.currentPhoto === store.sampleImage) {
    return;
  }
  store.info = {
    id: store.sampleImage,
    filename: store.sampleImage,
    filetype: "jpg",
    filesize: 0,
    metadata: null
  };
  setCurrentPhoto(store.sampleImage);
  setSaveable(store.READ_ONLY);
  return loadUrl(getSampleDir() + store.sampleImage);
};

loadUrl = function(url, exif, adjustments, callback) {
  var loaded;
  loaded = function(success) {
    if (success) {
      manager.set("photo_status", {
        status: "loaded"
      });
      storage.setItem("currentPhoto", store.currentPhoto);
    } else {
      manager.set("photo_status", {
        status: "error",
        message: "status.photo_load_error"
      });
    }
    return typeof callback === "function" ? callback(success) : void 0;
  };
  return trigger("loadImage", url, exif, adjustments, loaded);
};

loadData = function(data, exif, adjustments, callback) {
  var blob, url;
  if (typeof data === "string") {
    url = data;
  } else {
    blob = new Blob([data]);
    url = URL.createObjectURL(blob);
  }
  return loadUrl(url, exif, adjustments, callback);
};

loadPhoto = function(id, callback) {
  var index;
  if (store.currentPhoto === id) {
    return;
  }
  index = store.photos.indexOf(id);
  if (index === -1) {
    index = 0;
  }
  store.lastPhotoIndex = index;
  id = store.photos[index];
  setCurrentPhoto(id);
  manager.set("photo_status", {
    status: "loading"
  });
  return getPhotoData(id, function(data, info, state) {
    store.info = info;
    return loadData(data, info.metadata, state, callback);
  });
};

loadPhotos = function(callback) {
  return storage.getItem("photos", function(success, photos) {
    if (photos == null) {
      photos = [];
    }
    store.photos = photos;
    trigger("photos", store.photos);
    if (store.preloadedImages || !manager.state.settings.loadLastPhoto) {

    } else if (photos.length) {
      return storage.getItem("currentPhoto", function(success, photo) {
        if (photo) {
          return loadPhoto(photo, callback);
        }
      });
    }
  });
};

preloadImages = function(files) {
  store.preloadedImages = files;
  return manager.restoreState.then(function() {
    importFiles(files);
    return store.preloadedImages = null;
  });
};

readURL = function(url, success, error) {
  var request;
  request = new XMLHttpRequest();
  request.open("GET", url, true);
  request.responseType = "arraybuffer";
  request.onload = function() {
    var filename, filesize, filetype, id, info, metadata;
    filename = url.split("/").pop();
    filetype = filename.split(".").pop();
    filesize = request.response.byteLength;
    id = filename + "|" + filesize;
    metadata = readMetadata(request.response, metadataOverride);
    info = {
      id: id,
      url: url,
      filetype: filetype || "jpg",
      filename: filename,
      filesize: filesize,
      metadata: metadata
    };
    return success(request.response, info);
  };
  request.onerror = function(err) {
    if (error != null) {
      return error(err);
    }
  };
  return request.send(null);
};

readFile = function(file, success, error) {
  var fileReader, filename, filesize, filetype, id, request, url;
  filename = file.name || file.filename;
  filesize = file.size || file.filesize;
  filetype = filename.split(".").pop() || "jpg";
  id = filename + "|" + (file.id || filesize);
  if (store.photos.indexOf(id) >= 0) {
    return typeof error === "function" ? error({
      message: "status.file_exists"
    }) : void 0;
  } else if (!filetypes.isSupported(filename)) {
    return typeof error === "function" ? error({
      message: "status.file_not_supported"
    }) : void 0;
  } else if (file.url) {
    url = file.url;
    request = new XMLHttpRequest();
    request.open("GET", url, true);
    request.responseType = "arraybuffer";
    request.onload = function() {
      var info, metadata;
      metadata = readMetadata(request.response, metadataOverride);
      info = {
        id: id,
        url: url,
        filetype: filetype,
        filename: filename,
        filesize: filesize,
        metadata: metadata,
        faces: file.faces || []
      };
      return success(request.response, info);
    };
    request.onerror = function(err) {
      if (error != null) {
        return error(err);
      }
    };
    return request.send(null);
  } else {
    fileReader = new FileReader();
    fileReader.onload = function() {
      var done, isRAW;
      isRAW = filetypes.isRAW(filename);
      done = function(data) {
        var info, metadata;
        metadata = readMetadata(data, metadataOverride);
        info = {
          id: id,
          isRAW: isRAW,
          filetype: filetype,
          filename: filename,
          filesize: filesize,
          metadata: metadata
        };
        return success(data, info);
      };
      if (isRAW && (typeof polarr_rf !== "undefined" && polarr_rf !== null)) {
        return polarr_rf(filename, new Uint8Array(this.result), ['-e', '/' + filename], function(buffer) {
          return done(buffer);
        }, function(err) {
          return typeof error === "function" ? error({
            message: "status.raw_failed"
          }) : void 0;
        });
      } else {
        return done(this.result);
      }
    };
    fileReader.onerror = function(err) {
      return typeof error === "function" ? error(err) : void 0;
    };
    return fileReader.readAsArrayBuffer(file);
  }
};

readThumbnail = function(data, info, callback) {
  var blob, crop, ctx, orientation, ref, ref1, thumbnailSize, url;
  if (info == null) {
    info = {};
  }
  if (data instanceof Blob || data instanceof File) {
    blob = data;
  } else {
    blob = new Blob([data]);
  }
  url = URL.createObjectURL(blob);
  ctx = thumbnailCanvas.getContext('2d');
  orientation = ((ref = info.metadata) != null ? (ref1 = ref.exif) != null ? ref1.Orientation : void 0 : void 0) || 0;
  thumbnailSize = info.thumbnailSize || 160;
  crop = info.crop || [0, 0, 1, 1];
  thumbnailImage.onload = function() {
    var angle, height, offsetX, offsetY, ref2, scale, scaleX, scaleY, thumb, width;
    width = thumbnailImage.width, height = thumbnailImage.height;
    orientation = Filtr.getOrientationSettings(orientation);
    angle = orientation.rotate90 * 90;
    scaleX = orientation.flip_x ? -1 : 1;
    scaleY = orientation.flip_y ? -1 : 1;
    if (orientation.rotate90 % 2) {
      ref2 = [height, width], width = ref2[0], height = ref2[1];
    }
    scale = thumbnailSize / crop[2] / width;
    offsetX = width * crop[0] >> 1;
    offsetY = height * crop[1] >> 1;
    width = width * scale * crop[2] >> 0;
    height = height * scale * crop[3] >> 0;
    thumbnailCanvas.width = width;
    thumbnailCanvas.height = height;
    ctx.save();
    ctx.translate(width / 2, height / 2);
    ctx.rotate((Math.PI / 180) * angle);
    ctx.scale(scaleX * scale, scaleY * scale);
    ctx.drawImage(thumbnailImage, -thumbnailImage.width / 2 - offsetX, -thumbnailImage.height / 2 - offsetY);
    ctx.restore();
    thumb = ctx.getImageData(0, 0, width, height);
    URL.revokeObjectURL(url);
    thumbnailImage.onload = null;
    thumbnailImage.src = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
    return callback(thumb);
  };
  thumbnailImage.onerror = function() {
    return callback();
  };
  return thumbnailImage.src = url;
};

importFiles = (function() {
  var complete, error, getProgress, next, queue, success;
  complete = null;
  queue = {
    files: [],
    total: 0,
    processing: false
  };
  getProgress = function() {
    return (1 - queue.files.length / queue.total) * 100;
  };
  next = function() {
    var file;
    file = queue.files.shift();
    queue.processing = true;
    if (!file) {
      queue.total = 0;
      queue.processing = false;
      return;
    }
    if (store.photos.length >= (CONFIG.importLimit || 50)) {
      manager.set("photo_status", {
        status: "import_limit"
      });
      queue.total = 0;
      queue.processing = false;
    } else {
      return readFile(file, success, error);
    }
  };
  success = function(data, info) {
    if (queue.total > 1) {
      manager.set("photo_status", {
        status: "importing",
        progress: getProgress()
      });
    }
    return readThumbnail(data, info, function(thumb) {
      var photoData;
      photoData = info.url ? info.url : data;
      addPhoto(info.id);
      set("info", info.id, info);
      set("photo", info.id, photoData);
      if (thumb) {
        set("thumbnail", info.id, thumb);
      }
      if (queue.files.length) {
        return next();
      } else {
        queue.total = 0;
        queue.processing = false;
        store.info = info;
        setCurrentPhoto(info.id);
        setSaveable(store.READ_ONLY);
        return loadData(data, info.metadata, null, complete);
      }
    });
  };
  error = function(err) {
    console.log("Error reading file: ", err);
    if (err) {
      manager.set("photo_status", {
        status: "error",
        message: err.message
      });
    }
    return next();
  };
  return function(files, callback) {
    var file, j, len;
    if (!files || !files.length) {
      return;
    }
    utils.trackEventGA('photo', 'import', files.length);
    complete = callback;
    for (j = 0, len = files.length; j < len; j++) {
      file = files[j];
      queue.files.push(file);
    }
    queue.total += files.length;
    if (queue.total > 1) {
      manager.set("photo_status", {
        status: "importing",
        progress: getProgress()
      });
    } else {
      manager.set("photo_status", {
        status: "importing"
      });
    }
    if (queue.processing) {
      return;
    }
    return next();
  };
})();

events = {};

trigger = function() {
  var args, e, event, j, len, ref, results;
  event = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
  if (!events[event]) {
    return;
  }
  ref = events[event];
  results = [];
  for (j = 0, len = ref.length; j < len; j++) {
    e = ref[j];
    results.push(e != null ? e.apply(this, args) : void 0);
  }
  return results;
};

addListener = function(event, func) {
  if (!events[event]) {
    events[event] = [];
  }
  return events[event].push(func);
};

removeListener = function(event, func) {
  if (!events[event]) {
    return;
  }
  return events[event].splice(events[event].indexOf(func), 1);
};

store = {
  storage: storage,
  manager: storage.manager,
  photos: [],
  currentPhoto: null,

  /*
  saveable:
    READ_WRITE = all operations allowed
    READ_WRITE_COPY = save not allowed, copy allowed
    READ_ONLY = save and copy not allowed (must export hard copy)
   */
  READ_WRITE: 0,
  READ_WRITE_COPY: 1,
  READ_ONLY: 2,
  saveable: 2,
  getRootUrl: getRootUrl,
  getSampleDir: getSampleDir,
  getImportDir: getImportDir,
  get: get,
  set: set,
  getURI: getURI,
  setSaveable: setSaveable,
  setExtra: setExtra,
  removeAllExtra: removeAllExtra,
  loadSampleImage: loadSampleImage,
  loadImageURL: loadImageURL,
  loadUrl: loadUrl,
  loadData: loadData,
  loadPhoto: loadPhoto,
  loadPhotos: loadPhotos,
  getParts: getParts,
  getPhotoData: getPhotoData,
  readURL: readURL,
  readFile: readFile,
  removePhoto: removePhoto,
  removeAllPhotos: removeAllPhotos,
  importFiles: importFiles,
  readThumbnail: readThumbnail,
  on: addListener,
  off: removeListener
};

window.preloadImages = preloadImages;

module.exports = store;


},{"./filesystem-adapters/filetypes.coffee":305,"./lib/metadata":315,"./manager.coffee":338,"./storage.coffee":343,"./utils.coffee":360}],346:[function(require,module,exports){
if (CONFIG.platform === "electron") {
  module.exports = require("./store-electron.coffee");
} else {
  module.exports = require("./store-universal.coffee");
}


},{"./store-electron.coffee":344,"./store-universal.coffee":345}],347:[function(require,module,exports){
var WebFont, blendModes, defaultFont, downloaded, fontCache, fonts, getCachedFont, storage, translate;

translate = require("counterpart");

storage = require("./storage.coffee");

WebFont = require("./lib/webfont");

fonts = {};

if (CONFIG.platform === "windows") {
  fonts.system = {
    'Arial': {
      fontFamily: '"Arial", sans-serif'
    },
    'Calibri': {
      fontFamily: '"Calibri", sans-serif'
    },
    'Condensed': {
      fontFamily: '"Impact", "HelveticaNeue-CondensedBlack", sans-serif'
    },
    'Times': {
      fontFamily: '"Times New Roman", serif'
    },
    'Cambria': {
      fontFamily: '"Cambria", serif'
    },
    'Segoe': {
      fontFamily: '"Segoe UI", sans-serif'
    }
  };
} else {
  fonts.system = {
    'Avenir': {
      fontFamily: '"Avenir Next", "Avenir", sans-serif'
    },
    'Futura': {
      fontFamily: '"Futura", sans-serif'
    },
    'Helvetica': {
      fontFamily: '"Helvetica Neue", "Helvetica", sans-serif'
    },
    'Condensed': {
      fontFamily: '"Impact", "HelveticaNeue-CondensedBlack", sans-serif'
    },
    'Papyrus': {
      fontFamily: '"Papyrus", cursive'
    },
    'Copperplate': {
      fontFamily: '"Copperplate", serif'
    },
    'Didot': {
      fontFamily: '"Didot", serif'
    }
  };
}

defaultFont = (function() {
  switch (CONFIG.os) {
    case "windows":
      return {
        name: "Calibri",
        family: fonts.system["Calibri"].fontFamily
      };
    case "osx":
      return {
        name: "Avenir",
        family: fonts.system["Avenir"].fontFamily
      };
    case "ios":
      return {
        name: "Avenir",
        family: fonts.system["Avenir"].fontFamily
      };
    default:
      return {
        name: "Sans Serif",
        family: "sans-serif"
      };
  }
})();

fonts.custom = {
  'Abril Fatface': {
    fontFamily: '"Abril Fatface", serif'
  },
  'Amatica SC': {
    fontFamily: '"Amatica SC", cursive'
  },
  'Audiowide': {
    fontFamily: '"Audiowide", sans-serif'
  },
  'Bangers': {
    fontFamily: '"Bangers", cursive'
  },
  'BioRhyme': {
    fontFamily: '"BioRhyme", serif'
  },
  'Cinzel': {
    fontFamily: '"Cinzel", serif'
  },
  'GUERRILLA': {
    fontFamily: '"GUERRILLA", cursive'
  },
  'Intro Inline': {
    fontFamily: '"Intro Inline", sans-serif'
  },
  'Intro': {
    fontFamily: '"Intro", sans-serif'
  },
  'Monoton': {
    fontFamily: '"Monoton", serif'
  },
  'Panton B': {
    fontFamily: '"Panton", sans-serif',
    fontWeight: 900
  },
  'Panton B I': {
    fontFamily: '"Panton", sans-serif',
    fontStyle: 'italic',
    fontWeight: 900
  },
  'Panton L': {
    fontFamily: '"Panton", sans-serif',
    fontWeight: 300
  },
  'Panton L I': {
    fontFamily: '"Panton", sans-serif',
    fontStyle: 'italic',
    fontWeight: 300
  },
  'Sensa Brush': {
    fontFamily: '"Sensa Brush", cursive'
  },
  'Special Elite': {
    fontFamily: '"Special Elite", monospaced'
  },
  'Sprite Graffiti': {
    fontFamily: '"Sprite Graffiti", cursive'
  },
  'Sunday': {
    fontFamily: '"Sunday", serif'
  },
  'Unkempt': {
    fontFamily: '"Unkempt", cursive'
  }
};

fonts.artwork = {
  'Artwork': {
    fontFamily: '"Artwork", cursive'
  }
};

fonts.downloadable = {
  'Source Han Sans': {
    fontFamily: '"Source Han Sans", sans-serif',
    src: 'SourceHanSansCN-Light.otf',
    thumbnail: 'fonts/previews/SourceHanSans.png'
  },
  '方正书宋简体': {
    fontFamily: '"方正书宋简体", "FZShuSong", sans-serif',
    src: '方正书宋简体.ttf',
    thumbnail: 'fonts/previews/FZShuSong.png'
  },
  '方正仿宋简体': {
    fontFamily: '"方正仿宋简体", "FZFangSong", sans-serif',
    src: '方正仿宋简体.ttf',
    thumbnail: 'fonts/previews/FZFangSong.png'
  },
  '方正楷体简体': {
    fontFamily: '"方正楷体简体", "FZKai", sans-serif',
    src: '方正楷体简体.ttf',
    thumbnail: 'fonts/previews/FZKai.png'
  },
  '方正黑体简体': {
    fontFamily: '"方正黑体简体", "FZHei", sans-serif',
    src: '方正黑体简体.ttf',
    thumbnail: 'fonts/previews/FZHei.png'
  },
  '站酷快乐体2016修订版': {
    fontFamily: '"站酷快乐体2016修订版", "HappyZCool2016", sans-serif',
    src: '站酷快乐体2016修订版.ttf',
    thumbnail: 'fonts/previews/HappyZCool2016.png'
  },
  '站酷酷黑': {
    fontFamily: '"站酷酷黑", "HuXiaoBo_KuHei", sans-serif',
    src: '站酷酷黑.ttf',
    thumbnail: 'fonts/previews/HuXiaoBo_KuHei.png'
  },
  '高端黑修订': {
    fontFamily: '"高端黑修订", "zcool-gdh", sans-serif',
    src: '高端黑修订.ttf',
    thumbnail: 'fonts/previews/zcool-gdh.png'
  }
};

fontCache = {};

downloaded = {};

getCachedFont = function(name, callback) {
  if (fontCache[name]) {
    return callback(fontCache[name]);
  } else {
    return storage.getItem("font_" + name, function(success, res) {
      fontCache[name] = res;
      return callback(res);
    });
  }
};

exports.restoreFonts = function(callback) {
  return storage.getItem("downloaded_fonts", function(success, res) {
    if (res == null) {
      res = [];
    }
    return Promise.all(res.map(function(font) {
      return new Promise(function(resolve) {
        return exports.loadFont(font, resolve);
      });
    })).then(callback);
  });
};

exports.downloadFont = function(name, success, error) {
  var font, request, url;
  font = fonts.downloadable[name];
  url = 'http://ppe.oss-cn-shenzhen.aliyuncs.com/fonts/' + encodeURI(font.src);
  request = new XMLHttpRequest();
  request.open("GET", url, true);
  request.responseType = "arraybuffer";
  request.onload = function() {
    fontCache[name] = request.response;
    downloaded[name] = true;
    storage.setItem("downloaded_fonts", Object.keys(downloaded));
    storage.setItem("font_" + name, new Uint8Array(request.response));
    return success(request.response);
  };
  request.onerror = function(err) {
    if (error != null) {
      return error(err);
    }
  };
  return request.send(null);
};

exports.loadFont = function(name, callback) {
  return getCachedFont(name, function(buffer) {
    var blob, css, ref, style, url;
    if ((ref = fonts.downloadable[name]) != null) {
      ref.downloaded = true;
    }
    blob = new Blob([buffer]);
    url = URL.createObjectURL(blob);
    css = "@font-face { font-family: '" + name + "'; src: url('" + url + "'); }";
    style = document.createElement('style');
    style.appendChild(document.createTextNode(css));
    document.head.appendChild(style);
    return WebFont.load({
      classes: false,
      custom: {
        families: [name]
      },
      active: (function(_this) {
        return function() {
          return callback();
        };
      })(this)
    });
  });
};

exports.fonts = fonts;

exports.systemFonts = Object.keys(fonts.system).sort();

exports.customFonts = Object.keys(fonts.custom).sort();

exports.artworkFonts = Object.keys(fonts.artwork).sort();

exports.downloadableFonts = Object.keys(fonts.downloadable).sort();

exports.blendModesMap = {
  'Normal': 0,
  'Darken': 1,
  'Multiply': 2,
  'Color Burn': 3,
  'Linear Burn (Subtract)': 4,
  'Lighten': 5,
  'Screen': 6,
  'Color Dodge': 7,
  'Add': 8,
  'Overlay': 9,
  'Soft Light': 10,
  'Hard Light': 11,
  'Vivid Light': 12,
  'Linear Light': 13,
  'Difference': 14,
  'Exclusion': 15,
  'Subtract': 16,
  'Divide': 17,
  'Hue': 18,
  'Saturation': 19,
  'Color': 20,
  'Luminosity': 21
};

exports.blendModes = blendModes = Object.keys(exports.blendModesMap);

exports.commonBlendModes = [0, 8, 16, 7, 3, 9].map(function(key) {
  return blendModes[key];
});

exports.uncommonBlendModes = [1, 2, 5, 6, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21].map(function(key) {
  return blendModes[key];
});

exports.defaults = {
  position: [0.5, 0.5],
  angle: 0,
  orientation: 0,
  text: translate("tooltips.double_tap_edit"),
  align: 'center',
  mode: 'normal',
  font_name: defaultFont.name,
  font_family: defaultFont.family,
  font_weight: 'normal',
  font_style: 'normal',
  font_size: 0.75,
  color: [1, 1, 1, 1],
  stroke_size: 0.01,
  opacity: 1,
  blend_mode: 0,
  blend_mix: 0,
  line_height: 1.25,
  letter_spacing: 0,
  shadow_opacity: 0,
  shadow_color: [0, 0, 0],
  shadow_offset: [0.05, 0.05],
  shadow_blur: 0.1,
  wrap: 0,
  scale: [1, 1]
};

exports.artwork = [
  {
    name: "Shapes",
    presets: [
      {
        state: [
          {
            text: "a",
            type: "artwork",
            line_height: 1,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "b",
            type: "artwork",
            line_height: 1,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "c",
            type: "artwork",
            line_height: 1,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "d",
            type: "artwork",
            line_height: 1,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "e",
            type: "artwork",
            line_height: 1,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "f",
            type: "artwork",
            line_height: 1,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "g",
            type: "artwork",
            line_height: 1,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "h",
            type: "artwork",
            line_height: 1,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "i",
            type: "artwork",
            line_height: 1,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "j",
            type: "artwork",
            line_height: 1,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "k",
            type: "artwork",
            line_height: 1,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "l",
            type: "artwork",
            line_height: 1,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "a",
            type: "artwork",
            line_height: 1,
            mode: "outline",
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "b",
            type: "artwork",
            line_height: 1,
            mode: "outline",
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "c",
            type: "artwork",
            line_height: 1,
            mode: "outline",
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "d",
            type: "artwork",
            line_height: 1,
            mode: "outline",
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "e",
            type: "artwork",
            line_height: 1,
            mode: "outline",
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "f",
            type: "artwork",
            line_height: 1,
            mode: "outline",
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "g",
            type: "artwork",
            line_height: 1,
            mode: "outline",
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "h",
            type: "artwork",
            line_height: 1,
            mode: "outline",
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "i",
            type: "artwork",
            line_height: 1,
            mode: "outline",
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "j",
            type: "artwork",
            line_height: 1,
            mode: "outline",
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "k",
            type: "artwork",
            line_height: 1,
            mode: "outline",
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "l",
            type: "artwork",
            line_height: 1,
            mode: "outline",
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }
    ]
  }, {
    name: "Dividers",
    presets: [
      {
        state: [
          {
            text: "0",
            type: "artwork",
            line_height: 0.08,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "1",
            type: "artwork",
            line_height: 0.08,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "2",
            type: "artwork",
            line_height: 0.08,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "3",
            type: "artwork",
            line_height: 0.08,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "4",
            type: "artwork",
            line_height: 0.08,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }, {
        state: [
          {
            text: "5",
            type: "artwork",
            line_height: 0.08,
            font_family: "Artwork",
            font_size: 2
          }
        ]
      }
    ]
  }
];

exports.presets = [
  {
    name: "Presets",
    presets: [
      {
        state: [
          {
            "position": [0.41152815348367716, 0.45975854852118436],
            "angle": 0,
            "orientation": 0,
            "text": "DOUBLE TAP TO EDIT",
            "align": "left",
            "mode": "normal",
            "font_name": "Intro Inline",
            "font_family": "\"Intro Inline\", sans-serif",
            "font_weight": "normal",
            "font_style": "normal",
            "font_size": 1.361842105263158,
            "color": [1, 1, 1, 1],
            "stroke_size": 0.01,
            "opacity": 1,
            "blend_mode": 0,
            "blend_mix": 0,
            "line_height": 0.986842105263158,
            "letter_spacing": 0,
            "shadow_opacity": 0,
            "shadow_color": [0, 0, 0],
            "shadow_offset": [0.05, 0.05],
            "shadow_blur": 0.1,
            "wrap": 0.6771995502185353,
            "scale": [1, 1],
            "disabled": false
          }, {
            "position": [0.5, 0.5],
            "angle": 0,
            "orientation": 0,
            "text": "a",
            "align": "center",
            "mode": "outline",
            "font_name": "Sans Serif",
            "font_family": "Artwork",
            "font_weight": "normal",
            "font_style": "normal",
            "font_size": 2,
            "color": [1, 1, 1, 1],
            "stroke_size": 0.01,
            "opacity": 1,
            "blend_mode": 0,
            "blend_mix": 0,
            "line_height": 1,
            "letter_spacing": 0,
            "shadow_opacity": 0,
            "shadow_color": [0, 0, 0],
            "shadow_offset": [0.05, 0.05],
            "shadow_blur": 0.1,
            "wrap": 0,
            "scale": [1.5272564590366715, 1],
            "type": "artwork",
            "disabled": false
          }, {
            "position": [0.8069704977611809, 0.7374245637250129],
            "angle": 0,
            "orientation": 0,
            "text": "Double tap to edit",
            "align": "left",
            "mode": "normal",
            "font_name": "Sensa Brush",
            "font_family": "\"Sensa Brush\", cursive",
            "font_weight": "normal",
            "font_style": "normal",
            "font_size": 0.3355263157894737,
            "color": [1, 1, 1, 1],
            "stroke_size": 0.01,
            "opacity": 1,
            "blend_mode": 0,
            "blend_mix": 0,
            "line_height": 1.25,
            "letter_spacing": 0,
            "shadow_opacity": 0,
            "shadow_color": [0, 0, 0],
            "shadow_offset": [0.05, 0.05],
            "shadow_blur": 0.1,
            "wrap": 0,
            "scale": [1, 1],
            "disabled": false
          }
        ]
      }, {
        state: [
          {
            "position": [0.4978817866541365, 0.504265852533028],
            "angle": 0,
            "orientation": 0,
            "text": "Double tap to edit",
            "align": "center",
            "mode": "normal",
            "font_name": "Avenir",
            "font_family": "\"Avenir Next\", \"Avenir\", sans-serif",
            "font_weight": "normal",
            "font_style": "normal",
            "font_size": 0.6907894736842105,
            "color": [1, 1, 1, 1],
            "stroke_size": 0.01,
            "opacity": 1,
            "blend_mode": 0,
            "blend_mix": -0.35263157894736863,
            "line_height": 1.118421052631579,
            "letter_spacing": 0.06578947368421052,
            "shadow_opacity": 0,
            "shadow_color": [0, 0, 0],
            "shadow_offset": [0.05, 0.05],
            "shadow_blur": 0.1,
            "wrap": 0.46809609795363905,
            "scale": [1, 1],
            "disabled": false
          }, {
            "position": [0.5, 0.5],
            "angle": 0,
            "orientation": 0,
            "text": "b",
            "align": "center",
            "mode": "outline",
            "font_name": "Sans Serif",
            "font_family": "Artwork",
            "font_weight": "normal",
            "font_style": "normal",
            "font_size": 2,
            "color": [1, 1, 1, 1],
            "stroke_size": 0.01,
            "opacity": 1,
            "blend_mode": 0,
            "blend_mix": -0.3026315789473686,
            "line_height": 1,
            "letter_spacing": 0,
            "shadow_opacity": 0,
            "shadow_color": [0, 0, 0],
            "shadow_offset": [0.05, 0.05],
            "shadow_blur": 0.1,
            "wrap": 0,
            "scale": [1, 1],
            "type": "artwork",
            "disabled": false
          }
        ]
      }, {
        state: [
          {
            "position": [0.4959568735279206, 0.23321224759932524],
            "angle": 0,
            "orientation": 0,
            "text": "Double tap to edit",
            "align": "center",
            "mode": "normal",
            "font_name": "Condensed",
            "font_family": "\"Impact\", \"HelveticaNeue-CondensedBlack\", sans-serif",
            "font_weight": "normal",
            "font_style": "normal",
            "font_size": 0.75,
            "color": [1, 1, 1, 1],
            "stroke_size": 0.01,
            "opacity": 1,
            "blend_mode": 0,
            "blend_mix": 0,
            "line_height": 1.25,
            "letter_spacing": 0,
            "shadow_opacity": 0,
            "shadow_color": [0, 0, 0],
            "shadow_offset": [0.05, 0.05],
            "shadow_blur": 0.1,
            "wrap": 0,
            "scale": [1, 1],
            "disabled": false
          }, {
            "position": [0.5, 0.7699049649098747],
            "angle": 0,
            "orientation": 0,
            "text": "Double tap to edit",
            "align": "center",
            "mode": "normal",
            "font_name": "Condensed",
            "font_family": "\"Impact\", \"HelveticaNeue-CondensedBlack\", sans-serif",
            "font_weight": "normal",
            "font_style": "normal",
            "font_size": 0.75,
            "color": [1, 1, 1, 1],
            "stroke_size": 0.01,
            "opacity": 1,
            "blend_mode": 0,
            "blend_mix": 0,
            "line_height": 1.25,
            "letter_spacing": 0,
            "shadow_opacity": 0,
            "shadow_color": [0, 0, 0],
            "shadow_offset": [0.05, 0.05],
            "shadow_blur": 0.1,
            "wrap": 0,
            "scale": [1, 1],
            "disabled": false
          }
        ]
      }
    ]
  }
];


},{"./lib/webfont":318,"./storage.coffee":343,"counterpart":3}],348:[function(require,module,exports){
var tutorials;

tutorials = [require("./tutorials/walkthrough_clarify.coffee"), require("./tutorials/walkthrough_whitebalancing.coffee"), require("./tutorials/walkthrough_distortion.coffee"), require("./tutorials/walkthrough_deepspace.coffee"), require("./tutorials/adjustments_guide.coffee"), require("./tutorials/face_tools.coffee"), require("./tutorials/radial_mask.coffee"), require("./tutorials/gradient_mask.coffee"), require("./tutorials/color_mask.coffee"), require("./tutorials/brush_mask.coffee"), require("./tutorials/getting_started.coffee")];

module.exports = tutorials;


},{"./tutorials/adjustments_guide.coffee":349,"./tutorials/brush_mask.coffee":350,"./tutorials/color_mask.coffee":351,"./tutorials/face_tools.coffee":352,"./tutorials/getting_started.coffee":353,"./tutorials/gradient_mask.coffee":354,"./tutorials/radial_mask.coffee":355,"./tutorials/walkthrough_clarify.coffee":356,"./tutorials/walkthrough_deepspace.coffee":357,"./tutorials/walkthrough_distortion.coffee":358,"./tutorials/walkthrough_whitebalancing.coffee":359}],349:[function(require,module,exports){
var translate;

translate = require("counterpart");

module.exports = {
  sep: true,
  id: "adjustment_guide",
  name: translate("tutorial.mastering_adjustments"),
  icon: "adjustments",
  triggerStepOn: ["activate_control", "show_adjustment_panel"],
  steps: [
    {
      showIcons: ["adjustments"],
      tip: "<b>" + translate("labels.temperature") + "</b><br/>" + translate("tutorial.temperature"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='temperature']",
      triggerOn: "temperature",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "color",
      images: [
        {
          src: "guides/temp.jpg",
          label: translate("labels.temperature") + " 0"
        }, {
          src: "guides/temp_100.jpg",
          label: translate("labels.temperature") + " 100"
        }, {
          src: "guides/temp_-100.jpg",
          label: translate("labels.temperature") + " -100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.tint") + "</b><br/>" + translate("tutorial.tint"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='tint']",
      triggerOn: "tint",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "color",
      images: [
        {
          src: "guides/temp.jpg",
          label: translate("labels.tint") + " 0"
        }, {
          src: "guides/tint_100.jpg",
          label: translate("labels.tint") + " 100"
        }, {
          src: "guides/tint_-100.jpg",
          label: translate("labels.tint") + " -100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.vibrance") + "</b><br/>" + translate("tutorial.vibrance"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='vibrance']",
      triggerOn: "vibrance",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "color",
      images: [
        {
          src: "guides/vibrance.jpg",
          label: translate("labels.vibrance") + " 0"
        }, {
          src: "guides/vibrance_100.jpg",
          label: translate("labels.vibrance") + " 100"
        }, {
          src: "guides/vibrance_-100.jpg",
          label: translate("labels.vibrance") + " -100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.saturation") + "</b><br/>" + translate("tutorial.saturation"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='saturation']",
      triggerOn: "saturation",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "color",
      images: [
        {
          src: "guides/saturation.jpg",
          label: translate("labels.saturation") + " 0"
        }, {
          src: "guides/saturation_100.jpg",
          label: translate("labels.saturation") + " 100"
        }, {
          src: "guides/saturation_-100.jpg",
          label: translate("labels.saturation") + " -100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.dehaze") + "</b><br/>" + translate("tutorial.dehaze"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='dehaze']",
      triggerOn: "dehaze",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      images: [
        {
          src: "guides/dehaze_0.jpg",
          label: translate("labels.dehaze") + " 0"
        }, {
          src: "guides/dehaze_80.jpg",
          label: translate("labels.dehaze") + " 80"
        }, {
          src: "guides/dehaze_-80.jpg",
          label: translate("labels.dehaze") + " -80"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.exposure") + "</b><br/>" + translate("tutorial.exposure"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='exposure']",
      triggerOn: "exposure",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      images: [
        {
          src: "guides/exposure.jpg",
          label: translate("labels.exposure") + " 0"
        }, {
          src: "guides/exposure_50.jpg",
          label: translate("labels.exposure") + " 50"
        }, {
          src: "guides/exposure_-50.jpg",
          label: translate("labels.exposure") + " -50"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.brightness") + "</b><br/>" + translate("tutorial.brightness"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='gamma']",
      triggerOn: "gamma",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      images: [
        {
          src: "guides/exposure.jpg",
          label: translate("labels.brightness") + " 0"
        }, {
          src: "guides/brightness_50.jpg",
          label: translate("labels.brightness") + " 50"
        }, {
          src: "guides/brightness_-50.jpg",
          label: translate("labels.brightness") + " -50"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.contrast") + "</b><br/>" + translate("tutorial.contrast"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='contrast']",
      triggerOn: "contrast",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      images: [
        {
          src: "guides/temp.jpg",
          label: translate("labels.contrast") + " 0"
        }, {
          src: "guides/contrast_100.jpg",
          label: translate("labels.contrast") + " 100"
        }, {
          src: "guides/contrast_-100.jpg",
          label: translate("labels.contrast") + " -100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.highlights") + "</b><br/>" + translate("tutorial.highlights"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='highlights']",
      triggerOn: "highlights",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      images: [
        {
          src: "guides/highlights.jpg",
          label: translate("labels.highlights") + " 0"
        }, {
          src: "guides/highlights_-100.jpg",
          label: translate("labels.highlights") + " -100"
        }, {
          src: "guides/highlights_100.jpg",
          label: translate("labels.highlights") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.shadows") + "</b><br/>" + translate("tutorial.shadows"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='shadows']",
      triggerOn: "shadows",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      images: [
        {
          src: "guides/shadows.jpg",
          label: translate("labels.shadows") + " 0"
        }, {
          src: "guides/shadows_-100.jpg",
          label: translate("labels.shadows") + " -100"
        }, {
          src: "guides/shadows_100.jpg",
          label: translate("labels.shadows") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.whites") + "</b><br/>" + translate("tutorial.whites"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='whites']",
      triggerOn: "whites",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      images: [
        {
          src: "guides/highlights.jpg",
          label: translate("labels.whites") + " 0"
        }, {
          src: "guides/whites_-100.jpg",
          label: translate("labels.whites") + " -100"
        }, {
          src: "guides/whites_100.jpg",
          label: translate("labels.whites") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.blacks") + "</b><br/>" + translate("tutorial.blacks"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='blacks']",
      triggerOn: "blacks",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      images: [
        {
          src: "guides/shadows.jpg",
          label: translate("labels.blacks") + " 0"
        }, {
          src: "guides/blacks_-100.jpg",
          label: translate("labels.blacks") + " -100"
        }, {
          src: "guides/blacks_100.jpg",
          label: translate("labels.blacks") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.clarity") + "</b><br/>" + translate("tutorial.clarity"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='clarity']",
      triggerOn: "clarity",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "detail",
      images: [
        {
          src: "guides/clarity_0.jpg",
          label: translate("labels.clarity") + " 0"
        }, {
          src: "guides/clarity_100.jpg",
          label: translate("labels.clarity") + " 100"
        }, {
          src: "guides/clarity_-100.jpg",
          label: translate("labels.clarity") + " -100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.sharpen") + "</b><br/>" + translate("tutorial.sharpen"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='sharpen']",
      triggerOn: "sharpen",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "detail",
      images: [
        {
          src: "guides/sharpen_0.jpg",
          label: translate("labels.sharpen") + " 0"
        }, {
          src: "guides/sharpen_100.jpg",
          label: translate("labels.sharpen") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.denoise") + "</b><br/>" + translate("tutorial.denoise"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='luminance_denoise']",
      triggerOn: "luminance_denoise",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "detail",
      images: [
        {
          src: "guides/denoise_0.jpg",
          label: translate("labels.denoise") + " 0"
        }, {
          src: "guides/denoise_100.jpg",
          label: translate("labels.denoise") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.vignette") + "</b><br/>" + translate("tutorial.vignette_amount"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='vignette_amount']",
      triggerOn: "vignette_amount",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "vignette",
      images: [
        {
          src: "guides/effects.jpg",
          label: translate("labels.vignette") + " 0"
        }, {
          src: "guides/vignette_-100.jpg",
          label: translate("labels.vignette") + " -100"
        }, {
          src: "guides/vignette_100.jpg",
          label: translate("labels.vignette") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.vignette") + " " + translate("labels.feather") + "</b><br/>" + translate("tutorial.vignette_feather"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='vignette_feather']",
      triggerOn: "vignette_feather",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "vignette",
      images: [
        {
          src: "guides/vignette_feather_0.jpg",
          label: translate("labels.feather") + " 0"
        }, {
          src: "guides/vignette_feather_100.jpg",
          label: translate("labels.feather") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.vignette") + " " + translate("labels.highlights") + "</b><br/>" + translate("tutorial.vignette_highlights"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='vignette_highlights']",
      triggerOn: "vignette_highlights",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "vignette",
      images: [
        {
          src: "guides/vignette_highlights_0.jpg",
          label: translate("labels.highlights") + " 0"
        }, {
          src: "guides/vignette_highlights_100.jpg",
          label: translate("labels.highlights") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.vignette") + " " + translate("labels.size") + "</b><br/>" + translate("tutorial.vignette_size"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='vignette_size']",
      triggerOn: "vignette_size",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "vignette",
      images: [
        {
          src: "guides/vignette_size_100.jpg",
          label: translate("labels.size") + " 100"
        }, {
          src: "guides/vignette_size_50.jpg",
          label: translate("labels.size") + " 50"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.vignette") + " " + translate("labels.roundness") + "</b><br/>" + translate("tutorial.vignette_roundness"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='vignette_roundness']",
      triggerOn: "vignette_roundness",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "vignette",
      images: [
        {
          src: "guides/vignette_feather_0.jpg",
          label: translate("labels.vignette") + " " + translate("labels.roundness") + " 0"
        }, {
          src: "guides/vignette_roundness_100.jpg",
          label: translate("labels.roundness") + " 100"
        }, {
          src: "guides/vignette_roundness_-100.jpg",
          label: translate("labels.roundness") + " -100"
        }
      ]
    }, {
      tip: "<b>" + translate("tutorial.hsl_header") + "</b><br/>" + translate("tutorial.hsl"),
      tipAnchor: ".panel[data-name='adjustments'] .panel-header[data-panel='hsl']",
      triggerOn: "hsl",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "hsl",
      images: [
        {
          src: "guides/hsl_red_0.jpg",
          label: translate("labels.hsl") + " " + translate("labels.red") + " 0"
        }, {
          src: "guides/hsl_red_100.jpg",
          label: translate("labels.hsl") + " " + translate("labels.red") + " 100"
        }, {
          src: "guides/hsl_red_-100.jpg",
          label: translate("labels.hsl") + " " + translate("labels.red") + " -100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.curves") + "</b><br/>" + translate("tutorial.curves"),
      tipAnchor: ".panel[data-name='adjustments'] .panel-header[data-panel='curves']",
      triggerOn: "curves",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "curves",
      images: [
        {
          src: "guides/curves.jpg",
          label: translate("labels.curves")
        }, {
          src: "guides/curves_brighten.jpg",
          label: translate("labels.curves")
        }, {
          src: "guides/curves_darken.jpg",
          label: translate("labels.curves")
        }, {
          src: "guides/curves_contrast.jpg",
          label: translate("labels.curves")
        }
      ]
    }, {
      tip: "<b>" + translate("labels.split_tone") + "</b><br/>" + translate("tutorial.toning_shadows"),
      tipAnchor: ".panel[data-name='adjustments'] .swatches.shadows",
      triggerOn: "toning",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "toning",
      images: [
        {
          src: "guides/shadows.jpg",
          label: translate("labels.shadows")
        }, {
          src: "guides/shadows_red.jpg",
          label: translate("labels.shadows") + " " + translate("labels.red")
        }
      ]
    }, {
      tip: "<b>" + translate("labels.split_tone") + "</b><br/>" + translate("tutorial.toning_highlights"),
      tipAnchor: ".panel[data-name='adjustments'] .swatches.highlights",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "toning",
      images: [
        {
          src: "guides/shadows.jpg",
          label: translate("labels.highlights")
        }, {
          src: "guides/highlights_red.jpg",
          label: translate("labels.highlights") + " " + translate("labels.red")
        }
      ]
    }, {
      tip: "<b>" + translate("labels.lens_distortion") + "</b><br/>" + translate("tutorial.distortion_amount"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='distortion_amount']",
      triggerOn: "distortion_amount",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "distortion",
      images: [
        {
          src: "guides/lens_distort.jpg",
          label: translate("labels.lens_distortion") + " 0"
        }, {
          src: "guides/lens_distort_-100.jpg",
          label: translate("labels.lens_distortion") + " -100"
        }, {
          src: "guides/lens_distort_100.jpg",
          label: translate("labels.lens_distortion") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.distortion_horizontal") + "</b><br/>" + translate("tutorial.distortion_horizontal"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='distortion_horizontal']",
      triggerOn: "distortion_horizontal",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "distortion",
      images: [
        {
          src: "guides/lens_distort.jpg",
          label: translate("labels.distortion_horizontal") + " 0"
        }, {
          src: "guides/h_distort_-100.jpg",
          label: translate("labels.distortion_horizontal") + " -100"
        }, {
          src: "guides/h_distort_100.jpg",
          label: translate("labels.distortion_horizontal") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.distortion_vertical") + "</b><br/>" + translate("tutorial.distortion_vertical"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='distortion_vertical']",
      triggerOn: "distortion_vertical",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "distortion",
      images: [
        {
          src: "guides/lens_distort.jpg",
          label: translate("labels.distortion_vertical") + " 0"
        }, {
          src: "guides/v_distort_-100.jpg",
          label: translate("labels.distortion_vertical") + " -100"
        }, {
          src: "guides/v_distort_100.jpg",
          label: translate("labels.distortion_vertical") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.fringing") + "</b><br/>" + translate("tutorial.fringing"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='fringing']",
      triggerOn: "fringing",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "effects",
      images: [
        {
          src: "guides/effects.jpg",
          label: translate("labels.fringing") + " 0"
        }, {
          src: "guides/fringing_50.jpg",
          label: translate("labels.fringing") + " 50"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.mosaic") + "</b><br/>" + translate("tutorial.mosaic"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='mosaic_size']",
      triggerOn: "mosaic_size",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "effects",
      images: [
        {
          src: "guides/effects.jpg",
          label: translate("labels.mosaic") + " 0"
        }, {
          src: "guides/pixelate_10.jpg",
          label: translate("labels.mosaic") + " 10"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.diffuse") + "</b><br/>" + translate("tutorial.diffuse"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='diffuse']",
      triggerOn: "diffuse",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "effects",
      images: [
        {
          src: "guides/diffuse_0.jpg",
          label: translate("labels.diffuse") + " 0"
        }, {
          src: "guides/diffuse_100.jpg",
          label: translate("labels.diffuse") + " 100"
        }
      ]
    }, {
      tip: "<b>" + translate("labels.grain") + "</b><br/>" + translate("tutorial.grain"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='grain_amount']",
      triggerOn: "grain_amount",
      showNext: true,
      showPrev: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "effects",
      images: [
        {
          src: "guides/grain_0.jpg",
          label: translate("labels.grain") + " 0"
        }, {
          src: "guides/grain_100.jpg",
          label: translate("labels.grain") + " 100"
        }
      ]
    }
  ]
};


},{"counterpart":3}],350:[function(require,module,exports){
var translate;

translate = require("counterpart");

module.exports = {
  id: "brush_mask",
  name: translate("tooltips.brush_filter"),
  icon: "brush",
  steps: [
    {
      showIcons: ["local-adjustments"],
      tip: translate("tutorial.brush_mask_0"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      video: {
        src: "guides/brush_0.mp4",
        width: 150,
        height: 150
      }
    }, {
      tip: translate("tutorial.brush_mask_1"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      control: {
        type: "slider",
        name: "exposure",
        value: 0.5
      },
      imageStyle: "cover",
      images: [
        {
          src: "guides/brush_1a.jpg",
          value: 0.5
        }, {
          src: "guides/brush_1b.jpg",
          value: -1
        }, {
          src: "guides/brush_1c.jpg",
          value: 1
        }
      ]
    }
  ]
};


},{"counterpart":3}],351:[function(require,module,exports){
var translate;

translate = require("counterpart");

module.exports = {
  id: "color_mask",
  name: translate("tooltips.color_filter"),
  icon: "color",
  steps: [
    {
      showIcons: ["local-adjustments"],
      tip: translate("tutorial.color_mask_0"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      control: {
        type: "picker",
        name: "tint"
      },
      imageStyle: "cover",
      images: [
        {
          src: "guides/color_1a.jpg",
          value: [0, 0]
        }, {
          src: "guides/color_1b.jpg",
          value: [0, 1]
        }
      ]
    }, {
      tip: translate("tutorial.color_mask_1"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      video: {
        src: "guides/color_0.mp4",
        width: 150,
        height: 150
      }
    }, {
      tip: translate("tutorial.color_mask_2"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      control: {
        type: "slider",
        name: "threshold",
        min: 0
      },
      imageStyle: "cover",
      images: [
        {
          src: "guides/color_2a.jpg",
          value: 0
        }, {
          src: "guides/color_2b.jpg",
          value: 1
        }
      ]
    }
  ]
};


},{"counterpart":3}],352:[function(require,module,exports){
var translate;

translate = require("counterpart");

module.exports = {
  id: "face_tools",
  name: translate("tooltips.face"),
  icon: "face",
  steps: [
    {
      tip: translate("tutorial.face_0"),
      showStep: true,
      showNext: true,
      showPanel: "face",
      imageStyle: "cover",
      images: [
        {
          src: "guides/face_1a.jpg"
        }, {
          src: "guides/face_1b.jpg"
        }
      ]
    }, {
      tip: translate("tutorial.face_1"),
      showStep: true,
      showNext: true,
      showPanel: "face",
      video: {
        src: "guides/face_0.mp4",
        width: 150,
        height: 150
      }
    }
  ]
};


},{"counterpart":3}],353:[function(require,module,exports){
var desktopSteps, mobileSteps, translate;

translate = require("counterpart");

desktopSteps = [
  {
    tip: translate("onboarding.step_0"),
    tipAnchor: ".toolbar .icon-looks",
    pointer: ".toolbar .icon-looks",
    set: {
      filter_tab: 2
    },
    hidePanels: true,
    nextOn: {
      event: "show_panel",
      when: "looks"
    },
    showIcons: ["looks"]
  }, {
    tip: translate("onboarding.step_1"),
    tipAnchor: ".tab-panel[data-index='2'] .preview",
    pointer: ".tab-panel[data-index='2'] .preview",
    nextOn: "select_filter",
    showPanel: "looks",
    showIcons: ["looks"]
  }, {
    tip: translate("onboarding.step_2"),
    tipAnchor: ".popout-slider .icon-check",
    pointer: ".popout-slider .icon-check",
    nextOn: "apply_filter",
    showIcons: ["looks"]
  }, {
    tip: translate("onboarding.step_3"),
    tipAnchor: ".toolbar .icon-adjustments",
    pointer: ".toolbar .icon-adjustments",
    nextOn: {
      event: "show_panel",
      when: "adjustments"
    },
    showIcons: ["looks", "adjustments"]
  }, {
    tip: translate("onboarding.step_4"),
    tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='temperature']",
    pointer: ".panel[data-name='adjustments'] .slider[data-name='temperature']",
    nextOn: {
      event: "history",
      when: {
        message: "labels.temperature"
      }
    },
    showAdjustmentPanel: "color",
    showIcons: ["looks", "adjustments"]
  }, {
    tip: translate("onboarding.step_5"),
    tipAnchor: ".toolbar .icon-history",
    pointer: ".toolbar .icon-history",
    nextOn: {
      event: "show_panel",
      when: "history"
    },
    showIcons: ["looks", "adjustments", "history"]
  }, {
    tip: translate("onboarding.step_6"),
    tipAnchor: ".panel[data-name='history']",
    pointer: ".toolbar .icon-undo",
    showNext: true,
    showIcons: ["looks", "adjustments", "history", "undo"]
  }, {
    tip: translate("onboarding.step_7"),
    tipAnchor: ".toolbars .icon-export",
    pointer: ".toolbars .icon-export",
    showNext: true,
    showIcons: ["looks", "adjustments", "history", "undo", "export"]
  }, {
    tip: translate("onboarding.step_8"),
    tipAnchor: ".toolbars .icon-import",
    pointer: ".toolbars .icon-import",
    nextOn: "load_image",
    showIcons: ["looks", "adjustments", "history", "undo", "export", "import"]
  }, {
    tip: translate("onboarding.step_9"),
    tipAnchor: ".toolbars .icon-filmstrip",
    pointer: ".toolbars .icon-filmstrip",
    nextOn: {
      event: "show_panel",
      when: "filmstrip"
    },
    showIcons: ["looks", "adjustments", "history", "undo", "export", "import", "filmstrip"]
  }, {
    tip: translate("onboarding.step_10"),
    fullscreen: true,
    video: {
      src: "guides/icons_landscape.mp4",
      width: 500 / 2,
      height: 280 / 2
    },
    showNext: true,
    showIcons: ["looks", "adjustments", "history", "undo", "export", "import", "filmstrip", "local-adjustments", "crop"]
  }
];

mobileSteps = [
  {
    tip: translate("onboarding.step_0"),
    tipAnchor: ".toolbar .icon-looks",
    pointer: ".toolbar .icon-looks",
    hidePanels: true,
    set: {
      filter_tab: 2
    },
    nextOn: {
      event: "show_panel",
      when: "looks"
    },
    showIcons: ["looks"]
  }, {
    tip: translate("onboarding.step_1"),
    tipAnchor: ".tab-panel[data-index='2'] .preview",
    pointer: ".tab-panel[data-index='2'] .preview",
    nextOn: "select_filter",
    showPanel: "looks",
    showIcons: ["looks"]
  }, {
    tip: translate("onboarding.step_2"),
    tipAnchor: ".popout-slider .icon-check",
    pointer: ".popout-slider .icon-check",
    nextOn: "apply_filter",
    showIcons: ["looks"]
  }, {
    tip: translate("onboarding.step_3"),
    tipAnchor: ".toolbar .icon-adjustments",
    pointer: ".toolbar .icon-adjustments",
    nextOn: {
      event: "show_panel",
      when: "adjustments"
    },
    showIcons: ["looks", "adjustments"]
  }, {
    tip: translate("onboarding.step_4a"),
    video: {
      src: "guides/mobile_slider.mp4",
      width: 160 / 2,
      height: 160 / 2
    },
    tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='temperature']",
    pointer: ".panel[data-name='adjustments'] .slider[data-name='temperature']",
    nextOn: {
      event: "history",
      when: {
        message: "labels.temperature"
      }
    },
    showAdjustmentPanel: "color",
    showIcons: ["looks", "adjustments"]
  }, {
    tip: translate("onboarding.step_5"),
    tipAnchor: ".toolbar .icon-history",
    pointer: ".toolbar .icon-history",
    nextOn: {
      event: "show_panel",
      when: "history"
    },
    showIcons: ["looks", "adjustments", "history"]
  }, {
    tip: translate("onboarding.step_6"),
    tipAnchor: ".panel[data-name='history']",
    pointer: ".toolbar .icon-undo",
    showNext: true,
    showIcons: ["looks", "adjustments", "history", "undo"]
  }, {
    tip: translate("onboarding.step_7a"),
    tipAnchor: ".toolbars .icon-export",
    pointer: ".toolbars .icon-export",
    showNext: true,
    showIcons: ["looks", "adjustments", "history", "undo", "export"]
  }, {
    tip: translate("onboarding.step_8"),
    tipAnchor: CONFIG.os === "android" ? ".toolbars .icon-import" : ".toolbars .icon-back",
    pointer: CONFIG.os === "android" ? ".toolbars .icon-import" : ".toolbars .icon-back",
    nextOn: "load_image",
    showIcons: ["looks", "adjustments", "history", "undo", "export", "back", "import"]
  }, {
    tip: translate("onboarding.step_10"),
    fullscreen: true,
    video: {
      src: "guides/icons_portrait.mp4",
      width: 750 / 2,
      height: 1334 / 2
    },
    showNext: true,
    showIcons: ["looks", "adjustments", "history", "undo", "export", "back", "import", "filmstrip", "local-adjustments", "crop"]
  }
];

module.exports = {
  id: "getting_started",
  name: {
    en: "Getting started"
  },
  icon: "play",
  steps: CONFIG.platform === "mobile" ? mobileSteps : desktopSteps
};


},{"counterpart":3}],354:[function(require,module,exports){
var translate;

translate = require("counterpart");

module.exports = {
  id: "gradient_mask",
  name: translate("tooltips.gradient_filter"),
  icon: "gradient",
  steps: [
    {
      showIcons: ["local-adjustments"],
      tip: translate("tutorial.gradient_mask_0"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      control: {
        type: "slider",
        name: "temperature"
      },
      imageStyle: "cover",
      images: [
        {
          src: "guides/gradient_1a.jpg",
          value: 0
        }, {
          src: "guides/gradient_1b.jpg",
          value: 1
        }, {
          src: "guides/gradient_1c.jpg",
          value: -1
        }
      ]
    }, {
      tip: translate("tutorial.gradient_mask_1"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      video: {
        src: "guides/gradient_0.mp4",
        width: 150,
        height: 150
      }
    }, {
      tip: translate("tutorial.gradient_mask_2"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      control: {
        type: "button",
        name: "invert"
      },
      imageStyle: "cover",
      images: [
        {
          src: "guides/gradient_2a.jpg",
          value: false
        }, {
          src: "guides/gradient_2b.jpg",
          label: translate("labels.invert"),
          value: true
        }
      ]
    }, {
      tip: translate("tutorial.gradient_mask_3"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      control: {
        type: "button",
        name: "reflect"
      },
      imageStyle: "cover",
      images: [
        {
          src: "guides/gradient_2a.jpg",
          value: false
        }, {
          src: "guides/gradient_2c.jpg",
          label: translate("labels.reflect"),
          value: true
        }
      ]
    }
  ]
};


},{"counterpart":3}],355:[function(require,module,exports){
var translate;

translate = require("counterpart");

module.exports = {
  id: "radial_mask",
  name: translate("tooltips.radial_filter"),
  icon: "radial",
  steps: [
    {
      showIcons: ["local-adjustments"],
      tip: translate("tutorial.radial_mask_0"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      control: {
        type: "slider",
        name: "exposure"
      },
      imageStyle: "cover",
      images: [
        {
          src: "guides/radial_1a.jpg",
          value: 0
        }, {
          src: "guides/radial_1b.jpg",
          value: -1
        }, {
          src: "guides/radial_1c.jpg",
          value: 1
        }
      ]
    }, {
      tip: translate("tutorial.radial_mask_1"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      video: {
        src: "guides/radial_0.mp4",
        width: 150,
        height: 150
      }
    }, {
      tip: translate("tutorial.radial_mask_2"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      control: {
        type: "slider",
        name: "feather",
        min: 0,
        value: 1
      },
      imageStyle: "cover",
      images: [
        {
          src: "guides/radial_2a.jpg",
          value: 1
        }, {
          src: "guides/radial_2b.jpg",
          value: 0
        }
      ]
    }, {
      tip: translate("tutorial.radial_mask_3"),
      showStep: true,
      showNext: true,
      showPanel: "local-adjustments",
      control: {
        type: "button",
        name: "invert",
        active: true
      },
      imageStyle: "cover",
      images: [
        {
          src: "guides/radial_2b.jpg",
          label: translate("labels.invert"),
          value: true
        }, {
          src: "guides/radial_2c.jpg",
          value: false
        }
      ]
    }
  ]
};


},{"counterpart":3}],356:[function(require,module,exports){
var state, translate;

translate = require("counterpart");

state = Filtr.utils.assign(Filtr.getDefaultState(true), {
  dehaze: 0.85,
  exposure: 0.3,
  contrast: 0.6,
  clarity: 0.4
});

module.exports = {
  id: "clarify",
  name: translate("tutorial.clarify"),
  description: translate("tutorial.clarify_description"),
  image: "tutorials/clarify_thumb.jpg",
  state: state,
  steps: [
    {
      loadImage: "tutorials/clarify.jpg",
      nextOn: "load_image"
    }, {
      showBeforeAfter: true,
      nextOn: "start_tutorial",
      hidePanels: true,
      showIcons: ["adjustments"]
    }, {
      tip: translate("tutorial.clarify_1"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='dehaze']",
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      set: {
        sliderSnap: {
          dehaze: 0.85
        }
      },
      nextOn: {
        event: "history",
        when: {
          message: "labels.dehaze",
          value: 85
        }
      }
    }, {
      tip: translate("tutorial.clarify_2"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='exposure']",
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      set: {
        sliderSnap: {
          exposure: 0.3
        }
      },
      nextOn: {
        event: "history",
        when: {
          message: "labels.exposure",
          value: 30
        }
      }
    }, {
      tip: translate("tutorial.clarify_3"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='contrast']",
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      set: {
        sliderSnap: {
          contrast: 0.6
        }
      },
      nextOn: {
        event: "history",
        when: {
          message: "labels.contrast",
          value: 60
        }
      }
    }, {
      tip: translate("tutorial.clarify_4"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='clarity']",
      showPanel: "adjustments",
      showAdjustmentPanel: "detail",
      set: {
        sliderSnap: {
          clarity: 0.4
        }
      },
      nextOn: {
        event: "history",
        when: {
          message: "labels.clarity",
          value: 40
        }
      }
    }, {
      tip: translate("tutorial.clarify_5"),
      showNext: true,
      showFeedback: true
    }
  ]
};


},{"counterpart":3}],357:[function(require,module,exports){
var state, translate;

translate = require("counterpart");

state = Filtr.utils.assign(Filtr.getDefaultState(true), {
  exposure: 1.0,
  clarity: 1.0,
  vignette_amount: -1.0,
  vignette_feather: 1.0,
  vignette_highlights: 1.0,
  hue_aqua: 1.0
});

module.exports = {
  id: "deepspace",
  name: translate("tutorial.deep_space"),
  description: translate("tutorial.deep_space_description"),
  image: "tutorials/deepspace_thumb.jpg",
  state: state,
  steps: [
    {
      loadImage: "tutorials/deepspace.jpg",
      nextOn: "load_image"
    }, {
      showBeforeAfter: true,
      nextOn: "start_tutorial",
      hidePanels: true,
      showIcons: ["adjustments"]
    }, {
      tip: translate("tutorial.deep_space_1"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='exposure']",
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      nextOn: {
        event: "history",
        when: {
          message: "labels.exposure",
          value: 100
        }
      }
    }, {
      tip: translate("tutorial.deep_space_2"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='clarity']",
      showPanel: "adjustments",
      showAdjustmentPanel: "detail",
      nextOn: {
        event: "history",
        when: {
          message: "labels.clarity",
          value: 100
        }
      }
    }, {
      tip: translate("tutorial.deep_space_3"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='vignette_amount']",
      showPanel: "adjustments",
      showAdjustmentPanel: "vignette",
      nextOn: {
        event: "history",
        when: {
          message: "labels.vignette labels.amount",
          value: -100
        }
      }
    }, {
      tip: translate("tutorial.deep_space_4"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='vignette_feather']",
      showPanel: "adjustments",
      showAdjustmentPanel: "vignette",
      nextOn: {
        event: "history",
        when: {
          message: "labels.vignette labels.feather",
          value: 100
        }
      }
    }, {
      tip: translate("tutorial.deep_space_5"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='vignette_highlights']",
      showPanel: "adjustments",
      showAdjustmentPanel: "vignette",
      nextOn: {
        event: "history",
        when: {
          message: "labels.vignette labels.highlights",
          value: 100
        }
      }
    }, {
      tip: translate("tutorial.deep_space_6"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='hue_aqua']",
      showPanel: "adjustments",
      showAdjustmentPanel: "hsl",
      set: {
        "hsl_tab": "aqua"
      },
      nextOn: {
        event: "history",
        when: {
          message: "labels.aqua labels.hue",
          value: 100
        }
      }
    }, {
      tip: translate("tutorial.deep_space_7"),
      showNext: true,
      showFeedback: true
    }
  ]
};


},{"counterpart":3}],358:[function(require,module,exports){
var state, translate;

translate = require("counterpart");

state = Filtr.utils.assign(Filtr.getDefaultState(true), {
  distortion_vertical: 0.35,
  vignette_amount: 0.2,
  clarity: 1.0
});

module.exports = {
  id: "distortion",
  name: translate("tutorial.distortion"),
  description: translate("tutorial.distortion_description"),
  image: "tutorials/distortion_thumb.jpg",
  state: state,
  steps: [
    {
      loadImage: "tutorials/distortion.jpg",
      nextOn: "load_image"
    }, {
      showBeforeAfter: true,
      nextOn: "start_tutorial",
      hidePanels: true,
      showIcons: ["adjustments"]
    }, {
      tip: translate("tutorial.distortion_1"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='distortion_vertical']",
      showStep: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "distortion",
      set: {
        sliderSnap: {
          distortion_vertical: 0.35
        }
      },
      nextOn: {
        event: "history",
        when: {
          message: "labels.distortion_vertical",
          value: 40
        }
      }
    }, {
      tip: translate("tutorial.distortion_2"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='vignette_amount']",
      showStep: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "vignette",
      set: {
        sliderSnap: {
          vignette_amount: 0.2
        }
      },
      nextOn: {
        event: "history",
        when: {
          message: "labels.vignette labels.amount",
          value: 20
        }
      }
    }, {
      tip: translate("tutorial.distortion_3"),
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='clarity']",
      showStep: true,
      showPanel: "adjustments",
      showAdjustmentPanel: "detail",
      nextOn: {
        event: "history",
        when: {
          message: "labels.clarity",
          value: 100
        }
      }
    }, {
      tip: translate("tutorial.distortion_4"),
      showNext: true,
      showFeedback: true
    }
  ]
};


},{"counterpart":3}],359:[function(require,module,exports){
var state, translate;

translate = require("counterpart");

state = Filtr.utils.assign(Filtr.getDefaultState(true), {
  temperature: -0.3,
  shadows: 1.0,
  clarity: 1.0
});

module.exports = {
  id: "whitebalacing",
  name: translate("tutorial.white_balance"),
  description: translate("tutorial.white_balance_description"),
  image: "tutorials/whitebalancing_thumb.jpg",
  state: state,
  steps: [
    {
      loadImage: "tutorials/whitebalancing.jpg",
      nextOn: "load_image"
    }, {
      showBeforeAfter: true,
      nextOn: "start_tutorial",
      hidePanels: true,
      showIcons: ["adjustments"]
    }, {
      tip: translate("tutorial.white_balance_1"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='temperature']",
      showPanel: "adjustments",
      showAdjustmentPanel: "color",
      set: {
        sliderSnap: {
          temperature: -0.3
        }
      },
      nextOn: {
        event: "history",
        when: {
          message: "labels.temperature",
          value: -30
        }
      }
    }, {
      tip: translate("tutorial.white_balance_2"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='shadows']",
      showPanel: "adjustments",
      showAdjustmentPanel: "light",
      nextOn: {
        event: "history",
        when: {
          message: "labels.shadows",
          value: 100
        }
      }
    }, {
      tip: translate("tutorial.white_balance_3"),
      showStep: true,
      tipAnchor: ".panel[data-name='adjustments'] .slider[data-name='clarity']",
      showPanel: "adjustments",
      showAdjustmentPanel: "detail",
      nextOn: {
        event: "history",
        when: {
          message: "labels.clarity",
          value: 100
        }
      }
    }, {
      tip: translate("tutorial.white_balance_4"),
      showNext: true,
      showFeedback: true
    }
  ]
};


},{"counterpart":3}],360:[function(require,module,exports){
var assign, clamp, deepClone, shortid, translate,
  slice = [].slice;

translate = require("counterpart");

shortid = require("shortid");

exports.getArraybuffer = function(url) {
  return new Promise(function(resolve, reject) {
    var request;
    request = new XMLHttpRequest();
    request.open("GET", url, true);
    request.responseType = "arraybuffer";
    request.onload = (function(_this) {
      return function() {
        return resolve(request.response);
      };
    })(this);
    request.onerror = function(err) {
      return reject(err);
    };
    return request.send(null);
  });
};

exports.debounce = function(func, ms) {
  var timeout;
  if (ms == null) {
    ms = 1;
  }
  timeout = null;
  return function() {
    var args, ctx;
    ctx = this;
    args = arguments;
    clearTimeout(timeout);
    return timeout = setTimeout(function() {
      return func.apply(ctx, args);
    }, ms);
  };
};

exports.drawImageData = function(canvas, output) {
  var buffer, ctx, data, e, error, height, imageData, width;
  ctx = canvas.getContext('2d');
  if ((typeof ImageBitmap !== "undefined" && ImageBitmap !== null) && output instanceof ImageBitmap) {
    ctx.drawImage(output, 0, 0);
    return;
  }
  width = output.width, height = output.height, data = output.data;
  buffer = new Uint8ClampedArray(data);
  try {
    imageData = ctx.createImageData(width, height);
    imageData.data.set(buffer);
    ctx.putImageData(imageData, 0, 0);
  } catch (error) {
    e = error;
    console.error(e);
  }
  buffer = null;
  return imageData = null;
};

exports.translateMessage = function(str) {
  var message;
  if (typeof str !== "string") {
    console.log("can't translate sting: ", str);
    return "no translation";
  }
  if (str.indexOf('.') > -1) {
    message = str.split(' ').reduce(function(prev, curr) {
      if (curr[0] === '%') {
        return prev + decodeURIComponent(curr);
      } else {
        return prev + (prev && ' ' || '') + (translate(curr));
      }
    }, '');
  } else {
    message = str;
  }
  return decodeURIComponent(message);
};

exports.translateReplace = function(str, replacements) {
  var key, text, val;
  text = translate(str);
  for (key in replacements) {
    val = replacements[key];
    text = text.replace("</" + key + ">", val);
  }
  return text;
};

exports.ucFirst = function(text) {
  return text.slice(0, 1).toUpperCase() + text.slice(1);
};

exports.getPlatformOS = function() {
  var OS;
  OS = (function() {
    switch (CONFIG.platform) {
      case "mobile":
        if (CONFIG.os === "android") {
          return "Android";
        } else {
          return "iOS";
        }
        break;
      case "windows":
        return "Windows";
      case "chrome":
        return "Chrome";
      case "web":
        return "Web";
      case "osx":
        return "osx";
      case "electron":
        return "Electron";
    }
  })();
  return OS;
};

exports.getURLParams = function() {
  var j, len, pair, param, params, urlParams;
  params = {};
  urlParams = location.search ? location.search.split("?")[1].split("&") : [];
  for (j = 0, len = urlParams.length; j < len; j++) {
    param = urlParams[j];
    pair = decodeURIComponent(param).split('=');
    params[pair[0]] = pair[1];
  }
  return params;
};

exports.forceRepaint = function(el) {
  return getComputedStyle(el).getPropertyValue("display");
};

exports.getCSSRule = function(selector) {
  var j, k, len, len1, ref, ref1, rule, sheet;
  ref = document.styleSheets;
  for (j = 0, len = ref.length; j < len; j++) {
    sheet = ref[j];
    ref1 = sheet.cssRules;
    for (k = 0, len1 = ref1.length; k < len1; k++) {
      rule = ref1[k];
      if (rule.selectorText && rule.selectorText.match(selector)) {
        return rule;
      }
    }
  }
};

exports.isChina = function() {
  var locale;
  locale = translate.getLocale();
  return locale === "zh" || locale === "zh-tw";
};

exports.getUrlRoot = function(no_https) {
  if (exports.isChina()) {
    return "http://www.polaxiong.com";
  } else {
    if (no_https) {
      return "http://www.polarr.co";
    } else {
      return "https://www.polarr.co";
    }
  }
};

exports.getUSUrlRoot = function(no_https) {
  if (no_https) {
    return "http://www.polarr.co";
  } else {
    return "https://www.polarr.co";
  }
};

exports.testEmail = function(email) {
  var re;
  re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  return re.test(email);
};

exports.getWebglInfo = function() {
  var gl, info, j, len, param, params;
  gl = Filtr.context;
  params = ['MAX_RENDERBUFFER_SIZE', 'MAX_TEXTURE_SIZE', 'MAX_TEXTURE_IMAGE_UNITS', 'MAX_COMBINED_TEXTURE_IMAGE_UNITS'];
  info = {};
  for (j = 0, len = params.length; j < len; j++) {
    param = params[j];
    info[param] = gl.getParameter(gl[param]);
  }
  return info;
};

exports.clamp = clamp = function(val, min, max) {
  return Math.max(min, Math.min(max, val));
};

exports.distance = function(p1, p2) {
  var dx, dy;
  dx = p2[0] - p1[0];
  dy = p2[1] - p1[1];
  dx *= dx;
  dy *= dy;
  return Math.sqrt(dx + dy);
};

exports.calcDiag = function(a, b) {
  return Math.sqrt((a * a) + (b * b));
};

exports.rotateVector = function(vec, degrees, origin) {
  var radians;
  if (origin == null) {
    origin = [0, 0];
  }
  radians = degrees * (Math.PI / 180);
  vec = [vec[0] - origin[0], vec[1] - origin[1]];
  return [vec[0] * Math.cos(radians) - vec[1] * Math.sin(radians) + origin[0], vec[0] * Math.sin(radians) + vec[1] * Math.cos(radians) + origin[1]];
};

exports.getTouchDistance = function(e) {
  var t1, t2;
  t1 = [e.targetTouches[0].pageX, e.targetTouches[0].pageY];
  t2 = [e.targetTouches[1].pageX, e.targetTouches[1].pageY];
  return exports.distance(t1, t2);
};

exports.getTouchCenter = function(e, rotated) {
  var x, y;
  x = e.targetTouches[0].pageX * 0.5 + e.targetTouches[1].pageX * 0.5;
  y = e.targetTouches[0].pageY * 0.5 + e.targetTouches[1].pageY * 0.5;
  if (rotated) {
    return [-y, x];
  } else {
    return [x, y];
  }
};

exports.trackEventGA = function(category, action, label, value) {
  var os;
  os = exports.getPlatformOS().toLowerCase();
  category = os + "_" + category;
  if (typeof ga !== "undefined" && ga !== null) {
    return ga('send', 'event', category, action, label, value);
  } else if (typeof tracker !== "undefined" && tracker !== null) {
    return tracker.sendEvent(category, action, label, value);
  }
};

exports.onVisibilityChange = function(handler) {
  var hidden, visibilityChange;
  if (typeof document.hidden !== "undefined") {
    hidden = "hidden";
    visibilityChange = "visibilitychange";
  } else if (typeof document.mozHidden !== "undefined") {
    hidden = "mozHidden";
    visibilityChange = "mozvisibilitychange";
  } else if (typeof document.msHidden !== "undefined") {
    hidden = "msHidden";
    visibilityChange = "msvisibilitychange";
  } else if (typeof document.webkitHidden !== "undefined") {
    hidden = "webkitHidden";
    visibilityChange = "webkitvisibilitychange";
  }
  document.addEventListener(visibilityChange, handler);
  return hidden;
};

exports.openURL = function(url) {
  var ref, uri;
  if ((typeof Windows !== "undefined" && Windows !== null ? Windows.Foundation : void 0) != null) {
    uri = new Windows.Foundation.Uri(url);
    return Windows.System.Launcher.launchUriAsync(uri);
  } else if (CONFIG.os === "ios") {
    return window.location = url;
  } else if (CONFIG.os === "android") {
    return typeof POIPCManager !== "undefined" && POIPCManager !== null ? (ref = POIPCManager.sharedManager) != null ? ref.sendMessage("ppe.external.open", {
      url: url
    }) : void 0 : void 0;
  } else if (CONFIG.platform === "electron") {
    return Electron.shell.openExternal(url);
  } else {
    return window.open(url, "_blank");
  }
};

exports.clone = function(collection) {
  var clone, key, val;
  clone = {};
  for (key in collection) {
    val = collection[key];
    if (val instanceof Array) {
      val = val.slice(0);
    }
    clone[key] = val;
  }
  return clone;
};

exports.deepClone = deepClone = function(collection) {
  var copy, key;
  copy = collection;
  if (collection && typeof collection === "object" && !(collection instanceof ArrayBuffer) && !(collection.buffer instanceof ArrayBuffer)) {
    copy = collection instanceof Array ? [] : {};
    for (key in collection) {
      copy[key] = deepClone(collection[key]);
    }
  }
  return copy;
};

exports.extend = function(object, properties) {
  var key, val;
  for (key in properties) {
    val = properties[key];
    object[key] = val;
  }
  return object;
};

exports.snap = (function() {
  var defaults, startValues;
  startValues = [];
  defaults = {
    startValue: 0,
    snapPoint: 0,
    threshold: 0.1
  };
  return {
    start: function() {
      var startOptions;
      startOptions = 1 <= arguments.length ? slice.call(arguments, 0) : [];
      return startValues = startOptions.map(function(options) {
        return assign(defaults, options, {
          changed: false,
          direction: 0
        });
      });
    },
    value: function() {
      var values;
      values = 1 <= arguments.length ? slice.call(arguments, 0) : [];
      values = values.map(function(value, i) {
        var options, snapPoint, startValue, threshold;
        options = startValues[i];
        startValue = options.startValue, snapPoint = options.snapPoint, threshold = options.threshold;
        if (!options.changed && value !== startValue) {
          options.changed = true;
          options.direction = value > snapPoint ? 1 : -1;
        }
        if (!options.changed) {
          return value;
        }
        value += threshold * options.direction;
        return value -= clamp(value - snapPoint, -threshold, threshold);
      });
      if (values.length === 1) {
        return values[0];
      }
      return values;
    }
  };
})();

exports.assign = assign = function() {
  var j, key, len, obj, source, sources, val;
  sources = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  obj = {};
  for (j = 0, len = sources.length; j < len; j++) {
    source = sources[j];
    for (key in source) {
      val = source[key];
      if (source.hasOwnProperty(key)) {
        obj[key] = val;
      }
    }
  }
  return obj;
};

exports.approx = function(a, b, threshold) {
  if (threshold == null) {
    threshold = 10;
  }
  return Math.abs(a - b) <= threshold;
};

window.utils = exports;


},{"counterpart":3,"shortid":216}],361:[function(require,module,exports){
var WebGLSupport, counterpart, html, support;

counterpart = require("counterpart");

WebGLSupport = (function() {
  var canvas, e, error, error1, gl, highp;
  canvas = document.createElement('canvas');
  if (!window.WebGLRenderingContext) {
    return 0;
  }
  try {
    gl = canvas.getContext('webgl');
  } catch (error) {
    e = error;
    gl = null;
  }
  try {
    gl = canvas.getContext('experimental-webgl');
  } catch (error1) {
    e = error1;
    gl = null;
  }
  if (!gl) {
    return 1;
  }
  highp = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
  if (!highp.precision) {
    return 2;
  }
  return 3;
})();

if (WebGLSupport < 3) {
  support = document.getElementById("support");
  html = "<img src='./img/sad_error_bear.png' width='100'/><br/>";
  if (WebGLSupport === 2) {
    window.CLOSE_SUPPORT_DIALOG = function() {
      return support.style.display = "none";
    };
    html += (counterpart("dialogs.highp_not_supported")) + " <button onclick='CLOSE_SUPPORT_DIALOG()'>" + (counterpart("buttons.continue_anyway")) + "</button>";
  } else {
    html += "" + (counterpart("dialogs.webgl_not_supported"));
  }
  support.innerHTML = html;
  support.style.display = "-webkit-flex";
  support.style.display = "flex";
}


},{"counterpart":3}],362:[function(require,module,exports){
var Filtr, brush, compositor, downsample, downsampleFactor, filters, frameRate, gl, histogram, io, render, renderFullDelay, renderer, setState, spline, state, text, throttleDelay, thumbnail, trackEvent, update, updateHistogram, updateThumbnail, utils;

gl = require("./context.coffee");

utils = require("./utils.coffee");

filters = require("./filters.coffee");

compositor = require("./compositor.coffee");

renderer = require("./renderer.coffee");

brush = require("./brush.coffee");

text = require("./text.coffee");

histogram = require("./histogram.coffee");

state = require("./state.coffee");

io = require("./io.coffee");

spline = require("./spline.coffee");

thumbnail = {};

frameRate = 60;

downsampleFactor = 1;

throttleDelay = 1000 / frameRate;

renderFullDelay = throttleDelay + 200;

render = (function() {
  var annealingFactor, busy, cancelFrame, frameTimeout, renderTime, requestId;
  busy = false;
  renderTime = 1000 / frameRate;
  annealingFactor = 10;
  requestId = null;
  frameTimeout = null;
  cancelFrame = function() {
    cancelAnimationFrame(requestId);
    renderTime = Math.min(renderTime, 500);
    return busy = false;
  };
  document.addEventListener("visibilitychange", cancelFrame);
  return function() {
    var startTime;
    if (!busy) {
      busy = true;
      startTime = utils.now();
      requestId = requestAnimationFrame(function() {
        compositor.update();
        renderer.render();
        renderTime += ((utils.now() - startTime) - renderTime) / annealingFactor;
        frameRate = Math.min(1000 / (renderTime * 2), 60);
        throttleDelay = 1000 / frameRate;
        renderFullDelay = throttleDelay + 200;
        return busy = false;
      });
      clearTimeout(frameTimeout);
      return frameTimeout = setTimeout(cancelFrame, 1000);
    }
  };
})();

setState = (function() {
  var a, b, current, func, last, timeout;
  func = function(settings, render) {
    if (render == null) {
      render = true;
    }
    state.set(settings);
    if (render) {
      return update();
    } else {
      return updateHistogram();
    }
  };
  timeout = null;
  a = null;
  b = null;
  last = null;
  current = null;
  return setState = function() {
    a = arguments[0];
    b = arguments[1];
    current = utils.now();
    if (last && current < last + throttleDelay) {
      clearTimeout(timeout);
      return timeout = setTimeout(function() {
        return func(a, b);
      }, throttleDelay);
    } else {
      last = current;
      return func(a, b);
    }
  };
})();

updateThumbnail = function() {
  thumbnail.width = histogram.cache.width;
  thumbnail.height = histogram.cache.height;
  return thumbnail.data = histogram.cache.data;
};

updateHistogram = (function() {
  var timeout;
  timeout = null;
  return function() {
    clearTimeout(timeout);
    return timeout = setTimeout(function() {
      histogram.updateCache();
      histogram.update();
      updateThumbnail();
      return state.trigger("thumbnail", thumbnail);
    }, renderFullDelay);
  };
})();

downsample = (function() {
  var timeout;
  timeout = null;
  return function() {
    gl.downsampleFactor = downsampleFactor >> 0;
    compositor.resampleTextures();
    clearTimeout(timeout);
    return timeout = setTimeout(function() {
      gl.downsampleFactor = 1;
      compositor.resampleTextures();
      renderer.renderBlurTexture();
      return render();
    }, renderFullDelay);
  };
})();

update = (function() {
  var timeout;
  timeout = null;
  return update = function(stateChanged, renderCached) {
    if (stateChanged == null) {
      stateChanged = true;
    }
    if (renderCached == null) {
      renderCached = true;
    }
    if (!stateChanged) {
      if (renderCached) {
        renderer.renderCached();
      }
      clearTimeout(timeout);
      if (gl.zoom < gl.minZoom) {
        return;
      }
      timeout = setTimeout(render, renderFullDelay);
    } else {
      downsample();
      render();
      updateHistogram();
    }
  };
})();

trackEvent = function(category, action, label, value) {
  if (typeof ga !== "undefined" && ga !== null) {
    return ga('send', 'event', category, action, label, value);
  }
};

Filtr = {
  _: gl,
  pixelRatio: compositor.pixelRatio,
  context: gl.context,
  canvas: gl.canvas,
  histogram: histogram.canvas,
  utils: utils,
  filters: filters,
  brush: brush,
  text: text,
  spline: spline,
  on: state.on,
  off: state.off,
  makeCompatible: state.makeCompatible,
  prepareAdjustments: state.prepareAdjustments,
  setPerformance: function(factor, framerate) {
    if (factor == null) {
      factor = downsampleFactor;
    }
    if (framerate == null) {
      framerate = frameRate;
    }
    downsampleFactor = factor;
    frameRate = framerate;
    throttleDelay = 1000 / frameRate;
    return renderFullDelay = throttleDelay + 200;
  },
  getThumbnail: function() {
    return thumbnail;
  },
  render: function(showOriginal) {
    if (showOriginal) {
      return renderer.renderCached(true);
    } else {
      return renderer.render();
    }
  },
  renderScreen: renderer.renderCached,
  updateCompositor: function() {
    return compositor.update();
  },
  renderTextureMaps: function() {
    return renderer.renderTextureMaps();
  },
  renderCacheTexture: function() {
    return renderer.renderCacheTexture();
  },
  update: function() {
    update();
    renderer.renderCacheTexture();
    return state.trigger("history", state.getHistory(), state.getIndex());
  },
  readPixels: function(x, y, width, height, texture) {
    var fb, pixels;
    pixels = new Uint8Array(width * height * 4);
    if (texture) {
      y = texture.height - y;
      fb = texture.getFramebuffer();
      gl.context.bindFramebuffer(gl.context.FRAMEBUFFER, fb);
      gl.context.framebufferTexture2D(gl.context.FRAMEBUFFER, gl.context.COLOR_ATTACHMENT0, gl.context.TEXTURE_2D, texture.id, 0);
      gl.context.readPixels(x, y, width, height, gl.context.RGBA, gl.context.UNSIGNED_BYTE, pixels);
      gl.context.bindFramebuffer(gl.context.FRAMEBUFFER, null);
    } else {
      y = gl.canvas.height - y;
      gl.context.readPixels(x, y, width, height, gl.context.RGBA, gl.context.UNSIGNED_BYTE, pixels);
    }
    return pixels;
  },
  getZoom: function() {
    return gl.zoom;
  },
  getMinZoom: compositor.getMinZoom,
  getPosition: function() {
    return [gl.offset[0], gl.offset[1]];
  },
  getCenter: function() {
    return compositor.getCenter();
  },
  setCompositor: function(props, options) {
    var completed, duration, shouldRender, step;
    if (options == null) {
      options = {};
    }
    duration = options.duration, completed = options.completed, step = options.step, shouldRender = options.shouldRender;
    return compositor.set(props, duration, completed, step, shouldRender);
  },
  set: function(props, duration, completed, step) {
    var done;
    done = function() {
      update(false, false);
      return typeof completed === "function" ? completed() : void 0;
    };
    return compositor.set(props, duration, done, step);
  },
  cropMode: function(val) {
    gl.cropMode = !!val;
    compositor.setOffset(0, 0);
    compositor.setZoom(0);
    return renderer.renderCacheTexture();
  },
  rotateCrop: function(angle, crop) {
    return compositor.rotateCrop(angle, crop);
  },
  setState: function(settings, render) {
    if (render == null) {
      render = true;
    }
    state.setPending(true);
    return setState(settings, render);
  },
  saveState: function(message, changed, render) {
    if (render == null) {
      render = true;
    }
    return state.saveState(message, changed, function() {
      if (render) {
        renderer.renderCacheTexture();
      }
      state.trigger("history", state.getHistory(), state.getIndex());
      return trackEvent('history', 'change', message);
    });
  },
  getState: function(clone) {
    if (clone) {
      return utils.clone(state.currentState);
    } else {
      return state.currentState;
    }
  },
  getDefaultState: function(clone) {
    if (clone) {
      return utils.clone(state.defaults);
    } else {
      return state.defaults;
    }
  },
  getDefaultLocalState: function(clone) {
    if (clone) {
      return utils.clone(state.localDefaults);
    } else {
      return state.localDefaults;
    }
  },
  setDisabled: function(settings) {
    state.setDisabled(settings);
    return update();
  },
  getDisabled: function() {
    return state.getDisabled();
  },
  enableAll: function() {
    state.enableAll();
    return update(false);
  },
  disableAll: function() {
    state.disableAll();
    return update(false);
  },
  restore: function() {
    state.set(state.defaults);
    return Filtr.setHistory([]);
  },
  undo: function() {
    state.undo();
    update();
    renderer.renderCacheTexture();
    return state.trigger("history", state.getHistory(), state.getIndex());
  },
  redo: function() {
    state.redo();
    update();
    renderer.renderCacheTexture();
    return state.trigger("history", state.getHistory(), state.getIndex());
  },
  getIndex: function() {
    return state.getIndex();
  },
  getHistory: function() {
    return state.getHistory();
  },
  setHistory: function(history) {
    state.setHistory(history);
    update();
    renderer.renderCacheTexture();
    return state.trigger("history", state.getHistory(), state.getIndex());
  },
  setHistoryAt: function(index) {
    var zoom;
    zoom = gl.zoom === compositor.getMinZoom() ? 0 : gl.zoom;
    state.setIndex(index);
    compositor.setZoom(zoom);
    update();
    renderer.renderCacheTexture();
    return state.trigger("history", state.getHistory(), state.getIndex());
  },
  getInfo: function() {
    var size;
    if (!gl.photoLoaded) {
      return {
        zoom: 0,
        megapixels: 0,
        cropWidth: 0,
        cropHeight: 0,
        width: 0,
        height: 0
      };
    }
    size = compositor.getImageSize();
    return {
      zoom: Math.round(gl.zoom * 1000) / 10,
      megapixels: Math.round(gl.texture.width * gl.texture.height / 10000) / 100,
      cropWidth: Math.round(gl.crop[2]),
      cropHeight: Math.round(gl.crop[3]),
      width: size[0],
      height: size[1]
    };
  },
  getImageSize: function() {
    return compositor.getImageSize();
  },
  getCanvasSize: function() {
    return compositor.getCanvasSize();
  },
  getMaxTextureSize: function() {
    return io.getMaxSize();
  },
  hasAlpha: function() {
    return gl.textureFormat === gl.context.RGBA;
  },
  "import": function(source, options) {
    var blob, url;
    if (options == null) {
      options = {};
    }
    options = utils.assign({
      done: function() {},
      error: function() {}
    }, options);
    if (typeof source === "string") {
      return io.loadURL(source, options);
    } else if (source instanceof Image) {
      return io.loadImage(source, options);
    } else if (source instanceof File || source instanceof Blob) {
      url = window.URL.createObjectURL(source);
      return io.loadURL(url, options);
    } else if (source instanceof ArrayBuffer || source.buffer instanceof ArrayBuffer) {
      if (options.width && options.height) {
        return io.loadData(source, options);
      } else {
        blob = new Blob([source]);
        url = window.URL.createObjectURL(blob);
        return io.loadURL(url, options);
      }
    }
  },
  "export": function(type, options) {
    var height, image, output, width;
    if (options == null) {
      options = {};
    }
    if (!gl.photoLoaded) {
      return;
    }
    type = type.toLowerCase();
    image = Filtr.getInfo();
    width = options.width || image.cropWidth;
    height = options.height || image.cropHeight;
    if (type === "jpeg" || type === "jpg") {
      output = io.exportJPEG(width, height, options);
    } else if (type === "png") {
      output = io.exportPNG(width, height, options);
    } else if (type === "tiff" || type === "tif") {
      output = io.exportTIFF(width, height, options);
    } else if (type === "data") {
      output = io.getOutput(width, height, options);
    }
    return output;
  },
  getBlob: function(data, type) {
    data = data instanceof Array ? data : [data];
    return new Blob(data, {
      type: type
    });
  },
  getFile: function(data, type, filename) {
    var blob;
    blob = data instanceof Blob ? data : Filtr.getBlob(data, type);
    return new File([blob], filename);
  },
  readAsArrayBuffer: function(blob, callback) {
    var fileReader;
    fileReader = new FileReader();
    fileReader.onload = function() {
      return callback(this.result);
    };
    return fileReader.readAsArrayBuffer(blob);
  },
  resize: function(width, height) {
    return renderer.resize(width, height);
  },
  getOrientationSettings: function(value) {
    return utils.getOrientationSettings(value);
  },
  getCropCoords: function(crop, size, zoom) {
    var center, height, position, width;
    if (crop == null) {
      crop = state.currentState.crop;
    }
    position = gl.offset;
    center = compositor.getCenter();
    zoom = zoom || gl.zoom;
    size = size || compositor.getImageSize();
    width = size[0] * zoom;
    height = size[1] * zoom;
    return [((gl.canvas.width - width) / 2 + width * crop[0] + (center[0] - position[0])) / compositor.pixelRatio, ((gl.canvas.height - height) / 2 + height * crop[1] + (center[1] - position[1])) / compositor.pixelRatio, (width * crop[2]) / compositor.pixelRatio, (height * crop[3]) / compositor.pixelRatio];
  },
  getCoordinates: function() {
    var b, h, h0, l, p0, p1, r, rotated, sign, t, w, w0, x0, x1, y0, y1;
    if (!gl.screenMatrix) {
      renderer.loadScreenMatrix();
    }
    rotated = !!gl.orientation;
    w = gl.canvas.width;
    h = gl.canvas.height;
    sign = rotated ? -1 : 1;
    p0 = gl.screenMatrix.transformPoint({
      x: -1,
      y: -1,
      z: 0
    });
    p1 = gl.screenMatrix.transformPoint({
      x: 1,
      y: 1,
      z: 0
    });
    l = p0.x * 0.5 + 0.5;
    t = -p1.y * sign * 0.5 + 0.5;
    r = p1.x * 0.5 + 0.5;
    b = -p0.y * sign * 0.5 + 0.5;
    x0 = w * l / compositor.pixelRatio;
    y0 = h * t / compositor.pixelRatio;
    x1 = w * r / compositor.pixelRatio;
    y1 = h * b / compositor.pixelRatio;
    if (rotated) {
      w0 = y0 - y1;
      h0 = x0 - x1;
      return [y0 - w0, x0 - h0, w0, h0];
    } else {
      return [x0, y0, x1 - x0, y1 - y0];
    }
  },
  getMouseCoords: function(x, y, world, matrix) {
    var coords, cx, cy, h, maxHeight, maxWidth, point, ref, ref1, w;
    if (matrix == null) {
      matrix = gl.matrixInv;
    }
    compositor.update();
    if (!gl.screenMatrix) {
      renderer.loadScreenMatrix();
    }
    maxWidth = window.innerWidth;
    maxHeight = window.innerHeight;
    if (gl.orientation) {
      ref = [maxHeight, maxWidth], maxWidth = ref[0], maxHeight = ref[1];
      ref1 = [window.innerHeight - y, x], x = ref1[0], y = ref1[1];
    }
    x *= gl.canvas.width / (window.innerWidth * compositor.pixelRatio);
    y *= gl.canvas.height / (window.innerHeight * compositor.pixelRatio);
    coords = Filtr.getCoordinates();
    cx = Math.max(coords[0], 0);
    cy = Math.max(coords[1], 0);
    w = Math.min(coords[2], maxWidth);
    h = Math.min(coords[3], maxHeight);
    x = (x - cx) / w;
    y = 1 - (y - cy) / h;
    point = {
      x: x * 2 - 1,
      y: y * 2 - 1,
      z: 0
    };
    coords = matrix.transformPoint(point);
    if (world) {
      return [coords.x, coords.y];
    } else {
      return [coords.x * 0.5 + 0.5, coords.y * 0.5 + 0.5];
    }
  },
  setWatermark: function(options) {
    var option, results;
    if (options == null) {
      options = {};
    }
    if (options.image) {
      gl.watermark = GL.Texture.fromImage(options.image);
      delete options.image;
    }
    results = [];
    for (option in options) {
      results.push(gl.watermarkOptions[option] = options[option]);
    }
    return results;
  },
  getWatermark: function() {
    return utils.clone(gl.watermarkOptions);
  },
  unload: function() {
    io.unloadImage();
    return renderer.clear();
  }
};

module.exports = Filtr;


},{"./brush.coffee":363,"./compositor.coffee":364,"./context.coffee":365,"./filters.coffee":367,"./histogram.coffee":368,"./io.coffee":369,"./renderer.coffee":373,"./spline.coffee":375,"./state.coffee":376,"./text.coffee":377,"./utils.coffee":378}],363:[function(require,module,exports){
var assign, brush, clear, compositePaintTexture, context, coordLocation, drawLines, filters, gl, lineShader, lines, linesBuffer, paintLines, paintStroke, prevPoint, renderPaintTexture, renderer, resizeTexture, setBrush, setPoint, shaders, startPaint, state, swapTextures, texHeight, texWidth, texel, utils;

context = require("./context.coffee");

filters = require("./filters.coffee");

shaders = require("./shaders.js");

renderer = require("./renderer.coffee");

utils = require("./utils.coffee");

state = require("./state.coffee");

assign = utils.assign;

resizeTexture = renderer.resizeTexture, clear = renderer.clear;

gl = context.context;

brush = {
  radius: 0.3,
  blend: 1.0,
  opacity: 0.5,
  channel: [1.0, 0.0, 0.0, 0.0]
};

lineShader = new GL.Shader(shaders.line_vertex, shaders.line);

lines = [];

linesBuffer = gl.createBuffer();

coordLocation = null;

prevPoint = null;

texel = [];

texWidth = 1;

texHeight = 1;

renderPaintTexture = function() {
  var brushes, i, j, len, len1, localAdjustments, mask, prevBrushes, stroke;
  localAdjustments = state.currentState.local_adjustments;
  brushes = [];
  prevBrushes = state.prevBrushes || [];
  if (localAdjustments.length) {
    for (i = 0, len = localAdjustments.length; i < len; i++) {
      mask = localAdjustments[i];
      if (mask.brush) {
        brushes = brushes.concat(mask.brush);
      }
    }
    if (brushes.length !== prevBrushes.length) {
      if (!utils.deepEqual(prevBrushes, brushes)) {
        logTime("render brush map");
        prevBrushes = brushes;
        context.paintTexture.drawTo(function() {
          return gl.clear(gl.COLOR_BUFFER_BIT);
        });
        for (j = 0, len1 = brushes.length; j < len1; j++) {
          stroke = brushes[j];
          paintStroke(stroke);
        }
        logTimeEnd("render brush map");
      }
    }
  }
  return state.prevBrushes = prevBrushes;
};

renderer.addRenderDependency(renderPaintTexture);

exports.paintStroke = paintStroke = function(stroke) {
  var cacheTexture, height, ref, width;
  cacheTexture = context.cacheTexture;
  context.cacheTexture = context.writableTexture;
  setBrush(stroke, stroke.points);
  startPaint();
  swapTextures();
  drawLines();
  ref = context.intermediateTexture, width = ref.width, height = ref.height;
  resizeTexture(context.paintTexture, width, height);
  compositePaintTexture();
  context.cacheTexture = cacheTexture;
  return gl.disable(gl.BLEND);
};

exports.setBrush = setBrush = function(options, points) {
  var height, ref, texScale, width;
  brush = assign(brush, options);
  lines = points || [];
  prevPoint = null;
  ref = context.paintTexture, width = ref.width, height = ref.height;
  texScale = 1.0 + brush.radius * 20.0;
  texWidth = Math.floor(width / texScale);
  texHeight = Math.floor(height / texScale);
  return texel = [1.0 / texWidth, 1.0 / texHeight];
};

exports.paint = function(point) {
  var delta, texScale;
  if (!prevPoint) {
    setPoint(point);
    return prevPoint = point;
  } else {
    texScale = 1.0 + brush.radius * 20.0;
    delta = [Math.abs(point[0] - prevPoint[0]) * texScale, Math.abs(point[1] - prevPoint[1]) * texScale];
    if (delta[0] >= texel[0] || delta[1] >= texel[1]) {
      setPoint(point);
      return prevPoint = point;
    }
  }
};

exports.endPaint = function() {
  var height, ref, width;
  gl.disable(gl.BLEND);
  if (lines.length < 4) {
    return null;
  }
  gl.loadIdentity();
  ref = context.intermediateTexture, width = ref.width, height = ref.height;
  resizeTexture(context.paintTexture, width, height);
  compositePaintTexture();
  return assign(brush, {
    points: lines
  });
};

startPaint = function() {
  resizeTexture(context.intermediateTexture, texWidth, texHeight);
  resizeTexture(context.cacheTexture, texWidth, texHeight);
  lineShader.uniforms({
    color: brush.channel,
    opacity: brush.opacity
  });
  return coordLocation = gl.getAttribLocation(lineShader.program, "coordinates");
};

paintLines = function(composite) {
  if (lines.length < 4) {
    return;
  } else if (lines.length === 4) {
    startPaint();
    swapTextures();
  }
  drawLines();
  if (composite) {
    return compositePaintTexture();
  }
};

drawLines = function() {
  gl.enable(gl.BLEND);
  gl.blendEquation(gl.FUNC_ADD);
  gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ZERO);
  gl.loadIdentity();
  gl.bindBuffer(gl.ARRAY_BUFFER, linesBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(lines), gl.STATIC_DRAW);
  gl.vertexAttribPointer(coordLocation, 2, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(coordLocation);
  gl.useProgram(lineShader.program);
  context.cacheTexture.drawTo(function() {
    gl.clearColor(0.0, 0.0, 0.0, 0.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    return gl.drawArrays(gl.LINE_STRIP, 0, lines.length / 2);
  });
  filters.blur_brush.set({
    delta: [0, texel[1]]
  });
  filters.blur_brush.render(context.cacheTexture, context.paintTexture);
  filters.blur_brush.set({
    delta: [texel[0], 0]
  });
  return filters.blur_brush.render(context.paintTexture, context.cacheTexture);
};

compositePaintTexture = function() {
  context.intermediateTexture.bind(1);
  filters.brush_composite.set({
    paintTexture: 1,
    blend: brush.blend
  });
  return filters.brush_composite.render(context.cacheTexture, context.paintTexture);
};

swapTextures = function() {
  var temp;
  temp = context.paintTexture;
  context.paintTexture = context.intermediateTexture;
  return context.intermediateTexture = temp;
};

setPoint = function(point) {
  lines.push(point[0] * 2 - 1);
  lines.push(point[1] * 2 - 1);
  return paintLines(true);
};


},{"./context.coffee":365,"./filters.coffee":367,"./renderer.coffee":373,"./shaders.js":374,"./state.coffee":376,"./utils.coffee":378}],364:[function(require,module,exports){
var flipX, flipXMatrix, flipY, flipYMatrix, getCanvasSize, getCenter, getFlip, getImageSize, getMinZoom, gl, needsUpdate, pixelRatio, renderer, resampleTextures, rotateVec4, set, setCrop, setMargins, setOffset, setProjectionMatrix, setRotation, setRotation90, setRotation90Matrix, setRotationMatrix, setScaleMatrix, setTranslationMatrix, setZoom, state, updateCompositeView, updateCropMatrix, updateMatrix, utils;

gl = require("./context.coffee");

renderer = require("./renderer.coffee");

utils = require("./utils.coffee");

state = require("./state.coffee");

needsUpdate = true;

flipYMatrix = GL.Matrix.rotate(180, 1, 0, 0);

flipXMatrix = GL.Matrix.rotate(180, 0, 1, 0);

flipY = function(flip) {
  needsUpdate = true;
  gl.flipY = !!flip;
  gl.screen.rotation[0] = gl.flipY * 180;
  return setRotationMatrix(-gl.rotation);
};

flipX = function(flip) {
  needsUpdate = true;
  gl.flipX = !!flip;
  gl.screen.rotation[1] = gl.flipX * 180;
  return setRotationMatrix(-gl.rotation);
};

exports.pixelRatio = pixelRatio = Math.min(window.devicePixelRatio, 2);

exports.set = set = function(props, duration, completed, step, render) {
  var rotateX, rotateY, rotateZ;
  if (!gl.photoLoaded) {
    gl.readyProps = props;
    return;
  }
  if (props.margins) {
    setMargins(props.margins);
  }
  if (props.position) {
    setOffset(props.position[0], props.position[1]);
  }
  if (props.rotation != null) {
    setRotation(props.rotation);
  }
  if (props.orientation != null) {
    gl.orientation = props.orientation;
  }
  if (props.zoom != null) {
    setZoom(props.zoom, props.origin);
  }
  rotateX = props.rotateX != null ? props.rotateX : gl.screen.rotation[0];
  rotateY = props.rotateY != null ? props.rotateY : gl.screen.rotation[1];
  rotateZ = props.rotateZ != null ? props.rotateZ : gl.screen.rotation[2];
  return renderer.setScreen({
    zoom: gl.zoom,
    orientation: gl.orientation,
    offset: gl.offset,
    center: getCenter(),
    rotation: [rotateX, rotateY, rotateZ],
    extra: props.extra
  }, duration, completed, step, render);
};

exports.getCanvasSize = getCanvasSize = function() {
  if ((gl.orientation / 90 >> 0) % 2) {
    return [gl.canvas.height, gl.canvas.width];
  } else {
    return [gl.canvas.width, gl.canvas.height];
  }
};

exports.getImageSize = getImageSize = function() {
  if (gl.rotate90 % 2) {
    return [gl.texture.height, gl.texture.width];
  } else {
    return [gl.texture.width, gl.texture.height];
  }
};

exports.getFlip = getFlip = function() {
  if (gl.rotate90 % 2) {
    return [gl.flipY, gl.flipX];
  } else {
    return [gl.flipX, gl.flipY];
  }
};

exports.setRotation = setRotation = function(angle) {
  var ch, cos, cw, h, sin, size, theta, w;
  needsUpdate = true;
  gl.rotation = angle;
  size = getImageSize();
  w = size[0] / 2;
  h = size[1] / 2;
  cw = w;
  ch = h;
  theta = gl.rotation * (Math.PI / 180);
  cos = Math.abs(Math.cos(theta));
  sin = Math.abs(Math.sin(theta));
  gl.cropScale = Math.min(1.0, w / (cw * cos + ch * sin), h / (cw * sin + ch * cos));
  return setRotationMatrix(-gl.rotation);
};

exports.setRotation90 = setRotation90 = function(times) {
  var size;
  gl.rotate90 = times;
  size = getImageSize();
  setRotation90Matrix(-gl.rotate90);
  setProjectionMatrix(size[0], size[1]);
  return setRotationMatrix(-gl.rotation);
};

exports.rotateCrop = rotateVec4 = function(angle, crop) {
  var bl, br, tl, tr, w, x, y, z;
  if (crop == null) {
    crop = state.currentState.crop;
  }
  x = crop[0] - 0.5;
  y = crop[1] - 0.5;
  z = x + crop[2];
  w = y + crop[3];
  tl = utils.rotateVector([x, y], angle);
  tr = utils.rotateVector([z, y], angle);
  br = utils.rotateVector([z, w], angle);
  bl = utils.rotateVector([x, w], angle);
  x = Math.min(tl[0], tr[0], br[0], bl[0]) + 0.5;
  y = Math.min(tl[1], tr[1], br[1], bl[1]) + 0.5;
  z = Math.max(tl[0], tr[0], br[0], bl[0]) + 0.5;
  w = Math.max(tl[1], tr[1], br[1], bl[1]) + 0.5;
  return [x, y, z - x, w - y];
};

exports.setCrop = setCrop = function(x, y, w, h) {
  needsUpdate = true;
  return gl.crop = [x, y, w, h];
};

exports.setOffset = setOffset = function(x, y) {
  needsUpdate = true;
  gl.offset[0] = x;
  return gl.offset[1] = y;
};

exports.setZoom = setZoom = function(zoom, origin) {
  var scale, x, y;
  needsUpdate = true;
  gl.prevZoom = gl.zoom;
  zoom = zoom || getMinZoom() || 1;
  zoom = utils.clamp(zoom, 0, 5);
  scale = gl.zoom / zoom;
  gl.zoom = zoom;
  if (origin) {
    x = (origin[0] + gl.offset[0]) / scale - origin[0];
    y = (origin[1] + gl.offset[1]) / scale - origin[1];
    return setOffset(x, y);
  }
};

exports.getMinZoom = getMinZoom = function(bySize, size) {
  var canvas, canvasHeight, canvasWidth, height, margin, margins, width;
  margins = gl.margins;
  size = size || getImageSize();
  canvas = getCanvasSize();
  margin = gl.margin;
  width = gl.cropMode || bySize ? size[0] : gl.crop[2];
  height = gl.cropMode || bySize ? size[1] : gl.crop[3];
  canvasWidth = canvas[0] - (margins[0] + margins[2]) * pixelRatio;
  canvasHeight = canvas[1] - (margins[1] + margins[3]) * pixelRatio;
  return Math.min(1, (canvasWidth - margin) / width, (canvasHeight - margin) / height);
};

exports.setMargins = setMargins = function(margins) {
  needsUpdate = true;
  return gl.margins = margins;
};

exports.getCenter = getCenter = function() {
  var margins;
  margins = gl.margins;
  return [(margins[0] - margins[2]) * pixelRatio / 2, (margins[1] - margins[3]) * pixelRatio / 2];
};

exports.setSize = setProjectionMatrix = function(w, h) {
  needsUpdate = true;
  gl.projectionMatrix = GL.Matrix.ortho(w / -2, w / 2, h / -2, h / 2, -9999, 9999);
  return gl.projectionMatrixInv = gl.projectionMatrix.inverse();
};

setScaleMatrix = function(x, y) {
  var h2, size, w2;
  size = getImageSize();
  w2 = size[0] / 2;
  h2 = size[1] / 2;
  x = w2 / x;
  y = h2 / y;
  return gl.scaleMatrix = GL.Matrix.scale(x, y, 1);
};

setTranslationMatrix = function(x, y) {
  var size;
  size = getImageSize();
  return gl.translationMatrix = GL.Matrix.translate(x / size[0] * 2, y / size[1] * 2, 0);
};

setRotationMatrix = function(z) {
  var flip;
  flip = getFlip();
  gl.rotationMatrix = GL.Matrix.rotate(z, 0, 0, 1);
  if (flip[0]) {
    gl.rotationMatrix = GL.Matrix.multiply(gl.rotationMatrix, flipXMatrix);
  }
  if (flip[1]) {
    return gl.rotationMatrix = GL.Matrix.multiply(gl.rotationMatrix, flipYMatrix);
  }
};

setRotation90Matrix = function(times) {
  gl.rotation90Matrix = GL.Matrix.rotate(times * 90, 0, 0, 1);
  return gl.rotation90MatrixInv = gl.rotation90Matrix.inverse();
};

updateMatrix = function() {
  GL.Matrix.identity(gl.matrix);
  gl.matrix = GL.Matrix.multiply(gl.matrix, gl.projectionMatrix);
  gl.matrix = GL.Matrix.multiply(gl.matrix, gl.scaleMatrix);
  gl.matrix = GL.Matrix.multiply(gl.matrix, gl.translationMatrix);
  gl.viewMatrix = gl.matrix;
  if (!gl.cropMode) {
    gl.matrix = GL.Matrix.multiply(gl.matrix, gl.projectionMatrix);
    gl.matrix = GL.Matrix.multiply(gl.matrix, gl.rotationMatrix);
    gl.matrix = GL.Matrix.multiply(gl.matrix, gl.projectionMatrixInv);
    gl.matrix = GL.Matrix.multiply(gl.matrix, gl.rotation90Matrix);
  }
  gl.matrixInv = GL.Matrix.inverse(gl.matrix);
  return gl.matrix;
};

updateCropMatrix = function(x, y, w, h) {
  GL.Matrix.identity(gl.cropMatrix);
  gl.cropMatrix = GL.Matrix.multiply(gl.cropMatrix, GL.Matrix.scale(w, h, 1));
  return gl.cropMatrix = GL.Matrix.multiply(gl.cropMatrix, GL.Matrix.translate(x, y, 0));
};


/*
This projects the viewable area of the image limited by the crop area
The output resolution is dynamic and can be less than but not greater than the viewport
Since the output can be smaller, the rendered area must composited on the screen
 */

exports.update = updateCompositeView = function(forceUpdate) {
  var ah, aw, ax, ay, canvas, center, ch, crop, cropScale, cw, cx, cy, h, h2, mh, mw, offsetX, offsetY, ox, oy, size, sx, sy, w, w2, x, y, zoom;
  if (!needsUpdate && !forceUpdate) {
    return;
  }
  size = getImageSize();
  canvas = getCanvasSize();
  crop = gl.crop;
  cropScale = gl.cropScale;
  if (gl.cropMode) {
    crop = [0, 0, size[0], size[1]];
    cropScale = 1;
  }
  w2 = size[0] / 2;
  h2 = size[1] / 2;
  crop = [(crop[0] - w2) * cropScale + w2, (crop[1] - h2) * cropScale + h2, crop[2] * cropScale, crop[3] * cropScale];
  zoom = gl.zoom / cropScale;
  center = getCenter();
  offsetX = gl.offset[0] - center[0];
  offsetY = center[1] + gl.offset[1];
  w = crop[2] * zoom;
  h = crop[3] * zoom;
  cx = (canvas[0] - w) >> 1;
  cy = (canvas[1] - h) >> 1;
  ox = cx - offsetX;
  oy = cy - offsetY;
  ox += canvas[0] % 2 / 2;
  oy += canvas[1] % 2 / 2;
  ax = ox + w - canvas[0];
  ay = oy + h - canvas[1];
  if (w < canvas[0]) {
    ax = Math.min(0, ox) + Math.max(0, ax);
  }
  if (h < canvas[1]) {
    ay = Math.min(0, oy) + Math.max(0, ay);
  }
  ax /= zoom;
  ay /= zoom;
  aw = Math.min(canvas[0] / zoom, crop[2]);
  ah = Math.min(canvas[1] / zoom, crop[3]);
  mw = (crop[2] - aw) >> 1;
  mh = (crop[3] - ah) >> 1;
  cw = (size[0] - crop[2]) / 2 - crop[0];
  ch = -(size[1] - crop[3]) / 2 + crop[1];
  x = mw - utils.clamp(ax, 0, crop[2] - aw) - cw;
  y = mh - utils.clamp(ay, 0, crop[3] - ah) - ch;
  sx = aw / size[0];
  sy = ah / size[1];
  gl.compositeResolution[0] = Math.min(canvas[0], w >> 0);
  gl.compositeResolution[1] = Math.min(canvas[1], h >> 0);
  gl.compositeCoords[0] = utils.clamp(offsetX, cx, -cx) - offsetX;
  gl.compositeCoords[1] = utils.clamp(offsetY, cy, -cy) - offsetY;
  gl.compositeCoords[2] = gl.compositeCoords[0] + gl.compositeResolution[0];
  gl.compositeCoords[3] = gl.compositeCoords[1] + gl.compositeResolution[1];
  setTranslationMatrix(-x, -y);
  setScaleMatrix(sx, sy);
  updateMatrix();
  x = -cw / crop[2] * 2;
  y = -ch / crop[3] * 2;
  sx = crop[2] / size[0];
  sy = crop[3] / size[1];
  updateCropMatrix(x, y, sx, sy);
  resampleTextures();
  gl.minZoom = getMinZoom();
  needsUpdate = false;
};

exports.resampleTextures = resampleTextures = function() {
  var height, width;
  if (gl.downsampleFactor > 1) {
    width = Math.floor(gl.compositeResolution[0] / gl.downsampleFactor / 32) * 32;
    height = Math.floor(gl.compositeResolution[1] / gl.downsampleFactor / 32) * 32;
  } else {
    width = gl.compositeResolution[0];
    height = gl.compositeResolution[1];
  }
  renderer.resizeTexture(gl.readableTexture, width, height);
  return renderer.resizeTexture(gl.writableTexture, width, height);
};

exports.updateForOutput = function(width, height, flipY, useCrop, offset) {
  var crop, h2, sign, size, sx, sy, w2, x, y;
  if (flipY == null) {
    flipY = true;
  }
  if (useCrop == null) {
    useCrop = true;
  }
  needsUpdate = true;
  if (useCrop) {
    size = getImageSize();
  } else {
    size = [gl.texture.width, gl.texture.height];
  }
  w2 = size[0] / 2;
  h2 = size[1] / 2;
  crop = gl.crop;
  if (offset) {
    crop = [crop[0] + offset[0], crop[1] + offset[1], Math.floor(crop[2] * offset[2]), Math.floor(crop[3] * offset[3])];
  }
  if (useCrop) {
    crop = [(crop[0] - w2) * gl.cropScale + w2, (crop[1] - h2) * gl.cropScale + h2, crop[2] * gl.cropScale, crop[3] * gl.cropScale];
  } else {
    crop = [0, 0, size[0], size[1]];
  }
  x = -(size[0] - crop[2]) / 2 + crop[0];
  y = (size[1] - crop[3]) / 2 - crop[1];
  sx = crop[2] / size[0];
  sy = crop[3] / size[1];
  sign = flipY ? -1 : 1;
  setTranslationMatrix(-x, -y);
  setScaleMatrix(sx, sy * sign);
  updateMatrix();
  renderer.resizeTexture(gl.readableTexture, width, height);
  renderer.resizeTexture(gl.writableTexture, width, height);
};

state.register("rotation", 0, setRotation);

state.register("rotate90", 0, setRotation90);

state.register("flip_y", 0, flipY);

state.register("flip_x", 0, flipX);

state.register("crop", [0, 0, 1, 1], function(crop) {
  var size;
  size = getImageSize();
  crop[0] = utils.clamp(crop[0], 0, 1);
  crop[1] = utils.clamp(crop[1], 0, 1);
  crop[2] = utils.clamp(crop[2], 0, 1 - crop[0]);
  crop[3] = utils.clamp(crop[3], 0, 1 - crop[1]);
  return setCrop(size[0] * crop[0] >> 0, size[1] * crop[1] >> 0, size[0] * crop[2] >> 0, size[1] * crop[3] >> 0);
});


},{"./context.coffee":365,"./renderer.coffee":373,"./state.coffee":376,"./utils.coffee":378}],365:[function(require,module,exports){
var context, contexts, createContext, deleteTexture, gl, maxBufferSize, maxTextureSize, popContext, pushContext, setContext;

gl = GL.create({
  depth: false,
  stencil: false,
  antialias: true,
  alpha: true,
  premultipliedAlpha: false,
  preserveDrawingBuffer: true,
  preferLowPowerToHighPerformance: false,
  failIfMajorPerformanceCaveat: false
});

maxTextureSize = gl.getParameter(gl['MAX_TEXTURE_SIZE']);

maxBufferSize = gl.getParameter(gl['MAX_RENDERBUFFER_SIZE']);

contexts = [];

context = {};

setContext = function(newContext) {
  var key, val;
  for (key in newContext) {
    val = newContext[key];
    context[key] = val;
  }
  return newContext;
};

pushContext = function() {
  var key, val;
  for (key in context) {
    val = context[key];
    contexts[0][key] = val;
  }
  contexts.unshift(createContext());
  return setContext(contexts[0]);
};

popContext = function() {
  var i, key, len, oldContext, ref, texture, val;
  oldContext = contexts.shift();
  ref = oldContext.textures;
  for (i = 0, len = ref.length; i < len; i++) {
    texture = ref[i];
    deleteTexture(texture.id);
  }
  for (key in oldContext) {
    val = oldContext[key];
    if (val instanceof GL.Texture) {
      deleteTexture(val.id);
    }
  }
  return setContext(contexts[0]);
};

deleteTexture = function(texture) {
  gl.bindTexture(gl.TEXTURE_2D, texture);
  return gl.deleteTexture(texture);
};

createContext = function() {
  var mesh;
  mesh = GL.Mesh.plane({
    coords: true,
    detail: 64,
    distortion: true
  });
  return {
    maxTextureSize: Math.min(maxBufferSize, maxTextureSize),
    context: gl,
    canvas: gl.canvas,
    mesh: mesh,
    distortionVertices: mesh.distortion,
    textures: [],
    texture: {},
    dehazeTexture: new GL.Texture(1, 1),
    denoiseTexture: new GL.Texture(1, 1),
    cacheTexture: new GL.Texture(1, 1),
    readableTexture: new GL.Texture(1, 1),
    writableTexture: new GL.Texture(1, 1),
    lensBlurTexture: new GL.Texture(1, 1),
    paintTexture: new GL.Texture(1, 1),
    intermediateTexture: new GL.Texture(1, 1),
    faceMask: new GL.Texture(1, 1),
    textLayers: [],
    matrix: new GL.Matrix(),
    matrixInv: new GL.Matrix(),
    perspectiveMatrix: new GL.Matrix(),
    projectionMatrix: new GL.Matrix(),
    projectionMatrixInv: new GL.Matrix(),
    translationMatrix: new GL.Matrix(),
    rotationMatrix: new GL.Matrix(),
    rotation90Matrix: new GL.Matrix(),
    rotation90MatrixInv: new GL.Matrix(),
    scaleMatrix: new GL.Matrix(),
    cropMatrix: new GL.Matrix(),
    zoom: 0,
    flipY: false,
    flipX: false,
    rotate90: 0,
    rotation: 0,
    orientation: 0,
    offset: [0, 0],
    crop: [0, 0, 0, 0],
    cropScale: 1,
    compositeResolution: [0, 0],
    compositeCoords: [0, 0, 0, 0],
    downsampleFactor: 1,
    margin: 0,
    margins: [0, 0, 0, 0],
    cropMode: false,
    backgroundColor: [30 / 255, 30 / 255, 30 / 255],
    thresholdAverage: [1, 1, 1],
    screen: {
      zoom: 0,
      offset: [0, 0],
      center: [0, 0],
      rotation: [0, 0, 0],
      orientation: 0
    },
    watermarkOptions: {
      scale: 0.5,
      opacity: 1,
      position: [1, 1],
      enabled: false
    },
    pushContext: pushContext,
    popContext: popContext
  };
};

pushContext();

module.exports = context;


},{}],366:[function(require,module,exports){
var canvas, compileFaces, compileMarkers, ctx, drawBounds, drawMask, drawPoints, filters, generateMask, gl, renderFaceMask, renderer, state, utils;

gl = require("./context.coffee");

renderer = require("./renderer.coffee");

filters = require("./filters.coffee");

utils = require("./utils.coffee");

state = require("./state.coffee");

state.register("faces", []);

renderFaceMask = function() {
  var faces, height, prevFaces, ref, width;
  faces = state.currentState.faces;
  prevFaces = state.prevFaces || [];
  if (faces && faces.length) {
    if (!utils.deepEqual(prevFaces, faces, true)) {
      logTime("generate face mask");
      prevFaces = faces;
      ref = gl.texture, width = ref.width, height = ref.height;
      generateMask(width, height, faces);
      logTimeEnd("generate face mask");
    }
  }
  return state.prevFaces = prevFaces;
};

renderer.addRenderDependency(renderFaceMask);

canvas = document.createElement('canvas');

ctx = canvas.getContext('2d');

drawMask = function(points, color, mirror) {
  var firstPoint, i, j, lastPoint, len1, len2, midPoint, point;
  ctx.fillStyle = color;
  lastPoint = points[points.length - 1];
  ctx.beginPath();
  ctx.moveTo(lastPoint[0], lastPoint[1]);
  for (i = 0, len1 = points.length; i < len1; i++) {
    point = points[i];
    ctx.lineTo(point[0], point[1]);
  }
  ctx.fill();
  if (mirror) {
    firstPoint = points[0];
    midPoint = [utils.lerp(0.5, firstPoint[0], lastPoint[0]), utils.lerp(0.5, firstPoint[1], lastPoint[1])];
    ctx.save();
    ctx.translate(midPoint[0], midPoint[1]);
    ctx.rotate(Math.PI);
    ctx.translate(-midPoint[0], -midPoint[1]);
    ctx.beginPath();
    ctx.moveTo(lastPoint[0], lastPoint[1]);
    for (j = 0, len2 = points.length; j < len2; j++) {
      point = points[j];
      ctx.lineTo(point[0], point[1]);
    }
    ctx.fill();
    return ctx.restore();
  }
};

drawBounds = function(bounds, color) {
  var h, h2, w, w2, x, y;
  ctx.fillStyle = color;
  x = bounds[0], y = bounds[1], w = bounds[2], h = bounds[3];
  w2 = w / 2;
  h2 = h / 2;
  ctx.ellipse(x + w2, y + h2, w2, h2, 0, 0, 2 * Math.PI);
  return ctx.fill();
};

drawPoints = function(points, color) {
  var i, len1, point, radius, results;
  if (color == null) {
    color = "white";
  }
  ctx.fillStyle = color;
  ctx.strokeStyle = "rgba(0,0,0,0.3)";
  radius = 6;
  results = [];
  for (i = 0, len1 = points.length; i < len1; i++) {
    point = points[i];
    ctx.beginPath();
    ctx.arc(point[0], point[1], radius, 0, Math.PI * 2);
    ctx.fill();
    results.push(ctx.stroke());
  }
  return results;
};

compileFaces = function(faces, width, height) {
  return faces.map(function(face) {
    var compiled, markers;
    compiled = {};
    if (face.markers) {
      markers = face.markers.map(function(markers) {
        return [markers[0] * width, markers[1] * height];
      });
      compiled.markers = compileMarkers(markers);
    }
    if (face.boundaries) {
      compiled.boundaries = [face.boundaries[0] * width, face.boundaries[1] * height, face.boundaries[2] * width, face.boundaries[3] * height];
    }
    return compiled;
  });
};

compileMarkers = function(markers) {
  var points;
  points = {
    face: markers.slice(0, 17),
    leftBrow: markers.slice(17, 22),
    rightBrow: markers.slice(22, 27),
    middleNose: markers.slice(27, 31),
    bottomNose: markers.slice(31, 36),
    leftEye: markers.slice(36, 42),
    rightEye: markers.slice(42, 48),
    upperLipTop: markers.slice(48, 55),
    upperLipBottom: markers.slice(60, 66),
    lowerLipBottom: markers.slice(54, 61),
    lowerLipTop: markers.slice(65, 69)
  };
  points.lips = points.upperLipTop.concat(points.lowerLipBottom);
  points.teeth = points.upperLipBottom.concat(points.lowerLipTop);
  return points;
};

generateMask = function(width, height, faces) {
  var averageColor, blurPixels, canvasHeight, canvasWidth, face, i, len, len1, mask, maskPixels, matrix, refSkinColor, scale, totals, weightSum;
  scale = Math.max(1024 / width, 1024 / height);
  canvasWidth = width * scale >> 0;
  canvasHeight = height * scale >> 0;
  canvas.width = canvasWidth;
  canvas.height = canvasHeight;
  faces = compileFaces(faces, canvasWidth, canvasHeight);
  for (i = 0, len1 = faces.length; i < len1; i++) {
    face = faces[i];
    if (face.markers) {
      drawMask(face.markers.face, "black", true);
      drawMask(face.markers.lips, "red");
      drawMask(face.markers.teeth, "green");
      drawMask(face.markers.leftEye, "blue");
      drawMask(face.markers.rightEye, "blue");
    } else if (face.boundaries) {
      drawBounds(face.boundaries, "black");
    }
  }
  matrix = gl.matrix;
  gl.matrix = new GL.Matrix();
  gl.context.loadIdentity();
  renderer.resizeTexture(gl.writableTexture, canvas.width, canvas.height);
  filters.basic.render(gl.blurTexture, gl.writableTexture);
  blurPixels = renderer.readTexture(gl.writableTexture);
  maskPixels = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
  gl.matrix = matrix;
  len = 0;
  totals = [0, 0, 0];
  weightSum = 0;
  refSkinColor = [0.78, 0.57, 0.44];
  for (var i = 0; i < maskPixels.length; i += 4) {
    if (maskPixels[i+3] === 255) {
      magnitudeColor = Math.sqrt(blurPixels[i]*blurPixels[i]+blurPixels[i+1]*blurPixels[i+1]+blurPixels[i+2]*blurPixels[i+2])
      weight = Math.pow(blurPixels[i] / magnitudeColor - refSkinColor[0], 2) 
      weight += Math.pow(blurPixels[i+1] / magnitudeColor - refSkinColor[1], 2)
      weight += Math.pow(blurPixels[i+2] / magnitudeColor - refSkinColor[2], 2)
      weight = Math.exp(-2.0 * weight)
      totals[0] += blurPixels[i] * weight
      totals[1] += blurPixels[i+1] * weight
      totals[2] += blurPixels[i+2] * weight
      len++
      weightSum += weight
    }
  };
  averageColor = [totals[0] / weightSum / 255, totals[1] / weightSum / 255, totals[2] / weightSum / 255];
  mask = GL.Texture.fromData(new Uint8Array(maskPixels.buffer), canvas.width, canvas.height);
  renderer.blurMask(mask);
  blurPixels = null;
  maskPixels = null;
  canvas.width = 0;
  canvas.height = 0;
  gl.faceMask = mask;
  return gl.skinColor = averageColor;
};


},{"./context.coffee":365,"./filters.coffee":367,"./renderer.coffee":373,"./state.coffee":376,"./utils.coffee":378}],367:[function(require,module,exports){
var CurvesFilter, Filter, HSLFilter, VignetteFilter, Watermark, compositor, gl, set, shaders, spline, state, utils,
  bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
  extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
  hasProp = {}.hasOwnProperty;

gl = require("./context.coffee");

compositor = require("./compositor.coffee");

shaders = require("./shaders.js");

state = require("./state.coffee");

spline = require("./spline.coffee");

utils = require("./utils.coffee");

state.register("distortion_mesh", null, function(mesh) {
  var i, index, j, len, ref, vertex, vertices;
  if (mesh) {
    vertices = [];
    ref = gl.mesh.distortion;
    for (i = j = 0, len = ref.length; j < len; i = ++j) {
      vertex = ref[i];
      index = i * 2;
      vertices[i] = [mesh[index], mesh[index + 1]];
    }
    gl.mesh.distortion = vertices;
  } else {
    gl.mesh.distortion = gl.distortionVertices;
  }
  return gl.mesh.compile();
});

set = function(uniforms) {
  var base, results, uniform, value;
  results = [];
  for (uniform in uniforms) {
    value = uniforms[uniform];
    results.push(typeof (base = state.map)[uniform] === "function" ? base[uniform](value) : void 0);
  }
  return results;
};

Filter = (function() {
  function Filter(props1) {
    var ref, setting, value, vertex;
    this.props = props1;
    this.draw = bind(this.draw, this);
    this.set = bind(this.set, this);
    vertex = this.props.vertex || "vertex";
    this.mesh = this.props.mesh || gl.mesh;
    this.shader = new GL.Shader(shaders[vertex], shaders[this.props.name]);
    this.settings = this.props.uniforms;
    this.set(this.settings);
    ref = this.props.uniforms;
    for (setting in ref) {
      value = ref[setting];
      state.register(setting, value, (function(_this) {
        return function(setting) {
          return function(val) {
            var obj;
            obj = {};
            obj[setting] = val;
            _this.settings[setting] = val;
            return _this.set(obj);
          };
        };
      })(this)(setting), this);
    }
    exports[this.props.name] = this;
  }

  Filter.prototype.set = function(uniforms) {
    return this.shader.uniforms(uniforms);
  };

  Filter.prototype.draw = function(readTexture, writeTexture) {
    if (readTexture && typeof readTexture.bind === "function") {
      readTexture.bind(0);
    }
    this.props.render.call(this, readTexture, writeTexture);
    return this.shader.draw(this.mesh);
  };

  Filter.prototype.render = function(readTexture, writeTexture) {
    var draw;
    draw = this.draw;
    if (writeTexture) {
      return writeTexture.drawTo(function() {
        return draw(readTexture, writeTexture);
      });
    } else {
      return draw(readTexture, writeTexture);
    }
  };

  Filter.prototype.shouldRender = function() {
    var ref, render, setting, value;
    render = false;
    ref = this.props.uniforms;
    for (setting in ref) {
      value = ref[setting];
      if (state.defaults[setting] !== this.settings[setting]) {
        render = true;
      }
    }
    return render;
  };

  return Filter;

})();


/*
Add filters
 */

new Filter({
  name: "fill",
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "layer",
  vertex: "layer_vertex",
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "distortion_multitexture",
  vertex: "fringing_vertex",
  render: function() {
    var loc;
    gl.textures[0].bind(0);
    gl.textures[1].bind(1);
    gl.textures[2].bind(2);
    gl.textures[3].bind(3);
    gl.lensBlurTexture.bind(4);
    this.set({
      distortion_amount: state.currentState.distortion_amount,
      fringing: state.currentState.fringing,
      distortion_horizontal: state.currentState.distortion_horizontal,
      distortion_vertical: state.currentState.distortion_vertical,
      blur_opacity: state.currentState.blur_opacity,
      blurTexture: 4,
      imgSize: [gl.texture.width, gl.texture.height]
    });
    loc = this.shader.getLocation("textures[0]");
    return gl.context.uniform1iv(loc, [2, 3, 0, 1]);
  }
});

new Filter({
  name: "distortion",
  vertex: "fringing_vertex",
  uniforms: {
    distortion_amount: 0,
    fringing: 0,
    distortion_horizontal: 0,
    distortion_vertical: 0,
    blur_opacity: 1
  },
  render: function() {
    gl.lensBlurTexture.bind(1);
    return this.set({
      texture: 0,
      blurTexture: 1,
      imgSize: [gl.texture.width, gl.texture.height]
    });
  }
});

new Filter({
  name: "multitexture",
  render: function() {
    var loc;
    gl.textures[0].bind(0);
    gl.textures[1].bind(1);
    gl.textures[2].bind(2);
    gl.textures[3].bind(3);
    this.set();
    loc = this.shader.getLocation("textures[0]");
    return gl.context.uniform1iv(loc, [2, 3, 0, 1]);
  }
});

new Filter({
  name: "out",
  vertex: "distortion_vertex",
  render: function() {
    gl.texture.bind(1);
    return this.set({
      texture: 0,
      original: 1,
      viewMatrix: gl.matrix,
      distortion_amount: state.currentState.distortion_amount,
      distortion_horizontal: state.currentState.distortion_horizontal,
      distortion_vertical: state.currentState.distortion_vertical,
      imgSize: [gl.texture.width, gl.texture.height]
    });
  }
});

new Filter({
  name: "out_multitexture",
  vertex: "distortion_vertex",
  render: function(readTexture) {
    var loc;
    gl.textures[0].bind(0);
    gl.textures[1].bind(1);
    gl.textures[2].bind(2);
    gl.textures[3].bind(3);
    readTexture.bind(4);
    this.set({
      texture: 4,
      viewMatrix: gl.matrix,
      distortion_amount: state.currentState.distortion_amount,
      distortion_horizontal: state.currentState.distortion_horizontal,
      distortion_vertical: state.currentState.distortion_vertical,
      imgSize: [gl.texture.width, gl.texture.height]
    });
    loc = this.shader.getLocation("textures[0]");
    return gl.context.uniform1iv(loc, [2, 3, 0, 1]);
  }
});

new Filter({
  name: "brush_composite",
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

Watermark = (function(superClass) {
  extend(Watermark, superClass);

  function Watermark(props) {
    this.settings = props.uniforms;
    Watermark.__super__.constructor.call(this, props);
  }

  Watermark.prototype.set = function(settings) {
    var key, results, setting;
    results = [];
    for (key in settings) {
      setting = settings[key];
      results.push(this.settings[key] = setting);
    }
    return results;
  };

  Watermark.prototype.setMatrix = function() {
    var fit, h1, h2, ox, oy, pos, r1, r2, ref, scale, sx, sy, tx, ty, w1, w2;
    w1 = gl.watermark.width;
    h1 = gl.watermark.height;
    ref = compositor.getImageSize(), w2 = ref[0], h2 = ref[1];
    r1 = w1 / w2;
    r2 = h1 / h2;
    pos = gl.watermarkOptions.position;
    scale = gl.watermarkOptions.scale;
    fit = Math.max(r1, r2);
    sx = r1 / fit * scale;
    sy = r2 / fit * scale;
    ox = pos[0];
    oy = 1.0 - (pos[1] + 1.0);
    tx = ox - sx * ox;
    ty = oy - sy * oy;
    this.matrix = gl.viewMatrix;
    this.matrix = GL.Matrix.multiply(this.matrix, GL.Matrix.scale(Filtr._.cropScale, Filtr._.cropScale, 1));
    this.matrix = GL.Matrix.multiply(this.matrix, GL.Matrix.translate(tx, ty, 1));
    return this.matrix = GL.Matrix.multiply(this.matrix, GL.Matrix.scale(sx, sy, 1));
  };

  return Watermark;

})(Filter);

new Watermark({
  name: "watermark",
  render: function() {
    if (gl.watermark && gl.watermarkOptions.enabled) {
      gl.watermark.bind(0);
      this.setMatrix();
      gl.context.loadMatrix(this.matrix);
      return this.shader.uniforms({
        texture: 0,
        opacity: gl.watermarkOptions.opacity
      });
    }
  }
});

new Filter({
  name: "denoise_nlm1",
  render: function() {
    return this.set({
      texture: 0,
      textureResolution: [gl.texture.width, gl.texture.height]
    });
  }
});

new Filter({
  name: "denoise_nlm2",
  render: function() {
    return this.set({
      texture: 0,
      textureResolution: [gl.texture.width, gl.texture.height]
    });
  }
});

new Filter({
  name: "denoise",
  vertex: "distortion_vertex",
  uniforms: {
    color_denoise: 0,
    luminance_denoise: 0
  },
  render: function() {
    gl.denoiseTexture.bind(1);
    gl.lensBlurTexture.bind(2);
    gl.dehazeTexture.bind(3);
    return this.set({
      texture: 0,
      denoiseMap: 1,
      blurMap: 2,
      dehazeMap: 3,
      viewMatrix: gl.matrix,
      distortion_amount: state.currentState.distortion_amount,
      distortion_horizontal: state.currentState.distortion_horizontal,
      distortion_vertical: state.currentState.distortion_vertical,
      imgSize: [gl.texture.width, gl.texture.height]
    });
  }
});

new Filter({
  name: "dehaze_map_min1",
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "dehaze_map_min2",
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "dehaze_map_2",
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "dehaze",
  vertex: "distortion_vertex",
  uniforms: {
    dehaze: 0
  },
  render: function() {
    gl.dehazeTexture.bind(1);
    gl.lensBlurTexture.bind(2);
    return this.set({
      texture: 0,
      dehazeMap: 1,
      blurMap: 2,
      average: gl.thresholdAverage,
      viewMatrix: gl.matrix,
      distortion_amount: state.currentState.distortion_amount,
      distortion_horizontal: state.currentState.distortion_horizontal,
      distortion_vertical: state.currentState.distortion_vertical,
      imgSize: [gl.texture.width, gl.texture.height]
    });
  }
});

new Filter({
  name: "mask_brush",
  vertex: "composite_vertex",
  render: function() {
    gl.paintTexture.bind(1);
    return this.set({
      texture: 0,
      brushTexture: 1,
      viewMatrix: gl.matrix
    });
  }
});

new Filter({
  name: "mask_color",
  vertex: "distortion_vertex",
  render: function() {
    gl.blurTexture.bind(1);
    return this.set({
      texture: 0,
      colorMap: 1,
      viewMatrix: gl.matrix,
      imgSize: [gl.texture.width, gl.texture.height],
      distortion_amount: state.currentState.distortion_amount,
      distortion_horizontal: state.currentState.distortion_horizontal,
      distortion_vertical: state.currentState.distortion_vertical
    });
  }
});

new Filter({
  name: "mask_radial",
  vertex: "composite_vertex",
  render: function() {
    return this.set({
      texture: 0,
      viewMatrix: gl.matrix,
      imgSize: [gl.texture.width, gl.texture.height]
    });
  }
});

new Filter({
  name: "mask_gradient",
  vertex: "composite_vertex",
  render: function() {
    return this.set({
      texture: 0,
      viewMatrix: gl.matrix,
      imgSize: [gl.texture.width, gl.texture.height]
    });
  }
});

new Filter({
  name: "basic",
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "strip_alpha",
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "screen",
  vertex: "screen_vertex",
  uniforms: {
    grid_size: [0, 0]
  },
  render: function(readTexture) {
    gl.cacheTexture.bind(1);
    return this.set({
      texture: 0,
      cacheTexture: 1,
      showTexture: !!readTexture,
      cropMatrix: gl.cropMatrix,
      viewMatrix: gl.viewMatrix,
      img_size: [gl.crop[2], gl.crop[3]],
      zoom: gl.zoom
    });
  }
});

new Filter({
  name: "blur",
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "blur_mask",
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "blur_brush",
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "diffuse",
  vertex: "distortion_vertex",
  uniforms: {
    diffuse: 0
  },
  render: function() {
    gl.blurTexture.bind(1);
    gl.lensBlurTexture.bind(2);
    return this.set({
      texture: 0,
      smoothMap: 1,
      blurMap: 2,
      viewMatrix: gl.matrix,
      distortion_amount: state.currentState.distortion_amount,
      distortion_horizontal: state.currentState.distortion_horizontal,
      distortion_vertical: state.currentState.distortion_vertical,
      imgSize: [gl.texture.width, gl.texture.height]
    });
  }
});

new Filter({
  name: "temperature",
  uniforms: {
    temperature: 0,
    tint: 0
  },
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "exposure",
  uniforms: {
    exposure: 0,
    gamma: 0
  },
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "contrast",
  uniforms: {
    contrast: 0
  },
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "shadows_highlights",
  vertex: "distortion_vertex",
  uniforms: {
    highlights: 0,
    shadows: 0
  },
  render: function() {
    gl.blurTexture.bind(1);
    gl.lensBlurTexture.bind(2);
    return this.set({
      texture: 0,
      smoothMap: 1,
      blurMap: 2,
      distortion_amount: state.currentState.distortion_amount,
      distortion_horizontal: state.currentState.distortion_horizontal,
      distortion_vertical: state.currentState.distortion_vertical,
      viewMatrix: gl.matrix,
      imgSize: [gl.texture.width, gl.texture.height]
    });
  }
});

new Filter({
  name: "whites_blacks",
  uniforms: {
    whites: 0,
    blacks: 0
  },
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "clarity",
  vertex: "distortion_vertex",
  uniforms: {
    clarity: 0
  },
  render: function() {
    gl.blurTexture.bind(1);
    gl.lensBlurTexture.bind(2);
    return this.set({
      texture: 0,
      smoothMap: 1,
      blurMap: 2,
      distortion_amount: state.currentState.distortion_amount,
      distortion_horizontal: state.currentState.distortion_horizontal,
      distortion_vertical: state.currentState.distortion_vertical,
      viewMatrix: gl.matrix,
      imgSize: [gl.texture.width, gl.texture.height]
    });
  }
});

new Filter({
  name: "face",
  vertex: "distortion_vertex",
  uniforms: {
    skin_smoothness: 0,
    skin_texture: 0,
    skin_tone: 0,
    skin_hue: 0,
    teeth_brightness: 0,
    teeth_whitening: 0,
    eyes_brightness: 0,
    eyes_clarity: 0,
    lips_brightness: 0,
    lips_saturation: 0
  },
  render: function() {
    gl.blurTexture.bind(1);
    gl.lensBlurTexture.bind(2);
    gl.faceMask.bind(3);
    return this.set({
      texture: 0,
      smoothMap: 1,
      blurMap: 2,
      faceMask: 3,
      skinColor: gl.skinColor,
      distortion_amount: state.currentState.distortion_amount,
      distortion_horizontal: state.currentState.distortion_horizontal,
      distortion_vertical: state.currentState.distortion_vertical,
      viewMatrix: gl.matrix,
      imgSize: [gl.texture.width, gl.texture.height]
    });
  }
});

new Filter({
  name: "saturation_vibrance",
  uniforms: {
    saturation: 0,
    vibrance: 0
  },
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

CurvesFilter = (function(superClass) {
  extend(CurvesFilter, superClass);

  function CurvesFilter(props) {
    this.data = new Uint8Array(256 * 4);
    this.map = new GL.Texture(256, 1, {
      filter: gl.context.NEAREST
    });
    this.settings = props.uniforms;
    CurvesFilter.__super__.constructor.call(this, props);
  }

  CurvesFilter.prototype.transformPoints = function(points) {
    var j, len, point, res;
    res = [[], []];
    for (j = 0, len = points.length; j < len; j++) {
      point = points[j];
      res[0].push(point[0]);
      res[1].push(point[1]);
    }
    return res;
  };

  CurvesFilter.prototype.interpolate = function(spline, i, p) {
    var v;
    v = Math.round(utils.clamp(spline.interpolate(i), 0, 255));
    if (isNaN(v)) {
      return p;
    } else {
      return v;
    }
  };

  CurvesFilter.prototype.set = function(settings) {
    var allPoints, allSpline, bluePoints, blueSpline, g, greenPoints, greenSpline, i, j, key, p1, p2, p3, p4, redPoints, redSpline, setting;
    for (key in settings) {
      setting = settings[key];
      this.settings[key] = setting;
    }
    redPoints = this.transformPoints(this.settings.curves_red);
    greenPoints = this.transformPoints(this.settings.curves_green);
    bluePoints = this.transformPoints(this.settings.curves_blue);
    allPoints = this.transformPoints(this.settings.curves_all);
    redSpline = new spline.cubic(redPoints[0], redPoints[1]);
    greenSpline = new spline.cubic(greenPoints[0], greenPoints[1]);
    blueSpline = new spline.cubic(bluePoints[0], bluePoints[1]);
    allSpline = new spline.cubic(allPoints[0], allPoints[1]);
    p1 = redPoints[1][0];
    p2 = greenPoints[1][0];
    p3 = bluePoints[1][0];
    p4 = allPoints[1][0];
    for (i = j = 0; j <= 255; i = ++j) {
      this.data[i * 4] = p1 = this.interpolate(redSpline, i, p1);
      this.data[i * 4 + 1] = p2 = this.interpolate(greenSpline, i, p2);
      this.data[i * 4 + 2] = p3 = this.interpolate(blueSpline, i, p3);
      this.data[i * 4 + 3] = p4 = this.interpolate(allSpline, i, p4);
    }
    g = gl.context;
    g.bindTexture(g.TEXTURE_2D, this.map.id);
    g.pixelStorei(g.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
    return g.texImage2D(g.TEXTURE_2D, 0, g.RGBA, 256, 1, 0, g.RGBA, g.UNSIGNED_BYTE, this.data);
  };

  return CurvesFilter;

})(Filter);

new CurvesFilter({
  name: "curves",
  uniforms: {
    curves_red: [[0, 0], [255, 255]],
    curves_green: [[0, 0], [255, 255]],
    curves_blue: [[0, 0], [255, 255]],
    curves_all: [[0, 0], [255, 255]]
  },
  render: function() {
    this.map.bind(1);
    return this.shader.uniforms({
      texture: 0,
      map: 1
    });
  }
});

HSLFilter = (function(superClass) {
  extend(HSLFilter, superClass);

  function HSLFilter(props) {
    this.data = new Uint8Array(360 * 3);
    this.map = new GL.Texture(360, 1, {
      format: gl.context.RGB
    });
    this.settings = props.uniforms;
    HSLFilter.__super__.constructor.call(this, props);
  }

  HSLFilter.prototype.getPoints = function(channel, settings) {
    return [[0, 30, 60, 90, 180, 240, 270, 300, 360], [(settings[channel + "_red"] / 2 + 0.5) * 255, (settings[channel + "_orange"] / 2 + 0.5) * 255, (settings[channel + "_yellow"] / 2 + 0.5) * 255, (settings[channel + "_green"] / 2 + 0.5) * 255, (settings[channel + "_aqua"] / 2 + 0.5) * 255, (settings[channel + "_blue"] / 2 + 0.5) * 255, (settings[channel + "_purple"] / 2 + 0.5) * 255, (settings[channel + "_magenta"] / 2 + 0.5) * 255, (settings[channel + "_red"] / 2 + 0.5) * 255]];
  };

  HSLFilter.prototype.set = function(settings) {
    var g, huePoints, hueSpline, i, j, key, lumPoints, lumSpline, p1, p2, p3, satPoints, satSpline, setting;
    for (key in settings) {
      setting = settings[key];
      this.settings[key] = setting;
    }
    huePoints = this.getPoints("hue", this.settings);
    satPoints = this.getPoints("saturation", this.settings);
    lumPoints = this.getPoints("luminance", this.settings);
    hueSpline = new spline.monotonic(huePoints[0], huePoints[1]);
    satSpline = new spline.monotonic(satPoints[0], satPoints[1]);
    lumSpline = new spline.monotonic(lumPoints[0], lumPoints[1]);
    p1 = p2 = p3 = 0;
    for (i = j = 0; j <= 359; i = ++j) {
      this.data[i * 3] = p1 = Math.floor(utils.clamp(hueSpline.interpolate(i), 0, 255)) || p1;
      this.data[i * 3 + 1] = p2 = Math.floor(utils.clamp(satSpline.interpolate(i), 0, 255)) || p2;
      this.data[i * 3 + 2] = p3 = Math.floor(utils.clamp(lumSpline.interpolate(i), 0, 255)) || p3;
    }
    g = gl.context;
    g.bindTexture(g.TEXTURE_2D, this.map.id);
    return g.texImage2D(g.TEXTURE_2D, 0, g.RGB, 360, 1, 0, g.RGB, g.UNSIGNED_BYTE, this.data);
  };

  return HSLFilter;

})(Filter);

new HSLFilter({
  name: "hsl",
  uniforms: {
    hue_red: 0,
    hue_orange: 0,
    hue_yellow: 0,
    hue_green: 0,
    hue_aqua: 0,
    hue_blue: 0,
    hue_purple: 0,
    hue_magenta: 0,
    saturation_red: 0,
    saturation_orange: 0,
    saturation_yellow: 0,
    saturation_green: 0,
    saturation_aqua: 0,
    saturation_blue: 0,
    saturation_purple: 0,
    saturation_magenta: 0,
    luminance_red: 0,
    luminance_orange: 0,
    luminance_yellow: 0,
    luminance_green: 0,
    luminance_aqua: 0,
    luminance_blue: 0,
    luminance_purple: 0,
    luminance_magenta: 0
  },
  render: function() {
    this.map.bind(1);
    return this.shader.uniforms({
      texture: 0,
      map: 1
    });
  }
});

new Filter({
  name: "split_tone",
  uniforms: {
    highlights_hue: 0,
    highlights_saturation: 0,
    shadows_hue: 0,
    shadows_saturation: 0,
    balance: 0
  },
  render: function() {
    return this.set({
      texture: 0
    });
  }
});

new Filter({
  name: "sharpen",
  vertex: "sharpen_vertex",
  uniforms: {
    sharpen: 0
  },
  render: function(texture) {
    return this.set({
      texture: 0,
      step: [1 / texture.width * gl.zoom, 1 / texture.height * gl.zoom]
    });
  }
});

new Filter({
  name: "grain",
  vertex: "composite_vertex",
  uniforms: {
    grain_amount: 0,
    grain_size: 0.25
  },
  render: function() {
    return this.set({
      texture: 0,
      scale: gl.zoom,
      imgSize: [gl.texture.width, gl.texture.height],
      viewMatrix: gl.matrix
    });
  }
});

new Filter({
  name: "mosaic",
  vertex: "composite_vertex",
  uniforms: {
    mosaic_size: 0
  },
  render: function() {
    gl.blurTexture.bind(1);
    return this.set({
      texture: 0,
      blurTexture: 1,
      viewMatrix: gl.matrix,
      imgSize: [gl.texture.width, gl.texture.height],
      distortion_amount: state.currentState.distortion_amount,
      distortion_horizontal: state.currentState.distortion_horizontal,
      distortion_vertical: state.currentState.distortion_vertical
    });
  }
});

VignetteFilter = (function(superClass) {
  extend(VignetteFilter, superClass);

  function VignetteFilter(props) {
    this.settings = props.uniforms;
    VignetteFilter.__super__.constructor.call(this, props);
  }

  VignetteFilter.prototype.set = function(settings) {
    var key, setting;
    for (key in settings) {
      setting = settings[key];
      this.settings[key] = setting;
    }
    return this.shader.uniforms({
      amount: this.settings.vignette_amount,
      spread: this.settings.vignette_feather,
      highlights: this.settings.vignette_highlights,
      vignette_roundness: this.settings.vignette_roundness,
      vignette_size: this.settings.vignette_size,
      vignette_exposure: this.settings.vignette_exposure,
      size: [1, 1],
      position: [0, 0]
    });
  };

  return VignetteFilter;

})(Filter);

new VignetteFilter({
  name: "vignette",
  vertex: "composite_vertex",
  uniforms: {
    vignette_amount: 0,
    vignette_feather: 0.5,
    vignette_highlights: 0.5,
    vignette_roundness: 0,
    vignette_size: 1.0,
    vignette_exposure: 0
  },
  render: function() {
    return this.shader.uniforms({
      texture: 0,
      crop: state.currentState.crop,
      rotationMatrix: gl.rotation90MatrixInv,
      viewMatrix: gl.matrix,
      imgSize: compositor.getImageSize()
    });
  }
});

exports.order = ["sharpen", "denoise", "mosaic", "face", "diffuse", "clarity", "dehaze", "whites_blacks", "shadows_highlights", "exposure", "temperature", "contrast", "saturation_vibrance", "curves", "hsl", "split_tone", "vignette", "grain"];

exports.set = set;

exports.create = function(props) {
  return new Filter(props);
};


},{"./compositor.coffee":364,"./context.coffee":365,"./shaders.js":374,"./spline.coffee":375,"./state.coffee":376,"./utils.coffee":378}],368:[function(require,module,exports){
var cache, canvas, ctx, gl, renderer, updateCache, updateHistogram, utils;

gl = require("./context.coffee");

utils = require("./utils.coffee");

renderer = require("./renderer.coffee");

canvas = document.createElement('canvas');

ctx = canvas.getContext('2d');

cache = {};

updateCache = function(maxWidth, maxHeight) {
  var height, pixels, scale, width;
  scale = Math.min((maxWidth || 300) / gl.crop[2], (maxHeight || 300) / gl.crop[3]);
  width = (gl.crop[2] * scale) >> 0;
  height = (gl.crop[3] * scale) >> 0;
  if (width === cache.width && height === cache.height) {
    pixels = cache.data;
  }
  return module.exports.cache = cache = renderer.renderOutput(width, height, null, null, null, pixels);
};

updateHistogram = function() {
  var b, b1, factor, g, g1, max, r, r1;
  factor = 2;
  canvas.width = 256 * factor;
  canvas.height = 104 * factor;
  r = new Uint16Array(256);
  g = new Uint16Array(256);
  b = new Uint16Array(256);
  r1 = new Uint16Array(256);
  g1 = new Uint16Array(256);
  b1 = new Uint16Array(256);
  max = 0;
  
  var colors, channels, j, i, i1, i2, i3, n;

  for (i = 0; i < cache.data.length; i += 4) {
      i1 = cache.data[i];
      i2 = cache.data[i+1];
      i3 = cache.data[i+2];

      // clip blacks and whites
      if (i1 > 0 && i1 < 255) r[i1]++;
      if (i2 > 0 && i2 < 255) g[i2]++;
      if (i3 > 0 && i3 < 255) b[i3]++;
  }

  // fill in gaps
  for (i = 1; i < 255; i++) {
      r1[i] = r[i] || r[i-1] || r[i+1];
      g1[i] = g[i] || g[i-1] || g[i+1];
      b1[i] = b[i] || b[i-1] || b[i+1];
  }

  // smooth histogram
  for (i = 1; i < 255; i++) {
      r[i] = (r1[i-1] + r1[i] + r1[i+1]) / 3;
      g[i] = (g1[i-1] + g1[i] + g1[i+1]) / 3;
      b[i] = (b1[i-1] + b1[i] + b1[i+1]) / 3;

      max = Math.max(max, r[i], g[i], b[i]);
  }

  max = Math.min(max, 1200)


  ctx.globalCompositeOperation = "lighter";
  ctx.lineWidth = 2;

  colors = ["360, 100%, 60%", "120, 100%, 60%", "230, 100%, 60%"];
  channels = [r, g, b];

  for (j = 0; j < channels.length; j++) {
    ctx.strokeStyle = "hsl(" + colors[j] + ")";
    ctx.fillStyle = "hsla(" + colors[j] + ", 0.5)";

    ctx.beginPath();
    ctx.moveTo(0, canvas.height);

    for (i = 0; i < 256; i++) {
      //n = Math.log(utils.lerp(channels[j][i] / max, 1, Math.E));
      n = channels[j][i] / max
      ctx.lineTo(i * factor, canvas.height - n * canvas.height);
    }

    ctx.closePath();

    // doing try and catch here, to avoid strange windows 10 bug
    try{
      ctx.stroke();
      ctx.fill();
    }catch(e){
      return;
    }
  }
  ;
};

module.exports = {
  canvas: canvas,
  cache: cache,
  updateCache: updateCache,
  update: updateHistogram
};


},{"./context.coffee":365,"./renderer.coffee":373,"./utils.coffee":378}],369:[function(require,module,exports){
var JPEG, PNG, TIFF, compositor, face, freeImage, freeTextures, getMaxSize, getOutput, getSubTexture, gl, loadImage, renderer, resetCompositor, resetState, state, textureCanvas, textureImage, utils;

gl = require("./context.coffee");

state = require("./state.coffee");

renderer = require("./renderer.coffee");

compositor = require("./compositor.coffee");

utils = require("./utils.coffee");

face = require("./face.coffee");

JPEG = require("./lib/jpeg_encoder.js");

PNG = require("./lib/png_encoder.js");

TIFF = require("./lib/tiff_encoder.js");

textureImage = new Image();

textureCanvas = document.createElement('canvas');

freeImage = function(image) {
  image.onerror = null;
  image.onload = null;
  window.URL.revokeObjectURL(image.src);
  textureImage.crossOrigin = null;
  image.src = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
  textureCanvas.width = 0;
  return textureCanvas.height = 0;
};

freeTextures = function() {
  var g, i, len, ref, tex;
  g = gl.context;
  if (gl.faceMask != null) {
    g.bindTexture(g.TEXTURE_2D, gl.faceMask.id);
    g.deleteTexture(gl.faceMask.id);
  }
  if (gl.texture != null) {
    g.bindTexture(g.TEXTURE_2D, gl.texture.id);
    g.deleteTexture(gl.texture.id);
  }
  if (gl.textures != null) {
    ref = gl.textures;
    for (i = 0, len = ref.length; i < len; i++) {
      tex = ref[i];
      g.bindTexture(g.TEXTURE_2D, tex.id);
      g.deleteTexture(tex.id);
    }
  }
  gl.texture = {};
  return gl.textures = [];
};

resetCompositor = function() {
  var size;
  size = compositor.getImageSize();
  compositor.setOffset(0, 0);
  compositor.setZoom(compositor.getMinZoom());
  compositor.setSize(size[0], size[1]);
  compositor.update();
  gl.screenMatrix = null;
  return gl.screen = {
    zoom: gl.zoom,
    center: compositor.getCenter(),
    offset: gl.offset.slice(0),
    rotation: [0, 0, 0],
    orientation: gl.orientation
  };
};

resetState = function(settings) {
  renderer.renderTextureMaps();
  if (settings instanceof Array) {
    state.setHistory(settings);
  } else {
    state.set(settings);
  }
  resetCompositor();
  if (!gl.photoLoaded) {
    gl.photoLoaded = true;
    if (gl.readyProps) {
      compositor.set(gl.readyProps);
    }
  }
  state.prevLocalAdjustments = [];
  state.prevBrushes = [];
  state.prevText = [];
  return renderer.renderBlurTexture();
};

getSubTexture = function(image, x, y, width, height) {
  var ctx;
  ctx = textureCanvas.getContext('2d');
  ctx.drawImage(image, x, y, width, height, 0, 0, textureCanvas.width, textureCanvas.height);
  return GL.Texture.fromImage(textureCanvas, {
    magFilter: gl.context.LINEAR,
    format: gl.textureFormat
  });
};

exports.getMaxSize = getMaxSize = function() {
  return gl.maxTextureSize;
};

exports.limitToMaxSize = function(width, height) {
  var maxSize, scale;
  maxSize = getMaxSize();
  scale = Math.max(1, Math.max(width, height) / maxSize);
  return [width / scale >> 0, height / scale >> 0];
};

exports.unloadImage = function() {
  freeTextures();
  return gl.photoLoaded = false;
};

exports.loadImage = loadImage = function(image, options) {
  var alpha, done, quadHeight, quadWidth, scale, settings;
  settings = options.settings, alpha = options.alpha, done = options.done;
  if (alpha == null) {
    alpha = false;
  }
  if (settings == null) {
    settings = state.currentState;
  }
  freeTextures();
  gl.textureFormat = alpha ? gl.context.RGBA : gl.context.RGB;
  if (image.width > gl.maxTextureSize || image.height > gl.maxTextureSize) {
    quadWidth = image.width >> 1;
    quadHeight = image.height >> 1;
    scale = Math.min(gl.maxTextureSize / Math.max(quadWidth, quadHeight), 1);
    textureCanvas.width = quadWidth * scale >> 0;
    textureCanvas.height = quadHeight * scale >> 0;
    gl.texture = {
      width: textureCanvas.width * 2,
      height: textureCanvas.height * 2
    };
    gl.textures = [getSubTexture(image, 0, 0, quadWidth, quadHeight), getSubTexture(image, quadWidth, 0, quadWidth, quadHeight), getSubTexture(image, 0, quadHeight, quadWidth, quadHeight), getSubTexture(image, quadWidth, quadHeight, quadWidth, quadHeight)];
  } else {
    gl.texture = GL.Texture.fromImage(image, {
      magFilter: gl.context.LINEAR,
      format: gl.textureFormat
    });
  }
  resetState(settings);
  if (typeof done === "function") {
    return done();
  }
};

exports.loadData = function(data, options) {
  var done, error, height, settings, width;
  settings = options.settings, width = options.width, height = options.height, done = options.done, error = options.error;
  if (settings == null) {
    settings = state.currentState;
  }
  freeTextures();
  gl.textureFormat = gl.context.RGB;
  if (width > gl.maxTextureSize || height > gl.maxTextureSize) {
    if (typeof error === "function") {
      error();
    }
  } else {
    gl.texture = GL.Texture.fromData(data, width, height, {
      magFilter: gl.context.LINEAR,
      format: gl.textureFormat
    });
  }
  resetState(settings);
  if (typeof done === "function") {
    return done();
  }
};

exports.loadURL = function(url, options) {
  textureImage.crossOrigin = "anonymous";
  textureImage.onload = function() {
    loadImage(textureImage, options);
    return freeImage(textureImage);
  };
  textureImage.onerror = function(err) {
    if (typeof options.error === "function") {
      options.error(err);
    }
    return freeImage(textureImage);
  };
  return textureImage.src = url;
};

exports.getOutput = getOutput = function(width, height, options) {
  var format, pixels, resize, settings, watermark;
  settings = options.settings, watermark = options.watermark, resize = options.resize, pixels = options.pixels, format = options.format;
  return renderer.renderOutput(width, height, settings, watermark, resize, pixels, format);
};

exports.exportJPEG = function(width, height, options) {
  var head, output, quality;
  quality = options.quality, head = options.head;
  quality = quality || 90;
  output = JPEG(getOutput(width, height, options), quality, head);
  return output;
};

exports.exportPNG = function(width, height, options) {
  var data, output;
  output = getOutput(width, height, options);
  data = PNG(output.data, output.width, output.height);
  output = {
    width: output.width,
    height: output.height,
    data: data
  };
  return output;
};

exports.exportTIFF = function(width, height, options) {
  var data, output;
  output = getOutput(width, height, options);
  data = TIFF(output.data, output.width, output.height);
  output = {
    width: output.width,
    height: output.height,
    data: data
  };
  return output;
};


},{"./compositor.coffee":364,"./context.coffee":365,"./face.coffee":366,"./lib/jpeg_encoder.js":370,"./lib/png_encoder.js":371,"./lib/tiff_encoder.js":372,"./renderer.coffee":373,"./state.coffee":376,"./utils.coffee":378}],370:[function(require,module,exports){
/* @@--
 * Copyright (C) 2014 Alberto Vigata
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the University of California, Berkeley nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

(function namespace() {


  //-------------------------------------------------------------------------------------------
  // petitoJPEG routines
  //-------------------------------------------------------------------------------------------

  /**
   * BitWriter class
   *
   * A class to write bits to a random output
   *
   * Provide a byte sink by providing an object like ByteWriter with the setByteWriter method
   *
   * Enhanced for memory by Polarr, Inc.
   */

  var image;

  function BitWriter() {
    var bufsize = 4096*8;
    var buffer = new Uint8Array(bufsize);
    var bufptr = 0;
    var bitcount = 0;
    var bitcache = 0;
    var writtenBytes = 0;

    function emptybuffer() {
      var arr = buffer;
      if (bufsize > bufptr) {
        arr = buffer.subarray(0, bufptr)
      }
      var total = arr.length + writtenBytes;
      if (total > image.data.length) {
        console.error("jpeg data exceeds raw image data byte length");
        arr = arr.slice(0, total - image.data.length)
      }
      try{
        image.data.set(arr, writtenBytes);
      }catch(e){
        console.error("Source is too large");
      }
      writtenBytes += bufptr;
      bufptr = 0;
    }

    function emptybitbuffer(){
      do {
        // empty buffer
        if (bufptr >= bufsize) {
          emptybuffer();
        }

        var b = (bitcache >> 24)&0xff;

        if ( b == 0xff ) { /*Add 0x00 stuffing*/
          bitcache &= 0x00ffffff;
          buffer[bufptr++] = 0xff;
          continue;
        }

        buffer[bufptr++] = b;

        bitcache <<= 8;/* remove bits from bitcache*/
        bitcount -= 8;

      } while( bitcount >= 8 );
    }

    // This ensures there is at least 16 free bits in the buffer
    function emptybitbuffer_16() {
      /* the following loop always adds two bytes at least. to the bitcache*/
      if (bitcount > 16) {
        emptybitbuffer();
      }
    }

    function shovebits(val, bits) {
      bitcache |= (val & ((1<<(bits))-1))  << (32 - bitcount - bits );
      bitcount += bits;
    }

    function putbits(val, bits) {
      emptybitbuffer_16();
      shovebits(val, bits);
    }

    var flush_buffers = function() {
      align8();
      if (bitcount>=8) {
        emptybitbuffer();
      }
    };

    var align8 = function () {
      putbits( 0xff, ((32 - bitcount)&0x7) );
    };


    /**
     * Public API
     */

    this.putbits = putbits;

    this.putshort = function(s) {
      flush_buffers();
      buffer[bufptr++]=((s)&0xffff)>>>8;
      buffer[bufptr++]=s&0xff;
    };

    this.putbyte = function(b) {
      flush_buffers();
      buffer[bufptr++]=b;
    };

    this.align = function() {
      align8();
    };

    this.end = function() {
      emptybuffer();
      buffer = null;
    };

    this.getWrittenBytes = function () {
      return writtenBytes;
    };
  }

  //-------------------------------------------------------------------------------------------
  // encoding tables
  //-------------------------------------------------------------------------------------------
  var std_dc_luminance_nrcodes = new Uint32Array([0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]);
  var std_dc_luminance_values = new Uint32Array([0,1,2,3,4,5,6,7,8,9,10,11]);
  var std_ac_luminance_nrcodes = new Uint32Array([0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]);
  var std_ac_luminance_values = new Uint32Array([0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,
    0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,
    0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
    0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
    0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,
    0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
    0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,
    0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
    0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
    0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
    0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,
    0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
    0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,
    0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,
    0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
    0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
    0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,
    0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
    0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,
    0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
    0xf9,0xfa]);

  var std_dc_chrominance_nrcodes = new Uint32Array([0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0]);
  var std_dc_chrominance_values = new Uint32Array([0,1,2,3,4,5,6,7,8,9,10,11]);
  var std_ac_chrominance_nrcodes = new Uint32Array([0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77]);
  var std_ac_chrominance_values = new Uint32Array([0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,
    0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,
    0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
    0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
    0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,
    0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
    0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,
    0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
    0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
    0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,
    0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,
    0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
    0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,
    0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,
    0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
    0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
    0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,
    0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
    0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,
    0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
    0xf9,0xfa
  ]);

  var jpeg_natural_order = new Uint32Array([
    0,  1,  8, 16,  9,  2,  3, 10,
    17, 24, 32, 25, 18, 11,  4,  5,
    12, 19, 26, 33, 40, 48, 41, 34,
    27, 20, 13,  6,  7, 14, 21, 28,
    35, 42, 49, 56, 57, 50, 43, 36,
    29, 22, 15, 23, 30, 37, 44, 51,
    58, 59, 52, 45, 38, 31, 39, 46,
    53, 60, 61, 54, 47, 55, 62, 63,
    63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */
    63, 63, 63, 63, 63, 63, 63, 63
  ]);

  /* zig zag scan table */
  var zz = new Uint32Array([
    0, 1, 5, 6,14,15,27,28,
    2, 4, 7,13,16,26,29,42,
    3, 8,12,17,25,30,41,43,
    9,11,18,24,31,40,44,53,
    10,19,23,32,39,45,52,54,
    20,22,33,38,46,51,55,60,
    21,34,37,47,50,56,59,61,
    35,36,48,49,57,58,62,63
  ]);

  /* aan dct scale factors */
  var aasf = new Float64Array([
    1.0, 1.387039845, 1.306562965, 1.175875602,
    1.0, 0.785694958, 0.541196100, 0.275899379
  ]);

  /* default quantization tables */
  var YQT = new Uint32Array([
    16, 11, 10, 16, 24, 40, 51, 61,
    12, 12, 14, 19, 26, 58, 60, 55,
    14, 13, 16, 24, 40, 57, 69, 56,
    14, 17, 22, 29, 51, 87, 80, 62,
    18, 22, 37, 56, 68,109,103, 77,
    24, 35, 55, 64, 81,104,113, 92,
    49, 64, 78, 87,103,121,120,101,
    72, 92, 95, 98,112,100,103, 99
  ]);

  var UVQT = new Uint32Array([
    17, 18, 24, 47, 99, 99, 99, 99,
    18, 21, 26, 66, 99, 99, 99, 99,
    24, 26, 56, 99, 99, 99, 99, 99,
    47, 66, 99, 99, 99, 99, 99, 99,
    99, 99, 99, 99, 99, 99, 99, 99,
    99, 99, 99, 99, 99, 99, 99, 99,
    99, 99, 99, 99, 99, 99, 99, 99,
    99, 99, 99, 99, 99, 99, 99, 99
  ]);

  //-------------------------------------------------------------------------------------------
  // PTTJPEG object
  //-------------------------------------------------------------------------------------------
  function PTTJPEG() {

    /* private context variables */
    var fdtbl_Y = new Float64Array(64);
    var fdtbl_UV = new Float64Array(64);
    var YDU = new Float64Array(64);
    var UDU = new Float64Array(64);
    var VDU = new Float64Array(64);

    var YTable = new Int32Array(64);
    var UVTable = new Int32Array(64);
    var outputfDCTQuant = new Int32Array(64);

    var sf = 1; // int. the scale factor

    var bitwriter;

    var YDC_HT = new Array(256);
    var UVDC_HT= new Array(256);
    var YAC_HT = new Array(256);
    var UVAC_HT= new Array(256);

    /**
     * var quality:int
     *
     */
    var init_quality_settings = function (quality) {
      if (quality <= 0)
        quality = 1;

      if (quality > 95)
        quality = 95;

      sf = quality < 50 ? (5000 / quality)|0 : (200 - (quality<<1))|0;

      /* init quantization tables */
      init_quant_tables(sf);
    };

    /**
     * var sf:int: the scale factor
     * @returns
     */
    var init_quant_tables = function (sff)  {
      var i;
      var I64 = 64;
      var I8 = 8;

      for (i = 0; i < I64; ++i)
      {
        var t = ((YQT[i]*sff+50)*0.01)|0;
        if (t < 1) {
          t = 1;
        } else if (t > 255) {
          t = 255;
        }
        YTable[zz[i]] = t;
      }

      for (i = 0; i < I64; i++)
      {
        var u = ((UVQT[i]*sff+50)*0.01)|0;
        if (u < 1) {
          u = 1;
        } else if (u > 255) {
          u = 255;
        }
        UVTable[zz[i]] = u;
      }
      i = 0;
      var row;
      var col;
      for (row = 0; row < I8; ++row)
      {
        for (col = 0; col < I8; ++col)
        {
          fdtbl_Y[i]  = (1 / (YTable [zz[i]] * aasf[row] * aasf[col] * I8));
          fdtbl_UV[i] = (1 / (UVTable[zz[i]] * aasf[row] * aasf[col] * I8));
          i++;
        }
      }
    };

    /**
     * const int nrcodes[]
     * const int std_table[]
     * BitString HT, Array(BitsString)
     *
     */
    var computeHuffmanTbl = function (nrcodes, std_table, HT)
    {
      var codevalue = 0;    //int
      var pos_in_table = 0; //int
      var k,j;                //int

      // initialize table
      for( k=0; k<256; k++ ) {
        HT[k] = [0, 0];
      }

      for (k=1; k<=16; ++k)
      {
        for (j=1; j<=nrcodes[k]; ++j)
        {
          HT[std_table[pos_in_table]] = [codevalue, k];
          pos_in_table++;
          codevalue++;
        }
        codevalue<<=1;
      }
    }


    /**
     * Initialize huffman tables
     */
    var init_huffman_tables = function()
    {
      computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values,YDC_HT);
      computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values, UVDC_HT);
      computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values, YAC_HT);
      computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values, UVAC_HT);
    }

    /**
     *
     * DCT and quantization core
     *
     * double data[]
     * double fdtbl[]
     *
     * returns quantized coefficients
     *
     */

    function fDCTQuant(data, fdtbl) {
      /* Pass 1: process rows. */
      var DCTQuant;
      var dataOff = 0;
      var d0,d1,d2,d3,d4,d5,d6,d7;
      var i;
      var I8 = 8;
      var I64 = 64;

      var tmp0,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7,tmp10,tmp11,tmp12,tmp13;
      var z1,z2,z3,z4,z5,z11,z13;

      for (i=0; i<I8; ++i)
      {
        d0 = data[dataOff];
        d1 = data[dataOff+1];
        d2 = data[dataOff+2];
        d3 = data[dataOff+3];
        d4 = data[dataOff+4];
        d5 = data[dataOff+5];
        d6 = data[dataOff+6];
        d7 = data[dataOff+7];

        tmp0 = d0 + d7;
        tmp7 = d0 - d7;
        tmp1 = d1 + d6;
        tmp6 = d1 - d6;
        tmp2 = d2 + d5;
        tmp5 = d2 - d5;
        tmp3 = d3 + d4;
        tmp4 = d3 - d4;

        /* Even part */
        tmp10 = tmp0 + tmp3;    /* phase 2 */
        tmp13 = tmp0 - tmp3;
        tmp11 = tmp1 + tmp2;
        tmp12 = tmp1 - tmp2;

        data[dataOff   ] = tmp10 + tmp11; /* phase 3 */
        data[dataOff+4 ] = tmp10 - tmp11;

        z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */
        data[dataOff+2] = tmp13 + z1; /* phase 5 */
        data[dataOff+6] = tmp13 - z1;

        /* Odd part */
        tmp10 = tmp4 + tmp5; /* phase 2 */
        tmp11 = tmp5 + tmp6;
        tmp12 = tmp6 + tmp7;

        /* The rotator is modified from fig 4-8 to avoid extra negations. */
        z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */
        z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */
        z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */
        z3 = tmp11 * 0.707106781; /* c4 */

        z11 = tmp7 + z3;    /* phase 5 */
        z13 = tmp7 - z3;

        data[dataOff+5] = z13 + z2; /* phase 6 */
        data[dataOff+3] = z13 - z2;
        data[dataOff+1] = z11 + z4;
        data[dataOff+7] = z11 - z4;

        dataOff += 8; /* advance pointer to next row */
      }

      /* Pass 2: process columns. */
      dataOff = 0;
      for (i=0; i<I8; ++i)
      {
        d0 = data[dataOff];
        d1 = data[dataOff + 8];
        d2 = data[dataOff + 16];
        d3 = data[dataOff + 24];
        d4 = data[dataOff + 32];
        d5 = data[dataOff + 40];
        d6 = data[dataOff + 48];
        d7 = data[dataOff + 56];

        tmp0 = d0 + d7;
        tmp7 = d0 - d7;
        tmp1 = d1 + d6;
        tmp6 = d1 - d6;
        tmp2 = d2 + d5;
        tmp5 = d2 - d5;
        tmp3 = d3 + d4;
        tmp4 = d3 - d4;

        /* Even part */
        tmp10 = tmp0 + tmp3;    /* phase 2 */
        tmp13 = tmp0 - tmp3;
        tmp11 = tmp1 + tmp2;
        tmp12 = tmp1 - tmp2;

        data[dataOff] = tmp10 + tmp11; /* phase 3 */
        data[dataOff+32] = tmp10 - tmp11;

        z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */
        data[dataOff+16] = tmp13 + z1; /* phase 5 */
        data[dataOff+48] = tmp13 - z1;

        /* Odd part */
        tmp10 = tmp4 + tmp5; /* phase 2 */
        tmp11 = tmp5 + tmp6;
        tmp12 = tmp6 + tmp7;

        /* The rotator is modified from fig 4-8 to avoid extra negations. */
        z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */
        z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */
        z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */
        z3 = tmp11 * 0.707106781; /* c4 */

        z11 = tmp7 + z3;    /* phase 5 */
        z13 = tmp7 - z3;

        data[dataOff+40] = z13 + z2; /* phase 6 */
        data[dataOff+24] = z13 - z2;
        data[dataOff+ 8] = z11 + z4;
        data[dataOff+56] = z11 - z4;

        dataOff++; /* advance po(int)er to next column */
      }

      // Quantize/descale the coefficients
      for (i=0; i<I64; ++i) {
        // Apply the quantization and scaling factor & Round to nearest (int)eger
        DCTQuant = data[i]*fdtbl[i];
        outputfDCTQuant[i] = (DCTQuant > 0.0) ? (DCTQuant + 0.5)|0 : (DCTQuant - 0.5)|0;
      }
      return outputfDCTQuant;
    }

    //-------------------------------------------------------------------------------------------
    // chunk writing routines
    function writeAPP0()
    {
      bitwriter.putshort( 0xFFE0); // marker
      bitwriter.putshort( 16); // length
      bitwriter.putbyte( 0x4A); // J
      bitwriter.putbyte( 0x46); // F
      bitwriter.putbyte( 0x49); // I
      bitwriter.putbyte( 0x46); // F
      bitwriter.putbyte( 0); // = "JFIF"'\0'
      bitwriter.putbyte( 1); // versionhi
      bitwriter.putbyte( 1); // versionlo
      bitwriter.putbyte( 0); // xyunits
      bitwriter.putshort( 1); // xdensity
      bitwriter.putshort( 1); // ydensity
      bitwriter.putbyte( 0); // thumbnwidth
      bitwriter.putbyte( 0); // thumbnheight
    }


    // width:int, height:int
    function writeSOF0( width, height)
    {
      bitwriter.putshort(0xFFC0); // marker
      bitwriter.putshort(17);   // length, truecolor YUV JPG
      bitwriter.putbyte(8);    // precision
      bitwriter.putshort(height);
      bitwriter.putshort(width);
      bitwriter.putbyte(3);    // nrofcomponents
      bitwriter.putbyte(1);    // IdY
      bitwriter.putbyte(0x11); // HVY
      bitwriter.putbyte(0);    // QTY
      bitwriter.putbyte(2);    // IdU
      bitwriter.putbyte(0x11); // HVU
      bitwriter.putbyte(1);    // QTU
      bitwriter.putbyte(3);    // IdV
      bitwriter.putbyte(0x11); // HVV
      bitwriter.putbyte(1);    // QTV
    }

    function writeDQT()
    {
      bitwriter.putshort(0xFFDB); // marker
      bitwriter.putshort(132);     // length
      bitwriter.putbyte(0);

      var i;
      var I64=64;
      for (i=0; i<I64; ++i)
        bitwriter.putbyte(YTable[i]);

      bitwriter.putbyte(1);

      for (i=0; i<I64; ++i)
        bitwriter.putbyte(UVTable[i]);
    }

    function writeDHT()
    {
      bitwriter.putshort( 0xFFC4); // marker
      bitwriter.putshort( 0x01A2); // length

      bitwriter.putbyte(0); // HTYDCinfno
      var i;
      var I11=11;
      var I16=16;
      var I161=161;

      for (i=0; i<I16; ++i) {
        bitwriter.putbyte(std_dc_luminance_nrcodes[i+1]);
      }

      for (i=0; i<=I11; ++i)
        bitwriter.putbyte(std_dc_luminance_values[i]);

      bitwriter.putbyte(0x10); // HTYACinfo

      for (i=0; i<I16; ++i)
        bitwriter.putbyte(std_ac_luminance_nrcodes[i+1]);

      for (i=0; i<=I161; ++i)
        bitwriter.putbyte(std_ac_luminance_values[i]);

      bitwriter.putbyte(1); // HTUDCinfo

      for (i=0; i<I16; ++i)
        bitwriter.putbyte(std_dc_chrominance_nrcodes[i+1]);

      for (i=0; i<=I11; ++i)
        bitwriter.putbyte(std_dc_chrominance_values[i]);

      bitwriter.putbyte(0x11); // HTUACinfo

      for (i=0; i<I16; ++i)
        bitwriter.putbyte(std_ac_chrominance_nrcodes[i+1]);

      for (i=0; i<=I161; ++i)
        bitwriter.putbyte(std_ac_chrominance_values[i]);
    }

    function writeSOS()
    {
      bitwriter.putshort(0xFFDA); // marker
      bitwriter.putshort(12); // length
      bitwriter.putbyte(3); // nrofcomponents
      bitwriter.putbyte(1); // IdY
      bitwriter.putbyte(0); // HTY
      bitwriter.putbyte(2); // IdU
      bitwriter.putbyte(0x11); // HTU
      bitwriter.putbyte(3); // IdV
      bitwriter.putbyte(0x11); // HTV
      bitwriter.putbyte(0); // Ss
      bitwriter.putbyte(0x3f); // Se
      bitwriter.putbyte(0); // Bf
    }

    function writeEOI()
    {
      bitwriter.align();
      bitwriter.putshort(0xFFD9); //EOI
      bitwriter.end();
    }

    //--------------------------------------------------------------------
    // Block Processing

    function huffman_extend(mag,size) { return ((mag) < (1<<((size)-1)) ? (mag) + (((-1)<<(size)) + 1) : (mag)); }
    function huffman_compact(mag,size) { return ((mag)<0 ? mag + (1<<size)-1 : mag); }
    function log2(x, res) {res = 0; while( x!=0 ){ x>>=1; res++; } return res; }
    function abs(x) { return ((x)>0?(x):(-(x)))}

    /**
     * double CDU[]
     * double fdtbl[]
     * double DC
     * BitString HTDC[]
     * BitString HTAC[]
     *
     * Returns double
     */
    function processDU( CDU, fdtbl, DC, HTDC, HTAC )
    {

      var DU_DCT = fDCTQuant( CDU, fdtbl);

      var dc_diff; //int
      var last_dc; // double

      // output
      // DC Bits
      dc_diff = DU_DCT[0] - DC|0;
      last_dc = DU_DCT[0];
      ///////////////////////
      //DC CODING

      // DC Size
      var dc_size = 0, diffabs = abs(dc_diff);
      dc_size = log2(diffabs, dc_size);

      bitwriter.putbits(HTDC[dc_size][0], HTDC[dc_size][1]);

      // DC Bits
      if( dc_size )
      {
        dc_diff = huffman_compact(dc_diff, dc_size);
        bitwriter.putbits( dc_diff, dc_size );
      }

      ////////////////////
      // AC CODING
      var run;
      var accoeff; //int16
      var lastcoeff_pos = 0; //ui32
      var maxcoeff = 64; // int

      var i = 0;
      while( 1 )
      {
        // find next coefficient to code
        i++;
        for( run=0 ;(accoeff = DU_DCT[ jpeg_natural_order[i] ])== 0 && i<maxcoeff; i++, run++);

        if( i>= maxcoeff )
          break;

        // Code runs greater than 16
        while( run>= 16 )
        {
          // Write value
          bitwriter.putbits(HTAC[0xf0][0], HTAC[0xf0][1]);
          run -= 16;
        }
        // AC Size
        var acsize = 0;
        var acabs = abs(accoeff);
        acsize = log2(acabs, acsize);

        // Write value
        var hv = (run << 4) | acsize;
        bitwriter.putbits(HTAC[hv][0], HTAC[hv][1]);

        // AC Bits
        if( acsize )
        {
          accoeff = huffman_compact(accoeff, acsize);
          bitwriter.putbits(accoeff, acsize );
        }

        // Keep position of last encoded coefficient
        lastcoeff_pos = i;
      }

      // Write EOB
      if( lastcoeff_pos != 63 )
        bitwriter.putbits(HTAC[0][0], HTAC[0][1]);


      return last_dc;
    }

    /**
     * xpos:int
     * ypos:int
     *
     * This functions calls the getpixels() object to obtain an McuImg object that contains
     * an Uint8Array() buffer with pixel data in RGBA byte order. McuImg includes an offset
     * to the beginning of the requested area as well as the stride in bytes.
     *
     * The method converts the RGB pixels into YUV ready for further processing. The destination
     * pixels are written to the local private PTTJPEG fields YDU,UDU,VDU
     *
     */
    function rgb2yuv_444(xpos, ypos, channels)
    {
      // RGB/RGBA format in unpacked bytes
      var mcuimg = {
        offset:ypos*image.width*channels + xpos*channels,
        stride:image.width*channels,
        xpos:xpos,
        ypos:ypos,
        w:xpos + 8 > image.width ? image.width-xpos : 8,
        h:ypos + 8 > image.height ? image.height-ypos : 8
      };

      var buf = image.data;
      var pel;
      var P=0;
      var x,y,off,off_1=0,R,G,B;

      if( mcuimg.w==8 && mcuimg.h==8 ) {
        /* block is 8x8 */
        for ( y=0; y<8; y++) {
          for (x=0; x<8; x++) {
            off = mcuimg.offset + y*mcuimg.stride + x*channels;

            R = buf[off];
            G = buf[off+1];
            B = buf[off+2];

            YDU[off_1]   =((( 0.29900)*R+( 0.58700)*G+( 0.11400)*B))-0x80;
            UDU[off_1]   =(((-0.16874)*R+(-0.33126)*G+( 0.50000)*B));
            VDU[off_1++] =((( 0.50000)*R+(-0.41869)*G+(-0.08131)*B));
          }
        }
      } else {
        /* we separate the borderline conditions to avoid having to branch out
         * on every mcu */
        for( y=0; y<8; y++ ) {
          for( x=0; x<8; x++ ) {
            var xx=x, yy=y;
            if( x >= mcuimg.w ) {
              xx = mcuimg.w-1;
            }

            if( y >= mcuimg.h ) {
              yy = mcuimg.h-1;
            }


            off = mcuimg.offset + yy*mcuimg.stride + xx*channels;

            R = buf[off];
            G = buf[off+1];
            B = buf[off+2];

            YDU[off_1]   =((( 0.29900)*R+( 0.58700)*G+( 0.11400)*B))-0x80;
            UDU[off_1]   =(((-0.16874)*R+(-0.33126)*G+( 0.50000)*B));
            VDU[off_1++] =((( 0.50000)*R+(-0.41869)*G+(-0.08131)*B));
          }
        }
      }
    }

    //--------------------------------------------------------------------
    // exported functions
    this.version = function() { return "petitóJPEG 0.4"; };

    /**
     * The encode function
     *
     * quality:int 0-100
     * img: pttJPEGImage object. The image object to encode
     * bw: byteWriter object. The object that will be used to write the compressed data
     *
     *
     */
    this.encode = function (quality, head) {
      if (!image)
        console.log("input image not provided. aborting encode");

      init_quality_settings(quality);

      /* start the bitwriter */
      bitwriter = new BitWriter(image);

      if (!head) {
        bitwriter.putshort(0xFFD8); // SOI
      }

      /* write headers out */
      writeAPP0();
      writeDQT();
      writeSOF0( image.width, image.height );
      writeDHT();
      writeSOS();

      /* MCU(minimum coding units) are 8x8 blocks for now*/
      var DCU=0, DCY=0, DCV=0;

      var width=image.width;
      var height=image.height;
      var ypos,xpos;
      var mcucount = 0;
      var channels = 4;

      if (image.data.length < width * height * 4) {
        channels = 3; // RGB
      }


      for (ypos=0; ypos<height; ypos+=8)
      {
        for (xpos=0; xpos<width; xpos+=8)
        {
          rgb2yuv_444(xpos, ypos, channels);
          DCY = processDU( YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);
          DCU = processDU( UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
          DCV = processDU( VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
        }
      }

      writeEOI();

      return bitwriter.getWrittenBytes()
    };


    /**
     * Setup the encoding envinment object
     */
    var startup = (function() {
      init_huffman_tables();
    }());
  }

  var encoder = new PTTJPEG();

  function encode(imgData, quality, head) {
    quality = quality || 80;

    image = imgData;

    var bytes = encoder.encode(quality, head);

    imgData.data = image.data.subarray(0, bytes);

    if (head) {
      imgData.data = [head, imgData.data];
    }

    image = null;

    return imgData;
  }

  module.exports = encode;
}());
},{}],371:[function(require,module,exports){
var pako = require('pako/lib/deflate');

var crc32 = (function () {
	var table = new Uint32Array(256);
	for (var i = 0; i < 256; i++) {
		var c = i;
		for (var k = 0; k < 8; k++)
			c = (c & 1 > 0) ? 0xEDB88320 ^ (c >>> 1) : c >>> 1;
		table[i] = c;
	}
	
	return function (crc) {
		for (var j = 1; j < arguments.length; j++)
			for (var i = 0; i < arguments[j].length; i++)
				crc = table[(crc ^ arguments[j][i]) & 0xFF] ^ (crc >>> 8);
		return ~crc;
	};
}());


function PNG(data, width, height) {
	logTime("encode PNG");
	
	// Generate a color palette and IDAT block
	var size = data.length;
	var i = 0;
	var idatTruecolor = new Uint8Array(size + height);
	var idatTruecolorPointer = 0;

	for (var y = 0; y < height; y++) {

		// No filter at the start of each row
		idatTruecolor[idatTruecolorPointer++] = 0;
	
		for (var x = 0; x < width; x++) {
			
			idatTruecolor[idatTruecolorPointer++] = data[i];
			idatTruecolor[idatTruecolorPointer++] = data[i+1];
			idatTruecolor[idatTruecolorPointer++] = data[i+2];
			idatTruecolor[idatTruecolorPointer++] = data[i+3];
			i += 4;
		
		}
	}

	var png = [];
	
	// The signature
	png.push((new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])).buffer);
	
	function createChunk(name, data) {
		var size = new Uint8Array(4);
		writeInt32(size, 0, data.length);
		
		var head = new Uint8Array(4);
		head[0] = name.charCodeAt(0);
		head[1] = name.charCodeAt(1);
		head[2] = name.charCodeAt(2);
		head[3] = name.charCodeAt(3);
			
		var tail = new Uint8Array(4);
		writeInt32(tail, 0, crc32(0xFFFFFFFF, head, data));
		
		png.push(size.buffer);
		png.push(head.buffer);
		png.push(data.buffer);
		png.push(tail.buffer);
	}

	function writeInt32(arr, i, value) {
		arr[i]     = value >>> 24;
		arr[i + 1] = value >>> 16;
		arr[i + 2] = value >>> 8;
		arr[i + 3] = value;
	}
	
	// The header (IHDR)
	var ihdr = new Uint8Array(13);
	writeInt32(ihdr, 0, width);
	writeInt32(ihdr, 4, height);
	ihdr[8] = 8; // Bit-depth
	ihdr[9] = 6; // Truecolor with Alpha
	ihdr[10] = 0; // Compression
	ihdr[11] = 0; // Filter
	ihdr[12] = 0; // Interlace
	createChunk('IHDR', ihdr);

	// compress pixel data
	var arr = pako.deflate(idatTruecolor, {level: 9});

	createChunk('IDAT', arr);
	
	createChunk('IEND', new Uint8Array(0));

	logTimeEnd("encode PNG");

	return png;

}

module.exports = PNG;
},{"pako/lib/deflate":22}],372:[function(require,module,exports){
"use strict";

function TIFF(imageData, width, height) {

  var littleEndian = (function() {
    var buffer = new ArrayBuffer(2);
    new DataView(buffer).setInt16(0, 256, true /* littleEndian */);
    // Int16Array uses the platform's endianness.
    return new Int16Array(buffer)[0] === 256;
  })();

  /*
   * IFD types:
   1 = BYTE 8-bit unsigned integer.
   2 = ASCII 8-bit byte that contains a 7-bit ASCII code; the last byte must be NUL (binary zero).
   3 = SHORT 16-bit (2-byte) unsigned integer.
   4 = LONG 32-bit (4-byte) unsigned integer.
   5 = RATIONAL Two LONGs: the first represents the numerator of a fraction; the second, the denominator.
   6 = SBYTE An 8-bit signed (twos-complement) integer.
   7 = UNDEFINED An 8-bit byte that may contain anything, depending on the definition of the field.
   8 = SSHORT A 16-bit (2-byte) signed (twos-complement) integer.
   9 = SLONG A 32-bit (4-byte) signed (twos-complement) integer.
   10 = SRATIONAL Two SLONG’s: the first represents the numerator of a fraction, the second the denominator.
   11 = FLOAT Single precision (4-byte) IEEE format.
   12 = DOUBLE Double precision (8-byte) IEEE format.
   * */
  var writeIFD = function(dataView, offset, tag, type, value) {
    // write tag
    dataView.setUint16(offset, tag);
    offset += 2;
    // write type
    dataView.setUint16(offset, type);
    offset += 2;
    // write count
    dataView.setUint32(offset, 1);
    offset += 4;
    // write value or offset
    switch (type) {
      case 1:
        dataView.setUint8(offset, value);
        break;
      case 2:
        dataView.setUint8(offset, value);
        break;
      case 3:
        dataView.setUint16(offset, value);
        break;
      case 4:
        dataView.setUint32(offset, value);
        break;
      //case 5:
      //  var hi = Math.floor(value / (4294967296)),
      //      lo = value - hi * (4294967296);
      //  dataView.setUint32(offset, lo);
      //  dataView.setUint32(offset + 4, hi);
      //  break;
    }
    offset += 4;

    return offset;
  };

  // header
  var header = new ArrayBuffer(1792);
  var dataView = new DataView(header);
  var offset;

  dataView.setUint16(0, littleEndian ? 0x4D4D : 0x4949); // byte order
  dataView.setUint16(2, 42); // 42
  dataView.setUint16(4, 0x0000); // 1st IFD offset
  // IFD
  dataView.setUint16(6, 0x0008);
  dataView.setUint16(8, 13); // Number of Directory Entries
  offset = writeIFD(dataView, 10, 0x00FE, 4, 0); // NewSubfileType
  offset = writeIFD(dataView, offset, 0x0100, 4, width); // ImageWidth
  offset = writeIFD(dataView, offset, 0x0101, 4, height); // ImageLength
  offset = writeIFD(dataView, offset, 0x0102, 3, 8); // BitsPerSample
  offset = writeIFD(dataView, offset, 0x0103, 3, 1); // Compression
  offset = writeIFD(dataView, offset, 0x0106, 3, 2); // PhotometricInterpretation
  offset = writeIFD(dataView, offset, 0x0111, 4, header.byteLength); // StripOffsets
  offset = writeIFD(dataView, offset, 0x0115, 3, 3); // SamplesPerPixel
  offset = writeIFD(dataView, offset, 0x0116, 4, height); // RowsPerStrip
  offset = writeIFD(dataView, offset, 0x0117, 4, width * height * 3); // StripByteCounts
  offset = writeIFD(dataView, offset, 0x011A, 4, 300); // XResolution
  offset = writeIFD(dataView, offset, 0x011B, 4, 300); // YResolution
  offset = writeIFD(dataView, offset, 0x0128, 3, 2); // ResolutionUnit
  dataView.setUint16(offset, 0x0000); // Next IFD offset

  var rgb = new Uint8Array(width * height * 3);
  var pos, i, ii;
  // convert to RGB
  for (var row = 0; row < height; row++) {
    for (var col = 0; col < width; col++) {
      pos = col + row * width;
      i = pos * 3;
      ii = pos * 4;
      rgb[i] = imageData[ii];
      rgb[i + 1] = imageData[ii + 1];
      rgb[i + 2] = imageData[ii + 2];
    }
  }

  return [header, rgb.buffer];
}

module.exports = TIFF;

},{}],373:[function(require,module,exports){
var clear, compareLocal, compositor, copyState, enableBlend, filters, gl, histogram, loadScreenMatrix, localOrder, readTexture, render, renderBlurTexture, renderDeps, renderFilter, renderFilters, renderLocalAlpha, renderLocalBlur, renderScreen, renderTextureMaps, resize, resizeTexture, state, swapTextures, textChannels, utils;

gl = require("./context.coffee");

utils = require("./utils.coffee");

filters = require("./filters.coffee");

compositor = require("./compositor.coffee");

histogram = require("./histogram.coffee");

state = require("./state.coffee");

exports.resize = resize = function(w, h) {
  var drawHeight, drawWidth;
  w = Math.max(100, w);
  h = Math.max(100, h);
  gl.fov = Math.tan(45 * Math.PI / 360);
  gl.perspectiveMatrix = GL.Matrix.perspective(45, 1, 0.1, 1000);
  gl.perspectiveMatrix = GL.Matrix.multiply(gl.perspectiveMatrix, GL.Matrix.scale(1 / (w / 2), 1 / (h / 2), 1));
  gl.canvas.width = w;
  gl.canvas.height = h;
  gl.context.viewport(0, 0, w, h);
  drawWidth = gl.context.drawingBufferWidth;
  drawHeight = gl.context.drawingBufferHeight;
  if (w > drawWidth || h > drawHeight) {
    gl.canvas.width = drawWidth;
    return gl.canvas.height = drawHeight;
  }
};

exports.resizeTexture = resizeTexture = function(tex, width, height, magFilter, minFilter, flipY) {
  if (tex.width !== width || tex.height !== height) {
    magFilter = magFilter || gl.context.LINEAR;
    minFilter = minFilter || gl.context.LINEAR;
    tex.width = (width || 1) >> 0;
    tex.height = (height || 1) >> 0;
    gl.context.bindTexture(gl.context.TEXTURE_2D, tex.id);
    if (flipY) {
      gl.context.pixelStorei(gl.context.UNPACK_FLIP_Y_WEBGL, 1);
    }
    gl.context.texParameteri(gl.context.TEXTURE_2D, gl.context.TEXTURE_MAG_FILTER, magFilter);
    gl.context.texParameteri(gl.context.TEXTURE_2D, gl.context.TEXTURE_MIN_FILTER, minFilter);
    return gl.context.texImage2D(gl.context.TEXTURE_2D, 0, tex.format, tex.width, tex.height, 0, tex.format, tex.type, null);
  }
};

exports.readTexture = readTexture = function(texture, pixels) {
  var channels, fb, height, width;
  width = texture.width, height = texture.height;
  channels = texture.format === gl.context.RGB ? 3 : 4;
  pixels = pixels || new Uint8Array(width * height * channels);
  fb = texture.getFramebuffer();
  gl.context.bindFramebuffer(gl.context.FRAMEBUFFER, fb);
  gl.context.readPixels(0, 0, width, height, texture.format, gl.context.UNSIGNED_BYTE, pixels);
  gl.context.bindFramebuffer(gl.context.FRAMEBUFFER, null);
  return pixels;
};

exports.swapTextures = swapTextures = function() {
  var ref;
  return ref = [gl.writableTexture, gl.readableTexture], gl.readableTexture = ref[0], gl.writableTexture = ref[1], ref;
};

exports.renderTextureMaps = renderTextureMaps = function() {
  var halfH, halfW, height, inTexture, kernel, loc, paintHeight, paintWidth, radius, ref, scale, shader, sigma, size, spatialX, spatialY, width;
  ref = gl.texture, width = ref.width, height = ref.height;
  halfW = width >> 1;
  halfH = height >> 1;
  resizeTexture(gl.writableTexture, halfW, halfH);
  resizeTexture(gl.readableTexture, halfW, halfH);
  resizeTexture(gl.dehazeTexture, halfW, halfH);
  resizeTexture(gl.denoiseTexture, halfW, halfH);
  scale = 1024 / Math.max.call(this, width, height);
  paintWidth = Math.floor(width * scale);
  paintHeight = Math.floor(height * scale);
  resizeTexture(gl.paintTexture, paintWidth, paintHeight);
  gl.context.loadIdentity();
  if (gl.textures.length) {
    logTime("render multi-texture");
    filters.multitexture.render(null, gl.readableTexture);
    logTimeEnd("render multi-texture");
    inTexture = gl.readableTexture;
  } else {
    inTexture = gl.texture;
  }
  logTime("render denoise map");
  kernel = [];
  radius = 7;
  size = radius * 2 + 1;
  sigma = 1.0 / size;
  spatialX = 1.0 / inTexture.width;
  spatialY = 1.0 / inTexture.height;
  for (var i = 0; i <= radius; i++) {
    kernel[radius+i] = kernel[radius-i] = Math.exp(-(i*i)*sigma);
  };
  shader = filters.denoise_nlm1.shader;
  shader.uniforms({
    delta: spatialX
  });
  loc = shader.getLocation("kernel[0]");
  gl.context.uniform1fv(loc, kernel);
  shader = filters.denoise_nlm2.shader;
  shader.uniforms({
    delta: spatialY
  });
  loc = shader.getLocation("kernel[0]");
  gl.context.uniform1fv(loc, kernel);
  filters.denoise_nlm1.render(inTexture, gl.writableTexture);
  filters.denoise_nlm2.render(gl.writableTexture, gl.denoiseTexture);
  logTimeEnd("render denoise map");
  logTime("render dehaze map");
  filters.dehaze_map_min1.render(gl.denoiseTexture, gl.readableTexture);
  filters.dehaze_map_min2.render(gl.readableTexture, gl.writableTexture);
  filters.dehaze_map_2.render(gl.writableTexture, gl.dehazeTexture);
  gl.blurTexture = gl.dehazeTexture;
  return logTimeEnd("render dehaze map");
};

renderFilter = function(name, toTexture) {
  var filter;
  if (toTexture == null) {
    toTexture = gl.writableTexture;
  }
  filter = filters[name];
  filter.render(gl.readableTexture, toTexture);
  if (filter.matrix) {
    gl.context.loadIdentity();
  }
  return swapTextures();
};

renderFilters = function(list) {
  var filter, j, len, results;
  results = [];
  for (j = 0, len = list.length; j < len; j++) {
    filter = list[j];
    if (filters[filter].disabled) {
      continue;
    }
    if (!filters[filter].shouldRender()) {
      continue;
    }
    results.push(renderFilter(filter));
  }
  return results;
};

copyState = function(list) {
  var copy, key, val;
  copy = {};
  for (key in list) {
    val = list[key];
    copy[key] = state.currentState[key];
  }
  return copy;
};

enableBlend = function(blend) {
  if (blend) {
    gl.context.enable(gl.context.BLEND);
    gl.context.blendEquation(gl.context.FUNC_ADD);
    return gl.context.blendFuncSeparate(gl.context.SRC_ALPHA, gl.context.ONE_MINUS_SRC_ALPHA, gl.context.ONE, gl.context.ONE_MINUS_SRC_ALPHA);
  } else {
    return gl.context.disable(gl.context.BLEND);
  }
};

compareLocal = function(prev, curr) {
  var i, isEqual, j, k, len, v;
  isEqual = true;
  if (prev instanceof Array && curr instanceof Array) {
    isEqual = prev.length === curr.length;
    if (!isEqual) {
      return isEqual;
    }
    for (i = j = 0, len = prev.length; j < len; i = ++j) {
      v = prev[i];
      isEqual = compareLocal(prev[i], curr[i]);
      if (!isEqual) {
        return isEqual;
      }
    }
  } else if (typeof prev === "object" && typeof curr === "object") {
    for (k in prev) {
      v = prev[k];
      if (k === "points") {
        isEqual = prev[k].length === curr[k].length;
      } else {
        isEqual = compareLocal(prev[k], curr[k]);
      }
      if (!isEqual) {
        return isEqual;
      }
    }
  } else {
    isEqual = prev === curr;
  }
  return isEqual;
};

renderLocalAlpha = function(inTexture) {
  var adjustment, alpha, filter, i, j, len, localAdjustments, results;
  localAdjustments = state.currentState.local_adjustments;
  if (inTexture) {
    gl.readableTexture = inTexture;
  } else {
    if (gl.textures.length) {
      filters.multitexture.render(null, gl.readableTexture);
    } else {
      filters.basic.render(gl.texture, gl.readableTexture);
    }
  }
  results = [];
  for (i = j = 0, len = localAdjustments.length; j < len; i = ++j) {
    adjustment = localAdjustments[i];
    alpha = Math.min(1, i);
    switch (adjustment.type) {
      case 'radial':
        filter = filters["mask_radial"];
        filter.set({
          feather: adjustment.feather,
          size: adjustment.size,
          position: adjustment.position,
          angle: adjustment.angle || 0,
          invert: adjustment.invert ? 1 : 0,
          opacity: adjustment.adjustments.blur || 0,
          alpha: alpha || 0
        });
        break;
      case 'gradient':
        filter = filters["mask_gradient"];
        filter.set({
          startPoint: adjustment.startPoint,
          endPoint: adjustment.endPoint,
          invert: adjustment.invert ? 1 : 0,
          reflect: adjustment.reflect ? 1 : 0,
          opacity: adjustment.adjustments.blur || 0,
          alpha: alpha || 0
        });
        break;
      case "color":
        filter = filters["mask_color"];
        filter.set({
          feather: adjustment.feather,
          threshold: adjustment.threshold,
          size: adjustment.size,
          position: adjustment.position,
          invert: adjustment.invert ? 1 : 0,
          opacity: adjustment.adjustments.blur || 0,
          alpha: alpha || 0
        });
        break;
      case "brush":
        filter = filters["mask_brush"];
        filter.set({
          invert: adjustment.invert ? 1 : 0,
          channel: adjustment.channel,
          opacity: adjustment.adjustments.blur || 0,
          alpha: alpha || 0
        });
    }
    filter.render(gl.readableTexture, gl.writableTexture);
    results.push(swapTextures());
  }
  return results;
};

renderLocalBlur = function(outTexture) {
  var aspect;
  outTexture = outTexture || gl.readableTexture;
  aspect = outTexture.width / outTexture.height;
  filters.blur.set({
    delta: [0, 0.036 * aspect]
  });
  filters.blur.render(gl.readableTexture, gl.writableTexture);
  filters.blur.set({
    delta: [0.036, 0]
  });
  return filters.blur.render(gl.writableTexture, outTexture);
};

exports.blurMask = function(inTexture, radius) {
  var aspect;
  if (radius == null) {
    radius = 0.024;
  }
  resizeTexture(gl.writableTexture, inTexture.width, inTexture.height);
  gl.context.loadIdentity();
  aspect = inTexture.width / inTexture.height;
  filters.blur_mask.set({
    delta: [0, radius * aspect]
  });
  filters.blur_mask.render(inTexture, gl.writableTexture);
  filters.blur_mask.set({
    delta: [radius, 0]
  });
  return filters.blur_mask.render(gl.writableTexture, inTexture);
};

renderDeps = [];

exports.addRenderDependency = function(renderFunc) {
  return renderDeps.push(renderFunc);
};

exports.renderBlurTexture = renderBlurTexture = function() {
  var adjustment, halfHeight, halfWidth, height, j, l, len, len1, len2, localAdjustments, m, matrix, prevLocalAdjustments, ref, renderBlur, renderFunc, updateBlur, width;
  for (j = 0, len = renderDeps.length; j < len; j++) {
    renderFunc = renderDeps[j];
    renderFunc();
  }
  ref = gl.writableTexture, width = ref.width, height = ref.height;
  localAdjustments = state.currentState.local_adjustments;
  prevLocalAdjustments = state.prevLocalAdjustments;
  renderBlur = false;
  updateBlur = false;
  if (localAdjustments.length) {
    for (l = 0, len1 = localAdjustments.length; l < len1; l++) {
      adjustment = localAdjustments[l];
      renderBlur = renderBlur || !!adjustment.adjustments.blur;
    }
    if (renderBlur) {
      updateBlur = !compareLocal(prevLocalAdjustments, localAdjustments);
      if (updateBlur) {
        logTime("render blur texture");
        halfWidth = gl.texture.width >> 1;
        halfHeight = gl.texture.height >> 1;
        resizeTexture(gl.readableTexture, halfWidth, halfHeight);
        resizeTexture(gl.writableTexture, halfWidth, halfHeight);
        resizeTexture(gl.lensBlurTexture, halfWidth, halfHeight);
        matrix = gl.matrix;
        gl.matrix = new GL.Matrix();
        gl.context.loadIdentity();
        prevLocalAdjustments = utils.clone(localAdjustments);
        for (m = 0, len2 = prevLocalAdjustments.length; m < len2; m++) {
          adjustment = prevLocalAdjustments[m];
          adjustment.adjustments = {
            blur: adjustment.adjustments.blur
          };
        }
        renderLocalAlpha();
        renderLocalBlur(gl.lensBlurTexture);
        resizeTexture(gl.readableTexture, width, height);
        resizeTexture(gl.writableTexture, width, height);
        gl.matrix = matrix;
        logTimeEnd("render blur texture");
      }
    }
  }
  if (!renderBlur) {
    resizeTexture(gl.lensBlurTexture, 1, 1);
    prevLocalAdjustments = [];
  }
  return state.prevLocalAdjustments = prevLocalAdjustments;
};

exports.render = render = function(toScreen, toTexture) {
  var adjustment, blendMode, i, j, l, layer, len, len1, localAdjustments, matrix, opacity, prevAdjustments, text, textLayers;
  if (toScreen == null) {
    toScreen = true;
  }
  if (!gl.writableTexture.width || !gl.writableTexture.height) {
    return;
  }
  gl.context.loadIdentity();
  gl.context.loadMatrix(gl.matrix);

  /*
  Render global adjustments
   */
  if (gl.textures.length) {
    filters.distortion_multitexture.render(null, gl.writableTexture);
  } else {
    filters.distortion.render(gl.texture, gl.writableTexture);
  }
  swapTextures();
  gl.context.loadIdentity();
  renderFilters(filters.order);

  /*
  Render local adjustments
   */
  localAdjustments = state.currentState.local_adjustments;
  if (localAdjustments.length) {
    prevAdjustments = copyState(state.localDefaults);
    for (j = 0, len = localAdjustments.length; j < len; j++) {
      adjustment = localAdjustments[j];
      if (adjustment.disable) {
        continue;
      }
      filters.set(utils.assign(state.localDefaults, adjustment.adjustments));
      if (adjustment.type === 'radial') {
        filters.mask_radial.set({
          feather: adjustment.feather,
          size: adjustment.size,
          position: adjustment.position,
          angle: adjustment.angle || 0,
          invert: adjustment.invert ? 1 : 0,
          opacity: 1,
          alpha: 0
        });
        renderFilter("mask_radial");
      } else if (adjustment.type === 'gradient') {
        filters.mask_gradient.set({
          startPoint: adjustment.startPoint,
          endPoint: adjustment.endPoint,
          invert: adjustment.invert ? 1 : 0,
          reflect: adjustment.reflect ? 1 : 0,
          opacity: 1,
          alpha: 0
        });
        renderFilter("mask_gradient");
      } else if (adjustment.type === "color") {
        filters.mask_color.set({
          feather: adjustment.feather,
          threshold: adjustment.threshold,
          size: adjustment.size,
          position: adjustment.position,
          invert: adjustment.invert ? 1 : 0,
          opacity: 1,
          alpha: 0
        });
        renderFilter("mask_color");
      } else if (adjustment.type === "brush") {
        filters.mask_brush.set({
          invert: adjustment.invert ? 1 : 0,
          channel: adjustment.channel,
          opacity: 1,
          alpha: 0
        });
        renderFilter("mask_brush");
      }
      renderFilters(localOrder);
    }
    filters.set(prevAdjustments);
    if (state.currentState.blur_opacity === 0) {
      renderLocalAlpha(gl.readableTexture);
      renderLocalBlur();
    }
  }

  /*
  Render text layers
   */
  textLayers = state.currentState.text;
  if (textLayers.length) {
    if (state.currentState.textInverted) {
      blendMode = 22;
      opacity = 1;
      filters.fill.set({
        color: state.currentState.textInvertedColor
      });
      filters.fill.render(gl.readableTexture, gl.writableTexture);
    } else {
      filters.fill.set({
        color: [0, 0, 0, 0]
      });
      filters.fill.render(gl.readableTexture, gl.writableTexture);
    }
    for (i = l = 0, len1 = textLayers.length; l < len1; i = ++l) {
      text = textLayers[i];
      if (text.disabled) {
        continue;
      }
      layer = gl.textLayers[i];
      if (layer == null) {
        continue;
      }
      layer.bind(1);
      matrix = GL.Matrix.multiply(gl.matrix, layer.matrix);
      gl.context.loadMatrix(matrix);
      filters.layer.set({
        layerTexture: 1,
        colorOverlay: text.color,
        opacity: opacity || text.opacity,
        blendMode: blendMode || text.blend_mode,
        blendMix: text.blend_mix,
        backgroundMatrix: matrix
      });
      enableBlend(true);
      filters.layer.render(gl.readableTexture, gl.writableTexture);
      enableBlend(false);
    }
    swapTextures();
    gl.context.loadIdentity();
  }

  /*
  Render output
   */
  if (gl.textureFormat === gl.context.RGBA) {
    if (gl.textures.length) {
      renderFilter("out_multitexture");
    } else {
      renderFilter("out");
    }
  }
  if (gl.watermarkOptions.enabled) {
    enableBlend(true);
    swapTextures();
    renderFilter("watermark");
    enableBlend(false);
  }
  if (gl.textureFormat === gl.context.RGBA) {
    renderFilter("basic", toTexture);
  } else {
    renderFilter("strip_alpha", toTexture);
  }
  gl.screenTexture = null;
  if (toScreen) {
    gl.screenTexture = gl.readableTexture;
    renderScreen();
  }
};

state.register("local_adjustments", []);

localOrder = ["mosaic", "clarity", "shadows_highlights", "exposure", "temperature", "contrast", "saturation_vibrance", "split_tone"];

textChannels = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];

exports.setScreen = (function() {
  var prevHeight, prevWidth, setScreen, shouldRender, step, stopAnimation, update;
  step = shouldRender = stopAnimation = function() {};
  prevWidth = gl.canvas.width;
  prevHeight = gl.canvas.height;
  update = function(a) {
    gl.screen = {
      zoom: a[0],
      center: [a[1], a[2]],
      offset: [a[3], a[4]],
      rotation: [a[5], a[6], a[7]],
      orientation: a[8]
    };
    if (shouldRender) {
      renderScreen();
    }
    return typeof step === "function" ? step(a) : void 0;
  };
  return setScreen = function(props, duration, done, onUpdate, render) {
    var from, key, screen, to;
    if (render == null) {
      render = true;
    }
    step = onUpdate;
    shouldRender = render;
    screen = utils.clone(gl.screen);
    for (key in props) {
      screen[key] = props[key];
    }
    state.trigger("screen", screen);
    if (gl.canvas.width !== prevWidth || gl.canvas.height !== prevHeight) {
      state.trigger("resize");
      prevWidth = gl.canvas.width;
      prevHeight = gl.canvas.height;
    }
    to = [screen.zoom, screen.center[0], screen.center[1], screen.offset[0], screen.offset[1], screen.rotation[0], screen.rotation[1], screen.rotation[2], screen.orientation];
    if (duration) {
      from = [gl.screen.zoom, gl.screen.center[0], gl.screen.center[1], gl.screen.offset[0], gl.screen.offset[1], gl.screen.rotation[0], gl.screen.rotation[1], gl.screen.rotation[2], gl.screen.orientation];
      stopAnimation();
      return stopAnimation = utils.animate(from, to, duration, function(val) {
        return update(val);
      }, done);
    } else {
      update(to);
      if (typeof done === "function") {
        return done();
      }
    }
  };
})();

exports.loadScreenMatrix = loadScreenMatrix = function() {
  var rotation, rx, ry, sx, sy;
  if (gl.cropMode) {
    sx = Math.round(gl.texture.width / 2 * gl.screen.zoom / gl.cropScale);
    sy = Math.round(gl.texture.height / 2 * gl.screen.zoom / gl.cropScale);
    rotation = gl.rotation + gl.screen.rotation[2] + gl.rotate90 * 90;
    rx = gl.screen.rotation[0];
    ry = gl.screen.rotation[1];
  } else {
    sx = Math.round(gl.crop[2] / 2 * gl.screen.zoom);
    sy = Math.round(gl.crop[3] / 2 * gl.screen.zoom);
  }
  gl.context.loadIdentity();
  gl.context.loadMatrix(gl.perspectiveMatrix);
  gl.context.rotate(-gl.screen.orientation, 0, 0, 1);
  gl.context.translate(gl.screen.center[0], -gl.screen.center[1], 0);
  gl.context.translate(-gl.screen.offset[0], -gl.screen.offset[1], -1 / gl.fov);
  if (gl.cropMode) {
    gl.context.rotate(-rotation, 0, 0, 1);
  }
  gl.context.scale(sx, sy, 1);
  if (gl.cropMode) {
    gl.context.rotate(rx, 1, 0, 0);
    gl.context.rotate(ry, 0, 1, 0);
  }
  gl.screenMatrix = gl.screenMatrix || new GL.Matrix();
  for (var i = 0; i < 16; i++) {
    gl.screenMatrix.m[i] = gl.context.modelviewMatrix.m[i];
  };
  return gl.screenMatrix;
};

exports.renderCached = renderScreen = function(showOriginal, texture) {
  if (texture == null) {
    texture = gl.screenTexture;
  }
  if (showOriginal) {
    compositor.update();
    gl.context.loadIdentity();
    gl.context.loadMatrix(gl.matrix);
    if (gl.textures.length) {
      filters.multitexture.render(null, gl.writableTexture);
    } else {
      filters.basic.render(gl.texture, gl.writableTexture);
    }
  }
  gl.context.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  loadScreenMatrix();
  clear();
  enableBlend(true);
  if (showOriginal) {
    filters.screen.render(gl.writableTexture);
  } else {
    filters.screen.render(texture);
  }
  enableBlend(false);
  return state.trigger("render");
};

exports.clear = clear = function() {
  return gl.context.clear(gl.context.COLOR_BUFFER_BIT);
};

exports.renderCacheTexture = function() {
  var height, ref, scale, viewHeight, viewWidth, width;
  renderBlurTexture();
  logTime("cache texture");
  if (gl.cropMode) {
    ref = gl.texture, width = ref.width, height = ref.height;
  } else {
    width = gl.crop[2];
    height = gl.crop[3];
  }
  viewWidth = gl.canvas.width;
  viewHeight = gl.canvas.height;
  scale = Math.min(viewWidth / width, viewHeight / height);
  width = width * scale >> 0;
  height = height * scale >> 0;
  resizeTexture(gl.cacheTexture, width, height);
  compositor.updateForOutput(width, height, false, !gl.cropMode);
  render(false, gl.cacheTexture);
  compositor.update();
  state.changed = false;
  return logTimeEnd("cache texture");
};

exports.renderOutput = function(width, height, settings, watermark, resize, pixels) {
  var buffer, ch, channels, chunk, coords, crop, cropH, cropHeight, cropMode, cropW, cropWidth, cw, h, matrix, matrixInv, newSettings, offsets, prevSettings, renderChunk, scale, w, writer, zoom;
  if (resize == null) {
    resize = "stretch";
  }
  zoom = gl.zoom;
  cropMode = gl.cropMode;
  matrix = gl.matrix;
  matrixInv = gl.matrixInv;
  gl.watermarkOptions.enabled = watermark;
  gl.zoom = Math.max(width, height) / Math.max(gl.texture.width, gl.texture.height);
  gl.cropMode = false;
  gl.matrix = new GL.Matrix();
  gl.matrixInv = new GL.Matrix();
  if (settings) {
    prevSettings = utils.clone(state.currentState);
    newSettings = utils.assign(state.defaults, settings);
    crop = newSettings.crop;
    state.set(newSettings);
  } else {
    crop = state.currentState.crop.slice(0);
  }
  if (resize === "none") {
    width = gl.crop[2] >> 0;
    height = gl.crop[3] >> 0;
  } else if (resize === "stretch") {
    width = (width || gl.crop[2]) >> 0;
    height = (height || gl.crop[3]) >> 0;
  } else if (resize === "fit") {
    scale = Math.min(width / gl.crop[2], height / gl.crop[3]);
    width = gl.crop[2] * scale >> 0;
    height = gl.crop[3] * scale >> 0;
  } else if (resize === "fill") {
    scale = Math.max(width / gl.crop[2], height / gl.crop[3]);
    cropWidth = gl.crop[2] * scale >> 0;
    cropHeight = gl.crop[3] * scale >> 0;
    cropW = crop[2] * (width / cropWidth);
    cropH = crop[3] * (height / cropHeight);
    crop = [crop[0] + (crop[2] - cropW) / 2, crop[1] + (crop[3] - cropH) / 2, cropW, cropH];
  }
  state.set({
    crop: crop
  });
  if (width > gl.maxTextureSize || height > gl.maxTextureSize) {
    w = width >> 1;
    h = height >> 1;
    width = w * 2;
    height = h * 2;
    cw = gl.crop[2] >> 1;
    ch = gl.crop[3] >> 1;
    offsets = [[0, 0], [w, 0], [0, h], [w, h]];
    coords = [[0, 0, 0.5, 0.5], [cw, 0, 0.5, 0.5], [0, ch, 0.5, 0.5], [cw, ch, 0.5, 0.5]];
    chunk = 0;
    channels = 3;
    buffer = new Uint8Array(w * h * 4);
    pixels = pixels || new Uint8Array(width * height * 3);
    writer = function(data, next) {
      var colOffset, index, rowOffset;
      colOffset = offsets[chunk][0];
      rowOffset = offsets[chunk][1];
      if (colOffset > 0) {
        colOffset = width - colOffset;
      }
      if (rowOffset > 0) {
        rowOffset = height - rowOffset;
      }
      index = (rowOffset * width + colOffset) * channels;
      
        var row, col, i = 0;
        for (row = 0; row < h; row++) {
          for (col = 0; col < w; col++) {
            pixels[index] = data[i];
            pixels[index+1] = data[i+1];
            pixels[index+2] = data[i+2];
            index += channels;
            i += 4
          }
          index += (width - w) * channels
        }
      ;
      chunk++;
      return next();
    };
    renderChunk = function(i) {
      if (i >= coords.length) {
        buffer = null;
        return;
      }
      compositor.updateForOutput(w, h, true, true, coords[i]);
      render(false);
      buffer = readTexture(gl.readableTexture, buffer);
      return writer(buffer, function() {
        return renderChunk(i + 1);
      });
    };
    renderChunk(0);
  } else {
    compositor.updateForOutput(width, height, true);
    render(false);
    pixels = readTexture(gl.readableTexture, pixels);
  }
  if (settings) {
    state.set(prevSettings);
  }
  gl.zoom = zoom;
  gl.cropMode = cropMode;
  gl.watermarkOptions.enabled = false;
  gl.matrix = matrix;
  gl.matrixInv = matrixInv;
  return {
    data: pixels,
    width: width,
    height: height
  };
};


},{"./compositor.coffee":364,"./context.coffee":365,"./filters.coffee":367,"./histogram.coffee":368,"./state.coffee":376,"./utils.coffee":378}],374:[function(require,module,exports){
exports['basic'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\n\r\nvoid main() {\r\n	gl_FragColor = texture2D(texture, coord.xy);\r\n}';
exports['blend_modes'] = 'varying vec4 coord;\r\nvarying vec4 backgroundCoords;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D layerTexture;\r\nuniform vec4 colorOverlay;\r\nuniform float opacity;\r\nuniform float blendMode;\r\n\r\nfloat sat(in vec3 c){\r\n	float n = min(min(c.r, c.g), c.b);\r\n	float x = max(max(c.r, c.g), c.b);\r\n\r\n	return x - n;\r\n}\r\n\r\nvec3 setSat(in vec3 c, in float s){\r\n	float cmin = min(min(c.r, c.g), c.b);\r\n	float cmax = max(max(c.r, c.g), c.b);\r\n\r\n	vec3 res = vec3(0.0);\r\n\r\n	if (cmax > cmin) {\r\n\r\n		if (c.r == cmin && c.b == cmax) { // R min G mid B max\r\n			res.r = 0.0;\r\n			res.g = ((c.g-cmin)*s) / (cmax-cmin);\r\n			res.b = s;\r\n		}\r\n		else if (c.r == cmin && c.g == cmax) { // R min B mid G max\r\n			res.r = 0.0;\r\n			res.b = ((c.b-cmin)*s) / (cmax-cmin);\r\n			res.g = s;\r\n		}\r\n		else if (c.g == cmin && c.b == cmax) { // G min R mid B max\r\n			res.g = 0.0;\r\n			res.r = ((c.r-cmin)*s) / (cmax-cmin);\r\n			res.b = s;\r\n		}\r\n		else if (c.g == cmin && c.r == cmax) { // G min B mid R max\r\n			res.g = 0.0;\r\n			res.b = ((c.b-cmin)*s) / (cmax-cmin);\r\n			res.r = s;\r\n		}\r\n		else if (c.b == cmin && c.r == cmax) { // B min G mid R max\r\n			res.b = 0.0;\r\n			res.g = ((c.g-cmin)*s) / (cmax-cmin);\r\n			res.r = s;\r\n		}\r\n		else { // B min R mid G max\r\n			res.b = 0.0;\r\n			res.r = ((c.r-cmin)*s) / (cmax-cmin);\r\n			res.g = s;\r\n		}\r\n\r\n	}\r\n\r\n	return res;\r\n}\r\n\r\nfloat luma(in vec3 color){\r\n	return dot(color, vec3(0.298839, 0.586811, 0.11435));\r\n}\r\n\r\nvec3 clipColor(in vec3 c) {\r\n	float l = luma(c);\r\n	float n = min(min(c.r, c.g), c.b);\r\n	float x = max(max(c.r, c.g), c.b);\r\n\r\n	if (n < 0.0) c = (c-l)*l / (l-n) + l;\r\n	if (x > 1.0) c = (c-l) * (1.0-l) / (x-l) + l;\r\n\r\n	return c;\r\n}\r\n\r\nvec3 setLuma(in vec3 c, in float l) {\r\n	float d = l - luma(c);\r\n\r\n	c.r = c.r + d;\r\n	c.g = c.g + d;\r\n	c.b = c.b + d;\r\n\r\n	return clipColor(c);\r\n}\r\n\r\nvec3 blendLighten(in vec3 base, in vec3 blend) {\r\n	return max(blend, base);\r\n}\r\n\r\nvec3 blendDarken(in vec3 base, in vec3 blend) {\r\n	return min(blend, base);\r\n}\r\n\r\nvec3 blendMultiply(in vec3 base, in vec3 blend) {\r\n  return base * blend;\r\n}\r\n\r\nvec3 blendDivide(in vec3 base, in vec3 blend) {\r\n  return base / blend;\r\n}\r\n\r\nvec3 blendAdd(in vec3 base, in vec3 blend) {\r\n  return min(base + blend, vec3(1.0));\r\n}\r\n\r\nvec3 blendSubtract(in vec3 base, in vec3 blend) {\r\n  return max(base + blend - vec3(1.0), vec3(0.0));\r\n}\r\n\r\nvec3 blendDifference(in vec3 base, in vec3 blend) {\r\n  return abs(base - blend);\r\n}\r\n\r\nvec3 blendExclusion(in vec3 base, in vec3 blend) {\r\n  return (base + blend - 2.0 * base * blend);\r\n}\r\n\r\nfloat blendScreen(in float base, in float blend) {\r\n	return 1.0-((1.0-base)*(1.0-blend));\r\n}\r\n\r\nvec3 blendScreen(in vec3 base, in vec3 blend) {\r\n	return vec3(blendScreen(base.r,blend.r),blendScreen(base.g,blend.g),blendScreen(base.b,blend.b));\r\n}\r\n\r\nfloat blendOverlay(in float base, in float blend) {\r\n	return base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend));\r\n}\r\n\r\nvec3 blendOverlay(in vec3 base, in vec3 blend) {\r\n	return vec3(blendOverlay(base.r,blend.r),blendOverlay(base.g,blend.g),blendOverlay(base.b,blend.b));\r\n}\r\n\r\nfloat blendSoftLight(in float base, in float blend) {\r\n	return (blend<0.5)?(2.0*base*blend+base*base*(1.0-2.0*blend)):(sqrt(base)*(2.0*blend-1.0)+2.0*base*(1.0-blend));\r\n}\r\n\r\nvec3 blendSoftLight(in vec3 base, in vec3 blend) {\r\n	return vec3(blendSoftLight(base.r,blend.r),blendSoftLight(base.g,blend.g),blendSoftLight(base.b,blend.b));\r\n}\r\n\r\nvec3 blendHardLight(in vec3 base, in vec3 blend) {\r\n	return blendOverlay(blend,base);\r\n}\r\n\r\nfloat blendColorDodge(in float base, in float blend) {\r\n	return (blend==1.0)?blend:min(base/(1.0-blend),1.0);\r\n}\r\n\r\nvec3 blendColorDodge(in vec3 base, in vec3 blend) {\r\n	return vec3(blendColorDodge(base.r,blend.r),blendColorDodge(base.g,blend.g),blendColorDodge(base.b,blend.b));\r\n}\r\n\r\nfloat blendColorBurn(in float base, in float blend) {\r\n	return (blend==0.0)?blend:max((1.0-((1.0-base)/blend)),0.0);\r\n}\r\n\r\nvec3 blendColorBurn(in vec3 base, in vec3 blend) {\r\n	return vec3(blendColorBurn(base.r,blend.r),blendColorBurn(base.g,blend.g),blendColorBurn(base.b,blend.b));\r\n}\r\n\r\nfloat blendLinearLight(in float base, in float blend) {\r\n	return blend<0.5?max(base+(2.0*blend)-1.0, 0.0):min(base+(2.0*(blend-0.5)), 1.0);\r\n}\r\n\r\nvec3 blendLinearLight(in vec3 base, in vec3 blend) {\r\n	return vec3(blendLinearLight(base.r,blend.r),blendLinearLight(base.g,blend.g),blendLinearLight(base.b,blend.b));\r\n}\r\n\r\nfloat blendVividLight(in float base, in float blend) {\r\n	return (blend<0.5)?blendColorBurn(base,(2.0*blend)):blendColorDodge(base,(2.0*(blend-0.5)));\r\n}\r\n\r\nvec3 blendVividLight(in vec3 base, in vec3 blend) {\r\n	return vec3(blendVividLight(base.r,blend.r),blendVividLight(base.g,blend.g),blendVividLight(base.b,blend.b));\r\n}\r\n\r\nvec3 blendHue(in vec3 base, in vec3 blend) {\r\n  return setLuma(setSat(blend, sat(base)), luma(base));\r\n}\r\n\r\nvec3 blendSaturation(in vec3 base, in vec3 blend) {\r\n  return setLuma(setSat(base, sat(blend)), luma(base));\r\n}\r\n\r\nvec3 blendColor(in vec3 base, in vec3 blend) {\r\n  return setLuma(blend, luma(base));\r\n}\r\n\r\nvec3 blendLuminosity(in vec3 base, in vec3 blend) {\r\n  return setLuma(base, luma(blend));\r\n}\r\n\r\nvoid main() {\r\n\r\n  lowp vec3 base = texture2D(texture, backgroundCoords.xy).rgb;\r\n  lowp vec4 layer = texture2D(layerTexture, coord.xy);\r\n  lowp vec3 blend = mix(layer.rgb, colorOverlay.rgb, colorOverlay.a);\r\n\r\n\r\n       if (blendMode == 1.0) blend = blendDarken(base, blend);\r\n  else if (blendMode == 2.0) blend = blendMultiply(base, blend);\r\n  else if (blendMode == 3.0) blend = blendColorBurn(base, blend);\r\n  else if (blendMode == 4.0) blend = blendSubtract(base, blend); // linear burn\r\n  else if (blendMode == 5.0) blend = blendLighten(base, blend);\r\n  else if (blendMode == 6.0) blend = blendScreen(base, blend);\r\n  else if (blendMode == 7.0) blend = blendColorDodge(base, blend);\r\n  else if (blendMode == 8.0) blend = blendAdd(base, blend); // linear dodge\r\n  else if (blendMode == 9.0) blend = blendOverlay(base, blend);\r\n  else if (blendMode == 10.0) blend = blendSoftLight(base, blend);\r\n  else if (blendMode == 11.0) blend = blendHardLight(base, blend);\r\n  else if (blendMode == 12.0) blend = blendVividLight(base, blend);\r\n  else if (blendMode == 13.0) blend = blendLinearLight(base, blend);\r\n  else if (blendMode == 14.0) blend = blendDifference(base, blend);\r\n  else if (blendMode == 15.0) blend = blendExclusion(base, blend);\r\n  else if (blendMode == 16.0) blend = blendSubtract(base, blend);\r\n  else if (blendMode == 17.0) blend = blendDivide(base, blend);\r\n  else if (blendMode == 18.0) blend = blendHue(base, blend);\r\n  else if (blendMode == 19.0) blend = blendSaturation(base, blend);\r\n  else if (blendMode == 20.0) blend = blendColor(base, blend);\r\n  else if (blendMode == 21.0) blend = blendLuminosity(base, blend);\r\n  else if (blendMode == 22.0) blend = base; // passthrough\r\n\r\n	gl_FragColor = vec4(blend, layer.a * opacity);\r\n}';
exports['blur'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform vec2 delta;\r\n\r\nfloat random(vec3 scale, float seed) {\r\n  /* use the fragment position for a different seed per-pixel */\r\n  return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\r\n}\r\n\r\nvoid main() {\r\n  /* randomize the lookup values to hide the fixed number of samples */\r\n  float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);\r\n\r\n  vec4 center = texture2D(texture, coord.xy);\r\n\r\n  float percent, weight, total = 0.0;\r\n  vec4 col, color = vec4(0.0);\r\n\r\n  for (float t = -20.0; t <= 20.0; t++) {\r\n    percent = (t + offset - 0.5) / 20.0;\r\n    col = texture2D(texture, coord.xy + (delta * center.a) * percent);\r\n    weight = (1.0 - abs(percent)) * max(col.a, 0.001);\r\n    color += col * weight;\r\n    total += weight;\r\n  }\r\n\r\n  gl_FragColor = vec4(color / total);\r\n}';
exports['blur_brush'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform vec2 delta;\r\n\r\nconst float radius = 7.0;\r\n\r\nvoid main() {\r\n\r\n  vec4 center = texture2D(texture, coord.xy);\r\n\r\n  float percent, weight, total = 0.0;\r\n  vec4 col, color = vec4(0.0);\r\n\r\n  for (float t = -radius; t <= radius; t++) {\r\n    col = texture2D(texture, coord.xy + t * delta) * 2.0;\r\n    weight = smoothstep(1.0, 0.0, abs(t / 8.0));\r\n    color += col * weight;\r\n    total += weight;\r\n  }\r\n\r\n  gl_FragColor = color / total;\r\n}';
exports['blur_mask'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform vec2 delta;\r\n\r\nfloat random(vec3 scale, float seed) {\r\n  /* use the fragment position for a different seed per-pixel */\r\n  return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\r\n}\r\n\r\nvoid main() {\r\n  /* randomize the lookup values to hide the fixed number of samples */\r\n  float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);\r\n\r\n  float percent, weight, total = 0.0;\r\n  lowp vec4 col, color = vec4(0.0);\r\n  const float radius = 10.0;\r\n\r\n  for (float t = -radius; t <= radius; t++) {\r\n    percent = (t + offset - 0.5) / radius;\r\n    col = vec4(\r\n      texture2D(texture, coord.xy + delta * 0.7 * percent).rgb,\r\n      texture2D(texture, coord.xy + delta * 2.0 * percent).a\r\n    );\r\n    weight = 1.0 - abs(percent);\r\n    color += col * weight;\r\n    total += weight;\r\n  }\r\n\r\n  gl_FragColor = vec4(color / total);\r\n}';
exports['blur_nlm1'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform vec2 delta;\r\n\r\nconst float sigma2_color = 25.0;\r\n\r\nfloat random(in vec3 scale, in float seed) {\r\n  /* use the fragment position for a different seed per-pixel */\r\n  return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\r\n}\r\nfloat Lum(vec3 c){\r\n  return 0.299*c.r + 0.587*c.g + 0.114*c.b;\r\n}\r\n\r\nvoid main() {\r\n\r\n  vec3 center_color = texture2D(texture, coord.xy).rgb;\r\n  vec3 color = center_color;\r\n  float total = 1.0;\r\n  float center_lum = Lum(center_color);\r\n\r\n  /* randomize the lookup values to hide the fixed number of samples */\r\n  float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);\r\n\r\n  for (float t = -20.0; t <= 20.0; t++) {\r\n    float percent = (t + offset - 0.5) / 20.0;\r\n    float weight = 1.0 - abs(percent);\r\n    vec3 tmp_color = texture2D(texture, coord.xy + delta * percent).rgb;\r\n    float diff_color=Lum(tmp_color)-center_lum;\r\n    weight *= exp(-min(diff_color*diff_color*sigma2_color,10.0));\r\n    color += tmp_color * weight;\r\n    total += weight;\r\n  }\r\n\r\n  vec4 res = vec4(color/total, total/20.0);\r\n  gl_FragColor = clamp(res,0.0,1.0);\r\n}\r\n';
exports['blur_nlm2'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform vec2 delta;\r\n\r\nconst float sigma2_color = 25.0;\r\n\r\nfloat random(in vec3 scale, in float seed) {\r\n  /* use the fragment position for a different seed per-pixel */\r\n  return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\r\n}\r\nfloat Lum(vec3 c){\r\n  return 0.299*c.r + 0.587*c.g + 0.114*c.b;\r\n}\r\n\r\nvoid main() {\r\n\r\n  vec3 center_color = texture2D(texture, coord.xy).rgb;\r\n  vec3 color = center_color;\r\n  float total = 1.0;\r\n  float center_lum = Lum(center_color);\r\n\r\n  /* randomize the lookup values to hide the fixed number of samples */\r\n  float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);\r\n\r\n  for (float t = -20.0; t <= 20.0; t++) {\r\n    float percent = (t + offset - 0.5) / 20.0;\r\n    float weight = 1.0 - abs(percent);\r\n    vec4 tmp_sample=texture2D(texture, coord.xy + delta * percent);\r\n    vec3 tmp_color=tmp_sample.rgb;\r\n    float diff_color=Lum(tmp_color)-center_lum;\r\n    float pre_weight = tmp_sample.a;\r\n    weight *= pre_weight / 20.0 * exp(-min(diff_color*diff_color*sigma2_color,10.0));\r\n    color += tmp_color * weight;\r\n    total += weight;\r\n  }\r\n\r\n  vec4 res = vec4(color/total, 1.0);\r\n  gl_FragColor = clamp(res,0.0,1.0);\r\n}\r\n';
exports['brush_composite'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D paintTexture;\r\nuniform sampler2D texture;\r\nuniform float blend;\r\n\r\nvoid main() {\r\n  vec4 brushMap = texture2D(paintTexture, coord.xy);\r\n  vec4 brushStroke = texture2D(texture, coord.xy);\r\n\r\n	gl_FragColor = brushMap + brushStroke * blend;\r\n}';
exports['clarity'] = 'varying vec4 coord;\r\nvarying vec2 distortionCoord;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D smoothMap;\r\nuniform sampler2D blurMap;\r\n\r\nuniform float clarity;\r\n\r\nconst mat3 matRGBtoROMM = mat3(0.5293459296226501, 0.3300727903842926, 0.14058130979537964, 0.09837432950735092, 0.8734610080718994, 0.028164653107523918, 0.01688321679830551, 0.11767247319221497, 0.8654443025588989);\r\nconst mat3 matROMMtoRGB = mat3(2.0340757369995117, -0.727334201335907, -0.3067416846752167, -0.22881317138671875, 1.2317301034927368, -0.0029169507324695587, -0.008569774217903614, -0.1532866358757019, 1.1618564128875732);\r\n\r\n\r\nfloat luma(in vec3 color){\r\n	return dot(color, vec3(0.298839, 0.586811, 0.11435));\r\n}\r\n\r\nvec3 rgb2hsv(in vec3 c) {\r\n    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\r\n    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\r\n    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\r\n\r\n    float d = q.x - min(q.w, q.y);\r\n    float e = 1.0e-10;\r\n    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\r\n}\r\n\r\nvec3 hsv2rgb(in vec3 c) {\r\n    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\r\n    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\r\n    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\r\n}\r\n\r\nvec3 setHue(in vec3 res, in vec3 base) {\r\n  vec3 base_hsv = rgb2hsv(base);\r\n  vec3 res_hsv = rgb2hsv(res);\r\n  return hsv2rgb(vec3(base_hsv.x, res_hsv.y, res_hsv.z));\r\n}\r\n\r\nvec3 blendOverlay(in vec3 base, in vec3 blend){\r\n  return mix(2.0 * base * blend, 1.0 - 2.0 * (1.0 - base) * (1.0 - blend), step(0.5, base));\r\n}\r\n\r\nvoid main() {\r\n  lowp vec4 col = texture2D(texture, coord.xy);\r\n  lowp vec3 smoothTexture = texture2D(smoothMap, distortionCoord).rgb;\r\n  lowp vec4 blurTexture = texture2D(blurMap, distortionCoord);\r\n\r\n  vec3 map = mix(smoothTexture, blurTexture.rgb, min(blurTexture.a / 0.05, 1.0));\r\n\r\n  float intensity = clarity > 0.0 ? clarity * 3.0 : clarity * 2.0;\r\n  float low_pass = (luma(col.rgb) + (1.0 - luma(map))) / 2.0;\r\n\r\n	vec3 base = col.rgb * matRGBtoROMM;\r\n	vec3 blend = vec3(clamp(mix(0.5, low_pass, intensity * col.a), 0.0, 1.0));\r\n\r\n  vec3 res = blendOverlay(base, blend);\r\n\r\n  res = setHue(res, base);\r\n\r\n  res = res * matROMMtoRGB;\r\n\r\n  gl_FragColor = vec4(res, col.a);\r\n\r\n}\r\n';
exports['composite_vertex'] = 'varying vec4 coord;\r\nvarying vec4 compositeCoord;\r\n\r\nuniform mat4 viewMatrix;\r\n\r\nvoid main() {\r\n  compositeCoord = gl_TexCoord;\r\n  coord = viewMatrix * gl_Vertex * 0.5 + 0.5;\r\n  gl_Position = viewMatrix * gl_Vertex;\r\n}\r\n';
exports['contrast'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform float contrast;\r\n\r\nconst mat3 matRGBtoROMM = mat3(0.5293459296226501, 0.3300727903842926, 0.14058130979537964, 0.09837432950735092, 0.8734610080718994, 0.028164653107523918, 0.01688321679830551, 0.11767247319221497, 0.8654443025588989);\r\nconst mat3 matROMMtoRGB = mat3(2.0340757369995117, -0.727334201335907, -0.3067416846752167, -0.22881317138671875, 1.2317301034927368, -0.0029169507324695587, -0.008569774217903614, -0.1532866358757019, 1.1618564128875732);\r\n\r\nvoid main() {\r\n  lowp vec4 col = texture2D(texture, coord.xy);\r\n\r\n	float amount = (contrast < 0.0) ? contrast / 2.0 : contrast;\r\n\r\n	vec3 base = col.rgb * matRGBtoROMM;\r\n  vec3 blend = mix(vec3(0.5), base, amount * col.a);\r\n  // overlay blend\r\n  vec3 res = mix(2.0 * base * blend, 1.0 - 2.0 * (1.0 - base) * (1.0 - blend), step(0.5, base));\r\n\r\n  res = res * matROMMtoRGB;\r\n\r\n	gl_FragColor = vec4(res, col.a);\r\n\r\n}\r\n';
exports['curves'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D map;\r\n\r\nvoid main() {\r\n\r\n  lowp vec4 color = texture2D(texture, coord.xy);\r\n  color.r = texture2D(map, vec2(texture2D(map, vec2(color.r)).r)).a;\r\n  color.g = texture2D(map, vec2(texture2D(map, vec2(color.g)).g)).a;\r\n  color.b = texture2D(map, vec2(texture2D(map, vec2(color.b)).b)).a;\r\n\r\n  gl_FragColor = color;\r\n  \r\n}';
exports['dehaze'] = '// fragment_dehaze.cpp\r\n// usage: use during rendering, correct dehazing using transition map\r\n// input: uniform sampler2D texture (loaded image)\r\n// input: uniform sampler2D map_t (generated from shader dehazemap2.cpp)\r\n// input: uniform float dehaze_scale from -1.0 to 1.0\r\n// output: dehazed image\r\n// ref: http://research.microsoft.com/en-us/um/people/jiansun/papers/dehaze_cvpr2009.pdf\r\n// https://www.shadertoy.com/view/4tXXz4\r\n\r\n\r\nvarying vec4 coord;\r\nvarying vec2 distortionCoord;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D blurMap;\r\nuniform sampler2D dehazeMap;\r\n\r\nuniform float dehaze; // -1.0~1.0\r\nuniform vec3 average;\r\n\r\nconst float mix_scale = 0.5;\r\n\r\nvoid main() {\r\n  lowp vec3 base = texture2D(texture, coord.xy).rgb;\r\n  lowp vec4 blurTexture = texture2D(blurMap, distortionCoord);\r\n\r\n  float opacity = 1.0 - min(blurTexture.a / 0.05, 1.0);\r\n\r\n  // transmission\r\n  float transmission = texture2D(dehazeMap, distortionCoord).a;\r\n\r\n  // correction\r\n  vec3 dehaze_color = vec3(1.0);\r\n//  dehaze_color = mix(dehaze_color, average, mix_scale);\r\n  float dehaze_adjust = clamp(1.0/transmission, 1.0, 5.0) - 1.0;\r\n  dehaze_adjust = mix(dehaze_adjust, pow(dehaze_adjust, 0.2), step(1.0, dehaze_adjust)) + 1.0;\r\n  vec3 J = clamp(((base-dehaze_color) * dehaze_adjust + dehaze_color), 0.0, 1.0);\r\n\r\n\r\n  // dehaze (-1,1)->d(0,1)\r\n  float d = 1.0 - dehaze;\r\n  float mixv = pow(transmission, d);\r\n  vec3 res = mix(dehaze_color, J, mixv);\r\n\r\n  // mix\r\n  res = mix(base, res, abs(dehaze) * opacity);\r\n\r\n  gl_FragColor = vec4(res, 1.0);\r\n}\r\n\r\n';
exports['dehaze_map_2'] = '// fragment_dehazemap2.cpp\r\n// usage: use before rendering to generate map, smooth the transmission map based on prior \r\n// input: uniform sampler2D texture (generated from shader dehazemap1.cpp)\r\n// output: gray image of transmission map(r=g=b=a), later I may want to use the redundant channels to save more information.\r\n// ref: http://research.microsoft.com/en-us/um/people/jiansun/papers/dehaze_cvpr2009.pdf\r\n\r\nvarying vec4 coord;\r\nuniform sampler2D texture;\r\n\r\nfloat random(in vec3 scale, in float seed) {\r\n  /* use the fragment position for a different seed per-pixel */\r\n  return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\r\n}\r\n\r\nconst int radius = 7;\r\nconst float sigma_s = 0.02;\r\nconst float sigma_c = 0.5 * 5.0 * 5.0;\r\nconst float wsize = 0.1 / float(radius * 2);\r\nconst float sigma_i = 0.5 * wsize * wsize / sigma_s / sigma_s;\r\n\r\n\r\nvoid main() {\r\n\r\n//  const int k_size = radius * 2 + 1;\r\n//  float kernel[k_size];\r\n//  for (int j = 0; j <= k_size; j++) {\r\n//    kernel[k_size+j] = kernel[k_size-j] = exp(-float(j * j) * sigma_i);\r\n//  }\r\n\r\n  vec2 uv;\r\n  vec3 diff;\r\n  float weight;\r\n  vec4 res = vec4(0.0);\r\n  float res_w = 0.0;\r\n  float offset = random(vec3(151.7182, 12.9898, 78.233), 0.0);\r\n  lowp vec3 center_c = texture2D(texture, coord.xy).rgb;\r\n  lowp vec4 tmp;\r\n\r\n  for (int i = -radius; i <= radius; i++) {\r\n      for (int j = -radius; j<= radius; j++) {\r\n          uv = coord.xy + (vec2(float(i), float(j)) + offset - 0.5) * wsize;\r\n          tmp = texture2D(texture, uv);\r\n          diff = tmp.rgb - center_c;\r\n          weight = exp(-min(float(i * i + j * j) * sigma_i, 10.0));\r\n//          weight = kernel[radius+i] * kernel[radius+j];\r\n          weight *= exp(-min(dot(diff, diff) * sigma_c, 10.0));\r\n          res += tmp * weight;\r\n          res_w += weight;\r\n      }\r\n  }\r\n\r\n  res = res / res_w;\r\n//  res.a = 1.0;//clamp(res.a, 1e-3, 1.0 - 1e-3);\r\n\r\n\r\n  // contrast\r\n//  vec3 base = res.rgb;\r\n//  vec3 blend = mix(vec3(0.5), base, 0.15);\r\n//  res.rgb = mix(2.0 * base * blend, 1.0 - 2.0 * (1.0 - base) * (1.0 - blend), step(0.5, base));\r\n\r\n  gl_FragColor = vec4(res);\r\n}\r\n\r\n';
exports['dehaze_map_min1'] = '// fragment_dehazemap1.cpp\r\n// usage: use before rendering to generate map, get transmission map prior from image\r\n// input: uniform sampler2D texture (image loaded)\r\n// output: rgb from image, alpha channel represent the transmission computed from darkchannel\r\n// ref: http://research.microsoft.com/en-us/um/people/jiansun/papers/dehaze_cvpr2009.pdf\r\n\r\nvarying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\n\r\nfloat random(in vec3 scale, in float seed) {\r\n    /* use the fragment position for a different seed per-pixel */\r\n    return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\r\n}\r\n\r\nconst float filter_window = 0.01;\r\nconst float sample_radius = 5.0;\r\nconst float wsize = filter_window/sample_radius/2.0;\r\nconst vec2 delta = vec2(0.9, -0.1);\r\n\r\nvoid main() {\r\n    vec2 uv;\r\n    float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);\r\n    lowp vec3 base = texture2D(texture, coord.xy).rgb;\r\n    lowp float min_color = 1.0;\r\n    lowp vec4 sample_neighbor;\r\n\r\n    for (int i = -5; i <= 5; i++) {\r\n        uv = coord.xy + (vec2(float(i)) * delta + offset - 0.5) * wsize;\r\n        sample_neighbor = texture2D(texture, uv);\r\n        min_color = min(min(min(\r\n          min_color,\r\n          sample_neighbor.r),\r\n          sample_neighbor.g),\r\n          sample_neighbor.b);\r\n    }\r\n\r\n    gl_FragColor = vec4(base, min_color);\r\n}\r\n\r\n\r\n';
exports['dehaze_map_min2'] = '// fragment_dehazemap1.cpp\r\n// usage: use before rendering to generate map, get transmission map prior from image\r\n// input: uniform sampler2D texture (image loaded)\r\n// output: rgb from image, alpha channel represent the transmission computed from darkchannel\r\n// ref: http://research.microsoft.com/en-us/um/people/jiansun/papers/dehaze_cvpr2009.pdf\r\n\r\nvarying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\n\r\nfloat random(in vec3 scale, in float seed) {\r\n    /* use the fragment position for a different seed per-pixel */\r\n    return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\r\n}\r\n\r\nconst float filter_window = 0.01;\r\nconst float sample_radius = 5.0;\r\nconst float wsize = filter_window/sample_radius/2.0;\r\nconst vec2 delta = vec2(0.1, 0.9);\r\n\r\nvoid main() {\r\n    vec2 uv;\r\n    float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);\r\n    lowp vec3 base = texture2D(texture, coord.xy).rgb;\r\n    lowp float min_color = 1.0;\r\n    lowp vec4 sample_neighbor;\r\n\r\n    for (int i = -5; i <= 5; i++) {\r\n        uv = coord.xy + (vec2(float(i)) * delta + offset - 0.5) * wsize;\r\n        sample_neighbor = texture2D(texture, uv);\r\n        min_color = min(min(min(min(\r\n          min_color,\r\n          sample_neighbor.r),\r\n          sample_neighbor.g),\r\n          sample_neighbor.b),\r\n          sample_neighbor.a);\r\n    }\r\n\r\n    lowp float res = clamp(1.0 - 0.95 * min_color, 1e-3, 1.0 - 1e-3);\r\n\r\n    gl_FragColor = vec4(base, res);\r\n}\r\n\r\n\r\n';
exports['denoise'] = 'varying vec4 coord;\r\nvarying vec2 distortionCoord;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D denoiseMap;\r\nuniform sampler2D blurMap;\r\nuniform sampler2D dehazeMap;\r\nuniform float color_denoise;\r\nuniform float luminance_denoise;\r\n\r\nfloat Lum(in vec3 color){\r\n	return dot(color, vec3(0.298839, 0.586811, 0.11435));\r\n}\r\n\r\nvec3 ClipColor(in vec3 c){\r\n  float l = Lum(c);\r\n  float n = min(min(c.r, c.g), c.b);\r\n  float x = max(max(c.r, c.g), c.b);\r\n\r\n  if (n < 0.0) c = (c-l)*l / (l-n) + l;\r\n  if (x > 1.0) c = (c-l) * (1.0-l) / (x-l) + l;\r\n\r\n  return c;\r\n}\r\n\r\nvec3 SetLum(in vec3 c, in float l){\r\n  float d = l - Lum(c);\r\n\r\n  return ClipColor(c + d);\r\n}\r\n\r\nvoid main() {\r\n	lowp vec3 col = texture2D(texture, coord.xy).rgb;\r\n  lowp vec4 denoiseTexture = texture2D(denoiseMap, distortionCoord);\r\n  lowp vec4 blurTexture = texture2D(blurMap, distortionCoord);\r\n  lowp vec4 dehazeTexture = texture2D(dehazeMap, distortionCoord);\r\n\r\n  // mask off blurred area from local adjustments\r\n  float localBlurMask = 1.0 - min(blurTexture.a / 0.05, 1.0);\r\n\r\n  // blend dehazeTexture with darker (noisier) of denoise map\r\n  float lumMask = exp(-3.0 * Lum(denoiseTexture.rgb));\r\n\r\n  // mask edges from luminance denoise (edges stored in alpha channel)\r\n  float luminance_blend = max(luminance_denoise - pow(denoiseTexture.a, 0.5), 0.0);\r\n  float color_blend = color_denoise;\r\n\r\n  vec4 lumMap = mix(denoiseTexture, dehazeTexture, luminance_denoise * lumMask);\r\n  vec4 colorMap = mix(denoiseTexture, dehazeTexture, color_denoise * lumMask);\r\n\r\n  // color denoise\r\n  col = mix(col, SetLum(colorMap.rgb, Lum(col)), color_denoise * localBlurMask);\r\n  // luminance denoise\r\n  col = mix(col, SetLum(col, Lum(lumMap.rgb)), luminance_blend * localBlurMask);\r\n  \r\n  gl_FragColor = vec4(col, 1.0);\r\n}\r\n';
exports['denoise_nlm'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform vec2 textureResolution;\r\n\r\nconst float sigma_color = 0.15;\r\nconst float sigma = 1.0/sigma_color;\r\nconst float sigma_c = 0.5 * sigma * sigma;\r\nconst float radius = 7.0;\r\nconst float wsize = radius * 2.0 + 1.0;\r\nconst float sigma_s = 0.5 * wsize * wsize;\r\n\r\nfloat luma(in vec3 color) {\r\n	return dot(color, vec3(0.298839, 0.586811, 0.11435));\r\n}\r\n\r\nfloat random(in vec3 scale, in float seed) {\r\n  /* use the fragment position for a different seed per-pixel */\r\n  return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\r\n}\r\n\r\nvoid main() {\r\n  vec2 sigma_spatial = 1.0 / (textureResolution);\r\n  float res_weight = 0.0;\r\n  vec3 res_color = vec3(0.0);\r\n  vec3 center_color = texture2D(texture, coord.xy).rgb;\r\n  float lum = luma(center_color);\r\n  float sigma_i = sigma_s * sigma_s * sigma_spatial.x * sigma_spatial.y;\r\n  float offset = 0.0;//(random(vec3(151.7182, 12.9898, 78.233), 0.0) - 0.5) * radius;\r\n\r\n  for (float i = -radius; i <= radius; i++) {\r\n    for (float j = -radius; j <= radius; j++) {\r\n      vec2 uv = coord.xy + (vec2(i, j)) * sigma_spatial;\r\n      vec3 tmp_color = texture2D(texture, uv).rgb;\r\n      vec3 diff = tmp_color - center_color;\r\n      float diff_color = luma(tmp_color) - lum;\r\n      float tmp_weight = exp(-min((i*i+j*j)*sigma_i,10.0));\r\n      tmp_weight *= exp(-min(diff_color * diff_color * sigma_c,10.0));\r\n      res_color += tmp_color * tmp_weight;\r\n      res_weight += tmp_weight;\r\n    }\r\n  }\r\n\r\n  gl_FragColor = vec4(res_color / res_weight, (1.0 / res_weight) * 2.0);\r\n}\r\n';
exports['denoise_nlm1'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform float delta;\r\nuniform float kernel[15];\r\n\r\nconst int radius = 7;\r\nconst float wsize = float(radius * 2 + 1);\r\nconst float sigma_color = 0.04;\r\nconst float sigma_c = 1.0 / sigma_color;\r\nconst float sigma = 0.5 * sigma_c * sigma_c;\r\n\r\nfloat luma(in vec3 color){\r\n	return dot(color, vec3(0.298839, 0.586811, 0.11435));\r\n}\r\n\r\nvoid main() {\r\n  float res_weight = 0.0;\r\n  vec3 res_color = vec3(0.0);\r\n  vec3 center_color = texture2D(texture, coord.xy).rgb;\r\n  float lum = luma(center_color);\r\n  float diff, weight;\r\n  vec3 tmp_color;\r\n\r\n  for (int i = -radius; i <= radius; i++) {\r\n      tmp_color = texture2D(texture, coord.xy + vec2(float(i) * delta, 0.0)).rgb;\r\n      diff = luma(tmp_color) - lum;\r\n      weight = kernel[radius+i] * exp(-min(diff * diff * sigma, 10.0));\r\n      res_color += tmp_color * weight;\r\n      res_weight += weight;\r\n  }\r\n\r\n  gl_FragColor = vec4(res_color / res_weight, res_weight / wsize);\r\n}\r\n';
exports['denoise_nlm2'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform float delta;\r\nuniform float kernel[15];\r\n\r\nconst int radius = 7;\r\nconst float wsize = float(radius * 2 + 1);\r\nconst float sigma_color = 0.04;\r\nconst float sigma_c = 1.0 / sigma_color;\r\nconst float sigma = 0.5 * sigma_c * sigma_c;\r\n\r\nfloat luma(in vec3 color){\r\n	return dot(color, vec3(0.298839, 0.586811, 0.11435));\r\n}\r\n\r\nvoid main() {\r\n  float res_weight = 0.0;\r\n  vec3 res_color = vec3(0.0);\r\n  vec3 center_color = texture2D(texture, coord.xy).rgb;\r\n  float lum = luma(center_color);\r\n  float diff, weight;\r\n  vec4 tmp_color;\r\n\r\n  for (int i = -radius; i <= radius; i++) {\r\n      tmp_color = texture2D(texture, coord.xy + vec2(0.0, float(i) * delta));\r\n      diff = luma(tmp_color.rgb) - lum;\r\n      weight = kernel[radius+i] * exp(-min(diff * diff * sigma,10.0));\r\n      res_color += tmp_color.rgb * (weight * tmp_color.a * wsize);\r\n      res_weight += weight * tmp_color.a * wsize;\r\n  }\r\n\r\n  gl_FragColor = vec4(res_color / res_weight, (1.0 / res_weight) * 2.0);\r\n}\r\n';
exports['diffuse'] = 'varying vec4 coord;\r\nvarying vec2 distortionCoord;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D smoothMap;\r\nuniform sampler2D blurMap;\r\n\r\nuniform float diffuse;\r\n\r\nfloat luma(in vec3 color){\r\n	return dot(color, vec3(0.298839, 0.586811, 0.11435));\r\n}\r\n\r\nvoid main() {\r\n  lowp vec3 col = texture2D(texture, coord.xy).rgb;\r\n  lowp vec3 smoothTexture = texture2D(smoothMap, distortionCoord).rgb;\r\n  lowp vec4 blurTexture = texture2D(blurMap, distortionCoord);\r\n\r\n  vec3 map = mix(smoothTexture, blurTexture.rgb, min(blurTexture.a / 0.05, 1.0));\r\n  vec3 diffuseMap = map / 2.0 + 0.5;\r\n  float mask = 1.0 - pow(luma(col), 2.72);\r\n  vec3 blend = mix(vec3(0.5), diffuseMap, diffuse * 2.0 * mask);\r\n  vec3 res = sqrt(col) * (2.0 * blend - 1.0) + 2.0 * col * (1.0 - blend);\r\n\r\n  gl_FragColor = vec4(res, 1.0);\r\n}\r\n';
exports['distortion'] = 'varying vec4 coord;\r\nvarying vec2 distortionCoord[3];\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D blurTexture;\r\nuniform lowp float blur_opacity;\r\n\r\nvoid main() {\r\n  lowp vec3 inputDistort = vec3(\r\n    texture2D(texture, distortionCoord[0]).r,\r\n    texture2D(texture, distortionCoord[1]).g,\r\n    texture2D(texture, distortionCoord[2]).b\r\n  );\r\n  lowp vec4 blur = texture2D(blurTexture, distortionCoord[1]);\r\n  lowp float mask = min(blur.a / 0.05, 1.0) * blur_opacity;\r\n  lowp vec3 res = mix(inputDistort, blur.rgb, mask);\r\n\r\n  gl_FragColor = vec4(res, 1.0);\r\n}\r\n';
exports['distortion_multitexture'] = 'varying vec4 coord;\r\nvarying vec2 distortionCoord[3];\r\n\r\nuniform sampler2D textures[4];\r\nuniform sampler2D blurTexture;\r\nuniform lowp float blur_opacity;\r\n\r\nvec4 lookupTexture(in vec2 coords) {\r\n  vec2 c = coords * 2.0;\r\n  vec2 f = floor(c);\r\n  int i = int(f.x + f.y * 2.0);\r\n  vec2 uv = c - f;\r\n  lowp vec4 col;\r\n\r\n	if (i == 0) {\r\n    col = texture2D(textures[0], uv);\r\n  } else if (i == 1) {\r\n    col = texture2D(textures[1], uv);\r\n  } else if (i == 2) {\r\n    col = texture2D(textures[2], uv);\r\n  } else /* if (i == 3) */ {\r\n    col = texture2D(textures[3], uv);\r\n  }\r\n\r\n	return col;\r\n}\r\n\r\nvoid main() {\r\n  lowp vec3 inputDistort = vec3(\r\n    lookupTexture(distortionCoord[0]).r,\r\n    lookupTexture(distortionCoord[1]).g,\r\n    lookupTexture(distortionCoord[2]).b\r\n  );\r\n  lowp vec4 blur = texture2D(blurTexture, distortionCoord[1]);\r\n  lowp float mask = min(blur.a / 0.05, 1.0) * blur_opacity;\r\n  lowp vec3 res = mix(inputDistort, blur.rgb, mask);\r\n\r\n  gl_FragColor = vec4(res, 1.0);\r\n}';
exports['distortion_vertex'] = 'varying vec4 coord;\r\nvarying vec4 compositeCoord;\r\nvarying vec2 distortionCoord;\r\n\r\nuniform mat4 viewMatrix;\r\nuniform float distortion_amount;\r\nuniform float distortion_horizontal;\r\nuniform float distortion_vertical;\r\nuniform vec2 imgSize;\r\n\r\nconst vec2 center = vec2(0.5);\r\nconst vec2 center_hv = vec2(0.0, 0.5);\r\nconst float barrel_size = 0.5625;\r\n\r\nvoid main() {\r\n  coord = viewMatrix * gl_Distortion * 0.5 + 0.5;\r\n\r\n  compositeCoord = gl_TexCoord;\r\n\r\n  float diag = length(imgSize);\r\n  vec2 perspective = 0.4 / diag * imgSize;\r\n  vec2 hv = vec2(distortion_horizontal, distortion_vertical);\r\n  vec2 inv = 1.0 - step(0.0, hv);\r\n  vec2 d, n;\r\n  float r, c;\r\n  float distortion = 1.0;\r\n  float zoom = 1.0;\r\n\r\n  vec2 coord_map = abs(inv - compositeCoord.xy);\r\n\r\n  if (distortion_horizontal != 0.0) {\r\n    n = coord_map - center_hv.xy;\r\n    d = 1.0 - abs(distortion_horizontal) * perspective;\r\n    n = mix(n, n * d, vec2(1.0, n.x));\r\n    coord_map = n + center_hv.xy;\r\n  }\r\n\r\n  if (distortion_vertical != 0.0) {\r\n    n = coord_map - center_hv.yx;\r\n    d = 1.0 - abs(distortion_vertical) * perspective;\r\n    n = mix(n, n * d, vec2(n.y, 1.0));\r\n    coord_map = n + center_hv.yx;\r\n  }\r\n\r\n  coord_map = abs(inv - coord_map) - center;\r\n\r\n  if (distortion_amount < 0.0) {\r\n    c = diag / (distortion_amount * -4.0);\r\n    r = max(length(coord_map * imgSize) / c, 0.0001);\r\n    distortion = atan(r)/r;\r\n    r = max(-0.5 * imgSize.x, -0.5 * imgSize.y) / c;\r\n    zoom = atan(r)/r;\r\n  }\r\n\r\n  if (distortion_amount > 0.0) {\r\n    r = dot(coord_map, coord_map) * barrel_size;\r\n    c = distortion_amount * 2.0;\r\n    distortion = 1.0 + r * c;\r\n    zoom = 1.0 + (0.5 * barrel_size) * c;\r\n  }\r\n\r\n  distortionCoord = distortion * coord_map / zoom + center;\r\n\r\n  gl_Position = viewMatrix * gl_Distortion;\r\n}\r\n';
exports['exposure'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform float exposure;\r\nuniform float gamma;\r\n\r\nconst float epsilon = 0.000001;\r\nconst float mx = 1.0 - epsilon;\r\n\r\nconst mat3 matRGBtoROMM = mat3(0.5293459296226501, 0.3300727903842926, 0.14058130979537964, 0.09837432950735092, 0.8734610080718994, 0.028164653107523918, 0.01688321679830551, 0.11767247319221497, 0.8654443025588989);\r\nconst mat3 matROMMtoRGB = mat3(2.0340757369995117, -0.727334201335907, -0.3067416846752167, -0.22881317138671875, 1.2317301034927368, -0.0029169507324695587, -0.008569774217903614, -0.1532866358757019, 1.1618564128875732);\r\n\r\nfloat ramp(in float t){\r\n    t *= 2.0;\r\n    if (t >= 1.0) {\r\n      t -= 1.0;\r\n      t = log(0.5) / log(0.5*(1.0-t) + 0.9332*t);\r\n    }\r\n    return clamp(t, 0.001, 10.0);\r\n}\r\n\r\nvec3 rgb2hsv(in vec3 c) {\r\n    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\r\n    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\r\n    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\r\n\r\n    float d = q.x - min(q.w, q.y);\r\n    float e = 1.0e-10;\r\n    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\r\n}\r\n\r\nvec3 hsv2rgb(in vec3 c) {\r\n    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\r\n    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\r\n    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\r\n}\r\n\r\nvec3 setHue(in vec3 res, in vec3 base) {\r\n  vec3 hsv = rgb2hsv(base);\r\n  vec3 res_hsv = rgb2hsv(res);\r\n  return hsv2rgb(vec3(hsv.x, res_hsv.y, res_hsv.z));\r\n}\r\n\r\nvoid main() {\r\n  lowp vec4 col = texture2D(texture, coord.xy);\r\n\r\n  vec3 base = col.rgb * matRGBtoROMM;\r\n\r\n  float a = abs(exposure) * col.a + epsilon;\r\n  float v = pow(2.0, a*2.0+1.0)-2.0;\r\n  float m = mx - exp(-v);\r\n  vec3 res = (exposure > 0.0) ? (1.0 - exp(-v*base)) / m : log(1.0-base*m) / -v;\r\n  res = mix(base, res, min(a*100.0, 1.0));\r\n  res = setHue(res, base);\r\n  res = pow(res, vec3(ramp(1.0 - (gamma + 1.0) / 2.0)));\r\n\r\n  res = res * matROMMtoRGB;\r\n\r\n	gl_FragColor = vec4(res, col.a);\r\n\r\n}\r\n';
exports['face'] = '/* face enhancement shader */\r\nvarying vec4 coord;\r\nvarying vec2 distortionCoord;\r\n\r\n/* same set of textures used in clarity*/\r\nuniform sampler2D texture;\r\nuniform sampler2D smoothMap;\r\nuniform sampler2D blurMap;\r\nuniform sampler2D faceMask;\r\n\r\nuniform float skin_smoothness;\r\nuniform float skin_texture; // sharpening\r\nuniform float skin_tone;\r\nuniform float skin_hue;\r\nuniform float teeth_brightness;\r\nuniform float teeth_whitening;\r\nuniform float eyes_brightness;\r\nuniform float eyes_clarity;\r\nuniform float lips_brightness;\r\nuniform float lips_saturation;\r\nuniform vec3 skinColor;\r\nuniform vec2 imgSize;\r\n\r\n/* const */\r\nconst mat3 matRGBtoXYZ = mat3(0.4360747, 0.3850649, 0.1430804, 0.2225045, 0.7168786, 0.0606169, 0.0139322, 0.0971045, 0.7141733);\r\nconst mat3 matRGBtoROMM = mat3(0.5293459296226501, 0.3300727903842926, 0.14058130979537964, 0.09837432950735092, 0.8734610080718994, 0.028164653107523918, 0.01688321679830551, 0.11767247319221497, 0.8654443025588989);\r\nconst mat3 matROMMtoRGB = mat3(2.0340757369995117, -0.727334201335907, -0.3067416846752167, -0.22881317138671875, 1.2317301034927368, -0.0029169507324695587, -0.008569774217903614, -0.1532866358757019, 1.1618564128875732);\r\nconst float feather = 3.0; \r\nconst float epsilon = 0.000001;\r\nconst float mx = 1.0 - epsilon;\r\nconst float PI = 3.1415926535897932384626433832795;\r\nconst float kE = 216.0 / 24389.0;\r\nconst float kK = 24389.0 / 27.0;\r\nconst float kKE = 8.0;\r\nconst vec3 D50 = vec3(0.96422, 1.0, 0.82521);\r\nconst vec3 D65 = vec3(0.95047, 1.0, 1.08883);\r\n\r\nvec3 xyz2lab(in vec3 xyz, in vec3 refWhite) {\r\n	vec3 r = xyz / refWhite;\r\n\r\n	vec3 f = vec3(\r\n		(r.x > kE) ? pow(r.x, 1.0 / 3.0) : ((kK * r.x + 16.0) / 116.0),\r\n		(r.y > kE) ? pow(r.y, 1.0 / 3.0) : ((kK * r.y + 16.0) / 116.0),\r\n		(r.z > kE) ? pow(r.z, 1.0 / 3.0) : ((kK * r.z + 16.0) / 116.0)\r\n	);\r\n\r\n	return vec3(\r\n    116.0 * f.y - 16.0,\r\n    500.0 * (f.x - f.y),\r\n    200.0 * (f.y - f.z)\r\n  );\r\n}\r\n\r\nvec3 invCompand(in vec3 companded) {\r\n  return vec3(\r\n    (companded.r <= 0.04045) ? (companded.r / 12.92) : pow((companded.r + 0.055) / 1.055, 2.4),\r\n    (companded.g <= 0.04045) ? (companded.g / 12.92) : pow((companded.g + 0.055) / 1.055, 2.4),\r\n    (companded.b <= 0.04045) ? (companded.b / 12.92) : pow((companded.b + 0.055) / 1.055, 2.4)\r\n  );\r\n}\r\n\r\n/* color related function */\r\nfloat luma(in vec3 color){\r\n  return dot(color, vec3(0.298839, 0.586811, 0.11435));\r\n}\r\n\r\nvec3 rgb2hsv(in vec3 c) {\r\n  vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\r\n  vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\r\n  vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\r\n\r\n  float d = q.x - min(q.w, q.y);\r\n  float e = 1.0e-10;\r\n  return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\r\n}\r\n\r\nvec3 hsv2rgb(in vec3 c) {\r\n  vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\r\n  vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\r\n  return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\r\n}\r\n\r\nvec3 setHue(in vec3 res, in vec3 base) {\r\n  vec3 base_hsv = rgb2hsv(base);\r\n  vec3 res_hsv = rgb2hsv(res);\r\n  return hsv2rgb(vec3(base_hsv.x, res_hsv.y, res_hsv.z));\r\n}\r\n\r\nvec3 blendOverlay(in vec3 base, in vec3 blend){\r\n  return mix(2.0 * base * blend, 1.0 - 2.0 * (1.0 - base) * (1.0 - blend), step(0.5, base));\r\n}\r\n\r\nfloat ramp(in float t){\r\n    t *= 2.0;\r\n    if (t >= 1.0) {\r\n      t -= 1.0;\r\n      t = log(0.5) / log(0.5*(1.0-t) + 0.9332*t);\r\n    }\r\n    return clamp(t, 0.001, 10.0);\r\n}\r\n\r\nfloat gaussian(in float x) {\r\n  return exp(-PI*2.0*x*x);\r\n}\r\n\r\n/* wrap the exposure shader in the function */\r\nvec3 setExposure(in vec3 base, in float exposure, in float gamma){\r\n  float a = abs(exposure) * 1.0 + epsilon;\r\n  float v = pow(2.0, a*2.0+1.0)-2.0;\r\n  float m = mx - exp(-v);\r\n  vec3 res = (exposure > 0.0) ? (1.0 - exp(-v*base)) / m : log(1.0-base*m) / -v;\r\n  res = mix(base, res, min(a*100.0, 1.0));\r\n  res = setHue(res, base);\r\n  res = pow(res, vec3(ramp(1.0 - (gamma + 1.0) / 2.0)));\r\n  return res;\r\n}\r\n\r\nvoid main() {\r\n  // sample textures\r\n  lowp vec4 col = texture2D(texture, coord.xy);\r\n  lowp vec3 smoothTexture = texture2D(smoothMap, distortionCoord).rgb;\r\n  lowp vec4 blurTexture = texture2D(blurMap, distortionCoord);\r\n  lowp vec4 mask = texture2D(faceMask, distortionCoord);\r\n\r\n  vec3 res = col.rgb;\r\n\r\n  // color mask\r\n  vec3 colorMap = invCompand(smoothTexture);\r\n  vec3 sel = invCompand(skinColor);\r\n  vec3 mapLAB = xyz2lab(colorMap * matRGBtoXYZ, D50) / 100.0;\r\n  vec3 selLAB = xyz2lab(sel * matRGBtoXYZ, D50) / 100.0;\r\n  selLAB.x += 0.3;\r\n  vec3 colorMask = clamp(1.0 - abs(mapLAB - selLAB) / 0.5, 0.0, 1.0);\r\n\r\n  // lips = mask.r\r\n  // teeth = mask.g\r\n  // eyes = mask.b\r\n  // skin = face excluded by lips, teeth, and eyes\r\n//  float skin_mask = 1.0 - clamp(mask.r + mask.g + mask.b, 0.0, 1.0);\r\n  float skin_mask = mask.a;\r\n  // color mask based on skin color\r\n  float skin_color_mask = colorMask.x * colorMask.y * colorMask.z * 1.5;  \r\n  // possibly a toggle to select only the face\r\n  skin_mask *= skin_color_mask;\r\n  // skin_color_mask = mix(skin_color_mask, skin_mask, only_face);  \r\n\r\n  // skin_texture (sharpen)\r\n  lowp vec3 sum = res * 5.0;\r\n  vec2 texel = 1.0 / imgSize;\r\n  sum -= texture2D(texture, coord.xy + vec2(texel.x, 0.0)).rgb;\r\n  sum -= texture2D(texture, coord.xy + vec2(-texel.x, 0.0)).rgb;\r\n  sum -= texture2D(texture, coord.xy + vec2(0.0, -texel.y)).rgb;\r\n  sum -= texture2D(texture, coord.xy + vec2(0.0, texel.y)).rgb;\r\n  res = mix(res, sum, skin_texture * skin_mask);\r\n\r\n  // convert to ROMM\r\n  res = res * matRGBtoROMM;\r\n\r\n  // skin_smoothness (negative clarity), eye_clarity\r\n  vec3 map = mix(smoothTexture, blurTexture.rgb, min(blurTexture.a / 0.05, 1.0)) * matRGBtoROMM;\r\n  float clarity = -skin_smoothness * skin_mask + eyes_clarity * mask.b;\r\n  float intensity = clarity > 0.0 ? clarity * 4.0 : clarity * 2.0;\r\n  float low_pass = (luma(res) + (1.0 - luma(map))) * 0.5;\r\n  vec3 blend = vec3(clamp(mix(0.5, low_pass, intensity * col.a), 0.0, 1.0));\r\n  res = blendOverlay(res, blend);\r\n\r\n  // skin_tone, eyes_brightness, lips_brightness (exposure)\r\n  float exposure =\r\n    (eyes_brightness * mask.b * 0.8) +\r\n    (skin_tone * skin_color_mask * 0.3) +\r\n    (lips_brightness * mask.r * 0.3) +\r\n    (teeth_brightness * mask.g);\r\n  res = setExposure(res, exposure, 0.0);\r\n\r\n  // skin_hue (shift orange)\r\n  vec3 hsv = rgb2hsv(res);\r\n  float orangeMask = (hsv.x - 0.084) / 0.3;\r\n  float hueMask = gaussian(orangeMask) * min(1.0, hsv.y * 2.0);\r\n  // weighted with skin color mask\r\n  hsv.x += skin_hue * 0.05 * hueMask * skin_color_mask;\r\n\r\n  // teeth_whitening\r\n  float yellowMask = (hsv.x - 0.166) / 0.3;\r\n  hsv.y *= 1.0 - teeth_whitening * mask.g * gaussian(yellowMask);\r\n\r\n  // lips sat\r\n  hsv.y = hsv.y * (1.0 + lips_saturation * mask.r);\r\n\r\n  res = hsv2rgb(hsv);\r\n\r\n  // out\r\n  res = res * matROMMtoRGB;\r\n  // comment this for now for color masked skin-tone and skin-hue\r\n  // other adjustments are already masked interneally so I guess this is not necessary\r\n  //res = mix(col.rgb, res, mask.a);\r\n\r\n  // debug\r\n//  res = mix(res, mask.rgb, mask.a * 0.8);\r\n//  res = vec3(skin_mask);\r\n//  res = smoothTexture.rgb;\r\n\r\n  /* return */\r\n  gl_FragColor = vec4(res, col.a);\r\n}\r\n';
exports['fill'] = 'varying vec4 coord;\r\n\r\nuniform vec4 color;\r\nuniform sampler2D texture;\r\n\r\nvoid main() {\r\n  lowp vec4 base = texture2D(texture, coord.xy);\r\n  lowp vec3 res = mix(base.rgb, color.rgb, color.a);\r\n\r\n	gl_FragColor = vec4(res, 1.0);\r\n}';
exports['fringing_vertex'] = 'varying vec4 coord;\r\nvarying vec2 distortionCoord[3];\r\n\r\nuniform sampler2D distortionMap;\r\nuniform float distortion_amount;\r\nuniform float distortion_horizontal;\r\nuniform float distortion_vertical;\r\nuniform float fringing;\r\nuniform vec2 imgSize;\r\n\r\nconst vec2 center = vec2(0.5);\r\nconst vec2 center_hv = vec2(0.0, 0.5);\r\nconst float barrel_size = 0.5625;\r\n\r\nconst float floatMid = 65536.0;\r\n\r\nvec2 mapCoords(in vec2 coords) {\r\n  vec4 packedCoords = texture2D(distortionMap, coords);\r\n  return vec2(packedCoords.x + packedCoords.y, packedCoords.z + packedCoords.w) / floatMid;\r\n}\r\n\r\nvoid main() {\r\n  coord = gl_TexCoord;\r\n\r\n  float diag = length(imgSize);\r\n  vec2 perspective = 0.4 / diag * imgSize;\r\n  vec2 hv = vec2(distortion_horizontal, distortion_vertical);\r\n  vec2 inv = 1.0 - step(0.0, hv);\r\n  vec2 d, n;\r\n  float r, c;\r\n  float distortion = 1.0;\r\n  float zoom = 1.0;\r\n\r\n  vec2 coord_map = abs(inv - coord.xy);\r\n\r\n  if (distortion_horizontal != 0.0) {\r\n    n = coord_map - center_hv.xy;\r\n    d = 1.0 - abs(distortion_horizontal) * perspective;\r\n    n = mix(n, n * d, vec2(1.0, n.x));\r\n    coord_map = n + center_hv.xy;\r\n  }\r\n\r\n  if (distortion_vertical != 0.0) {\r\n    n = coord_map - center_hv.yx;\r\n    d = 1.0 - abs(distortion_vertical) * perspective;\r\n    n = mix(n, n * d, vec2(n.y, 1.0));\r\n    coord_map = n + center_hv.yx;\r\n  }\r\n\r\n  coord_map = abs(inv - coord_map) - center;\r\n\r\n  if (distortion_amount < 0.0) {\r\n    c = diag / (distortion_amount * -4.0);\r\n    r = max(length(coord_map * imgSize) / c, 0.0001);\r\n    distortion = atan(r)/r;\r\n    r = max(-0.5 * imgSize.x, -0.5 * imgSize.y) / c;\r\n    zoom = atan(r)/r;\r\n  }\r\n\r\n  if (distortion_amount > 0.0) {\r\n    r = dot(coord_map, coord_map) * barrel_size;\r\n    c = distortion_amount * 2.0;\r\n    distortion = 1.0 + r * c;\r\n    zoom = 1.0 + (0.5 * barrel_size) * c;\r\n  }\r\n\r\n  coord_map /= zoom;\r\n\r\n//  coord_map = mapCoords(coord_map + center) - center;\r\n\r\n  vec3 refraction = vec3(1.0+fringing*0.05, 1.0, 1.0-fringing*0.05) * distortion;\r\n\r\n  // get the right pixel for the current position\r\n  distortionCoord[0] = refraction.r * coord_map + center;\r\n  distortionCoord[1] = refraction.g * coord_map + center;\r\n  distortionCoord[2] = refraction.b * coord_map + center;\r\n\r\n  gl_Position = gl_ModelViewProjectionMatrix * gl_Distortion;\r\n}\r\n';
exports['grain'] = 'varying vec4 coord;\r\nvarying vec4 compositeCoord;\r\n\r\nuniform sampler2D texture;\r\n\r\nuniform vec2 imgSize;\r\nuniform float scale;\r\nuniform float grain_amount;\r\nuniform float grain_size;\r\n\r\nconst float timer = 1.0;\r\nconst float intensity = 0.5;\r\n\r\nconst vec3 lumcoeff = vec3(0.299, 0.587, 0.114);\r\n\r\n//a random texture generator, but you can also use a pre-computed perturbation texture\r\nvec4 rnm(in vec2 tc) {\r\n  float noise = sin(dot(tc + vec2(timer), vec2(12.9898, 78.233))) * 43758.5453;\r\n  return fract(vec4(1.0, 1.2154, 1.3453, 1.3647) * noise) * 2.0 - 1.0;\r\n}\r\n\r\nfloat fade(in float t) {\r\n  return t*t*t*(t*(t*6.0-15.0)+10.0);\r\n}\r\n\r\nfloat pnoise3D(in vec3 p) {\r\n\r\n  vec2 permTexUnit = 1.0 / imgSize;    // Perm texture texel-size\r\n  vec2 permTexUnitHalf = 0.5 / imgSize;  // Half perm texture texel-size\r\n\r\n  vec3 pi = permTexUnit.xyy*floor(p)+permTexUnitHalf.xyy;\r\n  // and offset 1/2 texel to sample texel centers\r\n  vec3 pf = fract(p);     // Fractional part for interpolation\r\n  // Noise contributions from (x=0, y=0), z=0 and z=1\r\n  float perm00 = rnm(pi.xy).a;\r\n  vec3  grad000 = rnm(vec2(perm00, pi.z)).rgb * 4.0 - 1.0;\r\n  float n000 = dot(grad000, pf);\r\n  vec3  grad001 = rnm(vec2(perm00, pi.z + permTexUnit.y)).rgb * 4.0 - 1.0;\r\n  float n001 = dot(grad001, pf - vec3(0.0, 0.0, 1.0));\r\n  // Noise contributions from (x=0, y=1), z=0 and z=1\r\n  float perm01 = rnm(pi.xy + vec2(0.0, permTexUnit.y)).a;\r\n  vec3  grad010 = rnm(vec2(perm01, pi.z)).rgb * 4.0 - 1.0;\r\n  float n010 = dot(grad010, pf - vec3(0.0, 1.0, 0.0));\r\n  vec3  grad011 = rnm(vec2(perm01, pi.z + permTexUnit.y)).rgb * 4.0 - 1.0;\r\n  float n011 = dot(grad011, pf - vec3(0.0, 1.0, 1.0));\r\n  // Noise contributions from (x=1, y=0), z=0 and z=1\r\n  float perm10 = rnm(pi.xy + vec2(permTexUnit.x, 0.0)).a;\r\n  vec3  grad100 = rnm(vec2(perm10, pi.z)).rgb * 4.0 - 1.0;\r\n  float n100 = dot(grad100, pf - vec3(1.0, 0.0, 0.0));\r\n  vec3  grad101 = rnm(vec2(perm10, pi.z + permTexUnit.y)).rgb * 4.0 - 1.0;\r\n  float n101 = dot(grad101, pf - vec3(1.0, 0.0, 1.0));\r\n  // Noise contributions from (x=1, y=1), z=0 and z=1\r\n  float perm11 = rnm(pi.xy + vec2(permTexUnit.x, permTexUnit.y)).a;\r\n  vec3  grad110 = rnm(vec2(perm11, pi.z)).rgb * 4.0 - 1.0;\r\n  float n110 = dot(grad110, pf - vec3(1.0, 1.0, 0.0));\r\n  vec3  grad111 = rnm(vec2(perm11, pi.z + permTexUnit.y)).rgb * 4.0 - 1.0;\r\n  float n111 = dot(grad111, pf - vec3(1.0, 1.0, 1.0));\r\n  // Blend contributions along x\r\n  vec4 n_x = mix(vec4(n000, n001, n010, n011), vec4(n100, n101, n110, n111), fade(pf.x));\r\n  // Blend contributions along y\r\n  vec2 n_xy = mix(n_x.xy, n_x.zw, fade(pf.y));\r\n  // Blend contributions along z\r\n  return mix(n_xy.x, n_xy.y, fade(pf.z));\r\n}\r\n//2d coordinate orientation thing\r\nvec2 coordRot(in vec2 tc, in float angle) {\r\n  float aspect = imgSize.x/imgSize.y;\r\n  float rotX = ((tc.x*2.0-1.0)*aspect*cos(angle)) - ((tc.y*2.0-1.0)*sin(angle));\r\n  float rotY = ((tc.y*2.0-1.0)*cos(angle)) + ((tc.x*2.0-1.0)*aspect*sin(angle));\r\n  return vec2((rotX/aspect)*0.5+0.5, rotY*0.5+0.5);\r\n}\r\nvoid main() {\r\n  lowp vec3 col = texture2D(texture, coord.xy).rgb;\r\n\r\n  float size = (grain_size + 1.5); //grain particle size (1.5 - 2.5)\r\n  float grain = grain_amount / 32.0 * (min(scale, 1.0) * 8.0);\r\n\r\n  vec3 rotOffset = vec3(1.425, 3.892, 5.835); //rotation offset values\r\n  vec2 rotCoordsR = coordRot(compositeCoord.xy, timer + rotOffset.x);\r\n  vec3 noise = vec3(pnoise3D(vec3(imgSize / size * rotCoordsR, 0.0)));\r\n\r\n  //noisiness response curve based on scene luminance\r\n  float luminance = mix(0.0, dot(col, lumcoeff), intensity);\r\n  luminance = smoothstep(0.2, 0.0, luminance) + luminance;\r\n  noise = mix(noise, vec3(0.0), pow(luminance, 4.0));\r\n  col = col + noise * grain;\r\n\r\n  gl_FragColor = vec4(col, 1.0);\r\n}';
exports['hsl'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D map;\r\n\r\nvec3 RGBToHSL(vec3 color)\r\n{\r\n  vec3 hsl; // init to 0 to avoid warnings ? (and reverse if + remove first part)\r\n  \r\n  float fmin = min(min(color.r, color.g), color.b);    //Min. value of RGB\r\n  float fmax = max(max(color.r, color.g), color.b);    //Max. value of RGB\r\n  float delta = fmax - fmin;             //Delta RGB value\r\n\r\n  hsl.z = (fmax + fmin) / 2.0; // Luminance\r\n\r\n  if (delta == 0.0)   //This is a gray, no chroma...\r\n  {\r\n    hsl.x = 0.0;  // Hue\r\n    hsl.y = 0.0;  // Saturation\r\n  }\r\n  else                                    //Chromatic data...\r\n  {\r\n    if (hsl.z < 0.5)\r\n      hsl.y = delta / (fmax + fmin); // Saturation\r\n    else\r\n      hsl.y = delta / (2.0 - fmax - fmin); // Saturation\r\n    \r\n    float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta;\r\n    float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta;\r\n    float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta;\r\n\r\n    if (color.r == fmax )\r\n      hsl.x = deltaB - deltaG; // Hue\r\n    else if (color.g == fmax)\r\n      hsl.x = (1.0 / 3.0) + deltaR - deltaB; // Hue\r\n    else if (color.b == fmax)\r\n      hsl.x = (2.0 / 3.0) + deltaG - deltaR; // Hue\r\n\r\n    if (hsl.x < 0.0)\r\n      hsl.x += 1.0; // Hue\r\n    else if (hsl.x > 1.0)\r\n      hsl.x -= 1.0; // Hue\r\n  }\r\n\r\n  return clamp(hsl,0.0,1.0);\r\n}\r\n\r\nfloat HueToRGB(float f1, float f2, float hue)\r\n{\r\n  if (hue < 0.0)\r\n    hue += 1.0;\r\n  else if (hue > 1.0)\r\n    hue -= 1.0;\r\n  float res;\r\n  if ((6.0 * hue) < 1.0)\r\n    res = f1 + (f2 - f1) * 6.0 * hue;\r\n  else if ((2.0 * hue) < 1.0)\r\n    res = f2;\r\n  else if ((3.0 * hue) < 2.0)\r\n    res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0;\r\n  else\r\n    res = f1;\r\n  return res;\r\n}\r\n\r\nvec3 HSLToRGB(vec3 hsl)\r\n{\r\n  vec3 rgb;\r\n  \r\n  if (hsl.y == 0.0)\r\n    rgb = vec3(hsl.z); // Luminance\r\n  else\r\n  {\r\n    float f2;\r\n    \r\n    if (hsl.z < 0.5)\r\n      f2 = hsl.z * (1.0 + hsl.y);\r\n    else\r\n      f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z);\r\n      \r\n    float f1 = 2.0 * hsl.z - f2;\r\n    \r\n    rgb.r = HueToRGB(f1, f2, hsl.x + (1.0/3.0));\r\n    rgb.g = HueToRGB(f1, f2, hsl.x);\r\n    rgb.b = HueToRGB(f1, f2, hsl.x - (1.0/3.0));\r\n  }\r\n  \r\n  return rgb;\r\n}\r\n\r\nfloat Lum(vec3 c){\r\n  return 0.299*c.r + 0.587*c.g + 0.114*c.b;\r\n}\r\n\r\nvec3 ClipColor(vec3 c){\r\n  float l = Lum(c);\r\n  float n = min(min(c.r, c.g), c.b);\r\n  float x = max(max(c.r, c.g), c.b);\r\n\r\n  if (n < 0.0) c = (c-l)*l / (l-n) + l;\r\n  if (x > 1.0) c = (c-l) * (1.0-l) / (x-l) + l;\r\n  \r\n  return c;\r\n}\r\n\r\nvec3 SetLum(vec3 c, float l){\r\n  float d = l - Lum(c);\r\n\r\n  c.r = c.r + d;\r\n  c.g = c.g + d;\r\n  c.b = c.b + d;\r\n  \r\n  return ClipColor(c);\r\n}\r\n\r\nvoid main() {\r\n\r\n  lowp vec3 color = texture2D(texture, coord.xy).rgb;\r\n  vec3 hsl = RGBToHSL(color);\r\n  lowp vec3 hslMap = texture2D(map, vec2(hsl.x)).xyz;\r\n\r\n  vec3 rgb = HSLToRGB(vec3(\r\n    hsl.x - ((1.0 - hslMap.x*2.0)*60.0)/360.0,\r\n    hsl.y * (hslMap.y*2.0),\r\n    hsl.z// + (hslMap.z - 0.5)*hsl.y*0.5\r\n  ));\r\n\r\n  float lum = Lum(color);\r\n  rgb = SetLum(rgb, lum + (hslMap.z - 0.5) * (hsl.y*(1.0-lum)*4.0) * lum*lum);\r\n\r\n  gl_FragColor = vec4(rgb, 1.0);\r\n  \r\n}';
exports['layer'] = 'varying vec4 coord;\r\n//varying vec4 compositeCoord;\r\nvarying vec4 backgroundCoords;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D layerTexture;\r\nuniform vec4 colorOverlay;\r\nuniform float opacity;\r\nuniform float blendMode;\r\nuniform float blendMix;\r\n//uniform vec2 layerSize;\r\n//uniform vec2 layerPosition;\r\n//uniform float angle;\r\n//uniform vec2 imgSize;\r\n\r\nvec3 blendDifference(in vec3 base, in vec3 blend, in float fill) {\r\n  return abs(base - blend * fill);\r\n}\r\n\r\nfloat blendColorDodge(in float base, in float blend) {\r\n	return (blend==1.0)?blend:min(base/(1.0-blend),1.0);\r\n}\r\n\r\nfloat blendColorBurn(in float base, in float blend) {\r\n	return (blend==0.0)?blend:max((1.0-((1.0-base)/blend)),0.0);\r\n}\r\n\r\nfloat blendVividLight(in float base, in float blend) {\r\n	return (blend<0.5)?blendColorBurn(base,(2.0*blend)):blendColorDodge(base,(2.0*(blend-0.5)));\r\n}\r\n\r\nvec3 blendVividLight(in vec3 base, in vec3 blend, in float fill) {\r\n  blend = mix(vec3(0.5), blend, fill);\r\n	return vec3(blendVividLight(base.r,blend.r),blendVividLight(base.g,blend.g),blendVividLight(base.b,blend.b));\r\n}\r\n\r\nvoid main() {\r\n//  vec2 offset = vec2(layerPosition.x, 1.0 - layerPosition.y);\r\n//\r\n//  float rads = radians(angle);\r\n//  float s = sin(rads);\r\n//  float c = cos(rads);\r\n//\r\n//  vec2 layerCoords = (compositeCoord.xy - offset) * imgSize;\r\n//   rotate and scale\r\n//	layerCoords = vec2(\r\n//	  layerCoords.x * c - layerCoords.y * s,\r\n//    layerCoords.x * s + layerCoords.y * c\r\n//  ) / layerSize + 0.5;\r\n\r\n  lowp vec3 base = texture2D(texture, backgroundCoords.xy).rgb;\r\n  lowp vec4 layer = texture2D(layerTexture, coord.xy);\r\n  lowp vec3 blend = mix(layer.rgb, colorOverlay.rgb, colorOverlay.a);\r\n  lowp vec3 overlay;\r\n\r\n  float mixA = min(1.0, (1.0 - abs(blendMix)) * 1.333);\r\n  float mixB =  min(1.0, abs(blendMix) * 4.0);\r\n\r\n  overlay = (blendMix > 0.0) ? blendVividLight(base, blend, mixA) : blendDifference(base, blend, mixA);\r\n  blend = mix(blend, overlay, mixB);\r\n\r\n  if (blendMode == 22.0) blend = base; // passthrough\r\n\r\n	gl_FragColor = vec4(blend, layer.a * opacity);\r\n}';
exports['layer_vertex'] = 'varying vec4 coord;\r\nvarying vec4 backgroundCoords;\r\n\r\nuniform mat4 backgroundMatrix;\r\n\r\nvoid main() {\r\n  coord = gl_TexCoord;\r\n  backgroundCoords = backgroundMatrix * gl_Vertex * 0.5 + 0.5;\r\n  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\r\n}\r\n';
exports['lens_blur'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform float radius;\r\n//uniform float bokeh;\r\n\r\nfloat random(vec3 scale, float seed) {\r\n  /* use the fragment position for a different seed per-pixel */\r\n  return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\r\n}\r\n\r\nfloat Lum(vec3 c){\r\n	return 0.299*c.r + 0.587*c.g + 0.114*c.b;\r\n}\r\n\r\nvoid main() {\r\n\r\n//  vec3 color = vec3(0.0);\r\n//  float total = 0.0;\r\n\r\n  /* randomize the lookup values to hide the fixed number of samples */\r\n//  float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);\r\n\r\n  vec4 center = texture2D(texture, coord.xy);\r\n\r\n  vec3 color = vec3(0.0);\r\n  float total = 0.0;\r\n  float r = radius * center.a;\r\n  float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0) / 30.0 * r;\r\n//  float threshold = 1.0 - bokeh;\r\n//  float gain = 1.0 + 50.0 * bokeh;\r\n//  vec3 base = vec3(0.0);\r\n\r\n  for (float x = -15.0; x <= 15.0; x++) {\r\n    for (float y = -15.0; y <= 15.0; y++) {\r\n      vec2 pos = coord.xy + vec2(x, y) / 15.0 * r;\r\n      float weight = 1.0 - floor(distance(pos, coord.xy)/r);\r\n//        vec3 col = texture2D(texture, pos + offset).rgb;\r\n//        float lum = Lum(col.rgb);\r\n//        color += col + mix(base, col, max((lum-threshold)*gain, 0.0) * r);\r\n      color += texture2D(texture, pos + offset).rgb * weight;\r\n      total += weight;\r\n    }\r\n  }\r\n\r\n  gl_FragColor = vec4(color / total, center.a);\r\n}';
exports['line'] = 'uniform vec4 color;\r\nuniform float opacity;\r\n\r\nvoid main() {\r\n	gl_FragColor = color * opacity;\r\n}';
exports['line_vertex'] = 'attribute vec3 coordinates;\r\n\r\nvoid main() {\r\n  gl_Position = vec4(coordinates, 1.0);\r\n}\r\n';
exports['mask_brush'] = 'varying vec4 coord;\r\nvarying vec4 compositeCoord;\r\n\r\nuniform float opacity;\r\nuniform float alpha;\r\nuniform float invert;\r\nuniform vec4 channel;\r\nuniform sampler2D texture;\r\nuniform sampler2D brushTexture;\r\n\r\nvoid main() {\r\n  vec4 col = texture2D(texture, coord.xy);\r\n  vec4 brush = texture2D(brushTexture, compositeCoord.xy);\r\n  float mask = dot(brush, channel);\r\n\r\n  if (invert > 0.0) mask = 1.0 - mask;\r\n\r\n  float a = clamp(col.a * alpha + mask * opacity, 0.0, 1.0);\r\n\r\n	gl_FragColor = vec4(col.rgb, a);\r\n}';
exports['mask_color'] = 'varying vec4 coord;\r\nvarying vec4 compositeCoord;\r\nvarying vec2 distortionCoord;\r\n\r\nuniform float threshold;\r\nuniform float feather;\r\nuniform float invert;\r\nuniform float angle;\r\nuniform vec2 position;\r\nuniform vec2 size;\r\nuniform float opacity;\r\nuniform float alpha;\r\nuniform vec2 imgSize;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D colorMap;\r\n\r\nconst float epsilon = 0.000001;\r\nconst float mx = 1.0 - epsilon;\r\nconst float kE = 216.0 / 24389.0;\r\nconst float kK = 24389.0 / 27.0;\r\nconst float kKE = 8.0;\r\nconst float PI = 3.1415926535897932384626433832795;\r\n\r\nconst vec3 D50 = vec3(0.96422, 1.0, 0.82521);\r\nconst vec3 D65 = vec3(0.95047, 1.0, 1.08883);\r\n\r\nconst mat3 matRGBtoROMM = mat3(0.5293459296226501, 0.3300727903842926, 0.14058130979537964, 0.09837432950735092, 0.8734610080718994, 0.028164653107523918, 0.01688321679830551, 0.11767247319221497, 0.8654443025588989);\r\nconst mat3 matROMMtoRGB = mat3(2.0340757369995117, -0.727334201335907, -0.3067416846752167, -0.22881317138671875, 1.2317301034927368, -0.0029169507324695587, -0.008569774217903614, -0.1532866358757019, 1.1618564128875732);\r\n\r\nconst mat3 matROMMtoXYZ = mat3(0.7976749, 0.1351917, 0.0313534, 0.2880402, 0.7118741, 0.0000857, 0.0000000, 0.0000000, 0.8252100);\r\nconst mat3 matXYZtoROMM = mat3(1.3459433, -0.2556075, -0.0511118, -0.5445989, 1.5081673, 0.0205351, 0.0000000, 0.0000000, 1.2118128);\r\n\r\nconst mat3 matRGBtoXYZ = mat3(0.4124564390896922, 0.357576077643909, 0.18043748326639894, 0.21267285140562253, 0.715152155287818, 0.07217499330655958, 0.0193338955823293, 0.11919202588130297, 0.9503040785363679);\r\nconst mat3 matXYZtoRGB = mat3(3.2404541621141045, -1.5371385127977166, -0.498531409556016, -0.9692660305051868, 1.8760108454466942, 0.041556017530349834, 0.055643430959114726, -0.2040259135167538, 1.0572251882231791);\r\n\r\nconst mat3 matAdapt = mat3(0.8951, 0.2664, -0.1614, -0.7502, 1.7135, 0.0367, 0.0389, -0.0685, 1.0296);\r\nconst mat3 matAdaptInv = mat3(0.9869929054667123, -0.14705425642099013, 0.15996265166373125, 0.43230526972339456, 0.5183602715367776, 0.0492912282128556, -0.008528664575177328, 0.04004282165408487, 0.9684866957875502);\r\n\r\n\r\nvec3 hsv2rgb(in vec3 c) {\r\n    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\r\n    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\r\n    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\r\n}\r\n\r\nvec3 rgb2xyz(in vec3 rgb, in vec3 d, in vec3 s) {\r\n	vec3 xyz;\r\n\r\n	xyz = rgb * matRGBtoXYZ;\r\n\r\n	// adaption\r\n	xyz = (xyz * matAdapt) * (d/s) * matAdaptInv;\r\n\r\n	return xyz;\r\n}\r\n\r\nvec3 xyz2rgb(in vec3 xyz, in vec3 d, in vec3 s) {\r\n	vec3 rgb;\r\n\r\n	// adaption\r\n	xyz = (xyz * matAdapt) * (s/d) * matAdaptInv;\r\n\r\n	rgb = xyz * matXYZtoRGB;\r\n\r\n	return rgb;\r\n}\r\n\r\nvec3 xyz2lab(in vec3 xyz, in vec3 refWhite) {\r\n	vec3 r = xyz / refWhite;\r\n\r\n	vec3 f = vec3(\r\n		(r.x > kE) ? pow(r.x, 1.0 / 3.0) : ((kK * r.x + 16.0) / 116.0),\r\n		(r.y > kE) ? pow(r.y, 1.0 / 3.0) : ((kK * r.y + 16.0) / 116.0),\r\n		(r.z > kE) ? pow(r.z, 1.0 / 3.0) : ((kK * r.z + 16.0) / 116.0)\r\n	);\r\n\r\n	return vec3(\r\n    116.0 * f.y - 16.0,\r\n    500.0 * (f.x - f.y),\r\n    200.0 * (f.y - f.z)\r\n  );\r\n}\r\n\r\nvec3 lab2xyz(in vec3 lab, in vec3 refWhite) {\r\n	float fy = (lab.x + 16.0) / 116.0;\r\n	float fx = 0.002 * lab.y + fy;\r\n	float fz = fy - 0.005 * lab.z;\r\n\r\n	float fx3 = fx * fx * fx;\r\n	float fz3 = fz * fz * fz;\r\n\r\n	vec3 xyz = vec3(\r\n    (fx3 > kE) ? fx3 : ((116.0 * fx - 16.0) / kK),\r\n    (lab.x > kKE) ? pow((lab.x + 16.0) / 116.0, 3.0) : (lab.x / kK),\r\n    (fz3 > kE) ? fz3 : ((116.0 * fz - 16.0) / kK)\r\n  );\r\n\r\n  return xyz * refWhite;\r\n}\r\n\r\nvec3 lab2lch(in vec3 lab) {\r\n	vec3 lch = vec3(\r\n		lab.x,\r\n		sqrt(lab.y * lab.y + lab.z * lab.z),\r\n		180.0 * atan(lab.z, lab.y) / PI\r\n	);\r\n	if (lch.z < 0.0) lch.z += 360.0;\r\n	return lch;\r\n}\r\n\r\nvec3 lch2lab(in vec3 lch) {\r\n	return vec3(\r\n		lch.x,\r\n		lch.y * cos(lch.z * PI / 180.0),\r\n		lch.y * sin(lch.z * PI / 180.0)\r\n	);\r\n}\r\n\r\nvec3 invCompand(in vec3 companded) {\r\n  return vec3(\r\n    (companded.r <= 0.04045) ? (companded.r / 12.92) : pow((companded.r + 0.055) / 1.055, 2.4),\r\n    (companded.g <= 0.04045) ? (companded.g / 12.92) : pow((companded.g + 0.055) / 1.055, 2.4),\r\n    (companded.b <= 0.04045) ? (companded.b / 12.92) : pow((companded.b + 0.055) / 1.055, 2.4)\r\n  );\r\n}\r\n\r\nfloat random(vec3 scale, float seed) {\r\n    /* use the fragment position for a different seed per-pixel */\r\n    return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\r\n}\r\n\r\nvoid main() {\r\n  float blur = min(1.0 - feather, 0.990);\r\n  float rads = radians(angle);\r\n  float _s = sin(rads);\r\n  float _c = cos(rads);\r\n	vec2 coords = (compositeCoord.xy - 0.5 - position) * imgSize;\r\n	coords = (vec2(\r\n	  coords.x * _c - coords.y * _s,\r\n    coords.x * _s + coords.y * _c\r\n  ) / imgSize + position) / size;\r\n\r\n  vec4 col = texture2D(texture, coord.xy);\r\n  vec3 map = texture2D(colorMap, distortionCoord).rgb;\r\n  vec3 sel = texture2D(colorMap, position + 0.5).rgb;\r\n\r\n  // convert to linear\r\n  map = invCompand(map);\r\n  sel = invCompand(sel);\r\n\r\n	vec2 offset = 1.0 + (1.0 - vec2(feather));\r\n  float dist = distance(coords * offset, position / size * offset);\r\n  dist += random(vec3(12.9898, 78.233, 151.7182), 1.0)/50.0 * (1.0 - blur);\r\n\r\n  float mask = smoothstep(1.0, blur, dist);\r\n\r\n  //  color mask\r\n  vec3 refWhite = D50;\r\n  vec3 refWhiteRGB = D65;\r\n  vec3 d = refWhite * matAdapt;\r\n  vec3 s = refWhiteRGB * matAdapt;\r\n\r\n  vec3 mapLCH = (xyz2lab(rgb2xyz(map,d,s),refWhite));\r\n  vec3 selLCH = (xyz2lab(rgb2xyz(sel,d,s),refWhite));\r\n\r\n  float ml = mapLCH.x/100.0;\r\n  float ms = mapLCH.y/100.0;\r\n  float mh = mapLCH.z/100.0;\r\n  float sl = selLCH.x/100.0;\r\n  float ss = selLCH.y/100.0;\r\n  float sh = selLCH.z/100.0;\r\n\r\n  float t = threshold * 0.98 + 0.01;\r\n  float lMask = clamp(1.0 - (abs(ml - sl)) / t, 0.0, 1.0);\r\n  float aMask = clamp(1.0 - (abs(ms - ss)) / t, 0.0, 1.0);\r\n  float bMask = clamp(1.0 - (abs(mh - sh)) / t, 0.0, 1.0);\r\n\r\n  float colorMask = clamp(aMask * bMask * lMask, 0.0, 1.0);\r\n\r\n  mask *= colorMask;\r\n\r\n  if (invert > 0.0) mask = 1.0 - mask;\r\n\r\n  float a = clamp(col.a * alpha + mask * opacity, 0.0, 1.0);\r\n\r\n	gl_FragColor = vec4(col.rgb, a);\r\n}';
exports['mask_gradient'] = 'varying vec4 coord;\r\nvarying vec4 compositeCoord;\r\n\r\nuniform vec2 startPoint;\r\nuniform vec2 endPoint;\r\nuniform vec2 imgSize;\r\nuniform float opacity;\r\nuniform float alpha;\r\nuniform float invert;\r\nuniform float reflect;\r\n\r\nuniform sampler2D texture;\r\n\r\nvoid main() {\r\n  vec4 col = texture2D(texture, coord.xy);\r\n\r\n  vec2 compositeCoords = compositeCoord.xy - 0.5;\r\n	vec2 coords = vec2(compositeCoords.x, 1.0 - compositeCoords.y) * imgSize;\r\n\r\n  vec2 start = vec2(startPoint.x, startPoint.y + 1.0) * imgSize;\r\n  vec2 end = vec2(endPoint.x, endPoint.y + 1.0) * imgSize;\r\n\r\n  vec2 direction = end - start;\r\n  direction /= length(direction);\r\n  float scale = dot(direction, end - start);\r\n  float value = dot(direction, coords - start) / scale;\r\n  float mask = (reflect > 0.0) ? abs(1.0-value*2.0) : clamp(1.0-value, 0.0, 1.0);\r\n\r\n  if (invert > 0.0) mask = 1.0 - mask;\r\n\r\n  float a = clamp(col.a * alpha + mask * opacity, 0.0, 1.0);\r\n\r\n	gl_FragColor = vec4(col.rgb, a);\r\n}';
exports['mask_radial'] = 'varying vec4 coord;\r\nvarying vec4 compositeCoord;\r\n\r\nuniform float feather;\r\nuniform float invert;\r\nuniform float angle;\r\nuniform vec2 position;\r\nuniform vec2 size;\r\nuniform float opacity;\r\nuniform float alpha;\r\n\r\nuniform sampler2D texture;\r\nuniform vec2 imgSize;\r\n\r\nfloat random(vec3 scale, float seed) {\r\n    /* use the fragment position for a different seed per-pixel */\r\n    return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\r\n}\r\n\r\nvoid main() {\r\n  vec4 col = texture2D(texture, coord.xy);\r\n  float blur = min(1.0 - feather, 0.990);\r\n  float rads = radians(angle);\r\n  float s = sin(rads);\r\n  float c = cos(rads);\r\n\r\n	vec2 coords = (compositeCoord.xy - 0.5 - position) * imgSize;\r\n	coords = (vec2(\r\n	  coords.x * c - coords.y * s,\r\n    coords.x * s + coords.y * c\r\n  ) / imgSize + position) / size;\r\n\r\n	vec2 offset = 1.0 + (1.0 - vec2(feather));\r\n  float dist = distance(coords * offset, position / size * offset);\r\n  dist += random(vec3(12.9898, 78.233, 151.7182), 1.0)/50.0 * (1.0 - blur);\r\n\r\n  float mask = smoothstep(1.0, blur, dist);\r\n\r\n  if (invert > 0.0) mask = 1.0 - mask;\r\n\r\n  float a = clamp(col.a * alpha + mask * opacity, 0.0, 1.0);\r\n	gl_FragColor = vec4(col.rgb, a);\r\n}';
exports['mosaic'] = 'varying vec4 coord;\r\nvarying vec4 compositeCoord;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D blurTexture;\r\nuniform float mosaic_size; /*0~1 match t  o -> 0.01~1.0 */\r\n\r\nuniform float distortion_amount;\r\nuniform float distortion_horizontal;\r\nuniform float distortion_vertical;\r\nuniform vec2 imgSize;\r\n\r\nvec2 distort(in vec2 coord) {\r\n  const vec2 center = vec2(0.5);\r\n  const vec2 center_hv = vec2(0.0, 0.5);\r\n  const float barrel_size = 0.5625;\r\n\r\n  float diag = length(imgSize);\r\n  vec2 perspective = 0.2 / diag * imgSize;\r\n  vec2 hv = vec2(distortion_horizontal, distortion_vertical);\r\n  vec2 inv = 1.0 - step(0.0, hv);\r\n  vec2 d, n;\r\n  float r, c;\r\n  float distortion = 1.0;\r\n  float zoom = 1.0;\r\n\r\n  vec2 coord_map = abs(inv - coord);\r\n\r\n  if (distortion_horizontal != 0.0) {\r\n    n = coord_map - center_hv.xy;\r\n    d = 1.0 - abs(distortion_horizontal) * perspective;\r\n    n = mix(n, n * d, vec2(1.0, n.x));\r\n    coord_map = n + center_hv.xy;\r\n  }\r\n\r\n  if (distortion_vertical != 0.0) {\r\n    n = coord_map - center_hv.yx;\r\n    d = 1.0 - abs(distortion_vertical) * perspective;\r\n    n = mix(n, n * d, vec2(n.y, 1.0));\r\n    coord_map = n + center_hv.yx;\r\n  }\r\n\r\n  coord_map = abs(inv - coord_map) - center;\r\n\r\n  if (distortion_amount < 0.0) {\r\n    c = diag / (distortion_amount * -4.0);\r\n    r = length(coord_map * imgSize) / c;\r\n    distortion = atan(r)/r;\r\n    r = max(-0.5 * imgSize.x, -0.5 * imgSize.y) / c;\r\n    zoom = atan(r)/r;\r\n  }\r\n\r\n  if (distortion_amount > 0.0) {\r\n    r = dot(coord_map, coord_map) * barrel_size;\r\n    c = distortion_amount * 2.0;\r\n    distortion = 1.0 + r * c;\r\n    zoom = 1.0 + (0.5 * barrel_size) * c;\r\n  }\r\n\r\n  return distortion * coord_map / zoom + center;\r\n}\r\n\r\nvec2 coord2grid(vec2 coord_current, vec2 mosaic_size_scale)\r\n{\r\n  vec2 grid_current = coord_current - 0.5;\r\n  grid_current = floor(grid_current / (mosaic_size_scale + 0.0001) + 0.5) * mosaic_size_scale + 0.5;\r\n  return grid_current;\r\n}\r\n\r\n\r\nvoid main() {\r\n  /* compute mosaic block size and make sure it is squre */\r\n  vec2 mosaic_size_scale = vec2((0.01 + mosaic_size * mosaic_size) * min(imgSize.x, imgSize.y)) / imgSize;\r\n  vec2 coord_grid = coord2grid(compositeCoord.xy, mosaic_size_scale);\r\n  vec3 color = texture2D(blurTexture, distort(coord_grid)).rgb;\r\n  vec4 color_original = texture2D(texture,coord.xy);\r\n  color = mix(color_original.rgb, color, float(mosaic_size>0.0));\r\n  gl_FragColor = vec4(mix(color_original.rgb, color, color_original.a), color_original.a);\r\n}';
exports['multitexture'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D textures[4];\r\n\r\nvec4 lookupTexture(vec2 coords) {\r\n  vec2 c = coords * 2.0;\r\n  vec2 f = floor(c);\r\n  int i = int(f.x + f.y * 2.0);\r\n  vec2 uv = vec2(c.x - f.x, c.y - f.y);\r\n  vec4 col;\r\n\r\n	if (i == 0) {\r\n    col = texture2D(textures[0], uv);\r\n  } else if (i == 1) {\r\n    col = texture2D(textures[1], uv);\r\n  } else if (i == 2) {\r\n    col = texture2D(textures[2], uv);\r\n  } else /* if (i == 3) */ {\r\n    col = texture2D(textures[3], uv);\r\n  }\r\n\r\n	return col;\r\n}\r\n\r\n\r\nvoid main(){\r\n	gl_FragColor = lookupTexture(coord.xy);\r\n}\r\n';
exports['out'] = 'varying vec4 coord;\r\nvarying vec2 distortionCoord;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D original;\r\n\r\nvoid main() {\r\n  lowp vec3 col = texture2D(texture, coord.xy).rgb;\r\n  lowp float alpha = texture2D(original, distortionCoord).a;\r\n\r\n	gl_FragColor = vec4(col, alpha);\r\n}';
exports['out_multitexture'] = 'varying vec4 coord;\r\nvarying vec2 distortionCoord;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D original;\r\n\r\nuniform sampler2D textures[4];\r\n\r\nfloat lookupTexture(vec2 coords) {\r\n  vec2 c = coords * 2.0;\r\n  vec2 f = floor(c);\r\n  int i = int(f.x + f.y * 2.0);\r\n  vec2 uv = vec2(c.x - f.x, c.y - f.y);\r\n  lowp float col;\r\n\r\n	if (i == 0) {\r\n    col = texture2D(textures[0], uv).a;\r\n  } else if (i == 1) {\r\n    col = texture2D(textures[1], uv).a;\r\n  } else if (i == 2) {\r\n    col = texture2D(textures[2], uv).a;\r\n  } else /* if (i == 3) */ {\r\n    col = texture2D(textures[3], uv).a;\r\n  }\r\n\r\n	return col;\r\n}\r\n\r\nvoid main() {\r\n  lowp vec3 col = texture2D(texture, coord.xy).rgb;\r\n  lowp float alpha = lookupTexture(distortionCoord);\r\n\r\n	gl_FragColor = vec4(col, alpha);\r\n}';
exports['resample'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform float offset;\r\nuniform vec2 resolution;\r\n\r\nconst float B = 0.0;\r\nconst float C = 0.75;\r\n\r\nfloat cubic(in float x) {\r\n	if ( x < 0.0 ) x = -x;\r\n	float x2 = x * x;\r\n	float x3 = x2 * x;\r\n	if ( x < 1.0 )\r\n		return ( ( 12.0 - 9.0 * B - 6.0 * C ) * x3 + ( -18.0 + 12.0 * B + 6.0 * C ) * x2 + ( 6.0 - 2.0 * B ) ) / 6.0;\r\n	else if ( x >= 1.0 && x < 2.0 )\r\n		return ( ( -B - 6.0 * C ) * x3 + ( 6.0 * B + 30.0 * C ) * x2 + ( - ( 12.0 * B ) - 48.0 * C  ) * x +	8.0 * B + 24.0 * C) / 6.0;\r\n	else return 0.0;\r\n}\r\n\r\nvoid main() {\r\n	float texelSizeX = offset / resolution.x; //size of one texel \r\n  float texelSizeY = offset / resolution.y; //size of one texel \r\n  float a = fract( coord.x * resolution.x );\r\n  float b = fract( coord.y * resolution.y );\r\n\r\n  vec4 sum = vec4(0.0);\r\n	float w, wx, wy, weight = 0.0;\r\n\r\n  for( int m = -2; m <= 3; m++ )\r\n  {\r\n    for( int n =-2; n<= 3; n++)\r\n    {\r\n			wx = cubic ( float( m ) - a );\r\n			wy = cubic ( -( float( n ) - b ) );\r\n			w += wx + wy;\r\n      sum += texture2D(texture, coord.xy + vec2(texelSizeX * float( m ), texelSizeY * float( n ))) * w;\r\n      weight += w;\r\n    }\r\n  }\r\n\r\n  gl_FragColor = sum / weight;\r\n  // gl_FragColor = texture2D(texture, coord.xy);\r\n}';
exports['saturation_vibrance'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform float saturation;\r\nuniform float vibrance;\r\n\r\nvoid main() {\r\n  lowp vec4 col = texture2D(texture, coord.xy);\r\n\r\n	vec3 color = col.rgb;\r\n\r\n	float luminance = color.r*0.299 + color.g*0.587 + color.b*0.114;\r\n	float mn = min(min(color.r, color.g), color.b);\r\n	float mx = max(max(color.r, color.g), color.b);\r\n	float sat = (1.0-(mx - mn)) * (1.0-mx) * luminance * 5.0;\r\n	vec3 lightness = vec3((mn + mx)/2.0);\r\n\r\n	// vibrance\r\n	color = mix(color, mix(color, lightness, -vibrance), sat);\r\n\r\n	// negative vibrance\r\n	color = mix(color, lightness, (1.0-lightness)*(1.0-vibrance)/2.0*abs(vibrance));\r\n\r\n	// saturation\r\n	color = mix(color, vec3(luminance), -saturation);\r\n\r\n  gl_FragColor = vec4(mix(col.rgb, color, col.a), col.a);\r\n\r\n}';
exports['screen'] = 'varying vec4 coord;\r\nvarying vec4 compositeCoord;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D cacheTexture;\r\nuniform lowp float showTexture;\r\nuniform vec2 img_size;\r\nuniform vec2 grid_size;\r\nuniform float zoom;\r\n\r\nvoid main() {\r\n  vec2 resolution = img_size * zoom;\r\n  vec2 min_threshold = 1.0 / (resolution / 2.0 / grid_size);\r\n  vec2 max_threshold = 1.0 - min_threshold;\r\n\r\n  lowp vec2 mask = ceil(fract(clamp(compositeCoord.xy, 0.0, 1.0)));\r\n  lowp vec4 c1 = texture2D(cacheTexture, coord.xy);\r\n  lowp vec4 c2 = texture2D(texture, compositeCoord.xy);\r\n\r\n  vec4 col = mix(c1, c2, mask.x * mask.y * showTexture);\r\n\r\n  vec2 c = (coord.xy - 0.5) * (1.0 - min_threshold/2.0) + 0.5;\r\n  vec2 grid = fract(c * grid_size - min_threshold/2.0);\r\n\r\n  // draw grid\r\n  if((grid.x > max_threshold.x) || (grid.y > max_threshold.y)) {\r\n      col = mix(col, vec4(1.0), 0.5);\r\n  }\r\n\r\n	gl_FragColor = col;\r\n}';
exports['screen_vertex'] = 'varying vec4 coord;\r\nvarying vec4 compositeCoord;\r\n\r\nuniform mat4 cropMatrix;\r\nuniform mat4 viewMatrix;\r\n\r\nvoid main() {\r\n  coord = gl_TexCoord;\r\n  compositeCoord = viewMatrix * cropMatrix * gl_Vertex * 0.5 + 0.5;\r\n  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\r\n}\r\n';
exports['shadows_highlights'] = 'varying vec4 coord;\r\nvarying vec2 distortionCoord;\r\n\r\nuniform sampler2D texture;\r\nuniform sampler2D smoothMap;\r\nuniform sampler2D blurMap;\r\n\r\nuniform float shadows;\r\nuniform float highlights;\r\n\r\nconst float epsilon = 0.000001;\r\nconst float mx = 1.0 - epsilon;\r\nconst float PI = 3.1415926535897932384626433832795;\r\n\r\nconst mat3 matRGBtoROMM = mat3(0.5293459296226501, 0.3300727903842926, 0.14058130979537964, 0.09837432950735092, 0.8734610080718994, 0.028164653107523918, 0.01688321679830551, 0.11767247319221497, 0.8654443025588989);\r\nconst mat3 matROMMtoRGB = mat3(2.0340757369995117, -0.727334201335907, -0.3067416846752167, -0.22881317138671875, 1.2317301034927368, -0.0029169507324695587, -0.008569774217903614, -0.1532866358757019, 1.1618564128875732);\r\n\r\nfloat luma_romm(in vec3 color){\r\n	return dot(color, vec3(0.242655, 0.755158, 0.002187));\r\n}\r\n\r\nfloat luma(in vec3 color){\r\n	return dot(color, vec3(0.298839, 0.586811, 0.11435));\r\n}\r\n\r\nvec3 rgb2hsv(in vec3 c) {\r\n    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\r\n    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\r\n    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\r\n\r\n    float d = q.x - min(q.w, q.y);\r\n    float e = 1.0e-10;\r\n    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\r\n}\r\n\r\nvec3 hsv2rgb(in vec3 c) {\r\n    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\r\n    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\r\n    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\r\n}\r\n\r\nvec3 setHue(in vec3 res, in vec3 base) {\r\n  vec3 hsv = rgb2hsv(base);\r\n  vec3 res_hsv = rgb2hsv(res);\r\n  return hsv2rgb(vec3(hsv.x, res_hsv.y, res_hsv.z));\r\n}\r\n\r\nfloat gaussian(in float x) {\r\n  return 1.0 - exp(-PI*2.0*x*x);\r\n}\r\n\r\nvoid main() {\r\n  lowp vec4 col = texture2D(texture, coord.xy);\r\n  lowp vec3 color = texture2D(smoothMap, distortionCoord).rgb;\r\n  lowp vec4 blur = texture2D(blurMap, distortionCoord);\r\n\r\n  vec3 base = col.rgb * matRGBtoROMM;\r\n\r\n  vec3 map = mix(color, blur.rgb, min(blur.a / 0.05, 1.0));\r\n\r\n  float base_lum = luma(col.rgb);\r\n  float map_lum = luma_romm(map * matRGBtoROMM);\r\n\r\n	float exposure = mix(highlights, shadows, 1.0 - map_lum) * col.a;\r\n\r\n  float a = abs(exposure) * col.a + epsilon;\r\n  float v = pow(2.0, a+1.0)-2.0;\r\n  float m = mx - exp(-v);\r\n  vec3 res = (exposure > 0.0) ? (1.0 - exp(-v*base)) / m : log(1.0-base*m) / -v;\r\n  res = mix(base, res, min(a*100.0, 1.0));\r\n  res = setHue(res, base);\r\n\r\n  res = res * matROMMtoRGB;\r\n\r\n	gl_FragColor = vec4(res, col.a);\r\n}\r\n';
exports['sharpen'] = 'varying vec4 coord;\r\nvarying vec2 offsetCoord[4];\r\n\r\nuniform sampler2D texture;\r\nuniform float sharpen;\r\n\r\nvoid main() {\r\n\r\n	lowp vec4 midColor = texture2D(texture, coord.xy);\r\n\r\n	lowp vec4 sum = midColor * 5.0;\r\n\r\n  sum -= texture2D(texture, offsetCoord[0]);\r\n  sum -= texture2D(texture, offsetCoord[1]);\r\n  sum -= texture2D(texture, offsetCoord[2]);\r\n  sum -= texture2D(texture, offsetCoord[3]);\r\n\r\n	gl_FragColor = mix(midColor, sum, sharpen);\r\n}\r\n';
exports['sharpen_vertex'] = 'varying vec4 coord;\r\nvarying vec2 offsetCoord[4];\r\n\r\nuniform vec2 step;\r\nuniform float zoom;\r\n\r\nvoid main() {\r\n  coord = gl_TexCoord;\r\n\r\n	offsetCoord[0] = coord.xy + vec2(0.0, -step.y);\r\n	offsetCoord[1] = coord.xy + vec2(-step.x, 0.0);\r\n	offsetCoord[2] = coord.xy + vec2(step.x, 0.0);\r\n	offsetCoord[3] = coord.xy + vec2(0.0, step.y);\r\n\r\n  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\r\n}\r\n';
exports['split_tone'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform float highlights_hue;\r\nuniform float highlights_saturation;\r\nuniform float shadows_hue;\r\nuniform float shadows_saturation;\r\nuniform float balance;\r\n\r\nconst float epsilon = 0.000001;\r\nconst float mx = 1.0 - epsilon;\r\nconst float kE = 216.0 / 24389.0;\r\nconst float kK = 24389.0 / 27.0;\r\nconst float kKE = 8.0;\r\nconst float PI = 3.1415926535897932384626433832795;\r\n\r\nconst vec3 D50 = vec3(0.96422, 1.0, 0.82521);\r\nconst vec3 D65 = vec3(0.95047, 1.0, 1.08883);\r\n\r\nconst mat3 matRGBtoROMM = mat3(0.5293459296226501, 0.3300727903842926, 0.14058130979537964, 0.09837432950735092, 0.8734610080718994, 0.028164653107523918, 0.01688321679830551, 0.11767247319221497, 0.8654443025588989);\r\nconst mat3 matROMMtoRGB = mat3(2.0340757369995117, -0.727334201335907, -0.3067416846752167, -0.22881317138671875, 1.2317301034927368, -0.0029169507324695587, -0.008569774217903614, -0.1532866358757019, 1.1618564128875732);\r\n\r\nconst mat3 matROMMtoXYZ = mat3(0.7976749, 0.1351917, 0.0313534, 0.2880402, 0.7118741, 0.0000857, 0.0000000, 0.0000000, 0.8252100);\r\nconst mat3 matXYZtoROMM = mat3(1.3459433, -0.2556075, -0.0511118, -0.5445989, 1.5081673, 0.0205351, 0.0000000, 0.0000000, 1.2118128);\r\n\r\nconst mat3 matRGBtoXYZ = mat3(0.4124564390896922, 0.357576077643909, 0.18043748326639894, 0.21267285140562253, 0.715152155287818, 0.07217499330655958, 0.0193338955823293, 0.11919202588130297, 0.9503040785363679);\r\nconst mat3 matXYZtoRGB = mat3(3.2404541621141045, -1.5371385127977166, -0.498531409556016, -0.9692660305051868, 1.8760108454466942, 0.041556017530349834, 0.055643430959114726, -0.2040259135167538, 1.0572251882231791);\r\n\r\nconst mat3 matAdapt = mat3(0.8951, 0.2664, -0.1614, -0.7502, 1.7135, 0.0367, 0.0389, -0.0685, 1.0296);\r\nconst mat3 matAdaptInv = mat3(0.9869929054667123, -0.14705425642099013, 0.15996265166373125, 0.43230526972339456, 0.5183602715367776, 0.0492912282128556, -0.008528664575177328, 0.04004282165408487, 0.9684866957875502);\r\n\r\n\r\nvec3 hsv2rgb(in vec3 c) {\r\n    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\r\n    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\r\n    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\r\n}\r\n\r\nvec3 rgb2xyz(in vec3 rgb, in vec3 d, in vec3 s) {\r\n	vec3 xyz;\r\n\r\n	xyz = rgb * matRGBtoXYZ;\r\n\r\n	// adaption\r\n	xyz = (xyz * matAdapt) * (d/s) * matAdaptInv;\r\n\r\n	return xyz;\r\n}\r\n\r\nvec3 xyz2rgb(in vec3 xyz, in vec3 d, in vec3 s) {\r\n	vec3 rgb;\r\n\r\n	// adaption\r\n	xyz = (xyz * matAdapt) * (s/d) * matAdaptInv;\r\n\r\n	rgb = xyz * matXYZtoRGB;\r\n\r\n	return rgb;\r\n}\r\n\r\nvec3 xyz2lab(in vec3 xyz, in vec3 refWhite) {\r\n	vec3 r = xyz / refWhite;\r\n\r\n	vec3 f = vec3(\r\n		(r.x > kE) ? pow(r.x, 1.0 / 3.0) : ((kK * r.x + 16.0) / 116.0),\r\n		(r.y > kE) ? pow(r.y, 1.0 / 3.0) : ((kK * r.y + 16.0) / 116.0),\r\n		(r.z > kE) ? pow(r.z, 1.0 / 3.0) : ((kK * r.z + 16.0) / 116.0)\r\n	);\r\n\r\n	return vec3(\r\n    116.0 * f.y - 16.0,\r\n    500.0 * (f.x - f.y),\r\n    200.0 * (f.y - f.z)\r\n  );\r\n}\r\n\r\nvec3 lab2xyz(in vec3 lab, in vec3 refWhite) {\r\n	float fy = (lab.x + 16.0) / 116.0;\r\n	float fx = 0.002 * lab.y + fy;\r\n	float fz = fy - 0.005 * lab.z;\r\n\r\n	float fx3 = fx * fx * fx;\r\n	float fz3 = fz * fz * fz;\r\n\r\n	vec3 xyz = vec3(\r\n    (fx3 > kE) ? fx3 : ((116.0 * fx - 16.0) / kK),\r\n    (lab.x > kKE) ? pow((lab.x + 16.0) / 116.0, 3.0) : (lab.x / kK),\r\n    (fz3 > kE) ? fz3 : ((116.0 * fz - 16.0) / kK)\r\n  );\r\n\r\n  return xyz * refWhite;\r\n}\r\n\r\nvec3 lab2lch(in vec3 lab) {\r\n	vec3 lch = vec3(\r\n		lab.x,\r\n		sqrt(lab.y * lab.y + lab.z * lab.z),\r\n		180.0 * atan(lab.z, lab.y) / PI\r\n	);\r\n	if (lch.z < 0.0) lch.z += 360.0;\r\n	return lch;\r\n}\r\n\r\nvec3 lch2lab(in vec3 lch) {\r\n	return vec3(\r\n		lch.x,\r\n		lch.y * cos(lch.z * PI / 180.0),\r\n		lch.y * sin(lch.z * PI / 180.0)\r\n	);\r\n}\r\n\r\nfloat luma(in vec3 color){\r\n	return dot(color, vec3(0.242655, 0.755158, 0.002187));\r\n}\r\n\r\nvec3 invCompand(in vec3 companded) {\r\n  return vec3(\r\n    (companded.r <= 0.04045) ? (companded.r / 12.92) : pow((companded.r + 0.055) / 1.055, 2.4),\r\n    (companded.g <= 0.04045) ? (companded.g / 12.92) : pow((companded.g + 0.055) / 1.055, 2.4),\r\n    (companded.b <= 0.04045) ? (companded.b / 12.92) : pow((companded.b + 0.055) / 1.055, 2.4)\r\n  );\r\n}\r\n\r\nvec3 compand(in vec3 linear) {\r\n  return vec3(\r\n    (linear.r <= 0.0031308) ? (linear.r * 12.92) : (1.055 * pow(linear.r, 1.0 / 2.4) - 0.055),\r\n    (linear.g <= 0.0031308) ? (linear.g * 12.92) : (1.055 * pow(linear.g, 1.0 / 2.4) - 0.055),\r\n    (linear.b <= 0.0031308) ? (linear.b * 12.92) : (1.055 * pow(linear.b, 1.0 / 2.4) - 0.055)\r\n  );\r\n}\r\n\r\nvoid main() {\r\n  lowp vec4 col = texture2D(texture, coord.xy);\r\n	vec3 res = col.rgb;\r\n\r\n	float lum = luma(res * matRGBtoROMM);\r\n	float mask = col.a;\r\n\r\n  res = invCompand(res);\r\n\r\n  vec3 highlights = hsv2rgb(vec3(highlights_hue, highlights_saturation*2.0, 1.0));\r\n  vec3 shadows = hsv2rgb(vec3(shadows_hue, shadows_saturation*2.0, 1.0));\r\n\r\n  // define white\r\n  vec3 refWhite = D50;\r\n  vec3 refWhiteRGB = D65;\r\n  vec3 d = refWhite * matAdapt;\r\n  vec3 s = refWhiteRGB * matAdapt;\r\n\r\n  vec3 lab = xyz2lab(rgb2xyz(res,d,s),refWhite);\r\n  vec3 tintRGB = mix(shadows, highlights, clamp(lum + balance, 0.0, 1.0));\r\n  vec3 tintIn = rgb2xyz(tintRGB,d,s);\r\n  float tintMix = 1.0 - (1.0 - lum) * mask;\r\n\r\n  // define white\r\n  refWhite = D50;\r\n  refWhiteRGB = D50;\r\n  d = refWhite * matAdapt;\r\n  s = refWhiteRGB * matAdapt;\r\n\r\n  vec3 tintOut = rgb2xyz(tintRGB,d,s);\r\n\r\n  // define white\r\n  refWhite = mix(tintIn, D50, tintMix);\r\n  refWhiteRGB = D65;\r\n  d = refWhite * matAdapt;\r\n  s = refWhiteRGB * matAdapt;\r\n\r\n  res = rgb2xyz(res,d,s);\r\n\r\n  // define white\r\n  refWhite = D50;\r\n  refWhiteRGB = mix(tintOut, D65, tintMix);\r\n  d = refWhite * matAdapt;\r\n  s = refWhiteRGB * matAdapt;\r\n\r\n  vec3 ilab = xyz2lab(res,refWhite);\r\n  ilab.x = lab.x;\r\n  res = lab2xyz(ilab,refWhite);\r\n  res = xyz2rgb(res,d,s);\r\n  res = clamp(res, 0.0, 1.0);\r\n\r\n  res = compand(res);\r\n\r\n	gl_FragColor = vec4(res, 1.0);\r\n\r\n}';
exports['strip_alpha'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\n\r\nvoid main() {\r\n  lowp vec3 col = texture2D(texture, coord.xy).rgb;\r\n	gl_FragColor = vec4(col, 1.0);\r\n}';
exports['temperature'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform float temperature;\r\nuniform float tint;\r\n\r\nconst mat3 matRGBtoXYZ = mat3(\r\n	0.4124564390896922, 0.21267285140562253, 0.0193338955823293,\r\n	0.357576077643909, 0.715152155287818, 0.11919202588130297,\r\n	0.18043748326639894, 0.07217499330655958, 0.9503040785363679\r\n);\r\n\r\n\r\nconst mat3 matXYZtoRGB = mat3(\r\n	3.2404541621141045, -0.9692660305051868, 0.055643430959114726,\r\n	-1.5371385127977166, 1.8760108454466942, -0.2040259135167538,\r\n	-0.498531409556016, 0.041556017530349834, 1.0572251882231791\r\n);\r\n\r\nconst mat3 matAdapt = mat3(\r\n	0.8951, -0.7502, 0.0389,\r\n	0.2664, 1.7135, -0.0685,\r\n	-0.1614, 0.0367, 1.0296\r\n);\r\n\r\nconst mat3 matAdaptInv = mat3(\r\n	0.9869929054667123, 0.43230526972339456, -0.008528664575177328,\r\n	-0.14705425642099013, 0.5183602715367776, 0.04004282165408487,\r\n	0.15996265166373125, 0.0492912282128556, 0.9684866957875502\r\n);\r\n\r\nvec3 RGBtoXYZ(vec3 rgb, vec3 d, vec3 s){\r\n	vec3 xyz, XYZ;\r\n\r\n	xyz = matRGBtoXYZ * rgb;\r\n\r\n	// adaption\r\n	XYZ = matAdapt * xyz;\r\n	XYZ *= d/s;\r\n	xyz = matAdaptInv * XYZ;\r\n\r\n	return xyz;\r\n}\r\n\r\nvec3 XYZtoRGB(vec3 xyz, vec3 d, vec3 s){\r\n	vec3 rgb, RGB;\r\n\r\n	// adaption\r\n	RGB = matAdapt * xyz;\r\n	rgb *= s/d;\r\n	xyz = matAdaptInv * RGB;\r\n\r\n	rgb = matXYZtoRGB * xyz;\r\n\r\n	return rgb;\r\n}\r\n\r\nfloat Lum(vec3 c){\r\n	return 0.299*c.r + 0.587*c.g + 0.114*c.b;\r\n}\r\n\r\nvec3 ClipColor(vec3 c){\r\n	float l = Lum(c);\r\n	float n = min(min(c.r, c.g), c.b);\r\n	float x = max(max(c.r, c.g), c.b);\r\n\r\n	if (n < 0.0) c = (c-l)*l / (l-n) + l;\r\n	if (x > 1.0) c = (c-l) * (1.0-l) / (x-l) + l;\r\n	\r\n	return c;\r\n}\r\n\r\nvec3 SetLum(vec3 c, float l){\r\n	float d = l - Lum(c);\r\n\r\n	c.r = c.r + d;\r\n	c.g = c.g + d;\r\n	c.b = c.b + d;\r\n	\r\n	return ClipColor(c);\r\n}\r\n\r\n// illuminants\r\n//vec3 A = vec3(1.09850, 1.0, 0.35585);\r\n//vec3 D50 = vec3(0.96422, 1.0, 0.82521);\r\n//vec3 D65 = vec3(0.95047, 1.0, 1.08883);\r\n//vec3 D75 = vec3(0.94972, 1.0, 1.22638);\r\n\r\n\r\nconst vec3 D50 = vec3(0.981443, 1.0, 0.863177);\r\nconst vec3 D65 = vec3(0.968774, 1.0, 1.121774);\r\n\r\nconst vec3 CCT2K = vec3(1.274335, 1.0, 0.145233);\r\nconst vec3 CCT4K = vec3(1.009802, 1.0, 0.644496);\r\nconst vec3 CCT20K = vec3(0.995451, 1.0, 1.886109);\r\n\r\nvoid main() {\r\n	lowp vec4 col = texture2D(texture, coord.xy);\r\n\r\n	vec3 to, from;\r\n\r\n	vec3 d, s;\r\n\r\n	vec3 refWhite, refWhiteRGB;\r\n\r\n	if (temperature < 0.0) {\r\n	  to = CCT20K;\r\n	  from = D65;\r\n	} else {\r\n	  to = CCT4K;\r\n	  from = D65;\r\n	}\r\n\r\n	vec3 base = col.rgb;\r\n	float lum = Lum(base);\r\n	// mask by luminance\r\n	float temp = abs(temperature) * (1.0 - pow(lum, 2.72));\r\n\r\n	// from\r\n	refWhiteRGB = from;\r\n	// to\r\n	refWhite = vec3(mix(from.x, to.x, temp), mix(1.0, 0.9, tint), mix(from.z, to.z, temp));\r\n\r\n	// mix based on alpha for local adjustments\r\n	refWhite = mix(refWhiteRGB, refWhite, col.a);\r\n\r\n	d = matAdapt * refWhite;\r\n	s = matAdapt * refWhiteRGB;\r\n	vec3 xyz = RGBtoXYZ(base,d,s);\r\n	vec3 rgb = XYZtoRGB(xyz,d,s);\r\n	// brightness compensation\r\n	vec3 res = rgb * (1.0 + (temp + tint) / 10.0);\r\n	// preserve luminance\r\n	//vec3 res = SetLum(rgb, lum);\r\n\r\n	gl_FragColor = vec4(mix(base, res, col.a), col.a);\r\n\r\n}';
exports['vertex'] = 'varying vec4 coord;\r\n\r\nvoid main() {\r\n  coord = gl_TexCoord;\r\n  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\r\n}\r\n';
exports['vignette'] = 'varying vec4 coord;\r\nvarying vec4 compositeCoord;\r\n\r\nuniform sampler2D texture;\r\nuniform vec2 size;\r\nuniform vec2 position;\r\nuniform float spread;\r\nuniform float amount;\r\nuniform float highlights;\r\n/* new uniform */\r\nuniform float vignette_exposure;\r\nuniform float vignette_roundness;\r\nuniform float vignette_size;\r\nuniform vec2 imgSize;\r\nuniform vec4 crop;\r\nuniform mat4 rotationMatrix;\r\n\r\nfloat Lum(vec3 c){\r\n	return 0.299*c.r + 0.587*c.g + 0.114*c.b;\r\n}\r\n\r\nfloat BlendSoftLightf(float base, float blend){\r\n	return ((blend < 0.5) ? (2.0 * base * blend + base * base * (1.0 - 2.0 * blend)) : (sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend)));\r\n}\r\n\r\nvec3 BlendSoftLight(vec3 base, vec3 blend){\r\n	return vec3(BlendSoftLightf(base.r, blend.r), BlendSoftLightf(base.g, blend.g), BlendSoftLightf(base.b, blend.b));\r\n}\r\n\r\nfloat random(vec3 scale, float seed) {\r\n    /* use the fragment position for a different seed per-pixel */\r\n    return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\r\n}\r\n\r\nvoid main() {\r\n	float blur = min(1.0 - spread, 0.990);\r\n	float opacity = -amount;\r\n	float opacity_center = - vignette_exposure * 0.5;\r\n\r\n	/* base and crop */\r\n	vec3 base = texture2D(texture, coord.xy).rgb;\r\n	vec4 compositeCoords = (compositeCoord - 0.5) * rotationMatrix + 0.5;\r\n	vec2 cropCoords = (compositeCoords.xy - vec2(crop.x, 1.0-crop.w-crop.y)) / crop.zw;\r\n	/* scale size and limit normalized radius (midpoint in lightroom) */\r\n	float scale_size = mix(10.0,2.0,vignette_size);\r\n	float radius_normalized=1.0/scale_size;\r\n	/* order for length computation when vignette_roundness<0, (-1->maximum norm for square, 0->2nd order) */\r\n	float order_norm = 1.0/mix(0.5,0.05,max(0.0,-vignette_roundness));\r\n	/* scale of x and y when vignette_roundness>0, 0-> elipse 1->perfect circle */\r\n	vec2 iSize = imgSize * crop.zw;\r\n	float scalex=mix(1.0,max(1.0,iSize.x/iSize.y),vignette_roundness);\r\n	float scaley=mix(1.0,max(1.0,iSize.y/iSize.x),vignette_roundness);\r\n	vec2 size_weight = vec2(scalex,scaley);\r\n	/* highlight mask */\r\n	vec3 mask = vec3(1.0 - pow(Lum(base), 2.72) * highlights);\r\n	/* normalized distance to center */\r\n	vec2 coords = (cropCoords - 0.5)/size*(1.0 - spread/2.0);\r\n	vec2 dist = coords - position;\r\n	/* scale x or y (longer one) when roundness>=0 */\r\n	dist += dist*(size_weight-1.0)*float(vignette_roundness>=0.0);\r\n	/* compute p norm for normalized distance */\r\n	float dist_norm = pow(pow(abs(dist.x),order_norm)+pow(abs(dist.y),order_norm),1.0/order_norm);\r\n	/* offset, can add some randomess */\r\n	float offset = 1.0 + random(vec3(12.9898, 78.233, 151.7182), 0.0) / 100.0 * spread;\r\n	/* res for outer */\r\n	float weight = smoothstep(1.0, blur, dist_norm*scale_size*offset);\r\n	vec3 res = vec3(weight);\r\n	vec3 overlay = BlendSoftLight(base, mix(vec3(0.5), res/2.0, opacity));\r\n	res = overlay * mix(vec3(1.0), res, opacity*mask);\r\n	/* res for center */\r\n	float weight_center = 1.0-weight;\r\n	vec3 res_center = vec3(weight_center);\r\n	vec3 overlay_center = BlendSoftLight(base, mix(vec3(0.5), res_center/2.0, opacity_center));	\r\n	res_center = overlay_center * mix(vec3(1.0), res_center, opacity_center*mask);\r\n	/*final mix result */\r\n	float weight_mix = float(weight/(weight+weight_center));\r\n	gl_FragColor = vec4(mix(res,res_center,weight_mix),1.0);\r\n}\r\n';
exports['watermark'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform float opacity;\r\n\r\nvoid main() {\r\n  lowp vec4 col = texture2D(texture, coord.xy);\r\n  col.a *= opacity;\r\n\r\n	gl_FragColor = col;\r\n\r\n}';
exports['white_balance'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform vec2 white_balance;\r\n\r\nconst float epsilon = 0.000001;\r\nconst float mx = 1.0 - epsilon;\r\nconst float kE = 216.0 / 24389.0;\r\nconst float kK = 24389.0 / 27.0;\r\nconst float kKE = 8.0;\r\nconst float PI = 3.1415926535897932384626433832795;\r\n\r\nconst vec3 D50 = vec3(0.96422, 1.0, 0.82521);\r\nconst vec3 D65 = vec3(0.95047, 1.0, 1.08883);\r\n\r\nconst mat3 matRGBtoROMM = mat3(0.5293459296226501, 0.3300727903842926, 0.14058130979537964, 0.09837432950735092, 0.8734610080718994, 0.028164653107523918, 0.01688321679830551, 0.11767247319221497, 0.8654443025588989);\r\nconst mat3 matROMMtoRGB = mat3(2.0340757369995117, -0.727334201335907, -0.3067416846752167, -0.22881317138671875, 1.2317301034927368, -0.0029169507324695587, -0.008569774217903614, -0.1532866358757019, 1.1618564128875732);\r\n\r\nconst mat3 matROMMtoXYZ = mat3(0.7976749, 0.1351917, 0.0313534, 0.2880402, 0.7118741, 0.0000857, 0.0000000, 0.0000000, 0.8252100);\r\nconst mat3 matXYZtoROMM = mat3(1.3459433, -0.2556075, -0.0511118, -0.5445989, 1.5081673, 0.0205351, 0.0000000, 0.0000000, 1.2118128);\r\n\r\nconst mat3 matRGBtoXYZ = mat3(0.4124564390896922, 0.357576077643909, 0.18043748326639894, 0.21267285140562253, 0.715152155287818, 0.07217499330655958, 0.0193338955823293, 0.11919202588130297, 0.9503040785363679);\r\nconst mat3 matXYZtoRGB = mat3(3.2404541621141045, -1.5371385127977166, -0.498531409556016, -0.9692660305051868, 1.8760108454466942, 0.041556017530349834, 0.055643430959114726, -0.2040259135167538, 1.0572251882231791);\r\n\r\nconst mat3 matAdapt = mat3(0.8951, 0.2664, -0.1614, -0.7502, 1.7135, 0.0367, 0.0389, -0.0685, 1.0296);\r\nconst mat3 matAdaptInv = mat3(0.9869929054667123, -0.14705425642099013, 0.15996265166373125, 0.43230526972339456, 0.5183602715367776, 0.0492912282128556, -0.008528664575177328, 0.04004282165408487, 0.9684866957875502);\r\n\r\n\r\nvec3 hsv2rgb(in vec3 c) {\r\n    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\r\n    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\r\n    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\r\n}\r\n\r\nvec3 rgb2xyz(in vec3 rgb, in vec3 whiteRGB, in vec3 whiteXYZ) {\r\n  vec3 s = whiteRGB * matAdapt;\r\n  vec3 d = whiteXYZ * matAdapt;\r\n	vec3 xyz;\r\n\r\n	xyz = rgb * matRGBtoXYZ;\r\n\r\n	// adaption\r\n	xyz = (xyz * matAdapt) * (d/s) * matAdaptInv;\r\n\r\n	return xyz;\r\n}\r\n\r\nvec3 xyz2rgb(in vec3 xyz, in vec3 whiteRGB, in vec3 whiteXYZ) {\r\n  vec3 s = whiteRGB * matAdapt;\r\n  vec3 d = whiteXYZ * matAdapt;\r\n	vec3 rgb;\r\n\r\n	// adaption\r\n	xyz = (xyz * matAdapt) * (s/d) * matAdaptInv;\r\n\r\n	rgb = xyz * matXYZtoRGB;\r\n\r\n	return rgb;\r\n}\r\n\r\nvec3 xyz2lab(in vec3 xyz, in vec3 refWhite) {\r\n	vec3 r = xyz / refWhite;\r\n\r\n	vec3 f = vec3(\r\n		(r.x > kE) ? pow(r.x, 1.0 / 3.0) : ((kK * r.x + 16.0) / 116.0),\r\n		(r.y > kE) ? pow(r.y, 1.0 / 3.0) : ((kK * r.y + 16.0) / 116.0),\r\n		(r.z > kE) ? pow(r.z, 1.0 / 3.0) : ((kK * r.z + 16.0) / 116.0)\r\n	);\r\n\r\n	return vec3(\r\n    116.0 * f.y - 16.0,\r\n    500.0 * (f.x - f.y),\r\n    200.0 * (f.y - f.z)\r\n  );\r\n}\r\n\r\nvec3 lab2xyz(in vec3 lab, in vec3 refWhite) {\r\n	float fy = (lab.x + 16.0) / 116.0;\r\n	float fx = 0.002 * lab.y + fy;\r\n	float fz = fy - 0.005 * lab.z;\r\n\r\n	float fx3 = fx * fx * fx;\r\n	float fz3 = fz * fz * fz;\r\n\r\n	vec3 xyz = vec3(\r\n    (fx3 > kE) ? fx3 : ((116.0 * fx - 16.0) / kK),\r\n    (lab.x > kKE) ? pow((lab.x + 16.0) / 116.0, 3.0) : (lab.x / kK),\r\n    (fz3 > kE) ? fz3 : ((116.0 * fz - 16.0) / kK)\r\n  );\r\n\r\n  return xyz * refWhite;\r\n}\r\n\r\nvec3 lab2lch(in vec3 lab) {\r\n	vec3 lch = vec3(\r\n		lab.x,\r\n		sqrt(lab.y * lab.y + lab.z * lab.z),\r\n		180.0 * atan(lab.z, lab.y) / PI\r\n	);\r\n	if (lch.z < 0.0) lch.z += 360.0;\r\n	return lch;\r\n}\r\n\r\nvec3 lch2lab(in vec3 lch) {\r\n	return vec3(\r\n		lch.x,\r\n		lch.y * cos(lch.z * PI / 180.0),\r\n		lch.y * sin(lch.z * PI / 180.0)\r\n	);\r\n}\r\n\r\nfloat luma(in vec3 color){\r\n	return dot(color, vec3(0.242655, 0.755158, 0.002187));\r\n}\r\n\r\nvec3 invCompand(in vec3 companded) {\r\n  return vec3(\r\n    (companded.r <= 0.04045) ? (companded.r / 12.92) : pow((companded.r + 0.055) / 1.055, 2.4),\r\n    (companded.g <= 0.04045) ? (companded.g / 12.92) : pow((companded.g + 0.055) / 1.055, 2.4),\r\n    (companded.b <= 0.04045) ? (companded.b / 12.92) : pow((companded.b + 0.055) / 1.055, 2.4)\r\n  );\r\n}\r\n\r\nfloat invCompand(in float companded) {\r\n  return (companded <= 0.04045) ? (companded / 12.92) : pow((companded + 0.055) / 1.055, 2.4);\r\n}\r\n\r\nvec3 compand(in vec3 linear) {\r\n  return vec3(\r\n    (linear.r <= 0.0031308) ? (linear.r * 12.92) : (1.055 * pow(linear.r, 1.0 / 2.4) - 0.055),\r\n    (linear.g <= 0.0031308) ? (linear.g * 12.92) : (1.055 * pow(linear.g, 1.0 / 2.4) - 0.055),\r\n    (linear.b <= 0.0031308) ? (linear.b * 12.92) : (1.055 * pow(linear.b, 1.0 / 2.4) - 0.055)\r\n  );\r\n}\r\n\r\nfloat Lum(vec3 c){\r\n//  vec3 coeff = vec3(0.299, 0.587, 0.114);\r\n//	return dot(c, coeff * matRGBtoROMM);\r\n	return dot(c, vec3(0.242655, 0.755158, 0.002187));\r\n//	return 0.299*c.r + 0.587*c.g + 0.114*c.b;\r\n}\r\n\r\nvec3 ClipColor(vec3 c){\r\n	float l = Lum(c);\r\n	float n = min(min(c.r, c.g), c.b);\r\n	float x = max(max(c.r, c.g), c.b);\r\n\r\n	if (n < 0.0) c = (c-l)*l / (l-n) + l;\r\n	if (x > 1.0) c = (c-l) * (1.0-l) / (x-l) + l;\r\n\r\n	return c;\r\n}\r\n\r\nvec3 SetLum(vec3 c, float l){\r\n	float d = l - Lum(c);\r\n\r\n	c.r = c.r + d;\r\n	c.g = c.g + d;\r\n	c.b = c.b + d;\r\n\r\n	return c;\r\n//	return ClipColor(c);\r\n}\r\nconst vec3 CCT2K = vec3(1.274335, 1.0, 0.145233);\r\nconst vec3 CCT4K = vec3(1.009802, 1.0, 0.644496);\r\nconst vec3 CCT20K = vec3(0.995451, 1.0, 1.886109);\r\n\r\nvoid main() {\r\n  lowp vec4 col = texture2D(texture, coord.xy);\r\n	vec3 res = col.rgb;\r\n\r\n//	float mask = col.a;\r\n\r\n  res = invCompand(res);\r\n\r\n	float lum = Lum(res * matRGBtoROMM);\r\n\r\n  vec3 xyz = rgb2xyz(res, D65, D50);\r\n  vec3 lab = xyz2lab(xyz, D50);\r\n  float sat = sqrt(lab.y * lab.y + lab.z * lab.z) / 100.0;\r\n  float mask = invCompand(lab.x / 100.0 * (1.0 - sat));\r\n\r\n  vec3 whiteRGB = hsv2rgb(vec3(white_balance.x, white_balance.y, 1.0));\r\n\r\n//  vec3 whitePointIn = rgb2xyz(whiteRGB, D65, D65);\r\n  vec3 whitePointOut = rgb2xyz(whiteRGB, D65, D50);\r\n  whitePointOut.y = 1.0;\r\n\r\n  res = xyz2lab(xyz, mix(whitePointOut, CCT20K, 1.0));\r\n  res = xyz2rgb(lab2xyz(res, D50), D65, D50);\r\n\r\n//  res = rgb2xyz(res, mix(whitePointIn, D65, 1.0), mix(whitePointOut, D50, 0.0));\r\n\r\n//  vec3 ilab = xyz2lab(res, D50);\r\n//  ilab.x = lab.x;\r\n//  res = lab2xyz(ilab, D50);\r\n//  res = xyz2rgb(res, D65, mix(whitePointOut, D50, 1.0));\r\n\r\n//  res += lum * 0.5;\r\n  res = clamp(res, 0.0, 1.0);\r\n\r\n//  res = SetLum(res * matRGBtoROMM, clamp(lum * 1.0, 0.0, 1.0));\r\n//  res = res * matROMMtoRGB;\r\n\r\n//  res = vec3(lum) * matROMMtoRGB;\r\n\r\n//  res = vec3(lum * (1.0 - sat));\r\n\r\n  res = compand(res);\r\n\r\n	gl_FragColor = vec4(res, 1.0);\r\n\r\n}';
exports['whites_blacks'] = 'varying vec4 coord;\r\n\r\nuniform sampler2D texture;\r\nuniform float whites;\r\nuniform float blacks;\r\n\r\n// Luminance and Saturation functions\r\n// Adapted from: http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf\r\n\r\nfloat Lum(vec3 c){\r\n	return 0.299*c.r + 0.587*c.g + 0.114*c.b;\r\n}\r\n\r\nvec3 ClipColor(vec3 c){\r\n	float l = Lum(c);\r\n	float n = min(min(c.r, c.g), c.b);\r\n	float x = max(max(c.r, c.g), c.b);\r\n\r\n	if (n < 0.0) c = max((c-l)*l / (l-n) + l, 0.0);\r\n	if (x > 1.0) c = min((c-l) * (1.0-l) / (x-l) + l, 1.0);\r\n\r\n	return c;\r\n}\r\n\r\nvec3 SetLum(vec3 c, float l){\r\n	c += l - Lum(c);\r\n\r\n	return ClipColor(c);\r\n}\r\n\r\nconst float wb = 5.336778471840789E-03;\r\nconst float wc = 6.664243592410049E-01;\r\nconst float wd = 3.023761372137289E+00;\r\nconst float we = -6.994413182098681E+00;\r\nconst float wf = 3.293987131616894E+00;\r\nconst float wb2 = -1.881032803339283E-01;\r\nconst float wc2 = 2.812945435181010E+00;\r\nconst float wd2 = -1.495096839176419E+01;\r\nconst float we2 = 3.349416467551858E+01;\r\nconst float wf2 = -3.433024909629221E+01;\r\nconst float wg2 = 1.314308200442166E+01;\r\n\r\nconst float bb = 8.376727344831676E-01;\r\nconst float bc = -3.418495999327269E+00;\r\nconst float bd = 8.078054837335609E+00;\r\nconst float be = -1.209938703324099E+01;\r\nconst float bf = 9.520315785756406E+00;\r\nconst float bg = -2.919340722745241E+00;\r\nconst float ba2 = 5.088652898054800E-01;\r\nconst float bb2 = -9.767371127415029E+00;\r\nconst float bc2 = 4.910705739925203E+01;\r\nconst float bd2 = -1.212150899746360E+02;\r\nconst float be2 = 1.606205314047741E+02;\r\nconst float bf2 = -1.085660871669277E+02;\r\nconst float bg2 = 2.931582214601388E+01;\r\n\r\nvoid main() {\r\n	lowp vec3 base = texture2D(texture, coord.xy).rgb;\r\n\r\n	float maxx = max(base.r, max(base.g, base.b));\r\n	float minx = min(base.r, min(base.g, base.b));\r\n	float lum = (maxx+minx)/2.0;\r\n	float x = lum;\r\n	float x2 = x*x;\r\n	float x3 = x2*x;\r\n	float lum_pos, lum_neg;\r\n	vec3 res;\r\n\r\n	// whites\r\n	lum_pos = wb*x + wc*x2+ wd*x3 + we*x2*x2 + wf*x2*x3;\r\n	lum_pos = min(lum_pos,1.0-lum);\r\n	lum_neg = wb2*x + wc2*x2+ wd2*x3 + we2*x2*x2 + wf2*x2*x3 + wg2*x3*x3;\r\n	lum_neg = max(lum_neg,-lum);\r\n	res = whites>=0.0 ? base*(lum_pos*whites+lum)/lum : base * (lum-lum_neg*whites)/lum;\r\n	res = clamp(res, 0.0, 1.0);\r\n\r\n	// blacks\r\n	lum_pos = bb*x + bc*x2+ bd*x3 + be*x2*x2 + bf*x2*x3 + bg*x3*x3;\r\n	lum_pos = min(lum_pos,1.0-lum);\r\n	lum_neg = lum<=0.23 ? -lum : ba2 + bb2*x + bc2*x2+ bd2*x3 + be2*x2*x2 + bf2*x2*x3 + bg2*x3*x3;\r\n	lum_neg = max(lum_neg,-lum);\r\n	res = blacks>=0.0 ? res*(lum_pos*blacks+lum)/lum : res * (lum-lum_neg*blacks)/lum;\r\n	res = clamp(res, 0.0, 1.0);\r\n\r\n	gl_FragColor = vec4(SetLum(base, Lum(res)), 1.0);\r\n}\r\n';
},{}],375:[function(require,module,exports){
var CubicSpline, MonotonicCubicSpline;

MonotonicCubicSpline = (function() {
  function MonotonicCubicSpline(x, y) {
    var alpha, beta, delta, dist, i, j, len, len1, m, n, o, p, q, r, ref, ref1, ref2, ref3, tau, to_fix, v;
    n = x.length;
    delta = [];
    m = [];
    alpha = [];
    beta = [];
    dist = [];
    tau = [];
    for (i = j = 0, ref = n - 1; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
      delta[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]);
      if (i > 0) {
        m[i] = (delta[i - 1] + delta[i]) / 2;
      }
    }
    m[0] = delta[0];
    m[n - 1] = delta[n - 2];
    to_fix = [];
    for (i = o = 0, ref1 = n - 1; 0 <= ref1 ? o < ref1 : o > ref1; i = 0 <= ref1 ? ++o : --o) {
      if (delta[i] === 0) {
        to_fix.push(i);
      }
    }
    for (p = 0, len = to_fix.length; p < len; p++) {
      i = to_fix[p];
      m[i] = m[i + 1] = 0;
    }
    for (i = q = 0, ref2 = n - 1; 0 <= ref2 ? q < ref2 : q > ref2; i = 0 <= ref2 ? ++q : --q) {
      alpha[i] = m[i] / delta[i];
      beta[i] = m[i + 1] / delta[i];
      dist[i] = Math.pow(alpha[i], 2) + Math.pow(beta[i], 2);
      tau[i] = 3 / Math.sqrt(dist[i]);
    }
    to_fix = [];
    for (i = r = 0, ref3 = n - 1; 0 <= ref3 ? r < ref3 : r > ref3; i = 0 <= ref3 ? ++r : --r) {
      if (dist[i] > 9) {
        to_fix.push(i);
      }
    }
    for (v = 0, len1 = to_fix.length; v < len1; v++) {
      i = to_fix[v];
      m[i] = tau[i] * alpha[i] * delta[i];
      m[i + 1] = tau[i] * beta[i] * delta[i];
    }
    this.x = x.slice(0, n);
    this.y = y.slice(0, n);
    this.m = m;
  }

  MonotonicCubicSpline.prototype.interpolate = function(x) {
    var h, h00, h01, h10, h11, i, j, ref, t, t2, t3, y;
    for (i = j = ref = this.x.length - 2; ref <= 0 ? j <= 0 : j >= 0; i = ref <= 0 ? ++j : --j) {
      if (this.x[i] <= x) {
        break;
      }
    }
    h = this.x[i + 1] - this.x[i];
    t = (x - this.x[i]) / h;
    t2 = Math.pow(t, 2);
    t3 = Math.pow(t, 3);
    h00 = 2 * t3 - 3 * t2 + 1;
    h10 = t3 - 2 * t2 + t;
    h01 = -2 * t3 + 3 * t2;
    h11 = t3 - t2;
    y = h00 * this.y[i] + h10 * h * this.m[i] + h01 * this.y[i + 1] + h11 * h * this.m[i + 1];
    return y;
  };

  return MonotonicCubicSpline;

})();

CubicSpline = (function() {
  function CubicSpline(x, a, d0, dn) {
    var b, c, clamped, d, h, i, j, k, l, n, o, p, q, ref, ref1, ref2, ref3, s, u, y, z;
    if (!((x != null) && (a != null))) {
      return;
    }
    clamped = (d0 != null) && (dn != null);
    n = x.length - 1;
    h = [];
    y = [];
    l = [];
    u = [];
    z = [];
    c = [];
    b = [];
    d = [];
    k = [];
    s = [];
    for (i = j = 0, ref = n; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
      h[i] = x[i + 1] - x[i];
      k[i] = a[i + 1] - a[i];
      s[i] = k[i] / h[i];
    }
    if (clamped) {
      y[0] = 3 * (a[1] - a[0]) / h[0] - 3 * d0;
      y[n] = 3 * dn - 3 * (a[n] - a[n - 1]) / h[n - 1];
    }
    for (i = o = 1, ref1 = n; 1 <= ref1 ? o < ref1 : o > ref1; i = 1 <= ref1 ? ++o : --o) {
      y[i] = 3 / h[i] * (a[i + 1] - a[i]) - 3 / h[i - 1] * (a[i] - a[i - 1]);
    }
    if (clamped) {
      l[0] = 2 * h[0];
      u[0] = 0.5;
      z[0] = y[0] / l[0];
    } else {
      l[0] = 1;
      u[0] = 0;
      z[0] = 0;
    }
    for (i = p = 1, ref2 = n; 1 <= ref2 ? p < ref2 : p > ref2; i = 1 <= ref2 ? ++p : --p) {
      l[i] = 2 * (x[i + 1] - x[i - 1]) - h[i - 1] * u[i - 1];
      u[i] = h[i] / l[i];
      z[i] = (y[i] - h[i - 1] * z[i - 1]) / l[i];
    }
    if (clamped) {
      l[n] = h[n - 1] * (2 - u[n - 1]);
      z[n] = (y[n] - h[n - 1] * z[n - 1]) / l[n];
      c[n] = z[n];
    } else {
      l[n] = 1;
      z[n] = 0;
      c[n] = 0;
    }
    for (i = q = ref3 = n - 1; ref3 <= 0 ? q <= 0 : q >= 0; i = ref3 <= 0 ? ++q : --q) {
      c[i] = z[i] - u[i] * c[i + 1];
      b[i] = (a[i + 1] - a[i]) / h[i] - h[i] * (c[i + 1] + 2 * c[i]) / 3;
      d[i] = (c[i + 1] - c[i]) / (3 * h[i]);
    }
    this.x = x.slice(0, +n + 1 || 9e9);
    this.a = a.slice(0, n);
    this.b = b;
    this.c = c.slice(0, n);
    this.d = d;
  }

  CubicSpline.prototype.derivative = function() {
    var c, d, j, len, len1, o, p, ref, ref1, ref2, s, x;
    s = new this.constructor();
    s.x = this.x.slice(0, this.x.length);
    s.a = this.b.slice(0, this.b.length);
    ref = this.c;
    for (j = 0, len = ref.length; j < len; j++) {
      c = ref[j];
      s.b = 2 * c;
    }
    ref1 = this.d;
    for (o = 0, len1 = ref1.length; o < len1; o++) {
      d = ref1[o];
      s.c = 3 * d;
    }
    for (x = p = 0, ref2 = this.d.length; 0 <= ref2 ? p < ref2 : p > ref2; x = 0 <= ref2 ? ++p : --p) {
      s.d = 0;
    }
    return s;
  };

  CubicSpline.prototype.interpolate = function(x) {
    var deltaX, i, j, ref, y;
    for (i = j = ref = this.x.length - 1; ref <= 0 ? j <= 0 : j >= 0; i = ref <= 0 ? ++j : --j) {
      if (this.x[i] <= x) {
        break;
      }
    }
    deltaX = x - this.x[i];
    y = this.a[i] + this.b[i] * deltaX + this.c[i] * Math.pow(deltaX, 2) + this.d[i] * Math.pow(deltaX, 3);
    return y;
  };

  return CubicSpline;

})();

exports.monotonic = MonotonicCubicSpline;

exports.cubic = CubicSpline;


},{}],376:[function(require,module,exports){
var addListener, cloneState, contexts, currentState, defaults, disableAll, enableAll, events, flushPending, getDisabled, getHistory, getPrevState, history, index, localDefaults, makeCompatible, map, pending, pendingMessage, prepareAdjustments, redo, register, removeListener, saveState, set, setDisabled, setHistory, setIndex, setPending, trigger, undo, utils;

utils = require("./utils.coffee");

index = 0;

map = {};

contexts = {};

defaults = {};

currentState = {};

pending = false;

pendingMessage = null;

history = [];

localDefaults = {
  exposure: 0,
  contrast: 0,
  temperature: 0,
  tint: 0,
  highlights: 0,
  shadows: 0,
  clarity: 0,
  saturation: 0,
  vibrance: 0,
  shadows_hue: 0,
  shadows_saturation: 0,
  balance: -1,
  mosaic_size: 0,
  blur: 0
};

register = function(key, val, func, context) {
  currentState[key] = val;
  defaults[key] = val;
  map[key] = func;
  if (context) {
    return contexts[key] = context;
  }
};

set = function(state) {
  var key, val;
  for (key in state) {
    val = state[key];
    currentState[key] = val;
    if (map[key]) {
      map[key].call(contexts[key] || this, val);
    }
  }
  flushPending();
  return pending = false;
};

setPending = function(val) {
  return pending = !!val;
};

setDisabled = function(state) {
  var key, ref, results, val;
  results = [];
  for (key in state) {
    val = state[key];
    results.push((ref = contexts[key]) != null ? ref.disabled = val : void 0);
  }
  return results;
};

getDisabled = function() {
  var context, disabled, key;
  disabled = {};
  for (key in contexts) {
    context = contexts[key];
    disabled[key] = context.disabled;
  }
  return disabled;
};

disableAll = function() {
  var context, key, results;
  results = [];
  for (key in contexts) {
    context = contexts[key];
    results.push(context.disabled = true);
  }
  return results;
};

enableAll = function() {
  var context, key, results;
  results = [];
  for (key in contexts) {
    context = contexts[key];
    results.push(context.disabled = false);
  }
  return results;
};

makeCompatible = function(state) {
  var adjustment, j, len, ref;
  state = utils.assign(defaults, state);
  ref = state.local_adjustments;
  for (j = 0, len = ref.length; j < len; j++) {
    adjustment = ref[j];
    adjustment.adjustments = utils.assign(localDefaults, adjustment.adjustments);
  }
  return state;
};

prepareAdjustments = function(adjustments) {
  adjustments = cloneState(makeCompatible(adjustments));
  if (utils.shallowEqual(adjustments.crop, defaults.crop)) {
    delete adjustments.crop;
  }
  delete adjustments.faces;
  if (!adjustments.text.length) {
    delete adjustments.text;
  }
  if ((adjustments.rotation != null) && adjustments.rotation === 0) {
    delete adjustments.rotation;
  }
  delete adjustments.rotate90;
  delete adjustments.distortion_horizontal;
  delete adjustments.distortion_vertical;
  delete adjustments.distortion_mesh;
  return adjustments;
};

cloneState = function(state) {
  return utils.clone(state);
};

getPrevState = function() {
  var state;
  state = history.length ? history[index].state : defaults;
  return makeCompatible(cloneState(state));
};

setIndex = function(i) {
  index = utils.clamp(i, 0, history.length - 1);
  return set(getPrevState());
};

undo = function() {
  return setIndex(index - 1);
};

redo = function() {
  return setIndex(index + 1);
};

flushPending = function() {
  var state;
  if (!pendingMessage) {
    return;
  }
  state = cloneState(currentState);
  if (history.length - 1 > index) {
    history.splice(index + 1, Number.MAX_VALUE);
  }
  history.push({
    message: pendingMessage.message,
    state: state,
    changed: pendingMessage.changed
  });
  index = history.length - 1;
  pendingMessage.complete();
  return pendingMessage = null;
};

saveState = function(message, changed, done) {
  pendingMessage = {
    message: message || "Update",
    changed: changed,
    complete: done
  };
  if (!pending) {
    return flushPending();
  }
};

setHistory = function(arr) {
  var params;
  params = [0, Number.MAX_VALUE].concat(arr);
  Array.prototype.splice.apply(history, params);
  index = history.length - 1;
  return set(getPrevState());
};

getHistory = function() {
  return history.slice(0);
};

trigger = function() {
  var event, j, len, listener, ref;
  event = arguments[0];
  if (events[event]) {
    ref = events[event];
    for (j = 0, len = ref.length; j < len; j++) {
      listener = ref[j];
      listener.call(this, arguments[1], arguments[2], arguments[3]);
    }
  }
  return event;
};

addListener = function(event, func) {
  if (!events[event]) {
    events[event] = [];
  }
  return events[event].push(func);
};

removeListener = function(event, func) {
  if (!events[event]) {
    return;
  }
  index = events[event].indexOf(func);
  if (index >= 0) {
    return events[event].splice(index, 1);
  }
};

events = {
  "thumbnail": [],
  "history": [],
  "screen": []
};

module.exports = {
  map: map,
  defaults: defaults,
  localDefaults: localDefaults,
  currentState: currentState,
  prevLocalAdjustments: [],
  history: history,
  setHistory: setHistory,
  getHistory: getHistory,
  register: register,
  set: set,
  setPending: setPending,
  setDisabled: setDisabled,
  getDisabled: getDisabled,
  disableAll: disableAll,
  enableAll: enableAll,
  getPrevState: getPrevState,
  setIndex: setIndex,
  getIndex: function() {
    return index;
  },
  undo: undo,
  redo: redo,
  saveState: saveState,
  makeCompatible: makeCompatible,
  prepareAdjustments: prepareAdjustments,
  on: addListener,
  off: removeListener,
  trigger: trigger,
  changed: true
};


},{"./utils.coffee":378}],377:[function(require,module,exports){
var canvas, compositor, createTexture, ctx, drawText, filters, generateLayers, getLines, getMetrics, getStyle, getWidth, gl, propsToTest, renderTextMask, renderer, setLayerMatrix, state, utils;

gl = require("./context.coffee");

renderer = require("./renderer.coffee");

compositor = require("./compositor.coffee");

filters = require("./filters.coffee");

utils = require("./utils.coffee");

state = require("./state.coffee");

state.register("text", [], function(textLayers) {
  var i, k, len, results, text;
  results = [];
  for (i = k = 0, len = textLayers.length; k < len; i = ++k) {
    text = textLayers[i];
    results.push(setLayerMatrix(gl.textLayers[i], text));
  }
  return results;
});

state.register("textInvertedColor", [1, 1, 1, 1]);

setLayerMatrix = function(layer, text) {
  var h, matrix, projectionMatrix, projectionMatrixInv, ratio, rotateMatrix, scale, scaleMatrix, size, translateMatrix, w;
  if (!layer) {
    return;
  }
  ratio = gl.texture.width / gl.texture.height;
  if (text.orientation % 2) {
    size = [gl.texture.height, gl.texture.width];
  } else {
    size = [gl.texture.width, gl.texture.height];
  }
  w = size[0], h = size[1];
  scale = text.scale || [1, 1];
  translateMatrix = GL.Matrix.translate((text.position[0] - 0.5) * 2, -(text.position[1] - 0.5) * 2 * ratio, 0);
  rotateMatrix = GL.Matrix.rotate(-text.angle - text.orientation, 0, 0, 1);
  scaleMatrix = GL.Matrix.scale((layer.compositeWidth / gl.texture.width) * scale[0], (layer.compositeHeight / gl.texture.height) * scale[1], 1);
  projectionMatrix = GL.Matrix.ortho(w / -2, w / 2, h / -2, h / 2, -1, 1);
  projectionMatrixInv = projectionMatrix.inverse();
  matrix = GL.Matrix.identity();
  matrix = GL.Matrix.multiply(matrix, translateMatrix);
  matrix = GL.Matrix.multiply(matrix, projectionMatrix);
  matrix = GL.Matrix.multiply(matrix, rotateMatrix);
  matrix = GL.Matrix.multiply(matrix, projectionMatrixInv);
  matrix = GL.Matrix.multiply(matrix, scaleMatrix);
  return layer.matrix = matrix;
};

propsToTest = {
  align: true,
  font_name: true,
  font_size: true,
  line_height: true,
  letter_spacing: true,
  text: true,
  shadow_blur: true,
  shadow_offset: true,
  shadow_color: true,
  shadow_opacity: true,
  mode: true,
  wrap: true,
  scale: true
};

renderTextMask = function() {
  var prevText, text;
  text = state.currentState.text;
  prevText = state.prevText || [];
  if (text && text.length) {
    if (!utils.deepEqual(prevText, text, true, propsToTest)) {
      logTime("generate text mask");
      prevText = text;
      generateLayers(text, gl.texture, createTexture);
      logTimeEnd("generate text mask");
    }
  }
  return state.prevText = prevText;
};

renderer.addRenderDependency(renderTextMask);

canvas = document.createElement('canvas');

ctx = canvas.getContext('2d');

createTexture = function(options) {
  var index, text, texture;
  canvas = options.canvas, text = options.text, index = options.index;
  texture = gl.textLayers[index] || new GL.Texture(1, 1, {
    format: gl.context.RGBA
  });
  texture.width = canvas.width;
  texture.height = canvas.height;
  texture.compositeWidth = options.compositeWidth;
  texture.compositeHeight = options.compositeHeight;
  texture.update = function(text) {
    return setLayerMatrix(this, text);
  };
  gl.textLayers[index] = texture;
  texture.update(text);
  gl.context.bindTexture(gl.context.TEXTURE_2D, texture.id);
  gl.context.pixelStorei(gl.context.UNPACK_FLIP_Y_WEBGL, 1);
  gl.context.pixelStorei(gl.context.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
  gl.context.texParameteri(gl.context.TEXTURE_2D, gl.context.TEXTURE_MAG_FILTER, gl.context.LINEAR);
  gl.context.texParameteri(gl.context.TEXTURE_2D, gl.context.TEXTURE_MIN_FILTER, gl.context.LINEAR);
  return gl.context.texImage2D(gl.context.TEXTURE_2D, 0, texture.format, texture.format, texture.type, canvas);
};

getWidth = function(str) {
  return ctx.measureText(str).width;
};

drawText = function(str, x, y, lineWidth, spacing, mode) {
  var char, charWidth, remainingWidth;
  while (true) {
    char = str.substr(0, 1);
    str = str.substr(1);
    if (mode === "outline") {
      ctx.strokeText(char, x, y);
    } else {
      ctx.fillText(char, x, y);
    }
    if (str === "") {
      remainingWidth = 0;
    } else {
      remainingWidth = getWidth(str);
    }
    charWidth = lineWidth - remainingWidth;
    x += charWidth + spacing;
    lineWidth = remainingWidth;
    if (str === "") {
      return x;
    }
  }
};

exports.getLines = getLines = function(text, style) {
  var i, k, l, len, len1, line, lines, newWords, ref, spaceWidth, spacing, width, word, wordWidth, words, wrapWidth;
  spacing = style.spacing, wrapWidth = style.wrapWidth;
  ctx.font = style.font;
  if (!wrapWidth) {
    return text.split(/[\r\n]/g);
  }
  lines = [];
  spaceWidth = getWidth(" ") + spacing;
  wrapWidth = wrapWidth >> 0;
  ref = text.split(/[\r\n]/g);
  for (k = 0, len = ref.length; k < len; k++) {
    line = ref[k];
    newWords = [];
    width = 0;
    words = line.split(" ");
    for (i = l = 0, len1 = words.length; l < len1; i = ++l) {
      word = words[i];
      wordWidth = getWidth(word) + spacing * word.length;
      width += wordWidth;
      if (width > wrapWidth) {
        if (i > 0) {
          lines.push(newWords.join(" "));
          newWords = [];
          width = wordWidth;
        }
      } else {
        width += spaceWidth;
      }
      newWords.push(word);
    }
    lines.push(newWords.join(" "));
  }
  return lines;
};

exports.getMetrics = getMetrics = function(text, style) {
  var k, len, line, lineHeight, lines, maxHeight, maxWidth, padding, spacing, textWidth, widths, wrapWidth;
  padding = style.padding, spacing = style.spacing, lineHeight = style.lineHeight, wrapWidth = style.wrapWidth;
   var str = text.text.replace(/ +$/g, '');
  lines = getLines(str, style);
  maxWidth = 0;
  widths = [];
  for (k = 0, len = lines.length; k < len; k++) {
    line = lines[k];
    textWidth = getWidth(line);
    maxWidth = Math.max(maxWidth, textWidth + spacing * line.length);
    widths.push(textWidth);
  }
  maxHeight = lineHeight * lines.length + padding.bottom;
  if (wrapWidth) {
    maxWidth = Math.max(maxWidth, wrapWidth);
  }
  return {
    maxWidth: maxWidth,
    maxHeight: maxHeight,
    lines: lines,
    widths: widths
  };
};

exports.getStyle = getStyle = function(text, width) {
  var fontSize, padding, wrapWidth;
  if (width == null) {
    width = gl.texture.width;
  }
  if (text.type === "artwork") {
    fontSize = text.font_size * width * 0.3;
    padding = {
      top: 0,
      bottom: 0,
      left: 0,
      right: 0
    };
  } else {
    fontSize = text.font_size * width * 0.1;
    padding = {
      top: 0,
      bottom: Math.max(0, 1.2 - text.line_height) * fontSize,
      left: fontSize * 0.2,
      right: fontSize * 0.2
    };
    if (text.wrap) {
      wrapWidth = width * text.wrap;
      wrapWidth -= padding.left + padding.right;
    }
  }
  return {
    wrapWidth: wrapWidth,
    padding: padding,
    fontSize: fontSize,
    font: text.font_style + " " + text.font_weight + " " + fontSize + "px " + text.font_family,
    lineHeight: fontSize * text.line_height,
    strokeWidth: fontSize * 0.01,
    spacing: fontSize * text.letter_spacing,
    scale: text.scale || [1, 1],
    color: "white"
  };
};

exports.getSize = function(text, options) {
  var metrics, style;
  if (options == null) {
    options = gl.texture;
  }
  style = getStyle(text, options.width);
  metrics = getMetrics(text, style);
  return {
    offsetWidth: (metrics.maxWidth + style.padding.left + style.padding.right) / options.width,
    offsetHeight: (metrics.maxHeight + style.padding.top + style.padding.bottom) / options.width,
    width: metrics.maxWidth / options.width,
    height: metrics.maxHeight / options.width
  };
};

exports.generateLayers = generateLayers = function(textLayers, options, callback, forceUpdate) {
  var canvasScale, compositeHeight, compositeWidth, gap, i, index, j, k, l, len, len1, len2, len3, line, lineHeight, lines, m, max, maxHeight, maxScale, maxSize, maxWidth, n, padding, ref, results, scale, spacing, startX, startY, style, text, totalWidth, width, widths, word, wordWidths, words, x, y;
  maxSize = gl.maxTextureSize;
  results = [];
  for (index = k = 0, len = textLayers.length; k < len; index = ++k) {
    text = textLayers[index];
    if (utils.deepEqual(state.prevText[index], text, true, propsToTest) && !forceUpdate) {
      continue;
    }
    style = getStyle(text, options.width);
    padding = style.padding, spacing = style.spacing, lineHeight = style.lineHeight, scale = style.scale;
    ref = getMetrics(text, style), lines = ref.lines, widths = ref.widths, maxWidth = ref.maxWidth, maxHeight = ref.maxHeight;
    compositeWidth = (maxWidth + padding.left + padding.right) >> 0;
    compositeHeight = maxHeight >> 0;
    max = Math.max(compositeWidth * scale[0], compositeHeight * scale[1]);
    maxScale = Math.min(max, maxSize) / max;
    canvasScale = [Math.abs(maxScale * scale[0]), Math.abs(maxScale * scale[1])];
    canvas.width = compositeWidth * canvasScale[0] >> 0;
    canvas.height = compositeHeight * canvasScale[1] >> 0;
    ctx.scale(canvasScale[0], canvasScale[1]);
    startX = padding.left;
    x = startX;
    y = maxHeight / 2;
    startY = y - (lines.length - 1) * lineHeight / 2 - padding.bottom / 2;
    if (text.mode === "outline") {
      ctx.lineWidth = style.strokeWidth;
      ctx.strokeStyle = style.color;
    } else {
      ctx.fillStyle = style.color;
    }
    ctx.font = style.font;
    ctx.textBaseline = "middle";
    ctx.textAlign = "left";
    if (text.align === "justify") {
      for (i = l = 0, len1 = lines.length; l < len1; i = ++l) {
        line = lines[i];
        max = maxWidth - spacing * line.length;
        y = startY + lineHeight * i;
        if (widths[i] < max) {
          words = line.split(/\s/g);
          totalWidth = 0;
          wordWidths = words.map(function(word) {
            var textWidth;
            textWidth = ctx.measureText(word).width;
            totalWidth += textWidth;
            return textWidth;
          });
          gap = (max - totalWidth) / (words.length - 1) + spacing;
          x = startX;
          for (j = m = 0, len2 = words.length; m < len2; j = ++m) {
            word = words[j];
            x = drawText(word, x, y, wordWidths[j], spacing, text.mode) + gap;
          }
        } else {
          drawText(line, startX, y, widths[i], spacing, text.mode);
        }
      }
    } else {
      for (i = n = 0, len3 = lines.length; n < len3; i = ++n) {
        line = lines[i];
        width = widths[i];
        max = maxWidth - spacing * line.length;
        x = (function() {
          switch (text.align) {
            case "right":
              return startX + max - width;
            case "center":
              return startX + (max - width) / 2;
            default:
              return startX;
          }
        })();
        y = startY + lineHeight * i;
        drawText(line, x, y, width, spacing, text.mode);
      }
    }
    results.push(callback({
      context: ctx,
      canvas: canvas,
      compositeWidth: compositeWidth,
      compositeHeight: compositeHeight,
      text: text,
      index: index
    }));
  }
  return results;
};

exports.forceUpdate = function() {
  var text;
  text = state.currentState.text;
  generateLayers(text, gl.texture, createTexture, true);
  return renderer.renderCacheTexture();
};


},{"./compositor.coffee":364,"./context.coffee":365,"./filters.coffee":367,"./renderer.coffee":373,"./state.coffee":376,"./utils.coffee":378}],378:[function(require,module,exports){
var clamp, clone, deepEqual, lerp, now,
  slice = [].slice;

exports.resize = function(from, to, method) {
  var scale;
  if (from == null) {
    from = [];
  }
  if (to == null) {
    to = [];
  }
  if (method == null) {
    method = "stretch";
  }
  if (method === "stretch") {
    return to;
  } else if (method === "fit") {
    scale = Math.min(to[0] / from[0], to[1] / from[1]);
    return [from[0] * scale >> 0, from[1] * scale >> 0];
  } else if (method === "fill") {
    scale = Math.max(to[0] / from[0], to[1] / from[1]);
    return [from[0] * scale >> 0, from[1] * scale >> 0];
  }
};

exports.rotateBounds = function(box, angle, flipX, flipY) {
  var bl, br, tl, tr, w, x, y, z;
  x = box[0];
  y = box[1];
  z = x + box[2];
  w = y + box[3];
  if (flipX) {
    x = 1.0 - x;
    z = 1.0 - z;
  }
  if (flipY) {
    y = 1.0 - y;
    w = 1.0 - w;
  }
  x -= 0.5;
  y -= 0.5;
  z -= 0.5;
  w -= 0.5;
  tl = exports.rotateVector([x, y], angle);
  tr = exports.rotateVector([z, y], angle);
  br = exports.rotateVector([z, w], angle);
  bl = exports.rotateVector([x, w], angle);
  x = Math.min(tl[0], tr[0], br[0], bl[0]) + 0.5;
  y = Math.min(tl[1], tr[1], br[1], bl[1]) + 0.5;
  z = Math.max(tl[0], tr[0], br[0], bl[0]) + 0.5;
  w = Math.max(tl[1], tr[1], br[1], bl[1]) + 0.5;
  return [x, y, z - x, w - y];
};

exports.rotatePoint = function(point, angle, flipX, flipY) {
  var ref, x, y;
  x = point[0], y = point[1];
  if (flipX) {
    x = 1.0 - x;
  }
  if (flipY) {
    y = 1.0 - y;
  }
  x -= 0.5;
  y -= 0.5;
  ref = exports.rotateVector([x, y], angle), x = ref[0], y = ref[1];
  return [x + 0.5, y + 0.5];
};

exports.rotateVector = function(vec, degrees) {
  var radians;
  radians = degrees * (Math.PI / 180);
  return [vec[0] * Math.cos(radians) - vec[1] * Math.sin(radians), vec[0] * Math.sin(radians) + vec[1] * Math.cos(radians)];
};

exports.clamp = clamp = function(val, min, max) {
  return Math.max(min, Math.min(max, val));
};

exports.ramp = function(t) {
  t *= 2.0;
  if (t >= 1.0) {
    t -= 1.0;
    t = Math.log(0.5) / Math.log(0.5 * (1.0 - t) + 0.9332 * t);
  }
  return clamp(t, 0.0, 10.0);
};

exports.lerp = lerp = function(t, a, b) {
  return a * (1 - t) + b * t;
};

exports.distance = function(p1, p2) {
  var dx, dy;
  dx = p2[0] - p1[0];
  dy = p2[1] - p1[1];
  dx *= dx;
  dy *= dy;
  return Math.sqrt(dx + dy);
};

exports.gaussian = function(x) {
  return Math.exp(-Math.PI * 2 * x * x);
};

exports.now = now = (function() {
  var _performance;
  _performance = window.performance || Date;
  return function() {
    return _performance.now.apply(_performance);
  };
})();

exports.throttleAnimation = function(func) {
  var busy, requestId;
  requestId = null;
  busy = false;
  return function() {
    var _this, args;
    _this = this;
    args = arguments;
    cancelAnimationFrame(requestId);
    if (busy) {
      requestId = requestAnimationFrame(function() {
        return func.apply(_this, args);
      });
      return;
    }
    busy = true;
    func.apply(_this, args);
    requestAnimationFrame(function() {
      return busy = false;
    });
  };
};

exports.throttle = function(func, ms) {
  var current, last, throttle, timeout;
  timeout = null;
  last = null;
  current = null;
  return throttle = function() {
    var args, context;
    context = this;
    args = arguments;
    current = now();
    if (last && current < last + ms) {
      clearTimeout(timeout);
      return timeout = setTimeout(function() {
        return func.apply(context, args);
      }, ms);
    } else {
      last = current;
      return func.apply(context, args);
    }
  };
};

exports.debounce = function(func, ms) {
  var timeout;
  if (ms == null) {
    ms = 1;
  }
  timeout = null;
  return function() {
    var _this, args;
    _this = this;
    args = arguments;
    clearTimeout(timeout);
    return timeout = setTimeout(function() {
      return func.apply(_this, args);
    }, ms);
  };
};

exports.clone = clone = function(collection) {
  var copy, key;
  copy = collection;
  if (collection && typeof collection === "object" && !(collection instanceof ArrayBuffer) && !(collection.buffer instanceof ArrayBuffer)) {
    copy = collection instanceof Array ? [] : {};
    for (key in collection) {
      copy[key] = clone(collection[key]);
    }
  }
  return copy;
};

exports.diff = function(a, b) {
  var diff, key, val;
  diff = {};
  for (key in a) {
    val = a[key];
    if (val instanceof Array) {
      continue;
    }
    if (b[key] !== val) {
      diff[key] = b[key];
    }
  }
  return diff;
};

exports.shallowEqual = function(a, b) {
  var i, j, key, keysA, keysB, len;
  if (a === b) {
    return true;
  }
  if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {
    return false;
  }
  keysA = Object.keys(a);
  keysB = Object.keys(b);
  if (keysA.length !== keysB.length) {
    return false;
  }
  for (i = j = 0, len = keysA.length; j < len; i = ++j) {
    key = keysA[i];
    if (!hasOwnProperty.call(b, keysA[i]) || !(a[keysA[i]] === b[keysA[i]])) {
      return false;
    }
  }
  return true;
};

exports.deepEqual = deepEqual = function(prev, curr, compareArray, onlyProps) {
  var i, isEqual, j, k, len, v;
  isEqual = true;
  if (prev instanceof Array) {
    isEqual = curr && prev.length === curr.length;
    if (!isEqual) {
      return isEqual;
    }
    if (compareArray) {
      for (i = j = 0, len = prev.length; j < len; i = ++j) {
        v = prev[i];
        isEqual = deepEqual(prev[i], curr[i], compareArray, onlyProps);
        if (!isEqual) {
          return isEqual;
        }
      }
    }
  } else if (typeof prev === "object") {
    for (k in prev) {
      v = prev[k];
      if (onlyProps && !onlyProps.hasOwnProperty(k)) {
        continue;
      }
      isEqual = deepEqual(prev[k], curr[k], compareArray, onlyProps);
      if (!isEqual) {
        return isEqual;
      }
    }
  } else {
    isEqual = prev === curr;
  }
  return isEqual;
};

exports.assign = function() {
  var j, key, len, obj, source, sources, val;
  sources = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  obj = {};
  for (j = 0, len = sources.length; j < len; j++) {
    source = sources[j];
    for (key in source) {
      val = source[key];
      if (source.hasOwnProperty(key)) {
        obj[key] = val;
      }
    }
  }
  return obj;
};

exports.interpolateAdjustments = function(state, percent, output) {
  var adjustments, defaults, i, j, k, key, l, len, len1, localDefaults, m, mask, p, point, val;
  defaults = Filtr.getDefaultState();
  localDefaults = Filtr.getDefaultLocalState();
  adjustments = output || state;
  for (key in adjustments) {
    val = adjustments[key];
    if (typeof val === "number") {
      if (defaults[key] != null) {
        adjustments[key] = lerp(percent, defaults[key], state[key]);
      }
    } else {
      switch (key) {
        case "curves_all":
        case "curves_red":
        case "curves_green":
        case "curves_blue":
          for (i = j = 0, len = val.length; j < len; i = ++j) {
            p = val[i];
            point = state[key][i];
            p[1] = lerp(percent, point[0], point[1]);
          }
          break;
        case "local_adjustments":
          for (i = l = 0, len1 = val.length; l < len1; i = ++l) {
            m = val[i];
            mask = state.local_adjustments[i];
            for (k in m.adjustments) {
              m.adjustments[k] = lerp(percent, localDefaults[k], mask.adjustments[k]);
            }
          }
      }
    }
  }
  return adjustments;
};

exports.getOrientationSettings = function(value) {
  var values;
  if (value == null) {
    value = 0;
  }
  if (value > 8 || value < 0) {
    value = 1;
  }
  values = [
    {
      rotate90: 0,
      flip_x: false,
      flip_y: false
    }, {
      rotate90: 0,
      flip_x: false,
      flip_y: false
    }, {
      rotate90: 0,
      flip_x: true,
      flip_y: false
    }, {
      rotate90: 2,
      flip_x: false,
      flip_y: false
    }, {
      rotate90: 0,
      flip_x: false,
      flip_y: true
    }, {
      rotate90: 1,
      flip_x: true,
      flip_y: false
    }, {
      rotate90: 1,
      flip_x: false,
      flip_y: false
    }, {
      rotate90: -1,
      flip_x: true,
      flip_y: false
    }, {
      rotate90: -1,
      flip_x: false,
      flip_y: false
    }
  ];
  return values[value];
};

exports.animate = function(a, b, duration, callback, done) {
  var animate, d, end, frameId, from, m, res, start, time, to;
  time = 0;
  start = Date.now();
  end = start + 1000 * duration;
  from = a instanceof Array ? a : [a];
  to = b instanceof Array ? b : [b];
  res = from.slice(0);
  d = end - start;
  m = 0;
  frameId = null;
  animate = function() {
    var c, i, j, len, t;
    time = Date.now();
    t = 1 - (time - start) / d;
    m = -(t * t * t - 1);
    for (i = j = 0, len = from.length; j < len; i = ++j) {
      c = from[i];
      res[i] = (to[i] - c) * m + c;
    }
    callback(res);
    if (time <= end) {
      return frameId = requestAnimationFrame(animate);
    } else {
      callback(to);
      if (typeof done === "function") {
        return done();
      }
    }
  };
  animate();
  return function() {
    cancelAnimationFrame(frameId);
    return animate = callback = done = function() {};
  };
};


},{}]},{},[337]);
